lib3mf_core/crypto/
verification.rs1use crate::error::{Lib3mfError, Result};
2use crate::model::crypto::*;
3use base64::prelude::*;
4use rsa::RsaPublicKey;
5use rsa::signature::Verifier;
6use sha1::Sha1;
7use sha2::{Digest, Sha256};
8use x509_parser::prelude::FromDer;
9
10pub fn verify_signature<F>(
20 signature: &Signature,
21 public_key: &RsaPublicKey,
22 content_resolver: F,
23 signed_info_bytes: &[u8],
24) -> Result<bool>
25where
26 F: Fn(&str) -> Result<Vec<u8>>,
27{
28 for reference in &signature.signed_info.references {
30 verify_reference(reference, &content_resolver)?;
31 }
32
33 let sig_value = BASE64_STANDARD
35 .decode(&signature.signature_value.value)
36 .map_err(|e| Lib3mfError::Validation(format!("Invalid base64 signature: {}", e)))?;
37
38 match signature.signed_info.signature_method.algorithm.as_str() {
41 "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" => {
42 let verifying_key = rsa::pkcs1v15::VerifyingKey::<Sha256>::new(public_key.clone());
44 let rsa_signature =
45 rsa::pkcs1v15::Signature::try_from(sig_value.as_slice()).map_err(|e| {
46 Lib3mfError::Validation(format!("Invalid RSA signature format: {}", e))
47 })?;
48
49 verifying_key
50 .verify(signed_info_bytes, &rsa_signature)
51 .map_err(|e| {
52 Lib3mfError::Validation(format!("Signature verification failed: {}", e))
53 })?;
54 }
55 _ => {
56 return Err(Lib3mfError::Validation(format!(
57 "Unsupported signature method: {}",
58 signature.signed_info.signature_method.algorithm
59 )));
60 }
61 }
62
63 Ok(true)
64}
65
66fn verify_reference<F>(reference: &Reference, content_resolver: &F) -> Result<()>
67where
68 F: Fn(&str) -> Result<Vec<u8>>,
69{
70 let content = content_resolver(&reference.uri)?;
72
73 let calculated_digest = match reference.digest_method.algorithm.as_str() {
75 "http://www.w3.org/2001/04/xmlenc#sha256" => {
76 let mut hasher = Sha256::new();
77 hasher.update(&content);
78 hasher.finalize().to_vec()
79 }
80 "http://www.w3.org/2000/09/xmldsig#sha1" => {
81 let mut hasher = Sha1::new();
82 hasher.update(&content);
83 hasher.finalize().to_vec()
84 }
85 _ => {
86 return Err(Lib3mfError::Validation(format!(
87 "Unsupported digest method: {}",
88 reference.digest_method.algorithm
89 )));
90 }
91 };
92
93 let stored_digest = BASE64_STANDARD
95 .decode(&reference.digest_value.value)
96 .map_err(|e| Lib3mfError::Validation(format!("Invalid base64 digest: {}", e)))?;
97
98 if calculated_digest != stored_digest {
99 return Err(Lib3mfError::Validation(format!(
100 "Digest mismatch for URI {}",
101 reference.uri
102 )));
103 }
104
105 Ok(())
106}
107
108pub fn verify_signature_extended<F>(
110 signature: &Signature,
111 content_resolver: F,
112 signed_info_bytes: &[u8],
113) -> Result<bool>
114where
115 F: Fn(&str) -> Result<Vec<u8>>,
116{
117 let key = extract_key_from_signature(signature)?;
118 verify_signature(signature, &key, content_resolver, signed_info_bytes)
119}
120
121pub fn extract_key_from_signature(signature: &Signature) -> Result<RsaPublicKey> {
122 if let Some(info) = &signature.key_info {
123 if let Some(kv) = &info.key_value
125 && let Some(rsa_val) = &kv.rsa_key_value
126 {
127 let n_bytes = BASE64_STANDARD
128 .decode(&rsa_val.modulus)
129 .map_err(|e| Lib3mfError::Validation(format!("Invalid modulus base64: {}", e)))?;
130 let e_bytes = BASE64_STANDARD
131 .decode(&rsa_val.exponent)
132 .map_err(|e| Lib3mfError::Validation(format!("Invalid exponent base64: {}", e)))?;
133
134 let n = rsa::BigUint::from_bytes_be(&n_bytes);
135 let e = rsa::BigUint::from_bytes_be(&e_bytes);
136
137 return RsaPublicKey::new(n, e).map_err(|e| {
138 Lib3mfError::Validation(format!("Invalid RSA key components: {}", e))
139 });
140 }
141
142 if let Some(x509) = &info.x509_data
144 && let Some(cert_b64) = &x509.certificate
145 {
146 let clean_b64: String = cert_b64.chars().filter(|c| !c.is_whitespace()).collect();
148 let cert_der = BASE64_STANDARD
149 .decode(&clean_b64)
150 .map_err(|e| Lib3mfError::Validation(format!("Invalid X509 base64: {}", e)))?;
151
152 let (_, cert) = x509_parser::certificate::X509Certificate::from_der(&cert_der)
153 .map_err(|e| Lib3mfError::Validation(format!("Invalid X509 certificate: {}", e)))?;
154
155 use rsa::pkcs8::DecodePublicKey;
159 return RsaPublicKey::from_public_key_der(cert.tbs_certificate.subject_pki.raw)
160 .map_err(|e| Lib3mfError::Validation(format!("Invalid RSA key in cert: {}", e)));
161 }
162 }
163 Err(Lib3mfError::Validation(
164 "No usable KeyValue or X509Certificate found in KeyInfo".into(),
165 ))
166}