#![cfg(all(feature = "argon2", feature = "parallel", not(miri)))]
use rscrypto::{Argon2Params, Argon2Version, Argon2d, Argon2i, Argon2id};
fn rs_params(m_kib: u32, t: u32, p: u32, out_len: u32) -> Argon2Params {
Argon2Params::new()
.memory_cost_kib(m_kib)
.time_cost(t)
.parallelism(p)
.output_len(out_len)
.version(Argon2Version::V0x13)
.build()
.unwrap()
}
fn oracle_hash(
algo: argon2::Algorithm,
password: &[u8],
salt: &[u8],
m_kib: u32,
t: u32,
p: u32,
out_len: usize,
) -> Vec<u8> {
let params = argon2::Params::new(m_kib, t, p, Some(out_len)).unwrap();
let ctx = argon2::Argon2::new(algo, argon2::Version::V0x13, params);
let mut out = vec![0u8; out_len];
ctx.hash_password_into(password, salt, &mut out).unwrap();
out
}
const PASSWORD: &[u8] = b"phase3-parallel-correctness-test";
const SALT: &[u8] = b"rscrypto-parallel-salt-bytes!!";
#[test]
fn argon2id_p8_matches_oracle() {
let m = 256u32; let t = 2u32;
let p = 8u32;
let out_len = 32usize;
let params = rs_params(m, t, p, out_len as u32);
let mut actual = vec![0u8; out_len];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut actual).unwrap();
let expected = oracle_hash(argon2::Algorithm::Argon2id, PASSWORD, SALT, m, t, p, out_len);
assert_eq!(actual, expected, "argon2id p=8 mismatch vs RustCrypto oracle");
}
#[test]
fn argon2d_p8_matches_oracle() {
let m = 256u32;
let t = 2u32;
let p = 8u32;
let out_len = 32usize;
let params = rs_params(m, t, p, out_len as u32);
let mut actual = vec![0u8; out_len];
Argon2d::hash(¶ms, PASSWORD, SALT, &mut actual).unwrap();
let expected = oracle_hash(argon2::Algorithm::Argon2d, PASSWORD, SALT, m, t, p, out_len);
assert_eq!(actual, expected, "argon2d p=8 mismatch vs RustCrypto oracle");
}
#[test]
fn argon2i_p8_matches_oracle() {
let m = 256u32;
let t = 2u32;
let p = 8u32;
let out_len = 32usize;
let params = rs_params(m, t, p, out_len as u32);
let mut actual = vec![0u8; out_len];
Argon2i::hash(¶ms, PASSWORD, SALT, &mut actual).unwrap();
let expected = oracle_hash(argon2::Algorithm::Argon2i, PASSWORD, SALT, m, t, p, out_len);
assert_eq!(actual, expected, "argon2i p=8 mismatch vs RustCrypto oracle");
}
#[test]
fn argon2id_p16_matches_oracle() {
let m = 512u32; let t = 2u32;
let p = 16u32;
let out_len = 32usize;
let params = rs_params(m, t, p, out_len as u32);
let mut actual = vec![0u8; out_len];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut actual).unwrap();
let expected = oracle_hash(argon2::Algorithm::Argon2id, PASSWORD, SALT, m, t, p, out_len);
assert_eq!(actual, expected, "argon2id p=16 mismatch vs RustCrypto oracle");
}
#[test]
fn argon2id_parallel_is_deterministic() {
let params = rs_params(256, 2, 8, 32);
let mut reference = [0u8; 32];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut reference).unwrap();
for trial in 0..16 {
let mut out = [0u8; 32];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut out).unwrap();
assert_eq!(out, reference, "non-deterministic output on trial {trial}");
}
}
#[test]
fn argon2d_parallel_is_deterministic() {
let params = rs_params(256, 2, 8, 32);
let mut reference = [0u8; 32];
Argon2d::hash(¶ms, PASSWORD, SALT, &mut reference).unwrap();
for trial in 0..16 {
let mut out = [0u8; 32];
Argon2d::hash(¶ms, PASSWORD, SALT, &mut out).unwrap();
assert_eq!(out, reference, "non-deterministic output on trial {trial}");
}
}
#[test]
fn argon2id_p1_fast_path_matches_oracle() {
let m = 32u32;
let t = 3u32;
let p = 1u32;
let out_len = 32usize;
let params = rs_params(m, t, p, out_len as u32);
let mut actual = vec![0u8; out_len];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut actual).unwrap();
let expected = oracle_hash(argon2::Algorithm::Argon2id, PASSWORD, SALT, m, t, p, out_len);
assert_eq!(actual, expected, "argon2id p=1 fast-path mismatch vs oracle");
}
#[test]
fn argon2id_parallel_verify_round_trip() {
let params = rs_params(256, 2, 8, 32);
let mut hash = [0u8; 32];
Argon2id::hash(¶ms, PASSWORD, SALT, &mut hash).unwrap();
assert!(Argon2id::verify(¶ms, PASSWORD, SALT, &hash).is_ok());
assert!(Argon2id::verify(¶ms, b"wrong-password-zzzzzzzzzzzzzzzzzz", SALT, &hash).is_err());
}