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:
parent
2bdb528eb8
commit
cbfc8dd59d
4 changed files with 54 additions and 23 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue