diff options
author | Sean Michael Wykes <8363933+SeanWykes@users.noreply.github.com> | 2022-01-03 08:27:28 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-03 12:27:28 +0100 |
commit | 9a42d65fc73cea9c8c523a2733d0b180bcdd78e7 (patch) | |
tree | 3a2aceb308a5006138b5cb2daab27e8fa5699493 /ext/crypto | |
parent | a721c34c19a07ece6677f4efc8aa0db881b310f0 (diff) |
feat(ext/crypto): support AES-CTR encrypt/decrypt (#13177)
Fixes #13201.
Diffstat (limited to 'ext/crypto')
-rw-r--r-- | ext/crypto/00_crypto.js | 68 | ||||
-rw-r--r-- | ext/crypto/01_webidl.js | 18 | ||||
-rw-r--r-- | ext/crypto/Cargo.toml | 1 | ||||
-rw-r--r-- | ext/crypto/decrypt.rs | 75 | ||||
-rw-r--r-- | ext/crypto/encrypt.rs | 77 | ||||
-rw-r--r-- | ext/crypto/lib.deno_crypto.d.ts | 17 |
6 files changed, 254 insertions, 2 deletions
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>; |