Skip to main content

script_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;
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;
11
12use crate::cryptokey::{CryptoKey, Handle, KeyUsageSliceHelper};
13use crate::subtlecrypto::{
14    Algorithm, Argon2Params, CryptoAlgorithm, KeyAlgorithm, KeyAlgorithmAndDerivatives,
15};
16use crate::traits::Equivalence;
17
18/// <https://wicg.github.io/webcrypto-modern-algos/#argon2-operations-derive-bits>
19pub(crate) fn derive_bits<D: Equivalence>(
20    normalized_algorithm: &Argon2Params,
21    key: &CryptoKey<D>,
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<D: Equivalence>(
131    cx: &mut JSContext,
132    global: &D::GlobalScope,
133    normalized_algorithm: &Algorithm,
134    format: KeyFormat,
135    key_data: &[u8],
136    extractable: bool,
137    usages: Vec<KeyUsage>,
138) -> Result<DomRoot<CryptoKey<D>>, 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    usages.ensure_only_contain_entries_from(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])?;
151
152    // Step 4. If extractable is not false, then throw a SyntaxError.
153    if extractable {
154        return Err(Error::Syntax(Some("Extrabctable is not false".to_string())));
155    }
156
157    // Step 5. Let key be a new CryptoKey representing keyData.
158    // Step 6. Set the [[type]] internal slot of key to "secret".
159    // Step 7. Let algorithm be a new KeyAlgorithm object.
160    // Step 8. Set the name attribute of algorithm to the name member of normalizedAlgorithm.
161    // Step 9. Set the [[algorithm]] internal slot of key to algorithm.
162    let algorithm = KeyAlgorithm {
163        name: normalized_algorithm.name,
164    };
165    let key = CryptoKey::new(
166        cx,
167        global,
168        KeyType::Secret,
169        extractable,
170        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
171        usages.normalized_value(),
172        Handle::Argon2Password(key_data.to_vec().into()),
173    );
174
175    // Step 10. Return key.
176    Ok(key)
177}
178
179/// <https://wicg.github.io/webcrypto-modern-algos/#argon2-operations-get-key-length>
180pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
181    // Step 1. Return null.
182    Ok(None)
183}