Support integrity proofs with DataIntegrityProof type

This commit is contained in:
silverpill 2023-04-21 20:13:14 +00:00 committed by Rafael Caricio
parent 05eeb5ae2a
commit b6e7fa5d13
Signed by: rafaelcaricio
GPG key ID: 3C86DBCE8E93C947
7 changed files with 52 additions and 22 deletions

View file

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

View file

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

View file

@ -3,7 +3,7 @@ use chrono::Utc;
use rsa::RsaPrivateKey;
use mitra_utils::crypto_rsa::{
create_rsa_signature,
create_rsa_sha256_signature,
get_message_digest,
};
@ -72,7 +72,7 @@ pub fn create_http_signature(
.map(|(name, _)| name.to_string())
.collect::<Vec<String>>()
.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_header = format!(
r#"keyId="{}",algorithm="{}",headers="{}",signature="{}""#,

View file

@ -5,7 +5,7 @@ use chrono::{DateTime, Duration, TimeZone, Utc};
use regex::Regex;
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>.+)"$"#;
@ -134,7 +134,7 @@ pub fn verify_http_signature(
log::warn!("signature has expired");
};
let signature = base64::decode(&signature_data.signature)?;
let is_valid_signature = verify_rsa_signature(
let is_valid_signature = verify_rsa_sha256_signature(
signer_key,
&signature_data.message,
&signature,

View file

@ -8,7 +8,7 @@ use mitra_utils::{
canonicalize_object,
CanonicalizationError,
},
crypto_rsa::create_rsa_signature,
crypto_rsa::create_rsa_sha256_signature,
did_key::DidKey,
did_pkh::DidPkh,
multibase::encode_multibase_base58btc,
@ -30,6 +30,8 @@ pub(super) const PROOF_PURPOSE: &str = "assertionMethod";
pub struct IntegrityProof {
#[serde(rename = "type")]
pub proof_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cryptosuite: Option<String>,
pub proof_purpose: String,
pub verification_method: String,
pub created: DateTime<Utc>,
@ -43,6 +45,7 @@ impl IntegrityProof {
) -> Self {
Self {
proof_type: PROOF_TYPE_JCS_RSA.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer_key_id.to_string(),
created: Utc::now(),
@ -56,6 +59,7 @@ impl IntegrityProof {
) -> Self {
Self {
proof_type: PROOF_TYPE_JCS_EIP191.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer.to_string(),
created: Utc::now(),
@ -69,6 +73,7 @@ impl IntegrityProof {
) -> Self {
Self {
proof_type: PROOF_TYPE_JCS_ED25519.to_string(),
cryptosuite: None,
proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer.to_string(),
created: Utc::now(),
@ -115,14 +120,17 @@ pub fn sign_object(
signer_key_id: &str,
) -> Result<Value, JsonSignatureError> {
// Canonicalize
let message = canonicalize_object(object)?;
let transformed_object = canonicalize_object(object)?;
// Sign
let signature = create_rsa_signature(signer_key, &message)?;
let signature = create_rsa_sha256_signature(
signer_key,
&transformed_object,
)?;
// Insert proof
let proof = IntegrityProof::jcs_rsa(signer_key_id, &signature);
let mut object_value = serde_json::to_value(object)?;
add_integrity_proof(&mut object_value, proof)?;
Ok(object_value)
let mut signed_object = object.clone();
add_integrity_proof(&mut signed_object, proof)?;
Ok(signed_object)
}
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
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/
// - Canonicalization algorithm: JCS
// - Digest algorithm: SHA-256
// - Signature algorithm: RSASSA-PKCS1-v1_5
pub const PROOF_TYPE_JCS_RSA: &str = "MitraJcsRsaSignature2022";
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
pub const PROOF_TYPE_JCS_EIP191: &str = "MitraJcsEip191Signature2022";
@ -49,3 +53,13 @@ impl FromStr for ProofType {
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,
CanonicalizationError,
},
crypto_rsa::verify_rsa_signature,
crypto_rsa::verify_rsa_sha256_signature,
did::Did,
did_key::DidKey,
did_pkh::DidPkh,
@ -24,7 +24,7 @@ use super::create::{
PROOF_KEY,
PROOF_PURPOSE,
};
use super::proofs::ProofType;
use super::proofs::{ProofType, DATA_INTEGRITY_PROOF};
#[derive(Debug, PartialEq)]
pub enum JsonSigner {
@ -75,8 +75,15 @@ pub fn get_json_signature(
if proof.proof_purpose != PROOF_PURPOSE {
return Err(VerificationError::InvalidProof("invalid proof purpose"));
};
let signature_type = proof.proof_type.parse()
.map_err(|_| VerificationError::InvalidProof("unsupported proof type"))?;
let proof_type = if proof.proof_type == DATA_INTEGRITY_PROOF {
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) {
JsonSigner::Did(did)
} else if Url::parse(&proof.verification_method).is_ok() {
@ -84,12 +91,12 @@ pub fn get_json_signature(
} else {
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_data = SignatureData {
signature_type,
signature_type: proof_type,
signer,
message,
message: transformed_object,
signature,
};
Ok(signature_data)
@ -99,7 +106,7 @@ pub fn verify_rsa_json_signature(
signature_data: &SignatureData,
signer_key: &RsaPublicKey,
) -> Result<(), VerificationError> {
let is_valid_signature = verify_rsa_signature(
let is_valid_signature = verify_rsa_sha256_signature(
signer_key,
&signature_data.message,
&signature_data.signature,