quantcrypt/cms/asn1/
auth_enveloped_data_builder.rs

1// Thanks to Carl Wallace for this code (it is adapted from his code):
2// https://github.com/carl-wallace/kemri_toy/blob/main/src/asn1/auth_env_data_builder.rs
3
4use crate::cea::common::cea_trait::Cea;
5use cms::authenticated_data::MessageAuthenticationCode;
6use cms::builder::Error;
7use cms::content_info::CmsVersion;
8use cms::enveloped_data::{EncryptedContentInfo, RecipientInfo, RecipientInfos};
9use cms::{builder::RecipientInfoBuilder, enveloped_data::OriginatorInfo};
10use const_oid::db::rfc5911::{ID_AES_128_GCM, ID_AES_192_GCM, ID_AES_256_GCM};
11use der::{Decode, Encode};
12use spki::ObjectIdentifier;
13use x509_cert::attr::Attributes;
14use zeroize::Zeroize;
15
16use crate::cea::cea_manager::CeaManager;
17use crate::cea::common::cea_type::CeaType;
18use crate::QuantCryptError;
19
20use crate::cms::asn1::auth_env_data::AuthEnvelopedData;
21
22type Result<T> = std::result::Result<T, QuantCryptError>;
23
24/// Content encryption algorithm for AuthEnvelopedData
25#[derive(Clone, Debug, Eq, PartialEq)]
26pub enum ContentEncryptionAlgorithmAead {
27    /// AES-128 GCM
28    Aes128Gcm,
29    /// AES-192 GCM
30    Aes192Gcm,
31    /// AES-256 GCM
32    Aes256Gcm,
33}
34
35/// Builds CMS `AuthEnvelopedData` according to RFC 5083 § 2.1.
36pub struct AuthEnvelopedDataBuilder<'c> {
37    content_id: Option<ObjectIdentifier>,
38    originator_info: Option<OriginatorInfo>,
39    recipient_infos: Vec<Box<dyn RecipientInfoBuilder + 'c>>,
40    unencrypted_content: &'c [u8],
41    // TODO bk Not good to offer both, `content_encryptor` and `content_encryption_algorithm`.
42    // We should
43    // (1) either derive `content_encryption_algorithm` from `content_encryptor` (but this is not
44    //            yet supported by RustCrypto),
45    // (2) or     pass `content_encryption_algorithm` and create an encryptor for it.
46    // In the first case, we might need a new trait here, e.g. `DynEncryptionAlgorithmIdentifier` in
47    // analogy to `DynSignatureAlgorithmIdentifier`.
48    // Going for (2)
49    //  content_encryptor: E,
50    content_encryption_algorithm: ContentEncryptionAlgorithmAead,
51    auth_attributes: Option<Attributes>,
52    unauth_attributes: Option<Attributes>,
53}
54
55impl ContentEncryptionAlgorithmAead {
56    /// Return the OID of the algorithm.
57    pub fn oid(&self) -> ObjectIdentifier {
58        match self {
59            ContentEncryptionAlgorithmAead::Aes128Gcm => ID_AES_128_GCM,
60            ContentEncryptionAlgorithmAead::Aes192Gcm => ID_AES_192_GCM,
61            ContentEncryptionAlgorithmAead::Aes256Gcm => ID_AES_256_GCM,
62        }
63    }
64}
65
66impl<'c> AuthEnvelopedDataBuilder<'c> {
67    /// Create a new builder for `AuthEnvelopedData`
68    pub fn new(
69        content_id: Option<ObjectIdentifier>,
70        originator_info: Option<OriginatorInfo>,
71        unencrypted_content: &'c [u8],
72        content_encryption_algorithm: ContentEncryptionAlgorithmAead,
73        auth_attributes: Option<Attributes>,
74        unauth_attributes: Option<Attributes>,
75    ) -> Result<AuthEnvelopedDataBuilder<'c>> {
76        Ok(AuthEnvelopedDataBuilder {
77            content_id,
78            originator_info,
79            recipient_infos: Vec::new(),
80            unencrypted_content,
81            content_encryption_algorithm,
82            auth_attributes,
83            unauth_attributes,
84        })
85    }
86
87    /// Add recipient info. A builder is used, which generates a `RecipientInfo` according to
88    /// RFC 5652 § 6.2, when `AuthEnvelopedData` is built.
89    pub fn add_recipient_info(
90        &mut self,
91        recipient_info_builder: impl RecipientInfoBuilder + 'c,
92    ) -> Result<&mut Self> {
93        self.recipient_infos.push(Box::new(recipient_info_builder));
94        Ok(self)
95    }
96
97    /// Generate an `AuthEnvelopedData` object according to RFC 5083 § 2.2 using a provided
98    /// random number generator.
99    pub fn build(&mut self) -> Result<AuthEnvelopedData> {
100        // DER encode authenticated attributes, if any
101        // Generate content encryption key
102        // Encrypt content and capture authentication tag
103        // Build recipient infos
104        // Make sure, content encryption key is securely destroyed
105        let aad = match &self.auth_attributes {
106            Some(attrs) => Some(attrs.to_der().map_err(|_| QuantCryptError::Unknown)?),
107            None => None,
108        };
109
110        let oid = self.content_encryption_algorithm.oid().to_string();
111        let cea_type = if let Some(oid) = CeaType::from_oid(&oid) {
112            oid
113        } else {
114            return Err(QuantCryptError::Unknown);
115        };
116
117        // Create an instance of CEA
118        let mut cea = CeaManager::new(cea_type)?;
119        // Generate a symmetric key
120        let mut cek = cea.key_gen()?;
121        let nonce = cea.nonce_gen()?;
122
123        // Convert content id to string
124        let content_id = self.content_id.map(|oid| oid.to_string());
125
126        let (tag, eci) = cea.encrypt(
127            &cek,
128            Some(&nonce),
129            self.unencrypted_content,
130            aad.as_deref(),
131            content_id.as_deref(),
132        )?;
133
134        type Result<T> = core::result::Result<T, Error>;
135
136        let recipient_infos_vec = self
137            .recipient_infos
138            .iter_mut()
139            .map(|ri| ri.build(&cek))
140            .collect::<Result<Vec<RecipientInfo>>>()
141            .map_err(|_| QuantCryptError::Unknown)?;
142
143        cek.zeroize();
144        let recip_infos =
145            RecipientInfos::try_from(recipient_infos_vec).map_err(|_| QuantCryptError::Unknown)?;
146
147        let mac = MessageAuthenticationCode::new(tag).map_err(|_| QuantCryptError::Unknown)?;
148
149        let eci = EncryptedContentInfo::from_der(&eci).map_err(|_| QuantCryptError::Unknown)?;
150
151        Ok(AuthEnvelopedData {
152            version: self.calculate_version(),
153            originator_info: self.originator_info.clone(),
154            recip_infos,
155            auth_encrypted_content: eci,
156            auth_attrs: self.auth_attributes.clone(),
157            mac,
158            unauth_attrs: self.unauth_attributes.clone(),
159        })
160    }
161
162    /// Calculate the `CMSVersion` of the `AuthEnvelopedData` according to RFC 5083 § 2.1, i.e., "MUST be set to 0"
163    fn calculate_version(&self) -> CmsVersion {
164        CmsVersion::V0
165    }
166}