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}