Skip to main content

script/dom/webcrypto/subtlecrypto/
argon2_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 argon2::{Argon2, AssociatedData, ParamsBuilder, Version};
6use js::context::JSContext;
7
8use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
9use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
10use crate::dom::bindings::error::Error;
11use crate::dom::bindings::root::DomRoot;
12use crate::dom::cryptokey::{CryptoKey, Handle, KeyUsageVecHelper};
13use crate::dom::globalscope::GlobalScope;
14use crate::dom::subtlecrypto::{
15    Algorithm, Argon2Params, CryptoAlgorithm, KeyAlgorithm, KeyAlgorithmAndDerivatives,
16};
17
18/// <https://wicg.github.io/webcrypto-modern-algos/#argon2-operations-derive-bits>
19pub(crate) fn derive_bits(
20    normalized_algorithm: &Argon2Params,
21    key: &CryptoKey,
22    length: Option<u32>,
23) -> Result<Vec<u8>, Error> {
24    // Step 1. If length is null, or is less than 32 (4*8), then throw an OperationError.
25    let length = length.ok_or(Error::Operation(Some(
26        "Length for deriving bits is null".to_string(),
27    )))?;
28    if length < 32 {
29        return Err(Error::Operation(Some(
30            "Length for deriving bits is less than 32".to_string(),
31        )));
32    }
33
34    // Step 2. If the version member of normalizedAlgorithm is present and is not 19 (0x13), then
35    // throw an OperationError.
36    if normalized_algorithm
37        .version
38        .is_some_and(|version| version != 19)
39    {
40        return Err(Error::Operation(Some(
41            "Argon2 version is not 19 (0x13)".to_string(),
42        )));
43    }
44
45    // Step 3. If the parallelism member of normalizedAlgorithm is zero, or greater than 16777215
46    // (2^24-1), then throw an OperationError.
47    if normalized_algorithm.parallelism == 0 || normalized_algorithm.parallelism > 16777215 {
48        return Err(Error::Operation(Some(
49            "Argon2 parallelism is zero, or greater than 16777215 (2^24-1)".to_string(),
50        )));
51    }
52
53    // Step 4. If the memory member of normalizedAlgorithm is less than 8 times the parallelism
54    // member of normalizedAlgorithm, then throw an OperationError.
55    if normalized_algorithm.memory < 8 * normalized_algorithm.parallelism {
56        return Err(Error::Operation(Some(
57            "Argon2 memory is less than 8 times the parallelism".to_string(),
58        )));
59    }
60
61    // Step 5. If the passes member of normalizedAlgorithm is zero, then throw an OperationError.
62    if normalized_algorithm.passes == 0 {
63        return Err(Error::Operation(Some("Argon2 passes is zero".to_string())));
64    }
65
66    // Step 6.
67    // If the name member of normalizedAlgorithm is a case-sensitive string match for "Argon2d":
68    //     Let type be 0.
69    // If the name member of normalizedAlgorithm is a case-sensitive string match for "Argon2i":
70    //     Let type be 1.
71    // If the name member of normalizedAlgorithm is a case-sensitive string match for "Argon2id":
72    //     Let type be 2.
73    let type_ = match normalized_algorithm.name {
74        CryptoAlgorithm::Argon2D => argon2::Algorithm::Argon2d,
75        CryptoAlgorithm::Argon2I => argon2::Algorithm::Argon2i,
76        CryptoAlgorithm::Argon2ID => argon2::Algorithm::Argon2id,
77        _ => {
78            return Err(Error::NotSupported(Some(format!(
79                "Unknown Argon2 algorithm name: {}",
80                normalized_algorithm.name.as_str()
81            ))));
82        },
83    };
84
85    // Step 7. Let secretValue be the secretValue member of normalizedAlgorithm, if present.
86    // Step 8. Let associatedData be the associatedData member of normalizedAlgorithm, if present.
87    // Step 9. Let result be the result of performing the Argon2 function defined in Section 3 of
88    // [RFC9106] using the password represented by [[handle]] internal slot of key as the message,
89    // P, the nonce attribute of normalizedAlgorithm as the nonce, S, the value of the parallelism
90    // attribute of normalizedAlgorithm as the degree of parallelism, p, the value of the memory
91    // attribute of normalizedAlgorithm as the memory size, m, the value of the passes attribute of
92    // normalizedAlgorithm as the number of passes, t, 0x13 as the version number, v, secretValue
93    // (if present) as the secret value, K, associatedData (if present) as the associated data, X,
94    // type as the type, y, and length divided by 8 as the tag length, T.
95    // Step 10. If the key derivation operation fails, then throw an OperationError.
96    let Handle::Argon2Password(password) = key.handle() else {
97        return Err(Error::Operation(Some(
98            "Key handle is not an Argon2 password".to_string(),
99        )));
100    };
101    let mut params_builder = ParamsBuilder::new();
102    if let Some(associated_data) = &normalized_algorithm.associated_data {
103        let _ = params_builder.data(AssociatedData::new(associated_data).map_err(|_| {
104            Error::Operation(Some(
105                "Argon2 fails to add associated data to parameter builder".to_string(),
106            ))
107        })?);
108    }
109    let params = params_builder
110        .p_cost(normalized_algorithm.parallelism)
111        .m_cost(normalized_algorithm.memory)
112        .t_cost(normalized_algorithm.passes)
113        .build()
114        .map_err(|_| Error::Operation(Some("Argon2 fails to build parameters".to_string())))?;
115    let argon2_context = match &normalized_algorithm.secret_value {
116        Some(secret) => Argon2::new_with_secret(secret, type_, Version::V0x13, params)
117            .map_err(|_| Error::Operation(Some("Argon2 fails to create context".to_string())))?,
118        None => Argon2::new(type_, Version::V0x13, params),
119    };
120    let mut result = vec![0u8; length as usize / 8];
121    argon2_context
122        .hash_password_into(password, &normalized_algorithm.nonce, &mut result)
123        .map_err(|_| Error::Operation(Some("Argon2 fails to hash the password".to_string())))?;
124
125    // Step 11. Return result.
126    Ok(result)
127}
128
129/// <https://wicg.github.io/webcrypto-modern-algos/#argon2-operations-import-key>
130pub(crate) fn import_key(
131    cx: &mut JSContext,
132    global: &GlobalScope,
133    normalized_algorithm: &Algorithm,
134    format: KeyFormat,
135    key_data: &[u8],
136    extractable: bool,
137    usages: Vec<KeyUsage>,
138) -> Result<DomRoot<CryptoKey>, Error> {
139    // Step 1. Let keyData be the key data to be imported.
140
141    // Step 2. If format is not "raw-secret", throw a NotSupportedError
142    if format != KeyFormat::Raw_secret {
143        return Err(Error::NotSupported(Some(
144            "Import key format is not \"raw-secret\"".to_string(),
145        )));
146    }
147
148    // Step 3. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a
149    // SyntaxError.
150    if usages
151        .iter()
152        .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
153    {
154        return Err(Error::Syntax(Some(
155            "Usages contains a value that is not \"deriveKey\" or \"deriveBits\"".to_string(),
156        )));
157    }
158
159    // Step 4. If extractable is not false, then throw a SyntaxError.
160    if extractable {
161        return Err(Error::Syntax(Some("Extrabctable is not false".to_string())));
162    }
163
164    // Step 5. Let key be a new CryptoKey representing keyData.
165    // Step 6. Set the [[type]] internal slot of key to "secret".
166    // Step 7. Let algorithm be a new KeyAlgorithm object.
167    // Step 8. Set the name attribute of algorithm to the name member of normalizedAlgorithm.
168    // Step 9. Set the [[algorithm]] internal slot of key to algorithm.
169    let algorithm = KeyAlgorithm {
170        name: normalized_algorithm.name,
171    };
172    let key = CryptoKey::new(
173        cx,
174        global,
175        KeyType::Secret,
176        extractable,
177        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
178        usages.normalized_value(),
179        Handle::Argon2Password(key_data.to_vec().into()),
180    );
181
182    // Step 10. Return key.
183    Ok(key)
184}
185
186/// <https://wicg.github.io/webcrypto-modern-algos/#argon2-operations-get-key-length>
187pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
188    // Step 1. Return null.
189    Ok(None)
190}