confium_pki/cms/
envelope.rs1use crate::cms::signed_data::{AlgorithmIdentifier, SignedData, SignerIdentifier, SignerInfo};
9
10#[derive(Debug, Default)]
12pub struct SignedDataBuilder {
13 content_type: Option<String>,
14 content: Option<Vec<u8>>,
15 signers: Vec<SignerInfo>,
16 certificates: Vec<Vec<u8>>,
17}
18
19impl SignedDataBuilder {
20 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn content_type(mut self, oid: impl Into<String>) -> Self {
27 self.content_type = Some(oid.into());
28 self
29 }
30
31 pub fn content(mut self, content: Option<Vec<u8>>) -> Self {
33 self.content = content;
34 self
35 }
36
37 pub fn signer(mut self, signer: SignerInfo) -> Self {
39 self.signers.push(signer);
40 self
41 }
42
43 pub fn certificate(mut self, cert_der: Vec<u8>) -> Self {
45 self.certificates.push(cert_der);
46 self
47 }
48
49 pub fn build(self) -> Result<SignedData, CmsError> {
51 let content_type = self
52 .content_type
53 .ok_or(CmsError::MissingField("content_type"))?;
54 let mut sd = SignedData::new(content_type, self.content);
55 for cert in self.certificates {
56 sd.add_certificate(cert);
57 }
58 for signer in self.signers {
59 sd.add_signer(signer);
60 }
61 Ok(sd)
62 }
63}
64
65#[derive(Debug, thiserror::Error)]
67pub enum CmsError {
68 #[error("missing required field: {0}")]
70 MissingField(&'static str),
71 #[error("serialization error: {0}")]
73 Serialize(String),
74 #[error("verification failure: {0}")]
76 Verify(String),
77 #[error("JSON error: {0}")]
79 Json(#[from] serde_json::Error),
80}
81
82pub fn build_detached_signature(
84 payload_hash: Vec<u8>,
85 signer_algorithm: impl Into<String>,
86 signature: Vec<u8>,
87 cert_chain_der: Vec<Vec<u8>>,
88) -> Result<SignedData, CmsError> {
89 let _ = payload_hash; let signer = SignerInfo {
91 version: 1,
92 sid: SignerIdentifier::SubjectKeyIdentifier {
93 key_identifier: cert_chain_der
94 .first()
95 .map(|c| c[..20].to_vec())
96 .unwrap_or_default(),
97 },
98 digest_algorithm: AlgorithmIdentifier {
99 oid: "2.16.840.1.101.3.4.2.1".into(), parameters: None,
101 },
102 signed_attrs: Vec::new(),
103 signature_algorithm: AlgorithmIdentifier {
104 oid: signer_algorithm.into(),
105 parameters: None,
106 },
107 signature,
108 unsigned_attrs: Vec::new(),
109 };
110 let mut builder = SignedDataBuilder::new()
111 .content_type("1.2.840.113549.1.7.1")
112 .content(None)
113 .signer(signer);
114 for cert in cert_chain_der {
115 builder = builder.certificate(cert);
116 }
117 builder.build()
118}
119
120pub use crate::cms::signed_data::EncapContentInfo as _ReExportEncapContentInfo;
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn builder_constructs_signed_data() {
130 let sd = SignedDataBuilder::new()
131 .content_type("1.2.840.113549.1.7.1")
132 .content(Some(b"hello".to_vec()))
133 .build()
134 .unwrap();
135 assert_eq!(sd.encap_content_info.content_type, "1.2.840.113549.1.7.1");
136 assert_eq!(sd.encap_content_info.content, Some(b"hello".to_vec()));
137 }
138
139 #[test]
140 fn detached_signature_helper() {
141 let sd = build_detached_signature(
142 vec![0u8; 32],
143 "1.2.840.113549.1.1.11",
144 vec![0u8; 256],
145 vec![vec![0u8; 100]],
146 )
147 .unwrap();
148 assert_eq!(sd.signer_count(), 1);
149 assert!(sd.encap_content_info.content.is_none()); }
151
152 #[test]
153 fn missing_content_type_fails() {
154 let result = SignedDataBuilder::new().build();
155 assert!(result.is_err());
156 }
157}