diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/crypto/00_crypto.js | 97 | ||||
-rw-r--r-- | ext/crypto/01_webidl.js | 22 | ||||
-rw-r--r-- | ext/crypto/key.rs | 11 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 28 |
4 files changed, 158 insertions, 0 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 0e5a511e5..2cb7a3bb2 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -61,6 +61,11 @@ RsaPssParams: {}, EcdsaParams: { hash: "HashAlgorithmIdentifier" }, HmacImportParams: { hash: "HashAlgorithmIdentifier" }, + HkdfParams: { + hash: "HashAlgorithmIdentifier", + salt: "BufferSource", + info: "BufferSource", + }, Pbkdf2Params: { hash: "HashAlgorithmIdentifier", salt: "BufferSource" }, RsaOaepParams: { label: "BufferSource" }, }; @@ -97,9 +102,11 @@ }, "importKey": { "HMAC": "HmacImportParams", + "HKDF": null, "PBKDF2": null, }, "deriveBits": { + "HKDF": "HkdfParams", "PBKDF2": "Pbkdf2Params", }, "encrypt": { @@ -893,6 +900,51 @@ // TODO(@littledivy): RSASSA-PKCS1-v1_5 // TODO(@littledivy): RSA-PSS // TODO(@littledivy): ECDSA + case "HKDF": { + if (format !== "raw") { + throw new DOMException("Format not supported", "NotSupportedError"); + } + + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 2. + if (extractable !== false) { + throw new DOMException( + "Key must not be extractable", + "SyntaxError", + ); + } + + // 3. + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "raw", + data: keyData, + }); + + // 4-8. + const algorithm = { + name: "HKDF", + }; + const key = constructKey( + "secret", + false, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + // 9. + return key; + } case "PBKDF2": { // 1. if (format !== "raw") { @@ -1604,6 +1656,51 @@ return buf.buffer; } + case "HKDF": { + // 1. + if (length === null || length === 0 || length % 8 !== 0) { + throw new DOMException("Invalid length", "OperationError"); + } + + const handle = baseKey[_handle]; + const keyDerivationKey = WeakMapPrototypeGet(KEY_STORE, handle); + + if (ArrayBufferIsView(normalizedAlgorithm.salt)) { + normalizedAlgorithm.salt = new Uint8Array( + normalizedAlgorithm.salt.buffer, + normalizedAlgorithm.salt.byteOffset, + normalizedAlgorithm.salt.byteLength, + ); + } else { + normalizedAlgorithm.salt = new Uint8Array(normalizedAlgorithm.salt); + } + normalizedAlgorithm.salt = TypedArrayPrototypeSlice( + normalizedAlgorithm.salt, + ); + + if (ArrayBufferIsView(normalizedAlgorithm.info)) { + normalizedAlgorithm.info = new Uint8Array( + normalizedAlgorithm.info.buffer, + normalizedAlgorithm.info.byteOffset, + normalizedAlgorithm.info.byteLength, + ); + } else { + normalizedAlgorithm.info = new Uint8Array(normalizedAlgorithm.info); + } + normalizedAlgorithm.info = TypedArrayPrototypeSlice( + normalizedAlgorithm.info, + ); + + const buf = await core.opAsync("op_crypto_derive_bits", { + key: keyDerivationKey, + algorithm: "HKDF", + hash: normalizedAlgorithm.hash.name, + info: normalizedAlgorithm.info, + length, + }, normalizedAlgorithm.salt); + + return buf.buffer; + } default: throw new DOMException("Not implemented", "NotSupportedError"); } diff --git a/ext/crypto/01_webidl.js b/ext/crypto/01_webidl.js index 3ef3eb175..78a897fbc 100644 --- a/ext/crypto/01_webidl.js +++ b/ext/crypto/01_webidl.js @@ -309,6 +309,28 @@ dictJsonWebKey, ); + const dictHkdfParams = [ + ...dictAlgorithm, + { + key: "hash", + converter: webidl.converters.HashAlgorithmIdentifier, + required: true, + }, + { + key: "salt", + converter: webidl.converters["BufferSource"], + required: true, + }, + { + key: "info", + converter: webidl.converters["BufferSource"], + required: true, + }, + ]; + + webidl.converters.HkdfParams = webidl + .createDictionaryConverter("HkdfParams", dictHkdfParams); + const dictPbkdf2Params = [ ...dictAlgorithm, { diff --git a/ext/crypto/key.rs b/ext/crypto/key.rs index 663217887..d55a1d062 100644 --- a/ext/crypto/key.rs +++ b/ext/crypto/key.rs @@ -2,6 +2,7 @@ use ring::agreement::Algorithm as RingAlgorithm; use ring::digest; +use ring::hkdf; use ring::hmac::Algorithm as HmacAlgorithm; use ring::signature::EcdsaSigningAlgorithm; use ring::signature::EcdsaVerificationAlgorithm; @@ -89,6 +90,14 @@ impl From<CryptoHash> for &'static digest::Algorithm { } } +pub struct HkdfOutput<T>(pub T); + +impl hkdf::KeyType for HkdfOutput<usize> { + fn len(&self) -> usize { + self.0 + } +} + #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub enum KeyUsage { @@ -126,4 +135,6 @@ pub enum Algorithm { Hmac, #[serde(rename = "PBKDF2")] Pbkdf2, + #[serde(rename = "HKDF")] + Hkdf, } diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index f2df7ba10..319f26c22 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -26,6 +26,7 @@ use rand::thread_rng; use rand::Rng; use rand::SeedableRng; use ring::digest; +use ring::hkdf; use ring::hmac::Algorithm as HmacAlgorithm; use ring::hmac::Key as HmacKey; use ring::pbkdf2; @@ -56,6 +57,7 @@ mod key; use crate::key::Algorithm; use crate::key::CryptoHash; use crate::key::CryptoNamedCurve; +use crate::key::HkdfOutput; // Allowlist for RSA public exponents. lazy_static! { @@ -558,6 +560,7 @@ pub struct DeriveKeyArg { hash: Option<CryptoHash>, length: usize, iterations: Option<u32>, + info: Option<ZeroCopyBuf>, } pub async fn op_crypto_derive_bits( @@ -589,6 +592,31 @@ pub async fn op_crypto_derive_bits( pbkdf2::derive(algorithm, iterations, salt, &secret, &mut out); Ok(out.into()) } + Algorithm::Hkdf => { + let algorithm = match args.hash.ok_or_else(not_supported)? { + CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, + CryptoHash::Sha256 => hkdf::HKDF_SHA256, + CryptoHash::Sha384 => hkdf::HKDF_SHA384, + CryptoHash::Sha512 => hkdf::HKDF_SHA512, + }; + + let info = args + .info + .ok_or_else(|| type_error("Missing argument info".to_string()))?; + // IKM + let secret = args.key.data; + // L + let length = args.length / 8; + + let salt = hkdf::Salt::new(algorithm, salt); + let prk = salt.extract(&secret); + let info = &[&*info]; + let okm = prk.expand(info, HkdfOutput(length))?; + let mut r = vec![0u8; length]; + okm.fill(&mut r)?; + + Ok(r.into()) + } _ => Err(type_error("Unsupported algorithm".to_string())), } } |