script/dom/webcrypto/subtlecrypto/
argon2_operation.rs1use 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
18pub(crate) fn derive_bits(
20 normalized_algorithm: &Argon2Params,
21 key: &CryptoKey,
22 length: Option<u32>,
23) -> Result<Vec<u8>, Error> {
24 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 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 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 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 if normalized_algorithm.passes == 0 {
63 return Err(Error::Operation(Some("Argon2 passes is zero".to_string())));
64 }
65
66 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 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 Ok(result)
127}
128
129pub(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 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 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 if extractable {
161 return Err(Error::Syntax(Some("Extrabctable is not false".to_string())));
162 }
163
164 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 Ok(key)
184}
185
186pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
188 Ok(None)
190}