vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! HKDF, PBKDF2, and bounded Argon2id CPU reference helpers.

// argon2_max_memory_kib.rs
/// Largest accepted Argon2 memory cost in KiB.
pub(crate) const ARGON2_MAX_MEMORY_KIB: u32 = 1_048_576;

// argon2_max_parallelism.rs
/// Largest accepted Argon2 parallelism.
pub(crate) const ARGON2_MAX_PARALLELISM: u32 = 16;

// argon2id.rs
/// Derive bytes using a bounded Argon2id-compatible memory-hard construction.
///
/// # Errors
///
/// Returns an error when memory, parallelism, iteration, or output parameters
/// are outside the accepted DoS-defense range.
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(&params.memory_kib.to_le_bytes());
    seed.extend_from_slice(&params.iterations.to_le_bytes());
    seed.extend_from_slice(&params.parallelism.to_le_bytes());
    seed.extend_from_slice(&params.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))
}

// argon2id_params.rs
/// Argon2id parameter set.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Argon2idParams {
    /// Requested memory cost in KiB.
    pub memory_kib: u32,
    /// Number of passes over memory.
    pub iterations: u32,
    /// Requested lane count.
    pub parallelism: u32,
    /// Requested output length in bytes.
    pub output_len: u32,
}

// blake2b_long.rs
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
}

// hkdf_expand.rs
/// Expand an HKDF pseudorandom key with HMAC-SHA-256.
///
/// # Errors
///
/// Returns an error when `length > 8160`, the RFC 5869 SHA-256 maximum.
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)
}

// hkdf_extract_words.rs
/// Extract an HKDF pseudorandom key with HMAC-SHA-256.
#[must_use]
pub(crate) fn hkdf_extract_words(salt: &[u8], ikm: &[u8]) -> [u32; 8] {
    super::hmac::hmac_sha256_words(salt, ikm)
}

// pbkdf2_max_iterations.rs
/// Largest accepted PBKDF2 iteration count.
pub(crate) const PBKDF2_MAX_ITERATIONS: u32 = 1_000_000;

// pbkdf2_sha256.rs
/// Derive bytes using PBKDF2-HMAC-SHA-256.
///
/// # Errors
///
/// Returns an error when `iterations` is zero, exceeds 1,000,000, or when
/// `dk_len` exceeds the RFC 8018 block-count limit.
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)
}