Skip to main content

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