1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

refactor(ext/crypto): symmetric jwk decode in rust (#13047)

This commit is contained in:
Luca Casonato 2021-12-10 22:23:19 +01:00 committed by GitHub
parent 2bdb528eb8
commit cbfc8dd59d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 23 deletions

1
Cargo.lock generated
View file

@ -767,6 +767,7 @@ name = "deno_crypto"
version = "0.42.0" version = "0.42.0"
dependencies = [ dependencies = [
"aes", "aes",
"base64 0.13.0",
"block-modes", "block-modes",
"deno_core", "deno_core",
"deno_web", "deno_web",

View file

@ -12,7 +12,7 @@
const core = window.Deno.core; const core = window.Deno.core;
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const { DOMException } = window.__bootstrap.domException; const { DOMException } = window.__bootstrap.domException;
const { atob, btoa } = window.__bootstrap.base64; const { btoa } = window.__bootstrap.base64;
const { const {
ArrayPrototypeFind, ArrayPrototypeFind,
@ -23,7 +23,6 @@
BigInt64Array, BigInt64Array,
StringPrototypeToUpperCase, StringPrototypeToUpperCase,
StringPrototypeReplace, StringPrototypeReplace,
StringPrototypeCharCodeAt,
StringFromCharCode, StringFromCharCode,
Symbol, Symbol,
SymbolFor, 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) { function unpaddedBase64(bytes) {
let binaryString = ""; let binaryString = "";
for (let i = 0; i < bytes.length; i++) { for (let i = 0; i < bytes.length; i++) {
@ -1901,7 +1883,12 @@
} }
// 4. // 4.
data = decodeSymmetricKey(jwk.k); const { rawData } = core.opSync(
"op_crypto_import_key",
{ algorithm: "AES" },
{ jwkSecret: jwk },
);
data = rawData.data;
// 5. // 5.
switch (data.byteLength * 8) { switch (data.byteLength * 8) {
@ -2038,6 +2025,7 @@
case "jwk": { case "jwk": {
// TODO(@littledivy): Why does the spec validate JWK twice? // TODO(@littledivy): Why does the spec validate JWK twice?
const jwk = keyData; const jwk = keyData;
// 2. // 2.
if (jwk.kty !== "oct") { if (jwk.kty !== "oct") {
throw new DOMException( throw new DOMException(
@ -2055,7 +2043,12 @@
} }
// 4. // 4.
data = decodeSymmetricKey(jwk.k); const { rawData } = core.opSync(
"op_crypto_import_key",
{ algorithm: "HMAC" },
{ jwkSecret: jwk },
);
data = rawData.data;
// 5. // 5.
hash = normalizedAlgorithm.hash; hash = normalizedAlgorithm.hash;

View file

@ -15,6 +15,7 @@ path = "lib.rs"
[dependencies] [dependencies]
aes = "0.7.5" aes = "0.7.5"
base64 = "0.13.0"
block-modes = "0.8.1" block-modes = "0.8.1"
deno_core = { version = "0.110.0", path = "../../core" } deno_core = { version = "0.110.0", path = "../../core" }
deno_web = { version = "0.59.0", path = "../web" } deno_web = { version = "0.59.0", path = "../web" }

View file

@ -11,12 +11,12 @@ use crate::OaepPrivateKeyParameters;
use crate::PssPrivateKeyParameters; use crate::PssPrivateKeyParameters;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "camelCase")]
pub enum KeyData { pub enum KeyData {
Spki(ZeroCopyBuf), Spki(ZeroCopyBuf),
Pkcs8(ZeroCopyBuf), Pkcs8(ZeroCopyBuf),
Raw(ZeroCopyBuf), Raw(ZeroCopyBuf),
Jwk { k: String }, JwkSecret { k: String },
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -32,6 +32,10 @@ pub enum ImportKeyOptions {
Ecdsa { named_curve: EcNamedCurve }, Ecdsa { named_curve: EcNamedCurve },
#[serde(rename = "ECDH", rename_all = "camelCase")] #[serde(rename = "ECDH", rename_all = "camelCase")]
Ecdh { named_curve: EcNamedCurve }, Ecdh { named_curve: EcNamedCurve },
#[serde(rename = "AES", rename_all = "camelCase")]
Aes {},
#[serde(rename = "HMAC", rename_all = "camelCase")]
Hmac {},
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -45,6 +49,10 @@ pub enum ImportKeyResult {
}, },
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
Ec { raw_data: RawKeyData }, 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( pub fn op_crypto_import_key(
@ -62,6 +70,8 @@ pub fn op_crypto_import_key(
| ImportKeyOptions::Ecdh { named_curve } => { | ImportKeyOptions::Ecdh { named_curve } => {
import_key_ec(key_data, 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()), _ => return Err(unsupported_format()),
}) })
} }
fn import_key_aes(key_data: KeyData) -> Result<ImportKeyResult, AnyError> {
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<ImportKeyResult, AnyError> {
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()),
})
}