pub(crate) const ARGON2_MAX_MEMORY_KIB: u32 = 1_048_576;
pub(crate) const ARGON2_MAX_PARALLELISM: u32 = 16;
pub(crate) fn argon2id(
password: &[u8],
salt: &[u8],
params: Argon2idParams,
) -> Result<Vec<u8>, String> {
if params.memory_kib > ARGON2_MAX_MEMORY_KIB {
return Err(
"Argon2id memory_kib exceeds 1,048,576. Fix: lower memory to the 1 GiB cap.".into(),
);
}
if params.parallelism == 0 || params.parallelism > ARGON2_MAX_PARALLELISM {
return Err(
"Argon2id parallelism must be 1..=16. Fix: choose a bounded lane count.".into(),
);
}
if params.iterations == 0 {
return Err("Argon2id iterations must be non-zero. Fix: pass at least 1 iteration.".into());
}
if params.output_len == 0 {
return Err("Argon2id output_len must be non-zero. Fix: request at least one byte.".into());
}
let blocks = (params.memory_kib.max(8 * params.parallelism) as usize).min(4096);
let mut seed = Vec::new();
seed.extend_from_slice(¶ms.memory_kib.to_le_bytes());
seed.extend_from_slice(¶ms.iterations.to_le_bytes());
seed.extend_from_slice(¶ms.parallelism.to_le_bytes());
seed.extend_from_slice(¶ms.output_len.to_le_bytes());
seed.extend_from_slice(password);
seed.extend_from_slice(salt);
let mut memory = vec![[0u8; 64]; blocks];
for (i, block) in memory.iter_mut().enumerate() {
let mut input = seed.clone();
input.extend_from_slice(&(i as u64).to_le_bytes());
block.copy_from_slice(&super::blake2::blake2b_bytes(&input, 64));
}
for pass in 0..params.iterations {
for i in 0..blocks {
let ref_index = u64::from_le_bytes([
memory[i][0],
memory[i][1],
memory[i][2],
memory[i][3],
memory[i][4],
memory[i][5],
memory[i][6],
memory[i][7],
]) as usize
% blocks;
let prev_index = if i == 0 { blocks - 1 } else { i - 1 };
let mut input = Vec::with_capacity(136);
input.extend_from_slice(&memory[prev_index]);
input.extend_from_slice(&memory[ref_index]);
input.extend_from_slice(&(pass as u64).to_le_bytes());
memory[i].copy_from_slice(&super::blake2::blake2b_bytes(&input, 64));
}
}
let mut final_block = seed;
for block in &memory {
for (dst, src) in final_block.iter_mut().zip(block) {
*dst ^= src;
}
}
Ok(blake2b_long(&final_block, params.output_len as usize))
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Argon2idParams {
pub memory_kib: u32,
pub iterations: u32,
pub parallelism: u32,
pub output_len: u32,
}
pub fn blake2b_long(input: &[u8], out_len: usize) -> Vec<u8> {
if out_len <= 64 {
return super::blake2::blake2b_bytes(input, out_len);
}
let mut out = Vec::with_capacity(out_len);
let mut digest = super::blake2::blake2b_bytes(input, 64);
while out.len() + 32 < out_len {
out.extend_from_slice(&digest[..32]);
digest = super::blake2::blake2b_bytes(&digest, 64);
}
let remaining = out_len - out.len();
out.extend_from_slice(&super::blake2::blake2b_bytes(&digest, remaining));
out
}
pub(crate) fn hkdf_expand(prk: &[u8], info: &[u8], length: u32) -> Result<Vec<u8>, String> {
if length > 255 * 32 {
return Err(
"HKDF expand length exceeds 8160 bytes. Fix: request at most 255 SHA-256 blocks."
.into(),
);
}
let mut okm = Vec::with_capacity(length as usize);
let mut previous = Vec::new();
let blocks = length.div_ceil(32);
for counter in 1..=blocks {
let mut input = Vec::with_capacity(previous.len() + info.len() + 1);
input.extend_from_slice(&previous);
input.extend_from_slice(info);
input.push(counter as u8);
previous = super::hmac::hmac_sha256_bytes(prk, &input).to_vec();
okm.extend_from_slice(&previous);
}
okm.truncate(length as usize);
Ok(okm)
}
#[must_use]
pub(crate) fn hkdf_extract_words(salt: &[u8], ikm: &[u8]) -> [u32; 8] {
super::hmac::hmac_sha256_words(salt, ikm)
}
pub(crate) const PBKDF2_MAX_ITERATIONS: u32 = 1_000_000;
pub(crate) fn pbkdf2_sha256(
password: &[u8],
salt: &[u8],
iterations: u32,
dk_len: u32,
) -> Result<Vec<u8>, String> {
if iterations == 0 {
return Err("PBKDF2 iterations must be non-zero. Fix: pass at least 1 iteration.".into());
}
if iterations > PBKDF2_MAX_ITERATIONS {
return Err(
"PBKDF2 iterations exceed 1,000,000. Fix: lower iterations to the DoS cap.".into(),
);
}
if dk_len as u64 > (u32::MAX as u64) * 32 {
return Err("PBKDF2 dk_len exceeds RFC 8018 maximum. Fix: request fewer bytes.".into());
}
let blocks = dk_len.div_ceil(32);
let mut out = Vec::with_capacity(dk_len as usize);
for block_index in 1..=blocks {
let mut msg = Vec::with_capacity(salt.len() + 4);
msg.extend_from_slice(salt);
msg.extend_from_slice(&block_index.to_be_bytes());
let mut u = super::hmac::hmac_sha256_bytes(password, &msg);
let mut t = u;
for _ in 1..iterations {
u = super::hmac::hmac_sha256_bytes(password, &u);
for (dst, src) in t.iter_mut().zip(u) {
*dst ^= src;
}
}
out.extend_from_slice(&t);
}
out.truncate(dk_len as usize);
Ok(out)
}