script/dom/subtlecrypto/
aes_cbc_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use aes::cipher::crypto_common::Key;
6use aes::{Aes128, Aes192, Aes256};
7use cbc::{Decryptor, Encryptor};
8use cipher::block_padding::Pkcs7;
9use cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
10
11use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
12use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
13use crate::dom::bindings::error::Error;
14use crate::dom::bindings::root::DomRoot;
15use crate::dom::cryptokey::{CryptoKey, Handle};
16use crate::dom::globalscope::GlobalScope;
17use crate::dom::subtlecrypto::aes_common::AesAlgorithm;
18use crate::dom::subtlecrypto::{
19    ALG_AES_CBC, ExportedKey, KeyAlgorithmAndDerivatives, SubtleAesCbcParams,
20    SubtleAesDerivedKeyParams, SubtleAesKeyAlgorithm, SubtleAesKeyGenParams, aes_common,
21};
22use crate::script_runtime::CanGc;
23
24/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-encrypt>
25pub(crate) fn encrypt(
26    normalized_algorithm: &SubtleAesCbcParams,
27    key: &CryptoKey,
28    plaintext: &[u8],
29) -> Result<Vec<u8>, Error> {
30    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
31    // throw an OperationError.
32    if normalized_algorithm.iv.len() != 16 {
33        return Err(Error::Operation(Some(
34            "The initialization vector length is not 16 bytes".into(),
35        )));
36    }
37
38    // Step 2. Let paddedPlaintext be the result of adding padding octets to plaintext according to
39    // the procedure defined in Section 10.3 of [RFC2315], step 2, with a value of k of 16.
40    // Step 3. Let ciphertext be the result of performing the CBC Encryption operation described in
41    // Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
42    // normalizedAlgorithm as the IV input parameter and paddedPlaintext as the input plaintext.
43    let iv = normalized_algorithm.iv.as_slice();
44    let ciphertext = match key.handle() {
45        Handle::Aes128Key(key) => {
46            let encryptor = Encryptor::<Aes128>::new(key, iv.into());
47            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
48        },
49        Handle::Aes192Key(key) => {
50            let encryptor = Encryptor::<Aes192>::new(key, iv.into());
51            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
52        },
53        Handle::Aes256Key(key) => {
54            let encryptor = Encryptor::<Aes256>::new(key, iv.into());
55            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
56        },
57        _ => {
58            return Err(Error::Operation(Some(
59                "The key handle is not representing an AES key".to_string(),
60            )));
61        },
62    };
63
64    // Step 4. Return ciphertext.
65    Ok(ciphertext)
66}
67
68/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-decrypt>
69pub(crate) fn decrypt(
70    normalized_algorithm: &SubtleAesCbcParams,
71    key: &CryptoKey,
72    ciphertext: &[u8],
73) -> Result<Vec<u8>, Error> {
74    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
75    // throw an OperationError.
76    if normalized_algorithm.iv.len() != 16 {
77        return Err(Error::Operation(Some(
78            "The initialization vector length is not 16 bytes".into(),
79        )));
80    }
81
82    // Step 2. If the length of ciphertext is zero or is not a multiple of 16 bytes, then throw an
83    // OperationError.
84    if ciphertext.is_empty() {
85        return Err(Error::Operation(Some("The ciphertext is empty".into())));
86    }
87    if ciphertext.len() % 16 != 0 {
88        return Err(Error::Operation(Some(
89            "The ciphertext length is not a multiple of 16 bytes".into(),
90        )));
91    }
92
93    // Step 3. Let paddedPlaintext be the result of performing the CBC Decryption operation
94    // described in Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
95    // normalizedAlgorithm as the IV input parameter and ciphertext as the input ciphertext.
96    // Step 4. Let p be the value of the last octet of paddedPlaintext.
97    // Step 5. If p is zero or greater than 16, or if any of the last p octets of paddedPlaintext
98    // have a value which is not p, then throw an OperationError.
99    // Step 6. Let plaintext be the result of removing p octets from the end of paddedPlaintext.
100    let iv = normalized_algorithm.iv.as_slice();
101    let plaintext = match key.handle() {
102        Handle::Aes128Key(key) => {
103            let decryptor = Decryptor::<Aes128>::new(key, iv.into());
104            decryptor
105                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
106                .map_err(|_| {
107                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
108                })?
109        },
110        Handle::Aes192Key(key) => {
111            let decryptor = Decryptor::<Aes192>::new(key, iv.into());
112            decryptor
113                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
114                .map_err(|_| {
115                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
116                })?
117        },
118        Handle::Aes256Key(key) => {
119            let decryptor = Decryptor::<Aes256>::new(key, iv.into());
120            decryptor
121                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
122                .map_err(|_| {
123                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
124                })?
125        },
126        _ => {
127            return Err(Error::Operation(Some(
128                "The key handle is not representing an AES key".to_string(),
129            )));
130        },
131    };
132    // Step 7. Return plaintext.
133    Ok(plaintext)
134}
135
136/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-generate-key>
137pub(crate) fn generate_key(
138    global: &GlobalScope,
139    normalized_algorithm: &SubtleAesKeyGenParams,
140    extractable: bool,
141    usages: Vec<KeyUsage>,
142    can_gc: CanGc,
143) -> Result<DomRoot<CryptoKey>, Error> {
144    aes_common::generate_key(
145        AesAlgorithm::AesCbc,
146        global,
147        normalized_algorithm,
148        extractable,
149        usages,
150        can_gc,
151    )
152}
153
154/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-import-key>
155pub(crate) fn import_key(
156    global: &GlobalScope,
157    format: KeyFormat,
158    key_data: &[u8],
159    extractable: bool,
160    usages: Vec<KeyUsage>,
161    can_gc: CanGc,
162) -> Result<DomRoot<CryptoKey>, Error> {
163    // Step 1. Let keyData be the key data to be imported.
164
165    // Step 2. If usages contains an entry which is not one of "encrypt", "decrypt", "wrapKey" or
166    // "unwrapKey", then throw a SyntaxError.
167    if usages.iter().any(|usage| {
168        !matches!(
169            usage,
170            KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
171        )
172    }) {
173        return Err(Error::Syntax(Some(
174            "Usages contains an entry which is not one of \"encrypt\", \"decrypt\", \"wrapKey\" \
175            or \"unwrapKey\""
176                .to_string(),
177        )));
178    }
179
180    // Step 3.
181    let data = aes_common::import_key_from_key_data(
182        AesAlgorithm::AesCbc,
183        format,
184        key_data,
185        extractable,
186        &usages,
187    )?;
188
189    // Step 4. Let key be a new CryptoKey object representing an AES key with value data.
190    // Step 5. Let algorithm be a new AesKeyAlgorithm.
191    // Step 6. Set the name attribute of algorithm to "AES-CBC".
192    // Step 7. Set the length attribute of algorithm to the length, in bits, of data.
193    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
194    let handle = match data.len() {
195        16 => Handle::Aes128Key(Key::<Aes128>::clone_from_slice(&data)),
196        24 => Handle::Aes192Key(Key::<Aes192>::clone_from_slice(&data)),
197        32 => Handle::Aes256Key(Key::<Aes256>::clone_from_slice(&data)),
198        _ => {
199            return Err(Error::Data(Some(
200                "The length in bits of data is not 128, 192 or 256".to_string(),
201            )));
202        },
203    };
204    let algorithm = SubtleAesKeyAlgorithm {
205        name: ALG_AES_CBC.to_string(),
206        length: data.len() as u16 * 8,
207    };
208    let key = CryptoKey::new(
209        global,
210        KeyType::Secret,
211        extractable,
212        KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algorithm),
213        usages,
214        handle,
215        can_gc,
216    );
217
218    // Step 9. Return key.
219    Ok(key)
220}
221
222/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-export-key>
223pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
224    aes_common::export_key(AesAlgorithm::AesCbc, format, key)
225}
226
227/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-get-key-length>
228pub(crate) fn get_key_length(
229    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
230) -> Result<Option<u32>, Error> {
231    aes_common::get_key_length(normalized_derived_key_algorithm)
232}