Skip to main content

neuron_encrypt_core/
utils.rs

1/// Compare two byte slices in constant time **within a fixed input length**.
2///
3/// # ⚠️ Limitations
4/// - The loop iterates `max(a.len(), b.len())` times, which leaks the longer
5///   input's length through timing. Do NOT use this to compare a secret
6///   (e.g., HMAC tag, session token) against an attacker-controlled input.
7/// - For comparing a secret against untrusted input, use `subtle::ConstantTimeEq`
8///   from the RustCrypto `subtle` crate instead.
9///
10/// The current call sites only compare two user-typed passwords during the
11/// "confirm passphrase" prompt, where both inputs are user-supplied and the
12/// length leak is harmless.
13pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
14    let len_a = a.len();
15    let len_b = b.len();
16    let max_len = len_a.max(len_b);
17
18    let mut byte_comparison_result = 0;
19
20    for i in 0..max_len {
21        let byte_a = a.get(i).unwrap_or(&0);
22        let byte_b = b.get(i).unwrap_or(&0);
23        byte_comparison_result |= byte_a ^ byte_b;
24    }
25
26    let len_diff = len_a ^ len_b;
27    let len_mismatch_flag = (((len_diff | len_diff.wrapping_neg()) >> (usize::BITS - 1)) & 1) as u8;
28
29    let final_result = byte_comparison_result | len_mismatch_flag;
30
31    final_result == 0
32}
33
34pub fn is_vx2_file(path: &std::path::Path) -> bool {
35    path.extension()
36        .and_then(|e| e.to_str())
37        .map(|s| s.eq_ignore_ascii_case("vx2"))
38        .unwrap_or(false)
39}
40
41pub fn format_size(bytes: u64) -> String {
42    if bytes < 1024 {
43        format!("{bytes} B")
44    } else if bytes < 1024 * 1024 {
45        format!("{:.1} KB", bytes as f64 / 1024.0)
46    } else if bytes < 1024 * 1024 * 1024 {
47        format!("{:.1} MB", bytes as f64 / 1024.0 / 1024.0)
48    } else {
49        format!("{:.1} GB", bytes as f64 / 1024.0 / 1024.0 / 1024.0)
50    }
51}
52
53pub struct HumanBytes(pub u64);
54
55impl std::fmt::Display for HumanBytes {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        let b = self.0;
58        if b < 1024 {
59            write!(f, "{b} B")
60        } else if b < 1024 * 1024 {
61            write!(f, "{:.1} KB", b as f64 / 1024.0)
62        } else if b < 1024 * 1024 * 1024 {
63            write!(f, "{:.1} MB", b as f64 / (1024.0 * 1024.0))
64        } else {
65            write!(f, "{:.2} GB", b as f64 / (1024.0 * 1024.0 * 1024.0))
66        }
67    }
68}