Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_hash/src/kdf.rs

47.0 KiB, 224 runs

created by r1870400018:353, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! This crate provides a concrete interface for multiple Key Derivation Function algorithms.
2//! Currently it uses only one, Argon2. The Argon2 tag depends on the lane count `p` by design, so
3//! a tag produced with `p = 4` differs from one produced with `p = 1` for otherwise identical
4//! inputs. Every parameter that changes the tag must therefore survive a round trip through the
5//! encoded configuration string, or the derived key will silently differ. The default here is a
6//! single lane, which the Open Worldwide Application Security Project (OWASP) recommends for
7//! Argon2id, but the encoding does not assume it.
8//!
9//! # Encoded strings
10//!
11//! `encode_to_string` emits the Password Hashing Competition (PHC) string format exactly, as
12//! `$argon2id$v=19$m=<mem_cost>,t=<time_cost>,p=<lanes>$<salt>$<hash>`, so that other Argon2
13//! implementations can read it. The tag length is implied by the length of the encoded hash.
14//!
15//! `encode_cfg_to_string` emits the same string sans hash. PHC defines no hash-less form, and
16//! without the hash the tag length would be lost, so the tag length is carried explicitly in an
17//! `l=<hash_length>` option, as `$argon2id$v=19$m=...,t=...,p=...,l=32$<salt>`. Associated data,
18//! when present, is carried in the `data=<base64>` option used by the reference implementation.
19//!
20//! The secret (pepper) is deliberately never encoded. A caller using one must set it on the
21//! decoded state out of band; the encoders return an error rather than drop it silently.
22//!
23use oxedyne_fe2o3_core::{
24 prelude::*,
25 alt::{
26 Alt,
27 DefAlt,
28 },
29 mem::Extract,
30 rand::Rand,
31};
32use oxedyne_fe2o3_jdat::{
33 prelude::*,
34 try_extract_tup2dat,
35 tup2dat,
36};
37use oxedyne_fe2o3_iop_hash::kdf::KeyDeriver;
38use oxedyne_fe2o3_namex::id::{
39 LocalId,
40 InNamex,
41 NamexId,
42};
43
44use std::{
45 fmt,
46 str,
47};
48
49use argon2;
50use base64;
51
52
53#[derive(Clone, Debug, Default)]
54pub struct KeyDerivConfig {
55 id: LocalId, // Key derivation function id.
56 cfg: String, // String encoded configuration information.
57}
58
59impl ToDat for KeyDerivConfig {
60 fn to_dat(&self) -> Outcome<Dat> {
61 Ok(tup2dat![
62 res!(self.id.to_dat()),
63 Dat::Str(self.cfg.clone()),
64 ])
65 }
66}
67
68impl FromDat for KeyDerivConfig {
69 fn from_dat(dat: Dat) -> Outcome<Self> {
70 let mut result = Self::default();
71 let mut v = try_extract_tup2dat!(dat);
72 result.id = res!(LocalId::from_dat(v[0].extract()));
73 result.cfg = try_extract_dat!(v[1].extract(), Str);
74 Ok(result)
75 }
76}
77
78impl KeyDerivConfig {
79
80 pub fn new(id: LocalId, cfg: String,) -> Self {
81 Self {
82 id,
83 cfg,
84 }
85 }
86 pub fn id(&self) -> &LocalId { &self.id }
87 pub fn config(&self) -> &String { &self.cfg }
88}
89
90#[derive(Clone, Eq, PartialEq)]
91pub enum KeyDerivationScheme {
92 Argon2(Argon2State),
93}
94
95impl fmt::Display for KeyDerivationScheme {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(f, "{:?}", self)
98 }
99}
100
101impl fmt::Debug for KeyDerivationScheme {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::Argon2(state) => {
105 let suffix = match state.version {
106 argon2::Version::Version10 => "_v0x10",
107 argon2::Version::Version13 => "_v0x13",
108 };
109 let mut variant = state.variant.as_uppercase_str().to_string();
110 variant.push_str(suffix);
111 write!(f, "{}", variant)
112 },
113 }
114 }
115}
116
117impl InNamex for KeyDerivationScheme {
118
119 fn name_id(&self) -> Outcome<NamexId> {
120 Ok(match self {
121 Self::Argon2(Argon2State {
122 variant: argon2::Variant::Argon2d,
123 version: argon2::Version::Version10,
124 ..
125 }) => res!(NamexId::try_from("sl6UqXw8nwjEzDMyO9TMlCzwUKtNDHgpDq+DOYjUukU=")),
126 //
127 Self::Argon2(Argon2State {
128 variant: argon2::Variant::Argon2i,
129 version: argon2::Version::Version10,
130 ..
131 }) => res!(NamexId::try_from("cu9pC9q0wONz80OCjaxtMJiyv4zqMUuljhDKMOiUE4o=")),
132 //
133 Self::Argon2(Argon2State {
134 variant: argon2::Variant::Argon2id,
135 version: argon2::Version::Version10,
136 ..
137 }) => res!(NamexId::try_from("D4NkV8upthkHwlPrkfq/wqo+S4dnXwU7EkM4vg4h4jo=")),
138 //
139 Self::Argon2(Argon2State {
140 variant: argon2::Variant::Argon2d,
141 version: argon2::Version::Version13,
142 ..
143 }) => res!(NamexId::try_from("bbj8wR0HwO6JJoWDWgauLWOacUfLfJZVnoWEyTYIfqI=")),
144 //
145 Self::Argon2(Argon2State {
146 variant: argon2::Variant::Argon2i,
147 version: argon2::Version::Version13,
148 ..
149 }) => res!(NamexId::try_from("6UQjGIfYSHD7/2mcr27mtB8B9IyQBRpye0ABSH9/YCo=")),
150 //
151 Self::Argon2(Argon2State {
152 variant: argon2::Variant::Argon2id,
153 version: argon2::Version::Version13,
154 ..
155 }) => res!(NamexId::try_from("M/dnApVP4FC91bbNYeKy73V6j3NVfwW7qcVspx53zl8=")),
156 })
157 }
158
159 fn local_id(&self) -> LocalId {
160 match self {
161 Self::Argon2(Argon2State {
162 variant: argon2::Variant::Argon2d,
163 version: argon2::Version::Version10,
164 ..
165 }) => LocalId(1),
166 //
167 Self::Argon2(Argon2State {
168 variant: argon2::Variant::Argon2i,
169 version: argon2::Version::Version10,
170 ..
171 }) => LocalId(2),
172 //
173 Self::Argon2(Argon2State {
174 variant: argon2::Variant::Argon2id,
175 version: argon2::Version::Version10,
176 ..
177 }) => LocalId(3),
178 //
179 Self::Argon2(Argon2State {
180 variant: argon2::Variant::Argon2d,
181 version: argon2::Version::Version13,
182 ..
183 }) => LocalId(4),
184 //
185 Self::Argon2(Argon2State {
186 variant: argon2::Variant::Argon2i,
187 version: argon2::Version::Version13,
188 ..
189 }) => LocalId(5),
190 //
191 Self::Argon2(Argon2State {
192 variant: argon2::Variant::Argon2id,
193 version: argon2::Version::Version13,
194 ..
195 }) => LocalId(6),
196 }
197 }
198
199 fn assoc_names_base64(
200 gname: &'static str,
201 )
202 -> Outcome<Option<Vec<(
203 &'static str,
204 &'static str,
205 )>>>
206 {
207 let ids = match gname {
208 "schemes" => [
209 ("Argon2d_v0x10", "sl6UqXw8nwjEzDMyO9TMlCzwUKtNDHgpDq+DOYjUukU="),
210 ("Argon2i_v0x10", "cu9pC9q0wONz80OCjaxtMJiyv4zqMUuljhDKMOiUE4o="),
211 ("Argon2id_v0x10", "D4NkV8upthkHwlPrkfq/wqo+S4dnXwU7EkM4vg4h4jo="),
212 ("Argon2d_v0x13", "bbj8wR0HwO6JJoWDWgauLWOacUfLfJZVnoWEyTYIfqI="),
213 ("Argon2i_v0x13", "6UQjGIfYSHD7/2mcr27mtB8B9IyQBRpye0ABSH9/YCo="),
214 ("Argon2id_v0x13", "M/dnApVP4FC91bbNYeKy73V6j3NVfwW7qcVspx53zl8="),
215 ],
216 _ => return Err(err!(
217 "The Namex group name '{}' is not recognised for KeyDerivationScheme.", gname;
218 Invalid, Input)),
219 };
220 Ok(if ids.len() == 0 {
221 None
222 } else {
223 Some(ids.to_vec())
224 })
225 }
226}
227
228impl str::FromStr for KeyDerivationScheme {
229 type Err = Error<ErrTag>;
230
231 fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
232 match name {
233 "Argon2d_v0x10" => Self::default_argon2("Argon2d", 0x10),
234 "Argon2i_v0x10" => Self::default_argon2("Argon2i", 0x10),
235 "Argon2id_v0x10" => Self::default_argon2("Argon2id", 0x10),
236 "Argon2d_v0x13" => Self::default_argon2("Argon2d", 0x13),
237 "Argon2i_v0x13" => Self::default_argon2("Argon2i", 0x13),
238 "Argon2id_v0x13" => Self::default_argon2("Argon2id", 0x13),
239 _ => Err(err!(
240 "The key derivation scheme '{}' is not recognised.", name;
241 Invalid, Input)),
242 }
243 }
244}
245
246impl TryFrom<LocalId> for KeyDerivationScheme {
247 type Error = Error<ErrTag>;
248
249 fn try_from(n: LocalId) -> std::result::Result<Self, Self::Error> {
250 match n {
251 LocalId(1) => Self::default_argon2("Argon2d", 0x10),
252 LocalId(2) => Self::default_argon2("Argon2i", 0x10),
253 LocalId(3) => Self::default_argon2("Argon2id", 0x10),
254 LocalId(4) => Self::default_argon2("Argon2d", 0x13),
255 LocalId(5) => Self::default_argon2("Argon2i", 0x13),
256 LocalId(6) => Self::default_argon2("Argon2id", 0x13),
257 _ => Err(err!(
258 "The key derivation scheme with local id {} is not recognised.", n;
259 Invalid, Input)),
260 }
261 }
262}
263
264impl KeyDerivationScheme {
265
266 /// Default `KeyDerivationScheme::Argon2` includes a new random salt.
267 pub fn default_argon2(
268 variant: &str,
269 version: u32,
270 )
271 -> Outcome<Self>
272 {
273 let mut state = Argon2State::default();
274 state.variant = res!(argon2::Variant::from_str(variant));
275 state.version = res!(argon2::Version::from_u32(version));
276 Ok(Self::Argon2(state))
277 }
278
279 pub fn new_argon2(
280 variant: &str,
281 version: u32,
282 mem_cost: u32,
283 time_cost: u32,
284 salt_length: usize,
285 hash_length: u32,
286 )
287 -> Outcome<Self>
288 {
289 let mut kdf = Self::Argon2(Argon2State {
290 hash_length,
291 lanes: 1,
292 mem_cost,
293 time_cost,
294 variant: res!(argon2::Variant::from_str(variant)),
295 version: res!(argon2::Version::from_u32(version)),
296 ..Default::default()
297 });
298 res!(kdf.set_rand_salt(salt_length));
299 Ok(kdf)
300 }
301}
302
303#[derive(Clone, Debug, Eq, PartialEq)]
304pub struct Argon2State {
305 // Direct copy of argon2::Config<'a>
306 pub ad: Vec<u8>,
307 pub hash_length: u32,
308 pub lanes: u32,
309 pub mem_cost: u32,
310 pub secret: Vec<u8>,
311 pub time_cost: u32,
312 pub variant: argon2::Variant,
313 pub version: argon2::Version,
314 // Extra
315 pub salt: Vec<u8>,
316 pub hash: Option<Vec<u8>>,
317}
318
319impl Default for Argon2State {
320 fn default() -> Self {
321 let mut salt = [0u8; 16];
322 Rand::fill_u8(&mut salt);
323 Self {
324 ad: Vec::new(),
325 hash_length: 32,
326 lanes: 1,
327 mem_cost: 65536,
328 secret: Vec::new(),
329 time_cost: 5,
330 variant: argon2::Variant::Argon2id,
331 version: argon2::Version::Version13,
332 salt: salt.to_vec(),
333 hash: None,
334 }
335 }
336}
337
338impl Argon2State {
339
340 /// Creates an Argon2 state with the given parameters, all of which change the resulting tag.
341 pub fn new(
342 variant: &str,
343 version: u32,
344 mem_cost: u32,
345 time_cost: u32,
346 lanes: u32,
347 hash_length: u32,
348 salt: Vec<u8>,
349 )
350 -> Outcome<Self>
351 {
352 Ok(Self {
353 hash_length,
354 lanes,
355 mem_cost,
356 time_cost,
357 variant: res!(argon2::Variant::from_str(variant)),
358 version: res!(argon2::Version::from_u32(version)),
359 salt,
360 ..Default::default()
361 })
362 }
363
364 pub fn to_argon2_config<'a>(&'a self) -> argon2::Config<'a> {
365 argon2::Config {
366 ad: &self.ad,
367 hash_length: self.hash_length,
368 lanes: self.lanes,
369 mem_cost: self.mem_cost,
370 secret: &self.secret,
371 time_cost: self.time_cost,
372 variant: self.variant.clone(),
373 version: self.version.clone(),
374 }
375 }
376
377 /// Decodes an Argon2 string, returning an error rather than silently defaulting any parameter
378 /// that changes the tag. The `argon2::encoding` module is private, so the decoding must be
379 /// replicated here. The secret, which is never encoded, is left untouched on `self`.
380 pub fn from_argon2_string(
381 &mut self,
382 encoded: &str,
383 expect_hash: bool,
384 )
385 -> Outcome<()>
386 {
387 let body = match encoded.strip_prefix('$') {
388 Some(s) => s,
389 None => return Err(err!(
390 "Encoded Argon2 string '{}' does not begin with a '$'.", encoded;
391 Invalid, Input)),
392 };
393 let (n_complete, n_v0x10) = if expect_hash { (5, 4) } else { (4, 3) };
394 let parts: Vec<&str> = body.split('$').collect();
395 // The version field is absent in the original v0x10 encoding.
396 let (variant_str, version, opts_str, salt_str, hash_str) = if parts.len() == n_complete {
397 let vstr = res!(Self::extract_value(parts[1], "v", "version"));
398 (
399 parts[0],
400 res!(argon2::Version::from_str(vstr)),
401 parts[2],
402 parts[3],
403 if expect_hash { Some(parts[4]) } else { None },
404 )
405 } else if parts.len() == n_v0x10 {
406 (
407 parts[0],
408 argon2::Version::Version10,
409 parts[1],
410 parts[2],
411 if expect_hash { Some(parts[3]) } else { None },
412 )
413 } else {
414 return Err(err!(
415 "The Argon2 string '{}' should have {} or {} parts separated by the '$' \
416 character, {} were found.", encoded, n_v0x10, n_complete, parts.len();
417 Decode, String, Invalid, Input));
418 };
419 let opts = res!(Self::extract_options(opts_str));
420 let salt = res!(base64::decode(salt_str));
421 let hash = match hash_str {
422 Some(s) => Some(res!(base64::decode(s))),
423 None => None,
424 };
425 // The tag length is implied by the hash when there is one, and carried in the 'l' option
426 // when there is not. A configuration string with neither is the format the previous
427 // release wrote, which never emitted 'l' at all: those strings are in every wallet in the
428 // field, and a wallet cannot be rebuilt from source, so refusing to read one would lock its
429 // owner out of their own master key. The tag length then stays as the state already has
430 // it, which is what the old decoder did, and so derives the key that string has always
431 // meant.
432 let hash_length = match (&hash, opts.hash_length) {
433 (Some(h), Some(l)) => {
434 if (h.len() as u32) != l {
435 return Err(err!(
436 "The Argon2 string '{}' declares a hash length of {} but encodes a hash \
437 of {} bytes.", encoded, l, h.len();
438 Decode, String, Mismatch, Invalid, Input));
439 }
440 l
441 },
442 (Some(h), None) => h.len() as u32,
443 (None, Some(l)) => l,
444 (None, None) => self.hash_length,
445 };
446 self.variant = res!(argon2::Variant::from_str(variant_str));
447 self.version = version;
448 self.mem_cost = opts.mem_cost;
449 self.time_cost = opts.time_cost;
450 self.lanes = opts.lanes;
451 self.hash_length = hash_length;
452 self.ad = opts.ad;
453 self.salt = salt;
454 self.hash = hash;
455 Ok(())
456 }
457
458 /// Returns an error if a secret is present, since the encoded string must never carry it.
459 fn require_no_secret(&self) -> Outcome<()> {
460 if !self.secret.is_empty() {
461 return Err(err!(
462 "This Argon2 state carries a secret of {} bytes. A secret is never written to the \
463 encoded string, and encoding it away silently would derive a different key on \
464 decoding. Supply the secret out of band on the decoded state instead.",
465 self.secret.len();
466 Invalid, Input, Security));
467 }
468 Ok(())
469 }
470
471 /// Encodes the associated data, when present, as the `data` option used by the reference
472 /// implementation, including the leading comma.
473 fn encode_ad(&self) -> String {
474 if self.ad.is_empty() {
475 String::new()
476 } else {
477 fmt!(",data={}", base64::encode_config(&self.ad, base64::STANDARD_NO_PAD))
478 }
479 }
480
481 fn extract_value<'a>(
482 s: &'a str,
483 name: &'static str,
484 err_str: &'static str,
485 )
486 -> Outcome<&'a str>
487 {
488 let parts: Vec<&str> = s.split('=').collect();
489 if parts.len() == 2 {
490 if parts[0] == name {
491 Ok(parts[1])
492 } else {
493 Err(err!(
494 "The Argon2 {} substring key must be '{}', found '{}'.", err_str, name, parts[0];
495 Missing, Decode, String, Invalid, Input))
496 }
497 } else {
498 Err(err!(
499 "The Argon2 {} substring '{}' should have 2 parts separated by the '=' \
500 character. {} were found.", err_str, s, parts.len();
501 Decode, String, Invalid, Input))
502 }
503 }
504
505 /// Decodes the comma separated options substring. The mandatory `m`, `t` and `p` come first,
506 /// in that order, and may be followed by the optional `l` and `data`. Any other option is an
507 /// error, because an option we do not understand may well be one that changes the tag.
508 fn extract_options(s: &str) -> Outcome<Argon2Options> {
509 let parts: Vec<&str> = s.split(',').collect();
510 if parts.len() < 3 {
511 return Err(err!(
512 "The Argon2 options substring '{}' should have at least the 3 parts 'm', 't' and \
513 'p' separated by the ',' character. {} were found.", s, parts.len();
514 Decode, String, Missing, Invalid, Input));
515 }
516 let mstr = res!(Self::extract_value(parts[0], "m", "mem_cost"));
517 let tstr = res!(Self::extract_value(parts[1], "t", "time_cost"));
518 let pstr = res!(Self::extract_value(parts[2], "p", "lanes"));
519 let mut result = Argon2Options {
520 mem_cost: res!(mstr.parse::<u32>()),
521 time_cost: res!(tstr.parse::<u32>()),
522 lanes: res!(pstr.parse::<u32>()),
523 hash_length: None,
524 ad: Vec::new(),
525 };
526 let mut ad_seen = false;
527 for part in &parts[3..] {
528 let kv: Vec<&str> = part.split('=').collect();
529 if kv.len() != 2 {
530 return Err(err!(
531 "The Argon2 option '{}' should have 2 parts separated by the '=' character. \
532 {} were found.", part, kv.len();
533 Decode, String, Invalid, Input));
534 }
535 match kv[0] {
536 "l" => {
537 if result.hash_length.is_some() {
538 return Err(err!(
539 "The Argon2 options substring '{}' repeats the 'l' option.", s;
540 Decode, String, Duplicate, Invalid, Input));
541 }
542 result.hash_length = Some(res!(kv[1].parse::<u32>()));
543 },
544 "data" => {
545 if ad_seen {
546 return Err(err!(
547 "The Argon2 options substring '{}' repeats the 'data' option.", s;
548 Decode, String, Duplicate, Invalid, Input));
549 }
550 result.ad = res!(base64::decode(kv[1]));
551 ad_seen = true;
552 },
553 _ => return Err(err!(
554 "The Argon2 option key '{}' in options substring '{}' is not recognised. An \
555 unrecognised option may change the derived key, so it cannot be ignored.",
556 kv[0], s;
557 Decode, String, Unknown, Invalid, Input)),
558 }
559 }
560 Ok(result)
561 }
562}
563
564/// The decoded options substring of an Argon2 encoded string.
565struct Argon2Options {
566 mem_cost: u32,
567 time_cost: u32,
568 lanes: u32,
569 hash_length: Option<u32>, // Absent from a string that carries a hash.
570 ad: Vec<u8>,
571}
572
573impl KeyDeriver for KeyDerivationScheme {
574
575 fn get_hash(&self) -> Outcome<&[u8]> {
576 match self {
577 Self::Argon2(state) => match &state.hash {
578 Some(hash) => return Ok(&hash[..]),
579 None => (),
580 },
581 }
582 Err(err!(
583 "{}: Expected hash to be be present, found none.", self;
584 Data, Missing))
585 }
586
587 fn set_rand_salt(&mut self, n: usize) -> Outcome<()> {
588 let mut salt = vec![0u8; n];
589 Rand::fill_u8(&mut salt);
590 match self {
591 Self::Argon2(state) => state.salt = salt,
592 }
593 Ok(())
594 }
595
596 fn derive(&mut self, pass: &[u8]) -> Outcome<()> {
597 match self {
598 Self::Argon2(state) => match argon2::hash_raw(
599 pass,
600 &state.salt,
601 &state.to_argon2_config(),
602 ) {
603 Ok(hash) => {
604 state.hash = Some(hash);
605 Ok(())
606 },
607 Err(e) => Err(err!(e, "While performing Argon2 hash."; Invalid, Input)),
608 },
609 }
610 }
611
612 fn verify(&self, pass: &[u8]) -> Outcome<bool> {
613 match self {
614 Self::Argon2(state) => match &state.hash {
615 Some(hash) => Ok(res!(argon2::verify_raw(
616 pass,
617 &state.salt,
618 &hash,
619 &state.to_argon2_config(),
620 ))),
621 None => Err(err!("Hash has not been created."; Missing)),
622 },
623 }
624 }
625
626 /// Encodes the state and its hash in the PHC string format, which other Argon2 implementations
627 /// can read. The tag length is implied by the length of the encoded hash.
628 fn encode_to_string(&self) -> Outcome<String> {
629 match self {
630 Self::Argon2(state) => match &state.hash {
631 Some(hash) => {
632 // The encoding function is copied from argon2 to avoid using argon2::Context,
633 // which requires the password.
634 res!(state.require_no_secret());
635 if (hash.len() as u32) != state.hash_length {
636 return Err(err!(
637 "This Argon2 state declares a hash length of {} but holds a hash of \
638 {} bytes.", state.hash_length, hash.len();
639 Bug, Mismatch, Invalid));
640 }
641 Ok(fmt!(
642 "${}$v={}$m={},t={},p={}{}${}${}",
643 state.variant,
644 state.version,
645 state.mem_cost,
646 state.time_cost,
647 state.lanes,
648 state.encode_ad(),
649 base64::encode_config(&state.salt, base64::STANDARD_NO_PAD),
650 base64::encode_config(&hash, base64::STANDARD_NO_PAD),
651 ))
652 },
653 None => Err(err!("Hash has not been created."; Missing)),
654 },
655 }
656 }
657
658 /// Encodes the state sans hash. Since PHC defines no hash-less form, and the tag length can
659 /// no longer be implied by a hash, it is carried explicitly in the `l` option.
660 fn encode_cfg_to_string(&self) -> Outcome<String> {
661 match self {
662 Self::Argon2(state) => {
663 res!(state.require_no_secret());
664 Ok(fmt!(
665 "${}$v={}$m={},t={},p={},l={}{}${}",
666 state.variant,
667 state.version,
668 state.mem_cost,
669 state.time_cost,
670 state.lanes,
671 state.hash_length,
672 state.encode_ad(),
673 base64::encode_config(&state.salt, base64::STANDARD_NO_PAD),
674 ))
675 },
676 }
677 }
678
679 fn decode_from_string(&mut self, s: &str) -> Outcome<()> {
680 match self {
681 Self::Argon2(state) => state.from_argon2_string(s, true),
682 }
683 }
684
685 fn decode_cfg_from_string(&mut self, s: &str) -> Outcome<()> {
686 match self {
687 Self::Argon2(state) => state.from_argon2_string(s, false),
688 }
689 }
690}
691
692#[derive(Clone, Debug, Default)]
693pub struct KeyDeriverDefAlt<
694 D: KeyDeriver,
695 G: KeyDeriver,
696> (pub DefAlt<D, G>);
697
698impl<
699 D: KeyDeriver,
700 G: KeyDeriver,
701>
702 std::ops::Deref for KeyDeriverDefAlt<D, G>
703{
704 type Target = DefAlt<D, G>;
705 fn deref(&self) -> &Self::Target { &self.0 }
706}
707
708impl<
709 D: KeyDeriver,
710 G: KeyDeriver,
711>
712 From<Option<G>> for KeyDeriverDefAlt<D, G>
713{
714 fn from(opt: Option<G>) -> Self {
715 Self(
716 DefAlt::from(opt),
717 )
718 }
719}
720
721impl<
722 D: KeyDeriver,
723 G: KeyDeriver,
724>
725 From<Alt<G>> for KeyDeriverDefAlt<D, G>
726{
727 fn from(alt: Alt<G>) -> Self {
728 Self(
729 DefAlt::from(alt),
730 )
731 }
732}
733
734impl<
735 D: KeyDeriver + InNamex,
736 G: KeyDeriver + InNamex,
737>
738 fmt::Display for KeyDeriverDefAlt<D, G>
739{
740 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
741 write!(f, "{:?}", self)
742 }
743}
744
745impl<
746 D: KeyDeriver + InNamex,
747 G: KeyDeriver + InNamex,
748>
749 InNamex for KeyDeriverDefAlt<D, G>
750{
751 fn name_id(&self) -> Outcome<NamexId> {
752 match &self.0 {
753 DefAlt::Default(inner) => inner.name_id(),
754 DefAlt::Given(inner) => inner.name_id(),
755 DefAlt::None => Err(err!(
756 "No Namex id can be specified for DefAlt::None.";
757 Missing, Bug)),
758 }
759 }
760
761 fn local_id(&self) -> LocalId {
762 match &self.0 {
763 DefAlt::Default(inner) => inner.local_id(),
764 DefAlt::Given(inner) => inner.local_id(),
765 DefAlt::None => LocalId::default(),
766 }
767 }
768
769 fn assoc_names_base64(
770 gname: &'static str,
771 )
772 -> Outcome<Option<Vec<(
773 &'static str,
774 &'static str,
775 )>>>
776 {
777 match res!(D::assoc_names_base64(gname)) {
778 Some(mut vd) => match res!(G::assoc_names_base64(gname)) {
779 Some(vg) => {
780 vd.extend(vg);
781 Ok(Some(vd))
782 },
783 None => Ok(Some(vd)),
784 },
785 None => match res!(G::assoc_names_base64(gname)) {
786 Some(vg) => Ok(Some(vg)),
787 None => Ok(None),
788 },
789 }
790 }
791}
792
793impl<
794 D: KeyDeriver,
795 G: KeyDeriver,
796>
797 KeyDeriver for KeyDeriverDefAlt<D, G>
798{
799 fn get_hash(&self) -> Outcome<&[u8]> {
800 match &self.0 {
801 DefAlt::Default(inner) => return inner.get_hash(),
802 DefAlt::Given(inner) => return inner.get_hash(),
803 DefAlt::None => (),
804 }
805 Err(err!(
806 "{}: Expected hash to be be present, found none.", self;
807 Data, Missing))
808 }
809
810 fn set_rand_salt(&mut self, n: usize) -> Outcome<()> {
811 match &mut self.0 {
812 DefAlt::Default(inner) => inner.set_rand_salt(n),
813 DefAlt::Given(inner) => inner.set_rand_salt(n),
814 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
815 Configuration, Missing)),
816 }
817 }
818
819 fn derive(&mut self, pass: &[u8]) -> Outcome<()> {
820 match &mut self.0 {
821 DefAlt::Default(inner) => inner.derive(pass),
822 DefAlt::Given(inner) => inner.derive(pass),
823 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
824 Configuration, Missing)),
825 }
826 }
827
828 fn verify(&self, pass: &[u8]) -> Outcome<bool> {
829 match &self.0 {
830 DefAlt::Default(inner) => inner.verify(pass),
831 DefAlt::Given(inner) => inner.verify(pass),
832 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
833 Configuration, Missing)),
834 }
835 }
836
837 fn encode_to_string(&self) -> Outcome<String> {
838 match &self.0 {
839 DefAlt::Default(inner) => inner.encode_to_string(),
840 DefAlt::Given(inner) => inner.encode_to_string(),
841 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
842 Configuration, Missing)),
843 }
844 }
845
846 fn encode_cfg_to_string(&self) -> Outcome<String> {
847 match &self.0 {
848 DefAlt::Default(inner) => inner.encode_cfg_to_string(),
849 DefAlt::Given(inner) => inner.encode_cfg_to_string(),
850 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
851 Configuration, Missing)),
852 }
853 }
854
855 fn decode_from_string(&mut self, s: &str) -> Outcome<()> {
856 match &mut self.0 {
857 DefAlt::Default(inner) => inner.decode_from_string(s),
858 DefAlt::Given(inner) => inner.decode_from_string(s),
859 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
860 Configuration, Missing)),
861 }
862 }
863
864 fn decode_cfg_from_string(&mut self, s: &str) -> Outcome<()> {
865 match &mut self.0 {
866 DefAlt::Default(inner) => inner.decode_cfg_from_string(s),
867 DefAlt::Given(inner) => inner.decode_cfg_from_string(s),
868 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
869 Configuration, Missing)),
870 }
871 }
872}
873
874impl<
875 D: KeyDeriver,
876 G: KeyDeriver,
877>
878 KeyDeriverDefAlt<D, G>
879{
880 pub const KDF_MISSING_MSG: &'static str = "Key deriver function not specified.";
881
882 pub fn or_get_hash<'a, OR: KeyDeriver>(&'a self, alt: &'a Alt<OR>) -> Outcome<&'a [u8]> {
883 match &alt {
884 Alt::Specific(Some(inner)) => return inner.get_hash(),
885 Alt::Specific(None) => (),
886 Alt::Unspecified => match &self.0 {
887 DefAlt::Default(inner) => return inner.get_hash(),
888 DefAlt::Given(inner) => return inner.get_hash(),
889 DefAlt::None => (),
890 },
891 }
892 Err(err!(
893 "{}: Expected hash to be be present, found none.", self;
894 Data, Missing))
895 }
896
897 pub fn or_set_rand_salt<OR: KeyDeriver>(&mut self, n: usize, mut alt: &mut Alt<OR>) -> Outcome<()> {
898 match &mut alt {
899 Alt::Specific(Some(inner)) => inner.set_rand_salt(n),
900 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
901 Configuration, Missing)),
902 Alt::Unspecified => match &mut self.0 {
903 DefAlt::Default(inner) => inner.set_rand_salt(n),
904 DefAlt::Given(inner) => inner.set_rand_salt(n),
905 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
906 Configuration, Missing)),
907 },
908 }
909 }
910
911 pub fn or_derive<OR: KeyDeriver>(&mut self, pass: &[u8], mut alt: &mut Alt<OR>) -> Outcome<()> {
912 match &mut alt {
913 Alt::Specific(Some(inner)) => inner.derive(pass),
914 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
915 Configuration, Missing)),
916 Alt::Unspecified => match &mut self.0 {
917 DefAlt::Default(inner) => inner.derive(pass),
918 DefAlt::Given(inner) => inner.derive(pass),
919 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
920 Configuration, Missing)),
921 },
922 }
923 }
924
925 pub fn or_verify<OR: KeyDeriver>(&self, pass: &[u8], alt: &Alt<OR>) -> Outcome<bool> {
926 match &alt {
927 Alt::Specific(Some(inner)) => inner.verify(pass),
928 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
929 Configuration, Missing)),
930 Alt::Unspecified => match &self.0 {
931 DefAlt::Default(inner) => inner.verify(pass),
932 DefAlt::Given(inner) => inner.verify(pass),
933 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
934 Configuration, Missing)),
935 },
936 }
937 }
938
939 pub fn or_encode_to_string<OR: KeyDeriver>(&self, alt: &Alt<OR>) -> Outcome<String> {
940 match &alt {
941 Alt::Specific(Some(inner)) => inner.encode_to_string(),
942 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
943 Configuration, Missing)),
944 Alt::Unspecified => match &self.0 {
945 DefAlt::Default(inner) => inner.encode_to_string(),
946 DefAlt::Given(inner) => inner.encode_to_string(),
947 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
948 Configuration, Missing)),
949 },
950 }
951 }
952
953 pub fn or_encode_cfg_to_string<OR: KeyDeriver>(&self, alt: &Alt<OR>) -> Outcome<String> {
954 match &alt {
955 Alt::Specific(Some(inner)) => inner.encode_cfg_to_string(),
956 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
957 Configuration, Missing)),
958 Alt::Unspecified => match &self.0 {
959 DefAlt::Default(inner) => inner.encode_cfg_to_string(),
960 DefAlt::Given(inner) => inner.encode_cfg_to_string(),
961 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
962 Configuration, Missing)),
963 },
964 }
965 }
966
967 pub fn or_decode_from_string<OR: KeyDeriver>(&mut self, s: &str, mut alt: &mut Alt<OR>) -> Outcome<()> {
968 match &mut alt {
969 Alt::Specific(Some(inner)) => inner.decode_from_string(s),
970 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
971 Configuration, Missing)),
972 Alt::Unspecified => match &mut self.0 {
973 DefAlt::Default(inner) => inner.decode_from_string(s),
974 DefAlt::Given(inner) => inner.decode_from_string(s),
975 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
976 Configuration, Missing)),
977 },
978 }
979 }
980
981 pub fn or_decode_cfg_from_string<OR: KeyDeriver>(&mut self, s: &str, mut alt: &mut Alt<OR>) -> Outcome<()> {
982 match &mut alt {
983 Alt::Specific(Some(inner)) => inner.decode_from_string(s),
984 Alt::Specific(None) => Err(err!("{}", Self::KDF_MISSING_MSG;
985 Configuration, Missing)),
986 Alt::Unspecified => match &mut self.0 {
987 DefAlt::Default(inner) => inner.decode_from_string(s),
988 DefAlt::Given(inner) => inner.decode_from_string(s),
989 DefAlt::None => Err(err!("{}", Self::KDF_MISSING_MSG;
990 Configuration, Missing)),
991 },
992 }
993 }
994}
995
996#[cfg(test)]
997mod tests {
998 use super::*;
999 use std::time::SystemTime;
1000
1001 // The Argon2id test vector of RFC 9106, section 5.3. It is the only vector here that pins the
1002 // lane count, the secret and the associated data, because it is the only one that uses them.
1003 const RFC9106_PASS: [u8; 32] = [0x01; 32];
1004 const RFC9106_SALT: [u8; 16] = [0x02; 16];
1005 const RFC9106_SECRET: [u8; 8] = [0x03; 8];
1006 const RFC9106_AD: [u8; 12] = [0x04; 12];
1007 const RFC9106_TAG: [u8; 32] = [
1008 0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c,
1009 0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9,
1010 0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e,
1011 0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01, 0xe6, 0x59,
1012 ];
1013
1014 /// The RFC 9106 section 5.3 inputs: 32 KiB of memory, 3 passes and, critically, 4 lanes.
1015 fn rfc9106_state() -> Argon2State {
1016 Argon2State {
1017 ad: RFC9106_AD.to_vec(),
1018 hash_length: 32,
1019 lanes: 4,
1020 mem_cost: 32,
1021 secret: RFC9106_SECRET.to_vec(),
1022 time_cost: 3,
1023 variant: argon2::Variant::Argon2id,
1024 version: argon2::Version::Version13,
1025 salt: RFC9106_SALT.to_vec(),
1026 hash: None,
1027 }
1028 }
1029
1030 /// Pins our Argon2id output against the published RFC 9106 tag.
1031 #[test]
1032 fn test_argon2_rfc9106_vector() -> Outcome<()> {
1033 let mut kdf = KeyDerivationScheme::Argon2(rfc9106_state());
1034 res!(kdf.derive(&RFC9106_PASS));
1035 req!(res!(kdf.get_hash()), &RFC9106_TAG[..]);
1036 Ok(())
1037 }
1038
1039 /// Pins the configuration string round trip against the published RFC 9106 tag. Should the
1040 /// lane count or the tag length be dropped in encoding or decoding, the derived key differs
1041 /// from the vector and this fails.
1042 #[test]
1043 fn test_argon2_rfc9106_cfg_round_trip() -> Outcome<()> {
1044 let mut state = rfc9106_state();
1045 state.secret = Vec::new(); // The secret is never encoded; it is supplied out of band.
1046 let kdf = KeyDerivationScheme::Argon2(state);
1047 let encoded_cfg = res!(kdf.encode_cfg_to_string());
1048 msg!("encoded cfg = '{}'", encoded_cfg);
1049 // A fresh scheme carrying the defaults, lanes = 1 and hash_length = 32, which the decoded
1050 // configuration must overwrite.
1051 let mut kdf2 = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1052 res!(kdf2.decode_cfg_from_string(&encoded_cfg));
1053 match &mut kdf2 {
1054 KeyDerivationScheme::Argon2(state) => {
1055 req!(state.lanes, 4, "The lane count did not survive the round trip.");
1056 req!(state.hash_length, 32);
1057 req!(state.mem_cost, 32);
1058 req!(state.time_cost, 3);
1059 req!(state.version, argon2::Version::Version13);
1060 req!(&state.ad[..], &RFC9106_AD[..]);
1061 req!(&state.salt[..], &RFC9106_SALT[..]);
1062 state.secret = RFC9106_SECRET.to_vec(); // Supplied out of band, as documented.
1063 },
1064 }
1065 res!(kdf2.derive(&RFC9106_PASS));
1066 req!(res!(kdf2.get_hash()), &RFC9106_TAG[..]);
1067 Ok(())
1068 }
1069
1070 /// A foreign PHC string, produced elsewhere with 4 lanes, must verify against its password.
1071 /// Dropping the lanes on decoding derives a different tag, and the verification fails.
1072 #[test]
1073 fn test_argon2_decode_foreign_phc_string() -> Outcome<()> {
1074 let encoded = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo\
1075 $BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
1076 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2i", 0x13));
1077 res!(kdf.decode_from_string(encoded));
1078 match &kdf {
1079 KeyDerivationScheme::Argon2(state) => {
1080 req!(state.lanes, 4, "The lane count was dropped on decoding.");
1081 req!(state.mem_cost, 4096);
1082 req!(state.time_cost, 3);
1083 req!(state.hash_length, 32);
1084 req!(&state.salt[..], &b"abcdefghijklmnopqrstuvwxyz"[..]);
1085 },
1086 }
1087 req!(res!(kdf.verify(b"foo")), true);
1088 req!(res!(kdf.verify(b"bar")), false);
1089 Ok(())
1090 }
1091
1092 /// Our PHC output must be readable by an independent Argon2 parser, here the wrapped crate's
1093 /// own, which we otherwise never exercise.
1094 #[test]
1095 fn test_argon2_phc_string_is_externally_readable() -> Outcome<()> {
1096 let pass = b"The meaning is 42";
1097 let state = res!(Argon2State::new("Argon2id", 0x13, 1024, 2, 4, 32, b"somesalt".to_vec()));
1098 let mut kdf = KeyDerivationScheme::Argon2(state);
1099 res!(kdf.derive(pass));
1100 let encoded = res!(kdf.encode_to_string());
1101 msg!("encoded with hash = '{}'", encoded);
1102 req!(encoded.starts_with("$argon2id$v=19$m=1024,t=2,p=4$"), true, "Not a PHC string.");
1103 match argon2::verify_encoded(&encoded, pass) {
1104 Ok(true) => (),
1105 Ok(false) => return Err(err!(
1106 "An independent Argon2 parser read our encoded string but did not verify \
1107 the password against it."; Test, Mismatch)),
1108 Err(e) => return Err(err!(e,
1109 "An independent Argon2 parser could not read our encoded string."; Test, Decode)),
1110 }
1111 Ok(())
1112 }
1113
1114 /// A configuration string written by the previous release must still decode, and must still
1115 /// derive the key it derived then.
1116 ///
1117 /// The deployed encoder wrote `$argon2id$v=19$m=..,t=..,p=..$<salt>` and no `l` option, because
1118 /// it never wrote the tag length at all. Every `kdf_cfg` in a wallet in the field has that
1119 /// shape, and a wallet is the one artefact that cannot be rebuilt from source: refusing to read
1120 /// it would lock every admin out of their own master key. So a missing `l` is not an error, it
1121 /// is the old format, and the tag length falls back to the one the state already carries.
1122 ///
1123 /// The expected tag comes from the wrapped crate's own hasher rather than from ours, so this
1124 /// pins the fallback to what the old string actually meant, not to what we now think it means.
1125 #[test]
1126 fn test_a_cfg_string_from_the_previous_release_still_decodes() -> Outcome<()> {
1127 let pass = b"The meaning is 42";
1128 let salt = b"somesalt";
1129 // Exactly what the previous release's `encode_cfg_to_string` emitted: no 'l' option.
1130 let old_cfg = fmt!(
1131 "$argon2id$v=19$m=1024,t=2,p=1${}",
1132 base64::encode_config(salt, base64::STANDARD_NO_PAD),
1133 );
1134
1135 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1136 res!(kdf.decode_cfg_from_string(&old_cfg));
1137 res!(kdf.derive(pass));
1138 let ours = res!(kdf.get_hash()).to_vec();
1139
1140 // The independent oracle: the wrapped crate, told the same parameters by hand.
1141 let cfg = argon2::Config {
1142 variant: argon2::Variant::Argon2id,
1143 version: argon2::Version::Version13,
1144 mem_cost: 1024,
1145 time_cost: 2,
1146 lanes: 1,
1147 hash_length: 32,
1148 ..argon2::Config::default()
1149 };
1150 let theirs = res!(argon2::hash_raw(pass, salt, &cfg));
1151
1152 req!(ours, theirs,
1153 "A cfg string from the previous release derived a different key than it used to, \
1154 which is how a wallet locks its owner out.");
1155 Ok(())
1156 }
1157
1158 #[test]
1159 fn test_argon2_encode_pass() -> Outcome<()> {
1160 // Here we store the hash along with the hasher config.
1161 let pass = b"The meaning is 42";
1162 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1163 res!(kdf.derive(pass));
1164 let encoded_cfg = res!(kdf.encode_cfg_to_string());
1165 let encoded_with_hash = res!(kdf.encode_to_string());
1166 let hash = res!(kdf.get_hash());
1167 msg!("hash = '{:02x?}'", hash);
1168 msg!("encoded cfg = '{}'", encoded_cfg);
1169 msg!("encoded with hash = '{}'", encoded_with_hash);
1170 req!(res!(kdf.verify(pass)), true);
1171 req!(res!(kdf.verify(b"The meaning is 43")), false,
1172 "The verification should have failed for a differing passphrase.");
1173 let mut kdf2 = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1174 res!(kdf2.decode_from_string(&encoded_with_hash));
1175 req!(kdf, kdf2);
1176 let mut kdf3 = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1177 res!(kdf3.decode_cfg_from_string(&encoded_cfg));
1178 res!(kdf3.derive(pass));
1179 req!(kdf, kdf3);
1180 Ok(())
1181 }
1182
1183 /// A configuration string carrying neither a hash nor an explicit tag length keeps the tag
1184 /// length the receiving state already holds.
1185 ///
1186 /// That is the old format, and it is what every deployed wallet contains, so it must decode.
1187 /// New strings do not rely on the fallback: `encode_cfg_to_string` always writes `l`, which is
1188 /// what makes them self-describing. See
1189 /// `test_a_cfg_string_from_the_previous_release_still_decodes` for the key it must derive.
1190 #[test]
1191 fn test_argon2_decode_without_a_tag_length_keeps_the_states_own() -> Outcome<()> {
1192 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1193 let expected = match &kdf {
1194 KeyDerivationScheme::Argon2(state) => state.hash_length,
1195 };
1196 res!(kdf.decode_cfg_from_string("$argon2id$v=19$m=65536,t=5,p=1$c29tZXNhbHQ"));
1197 match &kdf {
1198 KeyDerivationScheme::Argon2(state) => {
1199 req!(state.hash_length, expected,
1200 "An old configuration string changed the tag length it derives with.");
1201 req!(state.lanes, 1);
1202 },
1203 }
1204 Ok(())
1205 }
1206
1207 /// An option we do not understand may be one that changes the derived key, so it is rejected.
1208 #[test]
1209 fn test_argon2_decode_rejects_unknown_option() -> Outcome<()> {
1210 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1211 match kdf.decode_cfg_from_string("$argon2id$v=19$m=65536,t=5,p=1,l=32,x=9$c29tZXNhbHQ") {
1212 Ok(()) => Err(err!(
1213 "A configuration string with an unknown option should not decode.";
1214 Test, Unexpected)),
1215 Err(_) => Ok(()),
1216 }
1217 }
1218
1219 /// The lane count is mandatory, not optional.
1220 #[test]
1221 fn test_argon2_decode_rejects_missing_lanes() -> Outcome<()> {
1222 let mut kdf = res!(KeyDerivationScheme::default_argon2("Argon2id", 0x13));
1223 match kdf.decode_cfg_from_string("$argon2id$v=19$m=65536,t=5,l=32$c29tZXNhbHQ") {
1224 Ok(()) => Err(err!(
1225 "A configuration string with no lane count should not decode."; Test, Unexpected)),
1226 Err(_) => Ok(()),
1227 }
1228 }
1229
1230 /// A secret is never written to an encoded string, so encoding one must fail loudly rather
1231 /// than drop it, which would derive a different key on decoding.
1232 #[test]
1233 fn test_argon2_encode_refuses_to_drop_secret() -> Outcome<()> {
1234 let kdf = KeyDerivationScheme::Argon2(rfc9106_state());
1235 match kdf.encode_cfg_to_string() {
1236 Ok(s) => Err(err!(
1237 "Encoding a state holding a secret should have failed, but produced '{}'.", s;
1238 Test, Unexpected)),
1239 Err(_) => Ok(()),
1240 }
1241 }
1242
1243 /// A simple test of viability for a network packet header proof of work.
1244 #[test]
1245 fn test_argon2_pow() -> Outcome<()> {
1246 // Deliberately cheap parameters: this measures viability, not key strength.
1247 let mut kdf = res!(KeyDerivationScheme::new_argon2("Argon2i", 0x13, 512, 1, 16, 32));
1248 let prefix = [1u8]; // One byte, so around 256 derivations are expected.
1249 let lim: usize = 20_000;
1250 let mut count: usize = 0;
1251 let t = res!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1252 let mut tstamp = t.as_secs().to_be_bytes().to_vec();
1253 let mut pass = vec![192u8, 168, 0, 1, 127, 0, 0, 1];
1254 pass.append(&mut tstamp);
1255 loop {
1256 res!(kdf.derive(&pass));
1257 let hash = res!(kdf.get_hash());
1258 if hash.starts_with(&prefix) { break; }
1259 count += 1;
1260 if count > lim {
1261 return Err(err!(
1262 "No proof of work found for a {} byte prefix in {} derivations.",
1263 prefix.len(), lim;
1264 Test, Excessive));
1265 }
1266 res!(kdf.set_rand_salt(16));
1267 }
1268 msg!("Proof of work found after {} failed derivations.", count);
1269 Ok(())
1270 }
1271
1272}