script/dom/subtlecrypto/
pbkdf2_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 std::num::NonZero;
6
7use aws_lc_rs::pbkdf2;
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_PBKDF2, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, KeyAlgorithmAndDerivatives,
17    SubtleKeyAlgorithm, SubtlePbkdf2Params,
18};
19use crate::script_runtime::CanGc;
20
21/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-derive-bits>
22pub(crate) fn derive_bits(
23    normalized_algorithm: &SubtlePbkdf2Params,
24    key: &CryptoKey,
25    length: Option<u32>,
26) -> Result<Vec<u8>, Error> {
27    // Step 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError.
28    // FIXME: The spec is updated.
29    let Some(length) = length else {
30        return Err(Error::Operation);
31    };
32    if length == 0 || length % 8 != 0 {
33        return Err(Error::Operation);
34    };
35
36    // Step 2. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError.
37    let Ok(iterations) = NonZero::<u32>::try_from(normalized_algorithm.iterations) else {
38        return Err(Error::Operation);
39    };
40
41    // TODO: Step 3. If length is zero, return an empty byte sequence.
42    if length == 0 {
43        return Ok(Vec::new());
44    }
45
46    // Step 4. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] using
47    // the hash function described by the hash member of normalizedAlgorithm.
48    let prf = match normalized_algorithm.hash.name.as_str() {
49        ALG_SHA1 => pbkdf2::PBKDF2_HMAC_SHA1,
50        ALG_SHA256 => pbkdf2::PBKDF2_HMAC_SHA256,
51        ALG_SHA384 => pbkdf2::PBKDF2_HMAC_SHA384,
52        ALG_SHA512 => pbkdf2::PBKDF2_HMAC_SHA512,
53        _ => {
54            return Err(Error::NotSupported);
55        },
56    };
57
58    // Step 5. Let result be the result of performing the PBKDF2 operation defined in Section 5.2
59    // of [RFC8018] using prf as the pseudo-random function, PRF, the password represented by the
60    // [[handle]] internal slot of key as the password, P, the salt attribute of
61    // normalizedAlgorithm as the salt, S, the value of the iterations attribute of
62    // normalizedAlgorithm as the iteration count, c, and length divided by 8 as the intended key
63    // length, dkLen.
64    let mut result = vec![0; length as usize / 8];
65    pbkdf2::derive(
66        prf,
67        iterations,
68        &normalized_algorithm.salt,
69        key.handle().as_bytes(),
70        &mut result,
71    );
72
73    // Step 5. If the key derivation operation fails, then throw an OperationError.
74    // TODO: Investigate when key derivation can fail and how ring handles that case
75    // (pbkdf2::derive does not return a Result type)
76
77    // Step 6. Return result
78    Ok(result)
79}
80
81/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-import-key>
82pub(crate) fn import(
83    global: &GlobalScope,
84    format: KeyFormat,
85    key_data: &[u8],
86    extractable: bool,
87    usages: Vec<KeyUsage>,
88    can_gc: CanGc,
89) -> Result<DomRoot<CryptoKey>, Error> {
90    // Step 1. If format is not "raw", throw a NotSupportedError
91    if format != KeyFormat::Raw {
92        return Err(Error::NotSupported);
93    }
94
95    // Step 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
96    if usages
97        .iter()
98        .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
99        usages.is_empty()
100    {
101        return Err(Error::Syntax(None));
102    }
103
104    // Step 3. If extractable is not false, then throw a SyntaxError.
105    if extractable {
106        return Err(Error::Syntax(None));
107    }
108
109    // Step 4. Let key be a new CryptoKey representing keyData.
110    // Step 5. Set the [[type]] internal slot of key to "secret".
111    // Step 6. Let algorithm be a new KeyAlgorithm object.
112    // Step 7. Set the name attribute of algorithm to "PBKDF2".
113    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
114    let algorithm = SubtleKeyAlgorithm {
115        name: ALG_PBKDF2.to_string(),
116    };
117    let key = CryptoKey::new(
118        global,
119        KeyType::Secret,
120        extractable,
121        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
122        usages,
123        Handle::Pbkdf2(key_data.to_vec()),
124        can_gc,
125    );
126
127    // Step 9. Return key.
128    Ok(key)
129}
130
131/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-get-key-length>
132pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
133    // Step 1. Return null.
134    Ok(None)
135}