Support integrity proofs with DataIntegrityProof type

This commit is contained in:
silverpill 2023-04-21 20:13:14 +00:00
parent dbe5550075
commit a63d528302
7 changed files with 52 additions and 22 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added support for content warnings. - Added support for content warnings.
- Added `authentication_methods` field to `CredentialAccount` object (Mastodon API). - Added `authentication_methods` field to `CredentialAccount` object (Mastodon API).
- Support integrity proofs with `DataIntegrityProof` type.
### Changed ### Changed

View file

@ -47,7 +47,7 @@ pub fn deserialize_public_key(
} }
/// RSASSA-PKCS1-v1_5 signature /// RSASSA-PKCS1-v1_5 signature
pub fn create_rsa_signature( pub fn create_rsa_sha256_signature(
private_key: &RsaPrivateKey, private_key: &RsaPrivateKey,
message: &str, message: &str,
) -> Result<Vec<u8>, rsa::errors::Error> { ) -> Result<Vec<u8>, rsa::errors::Error> {
@ -63,7 +63,7 @@ pub fn get_message_digest(message: &str) -> String {
digest_b64 digest_b64
} }
pub fn verify_rsa_signature( pub fn verify_rsa_sha256_signature(
public_key: &RsaPublicKey, public_key: &RsaPublicKey,
message: &str, message: &str,
signature: &[u8], signature: &[u8],
@ -102,13 +102,13 @@ YsFtrgWDQ/s8k86sNBU+Ce2GOL7seh46kyAWgJeohh4Rcrr23rftHbvxOcRM8VzYuCeb1DgVhPGtA0xU
fn test_verify_rsa_signature() { fn test_verify_rsa_signature() {
let private_key = generate_weak_rsa_key().unwrap(); let private_key = generate_weak_rsa_key().unwrap();
let message = "test".to_string(); let message = "test".to_string();
let signature = create_rsa_signature( let signature = create_rsa_sha256_signature(
&private_key, &private_key,
&message, &message,
).unwrap(); ).unwrap();
let public_key = RsaPublicKey::from(&private_key); let public_key = RsaPublicKey::from(&private_key);
let is_valid = verify_rsa_signature( let is_valid = verify_rsa_sha256_signature(
&public_key, &public_key,
&message, &message,
&signature, &signature,

View file

@ -3,7 +3,7 @@ use chrono::Utc;
use rsa::RsaPrivateKey; use rsa::RsaPrivateKey;
use mitra_utils::crypto_rsa::{ use mitra_utils::crypto_rsa::{
create_rsa_signature, create_rsa_sha256_signature,
get_message_digest, get_message_digest,
}; };
@ -72,7 +72,7 @@ pub fn create_http_signature(
.map(|(name, _)| name.to_string()) .map(|(name, _)| name.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" "); .join(" ");
let signature = create_rsa_signature(signer_key, &message)?; let signature = create_rsa_sha256_signature(signer_key, &message)?;
let signature_parameter = base64::encode(signature); let signature_parameter = base64::encode(signature);
let signature_header = format!( let signature_header = format!(
r#"keyId="{}",algorithm="{}",headers="{}",signature="{}""#, r#"keyId="{}",algorithm="{}",headers="{}",signature="{}""#,

View file

@ -5,7 +5,7 @@ use chrono::{DateTime, Duration, TimeZone, Utc};
use regex::Regex; use regex::Regex;
use rsa::RsaPublicKey; use rsa::RsaPublicKey;
use mitra_utils::crypto_rsa::verify_rsa_signature; use mitra_utils::crypto_rsa::verify_rsa_sha256_signature;
const SIGNATURE_PARAMETER_RE: &str = r#"^(?P<key>[a-zA-Z]+)="(?P<value>.+)"$"#; const SIGNATURE_PARAMETER_RE: &str = r#"^(?P<key>[a-zA-Z]+)="(?P<value>.+)"$"#;
@ -134,7 +134,7 @@ pub fn verify_http_signature(
log::warn!("signature has expired"); log::warn!("signature has expired");
}; };
let signature = base64::decode(&signature_data.signature)?; let signature = base64::decode(&signature_data.signature)?;
let is_valid_signature = verify_rsa_signature( let is_valid_signature = verify_rsa_sha256_signature(
signer_key, signer_key,
&signature_data.message, &signature_data.message,
&signature, &signature,

View file

@ -8,7 +8,7 @@ use mitra_utils::{
canonicalize_object, canonicalize_object,
CanonicalizationError, CanonicalizationError,
}, },
crypto_rsa::create_rsa_signature, crypto_rsa::create_rsa_sha256_signature,
did_key::DidKey, did_key::DidKey,
did_pkh::DidPkh, did_pkh::DidPkh,
multibase::encode_multibase_base58btc, multibase::encode_multibase_base58btc,
@ -30,6 +30,8 @@ pub(super) const PROOF_PURPOSE: &str = "assertionMethod";
pub struct IntegrityProof { pub struct IntegrityProof {
#[serde(rename = "type")] #[serde(rename = "type")]
pub proof_type: String, pub proof_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cryptosuite: Option<String>,
pub proof_purpose: String, pub proof_purpose: String,
pub verification_method: String, pub verification_method: String,
pub created: DateTime<Utc>, pub created: DateTime<Utc>,
@ -43,6 +45,7 @@ impl IntegrityProof {
) -> Self { ) -> Self {
Self { Self {
proof_type: PROOF_TYPE_JCS_RSA.to_string(), proof_type: PROOF_TYPE_JCS_RSA.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(), proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer_key_id.to_string(), verification_method: signer_key_id.to_string(),
created: Utc::now(), created: Utc::now(),
@ -56,6 +59,7 @@ impl IntegrityProof {
) -> Self { ) -> Self {
Self { Self {
proof_type: PROOF_TYPE_JCS_EIP191.to_string(), proof_type: PROOF_TYPE_JCS_EIP191.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(), proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer.to_string(), verification_method: signer.to_string(),
created: Utc::now(), created: Utc::now(),
@ -69,6 +73,7 @@ impl IntegrityProof {
) -> Self { ) -> Self {
Self { Self {
proof_type: PROOF_TYPE_JCS_ED25519.to_string(), proof_type: PROOF_TYPE_JCS_ED25519.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(), proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer.to_string(), verification_method: signer.to_string(),
created: Utc::now(), created: Utc::now(),
@ -115,14 +120,17 @@ pub fn sign_object(
signer_key_id: &str, signer_key_id: &str,
) -> Result<Value, JsonSignatureError> { ) -> Result<Value, JsonSignatureError> {
// Canonicalize // Canonicalize
let message = canonicalize_object(object)?; let transformed_object = canonicalize_object(object)?;
// Sign // Sign
let signature = create_rsa_signature(signer_key, &message)?; let signature = create_rsa_sha256_signature(
signer_key,
&transformed_object,
)?;
// Insert proof // Insert proof
let proof = IntegrityProof::jcs_rsa(signer_key_id, &signature); let proof = IntegrityProof::jcs_rsa(signer_key_id, &signature);
let mut object_value = serde_json::to_value(object)?; let mut signed_object = object.clone();
add_integrity_proof(&mut object_value, proof)?; add_integrity_proof(&mut signed_object, proof)?;
Ok(object_value) Ok(signed_object)
} }
pub fn is_object_signed(object: &Value) -> bool { pub fn is_object_signed(object: &Value) -> bool {

View file

@ -9,12 +9,16 @@ pub const PROOF_TYPE_ID_EIP191: &str = "ethereum-eip191-00";
// Identity proof, version 2022A // Identity proof, version 2022A
pub const PROOF_TYPE_ID_MINISIGN: &str = "MitraMinisignSignature2022A"; pub const PROOF_TYPE_ID_MINISIGN: &str = "MitraMinisignSignature2022A";
// https://w3c.github.io/vc-data-integrity/#dataintegrityproof
pub const DATA_INTEGRITY_PROOF: &str = "DataIntegrityProof";
// Similar to https://identity.foundation/JcsEd25519Signature2020/ // Similar to https://identity.foundation/JcsEd25519Signature2020/
// - Canonicalization algorithm: JCS // - Canonicalization algorithm: JCS
// - Digest algorithm: SHA-256 // - Digest algorithm: SHA-256
// - Signature algorithm: RSASSA-PKCS1-v1_5 // - Signature algorithm: RSASSA-PKCS1-v1_5
pub const PROOF_TYPE_JCS_RSA: &str = "MitraJcsRsaSignature2022"; pub const PROOF_TYPE_JCS_RSA: &str = "MitraJcsRsaSignature2022";
pub const PROOF_TYPE_JCS_RSA_LEGACY: &str = "JcsRsaSignature2022"; pub const PROOF_TYPE_JCS_RSA_LEGACY: &str = "JcsRsaSignature2022";
pub const CRYPTOSUITE_JCS_RSA: &str = "mitra-jcs-rsa-2022";
// Similar to EthereumPersonalSignature2021 but with JCS // Similar to EthereumPersonalSignature2021 but with JCS
pub const PROOF_TYPE_JCS_EIP191: &str = "MitraJcsEip191Signature2022"; pub const PROOF_TYPE_JCS_EIP191: &str = "MitraJcsEip191Signature2022";
@ -49,3 +53,13 @@ impl FromStr for ProofType {
Ok(proof_type) Ok(proof_type)
} }
} }
impl ProofType {
pub fn from_cryptosuite(value: &str) -> Result<Self, ConversionError> {
let proof_type = match value {
CRYPTOSUITE_JCS_RSA => Self::JcsRsaSignature,
_ => return Err(ConversionError),
};
Ok(proof_type)
}
}

View file

@ -9,7 +9,7 @@ use mitra_utils::{
canonicalize_object, canonicalize_object,
CanonicalizationError, CanonicalizationError,
}, },
crypto_rsa::verify_rsa_signature, crypto_rsa::verify_rsa_sha256_signature,
did::Did, did::Did,
did_key::DidKey, did_key::DidKey,
did_pkh::DidPkh, did_pkh::DidPkh,
@ -25,7 +25,7 @@ use super::create::{
PROOF_KEY, PROOF_KEY,
PROOF_PURPOSE, PROOF_PURPOSE,
}; };
use super::proofs::ProofType; use super::proofs::{ProofType, DATA_INTEGRITY_PROOF};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum JsonSigner { pub enum JsonSigner {
@ -76,8 +76,15 @@ pub fn get_json_signature(
if proof.proof_purpose != PROOF_PURPOSE { if proof.proof_purpose != PROOF_PURPOSE {
return Err(VerificationError::InvalidProof("invalid proof purpose")); return Err(VerificationError::InvalidProof("invalid proof purpose"));
}; };
let signature_type = proof.proof_type.parse() let proof_type = if proof.proof_type == DATA_INTEGRITY_PROOF {
.map_err(|_| VerificationError::InvalidProof("unsupported proof type"))?; let cryptosuite = proof.cryptosuite.as_ref()
.ok_or(VerificationError::InvalidProof("cryptosuite is not specified"))?;
ProofType::from_cryptosuite(cryptosuite)
.map_err(|_| VerificationError::InvalidProof("unsupported proof type"))?
} else {
proof.proof_type.parse()
.map_err(|_| VerificationError::InvalidProof("unsupported proof type"))?
};
let signer = if let Ok(did) = Did::from_str(&proof.verification_method) { let signer = if let Ok(did) = Did::from_str(&proof.verification_method) {
JsonSigner::Did(did) JsonSigner::Did(did)
} else if Url::parse(&proof.verification_method).is_ok() { } else if Url::parse(&proof.verification_method).is_ok() {
@ -85,12 +92,12 @@ pub fn get_json_signature(
} else { } else {
return Err(VerificationError::InvalidProof("unsupported verification method")); return Err(VerificationError::InvalidProof("unsupported verification method"));
}; };
let message = canonicalize_object(&object)?; let transformed_object = canonicalize_object(&object)?;
let signature = decode_multibase_base58btc(&proof.proof_value)?; let signature = decode_multibase_base58btc(&proof.proof_value)?;
let signature_data = SignatureData { let signature_data = SignatureData {
signature_type, signature_type: proof_type,
signer, signer,
message, message: transformed_object,
signature, signature,
}; };
Ok(signature_data) Ok(signature_data)
@ -100,7 +107,7 @@ pub fn verify_rsa_json_signature(
signature_data: &SignatureData, signature_data: &SignatureData,
signer_key: &RsaPublicKey, signer_key: &RsaPublicKey,
) -> Result<(), VerificationError> { ) -> Result<(), VerificationError> {
let is_valid_signature = verify_rsa_signature( let is_valid_signature = verify_rsa_sha256_signature(
signer_key, signer_key,
&signature_data.message, &signature_data.message,
&signature_data.signature, &signature_data.signature,