Skip to main content

script/dom/webcrypto/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;
8use js::context::JSContext;
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, KeyAlgorithmAndDerivatives, NormalizedAlgorithm, SubtleKeyAlgorithm,
18    SubtlePbkdf2Params,
19};
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 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. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError.
38    let Ok(iterations) = NonZero::<u32>::try_from(normalized_algorithm.iterations) else {
39        return Err(Error::Operation(Some(
40            "Normalized algorithm's iterations is zero".into(),
41        )));
42    };
43
44    // Step 3. If length is zero, return an empty byte sequence.
45    if length == 0 {
46        return Ok(Vec::new());
47    }
48
49    // Step 4. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] using
50    // the hash function described by the hash member of normalizedAlgorithm.
51    let prf = match normalized_algorithm.hash.name() {
52        CryptoAlgorithm::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1,
53        CryptoAlgorithm::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256,
54        CryptoAlgorithm::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384,
55        CryptoAlgorithm::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512,
56        _ => {
57            return Err(Error::NotSupported(Some(
58                "Normalized algorithm's hash name is not supported".into(),
59            )));
60        },
61    };
62
63    // Step 5. Let result be the result of performing the PBKDF2 operation defined in Section 5.2
64    // of [RFC8018] using prf as the pseudo-random function, PRF, the password represented by the
65    // [[handle]] internal slot of key as the password, P, the salt attribute of
66    // normalizedAlgorithm as the salt, S, the value of the iterations attribute of
67    // normalizedAlgorithm as the iteration count, c, and length divided by 8 as the intended key
68    // length, dkLen.
69    let mut result = vec![0; length as usize / 8];
70    pbkdf2::derive(
71        prf,
72        iterations,
73        &normalized_algorithm.salt,
74        key.handle().as_bytes(),
75        &mut result,
76    );
77
78    // Step 5. If the key derivation operation fails, then throw an OperationError.
79    // TODO: Investigate when key derivation can fail and how ring handles that case
80    // (pbkdf2::derive does not return a Result type)
81
82    // Step 6. Return result
83    Ok(result)
84}
85
86/// <https://w3c.github.io/webcrypto/#pbkdf2-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. If format is not "raw", throw a NotSupportedError
96    if !matches!(format, KeyFormat::Raw | KeyFormat::Raw_secret) {
97        return Err(Error::NotSupported(Some("Format is not raw".into())));
98    }
99
100    // Step 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw 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 is empty or contains a value that is not a 'deriveKey' or 'deriveBits'".into(),
108        )));
109    }
110
111    // Step 3. 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 4. Let key be a new CryptoKey representing keyData.
117    // Step 5. Set the [[type]] internal slot of key to "secret".
118    // Step 6. Let algorithm be a new KeyAlgorithm object.
119    // Step 7. Set the name attribute of algorithm to "PBKDF2".
120    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
121    let algorithm = SubtleKeyAlgorithm {
122        name: CryptoAlgorithm::Pbkdf2,
123    };
124    let key = CryptoKey::new(
125        cx,
126        global,
127        KeyType::Secret,
128        extractable,
129        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
130        usages.normalized_value(),
131        Handle::Pbkdf2(key_data.to_vec().into()),
132    );
133
134    // Step 9. Return key.
135    Ok(key)
136}
137
138/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-get-key-length>
139pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
140    // Step 1. Return null.
141    Ok(None)
142}