1#[cfg(feature = "alloc")]
7extern crate alloc;
8
9#[cfg(feature = "alloc")]
10use alloc::{string::String, vec, vec::Vec};
11
12use crate::{Hasher, blake2::Blake2b};
13
14const VERSION: u32 = 0x13;
16
17const SYNC_POINTS: u32 = 4;
19
20const BLOCK_SIZE: usize = 1024;
22
23#[allow(dead_code)]
25const ARGON2D: u32 = 0;
26const ARGON2I: u32 = 1;
27const ARGON2ID: u32 = 2;
28
29#[derive(Debug, Clone)]
44pub struct Params {
45 pub iterations: u32,
47 pub memory: u32,
49 pub parallelism: u32,
51 pub tag_length: u32,
53}
54
55impl Default for Params {
56 fn default() -> Self {
58 Params {
59 iterations: 3,
60 memory: 65536,
61 parallelism: 4,
62 tag_length: 32,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69#[cfg(feature = "alloc")]
70pub enum Argon2Error {
71 InvalidParams(&'static str),
73 InvalidEncoding(&'static str),
75 VerifyMismatch,
77}
78
79#[cfg(feature = "alloc")]
80impl core::fmt::Display for Argon2Error {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 match self {
83 Argon2Error::InvalidParams(msg) => write!(f, "argon2: invalid params: {}", msg),
84 Argon2Error::InvalidEncoding(msg) => write!(f, "argon2: invalid encoding: {}", msg),
85 Argon2Error::VerifyMismatch => write!(f, "argon2: verification failed"),
86 }
87 }
88}
89
90#[derive(Clone)]
92struct Block {
93 v: [u64; 128],
94}
95
96impl Block {
97 fn zero() -> Self {
98 Block {
99 v: [0u64; 128],
100 }
101 }
102
103 fn xor_with(&mut self, other: &Block) {
104 for i in 0..128 {
105 self.v[i] ^= other.v[i];
106 }
107 }
108
109 fn from_bytes(bytes: &[u8]) -> Self {
110 let mut block = Block::zero();
111 for i in 0..128 {
112 let offset = i * 8;
113 block.v[i] = u64::from_le_bytes([
114 bytes[offset],
115 bytes[offset + 1],
116 bytes[offset + 2],
117 bytes[offset + 3],
118 bytes[offset + 4],
119 bytes[offset + 5],
120 bytes[offset + 6],
121 bytes[offset + 7],
122 ]);
123 }
124 block
125 }
126
127 fn to_bytes(&self) -> [u8; BLOCK_SIZE] {
128 let mut out = [0u8; BLOCK_SIZE];
129 for i in 0..128 {
130 let bytes = self.v[i].to_le_bytes();
131 let offset = i * 8;
132 out[offset..offset + 8].copy_from_slice(&bytes);
133 }
134 out
135 }
136}
137
138#[cfg(feature = "alloc")]
171pub fn derive_key(
172 password: &[u8],
173 salt: &[u8],
174 secret: &[u8],
175 ad: &[u8],
176 params: &Params,
177) -> Result<Vec<u8>, Argon2Error> {
178 argon2_core(ARGON2ID, password, salt, secret, ad, params)
179}
180
181#[cfg(feature = "alloc")]
183fn argon2_core(
184 argon_type: u32,
185 password: &[u8],
186 salt: &[u8],
187 secret: &[u8],
188 ad: &[u8],
189 params: &Params,
190) -> Result<Vec<u8>, Argon2Error> {
191 if params.iterations < 1 {
193 return Err(Argon2Error::InvalidParams("iterations must be >= 1"));
194 }
195 if params.parallelism < 1 {
196 return Err(Argon2Error::InvalidParams("parallelism must be >= 1"));
197 }
198 if params.tag_length < 4 {
199 return Err(Argon2Error::InvalidParams("tag_length must be >= 4"));
200 }
201 if params.memory < 8 * params.parallelism {
202 return Err(Argon2Error::InvalidParams("memory must be >= 8*parallelism"));
203 }
204
205 let p = params.parallelism;
206 let t = params.iterations;
207 let m = params.memory;
208 let tag_length = params.tag_length;
209
210 let h0 = compute_h0(argon_type, password, salt, secret, ad, p, tag_length, m, t);
212
213 let m_prime = 4 * p * (m / (4 * p));
215 let q = m_prime / p; let mut memory: Vec<Block> = vec![Block::zero(); m_prime as usize];
219
220 for i in 0..p {
222 let mut input = Vec::with_capacity(72);
224 input.extend_from_slice(&h0);
225 input.extend_from_slice(&0u32.to_le_bytes());
226 input.extend_from_slice(&i.to_le_bytes());
227 let block_bytes = variable_length_hash(&input, BLOCK_SIZE as u32);
228 memory[(i * q) as usize] = Block::from_bytes(&block_bytes);
229
230 let mut input = Vec::with_capacity(72);
232 input.extend_from_slice(&h0);
233 input.extend_from_slice(&1u32.to_le_bytes());
234 input.extend_from_slice(&i.to_le_bytes());
235 let block_bytes = variable_length_hash(&input, BLOCK_SIZE as u32);
236 memory[(i * q + 1) as usize] = Block::from_bytes(&block_bytes);
237 }
238
239 for pass in 0..t {
241 for slice in 0..SYNC_POINTS {
242 for lane in 0..p {
243 fill_segment(&mut memory, argon_type, pass, lane, slice, p, q, t, m_prime);
244 }
245 }
246 }
247
248 let mut final_block = memory[(q - 1) as usize].clone();
250 for i in 1..p {
251 let idx = (i * q + q - 1) as usize;
252 final_block.xor_with(&memory[idx]);
253 }
254
255 let final_bytes = final_block.to_bytes();
257 let tag = variable_length_hash(&final_bytes, tag_length);
258
259 Ok(tag)
260}
261
262#[cfg(feature = "alloc")]
264fn fill_segment(
265 memory: &mut [Block],
266 argon_type: u32,
267 pass: u32,
268 lane: u32,
269 slice: u32,
270 lanes: u32,
271 q: u32, t: u32, m_prime: u32, ) {
275 let segment_length = q / SYNC_POINTS;
276
277 let mut pseudo_rands: Vec<u64> = Vec::new();
279 let need_pseudo_rands = argon_type == ARGON2I || (argon_type == ARGON2ID && pass == 0 && slice < 2);
280
281 if need_pseudo_rands {
282 pseudo_rands = generate_addresses(pass, lane, slice, lanes, t, argon_type, m_prime, segment_length);
283 }
284
285 let start_index = if pass == 0 && slice == 0 { 2 } else { 0 };
286
287 for s in start_index..segment_length {
288 let j = slice * segment_length + s; let cur_index = (lane * q + j) as usize;
290
291 let prev_index = if j == 0 {
293 (lane * q + q - 1) as usize
294 } else {
295 (lane * q + j - 1) as usize
296 };
297
298 let (j1, j2) = if need_pseudo_rands {
300 let val = pseudo_rands[s as usize];
301 ((val & 0xFFFFFFFF) as u32, (val >> 32) as u32)
302 } else {
303 let prev = &memory[prev_index];
305 (prev.v[0] as u32, (prev.v[0] >> 32) as u32)
306 };
307
308 let ref_lane = if pass == 0 && slice == 0 { lane } else { j2 % lanes };
310
311 let ref_index = index_alpha(pass, slice, lanes, segment_length, s, q, ref_lane == lane, j1);
312 let ref_block_index = (ref_lane * q + ref_index) as usize;
313
314 let new_block = if pass == 0 {
316 compress(&memory[prev_index], &memory[ref_block_index])
317 } else {
318 let mut new = compress(&memory[prev_index], &memory[ref_block_index]);
319 new.xor_with(&memory[cur_index]);
320 new
321 };
322
323 memory[cur_index] = new_block;
324 }
325}
326
327#[cfg(feature = "alloc")]
329fn generate_addresses(
330 pass: u32,
331 lane: u32,
332 slice: u32,
333 _lanes: u32,
334 t: u32,
335 argon_type: u32,
336 m_prime: u32,
337 segment_length: u32,
338) -> Vec<u64> {
339 let mut pseudo_rands = Vec::with_capacity(segment_length as usize);
340
341 let mut input = Block::zero();
343 input.v[0] = pass as u64;
344 input.v[1] = lane as u64;
345 input.v[2] = slice as u64;
346 input.v[3] = m_prime as u64;
347 input.v[4] = t as u64;
348 input.v[5] = argon_type as u64;
349
350 let zero_block = Block::zero();
351 let mut counter = 1u64;
353 while pseudo_rands.len() < segment_length as usize {
354 input.v[6] = counter;
355 let tmp = compress(&zero_block, &input);
356 let addr_block = compress(&zero_block, &tmp);
357
358 for i in 0..128 {
359 if pseudo_rands.len() >= segment_length as usize {
360 break;
361 }
362 pseudo_rands.push(addr_block.v[i]);
363 }
364 counter += 1;
365 }
366
367 pseudo_rands
368}
369
370fn index_alpha(
372 pass: u32,
373 slice: u32,
374 _lanes: u32,
375 segment_length: u32,
376 index_in_segment: u32,
377 q: u32,
378 same_lane: bool,
379 j1: u32,
380) -> u32 {
381 let reference_area_size = if pass == 0 {
383 if slice == 0 {
385 index_in_segment.saturating_sub(1)
387 } else {
388 if same_lane {
389 slice * segment_length + index_in_segment - 1
390 } else {
391 slice * segment_length - if index_in_segment == 0 { 1 } else { 0 }
392 }
393 }
394 } else {
395 if same_lane {
397 q - segment_length + index_in_segment - 1
398 } else {
399 q - segment_length - if index_in_segment == 0 { 1 } else { 0 }
400 }
401 };
402
403 if reference_area_size == 0 {
404 return 0;
405 }
406
407 let j1_64 = j1 as u64;
409 let x = (j1_64 * j1_64) >> 32;
410 let y = (reference_area_size as u64 * x) >> 32;
411 let relative_position = (reference_area_size as u64 - 1 - y) as u32;
412
413 let start_position = if pass == 0 {
415 0
416 } else {
417 if slice == SYNC_POINTS - 1 {
418 0
419 } else {
420 (slice + 1) * segment_length
421 }
422 };
423
424 (start_position + relative_position) % q
425}
426
427#[cfg(feature = "alloc")]
429fn compute_h0(
430 argon_type: u32,
431 password: &[u8],
432 salt: &[u8],
433 secret: &[u8],
434 ad: &[u8],
435 p: u32,
436 tag_length: u32,
437 m: u32,
438 t: u32,
439) -> [u8; 64] {
440 let mut blake = Blake2b::new_keyed(&[], 64);
441
442 blake.update(&p.to_le_bytes());
443 blake.update(&tag_length.to_le_bytes());
444 blake.update(&m.to_le_bytes());
445 blake.update(&t.to_le_bytes());
446 blake.update(&VERSION.to_le_bytes());
447 blake.update(&argon_type.to_le_bytes());
448 blake.update(&(password.len() as u32).to_le_bytes());
449 blake.update(password);
450 blake.update(&(salt.len() as u32).to_le_bytes());
451 blake.update(salt);
452 blake.update(&(secret.len() as u32).to_le_bytes());
453 blake.update(secret);
454 blake.update(&(ad.len() as u32).to_le_bytes());
455 blake.update(ad);
456
457 let hash = blake.sum();
458 let mut result = [0u8; 64];
459 result.copy_from_slice(&hash.as_ref()[..64]);
460 result
461}
462
463#[cfg(feature = "alloc")]
467fn variable_length_hash(input: &[u8], tag_length: u32) -> Vec<u8> {
468 if tag_length <= 64 {
469 let mut blake = Blake2b::new_keyed(&[], tag_length as usize);
471 blake.update(&tag_length.to_le_bytes());
472 blake.update(input);
473 let hash = blake.sum();
474 hash.as_ref()[..tag_length as usize].to_vec()
475 } else {
476 let r = ((tag_length + 31) / 32) - 2;
479
480 let mut result = Vec::with_capacity(tag_length as usize);
481
482 let mut blake = Blake2b::new_keyed(&[], 64);
484 blake.update(&tag_length.to_le_bytes());
485 blake.update(input);
486 let hash = blake.sum();
487 let mut v_prev = hash.as_ref()[..64].to_vec();
488
489 result.extend_from_slice(&v_prev[..32]);
491
492 for _ in 2..=r {
494 let mut blake = Blake2b::new_keyed(&[], 64);
495 blake.update(&v_prev);
496 let hash = blake.sum();
497 v_prev = hash.as_ref()[..64].to_vec();
498 result.extend_from_slice(&v_prev[..32]);
499 }
500
501 let remaining = tag_length - 32 * r;
503 let mut blake = Blake2b::new_keyed(&[], remaining as usize);
504 blake.update(&v_prev);
505 let hash = blake.sum();
506 result.extend_from_slice(&hash.as_ref()[..remaining as usize]);
507
508 result
509 }
510}
511
512fn compress(x: &Block, y: &Block) -> Block {
520 let mut r = Block::zero();
522 for i in 0..128 {
523 r.v[i] = x.v[i] ^ y.v[i];
524 }
525
526 let mut q = r.clone();
527
528 for row in 0..8 {
531 let base = row * 16;
532 permutation_p(&mut q.v[base..base + 16]);
533 }
534
535 for col in 0..8 {
538 let mut buf = [0u64; 16];
539 for row in 0..8 {
540 let src = row * 16 + col * 2;
541 buf[row * 2] = q.v[src];
542 buf[row * 2 + 1] = q.v[src + 1];
543 }
544 permutation_p(&mut buf);
545 for row in 0..8 {
546 let dst = row * 16 + col * 2;
547 q.v[dst] = buf[row * 2];
548 q.v[dst + 1] = buf[row * 2 + 1];
549 }
550 }
551
552 for i in 0..128 {
554 q.v[i] ^= r.v[i];
555 }
556
557 q
558}
559
560fn permutation_p(v: &mut [u64]) {
564 gb(v, 0, 4, 8, 12);
566 gb(v, 1, 5, 9, 13);
567 gb(v, 2, 6, 10, 14);
568 gb(v, 3, 7, 11, 15);
569
570 gb(v, 0, 5, 10, 15);
572 gb(v, 1, 6, 11, 12);
573 gb(v, 2, 7, 8, 13);
574 gb(v, 3, 4, 9, 14);
575}
576
577#[inline(always)]
581fn gb(v: &mut [u64], a: usize, b: usize, c: usize, d: usize) {
582 v[a] = v[a]
583 .wrapping_add(v[b])
584 .wrapping_add(2u64.wrapping_mul((v[a] as u32 as u64).wrapping_mul(v[b] as u32 as u64)));
585 v[d] = (v[d] ^ v[a]).rotate_right(32);
586 v[c] = v[c]
587 .wrapping_add(v[d])
588 .wrapping_add(2u64.wrapping_mul((v[c] as u32 as u64).wrapping_mul(v[d] as u32 as u64)));
589 v[b] = (v[b] ^ v[c]).rotate_right(24);
590
591 v[a] = v[a]
592 .wrapping_add(v[b])
593 .wrapping_add(2u64.wrapping_mul((v[a] as u32 as u64).wrapping_mul(v[b] as u32 as u64)));
594 v[d] = (v[d] ^ v[a]).rotate_right(16);
595 v[c] = v[c]
596 .wrapping_add(v[d])
597 .wrapping_add(2u64.wrapping_mul((v[c] as u32 as u64).wrapping_mul(v[d] as u32 as u64)));
598 v[b] = (v[b] ^ v[c]).rotate_right(63);
599}
600
601#[cfg(feature = "alloc")]
611pub fn encode_phc(params: &Params, salt: &[u8], tag: &[u8]) -> String {
612 let salt_b64 = base64_encode_no_pad(salt);
613 let tag_b64 = base64_encode_no_pad(tag);
614 alloc::format!(
615 "$argon2id$v=19$m={},t={},p={}${}${}",
616 params.memory,
617 params.iterations,
618 params.parallelism,
619 salt_b64,
620 tag_b64
621 )
622}
623
624#[cfg(feature = "alloc")]
628pub fn decode_phc(encoded: &str) -> Result<(Params, Vec<u8>, Vec<u8>), Argon2Error> {
629 let parts: Vec<&str> = encoded.split('$').collect();
630 if parts.len() != 6 {
632 return Err(Argon2Error::InvalidEncoding("invalid PHC string format"));
633 }
634 if parts[0] != "" {
635 return Err(Argon2Error::InvalidEncoding("must start with $"));
636 }
637 if parts[1] != "argon2id" {
638 return Err(Argon2Error::InvalidEncoding("unsupported algorithm"));
639 }
640 if parts[2] != "v=19" {
641 return Err(Argon2Error::InvalidEncoding("unsupported version"));
642 }
643
644 let param_parts: Vec<&str> = parts[3].split(',').collect();
646 if param_parts.len() != 3 {
647 return Err(Argon2Error::InvalidEncoding("invalid parameters"));
648 }
649
650 let memory = parse_param(param_parts[0], "m=")?;
651 let iterations = parse_param(param_parts[1], "t=")?;
652 let parallelism = parse_param(param_parts[2], "p=")?;
653
654 let salt = base64_decode_no_pad(parts[4]).map_err(|_| Argon2Error::InvalidEncoding("invalid base64 in salt"))?;
655 let tag = base64_decode_no_pad(parts[5]).map_err(|_| Argon2Error::InvalidEncoding("invalid base64 in hash"))?;
656
657 let params = Params {
658 iterations,
659 memory,
660 parallelism,
661 tag_length: tag.len() as u32,
662 };
663
664 Ok((params, salt, tag))
665}
666
667#[cfg(feature = "alloc")]
684pub fn hash_password(password: &[u8], salt: &[u8], params: &Params) -> Result<String, Argon2Error> {
685 let tag = derive_key(password, salt, &[], &[], params)?;
686 Ok(encode_phc(params, salt, &tag))
687}
688
689#[cfg(feature = "alloc")]
693pub fn verify_password(password: &[u8], encoded: &str) -> Result<(), Argon2Error> {
694 let (params, salt, expected_tag) = decode_phc(encoded)?;
695 let computed_tag = derive_key(password, &salt, &[], &[], ¶ms)?;
696 if constant_time_eq::constant_time_eq(&computed_tag, &expected_tag) {
697 Ok(())
698 } else {
699 Err(Argon2Error::VerifyMismatch)
700 }
701}
702
703#[cfg(feature = "alloc")]
708fn base64_encode_no_pad(input: &[u8]) -> String {
709 base64::encode(input, base64::Alphabet::StandardNoPadding)
710}
711
712#[cfg(feature = "alloc")]
713fn base64_decode_no_pad(input: &str) -> Result<Vec<u8>, ()> {
714 base64::decode(input.as_bytes(), base64::Alphabet::StandardNoPadding).map_err(|_| ())
715}
716
717#[cfg(feature = "alloc")]
718fn parse_param(s: &str, prefix: &str) -> Result<u32, Argon2Error> {
719 if !s.starts_with(prefix) {
720 return Err(Argon2Error::InvalidEncoding("invalid parameter prefix"));
721 }
722 s[prefix.len()..]
723 .parse::<u32>()
724 .map_err(|_| Argon2Error::InvalidEncoding("invalid parameter value"))
725}
726
727#[cfg(test)]
732mod tests {
733 use super::*;
734
735 fn derive_key_typed(
736 argon_type: u32,
737 password: &[u8],
738 salt: &[u8],
739 secret: &[u8],
740 ad: &[u8],
741 iterations: u32,
742 memory: u32,
743 parallelism: u32,
744 tag_length: u32,
745 ) -> Vec<u8> {
746 let params = Params {
747 iterations: iterations,
748 memory: memory,
749 parallelism: parallelism,
750 tag_length: tag_length,
751 };
752 argon2_core(argon_type, password, salt, secret, ad, ¶ms).unwrap()
753 }
754
755 #[test]
762 fn test_rfc9106_argon2d() {
763 let pwd = vec![0x01u8; 32];
764 let salt = vec![0x02u8; 16];
765 let secret = vec![0x03u8; 8];
766 let ad = vec![0x04u8; 12];
767 let expected = hex::decode("512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb").unwrap();
768 let result = derive_key_typed(ARGON2D, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
769 assert_eq!(result, expected);
770 }
771
772 #[test]
773 fn test_rfc9106_argon2i() {
774 let pwd = vec![0x01u8; 32];
775 let salt = vec![0x02u8; 16];
776 let secret = vec![0x03u8; 8];
777 let ad = vec![0x04u8; 12];
778 let expected = hex::decode("c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8").unwrap();
779 let result = derive_key_typed(ARGON2I, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
780 assert_eq!(result, expected);
781 }
782
783 #[test]
790 fn test_h0() {
791 let pwd = vec![0x01u8; 32];
792 let salt = vec![0x02u8; 16];
793 let secret = vec![0x03u8; 8];
794 let ad = vec![0x04u8; 12];
795 let h0 = compute_h0(ARGON2ID, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
796 let expected = "2889de487eb42ae500c0007ed9252f1069eadec40d5765b485de6dc2437a67b8546a2f0acc1a0882db8fcf74714b472e94df421a5da1112ffa11434370a1e997";
797 assert_eq!(hex::encode(h0), expected);
798 }
799
800 struct Vec3 {
806 mode: u32,
807 time: u32,
808 memory: u32,
809 threads: u32,
810 hash: &'static str,
811 }
812
813 const GO_VECTORS: &[Vec3] = &[
814 Vec3 {
815 mode: ARGON2I,
816 time: 1,
817 memory: 64,
818 threads: 1,
819 hash: "b9c401d1844a67d50eae3967dc28870b22e508092e861a37",
820 },
821 Vec3 {
822 mode: ARGON2D,
823 time: 1,
824 memory: 64,
825 threads: 1,
826 hash: "8727405fd07c32c78d64f547f24150d3f2e703a89f981a19",
827 },
828 Vec3 {
829 mode: ARGON2ID,
830 time: 1,
831 memory: 64,
832 threads: 1,
833 hash: "655ad15eac652dc59f7170a7332bf49b8469be1fdb9c28bb",
834 },
835 Vec3 {
836 mode: ARGON2I,
837 time: 2,
838 memory: 64,
839 threads: 1,
840 hash: "8cf3d8f76a6617afe35fac48eb0b7433a9a670ca4a07ed64",
841 },
842 Vec3 {
843 mode: ARGON2D,
844 time: 2,
845 memory: 64,
846 threads: 1,
847 hash: "3be9ec79a69b75d3752acb59a1fbb8b295a46529c48fbb75",
848 },
849 Vec3 {
850 mode: ARGON2ID,
851 time: 2,
852 memory: 64,
853 threads: 1,
854 hash: "068d62b26455936aa6ebe60060b0a65870dbfa3ddf8d41f7",
855 },
856 Vec3 {
857 mode: ARGON2I,
858 time: 2,
859 memory: 64,
860 threads: 2,
861 hash: "2089f3e78a799720f80af806553128f29b132cafe40d059f",
862 },
863 Vec3 {
864 mode: ARGON2D,
865 time: 2,
866 memory: 64,
867 threads: 2,
868 hash: "68e2462c98b8bc6bb60ec68db418ae2c9ed24fc6748a40e9",
869 },
870 Vec3 {
871 mode: ARGON2ID,
872 time: 2,
873 memory: 64,
874 threads: 2,
875 hash: "350ac37222f436ccb5c0972f1ebd3bf6b958bf2071841362",
876 },
877 Vec3 {
878 mode: ARGON2I,
879 time: 3,
880 memory: 256,
881 threads: 2,
882 hash: "f5bbf5d4c3836af13193053155b73ec7476a6a2eb93fd5e6",
883 },
884 Vec3 {
885 mode: ARGON2D,
886 time: 3,
887 memory: 256,
888 threads: 2,
889 hash: "f4f0669218eaf3641f39cc97efb915721102f4b128211ef2",
890 },
891 Vec3 {
892 mode: ARGON2ID,
893 time: 3,
894 memory: 256,
895 threads: 2,
896 hash: "4668d30ac4187e6878eedeacf0fd83c5a0a30db2cc16ef0b",
897 },
898 Vec3 {
899 mode: ARGON2I,
900 time: 4,
901 memory: 4096,
902 threads: 4,
903 hash: "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7",
904 },
905 Vec3 {
906 mode: ARGON2D,
907 time: 4,
908 memory: 4096,
909 threads: 4,
910 hash: "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d",
911 },
912 Vec3 {
913 mode: ARGON2ID,
914 time: 4,
915 memory: 4096,
916 threads: 4,
917 hash: "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a",
918 },
919 Vec3 {
920 mode: ARGON2I,
921 time: 4,
922 memory: 1024,
923 threads: 8,
924 hash: "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17",
925 },
926 Vec3 {
927 mode: ARGON2D,
928 time: 4,
929 memory: 1024,
930 threads: 8,
931 hash: "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9",
932 },
933 Vec3 {
934 mode: ARGON2ID,
935 time: 4,
936 memory: 1024,
937 threads: 8,
938 hash: "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f",
939 },
940 Vec3 {
941 mode: ARGON2I,
942 time: 2,
943 memory: 64,
944 threads: 3,
945 hash: "5cab452fe6b8479c8661def8cd703b611a3905a6d5477fe6",
946 },
947 Vec3 {
948 mode: ARGON2D,
949 time: 2,
950 memory: 64,
951 threads: 3,
952 hash: "22474a423bda2ccd36ec9afd5119e5c8949798cadf659f51",
953 },
954 Vec3 {
955 mode: ARGON2ID,
956 time: 2,
957 memory: 64,
958 threads: 3,
959 hash: "4a15b31aec7c2590b87d1f520be7d96f56658172deaa3079",
960 },
961 Vec3 {
962 mode: ARGON2I,
963 time: 3,
964 memory: 1024,
965 threads: 6,
966 hash: "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced",
967 },
968 Vec3 {
969 mode: ARGON2D,
970 time: 3,
971 memory: 1024,
972 threads: 6,
973 hash: "a3351b0319a53229152023d9206902f4ef59661cdca89481",
974 },
975 Vec3 {
976 mode: ARGON2ID,
977 time: 3,
978 memory: 1024,
979 threads: 6,
980 hash: "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016",
981 },
982 ];
983
984 #[test]
985 fn test_go_vectors() {
986 let password = b"password";
987 let salt = b"somesalt";
988 for (i, v) in GO_VECTORS.iter().enumerate() {
989 let expected = hex::decode(v.hash).unwrap();
990 let result = derive_key_typed(
991 v.mode,
992 password,
993 salt,
994 &[],
995 &[],
996 v.time,
997 v.memory,
998 v.threads,
999 expected.len() as u32,
1000 );
1001 assert_eq!(
1002 result, expected,
1003 "Go vector {} failed (mode={}, t={}, m={}, p={})",
1004 i, v.mode, v.time, v.memory, v.threads
1005 );
1006 }
1007 }
1008
1009 struct CVector {
1016 mode: u32,
1017 time: u32,
1018 memory: u32,
1019 threads: u32,
1020 hash: &'static str,
1021 pwd: &'static str,
1022 slt: &'static str,
1023 }
1024
1025 const C_VECTORS: &[CVector] = &[
1026 CVector {
1027 mode: ARGON2I,
1028 time: 2,
1029 memory: 65536,
1030 threads: 1,
1031 hash: "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
1032 pwd: "password",
1033 slt: "somesalt",
1034 },
1035 CVector {
1036 mode: ARGON2I,
1037 time: 2,
1038 memory: 262144,
1039 threads: 1,
1040 hash: "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb",
1041 pwd: "password",
1042 slt: "somesalt",
1043 },
1044 CVector {
1045 mode: ARGON2I,
1046 time: 2,
1047 memory: 256,
1048 threads: 1,
1049 hash: "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f",
1050 pwd: "password",
1051 slt: "somesalt",
1052 },
1053 CVector {
1054 mode: ARGON2I,
1055 time: 2,
1056 memory: 256,
1057 threads: 2,
1058 hash: "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61",
1059 pwd: "password",
1060 slt: "somesalt",
1061 },
1062 CVector {
1063 mode: ARGON2I,
1064 time: 1,
1065 memory: 65536,
1066 threads: 1,
1067 hash: "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf",
1068 pwd: "password",
1069 slt: "somesalt",
1070 },
1071 CVector {
1072 mode: ARGON2I,
1073 time: 4,
1074 memory: 65536,
1075 threads: 1,
1076 hash: "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b",
1077 pwd: "password",
1078 slt: "somesalt",
1079 },
1080 CVector {
1081 mode: ARGON2I,
1082 time: 2,
1083 memory: 65536,
1084 threads: 1,
1085 hash: "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee",
1086 pwd: "differentpassword",
1087 slt: "somesalt",
1088 },
1089 CVector {
1090 mode: ARGON2I,
1091 time: 2,
1092 memory: 65536,
1093 threads: 1,
1094 hash: "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271",
1095 pwd: "password",
1096 slt: "diffsalt",
1097 },
1098 CVector {
1099 mode: ARGON2ID,
1100 time: 2,
1101 memory: 65536,
1102 threads: 1,
1103 hash: "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7",
1104 pwd: "password",
1105 slt: "somesalt",
1106 },
1107 CVector {
1108 mode: ARGON2ID,
1109 time: 2,
1110 memory: 262144,
1111 threads: 1,
1112 hash: "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c",
1113 pwd: "password",
1114 slt: "somesalt",
1115 },
1116 CVector {
1117 mode: ARGON2ID,
1118 time: 2,
1119 memory: 256,
1120 threads: 1,
1121 hash: "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe",
1122 pwd: "password",
1123 slt: "somesalt",
1124 },
1125 CVector {
1126 mode: ARGON2ID,
1127 time: 2,
1128 memory: 256,
1129 threads: 2,
1130 hash: "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037",
1131 pwd: "password",
1132 slt: "somesalt",
1133 },
1134 CVector {
1135 mode: ARGON2ID,
1136 time: 1,
1137 memory: 65536,
1138 threads: 1,
1139 hash: "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98",
1140 pwd: "password",
1141 slt: "somesalt",
1142 },
1143 CVector {
1144 mode: ARGON2ID,
1145 time: 4,
1146 memory: 65536,
1147 threads: 1,
1148 hash: "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c",
1149 pwd: "password",
1150 slt: "somesalt",
1151 },
1152 CVector {
1153 mode: ARGON2ID,
1154 time: 2,
1155 memory: 65536,
1156 threads: 1,
1157 hash: "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde",
1158 pwd: "differentpassword",
1159 slt: "somesalt",
1160 },
1161 CVector {
1162 mode: ARGON2ID,
1163 time: 2,
1164 memory: 65536,
1165 threads: 1,
1166 hash: "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c",
1167 pwd: "password",
1168 slt: "diffsalt",
1169 },
1170 ];
1171
1172 #[test]
1173 fn test_c_reference_vectors() {
1174 for (i, v) in C_VECTORS.iter().enumerate() {
1175 let expected = hex::decode(v.hash).unwrap();
1176 let result = derive_key_typed(
1177 v.mode,
1178 v.pwd.as_bytes(),
1179 v.slt.as_bytes(),
1180 &[],
1181 &[],
1182 v.time,
1183 v.memory,
1184 v.threads,
1185 expected.len() as u32,
1186 );
1187 assert_eq!(
1188 result, expected,
1189 "C ref vector {} failed (mode={}, t={}, m={}, p={})",
1190 i, v.mode, v.time, v.memory, v.threads
1191 );
1192 }
1193 }
1194
1195 #[test]
1200 fn test_phc_encode_decode() {
1201 let params = Params {
1202 iterations: 3,
1203 memory: 65536,
1204 parallelism: 4,
1205 tag_length: 32,
1206 };
1207 let salt = b"somesalt12345678";
1208 let tag = vec![0xAB; 32];
1209 let encoded = encode_phc(¶ms, salt, &tag);
1210 assert!(encoded.starts_with("$argon2id$v=19$m=65536,t=3,p=4$"));
1211 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
1212 assert_eq!(dp.iterations, 3);
1213 assert_eq!(dp.memory, 65536);
1214 assert_eq!(dp.parallelism, 4);
1215 assert_eq!(dp.tag_length, 32);
1216 assert_eq!(ds, salt);
1217 assert_eq!(dt, tag);
1218 }
1219
1220 #[test]
1221 fn test_hash_and_verify() {
1222 let password = b"correct horse battery staple";
1223 let salt = b"randomsalt123456";
1224 let params = Params {
1225 iterations: 1,
1226 memory: 64,
1227 parallelism: 1,
1228 tag_length: 32,
1229 };
1230 let encoded = hash_password(password, salt, ¶ms).unwrap();
1231 assert!(verify_password(password, &encoded).is_ok());
1232 assert_eq!(verify_password(b"wrong password", &encoded), Err(Argon2Error::VerifyMismatch));
1233 }
1234
1235 #[test]
1236 fn test_decode_phc_invalid() {
1237 assert!(decode_phc("").is_err());
1238 assert!(decode_phc("$argon2i$v=19$m=4096,t=3,p=1$salt$hash").is_err());
1239 assert!(decode_phc("$argon2id$v=16$m=4096,t=3,p=1$salt$hash").is_err());
1240 assert!(decode_phc("not a phc string").is_err());
1241 }
1242
1243 #[test]
1244 fn test_invalid_params() {
1245 assert!(
1246 derive_key(
1247 b"password",
1248 b"salt",
1249 &[],
1250 &[],
1251 &Params {
1252 iterations: 0,
1253 memory: 64,
1254 parallelism: 1,
1255 tag_length: 32
1256 }
1257 )
1258 .is_err()
1259 );
1260 assert!(
1261 derive_key(
1262 b"password",
1263 b"salt",
1264 &[],
1265 &[],
1266 &Params {
1267 iterations: 1,
1268 memory: 4,
1269 parallelism: 1,
1270 tag_length: 32
1271 }
1272 )
1273 .is_err()
1274 );
1275 assert!(
1276 derive_key(
1277 b"password",
1278 b"salt",
1279 &[],
1280 &[],
1281 &Params {
1282 iterations: 1,
1283 memory: 64,
1284 parallelism: 1,
1285 tag_length: 3
1286 }
1287 )
1288 .is_err()
1289 );
1290 }
1291
1292 #[test]
1293 fn test_variable_length_hash_short() {
1294 let input = b"test input";
1295 let r32 = variable_length_hash(input, 32);
1296 assert_eq!(r32.len(), 32);
1297 assert_eq!(variable_length_hash(input, 32), r32);
1298 let r48 = variable_length_hash(input, 48);
1299 assert_eq!(r48.len(), 48);
1300 assert_ne!(&r32[..], &r48[..32]);
1301 }
1302
1303 #[test]
1304 fn test_variable_length_hash_long() {
1305 assert_eq!(variable_length_hash(b"test input for long hash", 128).len(), 128);
1306 assert_eq!(variable_length_hash(b"test input for long hash", 1024).len(), 1024);
1307 }
1308
1309 #[test]
1310 fn test_argon2id_min_memory() {
1311 let result = derive_key(
1312 b"password",
1313 b"saltsalt",
1314 &[],
1315 &[],
1316 &Params {
1317 iterations: 1,
1318 memory: 8,
1319 parallelism: 1,
1320 tag_length: 32,
1321 },
1322 )
1323 .unwrap();
1324 assert_eq!(result.len(), 32);
1325 }
1326
1327 #[test]
1328 fn test_argon2id_multiple_lanes() {
1329 assert_eq!(
1330 derive_key(
1331 b"password",
1332 b"saltsaltsaltsalt",
1333 &[],
1334 &[],
1335 &Params {
1336 iterations: 1,
1337 memory: 64,
1338 parallelism: 4,
1339 tag_length: 32
1340 }
1341 )
1342 .unwrap()
1343 .len(),
1344 32
1345 );
1346 }
1347
1348 #[test]
1349 fn test_different_passwords() {
1350 let p = Params {
1351 iterations: 1,
1352 memory: 64,
1353 parallelism: 1,
1354 tag_length: 32,
1355 };
1356 assert_ne!(
1357 derive_key(b"password1", b"saltsaltsaltsalt", &[], &[], &p).unwrap(),
1358 derive_key(b"password2", b"saltsaltsaltsalt", &[], &[], &p).unwrap()
1359 );
1360 }
1361
1362 #[test]
1363 fn test_different_salts() {
1364 let p = Params {
1365 iterations: 1,
1366 memory: 64,
1367 parallelism: 1,
1368 tag_length: 32,
1369 };
1370 assert_ne!(
1371 derive_key(b"password", b"salt1234salt1234", &[], &[], &p).unwrap(),
1372 derive_key(b"password", b"salt5678salt5678", &[], &[], &p).unwrap()
1373 );
1374 }
1375
1376 #[test]
1377 fn test_long_tag() {
1378 let result = derive_key(
1379 b"password",
1380 b"saltsaltsaltsalt",
1381 &[],
1382 &[],
1383 &Params {
1384 iterations: 1,
1385 memory: 64,
1386 parallelism: 1,
1387 tag_length: 64,
1388 },
1389 )
1390 .unwrap();
1391 assert_eq!(result.len(), 64);
1392 }
1393
1394 #[test]
1395 fn test_phc_roundtrip() {
1396 let password = b"password";
1397 let salt = b"somesalt";
1398 let params = Params {
1399 iterations: 1,
1400 memory: 64,
1401 parallelism: 1,
1402 tag_length: 24,
1403 };
1404 let tag = derive_key(password, salt, &[], &[], ¶ms).unwrap();
1405 let encoded = encode_phc(¶ms, salt, &tag);
1406 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
1407 assert_eq!(dp.memory, params.memory);
1408 assert_eq!(dp.iterations, params.iterations);
1409 assert_eq!(dp.parallelism, params.parallelism);
1410 assert_eq!(ds, salt);
1411 assert_eq!(dt, tag);
1412 }
1413
1414 fn argon2_core_with_passes(
1422 argon_type: u32,
1423 password: &[u8],
1424 salt: &[u8],
1425 secret: &[u8],
1426 ad: &[u8],
1427 params: &Params,
1428 ) -> Vec<Vec<Block>> {
1429 let p = params.parallelism;
1430 let t = params.iterations;
1431 let m = params.memory;
1432 let tag_length = params.tag_length;
1433
1434 let h0 = compute_h0(argon_type, password, salt, secret, ad, p, tag_length, m, t);
1435 let m_prime = 4 * p * (m / (4 * p));
1436 let q = m_prime / p;
1437
1438 let mut memory: Vec<Block> = vec![Block::zero(); m_prime as usize];
1439
1440 for i in 0..p {
1441 let mut input = Vec::with_capacity(72);
1442 input.extend_from_slice(&h0);
1443 input.extend_from_slice(&0u32.to_le_bytes());
1444 input.extend_from_slice(&i.to_le_bytes());
1445 let block_bytes = variable_length_hash(&input, BLOCK_SIZE as u32);
1446 memory[(i * q) as usize] = Block::from_bytes(&block_bytes);
1447
1448 let mut input = Vec::with_capacity(72);
1449 input.extend_from_slice(&h0);
1450 input.extend_from_slice(&1u32.to_le_bytes());
1451 input.extend_from_slice(&i.to_le_bytes());
1452 let block_bytes = variable_length_hash(&input, BLOCK_SIZE as u32);
1453 memory[(i * q + 1) as usize] = Block::from_bytes(&block_bytes);
1454 }
1455
1456 let mut pass_snapshots = Vec::new();
1457
1458 for pass in 0..t {
1459 for slice in 0..SYNC_POINTS {
1460 for lane in 0..p {
1461 fill_segment(&mut memory, argon_type, pass, lane, slice, p, q, t, m_prime);
1462 }
1463 }
1464 pass_snapshots.push(memory.clone());
1465 }
1466
1467 pass_snapshots
1468 }
1469
1470 fn block0_word(block: &Block, idx: usize) -> String {
1471 format!("{:016x}", block.v[idx])
1472 }
1473
1474 fn block_last_word(block: &Block, idx: usize) -> String {
1475 format!("{:016x}", block.v[idx])
1476 }
1477
1478 #[test]
1479 fn test_rfc9106_argon2d_intermediate_blocks() {
1480 let pwd = vec![0x01u8; 32];
1481 let salt = vec![0x02u8; 16];
1482 let secret = vec![0x03u8; 8];
1483 let ad = vec![0x04u8; 12];
1484 let params = Params {
1485 iterations: 3,
1486 memory: 32,
1487 parallelism: 4,
1488 tag_length: 32,
1489 };
1490 let passes = argon2_core_with_passes(ARGON2D, &pwd, &salt, &secret, &ad, ¶ms);
1491
1492 let p = params.parallelism;
1493 let q = (4 * p * (params.memory / (4 * p))) / p;
1494 let m_prime = p * q;
1495
1496 assert_eq!(block0_word(&passes[0][0], 0), "db2fea6b2c6f5c8a");
1497 assert_eq!(block_last_word(&passes[0][(m_prime - 1) as usize], 127), "6a6c49d2cb75d5b6");
1498
1499 assert_eq!(block0_word(&passes[1][0], 0), "d3801200410f8c0d");
1500 assert_eq!(block_last_word(&passes[1][(m_prime - 1) as usize], 127), "2dbfff23f31b5883");
1501
1502 assert_eq!(block0_word(&passes[2][0], 0), "5f047b575c5ff4d2");
1503 assert_eq!(block_last_word(&passes[2][(m_prime - 1) as usize], 127), "c341b3ca45c10da5");
1504 }
1505
1506 #[test]
1507 fn test_rfc9106_argon2i_intermediate_blocks() {
1508 let pwd = vec![0x01u8; 32];
1509 let salt = vec![0x02u8; 16];
1510 let secret = vec![0x03u8; 8];
1511 let ad = vec![0x04u8; 12];
1512 let params = Params {
1513 iterations: 3,
1514 memory: 32,
1515 parallelism: 4,
1516 tag_length: 32,
1517 };
1518 let passes = argon2_core_with_passes(ARGON2I, &pwd, &salt, &secret, &ad, ¶ms);
1519
1520 let p = params.parallelism;
1521 let q = (4 * p * (params.memory / (4 * p))) / p;
1522 let m_prime = p * q;
1523
1524 assert_eq!(block0_word(&passes[0][0], 0), "f8f9e84545db08f6");
1525 assert_eq!(block_last_word(&passes[0][(m_prime - 1) as usize], 127), "c570f2ab2a86cf00");
1526
1527 assert_eq!(block0_word(&passes[1][0], 0), "b2e4ddfcf76dc85a");
1528 assert_eq!(block_last_word(&passes[1][(m_prime - 1) as usize], 127), "421b3c6e9555b79d");
1529
1530 assert_eq!(block0_word(&passes[2][0], 0), "af2a8bd8482c2f11");
1531 assert_eq!(block_last_word(&passes[2][(m_prime - 1) as usize], 127), "71e436f035f30ed0");
1532 }
1533
1534 #[test]
1539 fn test_h0_argon2d() {
1540 let pwd = vec![0x01u8; 32];
1541 let salt = vec![0x02u8; 16];
1542 let secret = vec![0x03u8; 8];
1543 let ad = vec![0x04u8; 12];
1544 let h0 = compute_h0(ARGON2D, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1545 let expected = "b8819791a0359660bb7709c85fa48f04d5d82c05c5f215ccdb885491717cf757082c28b951be381410b5fc2eb7274033b9fdc7ae672bcaac5d179097a4af3109";
1546 assert_eq!(hex::encode(h0), expected);
1547 }
1548
1549 #[test]
1550 fn test_h0_argon2i() {
1551 let pwd = vec![0x01u8; 32];
1552 let salt = vec![0x02u8; 16];
1553 let secret = vec![0x03u8; 8];
1554 let ad = vec![0x04u8; 12];
1555 let h0 = compute_h0(ARGON2I, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1556 let expected = "c46065815276a0b3e731731c902f1fd80cf776907fbb7b6a5ca72e7b56011feeca446c86dd75b9469a5e6879dec4b72d0863fb939b982e5f397cc7d164fddaa9";
1557 assert_eq!(hex::encode(h0), expected);
1558 }
1559
1560 #[test]
1565 fn test_argon2id_empty_secret_and_ad() {
1566 let result = derive_key(
1567 b"password",
1568 b"saltsaltsaltsalt",
1569 &[],
1570 &[],
1571 &Params {
1572 iterations: 1,
1573 memory: 64,
1574 parallelism: 1,
1575 tag_length: 32,
1576 },
1577 )
1578 .unwrap();
1579 assert_eq!(result.len(), 32);
1580 let result2 = derive_key(
1581 b"password",
1582 b"saltsaltsaltsalt",
1583 &[],
1584 &[],
1585 &Params {
1586 iterations: 1,
1587 memory: 64,
1588 parallelism: 1,
1589 tag_length: 32,
1590 },
1591 )
1592 .unwrap();
1593 assert_eq!(result, result2);
1594 }
1595
1596 #[test]
1597 fn test_argon2id_with_secret() {
1598 let p = Params {
1599 iterations: 1,
1600 memory: 64,
1601 parallelism: 1,
1602 tag_length: 32,
1603 };
1604 let without_secret = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p).unwrap();
1605 let with_secret = derive_key(b"password", b"saltsaltsaltsalt", b"secret", &[], &p).unwrap();
1606 assert_ne!(without_secret, with_secret);
1607 }
1608
1609 #[test]
1610 fn test_argon2id_with_ad() {
1611 let p = Params {
1612 iterations: 1,
1613 memory: 64,
1614 parallelism: 1,
1615 tag_length: 32,
1616 };
1617 let without_ad = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p).unwrap();
1618 let with_ad = derive_key(b"password", b"saltsaltsaltsalt", &[], b"associated data", &p).unwrap();
1619 assert_ne!(without_ad, with_ad);
1620 }
1621
1622 #[test]
1623 fn test_argon2id_tag_length_4() {
1624 let result = derive_key(
1625 b"password",
1626 b"saltsaltsaltsalt",
1627 &[],
1628 &[],
1629 &Params {
1630 iterations: 1,
1631 memory: 64,
1632 parallelism: 1,
1633 tag_length: 4,
1634 },
1635 )
1636 .unwrap();
1637 assert_eq!(result.len(), 4);
1638 }
1639
1640 #[test]
1641 fn test_argon2id_tag_length_128() {
1642 let result = derive_key(
1643 b"password",
1644 b"saltsaltsaltsalt",
1645 &[],
1646 &[],
1647 &Params {
1648 iterations: 1,
1649 memory: 64,
1650 parallelism: 1,
1651 tag_length: 128,
1652 },
1653 )
1654 .unwrap();
1655 assert_eq!(result.len(), 128);
1656 }
1657
1658 #[test]
1659 fn test_argon2id_tag_length_256() {
1660 let result = derive_key(
1661 b"password",
1662 b"saltsaltsaltsalt",
1663 &[],
1664 &[],
1665 &Params {
1666 iterations: 1,
1667 memory: 64,
1668 parallelism: 1,
1669 tag_length: 256,
1670 },
1671 )
1672 .unwrap();
1673 assert_eq!(result.len(), 256);
1674 }
1675
1676 #[test]
1677 fn test_argon2id_long_tag_consistency() {
1678 let p = Params {
1679 iterations: 1,
1680 memory: 64,
1681 parallelism: 1,
1682 tag_length: 100,
1683 };
1684 let r1 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p).unwrap();
1685 let r2 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p).unwrap();
1686 assert_eq!(r1, r2);
1687 assert_eq!(r1.len(), 100);
1688 }
1689
1690 #[test]
1691 fn test_argon2i_long_tag_consistency() {
1692 let params = Params {
1693 iterations: 1,
1694 memory: 64,
1695 parallelism: 1,
1696 tag_length: 100,
1697 };
1698 let r1 = argon2_core(ARGON2I, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms).unwrap();
1699 let r2 = argon2_core(ARGON2I, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms).unwrap();
1700 assert_eq!(r1, r2);
1701 assert_eq!(r1.len(), 100);
1702 }
1703
1704 #[test]
1705 fn test_argon2d_long_tag_consistency() {
1706 let params = Params {
1707 iterations: 1,
1708 memory: 64,
1709 parallelism: 1,
1710 tag_length: 100,
1711 };
1712 let r1 = argon2_core(ARGON2D, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms).unwrap();
1713 let r2 = argon2_core(ARGON2D, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms).unwrap();
1714 assert_eq!(r1, r2);
1715 assert_eq!(r1.len(), 100);
1716 }
1717
1718 #[test]
1719 fn test_argon2id_single_pass() {
1720 let result = derive_key(
1721 b"password",
1722 b"saltsalt",
1723 &[],
1724 &[],
1725 &Params {
1726 iterations: 1,
1727 memory: 32,
1728 parallelism: 1,
1729 tag_length: 32,
1730 },
1731 )
1732 .unwrap();
1733 assert_eq!(result.len(), 32);
1734 }
1735
1736 #[test]
1737 fn test_argon2id_high_parallelism() {
1738 let result = derive_key(
1739 b"password",
1740 b"saltsaltsaltsalt",
1741 &[],
1742 &[],
1743 &Params {
1744 iterations: 1,
1745 memory: 64,
1746 parallelism: 8,
1747 tag_length: 32,
1748 },
1749 )
1750 .unwrap();
1751 assert_eq!(result.len(), 32);
1752 }
1753
1754 #[test]
1755 fn test_argon2d_rfc_h0() {
1756 let pwd = vec![0x01u8; 32];
1757 let salt = vec![0x02u8; 16];
1758 let secret = vec![0x03u8; 8];
1759 let ad = vec![0x04u8; 12];
1760 let h0 = compute_h0(ARGON2D, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1761 assert_eq!(h0[0], 0xb8);
1762 assert_eq!(h0[1], 0x81);
1763 assert_eq!(h0[63], 0x09);
1764 }
1765
1766 #[test]
1767 fn test_variable_length_hash_exact_64() {
1768 let input = b"test";
1769 let result = variable_length_hash(input, 64);
1770 assert_eq!(result.len(), 64);
1771 }
1772
1773 #[test]
1774 fn test_variable_length_hash_65_bytes() {
1775 let input = b"test";
1776 let result = variable_length_hash(input, 65);
1777 assert_eq!(result.len(), 65);
1778 let result2 = variable_length_hash(input, 65);
1779 assert_eq!(result, result2);
1780 }
1781
1782 #[test]
1783 fn test_variable_length_hash_deterministic() {
1784 for len in [4, 16, 32, 48, 64, 65, 96, 128, 256, 512, 1024] {
1785 let r1 = variable_length_hash(b"determinism test", len);
1786 let r2 = variable_length_hash(b"determinism test", len);
1787 assert_eq!(r1, r2, "variable_length_hash not deterministic for len={}", len);
1788 assert_eq!(r1.len(), len as usize);
1789 }
1790 }
1791
1792 #[test]
1793 fn test_compress_deterministic() {
1794 let a = Block::from_bytes(&[0xAA; BLOCK_SIZE]);
1795 let b = Block::from_bytes(&[0xBB; BLOCK_SIZE]);
1796 let c1 = compress(&a, &b);
1797 let c2 = compress(&a, &b);
1798 assert_eq!(c1.v, c2.v);
1799 }
1800
1801 #[test]
1802 fn test_compress_xor_symmetry() {
1803 let a = Block::from_bytes(&[0x11; BLOCK_SIZE]);
1804 let b = Block::from_bytes(&[0x22; BLOCK_SIZE]);
1805 let c_ab = compress(&a, &b);
1806 let c_ba = compress(&b, &a);
1807 assert_eq!(c_ab.v, c_ba.v, "G(X,Y) should equal G(Y,X) since R = X XOR Y is symmetric");
1808 }
1809
1810 #[test]
1811 fn test_block_from_bytes_roundtrip() {
1812 let original = [0x42u8; BLOCK_SIZE];
1813 let block = Block::from_bytes(&original);
1814 let recovered = block.to_bytes();
1815 assert_eq!(original, recovered);
1816 }
1817
1818 #[test]
1819 fn test_argon2id_different_iterations() {
1820 let p1 = Params {
1821 iterations: 1,
1822 memory: 64,
1823 parallelism: 1,
1824 tag_length: 32,
1825 };
1826 let p2 = Params {
1827 iterations: 2,
1828 memory: 64,
1829 parallelism: 1,
1830 tag_length: 32,
1831 };
1832 let r1 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p1).unwrap();
1833 let r2 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p2).unwrap();
1834 assert_ne!(r1, r2);
1835 }
1836
1837 #[test]
1838 fn test_argon2id_different_memory() {
1839 let p1 = Params {
1840 iterations: 1,
1841 memory: 64,
1842 parallelism: 1,
1843 tag_length: 32,
1844 };
1845 let p2 = Params {
1846 iterations: 1,
1847 memory: 128,
1848 parallelism: 1,
1849 tag_length: 32,
1850 };
1851 let r1 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p1).unwrap();
1852 let r2 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p2).unwrap();
1853 assert_ne!(r1, r2);
1854 }
1855
1856 #[test]
1857 fn test_argon2id_different_parallelisms() {
1858 let p1 = Params {
1859 iterations: 1,
1860 memory: 64,
1861 parallelism: 1,
1862 tag_length: 32,
1863 };
1864 let p2 = Params {
1865 iterations: 1,
1866 memory: 64,
1867 parallelism: 2,
1868 tag_length: 32,
1869 };
1870 let r1 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p1).unwrap();
1871 let r2 = derive_key(b"password", b"saltsaltsaltsalt", &[], &[], &p2).unwrap();
1872 assert_ne!(r1, r2);
1873 }
1874
1875 #[test]
1876 fn test_argon2i_rfc9106_tag() {
1877 let pwd = vec![0x01u8; 32];
1878 let salt = vec![0x02u8; 16];
1879 let secret = vec![0x03u8; 8];
1880 let ad = vec![0x04u8; 12];
1881 let expected = hex::decode("c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8").unwrap();
1882 let result = derive_key_typed(ARGON2I, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
1883 assert_eq!(result, expected);
1884 }
1885
1886 #[test]
1887 fn test_argon2d_rfc9106_tag() {
1888 let pwd = vec![0x01u8; 32];
1889 let salt = vec![0x02u8; 16];
1890 let secret = vec![0x03u8; 8];
1891 let ad = vec![0x04u8; 12];
1892 let expected = hex::decode("512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb").unwrap();
1893 let result = derive_key_typed(ARGON2D, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
1894 assert_eq!(result, expected);
1895 }
1896
1897 #[test]
1898 fn test_phc_verify_known() {
1899 let password = b"password";
1900 let salt = b"randomsalt123456";
1901 let params = Params {
1902 iterations: 1,
1903 memory: 64,
1904 parallelism: 1,
1905 tag_length: 32,
1906 };
1907 let encoded = hash_password(password, salt, ¶ms).unwrap();
1908 assert!(verify_password(password, &encoded).is_ok());
1909 assert_eq!(verify_password(b"wrong", &encoded), Err(Argon2Error::VerifyMismatch));
1910 }
1911
1912 #[test]
1913 fn test_decode_phc_roundtrip_all_types() {
1914 for tag_len in [4, 16, 32, 64] {
1915 let params = Params {
1916 iterations: 1,
1917 memory: 64,
1918 parallelism: 1,
1919 tag_length: tag_len,
1920 };
1921 let salt = b"testsalt12345678";
1922 let tag = vec![0xAB; tag_len as usize];
1923 let encoded = encode_phc(¶ms, salt, &tag);
1924 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
1925 assert_eq!(dp.memory, 64);
1926 assert_eq!(dp.iterations, 1);
1927 assert_eq!(dp.parallelism, 1);
1928 assert_eq!(dp.tag_length, tag_len);
1929 assert_eq!(ds, salt);
1930 assert_eq!(dt, tag);
1931 }
1932 }
1933
1934 #[test]
1935 fn test_index_alpha_pass0_slice0() {
1936 let result = index_alpha(0, 0, 4, 2, 2, 8, true, 0xFFFFFFFF);
1937 assert!(result < 8);
1938 }
1939
1940 #[test]
1941 fn test_index_alpha_reference_area_size_zero() {
1942 let result = index_alpha(0, 0, 4, 2, 0, 8, true, 0xFFFFFFFF);
1943 assert_eq!(result, 0);
1944 }
1945
1946 #[test]
1947 fn test_gb_known_values() {
1948 let mut v = [0u64; 16];
1949 v[0] = 1;
1950 v[1] = 2;
1951 v[2] = 3;
1952 v[3] = 4;
1953 gb(&mut v, 0, 1, 2, 3);
1954 assert_ne!(v[0], 1);
1955 assert_ne!(v[1], 2);
1956 assert_ne!(v[2], 3);
1957 assert_ne!(v[3], 4);
1958 }
1959
1960 #[test]
1961 fn test_permutation_p_deterministic() {
1962 let mut v1: Vec<u64> = (0..16).collect();
1963 let mut v2: Vec<u64> = (0..16).collect();
1964 permutation_p(&mut v1);
1965 permutation_p(&mut v2);
1966 assert_eq!(v1, v2);
1967 }
1968}