summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-01-14 14:18:53 +0530
committerGitHub <noreply@github.com>2022-01-14 14:18:53 +0530
commit919ded1a0b3439ef0d2d3134603bf5840ea0a170 (patch)
tree9ce91bff89b9e3c525f357b4e4b686d5f4edbff0
parenteda6e58520276786bd87e411d0284eb56d9686a6 (diff)
feat(ext/crypto): implement AES-GCM decryption (#13319)
-rw-r--r--cli/tests/unit/webcrypto_test.ts9
-rw-r--r--ext/crypto/00_crypto.js61
-rw-r--r--ext/crypto/decrypt.rs100
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts1
-rw-r--r--tools/wpt/expectation.json124
5 files changed, 173 insertions, 122 deletions
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 3caa927ec..82ecae434 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -1444,6 +1444,15 @@ Deno.test(async function testAesGcmEncrypt() {
// deno-fmt-ignore
new Uint8Array([50,223,112,178,166,156,255,110,125,138,95,141,82,47,14,164,134,247,22]),
);
+
+ const plainText = await crypto.subtle.decrypt(
+ { name: "AES-GCM", iv, additionalData: new Uint8Array() },
+ key,
+ cipherText,
+ );
+ assert(plainText instanceof ArrayBuffer);
+ assertEquals(plainText.byteLength, 3);
+ assertEquals(new Uint8Array(plainText), data);
});
async function roundTripSecretJwk(
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 1f49d1849..81c475ad7 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -133,6 +133,7 @@
"decrypt": {
"RSA-OAEP": "RsaOaepParams",
"AES-CBC": "AesCbcParams",
+ "AES-GCM": "AesGcmParams",
"AES-CTR": "AesCtrParams",
},
"get key length": {
@@ -631,6 +632,66 @@
// 4.
return cipherText.buffer;
}
+ case "AES-GCM": {
+ normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv);
+
+ // 1.
+ 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",
+ );
+ }
+
+ // 2.
+ if (data.byteLength < normalizedAlgorithm.tagLength / 8) {
+ throw new DOMException(
+ "Tag length overflows ciphertext",
+ "OperationError",
+ );
+ }
+
+ // 3. We only support 96-bit nonce for now.
+ if (normalizedAlgorithm.iv.byteLength !== 12) {
+ throw new DOMException(
+ "Initialization vector length not supported",
+ "NotSupportedError",
+ );
+ }
+
+ // 4.
+ if (normalizedAlgorithm.additionalData !== undefined) {
+ if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) {
+ throw new DOMException(
+ "Additional data too large",
+ "OperationError",
+ );
+ }
+ normalizedAlgorithm.additionalData = copyBuffer(
+ normalizedAlgorithm.additionalData,
+ );
+ }
+
+ // 5-8.
+ const plaintext = await core.opAsync("op_crypto_decrypt", {
+ key: keyData,
+ algorithm: "AES-GCM",
+ length: key[_algorithm].length,
+ iv: normalizedAlgorithm.iv,
+ additionalData: normalizedAlgorithm.additionalData,
+ tagLength: normalizedAlgorithm.tagLength,
+ }, data);
+
+ // 9.
+ return plaintext.buffer;
+ }
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs
index 90916f9c3..9f1157608 100644
--- a/ext/crypto/decrypt.rs
+++ b/ext/crypto/decrypt.rs
@@ -2,8 +2,16 @@ use std::cell::RefCell;
use std::rc::Rc;
use crate::shared::*;
+use aes::cipher::generic_array::GenericArray;
+use aes::Aes192;
use aes::BlockEncrypt;
use aes::NewBlockCipher;
+use aes_gcm::AeadCore;
+use aes_gcm::AeadInPlace;
+use aes_gcm::Aes128Gcm;
+use aes_gcm::Aes256Gcm;
+use aes_gcm::NewAead;
+use aes_gcm::Nonce;
use block_modes::BlockMode;
use ctr::cipher::NewCipher;
use ctr::cipher::StreamCipher;
@@ -17,6 +25,7 @@ use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
+use elliptic_curve::consts::U12;
use rsa::pkcs1::FromRsaPrivateKey;
use rsa::PaddingScheme;
use serde::Deserialize;
@@ -56,8 +65,19 @@ pub enum DecryptAlgorithm {
ctr_length: usize,
key_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,
+ },
}
+type Aes192Gcm = aes_gcm::AesGcm<Aes192, U12>;
+
pub async fn op_crypto_decrypt(
_state: Rc<RefCell<OpState>>,
opts: DecryptOptions,
@@ -76,6 +96,12 @@ pub async fn op_crypto_decrypt(
ctr_length,
key_length,
} => decrypt_aes_ctr(key, key_length, &counter, ctr_length, &data),
+ DecryptAlgorithm::AesGcm {
+ iv,
+ additional_data,
+ length,
+ tag_length,
+ } => decrypt_aes_gcm(key, length, tag_length, iv, additional_data, &data),
};
let buf = tokio::task::spawn_blocking(fun).await.unwrap()?;
Ok(buf.into())
@@ -195,6 +221,30 @@ where
Ok(plaintext)
}
+fn decrypt_aes_gcm_gen<B>(
+ key: &[u8],
+ tag: &GenericArray<u8, <B as AeadCore>::TagSize>,
+ nonce: &GenericArray<u8, <B as AeadCore>::NonceSize>,
+ additional_data: Vec<u8>,
+ plaintext: &mut [u8],
+) -> Result<(), AnyError>
+where
+ B: AeadInPlace + NewAead,
+{
+ let cipher =
+ B::new_from_slice(key).map_err(|_| operation_error("Decryption failed"))?;
+ cipher
+ .decrypt_in_place_detached(
+ nonce,
+ additional_data.as_slice(),
+ plaintext,
+ tag,
+ )
+ .map_err(|_| operation_error("Decryption failed"))?;
+
+ Ok(())
+}
+
fn decrypt_aes_ctr(
key: RawKeyData,
key_length: usize,
@@ -228,3 +278,53 @@ fn decrypt_aes_ctr(
)),
}
}
+
+fn decrypt_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 sep = data.len() - (tag_length / 8);
+ let tag = &data[sep..];
+ // The actual ciphertext, called plaintext because it is reused in place.
+ let mut plaintext = data[..sep].to_vec();
+ match length {
+ 128 => decrypt_aes_gcm_gen::<Aes128Gcm>(
+ key,
+ tag.into(),
+ nonce,
+ additional_data,
+ &mut plaintext,
+ )?,
+ 192 => decrypt_aes_gcm_gen::<Aes192Gcm>(
+ key,
+ tag.into(),
+ nonce,
+ additional_data,
+ &mut plaintext,
+ )?,
+ 256 => decrypt_aes_gcm_gen::<Aes256Gcm>(
+ key,
+ tag.into(),
+ nonce,
+ additional_data,
+ &mut plaintext,
+ )?,
+ _ => return Err(type_error("invalid length")),
+ };
+
+ Ok(plaintext)
+}
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index f17c1f582..1f16beb04 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -264,6 +264,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 488b12d34..36dd68307 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 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",
- "AES-GCM 128-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 128-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 32-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 32-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 64-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 128-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 32-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 32-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 64-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 128-bit tag without decrypt usage",
"AES-GCM 128-bit key, illegal tag length 24-bits",
"AES-GCM 128-bit key, illegal tag length 48-bits",
"AES-GCM 128-bit key, illegal tag length 72-bits",
@@ -271,25 +229,7 @@
"AES-GCM 256-bit key, illegal tag length 72-bits",
"AES-GCM 256-bit key, illegal tag length 95-bits",
"AES-GCM 256-bit key, illegal tag length 129-bits",
- "AES-GCM 256-bit key, illegal tag length 256-bits",
- "AES-GCM 128-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 256-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 256-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 256-bits decryption"
+ "AES-GCM 256-bit key, illegal tag length 256-bits"
],
"aes_gcm.https.any.worker.html": [
"AES-GCM 128-bit key, 32-bit tag",
@@ -460,48 +400,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 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",
- "AES-GCM 128-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 128-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 128-bit key, no additional data, 128-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 32-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 32-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 64-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 192-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 192-bit key, no additional data, 128-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 32-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 32-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 64-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 64-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 96-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 96-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 104-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 104-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 112-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 112-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 120-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 120-bit tag without decrypt usage",
- "AES-GCM 256-bit key, 128-bit tag without decrypt usage",
- "AES-GCM 256-bit key, no additional data, 128-bit tag without decrypt usage",
"AES-GCM 128-bit key, illegal tag length 24-bits",
"AES-GCM 128-bit key, illegal tag length 48-bits",
"AES-GCM 128-bit key, illegal tag length 72-bits",
@@ -519,25 +417,7 @@
"AES-GCM 256-bit key, illegal tag length 72-bits",
"AES-GCM 256-bit key, illegal tag length 95-bits",
"AES-GCM 256-bit key, illegal tag length 129-bits",
- "AES-GCM 256-bit key, illegal tag length 256-bits",
- "AES-GCM 128-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 128-bit key, illegal tag length 256-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 192-bit key, illegal tag length 256-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 24-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 48-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 72-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 95-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 129-bits decryption",
- "AES-GCM 256-bit key, illegal tag length 256-bits decryption"
+ "AES-GCM 256-bit key, illegal tag length 256-bits"
],
"rsa_oaep.https.any.html": true,
"rsa_oaep.https.any.worker.html": true