mirror of
https://github.com/denoland/deno.git
synced 2025-02-08 07:16:56 -05:00
fix(ext/node): implement aes-128-ctr
, aes-192-ctr
, and aes-256-ctr
(#27630)
Fixes https://github.com/denoland/deno/issues/24864 Need to add some tests, also unsure about the right counter size (went with 128 bit to be safe) --------- Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
This commit is contained in:
parent
0e47205ebe
commit
094e268002
7 changed files with 194 additions and 77 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2047,6 +2047,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"cbc",
|
||||
"const-oid",
|
||||
"ctr",
|
||||
"data-encoding",
|
||||
"deno_core",
|
||||
"deno_error",
|
||||
|
|
|
@ -123,6 +123,7 @@ cbc = { version = "=0.1.2", features = ["alloc"] }
|
|||
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
|
||||
color-print = "0.3.5"
|
||||
console_static_text = "=0.8.1"
|
||||
ctr = { version = "0.9.2", features = ["alloc"] }
|
||||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.1"
|
||||
|
|
|
@ -20,7 +20,7 @@ aes-kw = { version = "0.2.1", features = ["alloc"] }
|
|||
base64.workspace = true
|
||||
cbc.workspace = true
|
||||
const-oid = "0.9.0"
|
||||
ctr = "0.9.1"
|
||||
ctr.workspace = true
|
||||
curve25519-dalek = "4.1.3"
|
||||
deno_core.workspace = true
|
||||
deno_error.workspace = true
|
||||
|
|
|
@ -27,6 +27,7 @@ brotli.workspace = true
|
|||
bytes.workspace = true
|
||||
cbc.workspace = true
|
||||
const-oid = "0.9.5"
|
||||
ctr.workspace = true
|
||||
data-encoding.workspace = true
|
||||
deno_core.workspace = true
|
||||
deno_error.workspace = true
|
||||
|
|
|
@ -9,6 +9,7 @@ use aes::cipher::BlockDecryptMut;
|
|||
use aes::cipher::BlockEncryptMut;
|
||||
use aes::cipher::KeyIvInit;
|
||||
use aes::cipher::KeySizeUser;
|
||||
use aes::cipher::StreamCipher;
|
||||
use deno_core::Resource;
|
||||
use digest::generic_array::GenericArray;
|
||||
use digest::KeyInit;
|
||||
|
@ -26,6 +27,9 @@ enum Cipher {
|
|||
Aes128Gcm(Box<Aes128Gcm>),
|
||||
Aes256Gcm(Box<Aes256Gcm>),
|
||||
Aes256Cbc(Box<cbc::Encryptor<aes::Aes256>>),
|
||||
Aes128Ctr(Box<ctr::Ctr128BE<aes::Aes128>>),
|
||||
Aes192Ctr(Box<ctr::Ctr128BE<aes::Aes192>>),
|
||||
Aes256Ctr(Box<ctr::Ctr128BE<aes::Aes256>>),
|
||||
// TODO(kt3k): add more algorithms Aes192Cbc, etc.
|
||||
}
|
||||
|
||||
|
@ -37,6 +41,9 @@ enum Decipher {
|
|||
Aes128Gcm(Box<Aes128Gcm>),
|
||||
Aes256Gcm(Box<Aes256Gcm>),
|
||||
Aes256Cbc(Box<cbc::Decryptor<aes::Aes256>>),
|
||||
Aes128Ctr(Box<ctr::Ctr128BE<aes::Aes128>>),
|
||||
Aes192Ctr(Box<ctr::Ctr128BE<aes::Aes192>>),
|
||||
Aes256Ctr(Box<ctr::Ctr128BE<aes::Aes256>>),
|
||||
// TODO(kt3k): add more algorithms Aes192Cbc, Aes128GCM, etc.
|
||||
}
|
||||
|
||||
|
@ -220,6 +227,33 @@ impl Cipher {
|
|||
|
||||
Aes256Cbc(Box::new(cbc::Encryptor::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-256-ctr" => {
|
||||
if key.len() != 32 {
|
||||
return Err(CipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(CipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes256Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-192-ctr" => {
|
||||
if key.len() != 24 {
|
||||
return Err(CipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(CipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes192Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-128-ctr" => {
|
||||
if key.len() != 16 {
|
||||
return Err(CipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(CipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes128Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
_ => return Err(CipherError::UnknownCipher(algorithm_name.to_string())),
|
||||
})
|
||||
}
|
||||
|
@ -279,6 +313,15 @@ impl Cipher {
|
|||
encryptor.encrypt_block_b2b_mut(input.into(), output.into());
|
||||
}
|
||||
}
|
||||
Aes256Ctr(encryptor) => {
|
||||
encryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
Aes192Ctr(encryptor) => {
|
||||
encryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
Aes128Ctr(encryptor) => {
|
||||
encryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,6 +402,7 @@ impl Cipher {
|
|||
);
|
||||
Ok(None)
|
||||
}
|
||||
(Aes256Ctr(_) | Aes128Ctr(_) | Aes192Ctr(_), _) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,6 +488,33 @@ impl Decipher {
|
|||
|
||||
Aes256Cbc(Box::new(cbc::Decryptor::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-256-ctr" => {
|
||||
if key.len() != 32 {
|
||||
return Err(DecipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(DecipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes256Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-192-ctr" => {
|
||||
if key.len() != 24 {
|
||||
return Err(DecipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(DecipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes192Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
"aes-128-ctr" => {
|
||||
if key.len() != 16 {
|
||||
return Err(DecipherError::InvalidKeyLength);
|
||||
}
|
||||
if iv.len() != 16 {
|
||||
return Err(DecipherError::InvalidInitializationVector);
|
||||
}
|
||||
Aes128Ctr(Box::new(ctr::Ctr128BE::new(key.into(), iv.into())))
|
||||
}
|
||||
_ => {
|
||||
return Err(DecipherError::UnknownCipher(algorithm_name.to_string()))
|
||||
}
|
||||
|
@ -505,6 +576,15 @@ impl Decipher {
|
|||
decryptor.decrypt_block_b2b_mut(input.into(), output.into());
|
||||
}
|
||||
}
|
||||
Aes256Ctr(decryptor) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
Aes192Ctr(decryptor) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
Aes128Ctr(decryptor) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,6 +695,18 @@ impl Decipher {
|
|||
);
|
||||
Ok(())
|
||||
}
|
||||
(Aes256Ctr(mut decryptor), _) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
(Aes192Ctr(mut decryptor), _) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
(Aes128Ctr(mut decryptor), _) => {
|
||||
decryptor.apply_keystream_b2b(input, output).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,7 +186,9 @@ export class Cipheriv extends Transform implements Cipher {
|
|||
this.#cache = new BlockModeCache(false);
|
||||
this.#context = op_node_create_cipheriv(cipher, toU8(key), toU8(iv));
|
||||
this.#needsBlockCache =
|
||||
!(cipher == "aes-128-gcm" || cipher == "aes-256-gcm");
|
||||
!(cipher == "aes-128-gcm" || cipher == "aes-256-gcm" ||
|
||||
cipher == "aes-128-ctr" || cipher == "aes-192-ctr" ||
|
||||
cipher == "aes-256-ctr");
|
||||
if (this.#context == 0) {
|
||||
throw new TypeError("Unknown cipher");
|
||||
}
|
||||
|
@ -344,7 +346,9 @@ export class Decipheriv extends Transform implements Cipher {
|
|||
this.#cache = new BlockModeCache(this.#autoPadding);
|
||||
this.#context = op_node_create_decipheriv(cipher, toU8(key), toU8(iv));
|
||||
this.#needsBlockCache =
|
||||
!(cipher == "aes-128-gcm" || cipher == "aes-256-gcm");
|
||||
!(cipher == "aes-128-gcm" || cipher == "aes-256-gcm" ||
|
||||
cipher == "aes-128-ctr" || cipher == "aes-192-ctr" ||
|
||||
cipher == "aes-256-ctr");
|
||||
if (this.#context == 0) {
|
||||
throw new TypeError("Unknown cipher");
|
||||
}
|
||||
|
|
|
@ -125,6 +125,21 @@ Deno.test({
|
|||
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b266491",
|
||||
"2e62607a5e8b715e4cb229a12169f2b2",
|
||||
],
|
||||
[
|
||||
["aes-128-ctr", 16, 16],
|
||||
"66e94bd4ef8a2c3b884cfa59ca342b2e58e2fccefa7e3061367f1d57a4e7455a0388dace60b6a392f328c2b971b2fe78f795",
|
||||
"",
|
||||
],
|
||||
[
|
||||
["aes-192-ctr", 24, 16],
|
||||
"aae06992acbf52a3e8f4a96ec9300bd7cd33b28ac773f74ba00ed1f31257243598e7247c07f0fe411c267e4384b0f6002a34",
|
||||
"",
|
||||
],
|
||||
[
|
||||
["aes-256-ctr", 32, 16],
|
||||
"dc95c078a2408989ad48a21492842087530f8afbc74536b9a963b4f1c4cb738bcea7403d4d606b6e074ec5d3baf39d187260",
|
||||
"",
|
||||
],
|
||||
] as const;
|
||||
for (
|
||||
const [[alg, keyLen, ivLen], expectedUpdate, expectedFinal] of table
|
||||
|
@ -217,13 +232,28 @@ Deno.test({
|
|||
["aes-256-cbc", 32, 16],
|
||||
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b2664915d0dcc192580aee9ef8a8568193f1b44bfca557c6bab7dc79da07ffd42191b2659e6bee99cb2a6a7299f0e9a21686fc7",
|
||||
],
|
||||
[
|
||||
["aes-128-ctr", 16, 16],
|
||||
"66e94bd4ef8a2c3b884cfa59ca342b2e58e2fccefa7e3061367f1d57a4e7455a0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0",
|
||||
Buffer.alloc(0),
|
||||
],
|
||||
[
|
||||
["aes-192-ctr", 24, 16],
|
||||
"aae06992acbf52a3e8f4a96ec9300bd7cd33b28ac773f74ba00ed1f31257243598e7247c07f0fe411c267e4384b0f6002a3493e66235ee67deeccd2f3b393bd8fdaa17c2cde20268fe36e164ea532151",
|
||||
Buffer.alloc(0),
|
||||
],
|
||||
[
|
||||
["aes-256-ctr", 32, 16],
|
||||
"dc95c078a2408989ad48a21492842087530f8afbc74536b9a963b4f1c4cb738bcea7403d4d606b6e074ec5d3baf39d18726003ca37a62a74d1a2f58e7506358edd4ab1284d4ae17b41e85924470c36f7",
|
||||
Buffer.alloc(0),
|
||||
],
|
||||
] as const;
|
||||
for (
|
||||
const [[alg, keyLen, ivLen], input] of table
|
||||
const [[alg, keyLen, ivLen], input, final] of table
|
||||
) {
|
||||
const cipher = crypto.createDecipheriv(alg, zeros(keyLen), zeros(ivLen));
|
||||
assertEquals(cipher.update(input, "hex"), Buffer.alloc(80));
|
||||
assertEquals(cipher.final(), Buffer.alloc(10));
|
||||
assertEquals(cipher.final(), final ?? Buffer.alloc(10));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -264,38 +294,36 @@ Deno.test({
|
|||
Deno.test({
|
||||
name: "createCipheriv - invalid inputs",
|
||||
fn() {
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createCipheriv("aes256", new Uint8Array(31), new Uint8Array(16)),
|
||||
RangeError,
|
||||
"Invalid key length",
|
||||
);
|
||||
const enum Invalid {
|
||||
Key,
|
||||
Iv,
|
||||
}
|
||||
const table = [
|
||||
["aes256", 31, 16, Invalid.Key],
|
||||
["aes-256-cbc", 31, 16, Invalid.Key],
|
||||
["aes256", 32, 15, Invalid.Iv],
|
||||
["aes-256-cbc", 32, 15, Invalid.Iv],
|
||||
["aes-128-ctr", 32, 16, Invalid.Key],
|
||||
["aes-128-ctr", 16, 32, Invalid.Iv],
|
||||
["aes-192-ctr", 16, 16, Invalid.Key],
|
||||
["aes-192-ctr", 24, 32, Invalid.Iv],
|
||||
["aes-256-ctr", 16, 16, Invalid.Key],
|
||||
["aes-256-ctr", 32, 32, Invalid.Iv],
|
||||
] as const;
|
||||
for (const [algorithm, keyLen, ivLen, invalid] of table) {
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createCipheriv(
|
||||
"aes-256-cbc",
|
||||
new Uint8Array(31),
|
||||
new Uint8Array(16),
|
||||
algorithm,
|
||||
new Uint8Array(keyLen),
|
||||
new Uint8Array(ivLen),
|
||||
),
|
||||
RangeError,
|
||||
"Invalid key length",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createCipheriv("aes256", new Uint8Array(32), new Uint8Array(15)),
|
||||
TypeError,
|
||||
"Invalid initialization vector",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createCipheriv(
|
||||
"aes-256-cbc",
|
||||
new Uint8Array(32),
|
||||
new Uint8Array(15),
|
||||
),
|
||||
TypeError,
|
||||
"Invalid initialization vector",
|
||||
invalid === Invalid.Key ? RangeError : TypeError,
|
||||
invalid === Invalid.Key
|
||||
? "Invalid key length"
|
||||
: "Invalid initialization vector",
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -314,46 +342,36 @@ Deno.test({
|
|||
Deno.test({
|
||||
name: "createDecipheriv - invalid inputs",
|
||||
fn() {
|
||||
const enum Invalid {
|
||||
Key,
|
||||
Iv,
|
||||
}
|
||||
const table = [
|
||||
["aes256", 31, 16, Invalid.Key],
|
||||
["aes-256-cbc", 31, 16, Invalid.Key],
|
||||
["aes256", 32, 15, Invalid.Iv],
|
||||
["aes-256-cbc", 32, 15, Invalid.Iv],
|
||||
["aes-128-ctr", 32, 16, Invalid.Key],
|
||||
["aes-128-ctr", 16, 32, Invalid.Iv],
|
||||
["aes-192-ctr", 16, 16, Invalid.Key],
|
||||
["aes-192-ctr", 24, 32, Invalid.Iv],
|
||||
["aes-256-ctr", 16, 16, Invalid.Key],
|
||||
["aes-256-ctr", 32, 32, Invalid.Iv],
|
||||
] as const;
|
||||
for (const [algorithm, keyLen, ivLen, invalid] of table) {
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createDecipheriv(
|
||||
"aes256",
|
||||
new Uint8Array(31),
|
||||
new Uint8Array(16),
|
||||
algorithm,
|
||||
new Uint8Array(keyLen),
|
||||
new Uint8Array(ivLen),
|
||||
),
|
||||
RangeError,
|
||||
"Invalid key length",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createDecipheriv(
|
||||
"aes-256-cbc",
|
||||
new Uint8Array(31),
|
||||
new Uint8Array(16),
|
||||
),
|
||||
RangeError,
|
||||
"Invalid key length",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createDecipheriv(
|
||||
"aes256",
|
||||
new Uint8Array(32),
|
||||
new Uint8Array(15),
|
||||
),
|
||||
TypeError,
|
||||
"Invalid initialization vector",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
crypto.createDecipheriv(
|
||||
"aes-256-cbc",
|
||||
new Uint8Array(32),
|
||||
new Uint8Array(15),
|
||||
),
|
||||
TypeError,
|
||||
"Invalid initialization vector",
|
||||
invalid === Invalid.Key ? RangeError : TypeError,
|
||||
invalid === Invalid.Key
|
||||
? "Invalid key length"
|
||||
: "Invalid initialization vector",
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue