cryptex 2.4.0-rc2

Cryptex uses system keyrings to store and retrieve secrets or a local file
Documentation
#[cfg(any(feature = "file", feature = "encrypted-vfs"))]
use std::time::Instant;

#[cfg(any(feature = "file", feature = "encrypted-vfs"))]
use argon2::{Algorithm, Argon2, Params as Argon2Params, Version};

#[cfg(any(feature = "file", feature = "encrypted-vfs"))]
fn main() -> Result<(), String> {
    let expected_default_memory_cost = cryptex::default_memory_cost();
    let expected_default_threads = cryptex::default_threads();
    let expected_default_parallel = cryptex::default_parallel();
    let hash_memory = argon2_param("CRYPTEX_ARGON2_MEMORY", expected_default_memory_cost)?;
    let hash_threads = argon2_param("CRYPTEX_ARGON2_THREADS", expected_default_threads)?;
    let hash_parallel = argon2_param("CRYPTEX_ARGON2_PARALLEL", expected_default_parallel)?;

    #[cfg(feature = "file")]
    {
        let params = cryptex::sqlcipher::ConnectionParams::default();
        assert_eq!(params.memory, expected_default_memory_cost);
        assert_eq!(params.threads, expected_default_threads);
        assert_eq!(params.parallel, expected_default_parallel);

        let parsed: cryptex::sqlcipher::ConnectionParams = "password=pw salt=saltsaltsaltsalt"
            .parse()
            .map_err(|e: cryptex::error::KeyRingError| e.to_string())?;
        assert_eq!(parsed.memory, expected_default_memory_cost);
        assert_eq!(parsed.threads, expected_default_threads);
        assert_eq!(parsed.parallel, expected_default_parallel);
        hash_password(
            "sqlcipher",
            hash_memory,
            hash_threads,
            hash_parallel,
            &parsed.password,
            &parsed.salt,
        )?;
    }

    #[cfg(feature = "encrypted-vfs")]
    {
        let params = cryptex::encrypted_vfs::ConnectionParams::default();
        assert_eq!(params.memory, expected_default_memory_cost);
        assert_eq!(params.threads, expected_default_threads);
        assert_eq!(params.parallel, expected_default_parallel);

        let parsed: cryptex::encrypted_vfs::ConnectionParams = "password=pw salt=saltsaltsaltsalt"
            .parse()
            .map_err(|e: cryptex::error::KeyRingError| e.to_string())?;
        assert_eq!(parsed.memory, expected_default_memory_cost);
        assert_eq!(parsed.threads, expected_default_threads);
        assert_eq!(parsed.parallel, expected_default_parallel);
        hash_password(
            "encrypted-vfs",
            hash_memory,
            hash_threads,
            hash_parallel,
            &parsed.password,
            &parsed.salt,
        )?;
    }

    Ok(())
}

#[cfg(any(feature = "file", feature = "encrypted-vfs"))]
fn argon2_param(name: &str, default: u32) -> Result<u32, String> {
    match std::env::var(name) {
        Ok(value) => value
            .parse()
            .map_err(|e| format!("invalid {name}={value}: {e}")),
        Err(std::env::VarError::NotPresent) => Ok(default),
        Err(e) => Err(format!("invalid {name}: {e}")),
    }
}

#[cfg(any(feature = "file", feature = "encrypted-vfs"))]
fn hash_password(
    backend: &str,
    memory: u32,
    threads: u32,
    parallel: u32,
    password: &[u8],
    salt: &[u8],
) -> Result<(), String> {
    let argon2_params = Argon2Params::new(
        memory,
        threads,
        parallel,
        Some(Argon2Params::DEFAULT_OUTPUT_LEN),
    )
    .map_err(|e| e.to_string())?;
    let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
    let mut okm = [0u8; 32];

    let started = Instant::now();
    argon2
        .hash_password_into(password, salt, &mut okm)
        .map_err(|e| e.to_string())?;
    let elapsed = started.elapsed();
    std::hint::black_box(okm);

    println!(
        "{backend}: memory={memory} KiB threads={threads} parallel={parallel} elapsed={elapsed:?}"
    );
    Ok(())
}

#[cfg(not(any(feature = "file", feature = "encrypted-vfs")))]
fn main() {}