1#[cfg(feature = "alloc")]
14extern crate alloc;
15
16#[cfg(feature = "alloc")]
17use alloc::{string::String, vec, vec::Vec};
18use core::mem::MaybeUninit;
19
20use crate::{Hasher, blake2::Blake2b};
21
22mod fill;
23
24#[cfg(target_arch = "x86_64")]
25mod fill_avx2;
26
27#[cfg(target_arch = "aarch64")]
28mod fill_neon;
29
30#[cfg(test)]
31use fill::{compress, permutation_p};
32
33const VERSION: u32 = 0x13;
35
36const SYNC_POINTS: u32 = 4;
38
39const BLOCK_SIZE: usize = 1024;
41
42#[allow(dead_code)]
44const ARGON2D: u32 = 0;
45const ARGON2I: u32 = 1;
46const ARGON2ID: u32 = 2;
47
48#[cfg(feature = "alloc")]
50const DEFAULT_TAG_LENGTH: usize = 64;
51
52#[derive(Debug, Clone)]
69pub struct Params {
70 pub iterations: u32,
72 pub memory: u32,
74 pub parallelism: u32,
76}
77
78impl Default for Params {
79 fn default() -> Self {
81 Params {
82 iterations: 3,
83 memory: 65536,
84 parallelism: 4,
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91#[cfg(feature = "alloc")]
92pub enum Argon2Error {
93 InvalidParams(&'static str),
95 InvalidEncoding(&'static str),
97 VerifyMismatch,
99}
100
101#[cfg(feature = "alloc")]
102impl core::fmt::Display for Argon2Error {
103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 match self {
105 Argon2Error::InvalidParams(msg) => write!(f, "argon2: invalid params: {}", msg),
106 Argon2Error::InvalidEncoding(msg) => write!(f, "argon2: invalid encoding: {}", msg),
107 Argon2Error::VerifyMismatch => write!(f, "argon2: verification failed"),
108 }
109 }
110}
111
112#[cfg(feature = "alloc")]
147pub fn derive_key(
148 out: &mut [u8],
149 password: &[u8],
150 salt: &[u8],
151 secret: &[u8],
152 ad: &[u8],
153 params: &Params,
154) -> Result<(), Argon2Error> {
155 argon2_core(ARGON2ID, password, salt, secret, ad, params, out)
156}
157
158#[cfg(feature = "alloc")]
177pub fn hash_password(password: &[u8], salt: &[u8], params: &Params) -> Result<String, Argon2Error> {
178 let mut tag = vec![0u8; DEFAULT_TAG_LENGTH];
179 derive_key(&mut tag, password, salt, &[], &[], params)?;
180 Ok(encode_phc(params, salt, &tag))
181}
182
183#[cfg(feature = "alloc")]
187pub fn verify_password(password: &[u8], encoded: &str) -> Result<(), Argon2Error> {
188 let (params, salt, expected_tag) = decode_phc(encoded)?;
189 let mut computed_tag = vec![0u8; expected_tag.len()];
190 derive_key(&mut computed_tag, password, &salt, &[], &[], ¶ms)?;
191 if constant_time_eq::constant_time_eq(&computed_tag, &expected_tag) {
192 Ok(())
193 } else {
194 Err(Argon2Error::VerifyMismatch)
195 }
196}
197
198#[cfg(feature = "alloc")]
208pub fn encode_phc(params: &Params, salt: &[u8], tag: &[u8]) -> String {
209 let salt_b64 = base64_encode_no_pad(salt);
210 let tag_b64 = base64_encode_no_pad(tag);
211 alloc::format!(
212 "$argon2id$v=19$m={},t={},p={}${}${}",
213 params.memory,
214 params.iterations,
215 params.parallelism,
216 salt_b64,
217 tag_b64
218 )
219}
220
221#[cfg(feature = "alloc")]
225pub fn decode_phc(encoded: &str) -> Result<(Params, Vec<u8>, Vec<u8>), Argon2Error> {
226 let parts: Vec<&str> = encoded.split('$').collect();
227 if parts.len() != 6 {
229 return Err(Argon2Error::InvalidEncoding("invalid PHC string format"));
230 }
231 if parts[0] != "" {
232 return Err(Argon2Error::InvalidEncoding("must start with $"));
233 }
234 if parts[1] != "argon2id" {
235 return Err(Argon2Error::InvalidEncoding("unsupported algorithm"));
236 }
237 if parts[2] != "v=19" {
238 return Err(Argon2Error::InvalidEncoding("unsupported version"));
239 }
240
241 let param_parts: Vec<&str> = parts[3].split(',').collect();
243 if param_parts.len() != 3 {
244 return Err(Argon2Error::InvalidEncoding("invalid parameters"));
245 }
246
247 let memory = parse_param(param_parts[0], "m=")?;
248 let iterations = parse_param(param_parts[1], "t=")?;
249 let parallelism = parse_param(param_parts[2], "p=")?;
250
251 let salt = base64_decode_no_pad(parts[4]).map_err(|_| Argon2Error::InvalidEncoding("invalid base64 in salt"))?;
252 let tag = base64_decode_no_pad(parts[5]).map_err(|_| Argon2Error::InvalidEncoding("invalid base64 in hash"))?;
253
254 let params = Params {
255 iterations,
256 memory,
257 parallelism,
258 };
259
260 Ok((params, salt, tag))
261}
262
263#[derive(Clone)]
272#[repr(align(64))]
273struct Block {
274 v: [u64; 128],
275}
276
277impl Block {
278 #[inline(always)]
279 const fn zero() -> Self {
280 Block {
281 v: [0u64; 128],
282 }
283 }
284
285 #[inline(always)]
286 fn xor_with(&mut self, other: &Block) {
287 for (dest, source) in self.v.iter_mut().zip(other.v.iter()) {
288 *dest ^= *source;
289 }
290 }
291
292 #[inline(always)]
294 fn from_bytes(bytes: &[u8; BLOCK_SIZE]) -> Self {
295 let mut v = [0u64; 128];
296 for (word, chunk) in v.iter_mut().zip(bytes.as_chunks::<8>().0) {
297 *word = u64::from_le_bytes(*chunk);
298 }
299 Block {
300 v,
301 }
302 }
303
304 #[inline(always)]
306 fn to_bytes(&self) -> [u8; BLOCK_SIZE] {
307 let mut out = [0u8; BLOCK_SIZE];
308 for (chunk, word) in out.as_chunks_mut::<8>().0.iter_mut().zip(self.v.iter()) {
309 *chunk = word.to_le_bytes();
310 }
311 out
312 }
313}
314
315#[cfg(feature = "alloc")]
317fn argon2_core(
318 argon_type: u32,
319 password: &[u8],
320 salt: &[u8],
321 secret: &[u8],
322 ad: &[u8],
323 params: &Params,
324 out: &mut [u8],
325) -> Result<(), Argon2Error> {
326 argon2_core_with_backend(argon_type, password, salt, secret, ad, params, out, detect_backend())
327}
328
329#[cfg(feature = "alloc")]
332#[allow(clippy::too_many_arguments)]
333fn argon2_core_with_backend(
334 argon_type: u32,
335 password: &[u8],
336 salt: &[u8],
337 secret: &[u8],
338 ad: &[u8],
339 params: &Params,
340 out: &mut [u8],
341 backend: Backend,
342) -> Result<(), Argon2Error> {
343 if params.iterations < 1 {
345 return Err(Argon2Error::InvalidParams("iterations must be >= 1"));
346 }
347 if params.parallelism < 1 {
348 return Err(Argon2Error::InvalidParams("parallelism must be >= 1"));
349 }
350 if out.len() < 4 {
351 return Err(Argon2Error::InvalidParams("output length must be >= 4"));
352 }
353 if params.memory < 8 * params.parallelism {
354 return Err(Argon2Error::InvalidParams("memory must be >= 8*parallelism"));
355 }
356
357 let p = params.parallelism;
358 let t = params.iterations;
359 let m = params.memory;
360 let tag_length = out.len() as u32;
361
362 let h0 = compute_h0(argon_type, password, salt, secret, ad, p, tag_length, m, t);
364
365 let m_prime = 4 * p * (m / (4 * p));
367 let q = m_prime / p; let mem = Memory::uninit(m_prime as usize);
373
374 for i in 0..p {
376 let mut input = [0u8; 72];
377 input[..64].copy_from_slice(&h0);
378 input[68..72].copy_from_slice(&i.to_le_bytes());
379
380 let mut block_bytes = [0u8; BLOCK_SIZE];
381
382 input[64..68].copy_from_slice(&0u32.to_le_bytes());
384 variable_length_hash_into(&input, &mut block_bytes);
385 mem.write((i * q) as usize, Block::from_bytes(&block_bytes));
386
387 input[64..68].copy_from_slice(&1u32.to_le_bytes());
389 variable_length_hash_into(&input, &mut block_bytes);
390 mem.write((i * q + 1) as usize, Block::from_bytes(&block_bytes));
391 }
392
393 fill_memory(backend, &mem, argon_type, p, q, t, m_prime);
395
396 let mut final_block = mem.get((q - 1) as usize).clone();
398 for i in 1..p {
399 let idx = (i * q + q - 1) as usize;
400 final_block.xor_with(mem.get(idx));
401 }
402
403 let final_bytes = final_block.to_bytes();
405 variable_length_hash_into(&final_bytes, out);
406
407 Ok(())
408}
409
410#[cfg(feature = "alloc")]
419fn fill_memory(backend: Backend, memory: &Memory, argon_type: u32, p: u32, q: u32, t: u32, m_prime: u32) {
420 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
421 {
422 const MIN_PARALLEL_SEGMENT_LENGTH: u32 = 64;
424 let segment_length = q / SYNC_POINTS;
425 if p > 1 && segment_length >= MIN_PARALLEL_SEGMENT_LENGTH {
426 let barrier = std::sync::Barrier::new(p as usize);
429 std::thread::scope(|scope| {
430 let barrier = &barrier;
431 for lane in 0..p {
432 scope.spawn(move || {
433 for pass in 0..t {
434 for slice in 0..SYNC_POINTS {
435 fill_segment(backend, memory, argon_type, pass, lane, slice, p, q, t, m_prime);
436 barrier.wait();
437 }
438 }
439 });
440 }
441 });
442 return;
443 }
444 }
445
446 for pass in 0..t {
447 for slice in 0..SYNC_POINTS {
448 for lane in 0..p {
449 fill_segment(backend, memory, argon_type, pass, lane, slice, p, q, t, m_prime);
450 }
451 }
452 }
453}
454
455#[derive(Clone, Copy, PartialEq, Eq, Debug)]
460#[allow(dead_code)] enum Backend {
462 Scalar,
464 #[cfg(target_arch = "aarch64")]
466 Neon,
467 #[cfg(target_arch = "aarch64")]
470 NeonSha3,
471 #[cfg(target_arch = "x86_64")]
473 Avx2,
474}
475
476#[allow(clippy::needless_return)]
482fn detect_backend() -> Backend {
483 #[cfg(target_arch = "aarch64")]
484 {
485 #[cfg(feature = "std")]
486 {
487 if std::arch::is_aarch64_feature_detected!("sha3") {
488 return Backend::NeonSha3;
489 }
490 return Backend::Neon;
491 }
492
493 #[cfg(all(not(feature = "std"), target_feature = "sha3"))]
494 return Backend::NeonSha3;
495
496 #[cfg(all(not(feature = "std"), not(target_feature = "sha3")))]
497 return Backend::Neon;
498 }
499
500 #[cfg(target_arch = "x86_64")]
501 {
502 #[cfg(feature = "std")]
503 {
504 if std::arch::is_x86_feature_detected!("avx2") {
505 return Backend::Avx2;
506 }
507 return Backend::Scalar;
508 }
509
510 #[cfg(all(not(feature = "std"), target_feature = "avx2"))]
511 return Backend::Avx2;
512
513 #[cfg(all(not(feature = "std"), not(target_feature = "avx2")))]
514 return Backend::Scalar;
515 }
516
517 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
518 return Backend::Scalar;
519}
520
521#[cfg(feature = "alloc")]
529#[allow(clippy::too_many_arguments)]
530fn fill_segment(
531 backend: Backend,
532 memory: &Memory,
533 argon_type: u32,
534 pass: u32,
535 lane: u32,
536 slice: u32,
537 lanes: u32,
538 q: u32, t: u32, m_prime: u32, ) {
542 let segment_length = q / SYNC_POINTS;
543
544 let need_pseudo_rands = argon_type == ARGON2I || (argon_type == ARGON2ID && pass == 0 && slice < 2);
548 let mut addr_words = [0u64; 128];
549 let mut addr_chunk = u32::MAX;
550
551 let start_index = if pass == 0 && slice == 0 { 2 } else { 0 };
552
553 for s in start_index..segment_length {
554 let j = slice * segment_length + s; let cur_index = (lane * q + j) as usize;
556
557 let prev_index = if j == 0 {
559 (lane * q + q - 1) as usize
560 } else {
561 (lane * q + j - 1) as usize
562 };
563
564 let (j1, j2) = if need_pseudo_rands {
566 let chunk = s / 128;
567 if chunk != addr_chunk {
568 generate_address_block(pass, lane, slice, t, argon_type, m_prime, (chunk + 1) as u64, &mut addr_words);
569 addr_chunk = chunk;
570 }
571 let val = addr_words[(s % 128) as usize];
572 ((val & 0xFFFFFFFF) as u32, (val >> 32) as u32)
573 } else {
574 let word = memory.first_word(prev_index);
576 (word as u32, (word >> 32) as u32)
577 };
578
579 let ref_lane = if pass == 0 && slice == 0 { lane } else { j2 % lanes };
581
582 let ref_index = index_alpha(pass, slice, lanes, segment_length, s, q, ref_lane == lane, j1);
583 let ref_block_index = (ref_lane * q + ref_index) as usize;
584
585 unsafe {
594 let prev = memory.get(prev_index);
595 let reference = memory.get(ref_block_index);
596 let cur = memory.get_mut(cur_index);
597 match backend {
598 Backend::Scalar => fill::fill_block(prev, reference, cur, pass != 0),
599 #[cfg(target_arch = "aarch64")]
600 Backend::Neon => fill_neon::fill_block(prev, reference, cur, pass != 0),
601 #[cfg(target_arch = "aarch64")]
602 Backend::NeonSha3 => fill_neon::fill_block_sha3(prev, reference, cur, pass != 0),
603 #[cfg(target_arch = "x86_64")]
604 Backend::Avx2 => fill_avx2::fill_block(prev, reference, cur, pass != 0),
605 }
606 }
607 }
608}
609
610#[cfg(feature = "alloc")]
613fn generate_address_block(
614 pass: u32,
615 lane: u32,
616 slice: u32,
617 t: u32,
618 argon_type: u32,
619 m_prime: u32,
620 counter: u64,
621 out: &mut [u64; 128],
622) {
623 let mut input = Block::zero();
625 input.v[0] = pass as u64;
626 input.v[1] = lane as u64;
627 input.v[2] = slice as u64;
628 input.v[3] = m_prime as u64;
629 input.v[4] = t as u64;
630 input.v[5] = argon_type as u64;
631 input.v[6] = counter;
632
633 let zero_block = Block::zero();
634 let mut tmp = Block::zero();
635 fill::fill_block_ref(&zero_block, &input, &mut tmp, false);
636 let mut addr_block = Block::zero();
637 fill::fill_block_ref(&zero_block, &tmp, &mut addr_block, false);
638 out.copy_from_slice(&addr_block.v);
639}
640
641fn index_alpha(
643 pass: u32,
644 slice: u32,
645 _lanes: u32,
646 segment_length: u32,
647 index_in_segment: u32,
648 q: u32,
649 same_lane: bool,
650 j1: u32,
651) -> u32 {
652 let reference_area_size = if pass == 0 {
654 if slice == 0 {
656 index_in_segment.saturating_sub(1)
658 } else {
659 if same_lane {
660 slice * segment_length + index_in_segment - 1
661 } else {
662 slice * segment_length - if index_in_segment == 0 { 1 } else { 0 }
663 }
664 }
665 } else {
666 if same_lane {
668 q - segment_length + index_in_segment - 1
669 } else {
670 q - segment_length - if index_in_segment == 0 { 1 } else { 0 }
671 }
672 };
673
674 if reference_area_size == 0 {
675 return 0;
676 }
677
678 let j1_64 = j1 as u64;
680 let x = (j1_64 * j1_64) >> 32;
681 let y = (reference_area_size as u64 * x) >> 32;
682 let relative_position = (reference_area_size as u64 - 1 - y) as u32;
683
684 let start_position = if pass == 0 {
686 0
687 } else {
688 if slice == SYNC_POINTS - 1 {
689 0
690 } else {
691 (slice + 1) * segment_length
692 }
693 };
694
695 (start_position + relative_position) % q
696}
697
698#[cfg(feature = "alloc")]
700fn compute_h0(
701 argon_type: u32,
702 password: &[u8],
703 salt: &[u8],
704 secret: &[u8],
705 ad: &[u8],
706 p: u32,
707 tag_length: u32,
708 m: u32,
709 t: u32,
710) -> [u8; 64] {
711 let mut blake = Blake2b::new_keyed(&[], 64);
712
713 blake.update(&p.to_le_bytes());
714 blake.update(&tag_length.to_le_bytes());
715 blake.update(&m.to_le_bytes());
716 blake.update(&t.to_le_bytes());
717 blake.update(&VERSION.to_le_bytes());
718 blake.update(&argon_type.to_le_bytes());
719 blake.update(&(password.len() as u32).to_le_bytes());
720 blake.update(password);
721 blake.update(&(salt.len() as u32).to_le_bytes());
722 blake.update(salt);
723 blake.update(&(secret.len() as u32).to_le_bytes());
724 blake.update(secret);
725 blake.update(&(ad.len() as u32).to_le_bytes());
726 blake.update(ad);
727
728 let hash = blake.sum();
729 let mut result = [0u8; 64];
730 result.copy_from_slice(&hash.as_ref()[..64]);
731 result
732}
733
734#[cfg(feature = "alloc")]
739fn variable_length_hash_into(input: &[u8], out: &mut [u8]) {
740 let tag_length = out.len();
741
742 if tag_length <= 64 {
743 let mut blake = Blake2b::new_keyed(&[], tag_length);
745 blake.update(&(tag_length as u32).to_le_bytes());
746 blake.update(input);
747 let hash = blake.sum();
748 out.copy_from_slice(&hash.as_ref()[..tag_length]);
749 return;
750 }
751
752 let r = tag_length.div_ceil(32) - 2;
755
756 let mut v = [0u8; 64];
758 {
759 let mut blake = Blake2b::new_keyed(&[], 64);
760 blake.update(&(tag_length as u32).to_le_bytes());
761 blake.update(input);
762 let hash = blake.sum();
763 v.copy_from_slice(&hash.as_ref()[..64]);
764 }
765
766 out[..32].copy_from_slice(&v[..32]);
768
769 let mut offset = 32;
771 for _ in 2..=r {
772 let mut blake = Blake2b::new_keyed(&[], 64);
773 blake.update(&v);
774 let hash = blake.sum();
775 v.copy_from_slice(&hash.as_ref()[..64]);
776 out[offset..offset + 32].copy_from_slice(&v[..32]);
777 offset += 32;
778 }
779
780 let remaining = tag_length - 32 * r;
782 let mut blake = Blake2b::new_keyed(&[], remaining);
783 blake.update(&v);
784 let hash = blake.sum();
785 out[offset..offset + remaining].copy_from_slice(&hash.as_ref()[..remaining]);
786}
787
788struct Memory {
806 blocks: Vec<MaybeUninit<Block>>,
807}
808
809unsafe impl Send for Memory {}
814unsafe impl Sync for Memory {}
815
816impl Memory {
817 fn uninit(len: usize) -> Self {
823 let mut blocks: Vec<MaybeUninit<Block>> = Vec::with_capacity(len);
824 unsafe { blocks.set_len(len) };
828 Memory {
829 blocks,
830 }
831 }
832
833 #[inline(always)]
835 fn base_ptr(&self) -> *mut Block {
836 self.blocks.as_ptr() as *mut Block
837 }
838
839 #[inline(always)]
841 fn len(&self) -> usize {
842 self.blocks.len()
843 }
844
845 #[inline(always)]
850 fn write(&self, index: usize, block: Block) {
851 debug_assert!(index < self.len());
852 unsafe { core::ptr::write(self.base_ptr().add(index), block) };
855 }
856
857 #[inline(always)]
862 fn get(&self, index: usize) -> &Block {
863 debug_assert!(index < self.len());
864 unsafe { &*self.base_ptr().add(index) }
867 }
868
869 #[inline(always)]
871 fn first_word(&self, index: usize) -> u64 {
872 self.get(index).v[0]
873 }
874
875 #[inline(always)]
880 fn get_mut(&self, index: usize) -> *mut Block {
881 debug_assert!(index < self.len());
882 unsafe { self.base_ptr().add(index) }
885 }
886}
887
888#[cfg(feature = "alloc")]
893fn base64_encode_no_pad(input: &[u8]) -> String {
894 base64::encode(input, base64::Alphabet::StandardNoPadding)
895}
896
897#[cfg(feature = "alloc")]
898fn base64_decode_no_pad(input: &str) -> Result<Vec<u8>, ()> {
899 base64::decode(input.as_bytes(), base64::Alphabet::StandardNoPadding).map_err(|_| ())
900}
901
902#[cfg(feature = "alloc")]
903fn parse_param(s: &str, prefix: &str) -> Result<u32, Argon2Error> {
904 if !s.starts_with(prefix) {
905 return Err(Argon2Error::InvalidEncoding("invalid parameter prefix"));
906 }
907 s[prefix.len()..]
908 .parse::<u32>()
909 .map_err(|_| Argon2Error::InvalidEncoding("invalid parameter value"))
910}
911
912#[cfg(test)]
917mod tests {
918 use super::*;
919
920 fn derive_key_typed(
921 argon_type: u32,
922 password: &[u8],
923 salt: &[u8],
924 secret: &[u8],
925 ad: &[u8],
926 iterations: u32,
927 memory: u32,
928 parallelism: u32,
929 tag_length: u32,
930 ) -> Vec<u8> {
931 let params = Params {
932 iterations: iterations,
933 memory: memory,
934 parallelism: parallelism,
935 };
936 let mut out = vec![0u8; tag_length as usize];
937 argon2_core(argon_type, password, salt, secret, ad, ¶ms, &mut out).unwrap();
938 out
939 }
940
941 #[allow(clippy::too_many_arguments)]
943 fn derive_key_typed_backend(
944 backend: Backend,
945 argon_type: u32,
946 password: &[u8],
947 salt: &[u8],
948 secret: &[u8],
949 ad: &[u8],
950 iterations: u32,
951 memory: u32,
952 parallelism: u32,
953 tag_length: u32,
954 ) -> Vec<u8> {
955 let params = Params {
956 iterations,
957 memory,
958 parallelism,
959 };
960 let mut out = vec![0u8; tag_length as usize];
961 argon2_core_with_backend(argon_type, password, salt, secret, ad, ¶ms, &mut out, backend).unwrap();
962 out
963 }
964
965 fn available_backends() -> Vec<Backend> {
968 let mut backends = vec![Backend::Scalar];
969
970 #[cfg(target_arch = "aarch64")]
971 {
972 backends.push(Backend::Neon);
973 #[cfg(feature = "std")]
974 if std::arch::is_aarch64_feature_detected!("sha3") {
975 backends.push(Backend::NeonSha3);
976 }
977 }
978
979 #[cfg(target_arch = "x86_64")]
980 {
981 #[cfg(feature = "std")]
982 if std::arch::is_x86_feature_detected!("avx2") {
983 backends.push(Backend::Avx2);
984 }
985 }
986
987 backends
988 }
989
990 #[test]
993 fn test_backends_match_reference() {
994 let password = b"password";
995 let salt = b"somesalt";
996 for backend in available_backends() {
997 for v in GO_VECTORS.iter() {
998 let expected = hex::decode(v.hash).unwrap();
999 let result = derive_key_typed_backend(
1000 backend,
1001 v.mode,
1002 password,
1003 salt,
1004 &[],
1005 &[],
1006 v.time,
1007 v.memory,
1008 v.threads,
1009 expected.len() as u32,
1010 );
1011 assert_eq!(
1012 result, expected,
1013 "backend {:?} failed Go vector (mode={}, t={}, m={}, p={})",
1014 backend, v.mode, v.time, v.memory, v.threads
1015 );
1016 }
1017 }
1018 }
1019
1020 fn derive_key_vec(
1022 tag_length: usize,
1023 password: &[u8],
1024 salt: &[u8],
1025 secret: &[u8],
1026 ad: &[u8],
1027 params: &Params,
1028 ) -> Vec<u8> {
1029 let mut out = vec![0u8; tag_length];
1030 derive_key(&mut out, password, salt, secret, ad, params).unwrap();
1031 out
1032 }
1033
1034 fn variable_length_hash_vec(input: &[u8], tag_length: usize) -> Vec<u8> {
1036 let mut out = vec![0u8; tag_length];
1037 variable_length_hash_into(input, &mut out);
1038 out
1039 }
1040
1041 #[test]
1048 fn test_rfc9106_argon2d() {
1049 let pwd = vec![0x01u8; 32];
1050 let salt = vec![0x02u8; 16];
1051 let secret = vec![0x03u8; 8];
1052 let ad = vec![0x04u8; 12];
1053 let expected = hex::decode("512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb").unwrap();
1054 let result = derive_key_typed(ARGON2D, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
1055 assert_eq!(result, expected);
1056 }
1057
1058 #[test]
1059 fn test_rfc9106_argon2i() {
1060 let pwd = vec![0x01u8; 32];
1061 let salt = vec![0x02u8; 16];
1062 let secret = vec![0x03u8; 8];
1063 let ad = vec![0x04u8; 12];
1064 let expected = hex::decode("c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8").unwrap();
1065 let result = derive_key_typed(ARGON2I, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
1066 assert_eq!(result, expected);
1067 }
1068
1069 #[test]
1076 fn test_h0() {
1077 let pwd = vec![0x01u8; 32];
1078 let salt = vec![0x02u8; 16];
1079 let secret = vec![0x03u8; 8];
1080 let ad = vec![0x04u8; 12];
1081 let h0 = compute_h0(ARGON2ID, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1082 let expected = "2889de487eb42ae500c0007ed9252f1069eadec40d5765b485de6dc2437a67b8546a2f0acc1a0882db8fcf74714b472e94df421a5da1112ffa11434370a1e997";
1083 assert_eq!(hex::encode(h0), expected);
1084 }
1085
1086 struct Vec3 {
1092 mode: u32,
1093 time: u32,
1094 memory: u32,
1095 threads: u32,
1096 hash: &'static str,
1097 }
1098
1099 const GO_VECTORS: &[Vec3] = &[
1100 Vec3 {
1101 mode: ARGON2I,
1102 time: 1,
1103 memory: 64,
1104 threads: 1,
1105 hash: "b9c401d1844a67d50eae3967dc28870b22e508092e861a37",
1106 },
1107 Vec3 {
1108 mode: ARGON2D,
1109 time: 1,
1110 memory: 64,
1111 threads: 1,
1112 hash: "8727405fd07c32c78d64f547f24150d3f2e703a89f981a19",
1113 },
1114 Vec3 {
1115 mode: ARGON2ID,
1116 time: 1,
1117 memory: 64,
1118 threads: 1,
1119 hash: "655ad15eac652dc59f7170a7332bf49b8469be1fdb9c28bb",
1120 },
1121 Vec3 {
1122 mode: ARGON2I,
1123 time: 2,
1124 memory: 64,
1125 threads: 1,
1126 hash: "8cf3d8f76a6617afe35fac48eb0b7433a9a670ca4a07ed64",
1127 },
1128 Vec3 {
1129 mode: ARGON2D,
1130 time: 2,
1131 memory: 64,
1132 threads: 1,
1133 hash: "3be9ec79a69b75d3752acb59a1fbb8b295a46529c48fbb75",
1134 },
1135 Vec3 {
1136 mode: ARGON2ID,
1137 time: 2,
1138 memory: 64,
1139 threads: 1,
1140 hash: "068d62b26455936aa6ebe60060b0a65870dbfa3ddf8d41f7",
1141 },
1142 Vec3 {
1143 mode: ARGON2I,
1144 time: 2,
1145 memory: 64,
1146 threads: 2,
1147 hash: "2089f3e78a799720f80af806553128f29b132cafe40d059f",
1148 },
1149 Vec3 {
1150 mode: ARGON2D,
1151 time: 2,
1152 memory: 64,
1153 threads: 2,
1154 hash: "68e2462c98b8bc6bb60ec68db418ae2c9ed24fc6748a40e9",
1155 },
1156 Vec3 {
1157 mode: ARGON2ID,
1158 time: 2,
1159 memory: 64,
1160 threads: 2,
1161 hash: "350ac37222f436ccb5c0972f1ebd3bf6b958bf2071841362",
1162 },
1163 Vec3 {
1164 mode: ARGON2I,
1165 time: 3,
1166 memory: 256,
1167 threads: 2,
1168 hash: "f5bbf5d4c3836af13193053155b73ec7476a6a2eb93fd5e6",
1169 },
1170 Vec3 {
1171 mode: ARGON2D,
1172 time: 3,
1173 memory: 256,
1174 threads: 2,
1175 hash: "f4f0669218eaf3641f39cc97efb915721102f4b128211ef2",
1176 },
1177 Vec3 {
1178 mode: ARGON2ID,
1179 time: 3,
1180 memory: 256,
1181 threads: 2,
1182 hash: "4668d30ac4187e6878eedeacf0fd83c5a0a30db2cc16ef0b",
1183 },
1184 Vec3 {
1185 mode: ARGON2I,
1186 time: 4,
1187 memory: 4096,
1188 threads: 4,
1189 hash: "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7",
1190 },
1191 Vec3 {
1192 mode: ARGON2D,
1193 time: 4,
1194 memory: 4096,
1195 threads: 4,
1196 hash: "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d",
1197 },
1198 Vec3 {
1199 mode: ARGON2ID,
1200 time: 4,
1201 memory: 4096,
1202 threads: 4,
1203 hash: "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a",
1204 },
1205 Vec3 {
1206 mode: ARGON2I,
1207 time: 4,
1208 memory: 1024,
1209 threads: 8,
1210 hash: "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17",
1211 },
1212 Vec3 {
1213 mode: ARGON2D,
1214 time: 4,
1215 memory: 1024,
1216 threads: 8,
1217 hash: "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9",
1218 },
1219 Vec3 {
1220 mode: ARGON2ID,
1221 time: 4,
1222 memory: 1024,
1223 threads: 8,
1224 hash: "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f",
1225 },
1226 Vec3 {
1227 mode: ARGON2I,
1228 time: 2,
1229 memory: 64,
1230 threads: 3,
1231 hash: "5cab452fe6b8479c8661def8cd703b611a3905a6d5477fe6",
1232 },
1233 Vec3 {
1234 mode: ARGON2D,
1235 time: 2,
1236 memory: 64,
1237 threads: 3,
1238 hash: "22474a423bda2ccd36ec9afd5119e5c8949798cadf659f51",
1239 },
1240 Vec3 {
1241 mode: ARGON2ID,
1242 time: 2,
1243 memory: 64,
1244 threads: 3,
1245 hash: "4a15b31aec7c2590b87d1f520be7d96f56658172deaa3079",
1246 },
1247 Vec3 {
1248 mode: ARGON2I,
1249 time: 3,
1250 memory: 1024,
1251 threads: 6,
1252 hash: "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced",
1253 },
1254 Vec3 {
1255 mode: ARGON2D,
1256 time: 3,
1257 memory: 1024,
1258 threads: 6,
1259 hash: "a3351b0319a53229152023d9206902f4ef59661cdca89481",
1260 },
1261 Vec3 {
1262 mode: ARGON2ID,
1263 time: 3,
1264 memory: 1024,
1265 threads: 6,
1266 hash: "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016",
1267 },
1268 ];
1269
1270 #[test]
1271 fn test_go_vectors() {
1272 let password = b"password";
1273 let salt = b"somesalt";
1274 for (i, v) in GO_VECTORS.iter().enumerate() {
1275 let expected = hex::decode(v.hash).unwrap();
1276 let result = derive_key_typed(
1277 v.mode,
1278 password,
1279 salt,
1280 &[],
1281 &[],
1282 v.time,
1283 v.memory,
1284 v.threads,
1285 expected.len() as u32,
1286 );
1287 assert_eq!(
1288 result, expected,
1289 "Go vector {} failed (mode={}, t={}, m={}, p={})",
1290 i, v.mode, v.time, v.memory, v.threads
1291 );
1292 }
1293 }
1294
1295 struct CVector {
1302 mode: u32,
1303 time: u32,
1304 memory: u32,
1305 threads: u32,
1306 hash: &'static str,
1307 pwd: &'static str,
1308 slt: &'static str,
1309 }
1310
1311 const C_VECTORS: &[CVector] = &[
1312 CVector {
1313 mode: ARGON2I,
1314 time: 2,
1315 memory: 65536,
1316 threads: 1,
1317 hash: "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
1318 pwd: "password",
1319 slt: "somesalt",
1320 },
1321 CVector {
1322 mode: ARGON2I,
1323 time: 2,
1324 memory: 262144,
1325 threads: 1,
1326 hash: "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb",
1327 pwd: "password",
1328 slt: "somesalt",
1329 },
1330 CVector {
1331 mode: ARGON2I,
1332 time: 2,
1333 memory: 256,
1334 threads: 1,
1335 hash: "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f",
1336 pwd: "password",
1337 slt: "somesalt",
1338 },
1339 CVector {
1340 mode: ARGON2I,
1341 time: 2,
1342 memory: 256,
1343 threads: 2,
1344 hash: "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61",
1345 pwd: "password",
1346 slt: "somesalt",
1347 },
1348 CVector {
1349 mode: ARGON2I,
1350 time: 1,
1351 memory: 65536,
1352 threads: 1,
1353 hash: "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf",
1354 pwd: "password",
1355 slt: "somesalt",
1356 },
1357 CVector {
1358 mode: ARGON2I,
1359 time: 4,
1360 memory: 65536,
1361 threads: 1,
1362 hash: "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b",
1363 pwd: "password",
1364 slt: "somesalt",
1365 },
1366 CVector {
1367 mode: ARGON2I,
1368 time: 2,
1369 memory: 65536,
1370 threads: 1,
1371 hash: "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee",
1372 pwd: "differentpassword",
1373 slt: "somesalt",
1374 },
1375 CVector {
1376 mode: ARGON2I,
1377 time: 2,
1378 memory: 65536,
1379 threads: 1,
1380 hash: "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271",
1381 pwd: "password",
1382 slt: "diffsalt",
1383 },
1384 CVector {
1385 mode: ARGON2ID,
1386 time: 2,
1387 memory: 65536,
1388 threads: 1,
1389 hash: "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7",
1390 pwd: "password",
1391 slt: "somesalt",
1392 },
1393 CVector {
1394 mode: ARGON2ID,
1395 time: 2,
1396 memory: 262144,
1397 threads: 1,
1398 hash: "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c",
1399 pwd: "password",
1400 slt: "somesalt",
1401 },
1402 CVector {
1403 mode: ARGON2ID,
1404 time: 2,
1405 memory: 256,
1406 threads: 1,
1407 hash: "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe",
1408 pwd: "password",
1409 slt: "somesalt",
1410 },
1411 CVector {
1412 mode: ARGON2ID,
1413 time: 2,
1414 memory: 256,
1415 threads: 2,
1416 hash: "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037",
1417 pwd: "password",
1418 slt: "somesalt",
1419 },
1420 CVector {
1421 mode: ARGON2ID,
1422 time: 1,
1423 memory: 65536,
1424 threads: 1,
1425 hash: "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98",
1426 pwd: "password",
1427 slt: "somesalt",
1428 },
1429 CVector {
1430 mode: ARGON2ID,
1431 time: 4,
1432 memory: 65536,
1433 threads: 1,
1434 hash: "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c",
1435 pwd: "password",
1436 slt: "somesalt",
1437 },
1438 CVector {
1439 mode: ARGON2ID,
1440 time: 2,
1441 memory: 65536,
1442 threads: 1,
1443 hash: "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde",
1444 pwd: "differentpassword",
1445 slt: "somesalt",
1446 },
1447 CVector {
1448 mode: ARGON2ID,
1449 time: 2,
1450 memory: 65536,
1451 threads: 1,
1452 hash: "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c",
1453 pwd: "password",
1454 slt: "diffsalt",
1455 },
1456 ];
1457
1458 #[test]
1459 fn test_c_reference_vectors() {
1460 for (i, v) in C_VECTORS.iter().enumerate() {
1461 let expected = hex::decode(v.hash).unwrap();
1462 let result = derive_key_typed(
1463 v.mode,
1464 v.pwd.as_bytes(),
1465 v.slt.as_bytes(),
1466 &[],
1467 &[],
1468 v.time,
1469 v.memory,
1470 v.threads,
1471 expected.len() as u32,
1472 );
1473 assert_eq!(
1474 result, expected,
1475 "C ref vector {} failed (mode={}, t={}, m={}, p={})",
1476 i, v.mode, v.time, v.memory, v.threads
1477 );
1478 }
1479 }
1480
1481 #[test]
1486 fn test_phc_encode_decode() {
1487 let params = Params {
1488 iterations: 3,
1489 memory: 65536,
1490 parallelism: 4,
1491 };
1492 let salt = b"somesalt12345678";
1493 let tag = vec![0xAB; 32];
1494 let encoded = encode_phc(¶ms, salt, &tag);
1495 assert!(encoded.starts_with("$argon2id$v=19$m=65536,t=3,p=4$"));
1496 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
1497 assert_eq!(dp.iterations, 3);
1498 assert_eq!(dp.memory, 65536);
1499 assert_eq!(dp.parallelism, 4);
1500 assert_eq!(ds, salt);
1501 assert_eq!(dt, tag);
1502 }
1503
1504 #[test]
1505 fn test_hash_and_verify() {
1506 let password = b"correct horse battery staple";
1507 let salt = b"randomsalt123456";
1508 let params = Params {
1509 iterations: 1,
1510 memory: 64,
1511 parallelism: 1,
1512 };
1513 let encoded = hash_password(password, salt, ¶ms).unwrap();
1514 assert!(verify_password(password, &encoded).is_ok());
1515 assert_eq!(verify_password(b"wrong password", &encoded), Err(Argon2Error::VerifyMismatch));
1516 }
1517
1518 #[test]
1519 fn test_decode_phc_invalid() {
1520 assert!(decode_phc("").is_err());
1521 assert!(decode_phc("$argon2i$v=19$m=4096,t=3,p=1$salt$hash").is_err());
1522 assert!(decode_phc("$argon2id$v=16$m=4096,t=3,p=1$salt$hash").is_err());
1523 assert!(decode_phc("not a phc string").is_err());
1524 }
1525
1526 #[test]
1527 fn test_invalid_params() {
1528 let mut out = [0u8; 32];
1529 assert!(
1530 derive_key(
1531 &mut out,
1532 b"password",
1533 b"salt",
1534 &[],
1535 &[],
1536 &Params {
1537 iterations: 0,
1538 memory: 64,
1539 parallelism: 1
1540 }
1541 )
1542 .is_err()
1543 );
1544 assert!(
1545 derive_key(
1546 &mut out,
1547 b"password",
1548 b"salt",
1549 &[],
1550 &[],
1551 &Params {
1552 iterations: 1,
1553 memory: 4,
1554 parallelism: 1
1555 }
1556 )
1557 .is_err()
1558 );
1559 let mut short = [0u8; 3];
1561 assert!(
1562 derive_key(
1563 &mut short,
1564 b"password",
1565 b"salt",
1566 &[],
1567 &[],
1568 &Params {
1569 iterations: 1,
1570 memory: 64,
1571 parallelism: 1
1572 }
1573 )
1574 .is_err()
1575 );
1576 }
1577
1578 #[test]
1579 fn test_variable_length_hash_short() {
1580 let input = b"test input";
1581 let r32 = variable_length_hash_vec(input, 32);
1582 assert_eq!(r32.len(), 32);
1583 assert_eq!(variable_length_hash_vec(input, 32), r32);
1584 let r48 = variable_length_hash_vec(input, 48);
1585 assert_eq!(r48.len(), 48);
1586 assert_ne!(&r32[..], &r48[..32]);
1587 }
1588
1589 #[test]
1590 fn test_variable_length_hash_long() {
1591 assert_eq!(variable_length_hash_vec(b"test input for long hash", 128).len(), 128);
1592 assert_eq!(variable_length_hash_vec(b"test input for long hash", 1024).len(), 1024);
1593 }
1594
1595 #[test]
1596 fn test_argon2id_min_memory() {
1597 let result = derive_key_vec(
1598 32,
1599 b"password",
1600 b"saltsalt",
1601 &[],
1602 &[],
1603 &Params {
1604 iterations: 1,
1605 memory: 8,
1606 parallelism: 1,
1607 },
1608 );
1609 assert_eq!(result.len(), 32);
1610 }
1611
1612 #[test]
1613 fn test_argon2id_multiple_lanes() {
1614 let result = derive_key_vec(
1615 32,
1616 b"password",
1617 b"saltsaltsaltsalt",
1618 &[],
1619 &[],
1620 &Params {
1621 iterations: 1,
1622 memory: 64,
1623 parallelism: 4,
1624 },
1625 );
1626 assert_eq!(result.len(), 32);
1627 }
1628
1629 #[test]
1630 fn test_different_passwords() {
1631 let p = Params {
1632 iterations: 1,
1633 memory: 64,
1634 parallelism: 1,
1635 };
1636 assert_ne!(
1637 derive_key_vec(32, b"password1", b"saltsaltsaltsalt", &[], &[], &p),
1638 derive_key_vec(32, b"password2", b"saltsaltsaltsalt", &[], &[], &p)
1639 );
1640 }
1641
1642 #[test]
1643 fn test_different_salts() {
1644 let p = Params {
1645 iterations: 1,
1646 memory: 64,
1647 parallelism: 1,
1648 };
1649 assert_ne!(
1650 derive_key_vec(32, b"password", b"salt1234salt1234", &[], &[], &p),
1651 derive_key_vec(32, b"password", b"salt5678salt5678", &[], &[], &p)
1652 );
1653 }
1654
1655 #[test]
1656 fn test_long_tag() {
1657 let result = derive_key_vec(
1658 64,
1659 b"password",
1660 b"saltsaltsaltsalt",
1661 &[],
1662 &[],
1663 &Params {
1664 iterations: 1,
1665 memory: 64,
1666 parallelism: 1,
1667 },
1668 );
1669 assert_eq!(result.len(), 64);
1670 }
1671
1672 #[test]
1673 fn test_phc_roundtrip() {
1674 let password = b"password";
1675 let salt = b"somesalt";
1676 let params = Params {
1677 iterations: 1,
1678 memory: 64,
1679 parallelism: 1,
1680 };
1681 let tag = derive_key_vec(24, password, salt, &[], &[], ¶ms);
1682 let encoded = encode_phc(¶ms, salt, &tag);
1683 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
1684 assert_eq!(dp.memory, params.memory);
1685 assert_eq!(dp.iterations, params.iterations);
1686 assert_eq!(dp.parallelism, params.parallelism);
1687 assert_eq!(ds, salt);
1688 assert_eq!(dt, tag);
1689 }
1690
1691 fn argon2_core_with_passes(
1699 argon_type: u32,
1700 password: &[u8],
1701 salt: &[u8],
1702 secret: &[u8],
1703 ad: &[u8],
1704 params: &Params,
1705 ) -> Vec<Vec<Block>> {
1706 let p = params.parallelism;
1707 let t = params.iterations;
1708 let m = params.memory;
1709 let tag_length = 32u32;
1710
1711 let h0 = compute_h0(argon_type, password, salt, secret, ad, p, tag_length, m, t);
1712 let m_prime = 4 * p * (m / (4 * p));
1713 let q = m_prime / p;
1714
1715 let mem = Memory::uninit(m_prime as usize);
1716
1717 let mut input = [0u8; 72];
1718 input[..64].copy_from_slice(&h0);
1719 let mut block_bytes = [0u8; BLOCK_SIZE];
1720 for i in 0..p {
1721 input[68..72].copy_from_slice(&i.to_le_bytes());
1722
1723 input[64..68].copy_from_slice(&0u32.to_le_bytes());
1724 variable_length_hash_into(&input, &mut block_bytes);
1725 mem.write((i * q) as usize, Block::from_bytes(&block_bytes));
1726
1727 input[64..68].copy_from_slice(&1u32.to_le_bytes());
1728 variable_length_hash_into(&input, &mut block_bytes);
1729 mem.write((i * q + 1) as usize, Block::from_bytes(&block_bytes));
1730 }
1731
1732 let mut pass_snapshots = Vec::new();
1733 for pass in 0..t {
1734 for slice in 0..SYNC_POINTS {
1735 for lane in 0..p {
1736 fill_segment(Backend::Scalar, &mem, argon_type, pass, lane, slice, p, q, t, m_prime);
1737 }
1738 }
1739 pass_snapshots.push((0..mem.len()).map(|i| mem.get(i).clone()).collect());
1740 }
1741
1742 pass_snapshots
1743 }
1744
1745 fn block0_word(block: &Block, idx: usize) -> String {
1746 format!("{:016x}", block.v[idx])
1747 }
1748
1749 fn block_last_word(block: &Block, idx: usize) -> String {
1750 format!("{:016x}", block.v[idx])
1751 }
1752
1753 #[test]
1754 fn test_rfc9106_argon2d_intermediate_blocks() {
1755 let pwd = vec![0x01u8; 32];
1756 let salt = vec![0x02u8; 16];
1757 let secret = vec![0x03u8; 8];
1758 let ad = vec![0x04u8; 12];
1759 let params = Params {
1760 iterations: 3,
1761 memory: 32,
1762 parallelism: 4,
1763 };
1764 let passes = argon2_core_with_passes(ARGON2D, &pwd, &salt, &secret, &ad, ¶ms);
1765
1766 let p = params.parallelism;
1767 let q = (4 * p * (params.memory / (4 * p))) / p;
1768 let m_prime = p * q;
1769
1770 assert_eq!(block0_word(&passes[0][0], 0), "db2fea6b2c6f5c8a");
1771 assert_eq!(block_last_word(&passes[0][(m_prime - 1) as usize], 127), "6a6c49d2cb75d5b6");
1772
1773 assert_eq!(block0_word(&passes[1][0], 0), "d3801200410f8c0d");
1774 assert_eq!(block_last_word(&passes[1][(m_prime - 1) as usize], 127), "2dbfff23f31b5883");
1775
1776 assert_eq!(block0_word(&passes[2][0], 0), "5f047b575c5ff4d2");
1777 assert_eq!(block_last_word(&passes[2][(m_prime - 1) as usize], 127), "c341b3ca45c10da5");
1778 }
1779
1780 #[test]
1781 fn test_rfc9106_argon2i_intermediate_blocks() {
1782 let pwd = vec![0x01u8; 32];
1783 let salt = vec![0x02u8; 16];
1784 let secret = vec![0x03u8; 8];
1785 let ad = vec![0x04u8; 12];
1786 let params = Params {
1787 iterations: 3,
1788 memory: 32,
1789 parallelism: 4,
1790 };
1791 let passes = argon2_core_with_passes(ARGON2I, &pwd, &salt, &secret, &ad, ¶ms);
1792
1793 let p = params.parallelism;
1794 let q = (4 * p * (params.memory / (4 * p))) / p;
1795 let m_prime = p * q;
1796
1797 assert_eq!(block0_word(&passes[0][0], 0), "f8f9e84545db08f6");
1798 assert_eq!(block_last_word(&passes[0][(m_prime - 1) as usize], 127), "c570f2ab2a86cf00");
1799
1800 assert_eq!(block0_word(&passes[1][0], 0), "b2e4ddfcf76dc85a");
1801 assert_eq!(block_last_word(&passes[1][(m_prime - 1) as usize], 127), "421b3c6e9555b79d");
1802
1803 assert_eq!(block0_word(&passes[2][0], 0), "af2a8bd8482c2f11");
1804 assert_eq!(block_last_word(&passes[2][(m_prime - 1) as usize], 127), "71e436f035f30ed0");
1805 }
1806
1807 #[test]
1812 fn test_h0_argon2d() {
1813 let pwd = vec![0x01u8; 32];
1814 let salt = vec![0x02u8; 16];
1815 let secret = vec![0x03u8; 8];
1816 let ad = vec![0x04u8; 12];
1817 let h0 = compute_h0(ARGON2D, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1818 let expected = "b8819791a0359660bb7709c85fa48f04d5d82c05c5f215ccdb885491717cf757082c28b951be381410b5fc2eb7274033b9fdc7ae672bcaac5d179097a4af3109";
1819 assert_eq!(hex::encode(h0), expected);
1820 }
1821
1822 #[test]
1823 fn test_h0_argon2i() {
1824 let pwd = vec![0x01u8; 32];
1825 let salt = vec![0x02u8; 16];
1826 let secret = vec![0x03u8; 8];
1827 let ad = vec![0x04u8; 12];
1828 let h0 = compute_h0(ARGON2I, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
1829 let expected = "c46065815276a0b3e731731c902f1fd80cf776907fbb7b6a5ca72e7b56011feeca446c86dd75b9469a5e6879dec4b72d0863fb939b982e5f397cc7d164fddaa9";
1830 assert_eq!(hex::encode(h0), expected);
1831 }
1832
1833 #[test]
1838 fn test_argon2id_empty_secret_and_ad() {
1839 let params = Params {
1840 iterations: 1,
1841 memory: 64,
1842 parallelism: 1,
1843 };
1844 let result = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms);
1845 assert_eq!(result.len(), 32);
1846 let result2 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms);
1847 assert_eq!(result, result2);
1848 }
1849
1850 #[test]
1851 fn test_argon2id_with_secret() {
1852 let p = Params {
1853 iterations: 1,
1854 memory: 64,
1855 parallelism: 1,
1856 };
1857 let without_secret = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p);
1858 let with_secret = derive_key_vec(32, b"password", b"saltsaltsaltsalt", b"secret", &[], &p);
1859 assert_ne!(without_secret, with_secret);
1860 }
1861
1862 #[test]
1863 fn test_argon2id_with_ad() {
1864 let p = Params {
1865 iterations: 1,
1866 memory: 64,
1867 parallelism: 1,
1868 };
1869 let without_ad = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p);
1870 let with_ad = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], b"associated data", &p);
1871 assert_ne!(without_ad, with_ad);
1872 }
1873
1874 #[test]
1875 fn test_argon2id_tag_length_4() {
1876 let result = derive_key_vec(
1877 4,
1878 b"password",
1879 b"saltsaltsaltsalt",
1880 &[],
1881 &[],
1882 &Params {
1883 iterations: 1,
1884 memory: 64,
1885 parallelism: 1,
1886 },
1887 );
1888 assert_eq!(result.len(), 4);
1889 }
1890
1891 #[test]
1892 fn test_argon2id_tag_length_128() {
1893 let result = derive_key_vec(
1894 128,
1895 b"password",
1896 b"saltsaltsaltsalt",
1897 &[],
1898 &[],
1899 &Params {
1900 iterations: 1,
1901 memory: 64,
1902 parallelism: 1,
1903 },
1904 );
1905 assert_eq!(result.len(), 128);
1906 }
1907
1908 #[test]
1909 fn test_argon2id_tag_length_256() {
1910 let result = derive_key_vec(
1911 256,
1912 b"password",
1913 b"saltsaltsaltsalt",
1914 &[],
1915 &[],
1916 &Params {
1917 iterations: 1,
1918 memory: 64,
1919 parallelism: 1,
1920 },
1921 );
1922 assert_eq!(result.len(), 256);
1923 }
1924
1925 #[test]
1926 fn test_argon2id_long_tag_consistency() {
1927 let p = Params {
1928 iterations: 1,
1929 memory: 64,
1930 parallelism: 1,
1931 };
1932 let r1 = derive_key_vec(100, b"password", b"saltsaltsaltsalt", &[], &[], &p);
1933 let r2 = derive_key_vec(100, b"password", b"saltsaltsaltsalt", &[], &[], &p);
1934 assert_eq!(r1, r2);
1935 assert_eq!(r1.len(), 100);
1936 }
1937
1938 #[test]
1939 fn test_argon2i_long_tag_consistency() {
1940 let params = Params {
1941 iterations: 1,
1942 memory: 64,
1943 parallelism: 1,
1944 };
1945 let mut r1 = [0u8; 100];
1946 let mut r2 = [0u8; 100];
1947 argon2_core(ARGON2I, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms, &mut r1).unwrap();
1948 argon2_core(ARGON2I, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms, &mut r2).unwrap();
1949 assert_eq!(r1, r2);
1950 }
1951
1952 #[test]
1953 fn test_argon2d_long_tag_consistency() {
1954 let params = Params {
1955 iterations: 1,
1956 memory: 64,
1957 parallelism: 1,
1958 };
1959 let mut r1 = [0u8; 100];
1960 let mut r2 = [0u8; 100];
1961 argon2_core(ARGON2D, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms, &mut r1).unwrap();
1962 argon2_core(ARGON2D, b"password", b"saltsaltsaltsalt", &[], &[], ¶ms, &mut r2).unwrap();
1963 assert_eq!(r1, r2);
1964 }
1965
1966 #[test]
1967 fn test_argon2id_single_pass() {
1968 let result = derive_key_vec(
1969 32,
1970 b"password",
1971 b"saltsalt",
1972 &[],
1973 &[],
1974 &Params {
1975 iterations: 1,
1976 memory: 32,
1977 parallelism: 1,
1978 },
1979 );
1980 assert_eq!(result.len(), 32);
1981 }
1982
1983 #[test]
1984 fn test_argon2id_high_parallelism() {
1985 let result = derive_key_vec(
1986 32,
1987 b"password",
1988 b"saltsaltsaltsalt",
1989 &[],
1990 &[],
1991 &Params {
1992 iterations: 1,
1993 memory: 64,
1994 parallelism: 8,
1995 },
1996 );
1997 assert_eq!(result.len(), 32);
1998 }
1999
2000 #[test]
2001 fn test_argon2d_rfc_h0() {
2002 let pwd = vec![0x01u8; 32];
2003 let salt = vec![0x02u8; 16];
2004 let secret = vec![0x03u8; 8];
2005 let ad = vec![0x04u8; 12];
2006 let h0 = compute_h0(ARGON2D, &pwd, &salt, &secret, &ad, 4, 32, 32, 3);
2007 assert_eq!(h0[0], 0xb8);
2008 assert_eq!(h0[1], 0x81);
2009 assert_eq!(h0[63], 0x09);
2010 }
2011
2012 #[test]
2013 fn test_variable_length_hash_exact_64() {
2014 let input = b"test";
2015 let result = variable_length_hash_vec(input, 64);
2016 assert_eq!(result.len(), 64);
2017 }
2018
2019 #[test]
2020 fn test_variable_length_hash_65_bytes() {
2021 let input = b"test";
2022 let result = variable_length_hash_vec(input, 65);
2023 assert_eq!(result.len(), 65);
2024 let result2 = variable_length_hash_vec(input, 65);
2025 assert_eq!(result, result2);
2026 }
2027
2028 #[test]
2029 fn test_variable_length_hash_deterministic() {
2030 for len in [4, 16, 32, 48, 64, 65, 96, 128, 256, 512, 1024] {
2031 let r1 = variable_length_hash_vec(b"determinism test", len);
2032 let r2 = variable_length_hash_vec(b"determinism test", len);
2033 assert_eq!(r1, r2, "variable_length_hash not deterministic for len={}", len);
2034 assert_eq!(r1.len(), len);
2035 }
2036 }
2037
2038 #[test]
2039 fn test_compress_deterministic() {
2040 let a = Block::from_bytes(&[0xAA; BLOCK_SIZE]);
2041 let b = Block::from_bytes(&[0xBB; BLOCK_SIZE]);
2042 let c1 = compress(&a, &b);
2043 let c2 = compress(&a, &b);
2044 assert_eq!(c1.v, c2.v);
2045 }
2046
2047 #[test]
2048 fn test_compress_xor_symmetry() {
2049 let a = Block::from_bytes(&[0x11; BLOCK_SIZE]);
2050 let b = Block::from_bytes(&[0x22; BLOCK_SIZE]);
2051 let c_ab = compress(&a, &b);
2052 let c_ba = compress(&b, &a);
2053 assert_eq!(c_ab.v, c_ba.v, "G(X,Y) should equal G(Y,X) since R = X XOR Y is symmetric");
2054 }
2055
2056 #[test]
2057 fn test_block_from_bytes_roundtrip() {
2058 let original = [0x42u8; BLOCK_SIZE];
2059 let block = Block::from_bytes(&original);
2060 let recovered = block.to_bytes();
2061 assert_eq!(original, recovered);
2062 }
2063
2064 #[test]
2065 fn test_argon2id_different_iterations() {
2066 let p1 = Params {
2067 iterations: 1,
2068 memory: 64,
2069 parallelism: 1,
2070 };
2071 let p2 = Params {
2072 iterations: 2,
2073 memory: 64,
2074 parallelism: 1,
2075 };
2076 let r1 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p1);
2077 let r2 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p2);
2078 assert_ne!(r1, r2);
2079 }
2080
2081 #[test]
2082 fn test_argon2id_different_memory() {
2083 let p1 = Params {
2084 iterations: 1,
2085 memory: 64,
2086 parallelism: 1,
2087 };
2088 let p2 = Params {
2089 iterations: 1,
2090 memory: 128,
2091 parallelism: 1,
2092 };
2093 let r1 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p1);
2094 let r2 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p2);
2095 assert_ne!(r1, r2);
2096 }
2097
2098 #[test]
2099 fn test_argon2id_different_parallelisms() {
2100 let p1 = Params {
2101 iterations: 1,
2102 memory: 64,
2103 parallelism: 1,
2104 };
2105 let p2 = Params {
2106 iterations: 1,
2107 memory: 64,
2108 parallelism: 2,
2109 };
2110 let r1 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p1);
2111 let r2 = derive_key_vec(32, b"password", b"saltsaltsaltsalt", &[], &[], &p2);
2112 assert_ne!(r1, r2);
2113 }
2114
2115 #[test]
2116 fn test_argon2i_rfc9106_tag() {
2117 let pwd = vec![0x01u8; 32];
2118 let salt = vec![0x02u8; 16];
2119 let secret = vec![0x03u8; 8];
2120 let ad = vec![0x04u8; 12];
2121 let expected = hex::decode("c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8").unwrap();
2122 let result = derive_key_typed(ARGON2I, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
2123 assert_eq!(result, expected);
2124 }
2125
2126 #[test]
2127 fn test_argon2d_rfc9106_tag() {
2128 let pwd = vec![0x01u8; 32];
2129 let salt = vec![0x02u8; 16];
2130 let secret = vec![0x03u8; 8];
2131 let ad = vec![0x04u8; 12];
2132 let expected = hex::decode("512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb").unwrap();
2133 let result = derive_key_typed(ARGON2D, &pwd, &salt, &secret, &ad, 3, 32, 4, 32);
2134 assert_eq!(result, expected);
2135 }
2136
2137 #[test]
2138 fn test_phc_verify_known() {
2139 let password = b"password";
2140 let salt = b"randomsalt123456";
2141 let params = Params {
2142 iterations: 1,
2143 memory: 64,
2144 parallelism: 1,
2145 };
2146 let encoded = hash_password(password, salt, ¶ms).unwrap();
2147 assert!(verify_password(password, &encoded).is_ok());
2148 assert_eq!(verify_password(b"wrong", &encoded), Err(Argon2Error::VerifyMismatch));
2149 }
2150
2151 #[test]
2152 fn test_decode_phc_roundtrip_all_types() {
2153 for tag_len in [4, 16, 32, 64] {
2154 let params = Params {
2155 iterations: 1,
2156 memory: 64,
2157 parallelism: 1,
2158 };
2159 let salt = b"testsalt12345678";
2160 let tag = vec![0xAB; tag_len as usize];
2161 let encoded = encode_phc(¶ms, salt, &tag);
2162 let (dp, ds, dt) = decode_phc(&encoded).unwrap();
2163 assert_eq!(dp.memory, 64);
2164 assert_eq!(dp.iterations, 1);
2165 assert_eq!(dp.parallelism, 1);
2166 assert_eq!(ds, salt);
2167 assert_eq!(dt, tag);
2168 }
2169 }
2170
2171 #[test]
2172 fn test_index_alpha_pass0_slice0() {
2173 let result = index_alpha(0, 0, 4, 2, 2, 8, true, 0xFFFFFFFF);
2174 assert!(result < 8);
2175 }
2176
2177 #[test]
2178 fn test_index_alpha_reference_area_size_zero() {
2179 let result = index_alpha(0, 0, 4, 2, 0, 8, true, 0xFFFFFFFF);
2180 assert_eq!(result, 0);
2181 }
2182
2183 #[test]
2184 fn test_permutation_p_changes_values() {
2185 let mut v = [0u64; 16];
2186 v[0] = 1;
2187 v[1] = 2;
2188 v[2] = 3;
2189 v[3] = 4;
2190 permutation_p(&mut v);
2191 assert_ne!(v[0], 1);
2192 assert_ne!(v[1], 2);
2193 assert_ne!(v[2], 3);
2194 assert_ne!(v[3], 4);
2195 }
2196
2197 #[test]
2198 fn test_permutation_p_deterministic() {
2199 let mut v1 = [0u64; 16];
2200 for (i, word) in v1.iter_mut().enumerate() {
2201 *word = i as u64;
2202 }
2203 let mut v2 = v1;
2204 permutation_p(&mut v1);
2205 permutation_p(&mut v2);
2206 assert_eq!(v1, v2);
2207 }
2208}