quantcrypt/cms/asn1/enveloped_data_content.rs
1use crate::cea::common::cea_type::CeaType;
2use cms::{
3 content_info::{CmsVersion, ContentInfo},
4 enveloped_data::{EnvelopedData, OriginatorInfo, RecipientInfos},
5};
6use der::{Decode, Encode};
7use x509_cert::attr::Attributes;
8
9use crate::{certificates::Certificate, keys::PrivateKey, QuantCryptError};
10
11type Result<T> = std::result::Result<T, QuantCryptError>;
12
13use crate::cms::cms_util::CmsUtil;
14use const_oid::db::rfc5911::ID_ENVELOPED_DATA;
15
16use crate::cms::enveloped_data_builder::EnvelopedDataBuilder;
17
18/// The content encryption algorithm used to encrypt the content
19pub enum ContentEncryptionAlgorithm {
20 /// AES 128 bit encryption in CBC mode
21 Aes128Cbc,
22 /// AES 192 bit encryption in CBC mode
23 Aes192Cbc,
24 /// AES 256 bit encryption in CBC mode
25 Aes256Cbc,
26}
27
28/// Main interaction point for the EnvelopedData content
29///
30/// This struct is used to create, read and manipulate EnvelopedData content
31///
32/// # Example
33/// ```
34/// use quantcrypt::content::EnvelopedDataContent;
35/// use quantcrypt::content::ContentEncryptionAlgorithm;
36/// use quantcrypt::certificates::Certificate;
37/// use quantcrypt::keys::PrivateKey;
38/// use quantcrypt::kdfs::KdfType;
39/// use quantcrypt::wraps::WrapType;
40/// use quantcrypt::content::UserKeyingMaterial;
41/// use quantcrypt::content::ObjectIdentifier;
42/// use quantcrypt::content::Attribute;
43/// use quantcrypt::content::Tag;
44/// use quantcrypt::content::AttributeValue;
45/// use quantcrypt::content::SetOfVec;
46///
47// Based on whether IPD feature is enabled or not, use the appropriate test data
48/// let rc_filename = if quantcrypt::is_ipd_mode_enabled() {
49/// "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_ee.der"
50/// } else {
51/// "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_ee.der"
52/// };
53///
54/// let recipient_cert = Certificate::from_file(
55/// rc_filename,
56/// ).unwrap();
57///
58/// let sk_filename = if quantcrypt::is_ipd_mode_enabled() {
59/// "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_priv.der"
60/// } else {
61/// "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_priv.der"
62/// };
63///
64/// let private_key = PrivateKey::from_file(
65/// sk_filename
66/// ).unwrap();
67///
68/// let ukm = UserKeyingMaterial::new("test".as_bytes()).unwrap();
69/// let data = b"abc";
70///
71/// let attribute_oid = ObjectIdentifier::new("1.3.6.1.4.1.22554.5.6").unwrap();
72/// let mut attribute_vals: SetOfVec<AttributeValue> = SetOfVec::<AttributeValue>::new();
73///
74/// let attr_val = AttributeValue::new(Tag::OctetString, data.to_vec()).unwrap();
75/// attribute_vals.insert(attr_val).unwrap();
76///
77/// let attribute = Attribute {
78/// oid: attribute_oid,
79/// values: attribute_vals,
80/// };
81///
82/// let mut builder =
83/// EnvelopedDataContent::get_builder(ContentEncryptionAlgorithm::Aes128Cbc).unwrap();
84///
85/// builder
86/// .kem_recipient(
87/// &recipient_cert,
88/// &KdfType::HkdfWithSha256,
89/// &WrapType::Aes256,
90/// Some(ukm),
91/// )
92/// .unwrap()
93/// .content(data)
94/// .unwrap()
95/// .unprotected_attribute(&attribute)
96/// .unwrap();
97///
98/// let content = builder.build().unwrap();
99/// // Now use this content to create a new EnvelopedDataContent
100/// let edc = EnvelopedDataContent::from_bytes_for_kem_recipient(
101/// &content,
102/// &recipient_cert,
103/// &private_key,
104/// )
105/// .unwrap();
106/// assert_eq!(edc.get_content(), data);
107/// ```
108
109pub struct EnvelopedDataContent {
110 /// The version of the EnvelopedData content
111 version: CmsVersion,
112 /// The originator info
113 originator_info: Option<OriginatorInfo>,
114 /// The recipient infos
115 recip_infos: RecipientInfos,
116 /// The content
117 content: Vec<u8>,
118 /// The unprotected attributes
119 unprotected_attrs: Option<Attributes>,
120}
121
122impl EnvelopedDataContent {
123 /// Create a new EnvelopedDataContent object from a file. The encrypted content is
124 /// wrapped in a ContentInfo object and the file contains the DER encoded bytes of the
125 /// ContentInfo object.
126 ///
127 /// # Arguments
128 ///
129 /// * `file` - The file path to read the EnvelopedData content from
130 /// * `recipient_cert` - The recipient certificate
131 /// * `recipient_private_key` - The recipient private key
132 ///
133 /// # Returns
134 ///
135 /// A new EnvelopedDataContent object
136 pub fn from_file_for_kem_recipient(
137 file: &str,
138 recipient_cert: &Certificate,
139 recipient_private_key: &PrivateKey,
140 ) -> Result<EnvelopedDataContent> {
141 let data = std::fs::read(file).map_err(|_| QuantCryptError::FileReadError)?;
142 EnvelopedDataContent::from_bytes_for_kem_recipient(
143 &data,
144 recipient_cert,
145 recipient_private_key,
146 )
147 }
148
149 /// Create a new EnvelopedDataContent object from bytes. The encrypted content is
150 /// wrapped in a ContentInfo object and the data is the DER encoded bytes of the
151 /// ContentInfo object.
152 ///
153 /// # Arguments
154 ///
155 /// * `data` - The bytes to read the EnvelopedData content from
156 /// * `recipient_cert` - The recipient certificate
157 /// * `recipient_private_key` - The recipient private key
158 ///
159 /// # Returns
160 ///
161 /// A new EnvelopedDataContent object
162 pub fn from_bytes_for_kem_recipient(
163 data: &[u8],
164 recipient_cert: &Certificate,
165 recipient_private_key: &PrivateKey,
166 ) -> Result<EnvelopedDataContent> {
167 // First try to read it as a der encoded ContentInfo
168 let ci = if let Ok(content_info) = ContentInfo::from_der(data) {
169 content_info
170 } else {
171 // If that fails, try to read it as a pem encoded ContentInfo
172 let pem = pem::parse(data).map_err(|_| QuantCryptError::InvalidContent)?;
173 ContentInfo::from_der(pem.contents()).map_err(|_| QuantCryptError::InvalidContent)?
174 };
175
176 // Check if the cotent type is EnvelopedData
177 if ci.content_type != ID_ENVELOPED_DATA {
178 return Err(QuantCryptError::InvalidContent);
179 }
180
181 let enveloped_data = ci
182 .content
183 .to_der()
184 .map_err(|_| QuantCryptError::InvalidEnvelopedData)?;
185
186 let ed = EnvelopedData::from_der(&enveloped_data)
187 .map_err(|_| QuantCryptError::InvalidContent)?;
188
189 // try to decrypt the content
190 let pt = CmsUtil::decrypt_kemri(data, recipient_private_key, recipient_cert)?;
191
192 Ok(EnvelopedDataContent {
193 version: ed.version,
194 originator_info: ed.originator_info,
195 recip_infos: ed.recip_infos,
196 content: pt,
197 unprotected_attrs: ed.unprotected_attrs,
198 })
199 }
200
201 /// Get the version of the EnvelopedData Cms content
202 pub fn get_version(&self) -> CmsVersion {
203 self.version
204 }
205
206 /// Get the originator info
207 pub fn get_originator_info(&self) -> Option<OriginatorInfo> {
208 self.originator_info.clone()
209 }
210
211 /// Get the content
212 pub fn get_content(&self) -> Vec<u8> {
213 self.content.clone()
214 }
215
216 /// Get the unprotected attributes
217 pub fn get_unprotected_attrs(&self) -> Option<Attributes> {
218 self.unprotected_attrs.clone()
219 }
220
221 /// Get the recipient infos
222 pub fn get_recipient_infos(&self) -> RecipientInfos {
223 self.recip_infos.clone()
224 }
225
226 /// Get a new EnvelopedDataContentBuilder
227 ///
228 /// # Arguments
229 ///
230 /// * `content_encryption_alg` - The content encryption algorithm to use
231 ///
232 /// # Returns
233 ///
234 /// A new EnvelopedDataContentBuilder which can be used to create a new EnvelopedDataContent object
235 pub fn get_builder(
236 content_encryption_alg: ContentEncryptionAlgorithm,
237 ) -> Result<EnvelopedDataBuilder<'static>> {
238 let cea = match content_encryption_alg {
239 ContentEncryptionAlgorithm::Aes128Cbc => CeaType::Aes128CbcPad,
240 ContentEncryptionAlgorithm::Aes192Cbc => CeaType::Aes192CbcPad,
241 ContentEncryptionAlgorithm::Aes256Cbc => CeaType::Aes256CbcPad,
242 };
243 EnvelopedDataBuilder::new(cea, false)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use der::{asn1::SetOfVec, Tag, Tagged};
250 use spki::ObjectIdentifier;
251 use x509_cert::attr::{Attribute, AttributeValue};
252
253 use super::*;
254 use crate::{content::UserKeyingMaterial, content::WrapType, kdf::common::kdf_type::KdfType};
255
256 #[test]
257 fn test_enveloped_data_content() {
258 #[cfg(feature = "ipd")]
259 let recipient_cert = Certificate::from_file(
260 "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_ee.der",
261 )
262 .unwrap();
263
264 #[cfg(not(feature = "ipd"))]
265 let recipient_cert =
266 Certificate::from_file("test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_ee.der").unwrap();
267
268 #[cfg(feature = "ipd")]
269 let private_key = PrivateKey::from_file(
270 "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_priv.der",
271 )
272 .unwrap();
273
274 #[cfg(not(feature = "ipd"))]
275 let private_key =
276 PrivateKey::from_file("test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_priv.der")
277 .unwrap();
278
279 let ukm = UserKeyingMaterial::new("test".as_bytes()).unwrap();
280 let data = b"abc";
281
282 let attribute_oid = ObjectIdentifier::new("1.3.6.1.4.1.22554.5.6").unwrap();
283 let mut attribute_vals: SetOfVec<AttributeValue> = SetOfVec::<AttributeValue>::new();
284
285 let attr_val = AttributeValue::new(Tag::OctetString, data.to_vec()).unwrap();
286 attribute_vals.insert(attr_val).unwrap();
287
288 let attribute = Attribute {
289 oid: attribute_oid,
290 values: attribute_vals,
291 };
292
293 let mut builder =
294 EnvelopedDataContent::get_builder(ContentEncryptionAlgorithm::Aes128Cbc).unwrap();
295
296 builder
297 .kem_recipient(
298 &recipient_cert,
299 &KdfType::HkdfWithSha256,
300 &WrapType::Aes256,
301 Some(ukm),
302 )
303 .unwrap()
304 .content(data)
305 .unwrap()
306 .unprotected_attribute(&attribute)
307 .unwrap();
308
309 let content = builder.build().unwrap();
310
311 // Now use this content to create a new EnvelopedDataContent
312 let edc = EnvelopedDataContent::from_bytes_for_kem_recipient(
313 &content,
314 &recipient_cert,
315 &private_key,
316 )
317 .unwrap();
318
319 assert_eq!(edc.get_content(), data);
320 assert_eq!(edc.get_recipient_infos().0.len(), 1);
321 assert_eq!(edc.get_unprotected_attrs().unwrap().len(), 1);
322
323 // Check the attribute
324 let attrs = edc.get_unprotected_attrs().unwrap();
325 let attr = attrs.get(0).unwrap();
326 assert_eq!(attr.oid.to_string(), "1.3.6.1.4.1.22554.5.6");
327 assert_eq!(attr.values.len(), 1);
328 let val: AttributeValue = attr.values.get(0).unwrap().clone();
329 assert_eq!(val.tag(), Tag::OctetString);
330 assert_eq!(val.value(), data);
331
332 // Check the version
333 assert_eq!(edc.get_version(), CmsVersion::V3);
334
335 // Check the originator info
336 assert_eq!(edc.get_originator_info(), None);
337
338 // Check the recipient infos length
339 assert_eq!(edc.get_recipient_infos().0.len(), 1);
340 }
341}