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