isideload_cryptographic_message_syntax/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5/*! Cryptographic Message Syntax (RFC 5652) in Pure Rust
6
7This crate attempts to implement parts of
8[RFC 5652](https://tools.ietf.org/rfc/rfc5652.txt) in pure, safe Rust.
9
10Functionality includes:
11
12* Partial (de)serialization support for ASN.1 data structures. The
13 Rust structs are all defined. But not everything has (de)serialization
14 code implemented.
15* High-level Rust API for extracting useful attributes from a parsed
16 `SignedData` structure and performing common operations, such as verifying
17 signature integrity.
18
19RFC 5652 is quite old. If you are looking to digitally sign content, you may
20want to look at something newer, such as RPKI (RFC 6488). (RPKI appears to
21be the spiritual success to this specification.)
22
23# IMPORTANT SECURITY LIMITATIONS
24
25**The verification functionality in this crate is purposefully limited
26and isn't sufficient for trusting signed data. You need to include additional
27trust verification if you are using this crate for verifying signed data.**
28
29This crate exposes functionality to verify signatures and content integrity
30of *signed data*. Specifically it can verify that an embedded cryptographic
31signature over some arbitrary/embedded content was issued by a known signing
32certificate. This answers the question *did certificate X sign content Y*.
33This is an important question to answer, but it fails to answer other important
34questions such as:
35
36* Is the signature cryptographically strong or weak? Do I trust the signature?
37* Do I trust the signer?
38
39Answering *do I trust the signer* is an extremely difficult and nuanced
40problem. It entails things like:
41
42* Ensuring the signing certificate is using secure cryptography.
43* Validating that the signing certificate is one you think it was or was
44 issued by a trusted party.
45* Validating the certificate isn't expired or hasn't been revoked.
46* Validating that the certificate contains attributes/extensions desired
47 (e.g. a certificate can be earmarked as used for signing code).
48
49If you are using this crate as part of verifying signed content, you need
50to have answers to these hard questions. This will require writing code
51beyond what is available in this crate. You ideally want to use existing
52libraries for this, as getting this correct is difficult. Ideally you would
53consult a security/cryptography domain expert for help.
54
55# Technical Notes
56
57RFC 5652 is based off PKCS #7 version 1.5 (RFC 2315). So common tools/libraries
58for interacting with PKCS #7 may have success parsing this format. For example,
59you can use OpenSSL to read the data structures:
60
61 $ openssl pkcs7 -inform DER -in <filename> -print
62 $ openssl pkcs7 -inform PEM -in <filename> -print
63 $ openssl asn1parse -inform DER -in <filename>
64
65RFC 5652 uses BER (not DER) for serialization. There were attempts to use
66other, more popular BER/DER/ASN.1 serialization crates. However, we could
67only get `bcder` working. In a similar vein, there are other crates
68implementing support for common ASN.1 functionality, such as serializing
69X.509 certificates. Again, many of these depend on serializers that don't
70seem to be compatible with BER. So we've recursively defined ASN.1 data
71structures referenced by RFC5652 and taught them to serialize using `bcder`.
72*/
73
74pub mod asn1;
75
76#[cfg(feature = "http")]
77mod signing;
78
79#[cfg(feature = "http")]
80pub use signing::{SignedDataBuilder, SignerBuilder};
81
82pub use {bcder::Oid, bytes::Bytes};
83
84use {
85 crate::asn1::{
86 rfc3161::OID_TIME_STAMP_TOKEN,
87 rfc5652::{
88 CertificateChoices, OID_CONTENT_TYPE, OID_MESSAGE_DIGEST, OID_SIGNING_TIME,
89 SignerIdentifier, Time,
90 },
91 },
92 aws_lc_rs::{digest::Digest, signature::UnparsedPublicKey},
93 bcder::{Integer, OctetString},
94 pem::PemError,
95 std::{
96 collections::HashSet,
97 fmt::{Debug, Display, Formatter},
98 ops::Deref,
99 },
100 x509_certificate::{
101 CapturedX509Certificate, DigestAlgorithm, SignatureAlgorithm, X509Certificate,
102 X509CertificateError, certificate::certificate_is_subset_of, rfc3280::Name,
103 },
104};
105
106#[derive(Debug)]
107pub enum CmsError {
108 /// An error occurred decoding ASN.1 data.
109 DecodeErr(bcder::decode::DecodeError<std::convert::Infallible>),
110
111 /// The content-type attribute is missing from the SignedAttributes structure.
112 MissingSignedAttributeContentType,
113
114 /// The content-type attribute in the SignedAttributes structure is malformed.
115 MalformedSignedAttributeContentType,
116
117 /// The message-digest attribute is missed from the SignedAttributes structure.
118 MissingSignedAttributeMessageDigest,
119
120 /// The message-digest attribute is malformed.
121 MalformedSignedAttributeMessageDigest,
122
123 /// The signing-time signed attribute is malformed.
124 MalformedSignedAttributeSigningTime,
125
126 /// The time-stamp token unsigned attribute is malformed.
127 MalformedUnsignedAttributeTimeStampToken,
128
129 /// Subject key identifiers in signer info is not supported.
130 SubjectKeyIdentifierUnsupported,
131
132 /// A general I/O error occurred.
133 Io(std::io::Error),
134
135 /// An unknown signing key algorithm was encountered.
136 UnknownKeyAlgorithm(Oid),
137
138 /// An unknown message digest algorithm was encountered.
139 UnknownDigestAlgorithm(Oid),
140
141 /// An unknown signature algorithm was encountered.
142 UnknownSignatureAlgorithm(Oid),
143
144 /// An unknown certificate format was encountered.
145 UnknownCertificateFormat,
146
147 /// A certificate was not found.
148 CertificateNotFound,
149
150 /// Signature verification fail.
151 SignatureVerificationError,
152
153 /// No `SignedAttributes` were present when they should have been.
154 NoSignedAttributes,
155
156 /// Two content digests were not equivalent.
157 DigestNotEqual,
158
159 /// Error encoding/decoding PEM data.
160 Pem(PemError),
161
162 /// Error occurred when creating a signature.
163 SignatureCreation(signature::Error),
164
165 /// Attempted to use a `Certificate` but we couldn't find the backing data for it.
166 CertificateMissingData,
167
168 /// Error occurred parsing a distinguished name field in a certificate.
169 DistinguishedNameParseError,
170
171 /// Error occurred in the x509-certificate crate.
172 X509Certificate(X509CertificateError),
173}
174
175impl std::error::Error for CmsError {}
176
177impl Display for CmsError {
178 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
179 match self {
180 Self::DecodeErr(e) => std::fmt::Display::fmt(e, f),
181 Self::MissingSignedAttributeContentType => {
182 f.write_str("content-type attribute missing from SignedAttributes")
183 }
184 Self::MalformedSignedAttributeContentType => {
185 f.write_str("content-type attribute in SignedAttributes is malformed")
186 }
187 Self::MissingSignedAttributeMessageDigest => {
188 f.write_str("message-digest attribute missing from SignedAttributes")
189 }
190 Self::MalformedSignedAttributeMessageDigest => {
191 f.write_str("message-digest attribute in SignedAttributes is malformed")
192 }
193 Self::MalformedSignedAttributeSigningTime => {
194 f.write_str("signing-time attribute in SignedAttributes is malformed")
195 }
196 Self::MalformedUnsignedAttributeTimeStampToken => {
197 f.write_str("time-stamp token attribute in UnsignedAttributes is malformed")
198 }
199 Self::SubjectKeyIdentifierUnsupported => {
200 f.write_str("signer info using subject key identifier is not supported")
201 }
202 Self::Io(e) => std::fmt::Display::fmt(e, f),
203 Self::UnknownKeyAlgorithm(oid) => {
204 f.write_fmt(format_args!("unknown signing key algorithm: {}", oid))
205 }
206 Self::UnknownDigestAlgorithm(oid) => {
207 f.write_fmt(format_args!("unknown digest algorithm: {}", oid))
208 }
209 Self::UnknownSignatureAlgorithm(oid) => {
210 f.write_fmt(format_args!("unknown signature algorithm: {}", oid))
211 }
212 Self::UnknownCertificateFormat => f.write_str("unknown certificate format"),
213 Self::CertificateNotFound => f.write_str("certificate not found"),
214 Self::SignatureVerificationError => f.write_str("signature verification failed"),
215 Self::NoSignedAttributes => f.write_str("SignedAttributes structure is missing"),
216 Self::DigestNotEqual => f.write_str("digests not equivalent"),
217 Self::Pem(e) => f.write_fmt(format_args!("PEM error: {}", e)),
218 Self::SignatureCreation(e) => {
219 f.write_fmt(format_args!("error during signature creation: {}", e))
220 }
221 Self::CertificateMissingData => f.write_str("certificate data not available"),
222 Self::DistinguishedNameParseError => {
223 f.write_str("could not parse distinguished name data")
224 }
225 Self::X509Certificate(e) => {
226 f.write_fmt(format_args!("X.509 certificate error: {:?}", e))
227 }
228 }
229 }
230}
231
232impl From<bcder::decode::DecodeError<std::convert::Infallible>> for CmsError {
233 fn from(e: bcder::decode::DecodeError<std::convert::Infallible>) -> Self {
234 Self::DecodeErr(e)
235 }
236}
237
238impl From<std::io::Error> for CmsError {
239 fn from(e: std::io::Error) -> Self {
240 Self::Io(e)
241 }
242}
243
244impl From<PemError> for CmsError {
245 fn from(e: PemError) -> Self {
246 Self::Pem(e)
247 }
248}
249
250impl From<signature::Error> for CmsError {
251 fn from(e: signature::Error) -> Self {
252 Self::SignatureCreation(e)
253 }
254}
255
256impl From<X509CertificateError> for CmsError {
257 fn from(e: X509CertificateError) -> Self {
258 Self::X509Certificate(e)
259 }
260}
261
262/// Represents a CMS SignedData structure.
263///
264/// This is the high-level type representing a CMS signature of some data.
265/// It contains a description of what was signed, the cryptographic signature
266/// of what was signed, and likely the X.509 certificate chain for the
267/// signing key.
268///
269/// This is a high-level data structure that ultimately gets (de)serialized
270/// from/to ASN.1. It exists to facilitate common interactions with the
271/// low-level ASN.1 without exposing the complexity of ASN.1.
272#[derive(Clone)]
273pub struct SignedData {
274 /// Content digest algorithms used.
275 digest_algorithms: HashSet<DigestAlgorithm>,
276
277 /// Content that was signed.
278 ///
279 /// This is optional because signed content can also be articulated
280 /// via signed attributes inside the `SignerInfo` structure.
281 signed_content: Option<Vec<u8>>,
282
283 /// Certificates embedded within the data structure.
284 ///
285 /// While not required, it is common for the SignedData data structure
286 /// to embed the X.509 certificates used to sign the data within. This
287 /// field holds those certificates.
288 ///
289 /// Typically the root CA is first and the actual signing certificate is
290 /// last.
291 certificates: Option<Vec<CapturedX509Certificate>>,
292
293 /// Describes content signatures.
294 signers: Vec<SignerInfo>,
295}
296
297impl Debug for SignedData {
298 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
299 let mut s = f.debug_struct("SignedData");
300 s.field("digest_algorithms", &self.digest_algorithms);
301 s.field(
302 "signed_content",
303 &format_args!("{:?}", self.signed_content.as_ref().map(hex::encode)),
304 );
305 s.field("certificates", &self.certificates);
306 s.field("signers", &self.signers);
307 s.finish()
308 }
309}
310
311impl SignedData {
312 /// Construct an instance by parsing BER data.
313 pub fn parse_ber(data: &[u8]) -> Result<Self, CmsError> {
314 Self::try_from(&crate::asn1::rfc5652::SignedData::decode_ber(data)?)
315 }
316
317 /// Compute the digest of the encapsulated content using a specified algorithm.
318 ///
319 /// The returned value is likely used as the `message-digest` attribute type
320 /// for use within signed attributes.
321 ///
322 /// You can get the raw bytes of the digest by calling its `.as_ref()`.
323 pub fn message_digest_with_algorithm(&self, alg: DigestAlgorithm) -> Digest {
324 let mut hasher = alg.digester();
325
326 if let Some(content) = &self.signed_content {
327 hasher.update(content);
328 }
329
330 hasher.finish()
331 }
332
333 /// Obtain encapsulated content that was signed.
334 ///
335 /// This is the defined `encapContentInfo cContent` value.
336 pub fn signed_content(&self) -> Option<&[u8]> {
337 if let Some(content) = &self.signed_content {
338 Some(content)
339 } else {
340 None
341 }
342 }
343
344 pub fn certificates(&self) -> Box<dyn Iterator<Item = &CapturedX509Certificate> + '_> {
345 match self.certificates.as_ref() {
346 Some(certs) => Box::new(certs.iter()),
347 None => Box::new(std::iter::empty()),
348 }
349 }
350
351 /// Obtain signing information attached to this instance.
352 ///
353 /// Each iterated value represents an entity that cryptographically signed
354 /// the content. Use these objects to validate the signed data.
355 pub fn signers(&self) -> impl Iterator<Item = &SignerInfo> {
356 self.signers.iter()
357 }
358}
359
360impl TryFrom<&crate::asn1::rfc5652::SignedData> for SignedData {
361 type Error = CmsError;
362
363 fn try_from(raw: &crate::asn1::rfc5652::SignedData) -> Result<Self, Self::Error> {
364 let digest_algorithms = raw
365 .digest_algorithms
366 .iter()
367 .map(DigestAlgorithm::try_from)
368 .collect::<Result<HashSet<_>, _>>()?;
369
370 let signed_content = raw
371 .content_info
372 .content
373 .as_ref()
374 .map(|content| content.to_bytes().to_vec());
375
376 let certificates = if let Some(certs) = &raw.certificates {
377 Some(
378 certs
379 .iter()
380 .map(|choice| match choice {
381 CertificateChoices::Certificate(cert) => {
382 // Doing the ASN.1 round-tripping here isn't ideal and may
383 // lead to correctness bugs.
384 let cert = X509Certificate::from(cert.deref().clone());
385 let cert_ber = cert.encode_ber()?;
386
387 Ok(CapturedX509Certificate::from_ber(cert_ber)?)
388 }
389 _ => Err(CmsError::UnknownCertificateFormat),
390 })
391 .collect::<Result<Vec<_>, CmsError>>()?,
392 )
393 } else {
394 None
395 };
396
397 let signers = raw
398 .signer_infos
399 .iter()
400 .map(SignerInfo::try_from)
401 .collect::<Result<Vec<_>, CmsError>>()?;
402
403 Ok(Self {
404 digest_algorithms,
405 signed_content,
406 certificates,
407 signers,
408 })
409 }
410}
411
412/// Represents a CMS SignerInfo structure.
413///
414/// This is a high-level interface to the SignerInfo ASN.1 type. It supports
415/// performing common operations against that type.
416///
417/// Instances of this type are logically equivalent to a single
418/// signed assertion within a `SignedData` payload. There can be multiple
419/// signers per `SignedData`, which is why this type exists on its own.
420#[derive(Clone)]
421pub struct SignerInfo {
422 /// The X.509 certificate issuer.
423 issuer: Name,
424
425 /// The X.509 certificate serial number.
426 serial_number: Integer,
427
428 /// The algorithm used for digesting signed content.
429 digest_algorithm: DigestAlgorithm,
430
431 /// Algorithm used for signing the digest.
432 signature_algorithm: SignatureAlgorithm,
433
434 /// The cryptographic signature.
435 signature: Vec<u8>,
436
437 /// Parsed signed attributes.
438 signed_attributes: Option<SignedAttributes>,
439
440 /// Raw data constituting SignedAttributes that needs to be digested.
441 digested_signed_attributes_data: Option<Vec<u8>>,
442
443 /// Parsed unsigned attributes.
444 unsigned_attributes: Option<UnsignedAttributes>,
445}
446
447impl Debug for SignerInfo {
448 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
449 let mut s = f.debug_struct("SignerInfo");
450 s.field("issuer", &self.issuer);
451 s.field("serial_number", &self.serial_number);
452 s.field("digest_algorithm", &self.digest_algorithm);
453 s.field("signature_algorithm", &self.signature_algorithm);
454 s.field(
455 "signature",
456 &format_args!("{}", hex::encode(&self.signature)),
457 );
458 s.field("signed_attributes", &self.signed_attributes);
459 s.field(
460 "digested_signed_attributes_data",
461 &format_args!(
462 "{:?}",
463 self.digested_signed_attributes_data
464 .as_ref()
465 .map(hex::encode)
466 ),
467 );
468 s.field("unsigned_attributes", &self.unsigned_attributes);
469 s.finish()
470 }
471}
472
473impl SignerInfo {
474 /// Obtain the signing X.509 certificate's issuer name and its serial number.
475 ///
476 /// The returned value can be used to locate the certificate so
477 /// verification can be performed.
478 pub fn certificate_issuer_and_serial(&self) -> Option<(&Name, &Integer)> {
479 Some((&self.issuer, &self.serial_number))
480 }
481
482 /// Obtain the message digest algorithm used by this signer.
483 pub fn digest_algorithm(&self) -> DigestAlgorithm {
484 self.digest_algorithm
485 }
486
487 /// Obtain the cryptographic signing algorithm used by this signer.
488 pub fn signature_algorithm(&self) -> SignatureAlgorithm {
489 self.signature_algorithm
490 }
491
492 /// Obtain the raw bytes constituting the cryptographic signature.
493 ///
494 /// This is the signature that should be verified.
495 pub fn signature(&self) -> &[u8] {
496 &self.signature
497 }
498
499 /// Obtain the `SignedAttributes` attached to this instance.
500 pub fn signed_attributes(&self) -> Option<&SignedAttributes> {
501 self.signed_attributes.as_ref()
502 }
503
504 /// Obtain the `UnsignedAttributes` attached to this instance.
505 pub fn unsigned_attributes(&self) -> Option<&UnsignedAttributes> {
506 self.unsigned_attributes.as_ref()
507 }
508
509 /// Verifies the signature defined by this signer given a [SignedData] instance.
510 ///
511 /// This function will perform cryptographic verification that the signature
512 /// contained within this `SignerInfo` instance is valid for the content that
513 /// was signed. The content that was signed is the encapsulated content from
514 /// the `SignedData` instance (its `.signed_data()` value) combined with
515 /// the `SignedAttributes` attached to this instance.
516 ///
517 /// # IMPORTANT SECURITY LIMITATIONS
518 ///
519 /// This method only performs signature verification. It:
520 ///
521 /// * DOES NOT verify the digest hash embedded within `SignedAttributes` (if present).
522 /// * DOES NOT validate the signing certificate in any way.
523 /// * DOES NOT validate that the cryptography used is appropriate.
524 /// * DOES NOT verify the time stamp token, if present.
525 ///
526 /// See the crate's documentation for more on the security implications.
527 pub fn verify_signature_with_signed_data(
528 &self,
529 signed_data: &SignedData,
530 ) -> Result<(), CmsError> {
531 let signed_content = self.signed_content_with_signed_data(signed_data);
532
533 self.verify_signature_with_signed_data_and_content(signed_data, &signed_content)
534 }
535
536 /// Verifies the signature defined by this signer given a [SignedData] and signed content.
537 ///
538 /// This function will perform cryptographic verification that the signature contained within
539 /// this [SignerInfo] is valid for `signed_content`. Unlike
540 /// [Self::verify_signature_with_signed_data()], the content that was signed is passed in
541 /// explicitly instead of derived from [SignedData].
542 ///
543 /// This is a low-level API that bypasses the normal rules for deriving the raw content a
544 /// cryptographic signature was made over. You probably want to use
545 /// [Self::verify_signature_with_signed_data()] instead. Also note that `signed_content` here
546 /// may or may not be the _encapsulated content_ which is ultimately signed.
547 ///
548 /// This method only performs cryptographic signature verification. It is therefore subject
549 /// to the same limitations as [Self::verify_signature_with_signed_data()].
550 pub fn verify_signature_with_signed_data_and_content(
551 &self,
552 signed_data: &SignedData,
553 signed_content: &[u8],
554 ) -> Result<(), CmsError> {
555 let verifier = self.signature_verifier(signed_data.certificates())?;
556 let signature = self.signature();
557
558 verifier
559 .verify(signed_content, signature)
560 .map_err(|_| CmsError::SignatureVerificationError)
561 }
562
563 /// Verifies the digest stored in signed attributes matches that of content in a `SignedData`.
564 ///
565 /// If signed attributes are present on this instance, they must contain
566 /// a `message-digest` attribute defining the digest of data that was
567 /// signed. The specification says this digested data should come from
568 /// the encapsulated content within `SignedData` (`SignedData.signed_content()`).
569 ///
570 /// Note that some utilities of CMS will not store a computed digest
571 /// in `message-digest` that came from `SignedData` or is using
572 /// the digest algorithm indicated by this `SignerInfo`. This is strictly
573 /// in violation of the specification but it does occur.
574 ///
575 /// # IMPORTANT SECURITY LIMITATIONS
576 ///
577 /// This method only performs message digest verification. It:
578 ///
579 /// * DOES NOT verify the signature over the signed data or anything about
580 /// the signer.
581 /// * DOES NOT validate that the digest algorithm is strong/appropriate.
582 /// * DOES NOT compare the digests in a manner that is immune to timing
583 /// side-channels.
584 ///
585 /// See the crate's documentation for more on the security implications.
586 pub fn verify_message_digest_with_signed_data(
587 &self,
588 signed_data: &SignedData,
589 ) -> Result<(), CmsError> {
590 let signed_attributes = self
591 .signed_attributes()
592 .ok_or(CmsError::NoSignedAttributes)?;
593
594 let wanted_digest: &[u8] = signed_attributes.message_digest.as_ref();
595 let got_digest = self.compute_digest_with_signed_data(signed_data);
596
597 // Susceptible to timing side-channel but we don't care per function
598 // documentation.
599 if wanted_digest == got_digest.as_ref() {
600 Ok(())
601 } else {
602 Err(CmsError::DigestNotEqual)
603 }
604 }
605
606 /// Verifies the message digest stored in signed attributes using explicit encapsulated content.
607 ///
608 /// Typically, the digest is computed over content stored in the [SignedData] instance.
609 /// However, it is possible for the signed content to be external. This function
610 /// allows you to define the source of that external content.
611 ///
612 /// Behavior is very similar to [SignerInfo::verify_message_digest_with_signed_data]
613 /// except the original content that was digested is explicitly passed in. This
614 /// content is appended with the signed attributes data on this [SignerInfo].
615 ///
616 /// The security limitations from [SignerInfo::verify_message_digest_with_signed_data]
617 /// apply to this function as well.
618 pub fn verify_message_digest_with_content(&self, data: &[u8]) -> Result<(), CmsError> {
619 let signed_attributes = self
620 .signed_attributes()
621 .ok_or(CmsError::NoSignedAttributes)?;
622
623 let wanted_digest: &[u8] = signed_attributes.message_digest.as_ref();
624 let got_digest = self.compute_digest(Some(data));
625
626 // Susceptible to timing side-channel but we don't care per function
627 // documentation.
628 if wanted_digest == got_digest.as_ref() {
629 Ok(())
630 } else {
631 Err(CmsError::DigestNotEqual)
632 }
633 }
634
635 /// Obtain an entity for validating the signature described by this instance.
636 ///
637 /// This will attempt to locate the certificate used by this signing info
638 /// structure in the passed iterable of certificates and then construct
639 /// a signature verifier that can be used to verify content integrity.
640 ///
641 /// If the certificate referenced by this signing info could not be found,
642 /// an error occurs.
643 ///
644 /// If the signing key's algorithm or signature algorithm aren't supported,
645 /// an error occurs.
646 pub fn signature_verifier<'a, C>(
647 &self,
648 mut certs: C,
649 ) -> Result<UnparsedPublicKey<Vec<u8>>, CmsError>
650 where
651 C: Iterator<Item = &'a CapturedX509Certificate>,
652 {
653 // The issuer of this signature is matched against the list of certificates.
654 let signing_cert = certs
655 .find(|cert| {
656 // We're only verifying signatures here, not validating the certificate.
657 // So even if the certificate comparison functionality is incorrect
658 // (the called function does non-exact matching of the RdnSequence in
659 // case the candidate certs have extra fields), that shouldn't have
660 // security implications.
661 certificate_is_subset_of(
662 &self.serial_number,
663 &self.issuer,
664 cert.serial_number_asn1(),
665 cert.issuer_name(),
666 )
667 })
668 .ok_or(CmsError::CertificateNotFound)?;
669
670 let key_algorithm = signing_cert.key_algorithm().ok_or_else(|| {
671 CmsError::UnknownKeyAlgorithm(signing_cert.key_algorithm_oid().clone())
672 })?;
673
674 let verification_algorithm = self
675 .signature_algorithm
676 .resolve_verification_algorithm(key_algorithm)?;
677
678 let public_key = UnparsedPublicKey::new(
679 verification_algorithm,
680 signing_cert.public_key_data().to_vec(),
681 );
682
683 Ok(public_key)
684 }
685
686 /// Obtain the raw bytes of content that was signed given a `SignedData`.
687 ///
688 /// This joins the encapsulated content from `SignedData` with `SignedAttributes`
689 /// on this instance to produce a new blob. This new blob is the message
690 /// that is signed and whose signature is embedded in `SignerInfo` instances.
691 pub fn signed_content_with_signed_data(&self, signed_data: &SignedData) -> Vec<u8> {
692 self.signed_content(signed_data.signed_content())
693 }
694
695 /// Obtain the raw bytes of content that were digested and signed.
696 ///
697 /// The returned value is the message that was signed and whose signature
698 /// of which needs to be verified.
699 ///
700 /// The optional content argument is the `encapContentInfo eContent`
701 /// field, typically the value of `SignedData.signed_content()`.
702 pub fn signed_content(&self, content: Option<&[u8]>) -> Vec<u8> {
703 // Per RFC 5652 Section 5.4:
704 //
705 // The result of the message digest calculation process depends on
706 // whether the signedAttrs field is present. When the field is absent,
707 // the result is just the message digest of the content as described
708 // above. When the field is present, however, the result is the message
709 // digest of the complete DER encoding of the SignedAttrs value
710 // contained in the signedAttrs field. Since the SignedAttrs value,
711 // when present, must contain the content-type and the message-digest
712 // attributes, those values are indirectly included in the result. The
713 // content-type attribute MUST NOT be included in a countersignature
714 // unsigned attribute as defined in Section 11.4. A separate encoding
715 // of the signedAttrs field is performed for message digest calculation.
716 // The IMPLICIT [0] tag in the signedAttrs is not used for the DER
717 // encoding, rather an EXPLICIT SET OF tag is used. That is, the DER
718 // encoding of the EXPLICIT SET OF tag, rather than of the IMPLICIT [0]
719 // tag, MUST be included in the message digest calculation along with
720 // the length and content octets of the SignedAttributes value.
721
722 if let Some(signed_attributes_data) = &self.digested_signed_attributes_data {
723 signed_attributes_data.clone()
724 } else if let Some(content) = content {
725 content.to_vec()
726 } else {
727 vec![]
728 }
729 }
730
731 /// Obtain the raw bytes constituting `SignerInfo.signedAttrs` as encoded for signatures.
732 ///
733 /// Cryptographic signatures in the `SignerInfo` ASN.1 type are made from the digest
734 /// of the `EXPLICIT SET OF` DER encoding of `SignerInfo.signedAttrs`, if signed
735 /// attributes are present. This function resolves the raw bytes that are used
736 /// for digest computation and later signing.
737 ///
738 /// This should always be `Some` if the instance was constructed from an ASN.1
739 /// value that had signed attributes.
740 pub fn signed_attributes_data(&self) -> Option<&[u8]> {
741 self.digested_signed_attributes_data
742 .as_ref()
743 .map(|x| x.as_ref())
744 }
745
746 /// Compute a message digest using a `SignedData` instance.
747 ///
748 /// This will obtain the encapsulated content blob from a `SignedData`
749 /// and digest it using the algorithm configured on this instance.
750 ///
751 /// The resulting digest is typically stored in the `message-digest`
752 /// attribute of `SignedData`.
753 pub fn compute_digest_with_signed_data(&self, signed_data: &SignedData) -> Digest {
754 self.compute_digest(signed_data.signed_content())
755 }
756
757 /// Compute a message digest using the configured algorithm.
758 ///
759 /// This method calls into `compute_digest_with_algorithm()` using the
760 /// digest algorithm stored in this instance.
761 pub fn compute_digest(&self, content: Option<&[u8]>) -> Digest {
762 self.compute_digest_with_algorithm(content, self.digest_algorithm)
763 }
764
765 /// Compute a message digest using an explicit digest algorithm.
766 ///
767 /// This will compute the hash/digest of the passed in content.
768 pub fn compute_digest_with_algorithm(
769 &self,
770 content: Option<&[u8]>,
771 alg: DigestAlgorithm,
772 ) -> Digest {
773 let mut hasher = alg.digester();
774
775 if let Some(content) = content {
776 hasher.update(content);
777 }
778
779 hasher.finish()
780 }
781}
782
783impl TryFrom<&crate::asn1::rfc5652::SignerInfo> for SignerInfo {
784 type Error = CmsError;
785
786 fn try_from(signer_info: &crate::asn1::rfc5652::SignerInfo) -> Result<Self, Self::Error> {
787 let (issuer, serial_number) = match &signer_info.sid {
788 SignerIdentifier::IssuerAndSerialNumber(issuer) => {
789 (issuer.issuer.clone(), issuer.serial_number.clone())
790 }
791 SignerIdentifier::SubjectKeyIdentifier(_) => {
792 return Err(CmsError::SubjectKeyIdentifierUnsupported);
793 }
794 };
795
796 let digest_algorithm = DigestAlgorithm::try_from(&signer_info.digest_algorithm)?;
797
798 // The "signature" algorithm can also be a key algorithm identifier. So we
799 // attempt to resolve using the more robust mechanism.
800 let signature_algorithm = SignatureAlgorithm::from_oid_and_digest_algorithm(
801 &signer_info.signature_algorithm.algorithm,
802 digest_algorithm,
803 )?;
804
805 let signature = signer_info.signature.to_bytes().to_vec();
806
807 let signed_attributes = if let Some(attributes) = &signer_info.signed_attributes {
808 // Content type attribute MUST be present.
809 let content_type = attributes
810 .iter()
811 .find(|attr| attr.typ == OID_CONTENT_TYPE)
812 .ok_or(CmsError::MissingSignedAttributeContentType)?;
813
814 // Content type attribute MUST have exactly 1 value.
815 if content_type.values.len() != 1 {
816 return Err(CmsError::MalformedSignedAttributeContentType);
817 }
818
819 let content_type = content_type
820 .values
821 .first()
822 .unwrap()
823 .deref()
824 .clone()
825 .decode(Oid::take_from)
826 .map_err(|_| CmsError::MalformedSignedAttributeContentType)?;
827
828 // Message digest attribute MUST be present.
829 let message_digest = attributes
830 .iter()
831 .find(|attr| attr.typ == OID_MESSAGE_DIGEST)
832 .ok_or(CmsError::MissingSignedAttributeMessageDigest)?;
833
834 // Message digest attribute MUST have exactly 1 value.
835 if message_digest.values.len() != 1 {
836 return Err(CmsError::MalformedSignedAttributeMessageDigest);
837 }
838
839 let message_digest = message_digest
840 .values
841 .first()
842 .unwrap()
843 .deref()
844 .clone()
845 .decode(OctetString::take_from)
846 .map_err(|_| CmsError::MalformedSignedAttributeMessageDigest)?
847 .to_bytes()
848 .to_vec();
849
850 // Signing time is optional, but common. So we pull it out for convenience.
851 let signing_time = attributes
852 .iter()
853 .find(|attr| attr.typ == OID_SIGNING_TIME)
854 .map(|attr| {
855 if attr.values.len() != 1 {
856 Err(CmsError::MalformedSignedAttributeSigningTime)
857 } else {
858 let time = attr
859 .values
860 .first()
861 .unwrap()
862 .deref()
863 .clone()
864 .decode(Time::take_from)?;
865
866 let time = chrono::DateTime::from(time);
867
868 Ok(time)
869 }
870 })
871 .transpose()?;
872
873 Some(SignedAttributes {
874 content_type,
875 message_digest,
876 signing_time,
877 raw: attributes.clone(),
878 })
879 } else {
880 None
881 };
882
883 let digested_signed_attributes_data = signer_info.signed_attributes_digested_content()?;
884
885 let unsigned_attributes = if let Some(attributes) = &signer_info.unsigned_attributes {
886 let time_stamp_token = attributes
887 .iter()
888 .find(|attr| attr.typ == OID_TIME_STAMP_TOKEN)
889 .map(|attr| {
890 if attr.values.len() != 1 {
891 Err(CmsError::MalformedUnsignedAttributeTimeStampToken)
892 } else {
893 Ok(attr
894 .values
895 .first()
896 .unwrap()
897 .deref()
898 .clone()
899 .decode(crate::asn1::rfc5652::SignedData::decode)?)
900 }
901 })
902 .transpose()?;
903
904 Some(UnsignedAttributes { time_stamp_token })
905 } else {
906 None
907 };
908
909 Ok(SignerInfo {
910 issuer,
911 serial_number,
912 digest_algorithm,
913 signature_algorithm,
914 signature,
915 signed_attributes,
916 digested_signed_attributes_data,
917 unsigned_attributes,
918 })
919 }
920}
921
922/// Represents the contents of a CMS SignedAttributes structure.
923///
924/// This is a high-level interface to the SignedAttributes ASN.1 type.
925#[derive(Clone)]
926pub struct SignedAttributes {
927 /// The content type of the value being signed.
928 ///
929 /// This is often `OID_ID_DATA`.
930 content_type: Oid,
931
932 /// Holds the digest of the content that was signed.
933 message_digest: Vec<u8>,
934
935 /// The time the signature was created.
936 signing_time: Option<chrono::DateTime<chrono::Utc>>,
937
938 /// The raw ASN.1 signed attributes.
939 raw: crate::asn1::rfc5652::SignedAttributes,
940}
941
942impl SignedAttributes {
943 pub fn content_type(&self) -> &Oid {
944 &self.content_type
945 }
946
947 pub fn message_digest(&self) -> &[u8] {
948 &self.message_digest
949 }
950
951 pub fn signing_time(&self) -> Option<&chrono::DateTime<chrono::Utc>> {
952 self.signing_time.as_ref()
953 }
954
955 pub fn attributes(&self) -> &crate::asn1::rfc5652::SignedAttributes {
956 &self.raw
957 }
958}
959
960impl Debug for SignedAttributes {
961 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
962 let mut s = f.debug_struct("SignedAttributes");
963 s.field("content_type", &format_args!("{}", self.content_type));
964 s.field(
965 "message_digest",
966 &format_args!("{}", hex::encode(&self.message_digest)),
967 );
968 s.field("signing_time", &self.signing_time);
969 s.finish()
970 }
971}
972
973#[derive(Clone, Debug)]
974pub struct UnsignedAttributes {
975 /// Time-Stamp Token from a Time-Stamp Protocol server.
976 time_stamp_token: Option<crate::asn1::rfc5652::SignedData>,
977}
978
979#[cfg(test)]
980mod tests {
981 use {
982 super::*,
983 bcder::{Mode, encode::Values},
984 };
985
986 // This signature was extracted from the Firefox.app/Contents/MacOS/firefox
987 // Mach-O executable on a aarch64 machine.
988 const FIREFOX_SIGNATURE: &[u8] = include_bytes!("testdata/firefox.ber");
989
990 const FIREFOX_CODE_DIRECTORY: &[u8] = include_bytes!("testdata/firefox-code-directory");
991
992 #[test]
993 fn parse_firefox() {
994 let raw = crate::asn1::rfc5652::SignedData::decode_ber(FIREFOX_SIGNATURE).unwrap();
995
996 // Try to round trip it.
997 let mut buffer = Vec::new();
998 raw.encode_ref()
999 .write_encoded(Mode::Ber, &mut buffer)
1000 .unwrap();
1001
1002 // The bytes aren't identical because we use definite length encoding, so we can't
1003 // compare that. But we can compare the parsed objects for equivalence.
1004
1005 let raw2 = crate::asn1::rfc5652::SignedData::decode_ber(&buffer).unwrap();
1006 assert_eq!(raw, raw2, "BER round tripping is identical");
1007 }
1008
1009 #[test]
1010 fn verify_firefox() {
1011 let signed_data = SignedData::parse_ber(FIREFOX_SIGNATURE).unwrap();
1012
1013 for signer in signed_data.signers.iter() {
1014 signer
1015 .verify_signature_with_signed_data(&signed_data)
1016 .unwrap();
1017
1018 // The message-digest does NOT match the encapsulated data in Apple code
1019 // signature's use of CMS. So digest verification will fail.
1020 signer
1021 .verify_message_digest_with_signed_data(&signed_data)
1022 .unwrap_err();
1023
1024 // But we know what that value is. So plug it in to verify.
1025 signer
1026 .verify_message_digest_with_content(FIREFOX_CODE_DIRECTORY)
1027 .unwrap();
1028
1029 // Now verify the time-stamp token embedded as an unsigned attribute.
1030 let tst_signed_data = signer.time_stamp_token_signed_data().unwrap().unwrap();
1031
1032 for signer in tst_signed_data.signers() {
1033 signer
1034 .verify_message_digest_with_signed_data(&tst_signed_data)
1035 .unwrap();
1036 signer
1037 .verify_signature_with_signed_data(&tst_signed_data)
1038 .unwrap();
1039 }
1040 }
1041 }
1042
1043 #[test]
1044 fn parse_no_certificate_version() {
1045 let signed = SignedData::parse_ber(include_bytes!("testdata/no-cert-version.ber")).unwrap();
1046
1047 let cert_orig = signed.certificates().collect::<Vec<_>>()[0].clone();
1048 let cert = CapturedX509Certificate::from_der(cert_orig.encode_ber().unwrap()).unwrap();
1049
1050 assert_eq!(
1051 hex::encode(cert.sha256_fingerprint().unwrap()),
1052 "b7c2eefd8dac7806af67dfcd92eb18126bc08312a7f2d6f3862e46013c7a6135"
1053 );
1054 }
1055
1056 const IZZYSOFT_SIGNED_DATA: &[u8] = include_bytes!("testdata/izzysoft-signeddata");
1057 const IZZYSOFT_DATA: &[u8] = include_bytes!("testdata/izzysoft-data");
1058
1059 #[test]
1060 fn verify_izzysoft() {
1061 let signed = SignedData::parse_ber(IZZYSOFT_SIGNED_DATA).unwrap();
1062 let cert = signed.certificates().next().unwrap();
1063
1064 for signer in signed.signers() {
1065 // The signed data is external. So this method will fail since it isn't looking at
1066 // the correct source data.
1067 assert!(matches!(
1068 signer.verify_signature_with_signed_data(&signed),
1069 Err(CmsError::SignatureVerificationError)
1070 ));
1071
1072 // There are no signed attributes. So this should error for that reason.
1073 assert!(matches!(
1074 signer.verify_message_digest_with_signed_data(&signed),
1075 Err(CmsError::NoSignedAttributes)
1076 ));
1077
1078 assert!(matches!(
1079 signer.verify_message_digest_with_signed_data(&signed),
1080 Err(CmsError::NoSignedAttributes)
1081 ));
1082
1083 // The certificate advertises SHA-256 for digests but the signature was made with
1084 // SHA-1. So the default algorithm choice will fail.
1085 assert!(matches!(
1086 cert.verify_signed_data(IZZYSOFT_DATA, signer.signature()),
1087 Err(X509CertificateError::CertificateSignatureVerificationFailed)
1088 ));
1089
1090 // But it verifies when SHA-1 digests are forced!
1091 cert.verify_signed_data_with_algorithm(
1092 IZZYSOFT_DATA,
1093 signer.signature(),
1094 &aws_lc_rs::signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
1095 )
1096 .unwrap();
1097
1098 signer
1099 .verify_signature_with_signed_data_and_content(&signed, IZZYSOFT_DATA)
1100 .unwrap();
1101
1102 let verifier = signer.signature_verifier(signed.certificates()).unwrap();
1103 verifier.verify(IZZYSOFT_DATA, signer.signature()).unwrap();
1104 }
1105 }
1106}