summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Michael Wykes <8363933+SeanWykes@users.noreply.github.com>2022-01-03 08:27:28 -0300
committerGitHub <noreply@github.com>2022-01-03 12:27:28 +0100
commit9a42d65fc73cea9c8c523a2733d0b180bcdd78e7 (patch)
tree3a2aceb308a5006138b5cb2daab27e8fa5699493
parenta721c34c19a07ece6677f4efc8aa0db881b310f0 (diff)
feat(ext/crypto): support AES-CTR encrypt/decrypt (#13177)
Fixes #13201.
-rw-r--r--Cargo.lock10
-rw-r--r--cli/tests/unit/webcrypto_test.ts111
-rw-r--r--ext/crypto/00_crypto.js68
-rw-r--r--ext/crypto/01_webidl.js18
-rw-r--r--ext/crypto/Cargo.toml1
-rw-r--r--ext/crypto/decrypt.rs75
-rw-r--r--ext/crypto/encrypt.rs77
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts17
-rw-r--r--tools/wpt/expectation.json78
9 files changed, 378 insertions, 77 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 15b7509ec..96909a83e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -558,6 +558,15 @@ dependencies = [
]
[[package]]
+name = "ctr"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -775,6 +784,7 @@ dependencies = [
"aes",
"base64 0.13.0",
"block-modes",
+ "ctr",
"deno_core",
"deno_web",
"elliptic-curve",
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index fec412937..c4958fbdf 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -1,4 +1,9 @@
-import { assert, assertEquals, assertRejects } from "./test_util.ts";
+import {
+ assert,
+ assertEquals,
+ assertNotEquals,
+ assertRejects,
+} from "./test_util.ts";
// https://github.com/denoland/deno/issues/11664
Deno.test(async function testImportArrayBufferKey() {
@@ -608,6 +613,110 @@ Deno.test(async function testAesCbcEncryptDecrypt() {
assertEquals(new Uint8Array(decrypted), new Uint8Array([1, 2, 3, 4, 5, 6]));
});
+Deno.test(async function testAesCtrEncryptDecrypt() {
+ async function aesCtrRoundTrip(
+ key: CryptoKey,
+ counter: Uint8Array,
+ length: number,
+ plainText: Uint8Array,
+ ) {
+ const cipherText = await crypto.subtle.encrypt(
+ {
+ name: "AES-CTR",
+ counter,
+ length,
+ },
+ key,
+ plainText,
+ );
+
+ assert(cipherText instanceof ArrayBuffer);
+ assertEquals(cipherText.byteLength, plainText.byteLength);
+ assertNotEquals(new Uint8Array(cipherText), plainText);
+
+ const decryptedText = await crypto.subtle.decrypt(
+ {
+ name: "AES-CTR",
+ counter,
+ length,
+ },
+ key,
+ cipherText,
+ );
+
+ assert(decryptedText instanceof ArrayBuffer);
+ assertEquals(decryptedText.byteLength, plainText.byteLength);
+ assertEquals(new Uint8Array(decryptedText), plainText);
+ }
+ for (const keySize of [128, 192, 256]) {
+ const key = await crypto.subtle.generateKey(
+ { name: "AES-CTR", length: keySize },
+ true,
+ ["encrypt", "decrypt"],
+ ) as CryptoKey;
+
+ // test normal operation
+ for (const length of [128 /*, 64, 128 */]) {
+ const counter = await crypto.getRandomValues(new Uint8Array(16));
+
+ await aesCtrRoundTrip(
+ key,
+ counter,
+ length,
+ new Uint8Array([1, 2, 3, 4, 5, 6]),
+ );
+ }
+
+ // test counter-wrapping
+ for (const length of [32, 64, 128]) {
+ const plaintext1 = await crypto.getRandomValues(new Uint8Array(32));
+ const counter = new Uint8Array(16);
+
+ // fixed upper part
+ for (let off = 0; off < 16 - (length / 8); ++off) {
+ counter[off] = off;
+ }
+ const ciphertext1 = await crypto.subtle.encrypt(
+ {
+ name: "AES-CTR",
+ counter,
+ length,
+ },
+ key,
+ plaintext1,
+ );
+
+ // Set lower [length] counter bits to all '1's
+ for (let off = 16 - (length / 8); off < 16; ++off) {
+ counter[off] = 0xff;
+ }
+
+ // = [ 1 block of 0x00 + plaintext1 ]
+ const plaintext2 = new Uint8Array(48);
+ plaintext2.set(plaintext1, 16);
+
+ const ciphertext2 = await crypto.subtle.encrypt(
+ {
+ name: "AES-CTR",
+ counter,
+ length,
+ },
+ key,
+ plaintext2,
+ );
+
+ // If counter wrapped, 2nd block of ciphertext2 should be equal to 1st block of ciphertext1
+ // since ciphertext1 used counter = 0x00...00
+ // and ciphertext2 used counter = 0xFF..FF which should wrap to 0x00..00 without affecting
+ // higher bits
+ assertEquals(
+ new Uint8Array(ciphertext1),
+ new Uint8Array(ciphertext2).slice(16),
+ );
+ }
+ }
+});
+
// TODO(@littledivy): Enable WPT when we have importKey support
Deno.test(async function testECDH() {
const namedCurve = "P-256";
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 0a78bff2f..5d216dbf4 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -126,10 +126,12 @@
"encrypt": {
"RSA-OAEP": "RsaOaepParams",
"AES-CBC": "AesCbcParams",
+ "AES-CTR": "AesCtrParams",
},
"decrypt": {
"RSA-OAEP": "RsaOaepParams",
"AES-CBC": "AesCbcParams",
+ "AES-CTR": "AesCtrParams",
},
"get key length": {
"AES-CBC": "AesDerivedKeyParams",
@@ -605,6 +607,39 @@
// 6.
return plainText.buffer;
}
+ case "AES-CTR": {
+ normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter);
+
+ // 1.
+ if (normalizedAlgorithm.counter.byteLength !== 16) {
+ throw new DOMException(
+ "Counter vector must be 16 bytes",
+ "OperationError",
+ );
+ }
+
+ // 2.
+ if (
+ normalizedAlgorithm.length === 0 || normalizedAlgorithm.length > 128
+ ) {
+ throw new DOMException(
+ "Counter length must not be 0 or greater than 128",
+ "OperationError",
+ );
+ }
+
+ // 3.
+ const cipherText = await core.opAsync("op_crypto_decrypt", {
+ key: keyData,
+ algorithm: "AES-CTR",
+ keyLength: key[_algorithm].length,
+ counter: normalizedAlgorithm.counter,
+ ctrLength: normalizedAlgorithm.length,
+ }, data);
+
+ // 4.
+ return cipherText.buffer;
+ }
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
@@ -3431,6 +3466,39 @@
// 4.
return cipherText.buffer;
}
+ case "AES-CTR": {
+ normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter);
+
+ // 1.
+ if (normalizedAlgorithm.counter.byteLength !== 16) {
+ throw new DOMException(
+ "Counter vector must be 16 bytes",
+ "OperationError",
+ );
+ }
+
+ // 2.
+ if (
+ normalizedAlgorithm.length == 0 || normalizedAlgorithm.length > 128
+ ) {
+ throw new DOMException(
+ "Counter length must not be 0 or greater than 128",
+ "OperationError",
+ );
+ }
+
+ // 3.
+ const cipherText = await core.opAsync("op_crypto_encrypt", {
+ key: keyData,
+ algorithm: "AES-CTR",
+ keyLength: key[_algorithm].length,
+ counter: normalizedAlgorithm.counter,
+ ctrLength: normalizedAlgorithm.length,
+ }, data);
+
+ // 4.
+ return cipherText.buffer;
+ }
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
diff --git a/ext/crypto/01_webidl.js b/ext/crypto/01_webidl.js
index a6470ce9e..04315204f 100644
--- a/ext/crypto/01_webidl.js
+++ b/ext/crypto/01_webidl.js
@@ -404,6 +404,24 @@
webidl.converters.AesCbcParams = webidl
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);
+ const dictAesCtrParams = [
+ ...dictAlgorithm,
+ {
+ key: "counter",
+ converter: webidl.converters["BufferSource"],
+ required: true,
+ },
+ {
+ key: "length",
+ converter: (V, opts) =>
+ webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
+ required: true,
+ },
+ ];
+
+ webidl.converters.AesCtrParams = webidl
+ .createDictionaryConverter("AesCtrParams", dictAesCtrParams);
+
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
"CryptoKey",
CryptoKey,
diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml
index 3ab1cc5e8..2ab3ff171 100644
--- a/ext/crypto/Cargo.toml
+++ b/ext/crypto/Cargo.toml
@@ -17,6 +17,7 @@ path = "lib.rs"
aes = "0.7.5"
base64 = "0.13.0"
block-modes = "0.8.1"
+ctr = "0.8.0"
deno_core = { version = "0.112.0", path = "../../core" }
deno_web = { version = "0.61.0", path = "../web" }
elliptic-curve = { version = "0.10.6", features = ["std", "pem"] }
diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs
index f487d7e34..90916f9c3 100644
--- a/ext/crypto/decrypt.rs
+++ b/ext/crypto/decrypt.rs
@@ -2,8 +2,18 @@ use std::cell::RefCell;
use std::rc::Rc;
use crate::shared::*;
+use aes::BlockEncrypt;
+use aes::NewBlockCipher;
use block_modes::BlockMode;
+use ctr::cipher::NewCipher;
+use ctr::cipher::StreamCipher;
+use ctr::flavors::Ctr128BE;
+use ctr::flavors::Ctr32BE;
+use ctr::flavors::Ctr64BE;
+use ctr::flavors::CtrFlavor;
+use ctr::Ctr;
use deno_core::error::custom_error;
+use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
@@ -39,6 +49,13 @@ pub enum DecryptAlgorithm {
iv: Vec<u8>,
length: usize,
},
+ #[serde(rename = "AES-CTR", rename_all = "camelCase")]
+ AesCtr {
+ #[serde(with = "serde_bytes")]
+ counter: Vec<u8>,
+ ctr_length: usize,
+ key_length: usize,
+ },
}
pub async fn op_crypto_decrypt(
@@ -54,6 +71,11 @@ pub async fn op_crypto_decrypt(
DecryptAlgorithm::AesCbc { iv, length } => {
decrypt_aes_cbc(key, length, iv, &data)
}
+ DecryptAlgorithm::AesCtr {
+ counter,
+ ctr_length,
+ key_length,
+ } => decrypt_aes_ctr(key, key_length, &counter, ctr_length, &data),
};
let buf = tokio::task::spawn_blocking(fun).await.unwrap()?;
Ok(buf.into())
@@ -153,3 +175,56 @@ fn decrypt_aes_cbc(
// 6.
Ok(plaintext)
}
+
+fn decrypt_aes_ctr_gen<B, F>(
+ key: &[u8],
+ counter: &[u8],
+ data: &[u8],
+) -> Result<Vec<u8>, AnyError>
+where
+ B: BlockEncrypt + NewBlockCipher,
+ F: CtrFlavor<B::BlockSize>,
+{
+ let mut cipher = Ctr::<B, F>::new(key.into(), counter.into());
+
+ let mut plaintext = data.to_vec();
+ cipher
+ .try_apply_keystream(&mut plaintext)
+ .map_err(|_| operation_error("tried to decrypt too much data"))?;
+
+ Ok(plaintext)
+}
+
+fn decrypt_aes_ctr(
+ key: RawKeyData,
+ key_length: usize,
+ counter: &[u8],
+ ctr_length: usize,
+ data: &[u8],
+) -> Result<Vec<u8>, deno_core::anyhow::Error> {
+ let key = key.as_secret_key()?;
+
+ match ctr_length {
+ 32 => match key_length {
+ 128 => decrypt_aes_ctr_gen::<aes::Aes128, Ctr32BE>(key, counter, data),
+ 192 => decrypt_aes_ctr_gen::<aes::Aes192, Ctr32BE>(key, counter, data),
+ 256 => decrypt_aes_ctr_gen::<aes::Aes256, Ctr32BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ 64 => match key_length {
+ 128 => decrypt_aes_ctr_gen::<aes::Aes128, Ctr64BE>(key, counter, data),
+ 192 => decrypt_aes_ctr_gen::<aes::Aes192, Ctr64BE>(key, counter, data),
+ 256 => decrypt_aes_ctr_gen::<aes::Aes256, Ctr64BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ 128 => match key_length {
+ 128 => decrypt_aes_ctr_gen::<aes::Aes128, Ctr128BE>(key, counter, data),
+ 192 => decrypt_aes_ctr_gen::<aes::Aes192, Ctr128BE>(key, counter, data),
+ 256 => decrypt_aes_ctr_gen::<aes::Aes256, Ctr128BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ _ => Err(type_error(
+ "invalid counter length. Currently supported 32/64/128 bits",
+ )),
+ }
+}
diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs
index 87e3fd2e0..99f4762d0 100644
--- a/ext/crypto/encrypt.rs
+++ b/ext/crypto/encrypt.rs
@@ -2,7 +2,19 @@ use std::cell::RefCell;
use std::rc::Rc;
use crate::shared::*;
+
+use aes::cipher::NewCipher;
+use aes::BlockEncrypt;
+use aes::NewBlockCipher;
+use ctr::Ctr;
+
use block_modes::BlockMode;
+use ctr::cipher::StreamCipher;
+use ctr::flavors::Ctr128BE;
+
+use ctr::flavors::Ctr32BE;
+use ctr::flavors::Ctr64BE;
+use ctr::flavors::CtrFlavor;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::OpState;
@@ -41,6 +53,13 @@ pub enum EncryptAlgorithm {
iv: Vec<u8>,
length: usize,
},
+ #[serde(rename = "AES-CTR", rename_all = "camelCase")]
+ AesCtr {
+ #[serde(with = "serde_bytes")]
+ counter: Vec<u8>,
+ ctr_length: usize,
+ key_length: usize,
+ },
}
pub async fn op_crypto_encrypt(
_state: Rc<RefCell<OpState>>,
@@ -55,6 +74,11 @@ pub async fn op_crypto_encrypt(
EncryptAlgorithm::AesCbc { iv, length } => {
encrypt_aes_cbc(key, length, iv, &data)
}
+ EncryptAlgorithm::AesCtr {
+ counter,
+ ctr_length,
+ key_length,
+ } => encrypt_aes_ctr(key, key_length, &counter, ctr_length, &data),
};
let buf = tokio::task::spawn_blocking(fun).await.unwrap()?;
Ok(buf.into())
@@ -136,3 +160,56 @@ fn encrypt_aes_cbc(
};
Ok(ciphertext)
}
+
+fn encrypt_aes_ctr_gen<B, F>(
+ key: &[u8],
+ counter: &[u8],
+ data: &[u8],
+) -> Result<Vec<u8>, AnyError>
+where
+ B: BlockEncrypt + NewBlockCipher,
+ F: CtrFlavor<B::BlockSize>,
+{
+ let mut cipher = Ctr::<B, F>::new(key.into(), counter.into());
+
+ let mut ciphertext = data.to_vec();
+ cipher
+ .try_apply_keystream(&mut ciphertext)
+ .map_err(|_| operation_error("tried to encrypt too much data"))?;
+
+ Ok(ciphertext)
+}
+
+fn encrypt_aes_ctr(
+ key: RawKeyData,
+ key_length: usize,
+ counter: &[u8],
+ ctr_length: usize,
+ data: &[u8],
+) -> Result<Vec<u8>, AnyError> {
+ let key = key.as_secret_key()?;
+
+ match ctr_length {
+ 32 => match key_length {
+ 128 => encrypt_aes_ctr_gen::<aes::Aes128, Ctr32BE>(key, counter, data),
+ 192 => encrypt_aes_ctr_gen::<aes::Aes192, Ctr32BE>(key, counter, data),
+ 256 => encrypt_aes_ctr_gen::<aes::Aes256, Ctr32BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ 64 => match key_length {
+ 128 => encrypt_aes_ctr_gen::<aes::Aes128, Ctr64BE>(key, counter, data),
+ 192 => encrypt_aes_ctr_gen::<aes::Aes192, Ctr64BE>(key, counter, data),
+ 256 => encrypt_aes_ctr_gen::<aes::Aes256, Ctr64BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ 128 => match key_length {
+ 128 => encrypt_aes_ctr_gen::<aes::Aes128, Ctr128BE>(key, counter, data),
+ 192 => encrypt_aes_ctr_gen::<aes::Aes192, Ctr128BE>(key, counter, data),
+ 256 => encrypt_aes_ctr_gen::<aes::Aes256, Ctr128BE>(key, counter, data),
+ _ => Err(type_error("invalid length")),
+ },
+ _ => Err(type_error(
+ "invalid counter length. Currently supported 32/64/128 bits",
+ )),
+ }
+}
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index 6a3255745..f7d735721 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -62,6 +62,11 @@ interface AesCbcParams extends Algorithm {
iv: BufferSource;
}
+interface AesCtrParams extends Algorithm {
+ counter: BufferSource;
+ length: number;
+}
+
interface HmacKeyGenParams extends Algorithm {
hash: HashAlgorithmIdentifier;
length?: number;
@@ -239,12 +244,20 @@ interface SubtleCrypto {
data: BufferSource,
): Promise<ArrayBuffer>;
encrypt(
- algorithm: AlgorithmIdentifier | RsaOaepParams | AesCbcParams,
+ algorithm:
+ | AlgorithmIdentifier
+ | RsaOaepParams
+ | AesCbcParams
+ | AesCtrParams,
key: CryptoKey,
data: BufferSource,
): Promise<ArrayBuffer>;
decrypt(
- algorithm: AlgorithmIdentifier | RsaOaepParams | AesCbcParams,
+ algorithm:
+ | AlgorithmIdentifier
+ | RsaOaepParams
+ | AesCbcParams
+ | AesCtrParams,
key: CryptoKey,
data: BufferSource,
): Promise<ArrayBuffer>;
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index f4d2869f4..5d6128cb3 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -39,80 +39,10 @@
"digest.https.any.worker.html": true
},
"encrypt_decrypt": {
- "aes_cbc.https.any.html": [
- "AES-CBC 128-bit key with mismatched key and algorithm",
- "AES-CBC 192-bit key with mismatched key and algorithm",
- "AES-CBC 256-bit key with mismatched key and algorithm"
- ],
- "aes_cbc.https.any.worker.html": [
- "AES-CBC 128-bit key with mismatched key and algorithm",
- "AES-CBC 192-bit key with mismatched key and algorithm",
- "AES-CBC 256-bit key with mismatched key and algorithm"
- ],
- "aes_ctr.https.any.html": [
- "AES-CTR 128-bit key",
- "AES-CTR 192-bit key",
- "AES-CTR 256-bit key",
- "AES-CTR 128-bit key with altered plaintext",
- "AES-CTR 192-bit key with altered plaintext",
- "AES-CTR 256-bit key with altered plaintext",
- "AES-CTR 128-bit key decryption",
- "AES-CTR 192-bit key decryption",
- "AES-CTR 256-bit key decryption",
- "AES-CTR 128-bit key decryption with altered ciphertext",
- "AES-CTR 192-bit key decryption with altered ciphertext",
- "AES-CTR 256-bit key decryption with altered ciphertext",
- "AES-CTR 128-bit key without encrypt usage",
- "AES-CTR 192-bit key without encrypt usage",
- "AES-CTR 256-bit key without encrypt usage",
- "AES-CTR 128-bit key without decrypt usage",
- "AES-CTR 192-bit key without decrypt usage",
- "AES-CTR 256-bit key without decrypt usage",
- "AES-CTR 128-bit key, 0-bit counter",
- "AES-CTR 128-bit key, 129-bit counter",
- "AES-CTR 192-bit key, 0-bit counter",
- "AES-CTR 192-bit key, 129-bit counter",
- "AES-CTR 256-bit key, 0-bit counter",
- "AES-CTR 256-bit key, 129-bit counter",
- "AES-CTR 128-bit key, 0-bit counter decryption",
- "AES-CTR 128-bit key, 129-bit counter decryption",
- "AES-CTR 192-bit key, 0-bit counter decryption",
- "AES-CTR 192-bit key, 129-bit counter decryption",
- "AES-CTR 256-bit key, 0-bit counter decryption",
- "AES-CTR 256-bit key, 129-bit counter decryption"
- ],
- "aes_ctr.https.any.worker.html": [
- "AES-CTR 128-bit key",
- "AES-CTR 192-bit key",
- "AES-CTR 256-bit key",
- "AES-CTR 128-bit key with altered plaintext",
- "AES-CTR 192-bit key with altered plaintext",
- "AES-CTR 256-bit key with altered plaintext",
- "AES-CTR 128-bit key decryption",
- "AES-CTR 192-bit key decryption",
- "AES-CTR 256-bit key decryption",
- "AES-CTR 128-bit key decryption with altered ciphertext",
- "AES-CTR 192-bit key decryption with altered ciphertext",
- "AES-CTR 256-bit key decryption with altered ciphertext",
- "AES-CTR 128-bit key without encrypt usage",
- "AES-CTR 192-bit key without encrypt usage",
- "AES-CTR 256-bit key without encrypt usage",
- "AES-CTR 128-bit key without decrypt usage",
- "AES-CTR 192-bit key without decrypt usage",
- "AES-CTR 256-bit key without decrypt usage",
- "AES-CTR 128-bit key, 0-bit counter",
- "AES-CTR 128-bit key, 129-bit counter",
- "AES-CTR 192-bit key, 0-bit counter",
- "AES-CTR 192-bit key, 129-bit counter",
- "AES-CTR 256-bit key, 0-bit counter",
- "AES-CTR 256-bit key, 129-bit counter",
- "AES-CTR 128-bit key, 0-bit counter decryption",
- "AES-CTR 128-bit key, 129-bit counter decryption",
- "AES-CTR 192-bit key, 0-bit counter decryption",
- "AES-CTR 192-bit key, 129-bit counter decryption",
- "AES-CTR 256-bit key, 0-bit counter decryption",
- "AES-CTR 256-bit key, 129-bit counter decryption"
- ],
+ "aes_cbc.https.any.html": true,
+ "aes_cbc.https.any.worker.html": true,
+ "aes_ctr.https.any.html": true,
+ "aes_ctr.https.any.worker.html": true,
"aes_gcm.https.any.html": [
"AES-GCM 128-bit key, 32-bit tag",
"AES-GCM 128-bit key, no additional data, 32-bit tag",