Skip to main content

isideload_cryptographic_message_syntax/
signing.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/*! Functionality for signing data. */
6
7use {
8    crate::{
9        CmsError,
10        asn1::rfc5652::{
11            CertificateChoices, CertificateSet, CmsVersion, DigestAlgorithmIdentifier,
12            DigestAlgorithmIdentifiers, EncapsulatedContentInfo, IssuerAndSerialNumber,
13            OID_CONTENT_TYPE, OID_ID_DATA, OID_ID_SIGNED_DATA, OID_MESSAGE_DIGEST,
14            OID_SIGNING_TIME, SignatureValue, SignedAttributes, SignedData, SignerIdentifier,
15            SignerInfo, SignerInfos,
16        },
17    },
18    bcder::{
19        Captured, Mode, OctetString, Oid,
20        encode::{PrimitiveContent, Values},
21    },
22    bytes::Bytes,
23    std::collections::HashSet,
24    x509_certificate::{
25        CapturedX509Certificate, DigestAlgorithm, KeyInfoSigner, SignatureAlgorithm,
26        asn1time::UtcTime,
27        rfc5652::{Attribute, AttributeValue},
28    },
29};
30
31/// Builder type to construct an entity that will sign some data.
32///
33/// Instances will be attached to `SignedDataBuilder` instances where they
34/// will sign data using configured settings.
35#[derive(Clone)]
36pub struct SignerBuilder<'a> {
37    /// The cryptographic key pair used for signing content.
38    signing_key: &'a dyn KeyInfoSigner,
39
40    /// Signer identifier - either explicitly provided, or
41    /// initialized from signing_certificate
42    signer_identifier: SignerIdentifier,
43
44    /// X.509 certificate used for signing.
45    signing_certificate: Option<CapturedX509Certificate>,
46
47    /// Content digest algorithm to use.
48    digest_algorithm: DigestAlgorithm,
49
50    /// Explicit content to use for calculating the `message-id`
51    /// attribute.
52    message_id_content: Option<Vec<u8>>,
53
54    /// The content type of the value being signed.
55    ///
56    /// This is a mandatory field for signed attributes. The default value
57    /// is `id-data`.
58    content_type: Oid,
59
60    /// Extra attributes to include in the SignedAttributes set.
61    extra_signed_attributes: Vec<Attribute>,
62}
63
64impl<'a> SignerBuilder<'a> {
65    /// Construct a new entity that will sign content.
66    ///
67    /// An entity is constructed from a signing key, which is mandatory.
68    pub fn new(
69        signing_key: &'a dyn KeyInfoSigner,
70        signing_certificate: CapturedX509Certificate,
71    ) -> Self {
72        Self {
73            signing_key,
74            signer_identifier: SignerIdentifier::IssuerAndSerialNumber(IssuerAndSerialNumber {
75                issuer: signing_certificate.issuer_name().clone(),
76                serial_number: signing_certificate.serial_number_asn1().clone(),
77            }),
78            signing_certificate: Some(signing_certificate),
79            digest_algorithm: DigestAlgorithm::Sha256,
80            message_id_content: None,
81            content_type: Oid(Bytes::copy_from_slice(OID_ID_DATA.as_ref())),
82            extra_signed_attributes: Vec::new(),
83        }
84    }
85
86    /// Construct a new entity that will sign content.
87    ///
88    /// An entity is constructed from a signing key and signer identifier, which are
89    /// mandatory.
90    pub fn new_with_signer_identifier(
91        signing_key: &'a dyn KeyInfoSigner,
92        signer_identifier: SignerIdentifier,
93    ) -> Self {
94        Self {
95            signing_key,
96            signer_identifier,
97            signing_certificate: None,
98            digest_algorithm: DigestAlgorithm::Sha256,
99            message_id_content: None,
100            content_type: Oid(Bytes::copy_from_slice(OID_ID_DATA.as_ref())),
101            extra_signed_attributes: Vec::new(),
102        }
103    }
104
105    /// Obtain the signature algorithm used by the signing key.
106    pub fn signature_algorithm(&self) -> Result<SignatureAlgorithm, CmsError> {
107        Ok(self.signing_key.signature_algorithm()?)
108    }
109
110    /// Define the content to use to calculate the `message-id` attribute.
111    ///
112    /// In most cases, this is never called and the encapsulated content
113    /// embedded within the generated message is used. However, some users
114    /// omit storing the data inline and instead use a `message-id` digest
115    /// calculated from a different source. This defines that different source.
116    #[must_use]
117    pub fn message_id_content(mut self, data: Vec<u8>) -> Self {
118        self.message_id_content = Some(data);
119        self
120    }
121
122    /// Define the content type of the signed content.
123    #[must_use]
124    pub fn content_type(mut self, oid: Oid) -> Self {
125        self.content_type = oid;
126        self
127    }
128
129    /// Add an additional attribute to sign.
130    #[must_use]
131    pub fn signed_attribute(mut self, typ: Oid, values: Vec<AttributeValue>) -> Self {
132        self.extra_signed_attributes.push(Attribute { typ, values });
133        self
134    }
135
136    /// Add an additional OctetString signed attribute.
137    ///
138    /// This is a helper for converting a byte slice to an OctetString and AttributeValue
139    /// without having to go through low-level ASN.1 code.
140    #[must_use]
141    pub fn signed_attribute_octet_string(self, typ: Oid, data: &[u8]) -> Self {
142        self.signed_attribute(
143            typ,
144            vec![AttributeValue::new(Captured::from_values(
145                Mode::Der,
146                data.encode_ref(),
147            ))],
148        )
149    }
150}
151
152/// Encapsulated content to sign.
153enum SignedContent {
154    /// No content is being signed.
155    None,
156
157    /// Signed content to be embedded in the signature.
158    Inline(Vec<u8>),
159
160    /// Signed content whose digest is to be captured but won't be included in the signature.
161    ///
162    /// Internal value is the raw content, not the digest.
163    External(Vec<u8>),
164}
165
166/// Entity for incrementally deriving a SignedData primitive.
167///
168/// Use this type for generating an RFC 5652 payload for signed data.
169///
170/// By default, the encapsulated content to sign is empty. Call [Self::content_inline()]
171/// or [Self::content_external()] to define encapsulated content.
172pub struct SignedDataBuilder<'a> {
173    /// Encapsulated content to sign.
174    signed_content: SignedContent,
175
176    /// Entities who will generated signatures.
177    signers: Vec<SignerBuilder<'a>>,
178
179    /// X.509 certificates to add to the payload.
180    certificates: Vec<CapturedX509Certificate>,
181
182    /// The OID to use for `ContentInfo.contentType`.
183    ///
184    /// This is supposed to be `signed-data` when there are signatures
185    /// present. But not all data producers use the same OID and this
186    /// can cause problems. So we allow overriding the default.
187    content_type: Oid,
188
189    /// The signing time to include in signatures.
190    ///
191    /// All signatures will use the same time.
192    signing_time: UtcTime,
193}
194
195impl Default for SignedDataBuilder<'_> {
196    fn default() -> Self {
197        Self {
198            signed_content: SignedContent::None,
199            signers: vec![],
200            certificates: vec![],
201            content_type: Oid(OID_ID_SIGNED_DATA.as_ref().into()),
202            signing_time: UtcTime::now(),
203        }
204    }
205}
206
207impl<'a> SignedDataBuilder<'a> {
208    /// Define encapsulated content that will be stored inline in the produced signature.
209    #[must_use]
210    pub fn content_inline(mut self, content: Vec<u8>) -> Self {
211        self.signed_content = SignedContent::Inline(content);
212        self
213    }
214
215    /// Define encapsulated content that won't be present in the produced signature.
216    ///
217    /// The content will be digested and that digest conveyed in the built signature.
218    /// But the content itself won't be present in the signature. RFC 5652 refers to
219    /// this as an _external signature_.
220    #[must_use]
221    pub fn content_external(mut self, content: Vec<u8>) -> Self {
222        self.signed_content = SignedContent::External(content);
223        self
224    }
225
226    /// Add a signer.
227    ///
228    /// The signer is the thing generating the cryptographic signature over
229    /// data to be signed.
230    #[must_use]
231    pub fn signer(mut self, signer: SignerBuilder<'a>) -> Self {
232        self.signers.push(signer);
233        self
234    }
235
236    /// Add a certificate defined by our crate's Certificate type.
237    #[must_use]
238    pub fn certificate(mut self, cert: CapturedX509Certificate) -> Self {
239        if !self.certificates.iter().any(|x| x == &cert) {
240            self.certificates.push(cert);
241        }
242
243        self
244    }
245
246    /// Add multiple certificates to the certificates chain.
247    #[must_use]
248    pub fn certificates(mut self, certs: impl Iterator<Item = CapturedX509Certificate>) -> Self {
249        for cert in certs {
250            if !self.certificates.iter().any(|x| x == &cert) {
251                self.certificates.push(cert);
252            }
253        }
254
255        self
256    }
257
258    /// Force the OID for the `ContentInfo.contentType` field.
259    #[must_use]
260    pub fn content_type(mut self, oid: Oid) -> Self {
261        self.content_type = oid;
262        self
263    }
264
265    /// Specify the signing time to use in signatures.
266    ///
267    /// If not called, current time at struct construction will be used.
268    #[must_use]
269    pub fn signing_time(mut self, time: UtcTime) -> Self {
270        self.signing_time = time;
271        self
272    }
273
274    /// Construct a `SignedData` object from the parameters received so far.
275    pub fn build_signed_data(&self) -> Result<SignedData, CmsError> {
276        let mut signer_infos = SignerInfos::default();
277        let mut seen_digest_algorithms = HashSet::new();
278        let mut seen_certificates = self.certificates.clone();
279
280        for signer in &self.signers {
281            seen_digest_algorithms.insert(signer.digest_algorithm);
282
283            if let Some(signing_certificate) = &signer.signing_certificate {
284                if !seen_certificates.iter().any(|x| x == signing_certificate) {
285                    seen_certificates.push(signing_certificate.clone());
286                }
287            }
288
289            let version = CmsVersion::V1;
290            let digest_algorithm = DigestAlgorithmIdentifier {
291                algorithm: signer.digest_algorithm.into(),
292                parameters: None,
293            };
294
295            // The message digest attribute is mandatory.
296            //
297            // Message digest is computed from override content on the signer
298            // or the encapsulated content if present. The "empty" hash is a
299            // valid value if no content (only signed attributes) are being signed.
300            let mut hasher = signer.digest_algorithm.digester();
301            if let Some(content) = &signer.message_id_content {
302                hasher.update(content);
303            } else {
304                match &self.signed_content {
305                    SignedContent::None => {}
306                    SignedContent::Inline(content) | SignedContent::External(content) => {
307                        hasher.update(content)
308                    }
309                }
310            }
311            let digest = hasher.finish();
312
313            let mut signed_attributes = SignedAttributes::default();
314
315            // The content-type field is mandatory.
316            signed_attributes.push(Attribute {
317                typ: Oid(Bytes::copy_from_slice(OID_CONTENT_TYPE.as_ref())),
318                values: vec![AttributeValue::new(Captured::from_values(
319                    Mode::Der,
320                    signer.content_type.encode_ref(),
321                ))],
322            });
323
324            // Set `messageDigest` field
325            signed_attributes.push(Attribute {
326                typ: Oid(Bytes::copy_from_slice(OID_MESSAGE_DIGEST.as_ref())),
327                values: vec![AttributeValue::new(Captured::from_values(
328                    Mode::Der,
329                    digest.as_ref().encode(),
330                ))],
331            });
332
333            // Add signing time because it is common to include.
334            signed_attributes.push(Attribute {
335                typ: Oid(Bytes::copy_from_slice(OID_SIGNING_TIME.as_ref())),
336                values: vec![AttributeValue::new(Captured::from_values(
337                    Mode::Der,
338                    self.signing_time.clone().encode(),
339                ))],
340            });
341
342            signed_attributes.extend(signer.extra_signed_attributes.iter().cloned());
343
344            // According to RFC 5652, signed attributes are DER encoded. This means a SET
345            // (which SignedAttributes is) should be sorted. But bcder doesn't appear to do
346            // this. So we manually sort here.
347            let signed_attributes = signed_attributes.as_sorted()?;
348
349            let signed_attributes = Some(signed_attributes);
350
351            let signature_algorithm = signer.signature_algorithm()?.into();
352
353            // The function for computing the signed attributes digested content
354            // is on SignerInfo. So construct an instance so we can compute the
355            // signature.
356            let mut signer_info = SignerInfo {
357                version,
358                sid: signer.signer_identifier.clone(),
359                digest_algorithm,
360                signed_attributes,
361                signature_algorithm,
362                signature: SignatureValue::new(Bytes::copy_from_slice(&[])),
363                unsigned_attributes: None,
364                signed_attributes_data: None,
365            };
366
367            // The content being signed is the DER encoded signed attributes, if present, or the
368            // encapsulated content. Since we always create signed attributes above, it *must* be
369            // the DER encoded signed attributes.
370            let signed_content = signer_info
371                .signed_attributes_digested_content()?
372                .expect("presence of signed attributes should ensure this is Some(T)");
373
374            let signature = signer.signing_key.try_sign(&signed_content)?;
375            let signature_algorithm = signer.signing_key.signature_algorithm()?;
376
377            signer_info.signature = SignatureValue::new(Bytes::from(signature.clone()));
378            signer_info.signature_algorithm = signature_algorithm.into();
379
380            signer_infos.push(signer_info);
381        }
382
383        let mut digest_algorithms = DigestAlgorithmIdentifiers::default();
384        digest_algorithms.extend(seen_digest_algorithms.into_iter().map(|alg| {
385            DigestAlgorithmIdentifier {
386                algorithm: alg.into(),
387                parameters: None,
388            }
389        }));
390
391        // Many consumers prefer the issuing certificate to come before the issued
392        // certificate. So we explicitly sort all the seen certificates in this order,
393        // attempting for all issuing certificates to come before the issued.
394        seen_certificates.sort_by(|a, b| a.compare_issuer(b));
395
396        let mut certificates = CertificateSet::default();
397        certificates.extend(
398            seen_certificates
399                .into_iter()
400                .map(|cert| CertificateChoices::Certificate(Box::new(cert.into()))),
401        );
402
403        // The certificates could have been encountered in any order. For best results,
404        // we want issuer certificates before their "children." So we apply sorting here.
405
406        let signed_data = SignedData {
407            version: CmsVersion::V1,
408            digest_algorithms,
409            content_info: EncapsulatedContentInfo {
410                content_type: self.content_type.clone(),
411                content: match &self.signed_content {
412                    SignedContent::None | SignedContent::External(_) => None,
413                    SignedContent::Inline(content) => {
414                        Some(OctetString::new(Bytes::copy_from_slice(content)))
415                    }
416                },
417            },
418            certificates: if certificates.is_empty() {
419                None
420            } else {
421                Some(certificates)
422            },
423            crls: None,
424            signer_infos,
425        };
426
427        Ok(signed_data)
428    }
429
430    /// Construct a DER-encoded ASN.1 document containing a `SignedData` object.
431    ///
432    /// RFC 5652 says `SignedData` is BER encoded. However, DER is a stricter subset
433    /// of BER. DER encodings are valid BER. So producing DER encoded data is perfectly
434    /// valid. We choose to go with the more well-defined encoding format.
435    pub fn build_der(&self) -> Result<Vec<u8>, CmsError> {
436        let signed_data = self.build_signed_data()?;
437
438        let mut ber = Vec::new();
439        signed_data
440            .encode_ref()
441            .write_encoded(Mode::Der, &mut ber)?;
442
443        Ok(ber)
444    }
445}
446
447#[cfg(test)]
448mod tests {
449    use {
450        super::*,
451        crate::SignedData,
452        x509_certificate::{EcdsaCurve, testutil::*},
453    };
454
455    const DIGICERT_TIMESTAMP_URL: &str = "http://timestamp.digicert.com";
456
457    #[test]
458    fn simple_rsa_signature_inline() {
459        let key = rsa_private_key();
460        let cert = rsa_cert();
461
462        let signer = SignerBuilder::new(&key, cert);
463
464        let ber = SignedDataBuilder::default()
465            .content_inline(vec![42])
466            .signer(signer)
467            .build_der()
468            .unwrap();
469
470        let signed_data = crate::SignedData::parse_ber(&ber).unwrap();
471        assert_eq!(signed_data.signed_content(), Some(vec![42].as_ref()));
472
473        for signer in signed_data.signers() {
474            signer
475                .verify_message_digest_with_signed_data(&signed_data)
476                .unwrap();
477            signer
478                .verify_signature_with_signed_data(&signed_data)
479                .unwrap();
480            assert!(signer.unsigned_attributes.is_none());
481        }
482    }
483
484    #[test]
485    fn simple_rsa_signature_external() {
486        let key = rsa_private_key();
487        let cert = rsa_cert();
488
489        let signer = SignerBuilder::new(&key, cert);
490
491        let ber = SignedDataBuilder::default()
492            .content_external(vec![42])
493            .signer(signer)
494            .build_der()
495            .unwrap();
496
497        let signed_data = crate::SignedData::parse_ber(&ber).unwrap();
498        assert!(signed_data.signed_content().is_none());
499
500        for signer in signed_data.signers() {
501            signer.verify_message_digest_with_content(&[42]).unwrap();
502            signer
503                .verify_signature_with_signed_data(&signed_data)
504                .unwrap();
505            assert!(signer.unsigned_attributes.is_none());
506        }
507    }
508
509    #[test]
510    fn time_stamp_url() {
511        let key = rsa_private_key();
512        let cert = rsa_cert();
513
514        let signer = SignerBuilder::new(&key, cert)
515            .time_stamp_url(DIGICERT_TIMESTAMP_URL)
516            .unwrap();
517
518        let ber = SignedDataBuilder::default()
519            .content_inline(vec![42])
520            .signer(signer)
521            .build_der()
522            .unwrap();
523
524        let signed_data = crate::SignedData::parse_ber(&ber).unwrap();
525
526        for signer in signed_data.signers() {
527            let unsigned = signer.unsigned_attributes().unwrap();
528            let tst = unsigned.time_stamp_token.as_ref().unwrap();
529            assert!(tst.certificates.is_some());
530
531            let tst_signed_data = signer.time_stamp_token_signed_data().unwrap().unwrap();
532            for signer in tst_signed_data.signers() {
533                signer
534                    .verify_message_digest_with_signed_data(&tst_signed_data)
535                    .unwrap();
536                signer
537                    .verify_signature_with_signed_data(&tst_signed_data)
538                    .unwrap();
539            }
540
541            assert!(signer.verify_time_stamp_token().unwrap().is_some());
542        }
543    }
544
545    #[test]
546    fn simple_ecdsa_signature() {
547        for curve in EcdsaCurve::all() {
548            let (cert, key) = self_signed_ecdsa_key_pair(Some(*curve));
549
550            let cms = SignedDataBuilder::default()
551                .content_inline("hello world".as_bytes().to_vec())
552                .certificate(cert.clone())
553                .signer(SignerBuilder::new(&key, cert))
554                .build_der()
555                .unwrap();
556
557            let signed_data = SignedData::parse_ber(&cms).unwrap();
558
559            for signer in signed_data.signers() {
560                signer
561                    .verify_signature_with_signed_data(&signed_data)
562                    .unwrap();
563            }
564        }
565    }
566
567    #[test]
568    fn simple_ed25519_signature() {
569        let (cert, key) = self_signed_ed25519_key_pair();
570
571        let cms = SignedDataBuilder::default()
572            .content_inline("hello world".as_bytes().to_vec())
573            .certificate(cert.clone())
574            .signer(SignerBuilder::new(&key, cert))
575            .build_der()
576            .unwrap();
577
578        let signed_data = SignedData::parse_ber(&cms).unwrap();
579
580        for signer in signed_data.signers() {
581            signer
582                .verify_signature_with_signed_data(&signed_data)
583                .unwrap();
584        }
585    }
586}