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