diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/crypto/00_crypto.js | 59 | ||||
-rw-r--r-- | ext/crypto/Cargo.toml | 1 | ||||
-rw-r--r-- | ext/crypto/lib.deno_crypto.d.ts | 13 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 75 |
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") } |