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