From cbfc8dd59d79fa6e8c5a59ca97508ea4285ff155 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Fri, 10 Dec 2021 22:23:19 +0100 Subject: [PATCH] refactor(ext/crypto): symmetric jwk decode in rust (#13047) --- Cargo.lock | 1 + ext/crypto/00_crypto.js | 35 ++++++++++++++--------------------- ext/crypto/Cargo.toml | 1 + ext/crypto/import_key.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de0095e967..05af039c8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,6 +767,7 @@ name = "deno_crypto" version = "0.42.0" dependencies = [ "aes", + "base64 0.13.0", "block-modes", "deno_core", "deno_web", diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 16c8628295..4e2a90f3bb 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -12,7 +12,7 @@ const core = window.Deno.core; const webidl = window.__bootstrap.webidl; const { DOMException } = window.__bootstrap.domException; - const { atob, btoa } = window.__bootstrap.base64; + const { btoa } = window.__bootstrap.base64; const { ArrayPrototypeFind, @@ -23,7 +23,6 @@ BigInt64Array, StringPrototypeToUpperCase, StringPrototypeReplace, - StringPrototypeCharCodeAt, StringFromCharCode, Symbol, SymbolFor, @@ -169,23 +168,6 @@ }, }; - // Decodes the unpadded base64 to the octet sequence containing key value `k` defined in RFC7518 Section 6.4 - function decodeSymmetricKey(key) { - // Decode from base64url without `=` padding. - const base64 = StringPrototypeReplace( - StringPrototypeReplace(key, /\-/g, "+"), - /\_/g, - "/", - ); - const decodedKey = atob(base64); - const keyLength = decodedKey.length; - const keyBytes = new Uint8Array(keyLength); - for (let i = 0; i < keyLength; i++) { - keyBytes[i] = StringPrototypeCharCodeAt(decodedKey, i); - } - return keyBytes; - } - function unpaddedBase64(bytes) { let binaryString = ""; for (let i = 0; i < bytes.length; i++) { @@ -1901,7 +1883,12 @@ } // 4. - data = decodeSymmetricKey(jwk.k); + const { rawData } = core.opSync( + "op_crypto_import_key", + { algorithm: "AES" }, + { jwkSecret: jwk }, + ); + data = rawData.data; // 5. switch (data.byteLength * 8) { @@ -2038,6 +2025,7 @@ case "jwk": { // TODO(@littledivy): Why does the spec validate JWK twice? const jwk = keyData; + // 2. if (jwk.kty !== "oct") { throw new DOMException( @@ -2055,7 +2043,12 @@ } // 4. - data = decodeSymmetricKey(jwk.k); + const { rawData } = core.opSync( + "op_crypto_import_key", + { algorithm: "HMAC" }, + { jwkSecret: jwk }, + ); + data = rawData.data; // 5. hash = normalizedAlgorithm.hash; diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index a9b37ba2d2..daa809422f 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] aes = "0.7.5" +base64 = "0.13.0" block-modes = "0.8.1" deno_core = { version = "0.110.0", path = "../../core" } deno_web = { version = "0.59.0", path = "../web" } diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 16a8d155f8..c93e2a9bbb 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -11,12 +11,12 @@ use crate::OaepPrivateKeyParameters; use crate::PssPrivateKeyParameters; #[derive(Deserialize)] -#[serde(rename_all = "lowercase")] +#[serde(rename_all = "camelCase")] pub enum KeyData { Spki(ZeroCopyBuf), Pkcs8(ZeroCopyBuf), Raw(ZeroCopyBuf), - Jwk { k: String }, + JwkSecret { k: String }, } #[derive(Deserialize)] @@ -32,6 +32,10 @@ pub enum ImportKeyOptions { Ecdsa { named_curve: EcNamedCurve }, #[serde(rename = "ECDH", rename_all = "camelCase")] Ecdh { named_curve: EcNamedCurve }, + #[serde(rename = "AES", rename_all = "camelCase")] + Aes {}, + #[serde(rename = "HMAC", rename_all = "camelCase")] + Hmac {}, } #[derive(Serialize)] @@ -45,6 +49,10 @@ pub enum ImportKeyResult { }, #[serde(rename_all = "camelCase")] Ec { raw_data: RawKeyData }, + #[serde(rename_all = "camelCase")] + Aes { raw_data: RawKeyData }, + #[serde(rename_all = "camelCase")] + Hmac { raw_data: RawKeyData }, } pub fn op_crypto_import_key( @@ -62,6 +70,8 @@ pub fn op_crypto_import_key( | ImportKeyOptions::Ecdh { named_curve } => { import_key_ec(key_data, named_curve) } + ImportKeyOptions::Aes {} => import_key_aes(key_data), + ImportKeyOptions::Hmac {} => import_key_hmac(key_data), } } @@ -576,3 +586,29 @@ fn import_key_ec( _ => return Err(unsupported_format()), }) } + +fn import_key_aes(key_data: KeyData) -> Result { + Ok(match key_data { + KeyData::JwkSecret { k } => { + let data = base64::decode_config(k, base64::URL_SAFE) + .map_err(|_| data_error("invalid key data"))?; + ImportKeyResult::Hmac { + raw_data: RawKeyData::Secret(data.into()), + } + } + _ => return Err(unsupported_format()), + }) +} + +fn import_key_hmac(key_data: KeyData) -> Result { + Ok(match key_data { + KeyData::JwkSecret { k } => { + let data = base64::decode_config(k, base64::URL_SAFE) + .map_err(|_| data_error("invalid key data"))?; + ImportKeyResult::Hmac { + raw_data: RawKeyData::Secret(data.into()), + } + } + _ => return Err(unsupported_format()), + }) +}