Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ext/node): add aes256 algorithm support #22198

Merged
merged 5 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions cli/tests/unit_node/crypto/crypto_cipher_test.ts
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I come up with test data for the corresponding createDecipheriv() test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how the test vectors were generated for createDecipheriv().

I think this should work:

# Encrypts null 10byte plaintext using zeroed key and IV and hex encodes it.
echo 0000000000 \
 | openssl enc -e -aes256 -K 0000000000000000000000000000000000000000000000000000000000000000 -iv 00000000000000000000000000000000 \
 | xxd -p

Add the output of the command as the input in the 'aes256' createDecipheriv test

@kt3k Do you know how they were created?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've used the same API of Node.js to create them

Copy link
Collaborator Author

@iuioiua iuioiua Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it using:

const { createCipheriv } = require('node:crypto');

const cipher = createCipheriv("aes256", zeros(32), zeros(16));
const input = cipher.update(zeros(90), undefined, "hex") + cipher.final("hex");

Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ Deno.test({
"dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087",
"0ac1d7e8655254c6814b46753932df88",
],
[
["aes256", 32, 16],
iuioiua marked this conversation as resolved.
Show resolved Hide resolved
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b266491",
"2e62607a5e8b715e4cb229a12169f2b2",
],
[
["aes-256-cbc", 32, 16],
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b266491",
"2e62607a5e8b715e4cb229a12169f2b2",
],
] as const;
for (
const [[alg, keyLen, ivLen], expectedUpdate, expectedFinal] of table
Expand Down Expand Up @@ -186,6 +196,14 @@ Deno.test({
["aes-256-ecb", 32, 0],
"dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a214928420877c45b49560579dd1ffc7ec626de2a968",
],
[
["aes256", 32, 16],
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b2664915d0dcc192580aee9ef8a8568193f1b44bfca557c6bab7dc79da07ffd42191b2659e6bee99cb2a6a7299f0e9a21686fc7",
],
[
["aes-256-cbc", 32, 16],
"dc95c078a2408989ad48a2149284208708c374848c228233c2b34f332bd2e9d38b70c515a6663d38cdb8e6532b2664915d0dcc192580aee9ef8a8568193f1b44bfca557c6bab7dc79da07ffd42191b2659e6bee99cb2a6a7299f0e9a21686fc7",
],
] as const;
for (
const [[alg, keyLen, ivLen], input] of table
Expand Down
37 changes: 35 additions & 2 deletions ext/node/ops/crypto/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ enum Cipher {
Aes256Ecb(Box<ecb::Encryptor<aes::Aes256>>),
Aes128Gcm(Box<Aes128Gcm>),
Aes256Gcm(Box<Aes256Gcm>),
// TODO(kt3k): add more algorithms Aes192Cbc, Aes256Cbc, etc.
Aes256Cbc(Box<cbc::Encryptor<aes::Aes256>>),
// TODO(kt3k): add more algorithms Aes192Cbc, etc.
}

enum Decipher {
Expand All @@ -35,7 +36,8 @@ enum Decipher {
Aes256Ecb(Box<ecb::Decryptor<aes::Aes256>>),
Aes128Gcm(Box<Aes128Gcm>),
Aes256Gcm(Box<Aes256Gcm>),
// TODO(kt3k): add more algorithms Aes192Cbc, Aes256Cbc, Aes128GCM, etc.
Aes256Cbc(Box<cbc::Decryptor<aes::Aes256>>),
// TODO(kt3k): add more algorithms Aes192Cbc, Aes128GCM, etc.
}

pub struct CipherContext {
Expand Down Expand Up @@ -141,6 +143,9 @@ impl Cipher {

Aes256Gcm(Box::new(cipher))
}
"aes256" | "aes-256-cbc" => {
Aes256Cbc(Box::new(cbc::Encryptor::new(key.into(), iv.into())))
}
_ => return Err(type_error(format!("Unknown cipher {algorithm_name}"))),
})
}
Expand Down Expand Up @@ -194,6 +199,12 @@ impl Cipher {
output[..input.len()].copy_from_slice(input);
cipher.encrypt(output);
}
Aes256Cbc(encryptor) => {
assert!(input.len() % 16 == 0);
for (input, output) in input.chunks(16).zip(output.chunks_mut(16)) {
encryptor.encrypt_block_b2b_mut(input.into(), output.into());
}
}
}
}

Expand Down Expand Up @@ -228,6 +239,12 @@ impl Cipher {
}
Aes128Gcm(cipher) => Ok(Some(cipher.finish().to_vec())),
Aes256Gcm(cipher) => Ok(Some(cipher.finish().to_vec())),
Aes256Cbc(encryptor) => {
let _ = (*encryptor)
.encrypt_padded_b2b_mut::<Pkcs7>(input, output)
.map_err(|_| type_error("Cannot pad the input data"))?;
Ok(None)
}
}
}
}
Expand Down Expand Up @@ -260,6 +277,9 @@ impl Decipher {

Aes256Gcm(Box::new(decipher))
}
"aes256" | "aes-256-cbc" => {
Aes256Cbc(Box::new(cbc::Decryptor::new(key.into(), iv.into())))
}
_ => return Err(type_error(format!("Unknown cipher {algorithm_name}"))),
})
}
Expand Down Expand Up @@ -313,6 +333,12 @@ impl Decipher {
output[..input.len()].copy_from_slice(input);
decipher.decrypt(output);
}
Aes256Cbc(decryptor) => {
assert!(input.len() % 16 == 0);
for (input, output) in input.chunks(16).zip(output.chunks_mut(16)) {
decryptor.decrypt_block_b2b_mut(input.into(), output.into());
}
}
}
}

Expand Down Expand Up @@ -369,6 +395,13 @@ impl Decipher {
Err(type_error("Failed to authenticate data"))
}
}
Aes256Cbc(decryptor) => {
assert!(input.len() == 16);
let _ = (*decryptor)
.decrypt_padded_b2b_mut::<Pkcs7>(input, output)
.map_err(|_| type_error("Cannot unpad the input data"))?;
Ok(())
}
}
}
}