neuron-encrypt 2.1.0

Military-grade file encryption — AES-256-GCM-SIV · Argon2id · HKDF-SHA512
Documentation
/// Compare two byte slices in constant time **within a fixed input length**.
///
/// # ⚠️ Limitations
/// - The loop iterates `max(a.len(), b.len())` times, which leaks the longer
///   input's length through timing. Do NOT use this to compare a secret
///   (e.g., HMAC tag, session token) against an attacker-controlled input.
/// - For comparing a secret against untrusted input, use `subtle::ConstantTimeEq`
///   from the RustCrypto `subtle` crate instead.
///
/// The current call sites only compare two user-typed passwords during the
/// "confirm passphrase" prompt, where both inputs are user-supplied and the
/// length leak is harmless.
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
    let len_a = a.len();
    let len_b = b.len();
    let max_len = len_a.max(len_b);

    let mut byte_comparison_result = 0;

    for i in 0..max_len {
        let byte_a = a.get(i).unwrap_or(&0);
        let byte_b = b.get(i).unwrap_or(&0);
        byte_comparison_result |= byte_a ^ byte_b;
    }

    let len_diff = len_a ^ len_b;
    let len_mismatch_flag = (((len_diff | len_diff.wrapping_neg()) >> (usize::BITS - 1)) & 1) as u8;

    let final_result = byte_comparison_result | len_mismatch_flag;

    final_result == 0
}

pub fn is_vx2_file(path: &std::path::Path) -> bool {
    path.extension()
        .and_then(|e| e.to_str())
        .map(|s| s.eq_ignore_ascii_case("vx2"))
        .unwrap_or(false)
}

pub fn format_size(bytes: u64) -> String {
    if bytes < 1024 {
        format!("{bytes} B")
    } else if bytes < 1024 * 1024 {
        format!("{:.1} KB", bytes as f64 / 1024.0)
    } else if bytes < 1024 * 1024 * 1024 {
        format!("{:.1} MB", bytes as f64 / 1024.0 / 1024.0)
    } else {
        format!("{:.1} GB", bytes as f64 / 1024.0 / 1024.0 / 1024.0)
    }
}

pub struct HumanBytes(pub u64);

impl std::fmt::Display for HumanBytes {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let b = self.0;
        if b < 1024 {
            write!(f, "{b} B")
        } else if b < 1024 * 1024 {
            write!(f, "{:.1} KB", b as f64 / 1024.0)
        } else if b < 1024 * 1024 * 1024 {
            write!(f, "{:.1} MB", b as f64 / (1024.0 * 1024.0))
        } else {
            write!(f, "{:.2} GB", b as f64 / (1024.0 * 1024.0 * 1024.0))
        }
    }
}