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    bcder::{Integer, OctetString},
93    pem::PemError,
94    ring::{digest::Digest, signature::UnparsedPublicKey},
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    /// Resolve the time-stamp token [SignedData] for this signer.
687    ///
688    /// The time-stamp token is a SignedData ASN.1 structure embedded as an unsigned
689    /// attribute. This is a convenience method to extract it and turn it into
690    /// a [SignedData].
691    ///
692    /// Returns `Ok(Some)` on success, `Ok(None)` if there is no time-stamp token,
693    /// and `Err` if there is a parsing error.
694    pub fn time_stamp_token_signed_data(&self) -> Result<Option<SignedData>, CmsError> {
695        if let Some(attrs) = self.unsigned_attributes() {
696            if let Some(signed_data) = &attrs.time_stamp_token {
697                Ok(Some(SignedData::try_from(signed_data)?))
698            } else {
699                Ok(None)
700            }
701        } else {
702            Ok(None)
703        }
704    }
705
706    /// Verify the time-stamp token in this instance.
707    ///
708    /// The time-stamp token is a SignedData ASN.1 structure embedded as an unsigned
709    /// attribute. So this method reconstructs that data structure and effectively
710    /// calls [SignerInfo::verify_signature_with_signed_data] and
711    /// [SignerInfo::verify_message_digest_with_signed_data].
712    ///
713    /// Returns `Ok(None)` if there is no time-stamp token and `Ok(Some(()))` if
714    /// there is and the token validates. `Err` occurs on any parse or verification
715    /// error.
716    pub fn verify_time_stamp_token(&self) -> Result<Option<()>, CmsError> {
717        let signed_data = match self.time_stamp_token_signed_data()? {
718            Some(v) => v,
719            _ => {
720                return Ok(None);
721            }
722        };
723
724        if signed_data.signers.is_empty() {
725            return Ok(None);
726        }
727
728        for signer in signed_data.signers() {
729            signer.verify_signature_with_signed_data(&signed_data)?;
730            signer.verify_message_digest_with_signed_data(&signed_data)?;
731        }
732
733        Ok(Some(()))
734    }
735
736    /// Obtain the raw bytes of content that was signed given a `SignedData`.
737    ///
738    /// This joins the encapsulated content from `SignedData` with `SignedAttributes`
739    /// on this instance to produce a new blob. This new blob is the message
740    /// that is signed and whose signature is embedded in `SignerInfo` instances.
741    pub fn signed_content_with_signed_data(&self, signed_data: &SignedData) -> Vec<u8> {
742        self.signed_content(signed_data.signed_content())
743    }
744
745    /// Obtain the raw bytes of content that were digested and signed.
746    ///
747    /// The returned value is the message that was signed and whose signature
748    /// of which needs to be verified.
749    ///
750    /// The optional content argument is the `encapContentInfo eContent`
751    /// field, typically the value of `SignedData.signed_content()`.
752    pub fn signed_content(&self, content: Option<&[u8]>) -> Vec<u8> {
753        // Per RFC 5652 Section 5.4:
754        //
755        //    The result of the message digest calculation process depends on
756        //    whether the signedAttrs field is present.  When the field is absent,
757        //    the result is just the message digest of the content as described
758        //    above.  When the field is present, however, the result is the message
759        //    digest of the complete DER encoding of the SignedAttrs value
760        //    contained in the signedAttrs field.  Since the SignedAttrs value,
761        //    when present, must contain the content-type and the message-digest
762        //    attributes, those values are indirectly included in the result.  The
763        //    content-type attribute MUST NOT be included in a countersignature
764        //    unsigned attribute as defined in Section 11.4.  A separate encoding
765        //    of the signedAttrs field is performed for message digest calculation.
766        //    The IMPLICIT [0] tag in the signedAttrs is not used for the DER
767        //    encoding, rather an EXPLICIT SET OF tag is used.  That is, the DER
768        //    encoding of the EXPLICIT SET OF tag, rather than of the IMPLICIT [0]
769        //    tag, MUST be included in the message digest calculation along with
770        //    the length and content octets of the SignedAttributes value.
771
772        if let Some(signed_attributes_data) = &self.digested_signed_attributes_data {
773            signed_attributes_data.clone()
774        } else if let Some(content) = content {
775            content.to_vec()
776        } else {
777            vec![]
778        }
779    }
780
781    /// Obtain the raw bytes constituting `SignerInfo.signedAttrs` as encoded for signatures.
782    ///
783    /// Cryptographic signatures in the `SignerInfo` ASN.1 type are made from the digest
784    /// of the `EXPLICIT SET OF` DER encoding of `SignerInfo.signedAttrs`, if signed
785    /// attributes are present. This function resolves the raw bytes that are used
786    /// for digest computation and later signing.
787    ///
788    /// This should always be `Some` if the instance was constructed from an ASN.1
789    /// value that had signed attributes.
790    pub fn signed_attributes_data(&self) -> Option<&[u8]> {
791        self.digested_signed_attributes_data
792            .as_ref()
793            .map(|x| x.as_ref())
794    }
795
796    /// Compute a message digest using a `SignedData` instance.
797    ///
798    /// This will obtain the encapsulated content blob from a `SignedData`
799    /// and digest it using the algorithm configured on this instance.
800    ///
801    /// The resulting digest is typically stored in the `message-digest`
802    /// attribute of `SignedData`.
803    pub fn compute_digest_with_signed_data(&self, signed_data: &SignedData) -> Digest {
804        self.compute_digest(signed_data.signed_content())
805    }
806
807    /// Compute a message digest using the configured algorithm.
808    ///
809    /// This method calls into `compute_digest_with_algorithm()` using the
810    /// digest algorithm stored in this instance.
811    pub fn compute_digest(&self, content: Option<&[u8]>) -> Digest {
812        self.compute_digest_with_algorithm(content, self.digest_algorithm)
813    }
814
815    /// Compute a message digest using an explicit digest algorithm.
816    ///
817    /// This will compute the hash/digest of the passed in content.
818    pub fn compute_digest_with_algorithm(
819        &self,
820        content: Option<&[u8]>,
821        alg: DigestAlgorithm,
822    ) -> Digest {
823        let mut hasher = alg.digester();
824
825        if let Some(content) = content {
826            hasher.update(content);
827        }
828
829        hasher.finish()
830    }
831}
832
833impl TryFrom<&crate::asn1::rfc5652::SignerInfo> for SignerInfo {
834    type Error = CmsError;
835
836    fn try_from(signer_info: &crate::asn1::rfc5652::SignerInfo) -> Result<Self, Self::Error> {
837        let (issuer, serial_number) = match &signer_info.sid {
838            SignerIdentifier::IssuerAndSerialNumber(issuer) => {
839                (issuer.issuer.clone(), issuer.serial_number.clone())
840            }
841            SignerIdentifier::SubjectKeyIdentifier(_) => {
842                return Err(CmsError::SubjectKeyIdentifierUnsupported);
843            }
844        };
845
846        let digest_algorithm = DigestAlgorithm::try_from(&signer_info.digest_algorithm)?;
847
848        // The "signature" algorithm can also be a key algorithm identifier. So we
849        // attempt to resolve using the more robust mechanism.
850        let signature_algorithm = SignatureAlgorithm::from_oid_and_digest_algorithm(
851            &signer_info.signature_algorithm.algorithm,
852            digest_algorithm,
853        )?;
854
855        let signature = signer_info.signature.to_bytes().to_vec();
856
857        let signed_attributes = if let Some(attributes) = &signer_info.signed_attributes {
858            // Content type attribute MUST be present.
859            let content_type = attributes
860                .iter()
861                .find(|attr| attr.typ == OID_CONTENT_TYPE)
862                .ok_or(CmsError::MissingSignedAttributeContentType)?;
863
864            // Content type attribute MUST have exactly 1 value.
865            if content_type.values.len() != 1 {
866                return Err(CmsError::MalformedSignedAttributeContentType);
867            }
868
869            let content_type = content_type
870                .values
871                .first()
872                .unwrap()
873                .deref()
874                .clone()
875                .decode(Oid::take_from)
876                .map_err(|_| CmsError::MalformedSignedAttributeContentType)?;
877
878            // Message digest attribute MUST be present.
879            let message_digest = attributes
880                .iter()
881                .find(|attr| attr.typ == OID_MESSAGE_DIGEST)
882                .ok_or(CmsError::MissingSignedAttributeMessageDigest)?;
883
884            // Message digest attribute MUST have exactly 1 value.
885            if message_digest.values.len() != 1 {
886                return Err(CmsError::MalformedSignedAttributeMessageDigest);
887            }
888
889            let message_digest = message_digest
890                .values
891                .first()
892                .unwrap()
893                .deref()
894                .clone()
895                .decode(OctetString::take_from)
896                .map_err(|_| CmsError::MalformedSignedAttributeMessageDigest)?
897                .to_bytes()
898                .to_vec();
899
900            // Signing time is optional, but common. So we pull it out for convenience.
901            let signing_time = attributes
902                .iter()
903                .find(|attr| attr.typ == OID_SIGNING_TIME)
904                .map(|attr| {
905                    if attr.values.len() != 1 {
906                        Err(CmsError::MalformedSignedAttributeSigningTime)
907                    } else {
908                        let time = attr
909                            .values
910                            .first()
911                            .unwrap()
912                            .deref()
913                            .clone()
914                            .decode(Time::take_from)?;
915
916                        let time = chrono::DateTime::from(time);
917
918                        Ok(time)
919                    }
920                })
921                .transpose()?;
922
923            Some(SignedAttributes {
924                content_type,
925                message_digest,
926                signing_time,
927                raw: attributes.clone(),
928            })
929        } else {
930            None
931        };
932
933        let digested_signed_attributes_data = signer_info.signed_attributes_digested_content()?;
934
935        let unsigned_attributes = if let Some(attributes) = &signer_info.unsigned_attributes {
936            let time_stamp_token = attributes
937                .iter()
938                .find(|attr| attr.typ == OID_TIME_STAMP_TOKEN)
939                .map(|attr| {
940                    if attr.values.len() != 1 {
941                        Err(CmsError::MalformedUnsignedAttributeTimeStampToken)
942                    } else {
943                        Ok(attr
944                            .values
945                            .first()
946                            .unwrap()
947                            .deref()
948                            .clone()
949                            .decode(crate::asn1::rfc5652::SignedData::decode)?)
950                    }
951                })
952                .transpose()?;
953
954            Some(UnsignedAttributes { time_stamp_token })
955        } else {
956            None
957        };
958
959        Ok(SignerInfo {
960            issuer,
961            serial_number,
962            digest_algorithm,
963            signature_algorithm,
964            signature,
965            signed_attributes,
966            digested_signed_attributes_data,
967            unsigned_attributes,
968        })
969    }
970}
971
972/// Represents the contents of a CMS SignedAttributes structure.
973///
974/// This is a high-level interface to the SignedAttributes ASN.1 type.
975#[derive(Clone)]
976pub struct SignedAttributes {
977    /// The content type of the value being signed.
978    ///
979    /// This is often `OID_ID_DATA`.
980    content_type: Oid,
981
982    /// Holds the digest of the content that was signed.
983    message_digest: Vec<u8>,
984
985    /// The time the signature was created.
986    signing_time: Option<chrono::DateTime<chrono::Utc>>,
987
988    /// The raw ASN.1 signed attributes.
989    raw: crate::asn1::rfc5652::SignedAttributes,
990}
991
992impl SignedAttributes {
993    pub fn content_type(&self) -> &Oid {
994        &self.content_type
995    }
996
997    pub fn message_digest(&self) -> &[u8] {
998        &self.message_digest
999    }
1000
1001    pub fn signing_time(&self) -> Option<&chrono::DateTime<chrono::Utc>> {
1002        self.signing_time.as_ref()
1003    }
1004
1005    pub fn attributes(&self) -> &crate::asn1::rfc5652::SignedAttributes {
1006        &self.raw
1007    }
1008}
1009
1010impl Debug for SignedAttributes {
1011    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1012        let mut s = f.debug_struct("SignedAttributes");
1013        s.field("content_type", &format_args!("{}", self.content_type));
1014        s.field(
1015            "message_digest",
1016            &format_args!("{}", hex::encode(&self.message_digest)),
1017        );
1018        s.field("signing_time", &self.signing_time);
1019        s.finish()
1020    }
1021}
1022
1023#[derive(Clone, Debug)]
1024pub struct UnsignedAttributes {
1025    /// Time-Stamp Token from a Time-Stamp Protocol server.
1026    time_stamp_token: Option<crate::asn1::rfc5652::SignedData>,
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031    use {
1032        super::*,
1033        bcder::{Mode, encode::Values},
1034    };
1035
1036    // This signature was extracted from the Firefox.app/Contents/MacOS/firefox
1037    // Mach-O executable on a aarch64 machine.
1038    const FIREFOX_SIGNATURE: &[u8] = include_bytes!("testdata/firefox.ber");
1039
1040    const FIREFOX_CODE_DIRECTORY: &[u8] = include_bytes!("testdata/firefox-code-directory");
1041
1042    #[test]
1043    fn parse_firefox() {
1044        let raw = crate::asn1::rfc5652::SignedData::decode_ber(FIREFOX_SIGNATURE).unwrap();
1045
1046        // Try to round trip it.
1047        let mut buffer = Vec::new();
1048        raw.encode_ref()
1049            .write_encoded(Mode::Ber, &mut buffer)
1050            .unwrap();
1051
1052        // The bytes aren't identical because we use definite length encoding, so we can't
1053        // compare that. But we can compare the parsed objects for equivalence.
1054
1055        let raw2 = crate::asn1::rfc5652::SignedData::decode_ber(&buffer).unwrap();
1056        assert_eq!(raw, raw2, "BER round tripping is identical");
1057    }
1058
1059    #[test]
1060    fn verify_firefox() {
1061        let signed_data = SignedData::parse_ber(FIREFOX_SIGNATURE).unwrap();
1062
1063        for signer in signed_data.signers.iter() {
1064            signer
1065                .verify_signature_with_signed_data(&signed_data)
1066                .unwrap();
1067
1068            // The message-digest does NOT match the encapsulated data in Apple code
1069            // signature's use of CMS. So digest verification will fail.
1070            signer
1071                .verify_message_digest_with_signed_data(&signed_data)
1072                .unwrap_err();
1073
1074            // But we know what that value is. So plug it in to verify.
1075            signer
1076                .verify_message_digest_with_content(FIREFOX_CODE_DIRECTORY)
1077                .unwrap();
1078
1079            // Now verify the time-stamp token embedded as an unsigned attribute.
1080            let tst_signed_data = signer.time_stamp_token_signed_data().unwrap().unwrap();
1081
1082            for signer in tst_signed_data.signers() {
1083                signer
1084                    .verify_message_digest_with_signed_data(&tst_signed_data)
1085                    .unwrap();
1086                signer
1087                    .verify_signature_with_signed_data(&tst_signed_data)
1088                    .unwrap();
1089            }
1090        }
1091    }
1092
1093    #[test]
1094    fn parse_no_certificate_version() {
1095        let signed = SignedData::parse_ber(include_bytes!("testdata/no-cert-version.ber")).unwrap();
1096
1097        let cert_orig = signed.certificates().collect::<Vec<_>>()[0].clone();
1098        let cert = CapturedX509Certificate::from_der(cert_orig.encode_ber().unwrap()).unwrap();
1099
1100        assert_eq!(
1101            hex::encode(cert.sha256_fingerprint().unwrap()),
1102            "b7c2eefd8dac7806af67dfcd92eb18126bc08312a7f2d6f3862e46013c7a6135"
1103        );
1104    }
1105
1106    const IZZYSOFT_SIGNED_DATA: &[u8] = include_bytes!("testdata/izzysoft-signeddata");
1107    const IZZYSOFT_DATA: &[u8] = include_bytes!("testdata/izzysoft-data");
1108
1109    #[test]
1110    fn verify_izzysoft() {
1111        let signed = SignedData::parse_ber(IZZYSOFT_SIGNED_DATA).unwrap();
1112        let cert = signed.certificates().next().unwrap();
1113
1114        for signer in signed.signers() {
1115            // The signed data is external. So this method will fail since it isn't looking at
1116            // the correct source data.
1117            assert!(matches!(
1118                signer.verify_signature_with_signed_data(&signed),
1119                Err(CmsError::SignatureVerificationError)
1120            ));
1121
1122            // There are no signed attributes. So this should error for that reason.
1123            assert!(matches!(
1124                signer.verify_message_digest_with_signed_data(&signed),
1125                Err(CmsError::NoSignedAttributes)
1126            ));
1127
1128            assert!(matches!(
1129                signer.verify_message_digest_with_signed_data(&signed),
1130                Err(CmsError::NoSignedAttributes)
1131            ));
1132
1133            // The certificate advertises SHA-256 for digests but the signature was made with
1134            // SHA-1. So the default algorithm choice will fail.
1135            assert!(matches!(
1136                cert.verify_signed_data(IZZYSOFT_DATA, signer.signature()),
1137                Err(X509CertificateError::CertificateSignatureVerificationFailed)
1138            ));
1139
1140            // But it verifies when SHA-1 digests are forced!
1141            cert.verify_signed_data_with_algorithm(
1142                IZZYSOFT_DATA,
1143                signer.signature(),
1144                &ring::signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
1145            )
1146            .unwrap();
1147
1148            signer
1149                .verify_signature_with_signed_data_and_content(&signed, IZZYSOFT_DATA)
1150                .unwrap();
1151
1152            let verifier = signer.signature_verifier(signed.certificates()).unwrap();
1153            verifier.verify(IZZYSOFT_DATA, signer.signature()).unwrap();
1154        }
1155    }
1156}