Skip to main content

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