2023-04-30 23:26:36 +00:00
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
|
2022-09-16 01:52:57 +00:00
|
|
|
//! Useful comparison functions.
|
|
|
|
|
|
|
|
/// Compare two slices for equality in fixed time. Panics if the slices are of non-equal length.
|
|
|
|
///
|
|
|
|
/// This works by XOR'ing each byte of the two inputs together and keeping an OR counter of the
|
|
|
|
/// results.
|
|
|
|
///
|
|
|
|
/// Instead of doing fancy bit twiddling to try to outsmart the compiler and prevent early exits,
|
|
|
|
/// which is not guaranteed to remain stable as compilers get ever smarter, we take the hit of
|
|
|
|
/// writing each intermediate value to memory with a volatile write and then re-reading it with a
|
|
|
|
/// volatile read. This should remain stable across compiler upgrades, but is much slower.
|
|
|
|
///
|
|
|
|
/// As of rust 1.31.0 disassembly looks completely within reason for this, see
|
|
|
|
/// <https://godbolt.org/z/mMbGQv>.
|
|
|
|
pub fn fixed_time_eq(a: &[u8], b: &[u8]) -> bool {
|
|
|
|
assert!(a.len() == b.len());
|
|
|
|
let count = a.len();
|
|
|
|
let lhs = &a[..count];
|
|
|
|
let rhs = &b[..count];
|
|
|
|
|
|
|
|
let mut r: u8 = 0;
|
|
|
|
for i in 0..count {
|
|
|
|
let mut rs = unsafe { core::ptr::read_volatile(&r) };
|
|
|
|
rs |= lhs[i] ^ rhs[i];
|
2023-02-21 23:25:12 +00:00
|
|
|
unsafe {
|
|
|
|
core::ptr::write_volatile(&mut r, rs);
|
|
|
|
}
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
let mut t = unsafe { core::ptr::read_volatile(&r) };
|
|
|
|
t |= t >> 4;
|
2023-02-21 23:25:12 +00:00
|
|
|
unsafe {
|
|
|
|
core::ptr::write_volatile(&mut r, t);
|
|
|
|
}
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
let mut t = unsafe { core::ptr::read_volatile(&r) };
|
|
|
|
t |= t >> 2;
|
2023-02-21 23:25:12 +00:00
|
|
|
unsafe {
|
|
|
|
core::ptr::write_volatile(&mut r, t);
|
|
|
|
}
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
let mut t = unsafe { core::ptr::read_volatile(&r) };
|
|
|
|
t |= t >> 1;
|
2023-02-21 23:25:12 +00:00
|
|
|
unsafe {
|
|
|
|
core::ptr::write_volatile(&mut r, t);
|
|
|
|
}
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
unsafe { (::core::ptr::read_volatile(&r) & 1) == 0 }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn eq_test() {
|
|
|
|
assert!(fixed_time_eq(&[0b00000000], &[0b00000000]));
|
|
|
|
assert!(fixed_time_eq(&[0b00000001], &[0b00000001]));
|
|
|
|
assert!(fixed_time_eq(&[0b00000010], &[0b00000010]));
|
|
|
|
assert!(fixed_time_eq(&[0b00000100], &[0b00000100]));
|
|
|
|
assert!(fixed_time_eq(&[0b00001000], &[0b00001000]));
|
|
|
|
assert!(fixed_time_eq(&[0b00010000], &[0b00010000]));
|
|
|
|
assert!(fixed_time_eq(&[0b00100000], &[0b00100000]));
|
|
|
|
assert!(fixed_time_eq(&[0b01000000], &[0b01000000]));
|
|
|
|
assert!(fixed_time_eq(&[0b10000000], &[0b10000000]));
|
|
|
|
assert!(fixed_time_eq(&[0b11111111], &[0b11111111]));
|
|
|
|
|
|
|
|
assert!(!fixed_time_eq(&[0b00000001], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000001], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000010], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000010], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000100], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000100], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00001000], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00001000], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00010000], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00010000], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00100000], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00100000], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b01000000], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b01000000], &[0b11111111]));
|
|
|
|
assert!(!fixed_time_eq(&[0b10000000], &[0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b10000000], &[0b11111111]));
|
|
|
|
|
2023-02-21 23:25:12 +00:00
|
|
|
assert!(fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000000, 0b00000000]));
|
2022-09-16 01:52:57 +00:00
|
|
|
assert!(!fixed_time_eq(&[0b00000001, 0b00000000], &[0b00000000, 0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000000, 0b00000001], &[0b00000000, 0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000001, 0b00000000]));
|
|
|
|
assert!(!fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000001, 0b00000001]));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(bench)]
|
|
|
|
mod benches {
|
|
|
|
use test::Bencher;
|
|
|
|
|
|
|
|
use crate::cmp::fixed_time_eq;
|
2023-02-21 23:25:12 +00:00
|
|
|
use crate::{sha256, sha512, Hash};
|
2022-09-16 01:52:57 +00:00
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_32b_constant_time_cmp_ne(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha256::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha256::Hash::hash(&[1; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| fixed_time_eq(&hash_a[..], &hash_b[..]))
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_32b_slice_cmp_ne(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha256::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha256::Hash::hash(&[1; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| &hash_a[..] == &hash_b[..])
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_32b_constant_time_cmp_eq(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha256::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha256::Hash::hash(&[0; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| fixed_time_eq(&hash_a[..], &hash_b[..]))
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_32b_slice_cmp_eq(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha256::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha256::Hash::hash(&[0; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| &hash_a[..] == &hash_b[..])
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_64b_constant_time_cmp_ne(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha512::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha512::Hash::hash(&[1; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| fixed_time_eq(&hash_a[..], &hash_b[..]))
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_64b_slice_cmp_ne(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha512::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha512::Hash::hash(&[1; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| &hash_a[..] == &hash_b[..])
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_64b_constant_time_cmp_eq(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha512::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha512::Hash::hash(&[0; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| fixed_time_eq(&hash_a[..], &hash_b[..]))
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_64b_slice_cmp_eq(bh: &mut Bencher) {
|
|
|
|
let hash_a = sha512::Hash::hash(&[0; 1]);
|
|
|
|
let hash_b = sha512::Hash::hash(&[0; 1]);
|
2023-02-21 23:25:12 +00:00
|
|
|
bh.iter(|| &hash_a[..] == &hash_b[..])
|
2022-09-16 01:52:57 +00:00
|
|
|
}
|
|
|
|
}
|