summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock56
-rw-r--r--cli/tests/unit/webcrypto_test.ts28
-rw-r--r--ext/crypto/00_crypto.js65
-rw-r--r--ext/crypto/01_webidl.js31
-rw-r--r--ext/crypto/Cargo.toml1
-rw-r--r--ext/crypto/encrypt.rs82
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts7
-rw-r--r--tools/wpt/expectation.json84
8 files changed, 263 insertions, 91 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f67a8e098..b164192a7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -25,6 +25,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
+name = "aead"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -37,6 +46,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -791,6 +814,7 @@ name = "deno_crypto"
version = "0.44.0"
dependencies = [
"aes",
+ "aes-gcm",
"base64 0.13.0",
"block-modes",
"ctr",
@@ -1615,6 +1639,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "glow"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2692,6 +2726,18 @@ dependencies = [
]
[[package]]
+name = "polyval"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
+dependencies = [
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4560,6 +4606,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
+name = "universal-hash"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
name = "unreachable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 8b5ce55e8..926ed6b6c 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -1418,6 +1418,34 @@ Deno.test(async function testImportEcSpkiPkcs8() {
}
});
+Deno.test(async function testAesGcmEncrypt() {
+ const key = await crypto.subtle.importKey(
+ "raw",
+ new Uint8Array(16),
+ { name: "AES-GCM", length: 256 },
+ true,
+ ["encrypt", "decrypt"],
+ );
+
+ // deno-fmt-ignore
+ const iv = new Uint8Array([0,1,2,3,4,5,6,7,8,9,10,11]);
+ const data = new Uint8Array([1, 2, 3]);
+
+ const cipherText = await crypto.subtle.encrypt(
+ { name: "AES-GCM", iv, additionalData: new Uint8Array() },
+ key,
+ data,
+ );
+
+ assert(cipherText instanceof ArrayBuffer);
+ assertEquals(cipherText.byteLength, 19);
+ assertEquals(
+ new Uint8Array(cipherText),
+ // deno-fmt-ignore
+ new Uint8Array([50,223,112,178,166,156,255,110,125,138,95,141,82,47,14,164,134,247,22]),
+ );
+});
+
async function roundTripSecretJwk(
jwk: JsonWebKey,
algId: AlgorithmIdentifier | HmacImportParams,
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 95eb18daa..f76232136 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -54,6 +54,7 @@
];
const simpleAlgorithmDictionaries = {
+ AesGcmParams: { iv: "BufferSource", additionalData: "BufferSource" },
RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" },
EcKeyGenParams: {},
HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" },
@@ -123,6 +124,7 @@
"encrypt": {
"RSA-OAEP": "RsaOaepParams",
"AES-CBC": "AesCbcParams",
+ "AES-GCM": "AesGcmParams",
"AES-CTR": "AesCtrParams",
},
"decrypt": {
@@ -3502,6 +3504,69 @@
// 4.
return cipherText.buffer;
}
+ case "AES-GCM": {
+ normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv);
+
+ // 1.
+ if (data.byteLength > (2 ** 39) - 256) {
+ throw new DOMException(
+ "Plaintext too large",
+ "OperationError",
+ );
+ }
+
+ // 2.
+ // We only support 96-bit nonce for now.
+ if (normalizedAlgorithm.iv.byteLength !== 12) {
+ throw new DOMException(
+ "Initialization vector length not supported",
+ "NotSupportedError",
+ );
+ }
+
+ // 3.
+ if (normalizedAlgorithm.additionalData !== undefined) {
+ if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) {
+ throw new DOMException(
+ "Additional data too large",
+ "OperationError",
+ );
+ }
+ }
+
+ // 4.
+ if (normalizedAlgorithm.tagLength == undefined) {
+ normalizedAlgorithm.tagLength = 128;
+ } else if (
+ !ArrayPrototypeIncludes(
+ [32, 64, 96, 104, 112, 120, 128],
+ normalizedAlgorithm.tagLength,
+ )
+ ) {
+ throw new DOMException(
+ "Invalid tag length",
+ "OperationError",
+ );
+ }
+ // 5.
+ if (normalizedAlgorithm.additionalData) {
+ normalizedAlgorithm.additionalData = copyBuffer(
+ normalizedAlgorithm.additionalData,
+ );
+ }
+ // 6-7.
+ const cipherText = await core.opAsync("op_crypto_encrypt", {
+ key: keyData,
+ algorithm: "AES-GCM",
+ length: key[_algorithm].length,
+ iv: normalizedAlgorithm.iv,
+ additionalData: normalizedAlgorithm.additionalData,
+ tagLength: normalizedAlgorithm.tagLength,
+ }, data);
+
+ // 8.
+ 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 04315204f..52f212461 100644
--- a/ext/crypto/01_webidl.js
+++ b/ext/crypto/01_webidl.js
@@ -398,11 +398,23 @@
},
];
- webidl.converters.AesDerivedKeyParams = webidl
- .createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
-
- webidl.converters.AesCbcParams = webidl
- .createDictionaryConverter("AesCbcParams", dictAesCbcParams);
+ const dictAesGcmParams = [
+ ...dictAlgorithm,
+ {
+ key: "iv",
+ converter: webidl.converters["BufferSource"],
+ required: true,
+ },
+ {
+ key: "tagLength",
+ converter: (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
+ },
+ {
+ key: "additionalData",
+ converter: webidl.converters["BufferSource"],
+ },
+ ];
const dictAesCtrParams = [
...dictAlgorithm,
@@ -419,6 +431,15 @@
},
];
+ webidl.converters.AesDerivedKeyParams = webidl
+ .createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
+
+ webidl.converters.AesCbcParams = webidl
+ .createDictionaryConverter("AesCbcParams", dictAesCbcParams);
+
+ webidl.converters.AesGcmParams = webidl
+ .createDictionaryConverter("AesGcmParams", dictAesGcmParams);
+
webidl.converters.AesCtrParams = webidl
.createDictionaryConverter("AesCtrParams", dictAesCtrParams);
diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml
index 2ab3ff171..26448c1a0 100644
--- a/ext/crypto/Cargo.toml
+++ b/ext/crypto/Cargo.toml
@@ -15,6 +15,7 @@ path = "lib.rs"
[dependencies]
aes = "0.7.5"
+aes-gcm = "0.9.4"
base64 = "0.13.0"
block-modes = "0.8.1"
ctr = "0.8.0"
diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs
index 99f4762d0..f1d88438b 100644
--- a/ext/crypto/encrypt.rs
+++ b/ext/crypto/encrypt.rs
@@ -6,6 +6,13 @@ use crate::shared::*;
use aes::cipher::NewCipher;
use aes::BlockEncrypt;
use aes::NewBlockCipher;
+use aes_gcm::aead::generic_array::typenum::U12;
+use aes_gcm::aes::Aes192;
+use aes_gcm::AeadInPlace;
+use aes_gcm::Aes128Gcm;
+use aes_gcm::Aes256Gcm;
+use aes_gcm::NewAead;
+use aes_gcm::Nonce;
use ctr::Ctr;
use block_modes::BlockMode;
@@ -53,6 +60,15 @@ pub enum EncryptAlgorithm {
iv: Vec<u8>,
length: usize,
},
+ #[serde(rename = "AES-GCM", rename_all = "camelCase")]
+ AesGcm {
+ #[serde(with = "serde_bytes")]
+ iv: Vec<u8>,
+ #[serde(with = "serde_bytes")]
+ additional_data: Option<Vec<u8>>,
+ length: usize,
+ tag_length: usize,
+ },
#[serde(rename = "AES-CTR", rename_all = "camelCase")]
AesCtr {
#[serde(with = "serde_bytes")]
@@ -74,6 +90,12 @@ pub async fn op_crypto_encrypt(
EncryptAlgorithm::AesCbc { iv, length } => {
encrypt_aes_cbc(key, length, iv, &data)
}
+ EncryptAlgorithm::AesGcm {
+ iv,
+ additional_data,
+ length,
+ tag_length,
+ } => encrypt_aes_gcm(key, length, tag_length, iv, additional_data, &data),
EncryptAlgorithm::AesCtr {
counter,
ctr_length,
@@ -89,7 +111,7 @@ fn encrypt_rsa_oaep(
hash: ShaHash,
label: Vec<u8>,
data: &[u8],
-) -> Result<Vec<u8>, deno_core::anyhow::Error> {
+) -> Result<Vec<u8>, AnyError> {
let label = String::from_utf8_lossy(&label).to_string();
let public_key = key.as_rsa_public_key()?;
@@ -129,7 +151,7 @@ fn encrypt_aes_cbc(
length: usize,
iv: Vec<u8>,
data: &[u8],
-) -> Result<Vec<u8>, deno_core::anyhow::Error> {
+) -> Result<Vec<u8>, AnyError> {
let key = key.as_secret_key()?;
let ciphertext = match length {
128 => {
@@ -161,6 +183,62 @@ fn encrypt_aes_cbc(
Ok(ciphertext)
}
+fn encrypt_aes_gcm(
+ key: RawKeyData,
+ length: usize,
+ tag_length: usize,
+ iv: Vec<u8>,
+ additional_data: Option<Vec<u8>>,
+ data: &[u8],
+) -> Result<Vec<u8>, AnyError> {
+ let key = key.as_secret_key()?;
+ let additional_data = additional_data.unwrap_or_default();
+
+ // Fixed 96-bit nonce
+ if iv.len() != 12 {
+ return Err(type_error("iv length not equal to 12"));
+ }
+
+ let nonce = Nonce::from_slice(&iv);
+
+ let mut ciphertext = data.to_vec();
+ let tag = match length {
+ 128 => {
+ let cipher = Aes128Gcm::new_from_slice(key)
+ .map_err(|_| operation_error("Encryption failed"))?;
+ cipher
+ .encrypt_in_place_detached(nonce, &additional_data, &mut ciphertext)
+ .map_err(|_| operation_error("Encryption failed"))?
+ }
+ 192 => {
+ type Aes192Gcm = aes_gcm::AesGcm<Aes192, U12>;
+
+ let cipher = Aes192Gcm::new_from_slice(key)
+ .map_err(|_| operation_error("Encryption failed"))?;
+ cipher
+ .encrypt_in_place_detached(nonce, &additional_data, &mut ciphertext)
+ .map_err(|_| operation_error("Encryption failed"))?
+ }
+ 256 => {
+ let cipher = Aes256Gcm::new_from_slice(key)
+ .map_err(|_| operation_error("Encryption failed"))?;
+ cipher
+ .encrypt_in_place_detached(nonce, &additional_data, &mut ciphertext)
+ .map_err(|_| operation_error("Encryption failed"))?
+ }
+ _ => return Err(type_error("invalid length")),
+ };
+
+ // Truncated tag to the specified tag length.
+ // `tag` is fixed to be 16 bytes long and (tag_length / 8) is always <= 16
+ let tag = &tag[..(tag_length / 8)];
+
+ // C | T
+ ciphertext.extend_from_slice(tag);
+
+ Ok(ciphertext)
+}
+
fn encrypt_aes_ctr_gen<B, F>(
key: &[u8],
counter: &[u8],
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index f7d735721..9c7386dc9 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -62,6 +62,12 @@ interface AesCbcParams extends Algorithm {
iv: BufferSource;
}
+interface AesGcmParams extends Algorithm {
+ iv: BufferSource;
+ additionalData?: BufferSource;
+ tagLength?: number;
+}
+
interface AesCtrParams extends Algorithm {
counter: BufferSource;
length: number;
@@ -248,6 +254,7 @@ interface SubtleCrypto {
| AlgorithmIdentifier
| RsaOaepParams
| AesCbcParams
+ | AesGcmParams
| AesCtrParams,
key: CryptoKey,
data: BufferSource,
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index 5d6128cb3..96944aff4 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -212,48 +212,6 @@
"AES-GCM 256-bit key, no additional data, 120-bit tag decryption with altered ciphertext",
"AES-GCM 256-bit key, 128-bit tag decryption with altered ciphertext",
"AES-GCM 256-bit key, no additional data, 128-bit tag decryption with altered ciphertext",
- "AES-GCM 128-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 128-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 128-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 128-bit tag without encrypt usage",
"AES-GCM 128-bit key, 32-bit tag without decrypt usage",
"AES-GCM 128-bit key, no additional data, 32-bit tag without decrypt usage",
"AES-GCM 128-bit key, 64-bit tag without decrypt usage",
@@ -502,48 +460,6 @@
"AES-GCM 256-bit key, no additional data, 120-bit tag decryption with altered ciphertext",
"AES-GCM 256-bit key, 128-bit tag decryption with altered ciphertext",
"AES-GCM 256-bit key, no additional data, 128-bit tag decryption with altered ciphertext",
- "AES-GCM 128-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 128-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 128-bit key, no additional data, 128-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 192-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 192-bit key, no additional data, 128-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 32-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 32-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 64-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 64-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 96-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 96-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 104-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 104-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 112-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 112-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 120-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 120-bit tag without encrypt usage",
- "AES-GCM 256-bit key, 128-bit tag without encrypt usage",
- "AES-GCM 256-bit key, no additional data, 128-bit tag without encrypt usage",
"AES-GCM 128-bit key, 32-bit tag without decrypt usage",
"AES-GCM 128-bit key, no additional data, 32-bit tag without decrypt usage",
"AES-GCM 128-bit key, 64-bit tag without decrypt usage",