Skip to main content

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}