Skip to main content

script_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 js::context::JSContext;
6use pbkdf2::pbkdf2_hmac;
7use script_bindings::codegen::GenericBindings::CryptoKeyBinding::{KeyType, KeyUsage};
8use script_bindings::codegen::GenericBindings::SubtleCryptoBinding::KeyFormat;
9use script_bindings::error::Error;
10use script_bindings::root::DomRoot;
11use sha1::Sha1;
12use sha2::{Sha256, Sha384, Sha512};
13
14use crate::cryptokey::{CryptoKey, Handle, KeyUsageSliceHelper};
15use crate::subtlecrypto::{
16    CryptoAlgorithm, KeyAlgorithm, KeyAlgorithmAndDerivatives, NormalizedAlgorithm, Pbkdf2Params,
17};
18use crate::traits::Equivalence;
19
20/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-derive-bits>
21pub(crate) fn derive_bits<D: Equivalence>(
22    normalized_algorithm: &Pbkdf2Params,
23    key: &CryptoKey<D>,
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. If the iterations member of normalizedAlgorithm is zero, then throw an
37    // OperationError.
38    if normalized_algorithm.iterations == 0 {
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    // Step 5. Let result be the result of performing the PBKDF2 operation defined in Section 5.2
52    // of [RFC8018] using prf as the pseudo-random function, PRF, the password represented by the
53    // [[handle]] internal slot of key as the password, P, the salt attribute of
54    // normalizedAlgorithm as the salt, S, the value of the iterations attribute of
55    // normalizedAlgorithm as the iteration count, c, and length divided by 8 as the intended key
56    // length, dkLen.
57    // Step 6. If the key derivation operation fails, then throw an OperationError.
58    let Handle::Pbkdf2Password(password) = key.handle() else {
59        return Err(Error::Operation(Some(
60            "[[handle]] internal slot of key is not a PBKDF2 password".into(),
61        )));
62    };
63    let mut result = vec![0; length as usize / 8];
64    match normalized_algorithm.hash.name() {
65        CryptoAlgorithm::Sha1 => {
66            pbkdf2_hmac::<Sha1>(
67                password,
68                &normalized_algorithm.salt,
69                normalized_algorithm.iterations,
70                &mut result,
71            );
72        },
73        CryptoAlgorithm::Sha256 => {
74            pbkdf2_hmac::<Sha256>(
75                password,
76                &normalized_algorithm.salt,
77                normalized_algorithm.iterations,
78                &mut result,
79            );
80        },
81        CryptoAlgorithm::Sha384 => {
82            pbkdf2_hmac::<Sha384>(
83                password,
84                &normalized_algorithm.salt,
85                normalized_algorithm.iterations,
86                &mut result,
87            );
88        },
89        CryptoAlgorithm::Sha512 => {
90            pbkdf2_hmac::<Sha512>(
91                password,
92                &normalized_algorithm.salt,
93                normalized_algorithm.iterations,
94                &mut result,
95            );
96        },
97        _ => {
98            return Err(Error::NotSupported(Some(
99                "Normalized algorithm's hash name is not supported".into(),
100            )));
101        },
102    };
103
104    // Step 7. Return result.
105    Ok(result)
106}
107
108/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-import-key>
109pub(crate) fn import_key<D: Equivalence>(
110    cx: &mut JSContext,
111    global: &D::GlobalScope,
112    format: KeyFormat,
113    key_data: &[u8],
114    extractable: bool,
115    usages: Vec<KeyUsage>,
116) -> Result<DomRoot<CryptoKey<D>>, Error> {
117    // Step 1. If format is not "raw", throw a NotSupportedError
118    if !matches!(format, KeyFormat::Raw | KeyFormat::Raw_secret) {
119        return Err(Error::NotSupported(Some("Format is not raw".into())));
120    }
121
122    // Step 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a
123    // SyntaxError.
124    usages.ensure_only_contain_entries_from(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])?;
125
126    // Step 3. If extractable is not false, then throw a SyntaxError.
127    if extractable {
128        return Err(Error::Syntax(Some("Extractable is not false".into())));
129    }
130
131    // Step 4. Let key be a new CryptoKey representing keyData.
132    // Step 5. Set the [[type]] internal slot of key to "secret".
133    // Step 6. Let algorithm be a new KeyAlgorithm object.
134    // Step 7. Set the name attribute of algorithm to "PBKDF2".
135    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
136    let algorithm = KeyAlgorithm {
137        name: CryptoAlgorithm::Pbkdf2,
138    };
139    let key = CryptoKey::new(
140        cx,
141        global,
142        KeyType::Secret,
143        extractable,
144        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
145        usages.normalized_value(),
146        Handle::Pbkdf2Password(key_data.to_vec().into()),
147    );
148
149    // Step 9. Return key.
150    Ok(key)
151}
152
153/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-get-key-length>
154pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
155    // Step 1. Return null.
156    Ok(None)
157}