summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/crypto/00_crypto.js59
-rw-r--r--ext/crypto/Cargo.toml1
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts13
-rw-r--r--ext/crypto/lib.rs75
4 files changed, 130 insertions, 18 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 880457416..1f49d1849 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -145,12 +145,10 @@
"PBKDF2": null,
},
"wrapKey": {
- // TODO(@littledivy): Enable this once implemented.
- // "AES-KW": "AesKeyWrapParams",
+ "AES-KW": null,
},
"unwrapKey": {
- // TODO(@littledivy): Enable this once implemented.
- // "AES-KW": "AesKeyWrapParams",
+ "AES-KW": null,
},
};
@@ -1271,14 +1269,30 @@
if (
supportedAlgorithms["wrapKey"][normalizedAlgorithm.name] !== undefined
) {
- // TODO(@littledivy): Implement this for AES-KW.
- throw new DOMException(
- "Not implemented",
- "NotSupportedError",
- );
+ const handle = wrappingKey[_handle];
+ const keyData = WeakMapPrototypeGet(KEY_STORE, handle);
+
+ switch (normalizedAlgorithm.name) {
+ case "AES-KW": {
+ const cipherText = await core.opSync("op_crypto_wrap_key", {
+ key: keyData,
+ algorithm: normalizedAlgorithm.name,
+ }, bytes);
+
+ // 4.
+ return cipherText.buffer;
+ }
+ default: {
+ throw new DOMException(
+ "Not implemented",
+ "NotSupportedError",
+ );
+ }
+ }
} else if (
supportedAlgorithms["encrypt"][normalizedAlgorithm.name] !== undefined
) {
+ // must construct a new key, since keyUsages is ["wrapKey"] and not ["encrypt"]
return await encrypt(
normalizedAlgorithm,
constructKey(
@@ -1391,14 +1405,31 @@
if (
supportedAlgorithms["unwrapKey"][normalizedAlgorithm.name] !== undefined
) {
- // TODO(@littledivy): Implement this for AES-KW.
- throw new DOMException(
- "Not implemented",
- "NotSupportedError",
- );
+ const handle = unwrappingKey[_handle];
+ const keyData = WeakMapPrototypeGet(KEY_STORE, handle);
+
+ switch (normalizedAlgorithm.name) {
+ case "AES-KW": {
+ const plainText = await core.opSync("op_crypto_unwrap_key", {
+ key: keyData,
+ algorithm: normalizedAlgorithm.name,
+ }, wrappedKey);
+
+ // 4.
+ key = plainText.buffer;
+ break;
+ }
+ default: {
+ throw new DOMException(
+ "Not implemented",
+ "NotSupportedError",
+ );
+ }
+ }
} else if (
supportedAlgorithms["decrypt"][normalizedAlgorithm.name] !== undefined
) {
+ // must construct a new key, since keyUsages is ["unwrapKey"] and not ["decrypt"]
key = await this.decrypt(
normalizedAlgorithm,
constructKey(
diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml
index a387644bf..f2652b091 100644
--- a/ext/crypto/Cargo.toml
+++ b/ext/crypto/Cargo.toml
@@ -16,6 +16,7 @@ path = "lib.rs"
[dependencies]
aes = "0.7.5"
aes-gcm = "0.9.4"
+aes-kw = { version = "0.1", features = ["alloc"] }
base64 = "0.13.0"
block-modes = "0.8.1"
ctr = "0.8.0"
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index 9c7386dc9..f17c1f582 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -293,7 +293,11 @@ interface SubtleCrypto {
format: KeyFormat,
key: CryptoKey,
wrappingKey: CryptoKey,
- wrapAlgorithm: AlgorithmIdentifier | RsaOaepParams,
+ wrapAlgorithm:
+ | AlgorithmIdentifier
+ | RsaOaepParams
+ | AesCbcParams
+ | AesCtrParams,
): Promise<ArrayBuffer>;
unwrapKey(
format: KeyFormat,
@@ -302,12 +306,13 @@ interface SubtleCrypto {
unwrapAlgorithm:
| AlgorithmIdentifier
| RsaOaepParams
- | AesCbcParams,
+ | AesCbcParams
+ | AesCtrParams,
unwrappedKeyAlgorithm:
| AlgorithmIdentifier
- | RsaHashedImportParams
| HmacImportParams
- | AesKeyAlgorithm,
+ | RsaHashedImportParams
+ | EcImportParams,
extractable: boolean,
keyUsages: KeyUsage[],
): Promise<CryptoKey>;
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs
index 8cbd399ee..0331d0dc3 100644
--- a/ext/crypto/lib.rs
+++ b/ext/crypto/lib.rs
@@ -1,5 +1,9 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use aes_kw::KekAes128;
+use aes_kw::KekAes192;
+use aes_kw::KekAes256;
+
use deno_core::error::custom_error;
use deno_core::error::not_supported;
use deno_core::error::type_error;
@@ -11,6 +15,7 @@ use deno_core::Extension;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
+use shared::operation_error;
use std::cell::RefCell;
use std::num::NonZeroU32;
@@ -47,6 +52,7 @@ use sha2::Digest;
use sha2::Sha256;
use sha2::Sha384;
use sha2::Sha512;
+use std::convert::TryFrom;
use std::path::PathBuf;
pub use rand; // Re-export rand
@@ -68,6 +74,7 @@ use crate::key::Algorithm;
use crate::key::CryptoHash;
use crate::key::CryptoNamedCurve;
use crate::key::HkdfOutput;
+use crate::shared::RawKeyData;
use crate::shared::ID_MFG1;
use crate::shared::ID_P_SPECIFIED;
use crate::shared::ID_SHA1_OID;
@@ -95,6 +102,8 @@ pub fn init(maybe_seed: Option<u64>) -> Extension {
("op_crypto_decrypt", op_async(op_crypto_decrypt)),
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
+ ("op_crypto_wrap_key", op_sync(op_crypto_wrap_key)),
+ ("op_crypto_unwrap_key", op_sync(op_crypto_unwrap_key)),
])
.state(move |state| {
if let Some(seed) = maybe_seed {
@@ -815,6 +824,72 @@ pub async fn op_crypto_subtle_digest(
Ok(output)
}
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct WrapUnwrapKeyArg {
+ key: RawKeyData,
+ algorithm: Algorithm,
+}
+
+pub fn op_crypto_wrap_key(
+ _state: &mut OpState,
+ args: WrapUnwrapKeyArg,
+ data: ZeroCopyBuf,
+) -> Result<ZeroCopyBuf, AnyError> {
+ let algorithm = args.algorithm;
+
+ match algorithm {
+ Algorithm::AesKw => {
+ let key = args.key.as_secret_key()?;
+
+ if data.len() % 8 != 0 {
+ return Err(type_error("Data must be multiple of 8 bytes"));
+ }
+
+ let wrapped_key = match key.len() {
+ 16 => KekAes128::new(key.into()).wrap_vec(&data),
+ 24 => KekAes192::new(key.into()).wrap_vec(&data),
+ 32 => KekAes256::new(key.into()).wrap_vec(&data),
+ _ => return Err(type_error("Invalid key length")),
+ }
+ .map_err(|_| operation_error("encryption error"))?;
+
+ Ok(wrapped_key.into())
+ }
+ _ => Err(type_error("Unsupported algorithm")),
+ }
+}
+
+pub fn op_crypto_unwrap_key(
+ _state: &mut OpState,
+ args: WrapUnwrapKeyArg,
+ data: ZeroCopyBuf,
+) -> Result<ZeroCopyBuf, AnyError> {
+ let algorithm = args.algorithm;
+ match algorithm {
+ Algorithm::AesKw => {
+ let key = args.key.as_secret_key()?;
+
+ if data.len() % 8 != 0 {
+ return Err(type_error("Data must be multiple of 8 bytes"));
+ }
+
+ let unwrapped_key = match key.len() {
+ 16 => KekAes128::new(key.into()).unwrap_vec(&data),
+ 24 => KekAes192::new(key.into()).unwrap_vec(&data),
+ 32 => KekAes256::new(key.into()).unwrap_vec(&data),
+ _ => return Err(type_error("Invalid key length")),
+ }
+ .map_err(|_| {
+ operation_error("decryption error - integrity check failed")
+ })?;
+
+ Ok(unwrapped_key.into())
+ }
+ _ => Err(type_error("Unsupported algorithm")),
+ }
+}
+
pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_crypto.d.ts")
}