argonautica/hasher.rs
1use futures::Future;
2use futures_cpupool::CpuPool;
3use scopeguard;
4
5use config::defaults::{default_cpu_pool, default_lanes};
6use config::{Backend, HasherConfig, Variant, Version};
7use input::{AdditionalData, Container, Password, Salt, SecretKey};
8use output::HashRaw;
9use {Error, ErrorKind};
10
11impl<'a> Default for Hasher<'a> {
12 /// Same as the [`new`](struct.Hasher.html#method.new) method
13 fn default() -> Hasher<'static> {
14 Hasher {
15 additional_data: None,
16 config: HasherConfig::default(),
17 password: None,
18 salt: Salt::default(),
19 secret_key: None,
20 }
21 }
22}
23
24/// <b><u>One of the two main structs.</u></b> Use it to turn passwords into hashes
25#[derive(Debug)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
28pub struct Hasher<'a> {
29 pub(crate) additional_data: Option<AdditionalData>,
30 pub(crate) config: HasherConfig,
31 #[cfg_attr(feature = "serde", serde(skip_serializing, skip_deserializing))]
32 pub(crate) password: Option<Password<'a>>,
33 pub(crate) salt: Salt,
34 #[cfg_attr(feature = "serde", serde(skip_serializing, skip_deserializing))]
35 pub(crate) secret_key: Option<SecretKey<'a>>,
36}
37
38impl<'a> Hasher<'a> {
39 /// Creates a new [`Hasher`](struct.Hasher.html) with a sensible default configuration
40 /// for the average machine (e.g. an early-2014 MacBook Air).
41 ///
42 /// <b>Note: If you are using this library to hash user passwords for storage in a database,
43 /// it is recommended that you adjust these settings for your machine (primarily `iterations`,
44 /// and `memory_size`) until the time it takes to hash a password is approximately 300-500
45 /// milliseconds</b>.
46 ///
47 /// There is a script in the examples directory that will show you the various configuration
48 /// options for your machine that produce hashing times between 300 and 500 milliseconds
49 /// (Don't forget to run it with the `--release` and `--features="simd"` flags). Alternatively,
50 /// you can clone the repository and run the benchmark suite with
51 /// `cargo bench --features="benches simd" -- inputs`, which will take longer but which runs
52 /// many iterations for each configuration scenario; so it provides information about
53 /// distributions of running time (e.g. mean, 95% confidence intervals, etc.) as opposed
54 /// to just point estimates.
55 ///
56 /// Here are the default configuration options:
57 /// * `backend`: [`Backend::C`](config/enum.Backend.html#variant.C)
58 /// * `cpu_pool`: A [`CpuPool`](https://docs.rs/futures-cpupool/0.1.8/futures_cpupool/struct.CpuPool.html) ...
59 /// * with threads equal to the number of logical cores on your machine
60 /// * that is lazily created, i.e. created only if / when you call the methods
61 /// that need it ([`hash_non_blocking`](struct.Hasher.html#method.hash_non_blocking) or
62 /// [`hash_raw_non_blocking`](struct.Hasher.html#method.hash_raw_non_blocking))
63 /// * `hash_len`: `32` bytes
64 /// * `iterations`: `192`
65 /// * `lanes`: The number of logical cores on your machine
66 /// * `memory_size`: `4096` kibibytes
67 /// * `opt_out_of_secret_key`: `false`
68 /// * `password_clearing`: `false`
69 /// * `salt`: random [`Salt`](input/struct.Salt.html) of length 32 bytes that renews with every hash
70 /// * `secret_key_clearing`: `false`
71 /// * `threads`: The number of logical cores on your machine
72 /// * `variant`: [`Variant::Argon2id`](config/enum.Variant.html#variant.Argon2id)
73 /// * `version`: [`Version::_0x13`](config/enum.Verion.html#variant._0x13)
74 pub fn new() -> Hasher<'static> {
75 Hasher::default()
76 }
77 /// Creates a new [`Hasher`](struct.Hasher.html) that is <b>fast but <u>highly</u> insecure</b>.
78 /// If for some reason you'd like to use Argon2 for hashing where security is not an issue,
79 /// you can use this configuration. It sets hash length to 32 bytes (256 bits), uses only
80 /// 1 iteration, sets memory size to the minimum of 8 * the number of lanes, uses a
81 /// deterministic salt of the minimum length of 8 bytes, and opts out of a secret key.
82 /// All other configuration options are the same as the defaults. On the developer's
83 /// early-2014 Macbook Air, this configuration hashes the full text of Shakespear's Hamlet
84 /// in approximately 1 millisecond (on average). [MD5](https://github.com/stainless-steel/md5)
85 /// does it in about half the time and [sha2](https://github.com/RustCrypto/hashes) with the
86 /// SHA-256 algorithm performs about the same as `argonautica`
87 pub fn fast_but_insecure() -> Hasher<'a> {
88 fn memory_size(lanes: u32) -> u32 {
89 let mut counter = 1;
90 let memory_size = loop {
91 if 2u32.pow(counter) < 8 * lanes {
92 counter += 1;
93 continue;
94 } else {
95 break 2u32.pow(counter);
96 }
97 };
98 memory_size
99 }
100 let lanes = default_lanes();
101 let mut hasher = Hasher::default();
102 hasher
103 .configure_hash_len(32)
104 .configure_iterations(1)
105 .configure_lanes(lanes)
106 .configure_memory_size(memory_size(lanes))
107 .configure_password_clearing(false)
108 .configure_secret_key_clearing(false)
109 .configure_threads(lanes)
110 .opt_out_of_secret_key(true)
111 .with_salt(&[0u8; 8][..]);
112 hasher
113 }
114 /// Allows you to configure [`Hasher`](struct.Hasher.html) with a custom backend. The
115 /// default backend is [`Backend::C`](config/enum.Backend.html#variant.C), <i>which is
116 /// currently the only backend supported. A Rust backend is planned, but is not currently
117 /// available. If you configure a [`Hasher`](struct.Hasher.html) with
118 /// [`Backend::Rust`](config/enum.Backend.html#variant.Rust) it will error when you
119 /// call [`hash`](struct.Hasher.html#method.hash),
120 /// [`hash_raw`](struct.Hasher.html#method.hash_raw) or their non-blocking equivalents</i>
121 pub fn configure_backend(&mut self, backend: Backend) -> &mut Hasher<'a> {
122 self.config.set_backend(backend);
123 self
124 }
125 /// Allows you to configure [`Hasher`](struct.Hasher.html) with a custom
126 /// [`CpuPool`](https://docs.rs/futures-cpupool/0.1.8/futures_cpupool/struct.CpuPool.html).
127 /// The default [`Hasher`](struct.Hasher.html) does not have a cpu pool, which is
128 /// only needed for the [`hash_non_blocking`](struct.Hasher.html#method.hash_non_blocking)
129 /// and [`hash_raw_non_blocking`](struct.Hasher.html#method.hash_raw_non_blocking) methods.
130 /// If you call either of these methods without a cpu pool, a default cpu pool will be created
131 /// for you on the fly; so even if you never configure [`Hasher`](struct.Hasher.html) with
132 /// this method you can still use the non-blocking hashing methods.
133 /// The default cpu pool has as many threads as the number of logical cores on your machine
134 pub fn configure_cpu_pool(&mut self, cpu_pool: CpuPool) -> &mut Hasher<'a> {
135 self.config.set_cpu_pool(cpu_pool);
136 self
137 }
138 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom hash length
139 /// (in number of bytes). The default is `32`.
140 ///
141 /// See [configuration example](index.html#configuration) for a more detailed discussion
142 /// of this parameter
143 pub fn configure_hash_len(&mut self, hash_len: u32) -> &mut Hasher<'a> {
144 self.config.set_hash_len(hash_len);
145 self
146 }
147 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom number of
148 /// iterations. The default is `192`.
149 ///
150 /// See [configuration example](index.html#configuration) for a more details on this parameter
151 pub fn configure_iterations(&mut self, iterations: u32) -> &mut Hasher<'a> {
152 self.config.set_iterations(iterations);
153 self
154 }
155 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom number of
156 /// lanes. The default is the number of physical cores on your machine.
157 ///
158 /// See [configuration example](index.html#configuration) for a more details on this parameter
159 pub fn configure_lanes(&mut self, lanes: u32) -> &mut Hasher<'a> {
160 self.config.set_lanes(lanes);
161 self
162 }
163 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom memory size
164 /// (in kibibytes). The default is `4096`.
165 ///
166 /// See [configuration example](index.html#configuration) for a more details on this parameter
167 pub fn configure_memory_size(&mut self, memory_size: u32) -> &mut Hasher<'a> {
168 self.config.set_memory_size(memory_size);
169 self
170 }
171 /// Allows you to configure [`Hasher`](struct.Hasher.html) to erase the password bytes
172 /// after each call to [`hash`](struct.Hasher.html#method.hash),
173 /// [`hash_raw`](struct.Hasher#method.hash_raw), or their non-blocking equivalents.
174 /// The default is to <b>not</b> clear out the password
175 /// bytes (i.e. `false`). If you set this option to `true`, you must provide
176 /// [`Hasher`](struct.Hasher.html) with a mutable password, e.g. a password
177 /// constructed from a `String`, `Vec<u8>`, `&mut str`, `&mut [u8]`, etc. as opposed to
178 /// one constructed from a `&str`, `&[u8]`, etc., or else hashing will return an
179 /// [`Error`](struct.Error.html).
180 ///
181 /// See [configuration example](index.html#configuration) for a more details on this parameter
182 pub fn configure_password_clearing(&mut self, boolean: bool) -> &mut Hasher<'a> {
183 self.config.set_password_clearing(boolean);
184 self
185 }
186 /// Allows you to configure [`Hasher`](struct.Hasher.html) to erase the secret key bytes
187 /// after each call to [`hash`](struct.Hasher.html#method.hash),
188 /// [`hash_raw`](struct.Hasher#method.hash_raw), or their non-blocking equivalents.
189 /// The default is to <b>not</b> clear out the secret key
190 /// bytes (i.e. `false`). If you set this option to `true`, you must provide
191 /// [`Hasher`](struct.Hasher.html) with a mutable secret key, e.g. a secret key
192 /// constructed from a `String`, `Vec<u8>`, `&mut str`, `&mut [u8]`, etc. as opposed to
193 /// one constructed from a `&str`, `&[u8]`, etc., or else hashing will return an
194 /// [`Error`](struct.Error.html).
195 ///
196 /// See [configuration example](index.html#configuration) for a more details on this parameter
197 pub fn configure_secret_key_clearing(&mut self, boolean: bool) -> &mut Hasher<'a> {
198 self.config.set_secret_key_clearing(boolean);
199 self
200 }
201 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom number of
202 /// threads. The default is the number of physical cores on your machine. If you choose
203 /// a number of threads that is greater than the lanes configuration,
204 /// [`Hasher`](struct.Hasher.html) will use the minimum of the two.
205 ///
206 /// See [configuration example](index.html#configuration) for a more details on this parameter
207 pub fn configure_threads(&mut self, threads: u32) -> &mut Hasher<'a> {
208 self.config.set_threads(threads);
209 self
210 }
211 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom Argon2
212 /// variant. The default is [`Variant::Argon2id`](config/enum.Variant.html#variant.Argon2id).
213 /// Do <b>not</b> use a different variant unless you have a specific reason to do so.
214 ///
215 /// See [configuration example](index.html#configuration) for a more details on this parameter
216 pub fn configure_variant(&mut self, variant: Variant) -> &mut Hasher<'a> {
217 self.config.set_variant(variant);
218 self
219 }
220 /// Allows you to configure [`Hasher`](struct.Hasher.html) to use a custom Argon2 version.
221 /// The default and latest (as of 5/18) is
222 /// [`Version::_0x13`](config/enum.Version.html#variant._0x13).
223 /// Do <b>not</b> use a different version unless you have a specific reason to do so.
224 ///
225 /// See [configuration example](index.html#configuration) for a more details on this parameter
226 pub fn configure_version(&mut self, version: Version) -> &mut Hasher<'a> {
227 self.config.set_version(version);
228 self
229 }
230 /// <b><u>The primary method (blocking version).</u></b>
231 ///
232 /// After you have configured a [`Hasher`](struct.Hasher.html) to your liking and provided
233 /// it will all the data you would like to hash, e.g.
234 /// * a [`Password`](input/struct.Password.html),
235 /// * a [`Salt`](input/struct.Password.html) (note: it is recommened you use the default random salt),
236 /// * a [`SecretKey`](input/struct.SecretKey.html),
237 /// * [`AdditionalData`](input/struct.AdditionalData.html) (optional),
238 ///
239 /// call this method in order to produce a string-encoded hash, which is safe to store in a
240 /// database and against which you can verify passwords later
241 pub fn hash(&mut self) -> Result<String, Error> {
242 let hash_raw = self.hash_raw()?;
243 let hash = hash_raw.encode_rust();
244 Ok(hash)
245 }
246 /// <b><u>The primary method (non-blocking version).</u></b>
247 ///
248 /// Same as [`hash`](struct.Hasher.html#method.hash) except it returns a
249 /// [`Future`](https://docs.rs/futures/0.1.21/futures/future/trait.Future.html)
250 /// instead of a [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
251 pub fn hash_non_blocking(&mut self) -> impl Future<Item = String, Error = Error> {
252 self.hash_raw_non_blocking().and_then(|hash_raw| {
253 let hash = hash_raw.encode_rust();
254 Ok::<_, Error>(hash)
255 })
256 }
257 /// Like the [`hash`](struct.Hasher.html#method.hash) method, but instead of producing
258 /// an string-encoded hash, it produces a [`HashRaw`](output/struct.HashRaw.html) struct
259 /// that contains all the components of the string-encoded version, including the raw
260 /// hash bytes and the raw salt bytes. In general, you should prefer to use the
261 /// [`hash`](struct.Hasher.html#method.hash) method instead of this method
262 pub fn hash_raw(&mut self) -> Result<HashRaw, Error> {
263 let mut hasher = scopeguard::guard(self, |hasher| {
264 hasher.clear();
265 });
266 hasher.validate()?;
267 hasher.salt.update()?;
268 let hash_raw = match hasher.config.backend() {
269 Backend::C => hasher.hash_raw_c()?,
270 Backend::Rust => return Err(Error::new(ErrorKind::BackendUnsupportedError)),
271 };
272 Ok(hash_raw)
273 }
274 /// Same as [`hash_raw`](struct.Hasher.html#method.hash) except it returns a
275 /// [`Future`](https://docs.rs/futures/0.1.21/futures/future/trait.Future.html)
276 /// instead of a [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
277 pub fn hash_raw_non_blocking(&mut self) -> impl Future<Item = HashRaw, Error = Error> {
278 let hasher = scopeguard::guard(self, |hasher| {
279 hasher.clear();
280 });
281 let mut hasher = hasher.to_owned();
282 match hasher.config.cpu_pool() {
283 Some(cpu_pool) => cpu_pool.spawn_fn(move || hasher.hash_raw()),
284 None => {
285 let cpu_pool = default_cpu_pool();
286 hasher.config.set_cpu_pool(cpu_pool.clone());
287 cpu_pool.spawn_fn(move || hasher.hash_raw())
288 }
289 }
290 }
291 /// As an extra security measure, if you want to hash without a secret key, which
292 /// is not recommended, you must explicitly declare that this is your intention
293 /// by calling this method and setting the `opt_out_of_secret_key` configuration to
294 /// `true` (by default, it is set to `false`); otherwise hashing will return an error
295 /// when you fail to provide a secret key
296 pub fn opt_out_of_secret_key(&mut self, boolean: bool) -> &mut Hasher<'a> {
297 self.config.set_opt_out_of_secret_key(boolean);
298 self
299 }
300 /// Clones the [`Hasher`](struct.Hasher.html), returning a new
301 /// [`Hasher`](struct.Hasher.html) with a `static` lifetime. Use this method if you
302 /// would like to move a [`Hasher`](struct.Hasher.html) to another thread
303 pub fn to_owned(&self) -> Hasher<'static> {
304 let password = self.password.as_ref().map(|password| password.to_owned());
305 let secret_key = self
306 .secret_key
307 .as_ref()
308 .map(|secret_key| secret_key.to_owned());
309 Hasher {
310 additional_data: self.additional_data.clone(),
311 config: self.config.clone(),
312 password,
313 salt: self.salt.clone(),
314 secret_key,
315 }
316 }
317 /// Allows you to add some additional data to the [`Hasher`](struct.Hasher.html)
318 /// that will be hashed alongside the [`Password`](input/struct.Password.html) and
319 /// other pieces of data you would like to hash (i.e. the [`Salt`](input/struct.Salt.html) and
320 /// an optional [`SecretKey`](input/struct.SecretKey.html)).
321 ///
322 /// Including additional data in your hash is not very common; so it is unlikely you will
323 /// need to use this method. If, however, you do add additional data, note that it is like
324 /// a secret key in that it will be required later in order to verify passwords, and
325 /// it is not stored in the string-encoded version of the hash, meaning you will have to
326 /// provide it manually to a [`Verifier`](struct.Verifier.html)
327 pub fn with_additional_data<AD>(&mut self, additional_data: AD) -> &mut Hasher<'a>
328 where
329 AD: Into<AdditionalData>,
330 {
331 self.additional_data = Some(additional_data.into());
332 self
333 }
334 /// Allows you to provide a [`Hasher`](struct.Hasher.html) with the password you would like
335 /// to hash. Hashing requires a password; so you must call this method before calling
336 /// [`hash`](struct.Hasher.html#method.hash), [`hash_raw`](struct.Hasher.html#method.hash_raw),
337 /// or their non-blocking version
338 pub fn with_password<P>(&mut self, password: P) -> &mut Hasher<'a>
339 where
340 P: Into<Password<'a>>,
341 {
342 self.password = Some(password.into());
343 self
344 }
345 /// Allows you to provide [`Hasher`](struct.Hasher.html) with a custom
346 /// [`Salt`](input/struct.Salt.html) to include in the hash. The default
347 /// [`Hasher`](struct.Hasher.html) is configured to use a random
348 /// [`Salt`](input/struct.Salt.html) of 32 bytes; so there is no need
349 /// to call this method. If you would like to use a random
350 /// [`Salt`](input/struct.Salt.html) of different length, you can call this method with
351 /// `Salt::random(your_custom_length_in_bytes)`. Using a deterministic
352 /// [`Salt`](input/struct.Salt.html) is possible, but discouraged
353 pub fn with_salt<S>(&mut self, salt: S) -> &mut Hasher<'a>
354 where
355 S: Into<Salt>,
356 {
357 self.salt = salt.into();
358 self
359 }
360 /// Allows you to provide [`Hasher`](struct.Hasher.html) with a secret key that will be used
361 /// to create the hash. The secret key will not be included in the hash output, meaning you
362 /// must save it somewhere (ideally outside your code) to use later, as the only way to
363 /// verify passwords against the hash later is to know the secret key. This library
364 /// encourages the use of a secret key
365 pub fn with_secret_key<SK>(&mut self, secret_key: SK) -> &mut Hasher<'a>
366 where
367 SK: Into<SecretKey<'a>>,
368 {
369 self.secret_key = Some(secret_key.into());
370 self
371 }
372 /// Read-only access to the [`Hasher`](struct.Hasher.html)'s
373 /// [`AdditionalData`](input/struct.AdditionalData.html), if any
374 pub fn additional_data(&self) -> Option<&AdditionalData> {
375 self.additional_data.as_ref()
376 }
377 /// Read-only access to the [`Hasher`](struct.Hasher.html)'s
378 /// [`HasherConfig`](config/struct.HasherConfig.html)
379 pub fn config(&self) -> &HasherConfig {
380 &self.config
381 }
382 /// Read-only access to the [`Hasher`](struct.Hasher.html)'s
383 /// [`Password`](input/struct.Password.html), if any
384 pub fn password(&self) -> Option<&Password<'a>> {
385 self.password.as_ref()
386 }
387 /// Read-only access to the [`Hasher`](struct.Hasher.html)'s [`Salt`](input/struct.Salt.html)
388 pub fn salt(&self) -> &Salt {
389 &self.salt
390 }
391 /// Read-only access to the [`Hasher`](struct.Hasher.html)'s
392 /// [`SecretKey`](input/struct.SecretKey.html), if any
393 pub fn secret_key(&self) -> Option<&SecretKey<'a>> {
394 self.secret_key.as_ref()
395 }
396}
397
398impl<'a> Hasher<'a> {
399 pub(crate) fn clear(&mut self) {
400 if self.password.is_some() && self.config.password_clearing() {
401 {
402 let password_mut_ref = self.password.as_mut().unwrap();
403 match password_mut_ref.inner {
404 Container::Borrowed(_) => (),
405 Container::BorrowedMut(ref mut bytes) => {
406 unsafe { ::std::ptr::write_bytes(bytes.as_mut_ptr(), 0, bytes.len()) };
407 }
408 Container::Owned(ref mut bytes) => {
409 unsafe { ::std::ptr::write_bytes(bytes.as_mut_ptr(), 0, bytes.len()) };
410 }
411 }
412 }
413 self.password = None;
414 }
415 if self.secret_key.is_some() && self.config.secret_key_clearing() {
416 {
417 let secret_key_mut_ref = self.secret_key.as_mut().unwrap();
418 match secret_key_mut_ref.inner {
419 Container::Borrowed(_) => (),
420 Container::BorrowedMut(ref mut bytes) => {
421 unsafe { ::std::ptr::write_bytes(bytes.as_mut_ptr(), 0, bytes.len()) };
422 }
423 Container::Owned(ref mut bytes) => {
424 unsafe { ::std::ptr::write_bytes(bytes.as_mut_ptr(), 0, bytes.len()) };
425 }
426 }
427 }
428 self.secret_key = None;
429 }
430 }
431 pub(crate) fn validate(&self) -> Result<(), Error> {
432 self.config.validate()?;
433 if let Some(ref additional_data) = self.additional_data {
434 additional_data.validate()?;
435 }
436 match self.password {
437 Some(ref password) => {
438 password.validate()?;
439 if self.config.password_clearing() && !password.is_mutable() {
440 return Err(Error::new(ErrorKind::PasswordImmutableError));
441 }
442 }
443 None => return Err(Error::new(ErrorKind::PasswordMissingError)),
444 }
445 self.salt.validate()?;
446 match self.secret_key {
447 Some(ref secret_key) => {
448 secret_key.validate()?;
449 if self.config.secret_key_clearing() && !secret_key.is_mutable() {
450 return Err(Error::new(ErrorKind::SecretKeyImmutableError));
451 }
452 }
453 None => {
454 if !self.config.opt_out_of_secret_key() {
455 return Err(Error::new(ErrorKind::SecretKeyMissingError));
456 }
457 }
458 }
459 Ok(())
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466 use config::{Variant, Version};
467
468 struct Test {
469 variant: Variant,
470 version: Version,
471 expected: Vec<u8>,
472 }
473
474 impl Test {
475 fn run(&self) {
476 let mut hasher = Hasher::default();
477 let raw_hash = hasher
478 .configure_hash_len(32)
479 .configure_iterations(3)
480 .configure_lanes(4)
481 .configure_memory_size(32)
482 .configure_threads(4)
483 .configure_variant(self.variant)
484 .configure_version(self.version)
485 .with_additional_data(vec![4; 12])
486 .with_password(vec![1; 32])
487 .with_salt(vec![2; 16])
488 .with_secret_key(vec![3; 8])
489 .hash_raw()
490 .unwrap();
491 assert_eq!(raw_hash.raw_hash_bytes(), self.expected.as_slice());
492 }
493 }
494
495 #[test]
496 fn test_hasher_0x10_2d() {
497 Test {
498 variant: Variant::Argon2d,
499 version: Version::_0x10,
500 expected: vec![
501 0x96, 0xa9, 0xd4, 0xe5, 0xa1, 0x73, 0x40, 0x92, 0xc8, 0x5e, 0x29, 0xf4, 0x10, 0xa4,
502 0x59, 0x14, 0xa5, 0xdd, 0x1f, 0x5c, 0xbf, 0x08, 0xb2, 0x67, 0x0d, 0xa6, 0x8a, 0x02,
503 0x85, 0xab, 0xf3, 0x2b,
504 ],
505 }.run();
506 }
507
508 #[test]
509 fn test_hasher_0x10_2i() {
510 Test {
511 variant: Variant::Argon2i,
512 version: Version::_0x10,
513 expected: vec![
514 0x87, 0xae, 0xed, 0xd6, 0x51, 0x7a, 0xb8, 0x30, 0xcd, 0x97, 0x65, 0xcd, 0x82, 0x31,
515 0xab, 0xb2, 0xe6, 0x47, 0xa5, 0xde, 0xe0, 0x8f, 0x7c, 0x05, 0xe0, 0x2f, 0xcb, 0x76,
516 0x33, 0x35, 0xd0, 0xfd,
517 ],
518 }.run();
519 }
520
521 #[test]
522 fn test_hasher_0x10_2id() {
523 Test {
524 variant: Variant::Argon2id,
525 version: Version::_0x10,
526 expected: vec![
527 0xb6, 0x46, 0x15, 0xf0, 0x77, 0x89, 0xb6, 0x6b, 0x64, 0x5b, 0x67, 0xee, 0x9e, 0xd3,
528 0xb3, 0x77, 0xae, 0x35, 0x0b, 0x6b, 0xfc, 0xbb, 0x0f, 0xc9, 0x51, 0x41, 0xea, 0x8f,
529 0x32, 0x26, 0x13, 0xc0,
530 ],
531 }.run();
532 }
533
534 #[test]
535 fn test_hasher_0x13_2d() {
536 Test {
537 variant: Variant::Argon2d,
538 version: Version::_0x13,
539 expected: vec![
540 0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97, 0x53, 0x71, 0xd3, 0x09, 0x19, 0x73,
541 0x42, 0x94, 0xf8, 0x68, 0xe3, 0xbe, 0x39, 0x84, 0xf3, 0xc1, 0xa1, 0x3a, 0x4d, 0xb9,
542 0xfa, 0xbe, 0x4a, 0xcb,
543 ],
544 }.run();
545 }
546
547 #[test]
548 fn test_hasher_0x13_2i() {
549 Test {
550 variant: Variant::Argon2i,
551 version: Version::_0x13,
552 expected: vec![
553 0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa, 0x13, 0xf0, 0xd7, 0x7f, 0x24, 0x94,
554 0xbd, 0xa1, 0xc8, 0xde, 0x6b, 0x01, 0x6d, 0xd3, 0x88, 0xd2, 0x99, 0x52, 0xa4, 0xc4,
555 0x67, 0x2b, 0x6c, 0xe8,
556 ],
557 }.run();
558 }
559
560 #[test]
561 fn test_hasher_0x13_2id() {
562 Test {
563 variant: Variant::Argon2id,
564 version: Version::_0x13,
565 expected: vec![
566 0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c, 0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b,
567 0x53, 0xc9, 0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e, 0xb5, 0x25, 0x20, 0xe9,
568 0x6b, 0x01, 0xe6, 0x59,
569 ],
570 }.run();
571 }
572
573 #[test]
574 fn test_hasher_clearing() {
575 // Password is cleared and secret key remains
576 let mut hasher = Hasher::default();
577 let hash = hasher
578 .configure_password_clearing(true)
579 .configure_secret_key_clearing(false)
580 .with_password("password")
581 .with_secret_key("secret")
582 .hash();
583 match hash {
584 Ok(_) => panic!("Should return an error"),
585 Err(e) => assert_eq!(e, Error::new(ErrorKind::PasswordImmutableError)),
586 }
587 assert!(hasher.password().is_none());
588 assert!(hasher.secret_key().is_some());
589
590 // Secret key is cleared and password remains
591 let mut hasher = Hasher::default();
592 let hash = hasher
593 .configure_password_clearing(false)
594 .configure_secret_key_clearing(true)
595 .with_password("password")
596 .with_secret_key("secret")
597 .hash();
598 match hash {
599 Ok(_) => panic!("Should return an error"),
600 Err(e) => assert_eq!(e, Error::new(ErrorKind::SecretKeyImmutableError)),
601 }
602 assert!(hasher.password().is_some());
603 assert!(hasher.secret_key().is_none());
604 }
605
606 #[test]
607 fn test_hasher_fast_but_insecure() {
608 let mut hasher = Hasher::fast_but_insecure();
609 let _ = hasher.with_password("P@ssw0rd").hash().unwrap();
610 }
611
612 #[cfg(feature = "serde")]
613 #[test]
614 fn test_hasher_serialization() {
615 use serde_json;
616
617 let password = "P@ssw0rd";
618 let secret_key = "secret";
619
620 let mut hasher1 = Hasher::default();
621 hasher1
622 .configure_password_clearing(false)
623 .with_additional_data("additional data")
624 .with_password(password)
625 .with_secret_key(secret_key)
626 .with_salt("somesalt");
627 let hash1 = hasher1.hash().expect("failed to hash");
628 let hash_raw1 = hasher1.hash_raw().expect("failed to hash_raw");
629
630 // Serialize Hasher
631 let j = serde_json::to_string_pretty(&hasher1).expect("failed to serialize hasher");
632 // Deserialize Hasher
633 let mut hasher2: Hasher = serde_json::from_str(&j).expect("failed to deserialize hasher");
634 // Assert that password and secret key have been erased
635 assert_eq!(hasher2.password(), None);
636 assert_eq!(hasher2.secret_key(), None);
637 // Assert that calling hash or hash_raw produces an error
638 assert!(hasher2.hash().is_err());
639 assert!(hasher2.hash_raw().is_err());
640 // Add a password and secret key and ensure hash and hash_raw now work
641 hasher2.with_password(password).with_secret_key(secret_key);
642 let hash2 = hasher2.hash().expect("failed to hash");
643 let hash_raw2 = hasher2.hash_raw().expect("failed to hash_raw");
644 // Assert hashes match originals
645 assert_eq!(hash1, hash2);
646 assert_eq!(hash_raw1, hash_raw2);
647 }
648
649 #[test]
650 fn test_send() {
651 fn assert_send<T: Send>() {}
652 assert_send::<Hasher>();
653 }
654
655 #[test]
656 fn test_sync() {
657 fn assert_sync<T: Sync>() {}
658 assert_sync::<Hasher>();
659 }
660
661 #[cfg(feature = "serde")]
662 #[test]
663 fn test_serialize() {
664 use serde;
665 fn assert_serialize<T: serde::Serialize>() {}
666 assert_serialize::<Hasher>();
667 }
668
669 #[cfg(feature = "serde")]
670 #[test]
671 fn test_deserialize() {
672 use serde;
673 fn assert_deserialize<'de, T: serde::Deserialize<'de>>() {}
674 assert_deserialize::<Hasher>();
675 }
676}