1use crate::error::{Error, Result};
9use cms::cert::CertificateChoices;
10use cms::content_info::ContentInfo;
11use cms::signed_data::SignerInfo;
12use der::{Decode, Encode};
13use rsa::RsaPublicKey;
14use rsa::pkcs1::DecodeRsaPublicKey;
15use rsa::signature::Verifier;
16use rsa::traits::PublicKeyParts;
17use sha2::{Sha256, Sha384, Sha512};
18use tracing::{debug, trace, warn};
19use x509_cert::certificate::Certificate;
20use x509_cert::spki::SubjectPublicKeyInfoRef;
21
22#[derive(Debug, Clone)]
24pub struct CmsSignatureInfo {
25 pub signed_data: SignedDataInfo,
27 pub signers: Vec<SignerDetails>,
29 pub certificates: Vec<CertificateDetails>,
31 pub raw_signed_data: Vec<u8>,
33}
34
35#[derive(Debug, Clone)]
37pub struct SignedDataInfo {
38 pub version: u8,
40 pub digest_algorithms: Vec<String>,
42 pub is_detached: bool,
44}
45
46#[derive(Debug, Clone)]
48pub struct SignerDetails {
49 pub identifier: SignerIdentifier,
51 pub digest_algorithm: String,
53 pub signature_algorithm: String,
55 pub signature: Vec<u8>,
57 pub certificate: Option<CertificateDetails>,
59 pub public_key: Option<PublicKeyInfo>,
61 pub has_signed_attributes: bool,
63 pub signed_attributes_der: Option<Vec<u8>>,
65}
66
67#[derive(Debug, Clone)]
69pub struct SignerIdentifier {
70 pub issuer: String,
72 pub serial_number: String,
74}
75
76#[derive(Debug, Clone)]
78pub struct CertificateDetails {
79 pub subject: String,
81 pub issuer: String,
83 pub serial_number: String,
85 pub public_key: Option<PublicKeyInfo>,
87}
88
89#[derive(Debug, Clone)]
91pub struct PublicKeyInfo {
92 pub algorithm: String,
94 pub key_size: usize,
96 pub key_bytes: Vec<u8>,
98}
99
100pub fn parse_cms_signature(signature_bytes: &[u8]) -> Result<CmsSignatureInfo> {
109 trace!("Parsing CMS signature: {} bytes", signature_bytes.len());
110
111 let content_info = ContentInfo::from_der(signature_bytes)
113 .map_err(|e| Error::Asn1Error(format!("Failed to parse ContentInfo: {e:?}")))?;
114
115 let signed_data_oid = der::asn1::ObjectIdentifier::new("1.2.840.113549.1.7.2")
117 .map_err(|e| Error::Asn1Error(format!("Invalid OID: {e}")))?;
118
119 if content_info.content_type != signed_data_oid {
120 return Err(Error::Asn1Error(
121 "ContentInfo is not SignedData".to_string(),
122 ));
123 }
124
125 let signed_data_bytes = content_info
127 .content
128 .to_der()
129 .map_err(|e| Error::Asn1Error(format!("Failed to encode content: {e:?}")))?;
130
131 let signed_data = cms::signed_data::SignedData::from_der(&signed_data_bytes)
133 .map_err(|e| Error::Asn1Error(format!("Failed to parse SignedData: {e:?}")))?;
134
135 debug!(
136 "Parsed SignedData: {} signers",
137 signed_data.signer_infos.0.len()
138 );
139
140 let digest_algorithms: Vec<String> = signed_data
142 .digest_algorithms
143 .iter()
144 .map(|alg| oid_to_algorithm_name(&alg.oid))
145 .collect();
146
147 let is_detached = signed_data.encap_content_info.econtent.is_none();
149
150 let mut certificates = Vec::new();
152 if let Some(cert_set) = &signed_data.certificates {
153 debug!("Certificate set present with {} entries", cert_set.0.len());
154 for (i, cert_choice) in cert_set.0.iter().enumerate() {
155 match cert_choice {
156 CertificateChoices::Certificate(cert) => {
157 debug!("Entry {} is a Certificate", i);
158 if let Ok(details) = extract_certificate_details(cert) {
159 certificates.push(details);
160 }
161 }
162 CertificateChoices::Other(_) => {
163 debug!(
164 "Entry {} is not a Certificate (different CertificateChoice variant)",
165 i
166 );
167 }
168 }
169 }
170 } else {
171 debug!("No certificate set in SignedData");
172 }
173
174 debug!("Found {} certificates", certificates.len());
175
176 let mut signers = Vec::new();
178 debug!("Processing {} signers", signed_data.signer_infos.0.len());
179 for (i, signer_info) in signed_data.signer_infos.0.iter().enumerate() {
180 debug!("Processing signer #{}", i);
181 match parse_signer_info(signer_info, &certificates) {
182 Ok(signer) => {
183 debug!("Successfully parsed signer #{}", i);
184 signers.push(signer);
185 }
186 Err(e) => {
187 warn!("Failed to parse signer #{}: {}", i, e);
188 }
189 }
190 }
191
192 Ok(CmsSignatureInfo {
193 signed_data: SignedDataInfo {
194 version: 1, digest_algorithms,
196 is_detached,
197 },
198 signers,
199 certificates,
200 raw_signed_data: signed_data_bytes,
201 })
202}
203
204fn parse_signer_info(
206 signer_info: &SignerInfo,
207 certificates: &[CertificateDetails],
208) -> Result<SignerDetails> {
209 let identifier = match &signer_info.sid {
210 cms::signed_data::SignerIdentifier::IssuerAndSerialNumber(isn) => {
211 debug!("Signer uses IssuerAndSerialNumber");
212 SignerIdentifier {
213 issuer: isn.issuer.to_string(),
214 serial_number: hex::encode(isn.serial_number.as_bytes()),
215 }
216 }
217 cms::signed_data::SignerIdentifier::SubjectKeyIdentifier(ski) => {
218 debug!("Signer uses SubjectKeyIdentifier");
219 let ski_hex = hex::encode(ski.0.as_bytes());
221 SignerIdentifier {
222 issuer: format!("SubjectKeyIdentifier: {ski_hex}"),
223 serial_number: ski_hex,
224 }
225 }
226 };
227
228 debug!(
229 "Looking for certificate matching issuer='{}', serial='{}'",
230 identifier.issuer, identifier.serial_number
231 );
232
233 let certificate = certificates
235 .iter()
236 .find(|cert| {
237 let matches =
238 cert.issuer == identifier.issuer && cert.serial_number == identifier.serial_number;
239 if !matches {
240 trace!(
241 "Certificate mismatch: cert.issuer='{}', cert.serial='{}'",
242 cert.issuer, cert.serial_number
243 );
244 }
245 matches
246 })
247 .cloned();
248
249 let public_key = certificate
251 .as_ref()
252 .and_then(|cert| cert.public_key.clone());
253
254 if certificate.is_none() {
255 warn!(
256 "No certificate found for signer: issuer='{}', serial='{}'",
257 identifier.issuer, identifier.serial_number
258 );
259 debug!("Available certificates: {}", certificates.len());
260 } else {
261 debug!("Found certificate for signer");
262 }
263
264 let (has_signed_attributes, signed_attributes_der) = if let Some(signed_attrs) =
266 &signer_info.signed_attrs
267 {
268 debug!("Signer has {} signed attributes", signed_attrs.len());
269
270 let mut attr_bytes = Vec::new();
273
274 let mut encoded_attrs = Vec::new();
277 for attr in signed_attrs.iter() {
278 encoded_attrs.push(
279 attr.to_der()
280 .map_err(|e| Error::Asn1Error(format!("Failed to encode attribute: {e}")))?,
281 );
282 }
283
284 encoded_attrs.sort();
286
287 attr_bytes.push(0x31); let content_len: usize = encoded_attrs.iter().map(std::vec::Vec::len).sum();
292 if content_len < 128 {
293 #[allow(clippy::cast_possible_truncation)]
294 {
295 attr_bytes.push(content_len as u8);
296 }
297 } else {
298 let len_bytes = content_len.to_be_bytes();
300 let len_bytes = &len_bytes[len_bytes.iter().position(|&b| b != 0).unwrap_or(0)..];
301 #[allow(clippy::cast_possible_truncation)]
302 {
303 attr_bytes.push(0x80 | len_bytes.len() as u8);
304 }
305 attr_bytes.extend_from_slice(len_bytes);
306 }
307
308 for attr in encoded_attrs {
310 attr_bytes.extend_from_slice(&attr);
311 }
312
313 (true, Some(attr_bytes))
314 } else {
315 debug!("Signer has no signed attributes - signature is directly over content");
316 (false, None)
317 };
318
319 Ok(SignerDetails {
320 identifier,
321 digest_algorithm: oid_to_algorithm_name(&signer_info.digest_alg.oid),
322 signature_algorithm: oid_to_algorithm_name(&signer_info.signature_algorithm.oid),
323 signature: signer_info.signature.as_bytes().to_vec(),
324 certificate,
325 public_key,
326 has_signed_attributes,
327 signed_attributes_der,
328 })
329}
330
331fn extract_certificate_details(cert: &Certificate) -> Result<CertificateDetails> {
333 let tbs = &cert.tbs_certificate;
334
335 let spki_der = tbs
338 .subject_public_key_info
339 .to_der()
340 .map_err(|e| Error::Asn1Error(format!("Failed to encode SPKI: {e}")))?;
341 let spki_ref = SubjectPublicKeyInfoRef::from_der(&spki_der)
342 .map_err(|e| Error::Asn1Error(format!("Failed to parse SPKI: {e}")))?;
343
344 let public_key = extract_public_key_info(&spki_ref);
345
346 Ok(CertificateDetails {
347 subject: tbs.subject.to_string(),
348 issuer: tbs.issuer.to_string(),
349 serial_number: hex::encode(tbs.serial_number.as_bytes()),
350 public_key: Some(public_key),
351 })
352}
353
354fn extract_public_key_info(spki: &SubjectPublicKeyInfoRef<'_>) -> PublicKeyInfo {
356 let algorithm = oid_to_algorithm_name(&spki.algorithm.oid);
357 let key_bytes = spki.subject_public_key.raw_bytes().to_vec();
358
359 let key_size = match algorithm.as_str() {
361 "RSA" => {
362 if let Ok(rsa_key) = RsaPublicKey::from_pkcs1_der(spki.subject_public_key.raw_bytes()) {
364 rsa_key.size() * 8
366 } else {
367 key_bytes.len() * 8
369 }
370 }
371 _ => key_bytes.len() * 8,
372 };
373
374 PublicKeyInfo {
375 algorithm,
376 key_size,
377 key_bytes,
378 }
379}
380
381fn oid_to_algorithm_name(oid: &der::asn1::ObjectIdentifier) -> String {
383 match oid.to_string().as_str() {
384 "2.16.840.1.101.3.4.2.1" => "SHA-256".to_string(),
386 "2.16.840.1.101.3.4.2.2" => "SHA-384".to_string(),
387 "2.16.840.1.101.3.4.2.3" => "SHA-512".to_string(),
388 "1.3.14.3.2.26" => "SHA-1".to_string(),
389
390 "1.2.840.113549.1.1.11" => "RSA with SHA-256".to_string(),
392 "1.2.840.113549.1.1.12" => "RSA with SHA-384".to_string(),
393 "1.2.840.113549.1.1.13" => "RSA with SHA-512".to_string(),
394 "1.2.840.113549.1.1.5" => "RSA with SHA-1".to_string(),
395 "1.2.840.113549.1.1.1" => "RSA".to_string(),
396
397 "1.2.840.10045.4.3.2" => "ECDSA with SHA-256".to_string(),
399 "1.2.840.10045.4.3.3" => "ECDSA with SHA-384".to_string(),
400 "1.2.840.10045.4.3.4" => "ECDSA with SHA-512".to_string(),
401
402 _ => format!("OID: {oid}"),
403 }
404}
405
406pub fn verify_with_public_key(
414 public_key: &PublicKeyInfo,
415 signed_data: &[u8],
416 signature: &[u8],
417 digest_algorithm: &str,
418) -> Result<bool> {
419 match public_key.algorithm.as_str() {
420 algo if algo == "RSA" || algo.starts_with("RSA with") => {
421 verify_rsa_signature(public_key, signed_data, signature, digest_algorithm)
422 }
423 _ => Err(Error::Asn1Error(format!(
424 "Unsupported algorithm for verification: {}",
425 public_key.algorithm
426 ))),
427 }
428}
429
430fn verify_rsa_signature(
432 public_key: &PublicKeyInfo,
433 signed_data: &[u8],
434 signature: &[u8],
435 digest_algorithm: &str,
436) -> Result<bool> {
437 let rsa_key = if let Ok(key) = RsaPublicKey::from_pkcs1_der(&public_key.key_bytes) {
440 key
441 } else {
442 let spki = x509_cert::spki::SubjectPublicKeyInfoOwned::from_der(&public_key.key_bytes)
444 .map_err(|e| Error::Asn1Error(format!("Failed to parse SubjectPublicKeyInfo: {e}")))?;
445
446 RsaPublicKey::from_pkcs1_der(spki.subject_public_key.raw_bytes())
447 .map_err(|e| Error::Asn1Error(format!("Failed to decode RSA key from SPKI: {e}")))?
448 };
449
450 let result = match digest_algorithm {
452 "SHA-256" => {
453 let verifying_key = rsa::pkcs1v15::VerifyingKey::<Sha256>::new(rsa_key);
454 let signature_obj = rsa::pkcs1v15::Signature::try_from(signature)
455 .map_err(|e| Error::Asn1Error(format!("Invalid signature format: {e}")))?;
456 verifying_key.verify(signed_data, &signature_obj).is_ok()
457 }
458 "SHA-384" => {
459 let verifying_key = rsa::pkcs1v15::VerifyingKey::<Sha384>::new(rsa_key);
460 let signature_obj = rsa::pkcs1v15::Signature::try_from(signature)
461 .map_err(|e| Error::Asn1Error(format!("Invalid signature format: {e}")))?;
462 verifying_key.verify(signed_data, &signature_obj).is_ok()
463 }
464 "SHA-512" => {
465 let verifying_key = rsa::pkcs1v15::VerifyingKey::<Sha512>::new(rsa_key);
466 let signature_obj = rsa::pkcs1v15::Signature::try_from(signature)
467 .map_err(|e| Error::Asn1Error(format!("Invalid signature format: {e}")))?;
468 verifying_key.verify(signed_data, &signature_obj).is_ok()
469 }
470 _ => {
471 return Err(Error::Asn1Error(format!(
472 "Unsupported digest algorithm: {digest_algorithm}"
473 )));
474 }
475 };
476
477 debug!(
478 "RSA signature verification with {}: {}",
479 digest_algorithm,
480 if result { "SUCCESS" } else { "FAILED" }
481 );
482
483 Ok(result)
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn test_oid_to_algorithm_name() {
492 use der::asn1::ObjectIdentifier;
493
494 let sha256_oid = ObjectIdentifier::new("2.16.840.1.101.3.4.2.1").unwrap();
495 assert_eq!(oid_to_algorithm_name(&sha256_oid), "SHA-256");
496
497 let rsa_sha256_oid = ObjectIdentifier::new("1.2.840.113549.1.1.11").unwrap();
498 assert_eq!(oid_to_algorithm_name(&rsa_sha256_oid), "RSA with SHA-256");
499 }
500}