dotlock-bin 1.0.1

Encrypted project-local environment variables manager
use argon2::{Algorithm, Argon2, Params, Version};

use crate::domain::{error::DotLockError, model::DotLockResult};

const MASTER_KEY_LEN: usize = 32;
const SALT_LEN: usize = 16;

#[derive(Debug, Clone, Copy)]
pub struct KdfParams {
    pub memory_kib: u32,
    pub iterations: u32,
    pub parallelism: u32,
}

impl Default for KdfParams {
    fn default() -> Self {
        Self {
            memory_kib: 65_536,
            iterations: 3,
            parallelism: 1,
        }
    }
}

pub fn generate_salt() -> DotLockResult<[u8; SALT_LEN]> {
    let mut salt = [0u8; SALT_LEN];
    getrandom::fill(&mut salt)
        .map_err(|e| DotLockError::Crypto(format!("failed to generate salt: {e}")))?;
    Ok(salt)
}

pub fn derive_master_key(
    passphrase: &str,
    salt: &[u8],
    params: KdfParams,
) -> DotLockResult<[u8; MASTER_KEY_LEN]> {
    let argon_params = Params::new(
        params.memory_kib,
        params.iterations,
        params.parallelism,
        Some(MASTER_KEY_LEN),
    )
    .map_err(|e| DotLockError::Crypto(format!("invalid Argon2 params: {e}")))?;

    let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon_params);

    let mut master_key = [0u8; MASTER_KEY_LEN];

    argon2
        .hash_password_into(passphrase.as_bytes(), salt, &mut master_key)
        .map_err(|e| {
            DotLockError::Crypto(format!("failed to derive master key with Argon2id: {e}"))
        })?;

    Ok(master_key)
}