diff --git a/src/lib.rs b/src/lib.rs index 143212b..989c632 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ #![deny(missing_docs)] #![doc(html_root_url = "https://docs.rs/subtle-ng/2.5.0")] - #[cfg(feature = "std")] #[macro_use] extern crate std; @@ -145,25 +144,23 @@ impl Not for Choice { /// code may break in a non-destructive way in the future, “constant-time” code /// is a continually moving target, and this is better than doing nothing. #[inline(never)] -fn black_box(input: u8) -> u8 { - debug_assert!((input == 0u8) | (input == 1u8)); - +fn black_box(input: T) -> T { unsafe { // Optimization barrier // - // Unsafe is ok, because: - // - &input is not NULL; - // - size of input is not zero; - // - u8 is neither Sync, nor Send; - // - u8 is Copy, so input is always live; - // - u8 type is always properly aligned. - core::ptr::read_volatile(&input as *const u8) + // SAFETY: + // - &input is not NULL because we own input; + // - input is Copy and always live; + // - input is always properly aligned. + core::ptr::read_volatile(&input) } } impl From for Choice { #[inline] fn from(input: u8) -> Choice { + debug_assert!((input == 0u8) | (input == 1u8)); + // Our goal is to prevent the compiler from inferring that the value held inside the // resulting `Choice` struct is really an `i1` instead of an `i8`. Choice(black_box(input)) @@ -728,7 +725,7 @@ macro_rules! generate_unsigned_integer_greater { Choice::from((bit & 1) as u8) } } - } + }; } generate_unsigned_integer_greater!(u8, 8); @@ -788,3 +785,21 @@ impl ConstantTimeLess for u32 {} impl ConstantTimeLess for u64 {} #[cfg(feature = "i128")] impl ConstantTimeLess for u128 {} + +/// Wrapper type which implements an optimization barrier for all accesses. +#[derive(Clone, Copy, Debug)] +pub struct BlackBox(T); + +impl BlackBox { + /// Constructs a new instance of `BlackBox` which will wrap the specified value. + /// + /// All access to the inner value will be mediated by a `black_box` optimization barrier. + pub fn new(value: T) -> Self { + Self(value) + } + + /// Read the inner value, applying an optimization barrier on access. + pub fn get(self) -> T { + black_box(self.0) + } +} diff --git a/tests/mod.rs b/tests/mod.rs index 4185480..cb6219a 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -266,16 +266,66 @@ fn test_ctoption() { )); // Test (in)equality - assert!(CtOption::new(1, Choice::from(0)).ct_eq(&CtOption::new(1, Choice::from(1))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(1, Choice::from(0))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(0)).ct_eq(&CtOption::new(2, Choice::from(1))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(2, Choice::from(0))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(0)).ct_eq(&CtOption::new(1, Choice::from(0))).unwrap_u8() == 1); - assert!(CtOption::new(1, Choice::from(0)).ct_eq(&CtOption::new(2, Choice::from(0))).unwrap_u8() == 1); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(2, Choice::from(1))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(2, Choice::from(1))).unwrap_u8() == 0); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(1, Choice::from(1))).unwrap_u8() == 1); - assert!(CtOption::new(1, Choice::from(1)).ct_eq(&CtOption::new(1, Choice::from(1))).unwrap_u8() == 1); + assert!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(0))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(0))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(1, Choice::from(0))) + .unwrap_u8() + == 1 + ); + assert!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(2, Choice::from(0))) + .unwrap_u8() + == 1 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8() + == 0 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8() + == 1 + ); + assert!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8() + == 1 + ); } #[test] @@ -303,7 +353,7 @@ macro_rules! generate_greater_than_test { assert!(z.unwrap_u8() == 1); } } - } + }; } #[test] @@ -337,7 +387,7 @@ fn greater_than_u128() { /// gives the correct result. (This fails using the bit-twiddling algorithm that /// go/crypto/subtle uses.) fn less_than_twos_compliment_minmax() { - let z = 1u32.ct_lt(&(2u32.pow(31)-1)); + let z = 1u32.ct_lt(&(2u32.pow(31) - 1)); assert!(z.unwrap_u8() == 1); } @@ -359,7 +409,7 @@ macro_rules! generate_less_than_test { assert!(z.unwrap_u8() == 1); } } - } + }; } #[test] @@ -387,3 +437,10 @@ fn less_than_u64() { fn less_than_u128() { generate_less_than_test!(u128); } + +#[test] +fn black_box_round_trip() { + let n = 42u64; + let black_box = BlackBox::new(n); + assert_eq!(n, black_box.get()); +}