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}