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 support for content warnings.
|
||||
- Support integrity proofs with `DataIntegrityProof` type.
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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="{}""#,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue