devolutions_crypto/
argon2parameters.rs1use std::{
2 convert::TryFrom,
3 io::{Cursor, Read, Write},
4};
5
6use argon2::{Config, Variant, Version};
7use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
8use rand::TryRng;
9use typed_builder::TypedBuilder;
10
11#[cfg(feature = "wbindgen")]
12use wasm_bindgen::prelude::*;
13
14use super::Error;
15use super::Result;
16
17pub mod defaults {
18 use super::Error;
19 use super::Result;
20 use argon2::Variant;
21 use argon2::Version;
22 use rand::TryRng;
23
24 pub const LENGTH: u32 = 32;
25 pub const LANES: u32 = 1;
26 pub const MEMORY: u32 = 4096;
27 pub const ITERATIONS: u32 = 2;
28 pub const VARIANT: Variant = Variant::Argon2id;
29 pub const VERSION: Version = Version::Version13;
30 pub const DC_VERSION: u32 = 1;
31
32 pub fn salt() -> Result<Vec<u8>> {
33 let mut salt = vec![0u8; 16];
34 rand::rngs::SysRng
35 .try_fill_bytes(salt.as_mut_slice())
36 .map_err(|_| Error::RandomError)?;
37 Ok(salt)
38 }
39}
40
41#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
52#[derive(Clone, TypedBuilder)]
53pub struct Argon2Parameters {
54 #[builder(default=defaults::LENGTH)]
56 pub length: u32,
57 #[builder(default=defaults::LANES)]
59 pub lanes: u32,
60 #[builder(default=defaults::MEMORY)]
62 pub memory: u32,
63 #[builder(default=defaults::ITERATIONS)]
65 pub iterations: u32,
66 #[builder(default=defaults::VARIANT)]
68 variant: Variant,
69 #[builder(default=defaults::VERSION)]
71 version: Version,
72 #[builder(default=defaults::DC_VERSION)]
74 dc_version: u32,
75 #[builder(default)]
77 associated_data: Vec<u8>,
78 #[builder(default)]
80 secret_key: Vec<u8>,
81 #[builder(default = defaults::salt().unwrap())]
83 salt: Vec<u8>,
84}
85
86impl Argon2Parameters {
87 pub fn get_salt_as_slice(&self) -> &[u8] {
88 self.salt.as_slice()
89 }
90
91 pub fn set_salt(&mut self, salt: Vec<u8>) {
92 self.salt = salt;
93 }
94}
95
96impl Default for Argon2Parameters {
98 fn default() -> Self {
99 let mut salt = vec![0u8; 16];
100
101 rand::rngs::SysRng
102 .try_fill_bytes(salt.as_mut_slice())
103 .unwrap();
104
105 Argon2Parameters {
106 associated_data: Vec::new(),
107 secret_key: Vec::new(),
108 length: 32,
109 lanes: 1,
110 memory: 4096,
111 iterations: 2,
112 variant: Variant::Argon2id,
113 version: Version::Version13,
114 dc_version: 1,
115 salt,
116 }
117 }
118}
119
120impl From<&Argon2Parameters> for Vec<u8> {
121 fn from(params: &Argon2Parameters) -> Self {
122 let mut data = Vec::with_capacity(
128 5 * 4 + 2 + 2 * 4 + params.associated_data.len() + params.salt.len(),
129 );
130 data.write_u32::<LittleEndian>(params.dc_version).unwrap();
131 data.write_u32::<LittleEndian>(params.length).unwrap();
132 data.write_u32::<LittleEndian>(params.lanes).unwrap();
133 data.write_u32::<LittleEndian>(params.memory).unwrap();
134 data.write_u32::<LittleEndian>(params.iterations).unwrap();
135 data.write_u8(params.variant.as_u32() as u8).unwrap();
136 data.write_u8(params.version.as_u32() as u8).unwrap();
137
138 data.write_u32::<LittleEndian>(params.associated_data.len() as u32)
139 .unwrap();
140
141 data.write_all(¶ms.associated_data).unwrap();
142
143 data.write_u32::<LittleEndian>(params.salt.len() as u32)
144 .unwrap();
145
146 data.write_all(¶ms.salt).unwrap();
147
148 data
149 }
150}
151
152impl TryFrom<&[u8]> for Argon2Parameters {
153 type Error = Error;
154
155 fn try_from(data: &[u8]) -> Result<Self> {
156 let mut data_cursor = Cursor::new(data);
157 let dc_version = data_cursor.read_u32::<LittleEndian>()?;
158 let length = data_cursor.read_u32::<LittleEndian>()?;
159 let lanes = data_cursor.read_u32::<LittleEndian>()?;
160 let memory = data_cursor.read_u32::<LittleEndian>()?;
161 let iterations = data_cursor.read_u32::<LittleEndian>()?;
162
163 let (variant, version) = match (
165 Variant::from_u32(data_cursor.read_u8()? as u32),
166 Version::from_u32(data_cursor.read_u8()? as u32),
167 ) {
168 (Ok(variant), Ok(version)) => (variant, version),
169 _ => return Err(Error::InvalidData),
170 };
171
172 let associated_data_length = data_cursor.read_u32::<LittleEndian>()? as usize;
173 let remaining = data.len() - (data_cursor.position() as usize);
174 if remaining < associated_data_length {
175 return Err(Error::InvalidLength);
176 }
177
178 let mut associated_data = vec![0u8; associated_data_length];
179 data_cursor.read_exact(&mut associated_data)?;
180
181 let salt_length = data_cursor.read_u32::<LittleEndian>()? as usize;
182 let remaining = data.len() - (data_cursor.position() as usize);
183 if remaining < salt_length {
184 return Err(Error::InvalidLength);
185 }
186
187 let mut salt = vec![0u8; salt_length];
188 data_cursor.read_exact(&mut salt)?;
189
190 Ok(Argon2Parameters {
191 associated_data,
192 secret_key: Vec::new(),
193 length,
194 lanes,
195 memory,
196 iterations,
197 variant,
198 version,
199 dc_version,
200 salt,
201 })
202 }
203}
204
205impl Argon2Parameters {
206 pub fn compute(&self, password: &[u8]) -> Result<Vec<u8>> {
208 let config = Config {
209 ad: &self.associated_data,
210 secret: &self.secret_key,
211 hash_length: self.length,
212 lanes: self.lanes,
213 mem_cost: self.memory,
214 time_cost: self.iterations,
215 variant: self.variant,
216 version: self.version,
217 thread_mode: argon2::ThreadMode::Sequential,
218 };
219
220 Ok(argon2::hash_raw(password, &self.salt, &config)?)
221 }
222}
223
224#[test]
225fn test_argon2() {
226 use std::convert::TryInto;
227
228 let mut config = Argon2Parameters::default();
229 config.iterations = 2;
230 config.memory = 32;
231
232 let hash1 = config.compute(b"Password1").unwrap();
234 let config_vec: Vec<u8> = (&config).into();
235
236 assert_ne!(config_vec.len(), 0);
237
238 let config: Argon2Parameters = config_vec.as_slice().try_into().unwrap();
239
240 let hash2 = config.compute(b"Password1").unwrap();
242
243 let hash3 = config.compute(b"Password2").unwrap();
245
246 let mut config = Argon2Parameters::default();
248 config.iterations = 2;
249 config.memory = 32;
250 let hash4 = config.compute(b"Password1").unwrap();
251
252 let mut config5 = Argon2Parameters::default();
254 config5.iterations = 2;
255 config5.memory = 32;
256 config5.length = 41;
257 let hash5 = config5.compute(b"Password1").unwrap();
258
259 assert_eq!(hash1.len(), config.length as usize);
260 assert_eq!(hash1, hash2);
261 assert_ne!(hash1, hash3);
262 assert_ne!(hash1, hash4);
263 assert_eq!(hash5.len(), config5.length as usize);
264}