Skip to main content

script/dom/webcrypto/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::{Aes128, Aes192, Aes256};
6use ctr::Ctr128BE;
7use ctr::cipher::StreamCipher;
8use ctr::cipher::common::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, SubtleAesCtrParams, SubtleAesDerivedKeyParams, SubtleAesKeyGenParams, aes_common,
20};
21
22/// Use aes::Ctr128BE by default. According to the WebCrypto API specification, the counter MUST be
23/// 16 bytes (the AES block size), and the counter bits are interpreted as a big-endian integer.
24type Ctr<T> = Ctr128BE<T>;
25
26/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-encrypt>
27pub(crate) fn encrypt(
28    normalized_algorithm: &SubtleAesCtrParams,
29    key: &CryptoKey,
30    plaintext: &[u8],
31) -> Result<Vec<u8>, Error> {
32    // Step 1. If the counter member of normalizedAlgorithm does not have a length of 16 bytes,
33    // then throw an OperationError.
34    if normalized_algorithm.counter.len() != 16 {
35        return Err(Error::Operation(Some(
36            "The initial counter block length is not 16 bytes".into(),
37        )));
38    }
39
40    // Step 2. If the length member of normalizedAlgorithm is zero or is greater than 128, then
41    // throw an OperationError.
42    if normalized_algorithm.length == 0 {
43        return Err(Error::Operation(Some("The counter length is zero".into())));
44    }
45    if normalized_algorithm.length > 128 {
46        return Err(Error::Operation(Some(
47            "The counter length is greater than 128".into(),
48        )));
49    }
50
51    // Step 3. Let ciphertext be the result of performing the CTR Encryption operation described in
52    // Section 6.5 of [NIST-SP800-38A] using AES as the block cipher, the counter member of
53    // normalizedAlgorithm as the initial value of the counter block, the length member of
54    // normalizedAlgorithm as the input parameter m to the standard counter block incrementing
55    // function defined in Appendix B.1 of [NIST-SP800-38A] and plaintext as the input plaintext.
56    let iv = normalized_algorithm
57        .counter
58        .as_slice()
59        .try_into()
60        .map_err(|_| Error::Operation(Some("Invalid AES-CTR counter".into())))?;
61    let mut ciphertext = plaintext.to_vec();
62    match key.handle() {
63        Handle::Aes128Key(key) => {
64            let mut cipher = Ctr::<Aes128>::new(key, iv);
65            cipher.apply_keystream(&mut ciphertext);
66        },
67        Handle::Aes192Key(key) => {
68            let mut cipher = Ctr::<Aes192>::new(key, iv);
69            cipher.apply_keystream(&mut ciphertext);
70        },
71        Handle::Aes256Key(key) => {
72            let mut cipher = Ctr::<Aes256>::new(key, iv);
73            cipher.apply_keystream(&mut ciphertext);
74        },
75        _ => {
76            return Err(Error::Operation(Some(
77                "The key handle is not representing an AES key".to_string(),
78            )));
79        },
80    };
81
82    // Step 4. Return ciphertext.
83    Ok(ciphertext)
84}
85
86/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-decrypt>
87pub(crate) fn decrypt(
88    normalized_algorithm: &SubtleAesCtrParams,
89    key: &CryptoKey,
90    ciphertext: &[u8],
91) -> Result<Vec<u8>, Error> {
92    // Step 1. If the counter member of normalizedAlgorithm does not have a length of 16 bytes,
93    // then throw an OperationError.
94    if normalized_algorithm.counter.len() != 16 {
95        return Err(Error::Operation(Some(
96            "The initial counter block length is not 16 bytes".into(),
97        )));
98    }
99
100    // Step 2. If the length member of normalizedAlgorithm is zero or is greater than 128, then
101    // throw an OperationError.
102    if normalized_algorithm.length == 0 {
103        return Err(Error::Operation(Some("The counter length is zero".into())));
104    }
105    if normalized_algorithm.length > 128 {
106        return Err(Error::Operation(Some(
107            "The counter length is greater than 128".into(),
108        )));
109    }
110
111    // Step 3. Let plaintext be the result of performing the CTR Decryption operation described in
112    // Section 6.5 of [NIST-SP800-38A] using AES as the block cipher, the counter member of
113    // normalizedAlgorithm as the initial value of the counter block, the length member of
114    // normalizedAlgorithm as the input parameter m to the standard counter block incrementing
115    // function defined in Appendix B.1 of [NIST-SP800-38A] and ciphertext as the input ciphertext.
116    let iv = normalized_algorithm
117        .counter
118        .as_slice()
119        .try_into()
120        .map_err(|_| Error::Operation(Some("Invalid AES-CTR counter".into())))?;
121    let mut plaintext = ciphertext.to_vec();
122    match key.handle() {
123        Handle::Aes128Key(key) => {
124            let mut cipher = Ctr::<Aes128>::new(key, &iv);
125            cipher.apply_keystream(&mut plaintext);
126        },
127        Handle::Aes192Key(key) => {
128            let mut cipher = Ctr::<Aes192>::new(key, &iv);
129            cipher.apply_keystream(&mut plaintext);
130        },
131        Handle::Aes256Key(key) => {
132            let mut cipher = Ctr::<Aes256>::new(key, &iv);
133            cipher.apply_keystream(&mut plaintext);
134        },
135        _ => {
136            return Err(Error::Operation(Some(
137                "The key handle is not representing an AES key".to_string(),
138            )));
139        },
140    };
141
142    // Step 4. Return plaintext.
143    Ok(plaintext)
144}
145
146/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-generate-key>
147pub(crate) fn generate_key(
148    cx: &mut JSContext,
149    global: &GlobalScope,
150    normalized_algorithm: &SubtleAesKeyGenParams,
151    extractable: bool,
152    usages: Vec<KeyUsage>,
153) -> Result<DomRoot<CryptoKey>, Error> {
154    aes_common::generate_key(
155        AesAlgorithm::AesCtr,
156        cx,
157        global,
158        normalized_algorithm,
159        extractable,
160        usages,
161    )
162}
163
164/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-import-key>
165pub(crate) fn import_key(
166    cx: &mut JSContext,
167    global: &GlobalScope,
168    format: KeyFormat,
169    key_data: &[u8],
170    extractable: bool,
171    usages: Vec<KeyUsage>,
172) -> Result<DomRoot<CryptoKey>, Error> {
173    aes_common::import_key(
174        AesAlgorithm::AesCtr,
175        cx,
176        global,
177        format,
178        key_data,
179        extractable,
180        usages,
181    )
182}
183
184/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-export-key>
185pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
186    aes_common::export_key(AesAlgorithm::AesCtr, format, key)
187}
188
189/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-get-key-length>
190pub(crate) fn get_key_length(
191    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
192) -> Result<Option<u32>, Error> {
193    aes_common::get_key_length(normalized_derived_key_algorithm)
194}