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::{Aes128, Aes192, Aes256};
6use cbc::{Decryptor, Encryptor};
7use cipher::block_padding::Pkcs7;
8use cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
9use js::context::JSContext;
10
11use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::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    ExportedKey, SubtleAesCbcParams, SubtleAesDerivedKeyParams, SubtleAesKeyGenParams, aes_common,
20};
21
22/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-encrypt>
23pub(crate) fn encrypt(
24    normalized_algorithm: &SubtleAesCbcParams,
25    key: &CryptoKey,
26    plaintext: &[u8],
27) -> Result<Vec<u8>, Error> {
28    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
29    // throw an OperationError.
30    if normalized_algorithm.iv.len() != 16 {
31        return Err(Error::Operation(Some(
32            "The initialization vector length is not 16 bytes".into(),
33        )));
34    }
35
36    // Step 2. Let paddedPlaintext be the result of adding padding octets to plaintext according to
37    // the procedure defined in Section 10.3 of [RFC2315], step 2, with a value of k of 16.
38    // Step 3. Let ciphertext be the result of performing the CBC Encryption operation described in
39    // Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
40    // normalizedAlgorithm as the IV input parameter and paddedPlaintext as the input plaintext.
41    let iv = normalized_algorithm.iv.as_slice();
42    let ciphertext = match key.handle() {
43        Handle::Aes128Key(key) => {
44            let encryptor = Encryptor::<Aes128>::new(key, iv.into());
45            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
46        },
47        Handle::Aes192Key(key) => {
48            let encryptor = Encryptor::<Aes192>::new(key, iv.into());
49            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
50        },
51        Handle::Aes256Key(key) => {
52            let encryptor = Encryptor::<Aes256>::new(key, iv.into());
53            encryptor.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
54        },
55        _ => {
56            return Err(Error::Operation(Some(
57                "The key handle is not representing an AES key".to_string(),
58            )));
59        },
60    };
61
62    // Step 4. Return ciphertext.
63    Ok(ciphertext)
64}
65
66/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-decrypt>
67pub(crate) fn decrypt(
68    normalized_algorithm: &SubtleAesCbcParams,
69    key: &CryptoKey,
70    ciphertext: &[u8],
71) -> Result<Vec<u8>, Error> {
72    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
73    // throw an OperationError.
74    if normalized_algorithm.iv.len() != 16 {
75        return Err(Error::Operation(Some(
76            "The initialization vector length is not 16 bytes".into(),
77        )));
78    }
79
80    // Step 2. If the length of ciphertext is zero or is not a multiple of 16 bytes, then throw an
81    // OperationError.
82    if ciphertext.is_empty() {
83        return Err(Error::Operation(Some("The ciphertext is empty".into())));
84    }
85    if ciphertext.len() % 16 != 0 {
86        return Err(Error::Operation(Some(
87            "The ciphertext length is not a multiple of 16 bytes".into(),
88        )));
89    }
90
91    // Step 3. Let paddedPlaintext be the result of performing the CBC Decryption operation
92    // described in Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
93    // normalizedAlgorithm as the IV input parameter and ciphertext as the input ciphertext.
94    // Step 4. Let p be the value of the last octet of paddedPlaintext.
95    // Step 5. If p is zero or greater than 16, or if any of the last p octets of paddedPlaintext
96    // have a value which is not p, then throw an OperationError.
97    // Step 6. Let plaintext be the result of removing p octets from the end of paddedPlaintext.
98    let iv = normalized_algorithm.iv.as_slice();
99    let plaintext = match key.handle() {
100        Handle::Aes128Key(key) => {
101            let decryptor = Decryptor::<Aes128>::new(key, iv.into());
102            decryptor
103                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
104                .map_err(|_| {
105                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
106                })?
107        },
108        Handle::Aes192Key(key) => {
109            let decryptor = Decryptor::<Aes192>::new(key, iv.into());
110            decryptor
111                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
112                .map_err(|_| {
113                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
114                })?
115        },
116        Handle::Aes256Key(key) => {
117            let decryptor = Decryptor::<Aes256>::new(key, iv.into());
118            decryptor
119                .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
120                .map_err(|_| {
121                    Error::Operation(Some("Failed to perform AES-CBC decryption".to_string()))
122                })?
123        },
124        _ => {
125            return Err(Error::Operation(Some(
126                "The key handle is not representing an AES key".to_string(),
127            )));
128        },
129    };
130    // Step 7. Return plaintext.
131    Ok(plaintext)
132}
133
134/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-generate-key>
135pub(crate) fn generate_key(
136    cx: &mut JSContext,
137    global: &GlobalScope,
138    normalized_algorithm: &SubtleAesKeyGenParams,
139    extractable: bool,
140    usages: Vec<KeyUsage>,
141) -> Result<DomRoot<CryptoKey>, Error> {
142    aes_common::generate_key(
143        AesAlgorithm::AesCbc,
144        cx,
145        global,
146        normalized_algorithm,
147        extractable,
148        usages,
149    )
150}
151
152/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-import-key>
153pub(crate) fn import_key(
154    cx: &mut JSContext,
155    global: &GlobalScope,
156    format: KeyFormat,
157    key_data: &[u8],
158    extractable: bool,
159    usages: Vec<KeyUsage>,
160) -> Result<DomRoot<CryptoKey>, Error> {
161    aes_common::import_key(
162        AesAlgorithm::AesCbc,
163        cx,
164        global,
165        format,
166        key_data,
167        extractable,
168        usages,
169    )
170}
171
172/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-export-key>
173pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
174    aes_common::export_key(AesAlgorithm::AesCbc, format, key)
175}
176
177/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-get-key-length>
178pub(crate) fn get_key_length(
179    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
180) -> Result<Option<u32>, Error> {
181    aes_common::get_key_length(normalized_derived_key_algorithm)
182}