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