Skip to main content

simoom_tls/
util.rs

1//! Small helpers: constant-time comparison and selection, hex for tests and diagnostics.
2
3/// Constant-time equality of two slices. The lengths are not secret.
4pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
5    if a.len() != b.len() {
6        return false;
7    }
8    let mut d = 0u8;
9    for (x, y) in a.iter().zip(b) {
10        d |= x ^ y;
11    }
12    core::hint::black_box(d) == 0
13}
14
15/// An all-ones (flag = 1) or all-zero (flag = 0) mask, hidden from the optimizer so that it
16/// cannot turn masked selection back into a branch (LLVM's cmov-to-branch conversion would
17/// otherwise make table lookups and swaps leak through the branch predictor).
18#[inline(always)]
19pub fn ct_mask(flag: u64) -> u64 {
20    simoom_sys::arch::opaque(flag).wrapping_neg()
21}
22
23/// All-ones if `a == b`, else zero, as an opaque mask.
24#[inline(always)]
25pub fn ct_eq_mask(a: u64, b: u64) -> u64 {
26    let d = a ^ b;
27    ct_mask(1 ^ ((d | d.wrapping_neg()) >> 63))
28}
29
30/// Lower-case hex.
31pub fn hex(data: &[u8]) -> String {
32    const D: &[u8; 16] = b"0123456789abcdef";
33    let mut s = String::with_capacity(data.len() * 2);
34    for b in data {
35        s.push(D[(b >> 4) as usize] as char);
36        s.push(D[(b & 15) as usize] as char);
37    }
38    s
39}
40
41/// Decodes hex, ignoring whitespace and `:`. Panics on bad input (constants and tests only).
42pub fn unhex(s: &str) -> Vec<u8> {
43    let digits: Vec<u8> = s.bytes().filter(|c| !c.is_ascii_whitespace() && *c != b':').collect();
44    assert!(digits.len() % 2 == 0, "odd hex length");
45    let val = |c: u8| -> u8 {
46        match c {
47            b'0'..=b'9' => c - b'0',
48            b'a'..=b'f' => c - b'a' + 10,
49            b'A'..=b'F' => c - b'A' + 10,
50            _ => panic!("invalid hex digit"),
51        }
52    };
53    digits.chunks(2).map(|p| val(p[0]) << 4 | val(p[1])).collect()
54}
55
56/// Best-effort zeroing of secret material (not elided: the writes go through `black_box`).
57pub fn zeroize(buf: &mut [u8]) {
58    for b in buf.iter_mut() {
59        *b = 0;
60    }
61    core::hint::black_box(buf);
62}