use crate::with_dit;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(not(miri))]
#[inline]
#[must_use]
fn optimizer_hide(mut value: u8) -> u8 {
unsafe {
core::arch::asm!("/* {0} */", inout(reg_byte) value, options(pure, nomem, nostack, preserves_flags));
value
}
}
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv32",
target_arch = "riscv64"
))]
#[cfg(not(miri))]
#[inline]
#[must_use]
#[allow(asm_sub_register)]
fn optimizer_hide(mut value: u8) -> u8 {
unsafe {
core::arch::asm!("/* {0} */", inout(reg) value, options(pure, nomem, nostack, preserves_flags));
value
}
}
#[cfg(any(
not(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv32",
target_arch = "riscv64",
)),
miri,
))]
#[inline(never)]
#[must_use]
fn optimizer_hide(value: u8) -> u8 {
core::hint::black_box(value)
}
#[inline]
#[must_use]
fn constant_time_ne(a: &[u8], b: &[u8]) -> u8 {
assert!(a.len() == b.len());
let len = a.len();
let a = &a[..len];
let b = &b[..len];
let mut tmp = 0;
for i in 0..len {
tmp |= a[i] ^ b[i];
}
optimizer_hide(tmp)
}
#[must_use]
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
with_dit(|| a.len() == b.len() && constant_time_ne(a, b) == 0)
}
#[inline]
#[must_use]
fn constant_time_ne_n<const N: usize>(a: &[u8; N], b: &[u8; N]) -> u8 {
let mut tmp = 0;
for i in 0..N {
tmp |= a[i] ^ b[i];
}
optimizer_hide(tmp)
}
#[must_use]
pub fn constant_time_eq_n<const N: usize>(a: &[u8; N], b: &[u8; N]) -> bool {
with_dit(|| constant_time_ne_n(a, b) == 0)
}
#[inline]
#[must_use]
pub fn constant_time_eq_16(a: &[u8; 16], b: &[u8; 16]) -> bool {
constant_time_eq_n(a, b)
}
#[inline]
#[must_use]
pub fn constant_time_eq_32(a: &[u8; 32], b: &[u8; 32]) -> bool {
constant_time_eq_n(a, b)
}
#[inline]
#[must_use]
pub fn constant_time_eq_64(a: &[u8; 64], b: &[u8; 64]) -> bool {
constant_time_eq_n(a, b)
}
#[cfg(test)]
mod tests {
#[cfg(feature = "count_instructions_test")]
extern crate std;
#[cfg(feature = "count_instructions_test")]
#[test]
fn count_optimizer_hide_instructions() -> std::io::Result<()> {
use super::optimizer_hide;
use count_instructions::count_instructions;
fn count() -> std::io::Result<usize> {
let mut count = 0;
assert_eq!(
10u8,
count_instructions(
|| optimizer_hide(1)
+ optimizer_hide(2)
+ optimizer_hide(3)
+ optimizer_hide(4),
|_| count += 1
)?
);
Ok(count)
}
fn count_optimized() -> std::io::Result<usize> {
#[inline]
fn inline_identity(value: u8) -> u8 {
value
}
let mut count = 0;
assert_eq!(
10u8,
count_instructions(
|| inline_identity(1)
+ inline_identity(2)
+ inline_identity(3)
+ inline_identity(4),
|_| count += 1
)?
);
Ok(count)
}
assert!(count()? > count_optimized()?);
Ok(())
}
}