quantcrypt/cms/asn1/
auth_enveloped_data_content.rs

1use crate::cea::common::cea_type::CeaType;
2use cms::{
3    content_info::{CmsVersion, ContentInfo},
4    enveloped_data::{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_CT_AUTH_ENVELOPED_DATA;
15
16use crate::cms::asn1::auth_enveloped_data_builder::ContentEncryptionAlgorithmAead;
17use crate::cms::enveloped_data_builder::EnvelopedDataBuilder;
18
19use super::auth_env_data::AuthEnvelopedData;
20
21/// Main interaction point for the AuthEnvelopedData content
22///
23/// This struct is used to create, read and manipulate AuthEnvelopedData content
24///
25/// # Example
26/// ```
27/// use quantcrypt::content::AuthEnvelopedDataContent;
28/// use quantcrypt::content::ContentEncryptionAlgorithmAead;
29/// use quantcrypt::certificates::Certificate;
30/// use quantcrypt::keys::PrivateKey;
31/// use quantcrypt::kdfs::KdfType;
32/// use quantcrypt::wraps::WrapType;
33/// use quantcrypt::content::UserKeyingMaterial;
34/// use quantcrypt::content::ObjectIdentifier;
35/// use quantcrypt::content::Attribute;
36/// use quantcrypt::content::Tag;
37/// use quantcrypt::content::AttributeValue;
38/// use quantcrypt::content::SetOfVec;
39///
40// Based on whether IPD feature is enabled or not, use the appropriate test data
41/// let rc_filename = if quantcrypt::is_ipd_mode_enabled() {
42///     "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_ee.der"
43/// } else {
44///     "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_ee.der"
45/// };
46///
47/// let recipient_cert = Certificate::from_file(
48///     rc_filename,
49/// ).unwrap();
50///
51/// let sk_filename = if quantcrypt::is_ipd_mode_enabled() {
52///     "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_priv.der"
53/// } else {
54///     "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_priv.der"
55/// };
56///
57/// let private_key = PrivateKey::from_file(
58///     sk_filename
59/// ).unwrap();
60///
61/// let ukm = UserKeyingMaterial::new("test".as_bytes()).unwrap();
62/// let data = b"abc";
63///
64/// let attribute_oid = ObjectIdentifier::new("1.3.6.1.4.1.22554.5.6").unwrap();
65/// let mut attribute_vals: SetOfVec<AttributeValue> = SetOfVec::<AttributeValue>::new();
66///
67/// let attr_val = AttributeValue::new(Tag::OctetString, data.to_vec()).unwrap();
68/// attribute_vals.insert(attr_val).unwrap();
69///
70/// let attribute = Attribute {
71///     oid: attribute_oid,
72///     values: attribute_vals,
73/// };
74///
75/// let mut builder =
76///     AuthEnvelopedDataContent::get_builder(ContentEncryptionAlgorithmAead::Aes256Gcm).unwrap();
77///
78/// builder
79///     .kem_recipient(
80///         &recipient_cert,
81///         &KdfType::HkdfWithSha256,
82///         &WrapType::Aes256,
83///         Some(ukm),
84///     )
85///     .unwrap()
86///     .content(data)
87///     .unwrap()
88///     .auth_attribute(&attribute)
89///     .unwrap();
90///     
91///
92/// let content = builder.build().unwrap();
93/// // Now use this content to create a new EnvelopedDataContent
94/// let edc = AuthEnvelopedDataContent::from_bytes_for_kem_recipient(
95///     &content,
96///     &recipient_cert,
97///     &private_key,
98/// )
99/// .unwrap();
100/// assert_eq!(edc.get_content(), data);
101/// ```
102
103pub struct AuthEnvelopedDataContent {
104    /// The CmsVersion
105    version: CmsVersion,
106    /// The OriginatorInfo if present
107    originator_info: Option<OriginatorInfo>,
108    /// The RecipientInfos
109    recip_infos: RecipientInfos,
110    /// The content
111    content: Vec<u8>,
112    /// The unprotected attributes
113    unprotected_attrs: Option<Attributes>,
114    /// The authenticated attributes
115    auth_attrs: Option<Attributes>,
116}
117
118impl AuthEnvelopedDataContent {
119    /// Load a AuthEnvelopedDataContent from a file. The content is wrapped in a ContentInfo
120    /// object. The content is decrypted using the provided recipient certificate and private key.
121    ///
122    /// # Arguments
123    ///
124    /// * `file` - The file to read the content from
125    /// * `recipient_cert` - The recipient certificate
126    /// * `recipient_private_key` - The recipient private key
127    ///
128    /// # Returns
129    ///
130    /// The AuthEnvelopedDataContent object
131    pub fn from_file_for_kem_recipient(
132        file: &str,
133        recipient_cert: &Certificate,
134        recipient_private_key: &PrivateKey,
135    ) -> Result<AuthEnvelopedDataContent> {
136        let data = std::fs::read(file).map_err(|_| QuantCryptError::FileReadError)?;
137        AuthEnvelopedDataContent::from_bytes_for_kem_recipient(
138            &data,
139            recipient_cert,
140            recipient_private_key,
141        )
142    }
143
144    /// Load a AuthEnvelopedDataContent from a byte array. The content is wrapped in a ContentInfo
145    /// object. The content is decrypted using the provided recipient certificate and private key.
146    ///
147    /// # Arguments
148    ///
149    /// * `data` - The byte array to read the content from
150    /// * `recipient_cert` - The recipient certificate
151    /// * `recipient_private_key` - The recipient private key
152    ///
153    /// # Returns
154    ///
155    /// The AuthEnvelopedDataContent object
156    pub fn from_bytes_for_kem_recipient(
157        data: &[u8],
158        recipient_cert: &Certificate,
159        recipient_private_key: &PrivateKey,
160    ) -> Result<AuthEnvelopedDataContent> {
161        // First try to read it as a der encoded ContentInfo
162        let ci = if let Ok(content_info) = ContentInfo::from_der(data) {
163            content_info
164        } else {
165            // If that fails, try to read it as a pem encoded ContentInfo
166            let pem = pem::parse(data).map_err(|_| QuantCryptError::InvalidContent)?;
167            ContentInfo::from_der(pem.contents()).map_err(|_| QuantCryptError::InvalidContent)?
168        };
169
170        // Check if the cotent type is EnvelopedData
171        if ci.content_type != ID_CT_AUTH_ENVELOPED_DATA {
172            return Err(QuantCryptError::InvalidContent);
173        }
174
175        let enveloped_data = ci
176            .content
177            .to_der()
178            .map_err(|_| QuantCryptError::InvalidEnvelopedData)?;
179
180        let ed = AuthEnvelopedData::from_der(&enveloped_data)
181            .map_err(|_| QuantCryptError::InvalidContent)?;
182
183        // try to decrypt the content
184        let pt = CmsUtil::decrypt_kemri(data, recipient_private_key, recipient_cert)?;
185
186        Ok(AuthEnvelopedDataContent {
187            version: ed.version,
188            originator_info: ed.originator_info,
189            recip_infos: ed.recip_infos,
190            content: pt,
191            unprotected_attrs: ed.unauth_attrs,
192            auth_attrs: ed.auth_attrs,
193        })
194    }
195
196    /// Get the CmsVersion
197    pub fn get_version(&self) -> CmsVersion {
198        self.version
199    }
200
201    /// Get the OriginatorInfo
202    pub fn get_originator_info(&self) -> Option<OriginatorInfo> {
203        self.originator_info.clone()
204    }
205
206    /// Get the content
207    pub fn get_content(&self) -> Vec<u8> {
208        self.content.clone()
209    }
210
211    /// Get the unprotected attributes
212    pub fn get_unprotected_attrs(&self) -> Option<Attributes> {
213        self.unprotected_attrs.clone()
214    }
215
216    /// Get the authenticated attributes
217    pub fn get_auth_attrs(&self) -> Option<Attributes> {
218        self.auth_attrs.clone()
219    }
220
221    /// Get the RecipientInfos
222    pub fn get_recipient_infos(&self) -> RecipientInfos {
223        self.recip_infos.clone()
224    }
225
226    /// Get a builder for the AuthEnvelopedDataContent. This is used to create new AuthEnvelopedDataContent objects
227    ///
228    /// # Arguments
229    ///
230    /// * `content_encryption_alg` - The content encryption algorithm
231    ///
232    /// # Returns
233    ///
234    /// The AuthEnvelopedDataContent builder
235    pub fn get_builder(
236        content_encryption_alg: ContentEncryptionAlgorithmAead,
237    ) -> Result<EnvelopedDataBuilder<'static>> {
238        let cea = match content_encryption_alg {
239            ContentEncryptionAlgorithmAead::Aes128Gcm => CeaType::Aes128Gcm,
240            ContentEncryptionAlgorithmAead::Aes192Gcm => CeaType::Aes192Gcm,
241            ContentEncryptionAlgorithmAead::Aes256Gcm => CeaType::Aes256Gcm,
242        };
243        EnvelopedDataBuilder::new(cea, true)
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_auth_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            AuthEnvelopedDataContent::get_builder(ContentEncryptionAlgorithmAead::Aes256Gcm)
295                .unwrap();
296
297        builder
298            .kem_recipient(
299                &recipient_cert,
300                &KdfType::HkdfWithSha256,
301                &WrapType::Aes256,
302                Some(ukm),
303            )
304            .unwrap()
305            .content(data)
306            .unwrap()
307            .unprotected_attribute(&attribute)
308            .unwrap()
309            .auth_attribute(&attribute)
310            .unwrap();
311
312        let content = builder.build().unwrap();
313
314        // Now use this content to create a new AuthEnvelopedDataContent
315        let edc = AuthEnvelopedDataContent::from_bytes_for_kem_recipient(
316            &content,
317            &recipient_cert,
318            &private_key,
319        )
320        .unwrap();
321
322        assert_eq!(edc.get_content(), data);
323        assert_eq!(edc.get_recipient_infos().0.len(), 1);
324        assert_eq!(edc.get_unprotected_attrs().unwrap().len(), 1);
325
326        // Check the attribute
327        let attrs = edc.get_unprotected_attrs().unwrap();
328        let attr = attrs.get(0).unwrap();
329        assert_eq!(attr.oid.to_string(), "1.3.6.1.4.1.22554.5.6");
330        assert_eq!(attr.values.len(), 1);
331        let val: AttributeValue = attr.values.get(0).unwrap().clone();
332        assert_eq!(val.tag(), Tag::OctetString);
333        assert_eq!(val.value(), data);
334
335        // Check the auth attribute
336        let attrs = edc.get_auth_attrs().unwrap();
337        let attr = attrs.get(0).unwrap();
338        assert_eq!(attr.oid.to_string(), "1.3.6.1.4.1.22554.5.6");
339        assert_eq!(attr.values.len(), 1);
340        let val: AttributeValue = attr.values.get(0).unwrap().clone();
341        assert_eq!(val.tag(), Tag::OctetString);
342        assert_eq!(val.value(), data);
343
344        // Check the version
345        assert_eq!(edc.get_version(), CmsVersion::V0);
346
347        // Check the originator info
348        assert_eq!(edc.get_originator_info(), None);
349
350        // Check the recipient infos length
351        assert_eq!(edc.get_recipient_infos().0.len(), 1);
352    }
353}