1use {
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#[derive(Clone)]
41pub struct SignerBuilder<'a> {
42 signing_key: &'a dyn KeyInfoSigner,
44
45 signer_identifier: SignerIdentifier,
48
49 signing_certificate: Option<CapturedX509Certificate>,
51
52 digest_algorithm: DigestAlgorithm,
54
55 message_id_content: Option<Vec<u8>>,
58
59 content_type: Oid,
64
65 extra_signed_attributes: Vec<Attribute>,
67
68 time_stamp_url: Option<reqwest::Url>,
70}
71
72impl<'a> SignerBuilder<'a> {
73 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 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 pub fn signature_algorithm(&self) -> Result<SignatureAlgorithm, CmsError> {
117 Ok(self.signing_key.signature_algorithm()?)
118 }
119
120 #[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 #[must_use]
134 pub fn content_type(mut self, oid: Oid) -> Self {
135 self.content_type = oid;
136 self
137 }
138
139 #[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 #[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 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
173enum SignedContent {
175 None,
177
178 Inline(Vec<u8>),
180
181 External(Vec<u8>),
185}
186
187pub struct SignedDataBuilder<'a> {
194 signed_content: SignedContent,
196
197 signers: Vec<SignerBuilder<'a>>,
199
200 certificates: Vec<CapturedX509Certificate>,
202
203 content_type: Oid,
209
210 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 #[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 #[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 #[must_use]
252 pub fn signer(mut self, signer: SignerBuilder<'a>) -> Self {
253 self.signers.push(signer);
254 self
255 }
256
257 #[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 #[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 #[must_use]
281 pub fn content_type(mut self, oid: Oid) -> Self {
282 self.content_type = oid;
283 self
284 }
285
286 #[must_use]
290 pub fn signing_time(mut self, time: UtcTime) -> Self {
291 self.signing_time = time;
292 self
293 }
294
295 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 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 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 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 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 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 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 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 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 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 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 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}