mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(ext/crypto): implement AES-CBC encryption & decryption (#12123)
* initial stuff * stuff * merge stuff * cleanup * fmt * length * update lockfile * decrypt * fixy * clippy hello? * hmm * fixs * fix lint * add AesCbcParams * fixes * fixy * lockfile fixy * fix dumb assertions * re run CI * rerun CI * rerun CI
This commit is contained in:
parent
426ebf854a
commit
3b2cb8e711
7 changed files with 258 additions and 6 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -24,6 +24,18 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -262,6 +274,22 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-modes"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
|
||||||
|
dependencies = [
|
||||||
|
"block-padding",
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-padding"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brotli"
|
name = "brotli"
|
||||||
version = "3.3.2"
|
version = "3.3.2"
|
||||||
|
@ -344,6 +372,15 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.33.3"
|
||||||
|
@ -734,6 +771,8 @@ dependencies = [
|
||||||
name = "deno_crypto"
|
name = "deno_crypto"
|
||||||
version = "0.34.0"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"block-modes",
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_web",
|
"deno_web",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -1996,9 +2035,9 @@ checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
|
@ -3899,9 +3938,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.12.5"
|
version = "0.12.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.29",
|
"proc-macro2 1.0.29",
|
||||||
"quote 1.0.10",
|
"quote 1.0.10",
|
||||||
|
|
|
@ -513,6 +513,40 @@ unitTest(async function testHkdfDeriveBits() {
|
||||||
assertEquals(result.byteLength, 128 / 8);
|
assertEquals(result.byteLength, 128 / 8);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unitTest(async function testAesCbcEncryptDecrypt() {
|
||||||
|
const key = await crypto.subtle.generateKey(
|
||||||
|
{ name: "AES-CBC", length: 128 },
|
||||||
|
true,
|
||||||
|
["encrypt", "decrypt"],
|
||||||
|
);
|
||||||
|
|
||||||
|
const iv = await crypto.getRandomValues(new Uint8Array(16));
|
||||||
|
const encrypted = await crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
name: "AES-CBC",
|
||||||
|
iv,
|
||||||
|
},
|
||||||
|
key as CryptoKey,
|
||||||
|
new Uint8Array([1, 2, 3, 4, 5, 6]),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(encrypted instanceof ArrayBuffer);
|
||||||
|
assertEquals(encrypted.byteLength, 16);
|
||||||
|
|
||||||
|
const decrypted = await crypto.subtle.decrypt(
|
||||||
|
{
|
||||||
|
name: "AES-CBC",
|
||||||
|
iv,
|
||||||
|
},
|
||||||
|
key as CryptoKey,
|
||||||
|
encrypted,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(decrypted instanceof ArrayBuffer);
|
||||||
|
assertEquals(decrypted.byteLength, 6);
|
||||||
|
assertEquals(new Uint8Array(decrypted), new Uint8Array([1, 2, 3, 4, 5, 6]));
|
||||||
|
});
|
||||||
|
|
||||||
// TODO(@littledivy): Enable WPT when we have importKey support
|
// TODO(@littledivy): Enable WPT when we have importKey support
|
||||||
unitTest(async function testECDH() {
|
unitTest(async function testECDH() {
|
||||||
const namedCurve = "P-256";
|
const namedCurve = "P-256";
|
||||||
|
|
|
@ -117,9 +117,11 @@
|
||||||
},
|
},
|
||||||
"encrypt": {
|
"encrypt": {
|
||||||
"RSA-OAEP": "RsaOaepParams",
|
"RSA-OAEP": "RsaOaepParams",
|
||||||
|
"AES-CBC": "AesCbcParams",
|
||||||
},
|
},
|
||||||
"decrypt": {
|
"decrypt": {
|
||||||
"RSA-OAEP": "RsaOaepParams",
|
"RSA-OAEP": "RsaOaepParams",
|
||||||
|
"AES-CBC": "AesCbcParams",
|
||||||
},
|
},
|
||||||
"wrapKey": {
|
"wrapKey": {
|
||||||
// TODO(@littledivy): Enable this once implemented.
|
// TODO(@littledivy): Enable this once implemented.
|
||||||
|
@ -440,6 +442,41 @@
|
||||||
// 6.
|
// 6.
|
||||||
return cipherText.buffer;
|
return cipherText.buffer;
|
||||||
}
|
}
|
||||||
|
case "AES-CBC": {
|
||||||
|
if (ArrayBufferIsView(normalizedAlgorithm.iv)) {
|
||||||
|
normalizedAlgorithm.iv = new Uint8Array(
|
||||||
|
normalizedAlgorithm.iv.buffer,
|
||||||
|
normalizedAlgorithm.iv.byteOffset,
|
||||||
|
normalizedAlgorithm.iv.byteLength,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
normalizedAlgorithm.iv = new Uint8Array(
|
||||||
|
normalizedAlgorithm.iv,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
normalizedAlgorithm.iv = TypedArrayPrototypeSlice(
|
||||||
|
normalizedAlgorithm.iv,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
if (normalizedAlgorithm.iv.byteLength !== 16) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Initialization vector must be 16 bytes",
|
||||||
|
"OperationError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const cipherText = await core.opAsync("op_crypto_encrypt_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "AES-CBC",
|
||||||
|
length: key[_algorithm].length,
|
||||||
|
iv: normalizedAlgorithm.iv,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
// 4.
|
||||||
|
return cipherText.buffer;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new DOMException("Not implemented", "NotSupportedError");
|
throw new DOMException("Not implemented", "NotSupportedError");
|
||||||
}
|
}
|
||||||
|
@ -524,6 +561,40 @@
|
||||||
// 6.
|
// 6.
|
||||||
return plainText.buffer;
|
return plainText.buffer;
|
||||||
}
|
}
|
||||||
|
case "AES-CBC": {
|
||||||
|
if (ArrayBufferIsView(normalizedAlgorithm.iv)) {
|
||||||
|
normalizedAlgorithm.iv = new Uint8Array(
|
||||||
|
normalizedAlgorithm.iv.buffer,
|
||||||
|
normalizedAlgorithm.iv.byteOffset,
|
||||||
|
normalizedAlgorithm.iv.byteLength,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
normalizedAlgorithm.iv = new Uint8Array(
|
||||||
|
normalizedAlgorithm.iv,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
normalizedAlgorithm.iv = TypedArrayPrototypeSlice(
|
||||||
|
normalizedAlgorithm.iv,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
if (normalizedAlgorithm.iv.byteLength !== 16) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Counter must be 16 bytes",
|
||||||
|
"OperationError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const plainText = await core.opAsync("op_crypto_decrypt_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "AES-CBC",
|
||||||
|
iv: normalizedAlgorithm.iv,
|
||||||
|
length: key[_algorithm].length,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
// 6.
|
||||||
|
return plainText.buffer;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new DOMException("Not implemented", "NotSupportedError");
|
throw new DOMException("Not implemented", "NotSupportedError");
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,6 +367,18 @@
|
||||||
webidl.converters.Pbkdf2Params = webidl
|
webidl.converters.Pbkdf2Params = webidl
|
||||||
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
|
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
|
||||||
|
|
||||||
|
const dictAesCbcParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "iv",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.AesCbcParams = webidl
|
||||||
|
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);
|
||||||
|
|
||||||
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
||||||
"CryptoKey",
|
"CryptoKey",
|
||||||
CryptoKey,
|
CryptoKey,
|
||||||
|
|
|
@ -14,6 +14,8 @@ description = "Web Cryptography API implementation for Deno"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
aes = "0.7.5"
|
||||||
|
block-modes = "0.8.1"
|
||||||
deno_core = { version = "0.102.0", path = "../../core" }
|
deno_core = { version = "0.102.0", path = "../../core" }
|
||||||
deno_web = { version = "0.51.0", path = "../web" }
|
deno_web = { version = "0.51.0", path = "../web" }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|
8
ext/crypto/lib.deno_crypto.d.ts
vendored
8
ext/crypto/lib.deno_crypto.d.ts
vendored
|
@ -56,6 +56,10 @@ interface JsonWebKey {
|
||||||
y?: string;
|
y?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AesCbcParams extends Algorithm {
|
||||||
|
iv: BufferSource;
|
||||||
|
}
|
||||||
|
|
||||||
interface HmacKeyGenParams extends Algorithm {
|
interface HmacKeyGenParams extends Algorithm {
|
||||||
hash: HashAlgorithmIdentifier;
|
hash: HashAlgorithmIdentifier;
|
||||||
length?: number;
|
length?: number;
|
||||||
|
@ -213,12 +217,12 @@ interface SubtleCrypto {
|
||||||
data: BufferSource,
|
data: BufferSource,
|
||||||
): Promise<ArrayBuffer>;
|
): Promise<ArrayBuffer>;
|
||||||
encrypt(
|
encrypt(
|
||||||
algorithm: AlgorithmIdentifier | RsaOaepParams,
|
algorithm: AlgorithmIdentifier | RsaOaepParams | AesCbcParams,
|
||||||
key: CryptoKey,
|
key: CryptoKey,
|
||||||
data: BufferSource,
|
data: BufferSource,
|
||||||
): Promise<ArrayBuffer>;
|
): Promise<ArrayBuffer>;
|
||||||
decrypt(
|
decrypt(
|
||||||
algorithm: AlgorithmIdentifier | RsaOaepParams,
|
algorithm: AlgorithmIdentifier | RsaOaepParams | AesCbcParams,
|
||||||
key: CryptoKey,
|
key: CryptoKey,
|
||||||
data: BufferSource,
|
data: BufferSource,
|
||||||
): Promise<ArrayBuffer>;
|
): Promise<ArrayBuffer>;
|
||||||
|
|
|
@ -19,6 +19,7 @@ use std::convert::TryInto;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use block_modes::BlockMode;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
@ -892,8 +893,12 @@ pub async fn op_crypto_derive_bits(
|
||||||
pub struct EncryptArg {
|
pub struct EncryptArg {
|
||||||
key: KeyData,
|
key: KeyData,
|
||||||
algorithm: Algorithm,
|
algorithm: Algorithm,
|
||||||
|
// RSA-OAEP
|
||||||
hash: Option<CryptoHash>,
|
hash: Option<CryptoHash>,
|
||||||
label: Option<ZeroCopyBuf>,
|
label: Option<ZeroCopyBuf>,
|
||||||
|
// AES-CBC
|
||||||
|
iv: Option<ZeroCopyBuf>,
|
||||||
|
length: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn op_crypto_encrypt_key(
|
pub async fn op_crypto_encrypt_key(
|
||||||
|
@ -945,6 +950,46 @@ pub async fn op_crypto_encrypt_key(
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Algorithm::AesCbc => {
|
||||||
|
let key = &*args.key.data;
|
||||||
|
let length = args
|
||||||
|
.length
|
||||||
|
.ok_or_else(|| type_error("Missing argument length".to_string()))?;
|
||||||
|
let iv = args
|
||||||
|
.iv
|
||||||
|
.ok_or_else(|| type_error("Missing argument iv".to_string()))?;
|
||||||
|
|
||||||
|
// 2-3.
|
||||||
|
let ciphertext = match length {
|
||||||
|
128 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes128Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes128, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
let cipher = Aes128Cbc::new_from_slices(key, &iv)?;
|
||||||
|
cipher.encrypt_vec(data)
|
||||||
|
}
|
||||||
|
192 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes192Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes192, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
let cipher = Aes192Cbc::new_from_slices(key, &iv)?;
|
||||||
|
cipher.encrypt_vec(data)
|
||||||
|
}
|
||||||
|
256 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes256Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes256, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
let cipher = Aes256Cbc::new_from_slices(key, &iv)?;
|
||||||
|
cipher.encrypt_vec(data)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ciphertext.into())
|
||||||
|
}
|
||||||
_ => Err(type_error("Unsupported algorithm".to_string())),
|
_ => Err(type_error("Unsupported algorithm".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1451,8 +1496,12 @@ pub async fn op_crypto_import_key(
|
||||||
pub struct DecryptArg {
|
pub struct DecryptArg {
|
||||||
key: KeyData,
|
key: KeyData,
|
||||||
algorithm: Algorithm,
|
algorithm: Algorithm,
|
||||||
|
// RSA-OAEP
|
||||||
hash: Option<CryptoHash>,
|
hash: Option<CryptoHash>,
|
||||||
label: Option<ZeroCopyBuf>,
|
label: Option<ZeroCopyBuf>,
|
||||||
|
// AES-CBC
|
||||||
|
iv: Option<ZeroCopyBuf>,
|
||||||
|
length: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn op_crypto_decrypt_key(
|
pub async fn op_crypto_decrypt_key(
|
||||||
|
@ -1503,6 +1552,47 @@ pub async fn op_crypto_decrypt_key(
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Algorithm::AesCbc => {
|
||||||
|
let key = &*args.key.data;
|
||||||
|
let length = args
|
||||||
|
.length
|
||||||
|
.ok_or_else(|| type_error("Missing argument length".to_string()))?;
|
||||||
|
let iv = args
|
||||||
|
.iv
|
||||||
|
.ok_or_else(|| type_error("Missing argument iv".to_string()))?;
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
let plaintext = match length {
|
||||||
|
128 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes128Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes128, block_modes::block_padding::Pkcs7>;
|
||||||
|
let cipher = Aes128Cbc::new_from_slices(key, &iv)?;
|
||||||
|
|
||||||
|
cipher.decrypt_vec(data)?
|
||||||
|
}
|
||||||
|
192 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes192Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes192, block_modes::block_padding::Pkcs7>;
|
||||||
|
let cipher = Aes192Cbc::new_from_slices(key, &iv)?;
|
||||||
|
|
||||||
|
cipher.decrypt_vec(data)?
|
||||||
|
}
|
||||||
|
256 => {
|
||||||
|
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||||
|
type Aes256Cbc =
|
||||||
|
block_modes::Cbc<aes::Aes256, block_modes::block_padding::Pkcs7>;
|
||||||
|
let cipher = Aes256Cbc::new_from_slices(key, &iv)?;
|
||||||
|
|
||||||
|
cipher.decrypt_vec(data)?
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.
|
||||||
|
Ok(plaintext.into())
|
||||||
|
}
|
||||||
_ => Err(type_error("Unsupported algorithm".to_string())),
|
_ => Err(type_error("Unsupported algorithm".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue