Support integrity proofs with DataIntegrityProof type
This commit is contained in:
parent
05eeb5ae2a
commit
b6e7fa5d13
7 changed files with 52 additions and 22 deletions
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added support for content warnings.
|
- Added support for content warnings.
|
||||||
|
- Support integrity proofs with `DataIntegrityProof` type.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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="{}""#,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -24,7 +24,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 {
|
||||||
|
@ -75,8 +75,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() {
|
||||||
|
@ -84,12 +91,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)
|
||||||
|
@ -99,7 +106,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,
|
||||||
|
|
Loading…
Reference in a new issue