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