script/dom/subtlecrypto/
hkdf_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use aws_lc_rs::hkdf;
6
7use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
8use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
9use crate::dom::bindings::error::Error;
10use crate::dom::bindings::root::DomRoot;
11use crate::dom::cryptokey::{CryptoKey, Handle};
12use crate::dom::globalscope::GlobalScope;
13use crate::dom::subtlecrypto::{
14    ALG_HKDF, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, KeyAlgorithmAndDerivatives,
15    SubtleHkdfParams, SubtleKeyAlgorithm,
16};
17use crate::script_runtime::CanGc;
18
19/// <https://w3c.github.io/webcrypto/#hkdf-operations-derive-bits>
20pub(crate) fn derive_bits(
21    normalized_algorithm: &SubtleHkdfParams,
22    key: &CryptoKey,
23    length: Option<u32>,
24) -> Result<Vec<u8>, Error> {
25    // Step 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError.
26    // FIXME: The spec is updated.
27    let Some(length) = length else {
28        return Err(Error::Operation);
29    };
30    if length == 0 || length % 8 != 0 {
31        return Err(Error::Operation);
32    };
33
34    // Step 2. Let keyDerivationKey be the secret represented by the [[handle]] internal slot of key.
35    let key_derivation_key = key.handle().as_bytes();
36
37    // Step 3. Let result be the result of performing the HKDF extract and then the HKDF expand
38    // step described in Section 2 of [RFC5869] using:
39    // * the hash member of normalizedAlgorithm as Hash,
40    // * keyDerivationKey as the input keying material, IKM,
41    // * the salt member of normalizedAlgorithm as salt,
42    // * the info member of normalizedAlgorithm as info,
43    // * length divided by 8 as the value of L,
44    // Step 4. If the key derivation operation fails, then throw an OperationError.
45    let mut result = vec![0; length as usize / 8];
46    let algorithm = match normalized_algorithm.hash.name.as_str() {
47        ALG_SHA1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
48        ALG_SHA256 => hkdf::HKDF_SHA256,
49        ALG_SHA384 => hkdf::HKDF_SHA384,
50        ALG_SHA512 => hkdf::HKDF_SHA512,
51        _ => {
52            return Err(Error::NotSupported);
53        },
54    };
55    let salt = hkdf::Salt::new(algorithm, &normalized_algorithm.salt);
56    let info = normalized_algorithm.info.as_slice();
57    let pseudo_random_key = salt.extract(key_derivation_key);
58    let Ok(output_key_material) = pseudo_random_key.expand(std::slice::from_ref(&info), algorithm)
59    else {
60        return Err(Error::Operation);
61    };
62    if output_key_material.fill(&mut result).is_err() {
63        return Err(Error::Operation);
64    };
65
66    // Step 5. Return result.
67    Ok(result)
68}
69
70/// <https://w3c.github.io/webcrypto/#hkdf-operations-import-key>
71pub(crate) fn import(
72    global: &GlobalScope,
73    format: KeyFormat,
74    key_data: &[u8],
75    extractable: bool,
76    usages: Vec<KeyUsage>,
77    can_gc: CanGc,
78) -> Result<DomRoot<CryptoKey>, Error> {
79    // Step 1. Let keyData be the key data to be imported.
80
81    // Step 2. If format is "raw":
82    if format == KeyFormat::Raw {
83        // Step 2.1. If usages contains a value that is not "deriveKey" or "deriveBits", then throw
84        // a SyntaxError.
85        if usages
86            .iter()
87            .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
88            usages.is_empty()
89        {
90            return Err(Error::Syntax(None));
91        }
92
93        // Step 2.2. If extractable is not false, then throw a SyntaxError.
94        if extractable {
95            return Err(Error::Syntax(None));
96        }
97
98        // Step 2.3. Let key be a new CryptoKey representing the key data provided in keyData.
99        // Step 2.4. Set the [[type]] internal slot of key to "secret".
100        // Step 2.5. Let algorithm be a new KeyAlgorithm object.
101        // Step 2.6. Set the name attribute of algorithm to "HKDF".
102        // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
103        let algorithm = SubtleKeyAlgorithm {
104            name: ALG_HKDF.to_string(),
105        };
106        let key = CryptoKey::new(
107            global,
108            KeyType::Secret,
109            extractable,
110            KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
111            usages,
112            Handle::Hkdf(key_data.to_vec()),
113            can_gc,
114        );
115
116        // Step 2.8. Return key.
117        Ok(key)
118    }
119    // Otherwise:
120    else {
121        // throw a NotSupportedError.
122        Err(Error::NotSupported)
123    }
124}
125
126/// <https://w3c.github.io/webcrypto/#hkdf-operations-get-key-length>
127pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
128    // Step 1. Return null.
129    Ok(None)
130}