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