diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7c1616a..319d1bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,63 +2,91 @@ on: [push, pull_request] name: Test +permissions: + contents: read + +env: + RUSTFLAGS: "-D warnings" + jobs: + no_std: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.85.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} + test: - name: cargo test runs-on: ubuntu-latest strategy: matrix: rust: + - 1.85.0 # MSRV - stable - beta - nightly - - 1.60.0 steps: - name: checkout - uses: actions/checkout@v2 - - name: toolchain - uses: actions-rs/toolchain@v1 + uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - target: thumbv7em-none-eabi - override: true - - name: test - uses: actions-rs/cargo@v1 - with: - command: test - - name: nightly - uses: actions-rs/cargo@v1 - with: - command: test - args: --features nightly - - name: no-default-features - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features - - name: std - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features std - - name: std const-generics - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std const-generics" - - name: std i128 - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std i128" - - name: std i128 const-generics - uses: actions-rs/cargo@v1 + - run: cargo check + - run: cargo test + - run: cargo test --release + + # Test using `cargo careful` + test-careful: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo install cargo-careful + - run: cargo careful test --all-features + + # Test using `cargo miri` + test-miri: + runs-on: ubuntu-latest + env: + MIRIFLAGS: "-Zmiri-symbolic-alignment-check -Zmiri-strict-provenance" + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + - s390x-unknown-linux-gnu + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@nightly + - run: rustup component add miri && cargo miri setup + - run: cargo miri test --target ${{ matrix.target }} --all-features --lib + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master with: - command: test - args: --no-default-features --features "std i128 const-generics" - - name: no std build - uses: actions-rs/cargo@v1 + toolchain: 1.92.0 # Pinned to prevent breakages + components: clippy + - run: cargo clippy --workspace --all-features --lib --bins --tests -- -D warnings + + rustfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master with: - command: build - args: --no-default-features --target thumbv7em-none-eabi + toolchain: stable + components: rustfmt + - run: cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml index ae7361c..331a803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,11 @@ name = "subtle" # Before incrementing: # - update CHANGELOG -# - update html_root_url # - update README if necessary by semver # - if any updates were made to the README, also update the module documentation in src/lib.rs -version = "2.6.0" -edition = "2018" +version = "3.0.0-pre" +edition = "2024" +rust-version = "1.85.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" @@ -17,23 +17,7 @@ documentation = "https://docs.rs/subtle" categories = ["cryptography", "no-std"] keywords = ["cryptography", "crypto", "constant-time", "utilities"] description = "Pure-Rust traits and utilities for constant-time cryptographic implementations." -exclude = [ - "**/.gitignore", - ".travis.yml", -] - -[badges] -travis-ci = { repository = "dalek-cryptography/subtle", branch = "master"} +exclude = [".github", ".gitignore"] [dev-dependencies] -rand = { version = "0.8" } - -[features] -const-generics = [] -# DEPRECATED: As of 2.5.1, this feature does nothing. -core_hint_black_box = [] -default = ["std", "i128"] -std = [] -i128 = [] -# DEPRECATED: As of 2.4.1, this feature does nothing. -nightly = [] +rand = { version = "0.9" } diff --git a/src/choice.rs b/src/choice.rs new file mode 100644 index 0000000..69d26b5 --- /dev/null +++ b/src/choice.rs @@ -0,0 +1,171 @@ +use crate::{ConditionallySelectable, ConstantTimeEq}; +use core::hint::black_box; +use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; + +/// The `Choice` struct represents a choice for use in conditional assignment. +/// +/// It is a wrapper around a `u8`, which should have the value either `1` (true) +/// or `0` (false). +/// +/// The conversion from `u8` to `Choice` passes the value through an optimization +/// barrier, as a best-effort attempt to prevent the compiler from inferring that +/// the `Choice` value is a boolean. This strategy is based on Tim Maclean's +/// [work on `rust-timing-shield`][rust-timing-shield], which attempts to provide +/// a more comprehensive approach for preventing software side-channels in Rust +/// code. +/// +/// The `Choice` struct implements operators for AND, OR, XOR, and NOT, to allow +/// combining `Choice` values. These operations do not short-circuit. +/// +/// [rust-timing-shield]: +/// https://www.chosenplaintext.ca/open-source/rust-timing-shield/security +#[derive(Copy, Clone, Debug)] +pub struct Choice(pub(crate) u8); + +impl Choice { + /// Unwrap the `Choice` wrapper to reveal the underlying `u8`. + /// + /// # Note + /// + /// This function only exists as an **escape hatch** for the rare case + /// where it's not possible to use one of the `subtle`-provided + /// trait impls. + /// + /// **To convert a `Choice` to a `bool`, use the `From` implementation instead.** + #[inline] + pub fn unwrap_u8(&self) -> u8 { + self.0 + } +} + +impl From for bool { + /// Convert the `Choice` wrapper into a `bool`, depending on whether + /// the underlying `u8` was a `0` or a `1`. + /// + /// # Note + /// + /// This function exists to avoid having higher-level cryptographic protocol + /// implementations duplicating this pattern. + /// + /// The intended use case for this conversion is at the _end_ of a + /// higher-level primitive implementation: for example, in checking a keyed + /// MAC, where the verification should happen in constant-time (and thus use + /// a `Choice`) but it is safe to return a `bool` at the end of the + /// verification. + #[inline] + fn from(source: Choice) -> bool { + debug_assert!((source.0 == 0u8) | (source.0 == 1u8)); + source.0 != 0 + } +} + +impl BitAnd for Choice { + type Output = Choice; + #[inline] + fn bitand(self, rhs: Choice) -> Choice { + (self.0 & rhs.0).into() + } +} + +impl BitAndAssign for Choice { + #[inline] + fn bitand_assign(&mut self, rhs: Choice) { + *self = *self & rhs; + } +} + +impl BitOr for Choice { + type Output = Choice; + #[inline] + fn bitor(self, rhs: Choice) -> Choice { + (self.0 | rhs.0).into() + } +} + +impl BitOrAssign for Choice { + #[inline] + fn bitor_assign(&mut self, rhs: Choice) { + *self = *self | rhs; + } +} + +impl BitXor for Choice { + type Output = Choice; + #[inline] + fn bitxor(self, rhs: Choice) -> Choice { + (self.0 ^ rhs.0).into() + } +} + +impl BitXorAssign for Choice { + #[inline] + fn bitxor_assign(&mut self, rhs: Choice) { + *self = *self ^ rhs; + } +} + +impl Not for Choice { + type Output = Choice; + #[inline] + fn not(self) -> Choice { + (1u8 & (!self.0)).into() + } +} + +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 a `bool` instead of a `u8`. + Choice(black_box(input)) + } +} + +impl ConditionallySelectable for Choice { + #[inline] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Choice(u8::conditional_select(&a.0, &b.0, choice)) + } +} + +impl ConstantTimeEq for Choice { + #[inline] + fn ct_eq(&self, rhs: &Choice) -> Choice { + !(*self ^ *rhs) + } +} + +#[cfg(test)] +mod tests { + use crate::{Choice, ConditionallySelectable, ConstantTimeEq}; + + #[test] + fn choice_into_bool() { + let choice_true: bool = Choice::from(1).into(); + assert!(choice_true); + + let choice_false: bool = Choice::from(0).into(); + assert!(!choice_false); + } + + #[test] + fn conditional_select_choice() { + let t = Choice::from(1); + let f = Choice::from(0); + + assert!(bool::from(Choice::conditional_select(&t, &f, f))); + assert!(!bool::from(Choice::conditional_select(&t, &f, t))); + assert!(!bool::from(Choice::conditional_select(&f, &t, f))); + assert!(bool::from(Choice::conditional_select(&f, &t, t))); + } + + #[test] + fn choice_equal() { + assert_eq!(Choice::from(0).ct_eq(&Choice::from(0)).unwrap_u8(), 1); + assert_eq!(Choice::from(0).ct_eq(&Choice::from(1)).unwrap_u8(), 0); + assert_eq!(Choice::from(1).ct_eq(&Choice::from(0)).unwrap_u8(), 0); + assert_eq!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8(), 1); + } +} diff --git a/src/ctoption.rs b/src/ctoption.rs new file mode 100644 index 0000000..5351a88 --- /dev/null +++ b/src/ctoption.rs @@ -0,0 +1,406 @@ +use crate::{Choice, ConditionallySelectable, ConstantTimeEq}; + +/// The `CtOption` type represents an optional value similar to the +/// [`Option`](core::option::Option) type but is intended for +/// use in constant time APIs. +/// +/// Any given `CtOption` is either `Some` or `None`, but unlike +/// `Option` these variants are not exposed. The +/// [`is_some()`](CtOption::is_some) method is used to determine if +/// the value is `Some`, and [`unwrap_or()`](CtOption::unwrap_or) and +/// [`unwrap_or_else()`](CtOption::unwrap_or_else) methods are +/// provided to access the underlying value. The value can also be +/// obtained with [`unwrap()`](CtOption::unwrap) but this will panic +/// if it is `None`. +/// +/// Functions that are intended to be constant time may not produce +/// valid results for all inputs, such as square root and inversion +/// operations in finite field arithmetic. Returning an `Option` +/// from these functions makes it difficult for the caller to reason +/// about the result in constant time, and returning an incorrect +/// value burdens the caller and increases the chance of bugs. +#[derive(Clone, Copy, Debug)] +pub struct CtOption { + pub(crate) value: T, + pub(crate) is_some: Choice, +} + +impl From> for Option { + /// Convert the `CtOption` wrapper into an `Option`, depending on whether + /// the underlying `is_some` `Choice` was a `0` or a `1` once unwrapped. + /// + /// # Note + /// + /// This function exists to avoid ending up with ugly, verbose and/or bad handled + /// conversions from the `CtOption` wraps to an `Option` or `Result`. + /// This implementation doesn't intend to be constant-time nor try to protect the + /// leakage of the `T` since the `Option` will do it anyways. + fn from(source: CtOption) -> Option { + if source.is_some().unwrap_u8() == 1u8 { + Option::Some(source.value) + } else { + None + } + } +} + +impl CtOption { + /// This method is used to construct a new `CtOption` and takes + /// a value of type `T`, and a `Choice` that determines whether + /// the optional value should be `Some` or not. If `is_some` is + /// false, the value will still be stored but its value is never + /// exposed. + #[inline] + pub fn new(value: T, is_some: Choice) -> CtOption { + CtOption { value, is_some } + } + + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + pub fn expect(self, msg: &str) -> T { + assert_eq!(self.is_some.unwrap_u8(), 1, "{}", msg); + + self.value + } + + /// This returns the underlying value but panics if it + /// is not `Some`. + #[inline] + pub fn unwrap(self) -> T { + assert_eq!(self.is_some.unwrap_u8(), 1); + + self.value + } + + /// This returns the underlying value if it is `Some` + /// or the provided value otherwise. + #[inline] + pub fn unwrap_or(self, def: T) -> T + where + T: ConditionallySelectable, + { + T::conditional_select(&def, &self.value, self.is_some) + } + + /// This returns the underlying value if it is `Some` + /// or the value produced by the provided closure otherwise. + /// + /// This operates in constant time, because the provided closure + /// is always called. + #[inline] + pub fn unwrap_or_else(self, f: F) -> T + where + T: ConditionallySelectable, + F: FnOnce() -> T, + { + T::conditional_select(&f(), &self.value, self.is_some) + } + + /// Returns a true `Choice` if this value is `Some`. + #[inline] + pub fn is_some(&self) -> Choice { + self.is_some + } + + /// Returns a true `Choice` if this value is `None`. + #[inline] + pub fn is_none(&self) -> Choice { + !self.is_some + } + + /// Returns a `None` value if the option is `None`, otherwise + /// returns a `CtOption` enclosing the value of the provided closure. + /// The closure is given the enclosed value or, if the option is + /// `None`, it is provided a dummy value computed using + /// `Default::default()`. + /// + /// This operates in constant time, because the provided closure + /// is always called. + #[inline] + pub fn map(self, f: F) -> CtOption + where + T: Default + ConditionallySelectable, + F: FnOnce(T) -> U, + { + CtOption::new( + f(T::conditional_select( + &T::default(), + &self.value, + self.is_some, + )), + self.is_some, + ) + } + + /// Returns a `None` value if the option is `None`, otherwise + /// returns the result of the provided closure. The closure is + /// given the enclosed value or, if the option is `None`, it + /// is provided a dummy value computed using `Default::default()`. + /// + /// This operates in constant time, because the provided closure + /// is always called. + #[inline] + pub fn and_then(self, f: F) -> CtOption + where + T: Default + ConditionallySelectable, + F: FnOnce(T) -> CtOption, + { + let mut tmp = f(T::conditional_select( + &T::default(), + &self.value, + self.is_some, + )); + tmp.is_some &= self.is_some; + + tmp + } + + /// Returns `self` if it contains a value, and otherwise returns the result of + /// calling `f`. The provided function `f` is always called. + #[inline] + pub fn or_else(self, f: F) -> CtOption + where + T: ConditionallySelectable, + F: FnOnce() -> CtOption, + { + let is_none = self.is_none(); + let f = f(); + + Self::conditional_select(&self, &f, is_none) + } + + /// Convert the `CtOption` wrapper into an `Option`, depending on whether + /// the underlying `is_some` `Choice` was a `0` or a `1` once unwrapped. + /// + /// # Note + /// + /// This function exists to avoid ending up with ugly, verbose and/or bad handled + /// conversions from the `CtOption` wraps to an `Option` or `Result`. + /// This implementation doesn't intend to be constant-time nor try to protect the + /// leakage of the `T` since the `Option` will do it anyways. + /// + /// It's equivalent to the corresponding `From` impl, however this version is + /// friendlier for type inference. + pub fn into_option(self) -> Option { + self.into() + } +} + +impl ConditionallySelectable for CtOption { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + CtOption::new( + T::conditional_select(&a.value, &b.value, choice), + Choice::conditional_select(&a.is_some, &b.is_some, choice), + ) + } +} + +impl ConstantTimeEq for CtOption { + /// Two `CtOption`s are equal if they are both `Some` and + /// their values are equal, or both `None`. + #[inline] + fn ct_eq(&self, rhs: &CtOption) -> Choice { + let a = self.is_some(); + let b = rhs.is_some(); + + (a & b & self.value.ct_eq(&rhs.value)) | (!a & !b) + } +} + +#[cfg(test)] +mod tests { + use crate::{Choice, ConstantTimeEq, CtOption}; + + #[test] + fn test_ctoption() { + let a = CtOption::new(10, Choice::from(1)); + let b = CtOption::new(9, Choice::from(1)); + let c = CtOption::new(10, Choice::from(0)); + let d = CtOption::new(9, Choice::from(0)); + + // Test is_some / is_none + assert!(bool::from(a.is_some())); + assert!(bool::from(!a.is_none())); + assert!(bool::from(b.is_some())); + assert!(bool::from(!b.is_none())); + assert!(bool::from(!c.is_some())); + assert!(bool::from(c.is_none())); + assert!(bool::from(!d.is_some())); + assert!(bool::from(d.is_none())); + + // Test unwrap for Some + assert_eq!(a.unwrap(), 10); + assert_eq!(b.unwrap(), 9); + + // Test equality + assert!(bool::from(a.ct_eq(&a))); + assert!(bool::from(!a.ct_eq(&b))); + assert!(bool::from(!a.ct_eq(&c))); + assert!(bool::from(!a.ct_eq(&d))); + + // Test equality of None with different + // dummy value + assert!(bool::from(c.ct_eq(&d))); + + // Test unwrap_or + assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or(2), 1); + assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or(2), 2); + + // Test unwrap_or_else + assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or_else(|| 2), 1); + assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or_else(|| 2), 2); + + // Test map + assert_eq!( + CtOption::new(1, Choice::from(1)) + .map(|v| { + assert_eq!(v, 1); + 2 + }) + .unwrap(), + 2 + ); + assert_eq!( + CtOption::new(1, Choice::from(0)) + .map(|_| 2) + .is_none() + .unwrap_u8(), + 1 + ); + + // Test and_then + assert_eq!( + CtOption::new(1, Choice::from(1)) + .and_then(|v| { + assert_eq!(v, 1); + CtOption::new(2, Choice::from(0)) + }) + .is_none() + .unwrap_u8(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .and_then(|v| { + assert_eq!(v, 1); + CtOption::new(2, Choice::from(1)) + }) + .unwrap(), + 2 + ); + + assert_eq!( + CtOption::new(1, Choice::from(0)) + .and_then(|_| CtOption::new(2, Choice::from(0))) + .is_none() + .unwrap_u8(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(0)) + .and_then(|_| CtOption::new(2, Choice::from(1))) + .is_none() + .unwrap_u8(), + 1 + ); + + // Test or_else + assert_eq!( + CtOption::new(1, Choice::from(0)) + .or_else(|| CtOption::new(2, Choice::from(1))) + .unwrap(), + 2 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .or_else(|| CtOption::new(2, Choice::from(0))) + .unwrap(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .or_else(|| CtOption::new(2, Choice::from(1))) + .unwrap(), + 1 + ); + assert!(bool::from( + CtOption::new(1, Choice::from(0)) + .or_else(|| CtOption::new(2, Choice::from(0))) + .is_none() + )); + + // Test (in)equality + assert_eq!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(0))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(0))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(1, Choice::from(0))) + .unwrap_u8(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(0)) + .ct_eq(&CtOption::new(2, Choice::from(0))) + .unwrap_u8(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(2, Choice::from(1))) + .unwrap_u8(), + 0 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8(), + 1 + ); + assert_eq!( + CtOption::new(1, Choice::from(1)) + .ct_eq(&CtOption::new(1, Choice::from(1))) + .unwrap_u8(), + 1 + ); + } + + #[test] + #[should_panic] + fn unwrap_none_ctoption() { + // This test might fail (in release mode?) if the + // compiler decides to optimize it away. + CtOption::new(10, Choice::from(0)).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9fc143b..ef70bd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,9 +9,9 @@ // - Henry de Valence #![no_std] -#![deny(missing_docs)] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/subtle/2.6.0")] +#![allow(clippy::inline_fn_without_body)] +#![warn(clippy::mod_module_files, missing_docs, unreachable_pub)] //! # subtle [![](https://img.shields.io/crates/v/subtle.svg)](https://crates.io/crates/subtle) [![](https://img.shields.io/badge/dynamic/json.svg?label=docs&uri=https%3A%2F%2Fcrates.io%2Fapi%2Fv1%2Fcrates%2Fsubtle%2Fversions&query=%24.versions%5B0%5D.num&colorB=4F74A6)](https://doc.dalek.rs/subtle) [![](https://travis-ci.org/dalek-cryptography/subtle.svg?branch=master)](https://travis-ci.org/dalek-cryptography/subtle) //! @@ -22,7 +22,7 @@ //! type is a wrapper around a `u8` that holds a `0` or `1`. //! //! ```toml -//! subtle = "2.6" +//! subtle = "3.0.0-pre" //! ``` //! //! This crate represents a “best-effort” attempt, since side-channels @@ -58,7 +58,7 @@ //! //! ## Minimum Supported Rust Version //! -//! Rust **1.41** or higher. +//! Rust **1.85** or higher. //! //! Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. //! @@ -88,907 +88,19 @@ //! [docs]: https://docs.rs/subtle //! [rust-timing-shield]: https://www.chosenplaintext.ca/open-source/rust-timing-shield/security -#[cfg(feature = "std")] -#[macro_use] -extern crate std; +mod choice; +mod ctoption; +mod traits; -use core::cmp; -use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not}; -use core::option::Option; +pub use choice::Choice; +pub use ctoption::CtOption; +pub use traits::{ + ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, + ConstantTimeLess, +}; -#[cfg(feature = "core_hint_black_box")] use core::hint::black_box; -/// The `Choice` struct represents a choice for use in conditional assignment. -/// -/// It is a wrapper around a `u8`, which should have the value either `1` (true) -/// or `0` (false). -/// -/// The conversion from `u8` to `Choice` passes the value through an optimization -/// barrier, as a best-effort attempt to prevent the compiler from inferring that -/// the `Choice` value is a boolean. This strategy is based on Tim Maclean's -/// [work on `rust-timing-shield`][rust-timing-shield], which attempts to provide -/// a more comprehensive approach for preventing software side-channels in Rust -/// code. -/// -/// The `Choice` struct implements operators for AND, OR, XOR, and NOT, to allow -/// combining `Choice` values. These operations do not short-circuit. -/// -/// [rust-timing-shield]: -/// https://www.chosenplaintext.ca/open-source/rust-timing-shield/security -#[derive(Copy, Clone, Debug)] -pub struct Choice(u8); - -impl Choice { - /// Unwrap the `Choice` wrapper to reveal the underlying `u8`. - /// - /// # Note - /// - /// This function only exists as an **escape hatch** for the rare case - /// where it's not possible to use one of the `subtle`-provided - /// trait impls. - /// - /// **To convert a `Choice` to a `bool`, use the `From` implementation instead.** - #[inline] - pub fn unwrap_u8(&self) -> u8 { - self.0 - } -} - -impl From for bool { - /// Convert the `Choice` wrapper into a `bool`, depending on whether - /// the underlying `u8` was a `0` or a `1`. - /// - /// # Note - /// - /// This function exists to avoid having higher-level cryptographic protocol - /// implementations duplicating this pattern. - /// - /// The intended use case for this conversion is at the _end_ of a - /// higher-level primitive implementation: for example, in checking a keyed - /// MAC, where the verification should happen in constant-time (and thus use - /// a `Choice`) but it is safe to return a `bool` at the end of the - /// verification. - #[inline] - fn from(source: Choice) -> bool { - debug_assert!((source.0 == 0u8) | (source.0 == 1u8)); - source.0 != 0 - } -} - -impl BitAnd for Choice { - type Output = Choice; - #[inline] - fn bitand(self, rhs: Choice) -> Choice { - (self.0 & rhs.0).into() - } -} - -impl BitAndAssign for Choice { - #[inline] - fn bitand_assign(&mut self, rhs: Choice) { - *self = *self & rhs; - } -} - -impl BitOr for Choice { - type Output = Choice; - #[inline] - fn bitor(self, rhs: Choice) -> Choice { - (self.0 | rhs.0).into() - } -} - -impl BitOrAssign for Choice { - #[inline] - fn bitor_assign(&mut self, rhs: Choice) { - *self = *self | rhs; - } -} - -impl BitXor for Choice { - type Output = Choice; - #[inline] - fn bitxor(self, rhs: Choice) -> Choice { - (self.0 ^ rhs.0).into() - } -} - -impl BitXorAssign for Choice { - #[inline] - fn bitxor_assign(&mut self, rhs: Choice) { - *self = *self ^ rhs; - } -} - -impl Not for Choice { - type Output = Choice; - #[inline] - fn not(self) -> Choice { - (1u8 & (!self.0)).into() - } -} - -/// This function is a best-effort attempt to prevent the compiler from knowing -/// anything about the value of the returned `u8`, other than its type. -/// -/// Because we want to support stable Rust, we don't have access to inline -/// assembly or test::black_box, so we use the fact that volatile values will -/// never be elided to register values. -/// -/// Note: Rust's notion of "volatile" is subject to change over time. While this -/// 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. -#[cfg(not(feature = "core_hint_black_box"))] -#[inline(never)] -fn black_box(input: T) -> T { - unsafe { - // Optimization barrier - // - // 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 a `bool` instead of a `u8`. - Choice(black_box(input)) - } -} - -/// An `Eq`-like trait that produces a `Choice` instead of a `bool`. -/// -/// # Example -/// -/// ``` -/// use subtle::ConstantTimeEq; -/// let x: u8 = 5; -/// let y: u8 = 13; -/// -/// assert_eq!(x.ct_eq(&y).unwrap_u8(), 0); -/// assert_eq!(x.ct_eq(&x).unwrap_u8(), 1); -/// ``` -// -// #[inline] is specified on these function prototypes to signify that they -#[allow(unused_attributes)] // should be in the actual implementation -pub trait ConstantTimeEq { - /// Determine if two items are equal. - /// - /// The `ct_eq` function should execute in constant time. - /// - /// # Returns - /// - /// * `Choice(1u8)` if `self == other`; - /// * `Choice(0u8)` if `self != other`. - #[inline] - #[allow(unused_attributes)] - fn ct_eq(&self, other: &Self) -> Choice; - - /// Determine if two items are NOT equal. - /// - /// The `ct_ne` function should execute in constant time. - /// - /// # Returns - /// - /// * `Choice(0u8)` if `self == other`; - /// * `Choice(1u8)` if `self != other`. - #[inline] - fn ct_ne(&self, other: &Self) -> Choice { - !self.ct_eq(other) - } -} - -impl ConstantTimeEq for [T] { - /// Check whether two slices of `ConstantTimeEq` types are equal. - /// - /// # Note - /// - /// This function short-circuits if the lengths of the input slices - /// are different. Otherwise, it should execute in time independent - /// of the slice contents. - /// - /// Since arrays coerce to slices, this function works with fixed-size arrays: - /// - /// ``` - /// # use subtle::ConstantTimeEq; - /// # - /// let a: [u8; 8] = [0,1,2,3,4,5,6,7]; - /// let b: [u8; 8] = [0,1,2,3,0,1,2,3]; - /// - /// let a_eq_a = a.ct_eq(&a); - /// let a_eq_b = a.ct_eq(&b); - /// - /// assert_eq!(a_eq_a.unwrap_u8(), 1); - /// assert_eq!(a_eq_b.unwrap_u8(), 0); - /// ``` - #[inline] - fn ct_eq(&self, _rhs: &[T]) -> Choice { - let len = self.len(); - - // Short-circuit on the *lengths* of the slices, not their - // contents. - if len != _rhs.len() { - return Choice::from(0); - } - - // This loop shouldn't be shortcircuitable, since the compiler - // shouldn't be able to reason about the value of the `u8` - // unwrapped from the `ct_eq` result. - let mut x = 1u8; - for (ai, bi) in self.iter().zip(_rhs.iter()) { - x &= ai.ct_eq(bi).unwrap_u8(); - } - - x.into() - } -} - -impl ConstantTimeEq for Choice { - #[inline] - fn ct_eq(&self, rhs: &Choice) -> Choice { - !(*self ^ *rhs) - } -} - -/// Given the bit-width `$bit_width` and the corresponding primitive -/// unsigned and signed types `$t_u` and `$t_i` respectively, generate -/// an `ConstantTimeEq` implementation. -macro_rules! generate_integer_equal { - ($t_u:ty, $t_i:ty, $bit_width:expr) => { - impl ConstantTimeEq for $t_u { - #[inline] - fn ct_eq(&self, other: &$t_u) -> Choice { - // x == 0 if and only if self == other - let x: $t_u = self ^ other; - - // If x == 0, then x and -x are both equal to zero; - // otherwise, one or both will have its high bit set. - let y: $t_u = (x | x.wrapping_neg()) >> ($bit_width - 1); - - // Result is the opposite of the high bit (now shifted to low). - ((y ^ (1 as $t_u)) as u8).into() - } - } - impl ConstantTimeEq for $t_i { - #[inline] - fn ct_eq(&self, other: &$t_i) -> Choice { - // Bitcast to unsigned and call that implementation. - (*self as $t_u).ct_eq(&(*other as $t_u)) - } - } - }; -} - -generate_integer_equal!(u8, i8, 8); -generate_integer_equal!(u16, i16, 16); -generate_integer_equal!(u32, i32, 32); -generate_integer_equal!(u64, i64, 64); -#[cfg(feature = "i128")] -generate_integer_equal!(u128, i128, 128); -generate_integer_equal!(usize, isize, ::core::mem::size_of::() * 8); - -/// `Ordering` is `#[repr(i8)]` making it possible to leverage `i8::ct_eq`. -impl ConstantTimeEq for cmp::Ordering { - #[inline] - fn ct_eq(&self, other: &Self) -> Choice { - (*self as i8).ct_eq(&(*other as i8)) - } -} - -/// A type which can be conditionally selected in constant time. -/// -/// This trait also provides generic implementations of conditional -/// assignment and conditional swaps. -// -// #[inline] is specified on these function prototypes to signify that they -#[allow(unused_attributes)] // should be in the actual implementation -pub trait ConditionallySelectable: Copy { - /// Select `a` or `b` according to `choice`. - /// - /// # Returns - /// - /// * `a` if `choice == Choice(0)`; - /// * `b` if `choice == Choice(1)`. - /// - /// This function should execute in constant time. - /// - /// # Example - /// - /// ``` - /// use subtle::ConditionallySelectable; - /// # - /// # fn main() { - /// let x: u8 = 13; - /// let y: u8 = 42; - /// - /// let z = u8::conditional_select(&x, &y, 0.into()); - /// assert_eq!(z, x); - /// let z = u8::conditional_select(&x, &y, 1.into()); - /// assert_eq!(z, y); - /// # } - /// ``` - #[inline] - #[allow(unused_attributes)] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self; - - /// Conditionally assign `other` to `self`, according to `choice`. - /// - /// This function should execute in constant time. - /// - /// # Example - /// - /// ``` - /// use subtle::ConditionallySelectable; - /// # - /// # fn main() { - /// let mut x: u8 = 13; - /// let mut y: u8 = 42; - /// - /// x.conditional_assign(&y, 0.into()); - /// assert_eq!(x, 13); - /// x.conditional_assign(&y, 1.into()); - /// assert_eq!(x, 42); - /// # } - /// ``` - #[inline] - fn conditional_assign(&mut self, other: &Self, choice: Choice) { - *self = Self::conditional_select(self, other, choice); - } - - /// Conditionally swap `self` and `other` if `choice == 1`; otherwise, - /// reassign both unto themselves. - /// - /// This function should execute in constant time. - /// - /// # Example - /// - /// ``` - /// use subtle::ConditionallySelectable; - /// # - /// # fn main() { - /// let mut x: u8 = 13; - /// let mut y: u8 = 42; - /// - /// u8::conditional_swap(&mut x, &mut y, 0.into()); - /// assert_eq!(x, 13); - /// assert_eq!(y, 42); - /// u8::conditional_swap(&mut x, &mut y, 1.into()); - /// assert_eq!(x, 42); - /// assert_eq!(y, 13); - /// # } - /// ``` - #[inline] - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - let t: Self = *a; - a.conditional_assign(&b, choice); - b.conditional_assign(&t, choice); - } -} - -macro_rules! to_signed_int { - (u8) => { - i8 - }; - (u16) => { - i16 - }; - (u32) => { - i32 - }; - (u64) => { - i64 - }; - (u128) => { - i128 - }; - (i8) => { - i8 - }; - (i16) => { - i16 - }; - (i32) => { - i32 - }; - (i64) => { - i64 - }; - (i128) => { - i128 - }; -} - -macro_rules! generate_integer_conditional_select { - ($($t:tt)*) => ($( - impl ConditionallySelectable for $t { - #[inline] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - // if choice = 0, mask = (-0) = 0000...0000 - // if choice = 1, mask = (-1) = 1111...1111 - let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; - a ^ (mask & (a ^ b)) - } - - #[inline] - fn conditional_assign(&mut self, other: &Self, choice: Choice) { - // if choice = 0, mask = (-0) = 0000...0000 - // if choice = 1, mask = (-1) = 1111...1111 - let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; - *self ^= mask & (*self ^ *other); - } - - #[inline] - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - // if choice = 0, mask = (-0) = 0000...0000 - // if choice = 1, mask = (-1) = 1111...1111 - let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; - let t = mask & (*a ^ *b); - *a ^= t; - *b ^= t; - } - } - )*) -} - -generate_integer_conditional_select!( u8 i8); -generate_integer_conditional_select!( u16 i16); -generate_integer_conditional_select!( u32 i32); -generate_integer_conditional_select!( u64 i64); -#[cfg(feature = "i128")] -generate_integer_conditional_select!(u128 i128); - -/// `Ordering` is `#[repr(i8)]` where: -/// -/// - `Less` => -1 -/// - `Equal` => 0 -/// - `Greater` => 1 -/// -/// Given this, it's possible to operate on orderings as if they're integers, -/// which allows leveraging conditional masking for predication. -impl ConditionallySelectable for cmp::Ordering { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - let a = *a as i8; - let b = *b as i8; - let ret = i8::conditional_select(&a, &b, choice); - - // SAFETY: `Ordering` is `#[repr(i8)]` and `ret` has been assigned to - // a value which was originally a valid `Ordering` then cast to `i8` - unsafe { *((&ret as *const _) as *const cmp::Ordering) } - } -} - -impl ConditionallySelectable for Choice { - #[inline] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Choice(u8::conditional_select(&a.0, &b.0, choice)) - } -} - -#[cfg(feature = "const-generics")] -impl ConditionallySelectable for [T; N] -where - T: ConditionallySelectable, -{ - #[inline] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - let mut output = *a; - output.conditional_assign(b, choice); - output - } - - fn conditional_assign(&mut self, other: &Self, choice: Choice) { - for (a_i, b_i) in self.iter_mut().zip(other) { - a_i.conditional_assign(b_i, choice) - } - } -} - -/// A type which can be conditionally negated in constant time. -/// -/// # Note -/// -/// A generic implementation of `ConditionallyNegatable` is provided -/// for types `T` which are `ConditionallySelectable` and have `Neg` -/// implemented on `&T`. -// -// #[inline] is specified on these function prototypes to signify that they -#[allow(unused_attributes)] // should be in the actual implementation -pub trait ConditionallyNegatable { - /// Negate `self` if `choice == Choice(1)`; otherwise, leave it - /// unchanged. - /// - /// This function should execute in constant time. - #[inline] - #[allow(unused_attributes)] - fn conditional_negate(&mut self, choice: Choice); -} - -impl ConditionallyNegatable for T -where - T: ConditionallySelectable, - for<'a> &'a T: Neg, -{ - #[inline] - fn conditional_negate(&mut self, choice: Choice) { - // Need to cast to eliminate mutability - let self_neg: T = -(self as &T); - self.conditional_assign(&self_neg, choice); - } -} - -/// The `CtOption` type represents an optional value similar to the -/// [`Option`](core::option::Option) type but is intended for -/// use in constant time APIs. -/// -/// Any given `CtOption` is either `Some` or `None`, but unlike -/// `Option` these variants are not exposed. The -/// [`is_some()`](CtOption::is_some) method is used to determine if -/// the value is `Some`, and [`unwrap_or()`](CtOption::unwrap_or) and -/// [`unwrap_or_else()`](CtOption::unwrap_or_else) methods are -/// provided to access the underlying value. The value can also be -/// obtained with [`unwrap()`](CtOption::unwrap) but this will panic -/// if it is `None`. -/// -/// Functions that are intended to be constant time may not produce -/// valid results for all inputs, such as square root and inversion -/// operations in finite field arithmetic. Returning an `Option` -/// from these functions makes it difficult for the caller to reason -/// about the result in constant time, and returning an incorrect -/// value burdens the caller and increases the chance of bugs. -#[derive(Clone, Copy, Debug)] -pub struct CtOption { - value: T, - is_some: Choice, -} - -impl From> for Option { - /// Convert the `CtOption` wrapper into an `Option`, depending on whether - /// the underlying `is_some` `Choice` was a `0` or a `1` once unwrapped. - /// - /// # Note - /// - /// This function exists to avoid ending up with ugly, verbose and/or bad handled - /// conversions from the `CtOption` wraps to an `Option` or `Result`. - /// This implementation doesn't intend to be constant-time nor try to protect the - /// leakage of the `T` since the `Option` will do it anyways. - fn from(source: CtOption) -> Option { - if source.is_some().unwrap_u8() == 1u8 { - Option::Some(source.value) - } else { - None - } - } -} - -impl CtOption { - /// This method is used to construct a new `CtOption` and takes - /// a value of type `T`, and a `Choice` that determines whether - /// the optional value should be `Some` or not. If `is_some` is - /// false, the value will still be stored but its value is never - /// exposed. - #[inline] - pub fn new(value: T, is_some: Choice) -> CtOption { - CtOption { - value: value, - is_some: is_some, - } - } - - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - pub fn expect(self, msg: &str) -> T { - assert_eq!(self.is_some.unwrap_u8(), 1, "{}", msg); - - self.value - } - - /// This returns the underlying value but panics if it - /// is not `Some`. - #[inline] - pub fn unwrap(self) -> T { - assert_eq!(self.is_some.unwrap_u8(), 1); - - self.value - } - - /// This returns the underlying value if it is `Some` - /// or the provided value otherwise. - #[inline] - pub fn unwrap_or(self, def: T) -> T - where - T: ConditionallySelectable, - { - T::conditional_select(&def, &self.value, self.is_some) - } - - /// This returns the underlying value if it is `Some` - /// or the value produced by the provided closure otherwise. - /// - /// This operates in constant time, because the provided closure - /// is always called. - #[inline] - pub fn unwrap_or_else(self, f: F) -> T - where - T: ConditionallySelectable, - F: FnOnce() -> T, - { - T::conditional_select(&f(), &self.value, self.is_some) - } - - /// Returns a true `Choice` if this value is `Some`. - #[inline] - pub fn is_some(&self) -> Choice { - self.is_some - } - - /// Returns a true `Choice` if this value is `None`. - #[inline] - pub fn is_none(&self) -> Choice { - !self.is_some - } - - /// Returns a `None` value if the option is `None`, otherwise - /// returns a `CtOption` enclosing the value of the provided closure. - /// The closure is given the enclosed value or, if the option is - /// `None`, it is provided a dummy value computed using - /// `Default::default()`. - /// - /// This operates in constant time, because the provided closure - /// is always called. - #[inline] - pub fn map(self, f: F) -> CtOption - where - T: Default + ConditionallySelectable, - F: FnOnce(T) -> U, - { - CtOption::new( - f(T::conditional_select( - &T::default(), - &self.value, - self.is_some, - )), - self.is_some, - ) - } - - /// Returns a `None` value if the option is `None`, otherwise - /// returns the result of the provided closure. The closure is - /// given the enclosed value or, if the option is `None`, it - /// is provided a dummy value computed using `Default::default()`. - /// - /// This operates in constant time, because the provided closure - /// is always called. - #[inline] - pub fn and_then(self, f: F) -> CtOption - where - T: Default + ConditionallySelectable, - F: FnOnce(T) -> CtOption, - { - let mut tmp = f(T::conditional_select( - &T::default(), - &self.value, - self.is_some, - )); - tmp.is_some &= self.is_some; - - tmp - } - - /// Returns `self` if it contains a value, and otherwise returns the result of - /// calling `f`. The provided function `f` is always called. - #[inline] - pub fn or_else(self, f: F) -> CtOption - where - T: ConditionallySelectable, - F: FnOnce() -> CtOption, - { - let is_none = self.is_none(); - let f = f(); - - Self::conditional_select(&self, &f, is_none) - } - - /// Convert the `CtOption` wrapper into an `Option`, depending on whether - /// the underlying `is_some` `Choice` was a `0` or a `1` once unwrapped. - /// - /// # Note - /// - /// This function exists to avoid ending up with ugly, verbose and/or bad handled - /// conversions from the `CtOption` wraps to an `Option` or `Result`. - /// This implementation doesn't intend to be constant-time nor try to protect the - /// leakage of the `T` since the `Option` will do it anyways. - /// - /// It's equivalent to the corresponding `From` impl, however this version is - /// friendlier for type inference. - pub fn into_option(self) -> Option { - self.into() - } -} - -impl ConditionallySelectable for CtOption { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - CtOption::new( - T::conditional_select(&a.value, &b.value, choice), - Choice::conditional_select(&a.is_some, &b.is_some, choice), - ) - } -} - -impl ConstantTimeEq for CtOption { - /// Two `CtOption`s are equal if they are both `Some` and - /// their values are equal, or both `None`. - #[inline] - fn ct_eq(&self, rhs: &CtOption) -> Choice { - let a = self.is_some(); - let b = rhs.is_some(); - - (a & b & self.value.ct_eq(&rhs.value)) | (!a & !b) - } -} - -/// A type which can be compared in some manner and be determined to be greater -/// than another of the same type. -pub trait ConstantTimeGreater { - /// Determine whether `self > other`. - /// - /// The bitwise-NOT of the return value of this function should be usable to - /// determine if `self <= other`. - /// - /// This function should execute in constant time. - /// - /// # Returns - /// - /// A `Choice` with a set bit if `self > other`, and with no set bits - /// otherwise. - /// - /// # Example - /// - /// ``` - /// use subtle::ConstantTimeGreater; - /// - /// let x: u8 = 13; - /// let y: u8 = 42; - /// - /// let x_gt_y = x.ct_gt(&y); - /// - /// assert_eq!(x_gt_y.unwrap_u8(), 0); - /// - /// let y_gt_x = y.ct_gt(&x); - /// - /// assert_eq!(y_gt_x.unwrap_u8(), 1); - /// - /// let x_gt_x = x.ct_gt(&x); - /// - /// assert_eq!(x_gt_x.unwrap_u8(), 0); - /// ``` - fn ct_gt(&self, other: &Self) -> Choice; -} - -macro_rules! generate_unsigned_integer_greater { - ($t_u: ty, $bit_width: expr) => { - impl ConstantTimeGreater for $t_u { - /// Returns Choice::from(1) iff x > y, and Choice::from(0) iff x <= y. - /// - /// # Note - /// - /// This algoritm would also work for signed integers if we first - /// flip the top bit, e.g. `let x: u8 = x ^ 0x80`, etc. - #[inline] - fn ct_gt(&self, other: &$t_u) -> Choice { - let gtb = self & !other; // All the bits in self that are greater than their corresponding bits in other. - let mut ltb = !self & other; // All the bits in self that are less than their corresponding bits in other. - let mut pow = 1; - - // Less-than operator is okay here because it's dependent on the bit-width. - while pow < $bit_width { - ltb |= ltb >> pow; // Bit-smear the highest set bit to the right. - pow += pow; - } - let mut bit = gtb & !ltb; // Select the highest set bit. - let mut pow = 1; - - while pow < $bit_width { - bit |= bit >> pow; // Shift it to the right until we end up with either 0 or 1. - pow += pow; - } - // XXX We should possibly do the above flattening to 0 or 1 in the - // Choice constructor rather than making it a debug error? - Choice::from((bit & 1) as u8) - } - } - }; -} - -generate_unsigned_integer_greater!(u8, 8); -generate_unsigned_integer_greater!(u16, 16); -generate_unsigned_integer_greater!(u32, 32); -generate_unsigned_integer_greater!(u64, 64); -#[cfg(feature = "i128")] -generate_unsigned_integer_greater!(u128, 128); - -impl ConstantTimeGreater for cmp::Ordering { - #[inline] - fn ct_gt(&self, other: &Self) -> Choice { - // No impl of `ConstantTimeGreater` for `i8`, so use `u8` - let a = (*self as i8) + 1; - let b = (*other as i8) + 1; - (a as u8).ct_gt(&(b as u8)) - } -} - -/// A type which can be compared in some manner and be determined to be less -/// than another of the same type. -pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater { - /// Determine whether `self < other`. - /// - /// The bitwise-NOT of the return value of this function should be usable to - /// determine if `self >= other`. - /// - /// A default implementation is provided and implemented for the unsigned - /// integer types. - /// - /// This function should execute in constant time. - /// - /// # Returns - /// - /// A `Choice` with a set bit if `self < other`, and with no set bits - /// otherwise. - /// - /// # Example - /// - /// ``` - /// use subtle::ConstantTimeLess; - /// - /// let x: u8 = 13; - /// let y: u8 = 42; - /// - /// let x_lt_y = x.ct_lt(&y); - /// - /// assert_eq!(x_lt_y.unwrap_u8(), 1); - /// - /// let y_lt_x = y.ct_lt(&x); - /// - /// assert_eq!(y_lt_x.unwrap_u8(), 0); - /// - /// let x_lt_x = x.ct_lt(&x); - /// - /// assert_eq!(x_lt_x.unwrap_u8(), 0); - /// ``` - #[inline] - fn ct_lt(&self, other: &Self) -> Choice { - !self.ct_gt(other) & !self.ct_eq(other) - } -} - -impl ConstantTimeLess for u8 {} -impl ConstantTimeLess for u16 {} -impl ConstantTimeLess for u32 {} -impl ConstantTimeLess for u64 {} -#[cfg(feature = "i128")] -impl ConstantTimeLess for u128 {} - -impl ConstantTimeLess for cmp::Ordering { - #[inline] - fn ct_lt(&self, other: &Self) -> Choice { - // No impl of `ConstantTimeLess` for `i8`, so use `u8` - let a = (*self as i8) + 1; - let b = (*other as i8) + 1; - (a as u8).ct_lt(&(b as u8)) - } -} - /// Wrapper type which implements an optimization barrier for all accesses. #[derive(Clone, Copy, Debug)] pub struct BlackBox(T); @@ -1006,3 +118,15 @@ impl BlackBox { black_box(self.0) } } + +#[cfg(test)] +mod tests { + use crate::BlackBox; + + #[test] + fn black_box_round_trip() { + let n = 42u64; + let black_box = BlackBox::new(n); + assert_eq!(n, black_box.get()); + } +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..7b6f280 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,791 @@ +use crate::Choice; +use core::cmp; +use core::ops::Neg; + +/// An `Eq`-like trait that produces a `Choice` instead of a `bool`. +/// +/// # Example +/// +/// ``` +/// use subtle::ConstantTimeEq; +/// let x: u8 = 5; +/// let y: u8 = 13; +/// +/// assert_eq!(x.ct_eq(&y).unwrap_u8(), 0); +/// assert_eq!(x.ct_eq(&x).unwrap_u8(), 1); +/// ``` +// +// #[inline] is specified on these function prototypes to signify that they +#[allow(unused_attributes)] // should be in the actual implementation +pub trait ConstantTimeEq { + /// Determine if two items are equal. + /// + /// The `ct_eq` function should execute in constant time. + /// + /// # Returns + /// + /// * `Choice(1u8)` if `self == other`; + /// * `Choice(0u8)` if `self != other`. + #[inline] + #[allow(unused_attributes)] + fn ct_eq(&self, other: &Self) -> Choice; + + /// Determine if two items are NOT equal. + /// + /// The `ct_ne` function should execute in constant time. + /// + /// # Returns + /// + /// * `Choice(0u8)` if `self == other`; + /// * `Choice(1u8)` if `self != other`. + #[inline] + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } +} + +impl ConstantTimeEq for [T] { + /// Check whether two slices of `ConstantTimeEq` types are equal. + /// + /// # Note + /// + /// This function short-circuits if the lengths of the input slices + /// are different. Otherwise, it should execute in time independent + /// of the slice contents. + /// + /// Since arrays coerce to slices, this function works with fixed-size arrays: + /// + /// ``` + /// # use subtle::ConstantTimeEq; + /// # + /// let a: [u8; 8] = [0,1,2,3,4,5,6,7]; + /// let b: [u8; 8] = [0,1,2,3,0,1,2,3]; + /// + /// let a_eq_a = a.ct_eq(&a); + /// let a_eq_b = a.ct_eq(&b); + /// + /// assert_eq!(a_eq_a.unwrap_u8(), 1); + /// assert_eq!(a_eq_b.unwrap_u8(), 0); + /// ``` + #[inline] + fn ct_eq(&self, _rhs: &[T]) -> Choice { + let len = self.len(); + + // Short-circuit on the *lengths* of the slices, not their + // contents. + if len != _rhs.len() { + return Choice::from(0); + } + + // This loop shouldn't be shortcircuitable, since the compiler + // shouldn't be able to reason about the value of the `u8` + // unwrapped from the `ct_eq` result. + let mut x = 1u8; + for (ai, bi) in self.iter().zip(_rhs.iter()) { + x &= ai.ct_eq(bi).unwrap_u8(); + } + + x.into() + } +} + +/// Given the bit-width `$bit_width` and the corresponding primitive +/// unsigned and signed types `$t_u` and `$t_i` respectively, generate +/// an `ConstantTimeEq` implementation. +macro_rules! generate_integer_equal { + ($t_u:ty, $t_i:ty, $bit_width:expr) => { + impl ConstantTimeEq for $t_u { + #[inline] + fn ct_eq(&self, other: &$t_u) -> Choice { + // x == 0 if and only if self == other + let x: $t_u = self ^ other; + + // If x == 0, then x and -x are both equal to zero; + // otherwise, one or both will have its high bit set. + let y: $t_u = (x | x.wrapping_neg()) >> ($bit_width - 1); + + // Result is the opposite of the high bit (now shifted to low). + ((y ^ (1 as $t_u)) as u8).into() + } + } + impl ConstantTimeEq for $t_i { + #[inline] + fn ct_eq(&self, other: &$t_i) -> Choice { + // Bitcast to unsigned and call that implementation. + (*self as $t_u).ct_eq(&(*other as $t_u)) + } + } + }; +} + +generate_integer_equal!(u8, i8, 8); +generate_integer_equal!(u16, i16, 16); +generate_integer_equal!(u32, i32, 32); +generate_integer_equal!(u64, i64, 64); +generate_integer_equal!(u128, i128, 128); +generate_integer_equal!(usize, isize, ::core::mem::size_of::() * 8); + +/// `Ordering` is `#[repr(i8)]` making it possible to leverage `i8::ct_eq`. +impl ConstantTimeEq for cmp::Ordering { + #[inline] + fn ct_eq(&self, other: &Self) -> Choice { + (*self as i8).ct_eq(&(*other as i8)) + } +} + +/// A type which can be conditionally selected in constant time. +/// +/// This trait also provides generic implementations of conditional +/// assignment and conditional swaps. +// +// #[inline] is specified on these function prototypes to signify that they +#[allow(unused_attributes)] // should be in the actual implementation +pub trait ConditionallySelectable: Copy { + /// Select `a` or `b` according to `choice`. + /// + /// # Returns + /// + /// * `a` if `choice == Choice(0)`; + /// * `b` if `choice == Choice(1)`. + /// + /// This function should execute in constant time. + /// + /// # Example + /// + /// ``` + /// use subtle::ConditionallySelectable; + /// # + /// # fn main() { + /// let x: u8 = 13; + /// let y: u8 = 42; + /// + /// let z = u8::conditional_select(&x, &y, 0.into()); + /// assert_eq!(z, x); + /// let z = u8::conditional_select(&x, &y, 1.into()); + /// assert_eq!(z, y); + /// # } + /// ``` + #[inline] + #[allow(unused_attributes)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self; + + /// Conditionally assign `other` to `self`, according to `choice`. + /// + /// This function should execute in constant time. + /// + /// # Example + /// + /// ``` + /// use subtle::ConditionallySelectable; + /// # + /// # fn main() { + /// let mut x: u8 = 13; + /// let mut y: u8 = 42; + /// + /// x.conditional_assign(&y, 0.into()); + /// assert_eq!(x, 13); + /// x.conditional_assign(&y, 1.into()); + /// assert_eq!(x, 42); + /// # } + /// ``` + #[inline] + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + *self = Self::conditional_select(self, other, choice); + } + + /// Conditionally swap `self` and `other` if `choice == 1`; otherwise, + /// reassign both unto themselves. + /// + /// This function should execute in constant time. + /// + /// # Example + /// + /// ``` + /// use subtle::ConditionallySelectable; + /// # + /// # fn main() { + /// let mut x: u8 = 13; + /// let mut y: u8 = 42; + /// + /// u8::conditional_swap(&mut x, &mut y, 0.into()); + /// assert_eq!(x, 13); + /// assert_eq!(y, 42); + /// u8::conditional_swap(&mut x, &mut y, 1.into()); + /// assert_eq!(x, 42); + /// assert_eq!(y, 13); + /// # } + /// ``` + #[inline] + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + let t: Self = *a; + a.conditional_assign(b, choice); + b.conditional_assign(&t, choice); + } +} + +macro_rules! to_signed_int { + (u8) => { + i8 + }; + (u16) => { + i16 + }; + (u32) => { + i32 + }; + (u64) => { + i64 + }; + (u128) => { + i128 + }; + (i8) => { + i8 + }; + (i16) => { + i16 + }; + (i32) => { + i32 + }; + (i64) => { + i64 + }; + (i128) => { + i128 + }; +} + +macro_rules! generate_integer_conditional_select { + ($($t:tt)*) => ($( + impl ConditionallySelectable for $t { + #[inline] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + // if choice = 0, mask = (-0) = 0000...0000 + // if choice = 1, mask = (-1) = 1111...1111 + let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; + a ^ (mask & (a ^ b)) + } + + #[inline] + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + // if choice = 0, mask = (-0) = 0000...0000 + // if choice = 1, mask = (-1) = 1111...1111 + let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; + *self ^= mask & (*self ^ *other); + } + + #[inline] + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + // if choice = 0, mask = (-0) = 0000...0000 + // if choice = 1, mask = (-1) = 1111...1111 + let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t; + let t = mask & (*a ^ *b); + *a ^= t; + *b ^= t; + } + } + )*) +} + +generate_integer_conditional_select!( u8 i8); +generate_integer_conditional_select!( u16 i16); +generate_integer_conditional_select!( u32 i32); +generate_integer_conditional_select!( u64 i64); +generate_integer_conditional_select!(u128 i128); + +/// `Ordering` is `#[repr(i8)]` where: +/// +/// - `Less` => -1 +/// - `Equal` => 0 +/// - `Greater` => 1 +/// +/// Given this, it's possible to operate on orderings as if they're integers, +/// which allows leveraging conditional masking for predication. +impl ConditionallySelectable for cmp::Ordering { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let a = *a as i8; + let b = *b as i8; + let ret = i8::conditional_select(&a, &b, choice); + + // SAFETY: `Ordering` is `#[repr(i8)]` and `ret` has been assigned to + // a value which was originally a valid `Ordering` then cast to `i8` + unsafe { *((&ret as *const _) as *const cmp::Ordering) } + } +} + +impl ConditionallySelectable for [T; N] +where + T: ConditionallySelectable, +{ + #[inline] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut output = *a; + output.conditional_assign(b, choice); + output + } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + for (a_i, b_i) in self.iter_mut().zip(other) { + a_i.conditional_assign(b_i, choice) + } + } +} + +/// A type which can be conditionally negated in constant time. +/// +/// # Note +/// +/// A generic implementation of `ConditionallyNegatable` is provided +/// for types `T` which are `ConditionallySelectable` and have `Neg` +/// implemented on `&T`. +// +// #[inline] is specified on these function prototypes to signify that they +#[allow(unused_attributes)] // should be in the actual implementation +pub trait ConditionallyNegatable { + /// Negate `self` if `choice == Choice(1)`; otherwise, leave it + /// unchanged. + /// + /// This function should execute in constant time. + #[inline] + #[allow(unused_attributes)] + fn conditional_negate(&mut self, choice: Choice); +} + +impl ConditionallyNegatable for T +where + T: ConditionallySelectable, + for<'a> &'a T: Neg, +{ + #[inline] + fn conditional_negate(&mut self, choice: Choice) { + // Need to cast to eliminate mutability + let self_neg: T = -(self as &T); + self.conditional_assign(&self_neg, choice); + } +} + +/// A type which can be compared in some manner and be determined to be greater +/// than another of the same type. +pub trait ConstantTimeGreater { + /// Determine whether `self > other`. + /// + /// The bitwise-NOT of the return value of this function should be usable to + /// determine if `self <= other`. + /// + /// This function should execute in constant time. + /// + /// # Returns + /// + /// A `Choice` with a set bit if `self > other`, and with no set bits + /// otherwise. + /// + /// # Example + /// + /// ``` + /// use subtle::ConstantTimeGreater; + /// + /// let x: u8 = 13; + /// let y: u8 = 42; + /// + /// let x_gt_y = x.ct_gt(&y); + /// + /// assert_eq!(x_gt_y.unwrap_u8(), 0); + /// + /// let y_gt_x = y.ct_gt(&x); + /// + /// assert_eq!(y_gt_x.unwrap_u8(), 1); + /// + /// let x_gt_x = x.ct_gt(&x); + /// + /// assert_eq!(x_gt_x.unwrap_u8(), 0); + /// ``` + fn ct_gt(&self, other: &Self) -> Choice; +} + +macro_rules! generate_unsigned_integer_greater { + ($t_u: ty, $bit_width: expr) => { + impl ConstantTimeGreater for $t_u { + /// Returns Choice::from(1) iff x > y, and Choice::from(0) iff x <= y. + /// + /// # Note + /// + /// This algoritm would also work for signed integers if we first + /// flip the top bit, e.g. `let x: u8 = x ^ 0x80`, etc. + #[inline] + fn ct_gt(&self, other: &$t_u) -> Choice { + let gtb = self & !other; // All the bits in self that are greater than their corresponding bits in other. + let mut ltb = !self & other; // All the bits in self that are less than their corresponding bits in other. + let mut pow = 1; + + // Less-than operator is okay here because it's dependent on the bit-width. + while pow < $bit_width { + ltb |= ltb >> pow; // Bit-smear the highest set bit to the right. + pow += pow; + } + let mut bit = gtb & !ltb; // Select the highest set bit. + let mut pow = 1; + + while pow < $bit_width { + bit |= bit >> pow; // Shift it to the right until we end up with either 0 or 1. + pow += pow; + } + // XXX We should possibly do the above flattening to 0 or 1 in the + // Choice constructor rather than making it a debug error? + Choice::from((bit & 1) as u8) + } + } + }; +} + +generate_unsigned_integer_greater!(u8, 8); +generate_unsigned_integer_greater!(u16, 16); +generate_unsigned_integer_greater!(u32, 32); +generate_unsigned_integer_greater!(u64, 64); +generate_unsigned_integer_greater!(u128, 128); + +impl ConstantTimeGreater for cmp::Ordering { + #[inline] + fn ct_gt(&self, other: &Self) -> Choice { + // No impl of `ConstantTimeGreater` for `i8`, so use `u8` + let a = (*self as i8) + 1; + let b = (*other as i8) + 1; + (a as u8).ct_gt(&(b as u8)) + } +} + +/// A type which can be compared in some manner and be determined to be less +/// than another of the same type. +pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater { + /// Determine whether `self < other`. + /// + /// The bitwise-NOT of the return value of this function should be usable to + /// determine if `self >= other`. + /// + /// A default implementation is provided and implemented for the unsigned + /// integer types. + /// + /// This function should execute in constant time. + /// + /// # Returns + /// + /// A `Choice` with a set bit if `self < other`, and with no set bits + /// otherwise. + /// + /// # Example + /// + /// ``` + /// use subtle::ConstantTimeLess; + /// + /// let x: u8 = 13; + /// let y: u8 = 42; + /// + /// let x_lt_y = x.ct_lt(&y); + /// + /// assert_eq!(x_lt_y.unwrap_u8(), 1); + /// + /// let y_lt_x = y.ct_lt(&x); + /// + /// assert_eq!(y_lt_x.unwrap_u8(), 0); + /// + /// let x_lt_x = x.ct_lt(&x); + /// + /// assert_eq!(x_lt_x.unwrap_u8(), 0); + /// ``` + #[inline] + fn ct_lt(&self, other: &Self) -> Choice { + !self.ct_gt(other) & !self.ct_eq(other) + } +} + +impl ConstantTimeLess for u8 {} +impl ConstantTimeLess for u16 {} +impl ConstantTimeLess for u32 {} +impl ConstantTimeLess for u64 {} +impl ConstantTimeLess for u128 {} + +impl ConstantTimeLess for cmp::Ordering { + #[inline] + fn ct_lt(&self, other: &Self) -> Choice { + // No impl of `ConstantTimeLess` for `i8`, so use `u8` + let a = (*self as i8) + 1; + let b = (*other as i8) + 1; + (a as u8).ct_lt(&(b as u8)) + } +} + +#[cfg(test)] +mod tests { + use super::{ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; + use core::cmp; + use rand::{RngCore, TryRngCore, rngs::OsRng}; + + #[test] + #[should_panic] + fn slices_equal_different_lengths() { + let a: [u8; 3] = [0, 0, 0]; + let b: [u8; 4] = [0, 0, 0, 0]; + + assert_eq!(a.ct_eq(&b).unwrap_u8(), 1); + } + + #[test] + fn slices_equal() { + let a: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + let b: [u8; 8] = [1, 2, 3, 4, 4, 3, 2, 1]; + + let a_eq_a = a.ct_eq(&a); + let a_eq_b = a.ct_eq(&b); + + assert_eq!(a_eq_a.unwrap_u8(), 1); + assert_eq!(a_eq_b.unwrap_u8(), 0); + + let c: [u8; 16] = [0u8; 16]; + + let a_eq_c = a.ct_eq(&c); + assert_eq!(a_eq_c.unwrap_u8(), 0); + } + + #[test] + fn conditional_assign_i32() { + let mut a: i32 = 5; + let b: i32 = 13; + + a.conditional_assign(&b, 0.into()); + assert_eq!(a, 5); + a.conditional_assign(&b, 1.into()); + assert_eq!(a, 13); + } + + #[test] + fn conditional_assign_i64() { + let mut c: i64 = 2343249123; + let d: i64 = 8723884895; + + c.conditional_assign(&d, 0.into()); + assert_eq!(c, 2343249123); + c.conditional_assign(&d, 1.into()); + assert_eq!(c, 8723884895); + } + + macro_rules! generate_integer_conditional_select_tests { + ($($t:ty),*) => ($( + let x: $t = 0; // all 0 bits + let y: $t = !0; // all 1 bits + + assert_eq!(<$t>::conditional_select(&x, &y, 0.into()), x); + assert_eq!(<$t>::conditional_select(&x, &y, 1.into()), y); + + let mut z = x; + let mut w = y; + + <$t>::conditional_swap(&mut z, &mut w, 0.into()); + assert_eq!(z, x); + assert_eq!(w, y); + <$t>::conditional_swap(&mut z, &mut w, 1.into()); + assert_eq!(z, y); + assert_eq!(w, x); + + z.conditional_assign(&x, 1.into()); + w.conditional_assign(&y, 0.into()); + assert_eq!(z, x); + assert_eq!(w, x); + )*) +} + + #[test] + fn integer_conditional_select() { + generate_integer_conditional_select_tests!(u8, u16, u32, u64, i128); + generate_integer_conditional_select_tests!(i8, i16, i32, i64, u128); + } + + #[test] + fn custom_conditional_select_i16() { + let x: i16 = 257; + let y: i16 = 514; + + assert_eq!(i16::conditional_select(&x, &y, 0.into()), 257); + assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514); + } + + #[test] + fn ordering_conditional_select() { + assert_eq!( + cmp::Ordering::conditional_select( + &cmp::Ordering::Less, + &cmp::Ordering::Greater, + 0.into() + ), + cmp::Ordering::Less + ); + + assert_eq!( + cmp::Ordering::conditional_select( + &cmp::Ordering::Less, + &cmp::Ordering::Greater, + 1.into() + ), + cmp::Ordering::Greater + ); + } + + macro_rules! generate_integer_equal_tests { + ($($t:ty),*) => ($( + let y: $t = 0; // all 0 bits + let z: $t = !0; // all 1 bits + + let x = z; + + assert_eq!(x.ct_eq(&y).unwrap_u8(), 0); + assert_eq!(x.ct_eq(&z).unwrap_u8(), 1); + assert_eq!(x.ct_ne(&y).unwrap_u8(), 1); + assert_eq!(x.ct_ne(&z).unwrap_u8(), 0); + )*) +} + + #[test] + fn integer_equal() { + generate_integer_equal_tests!(u8, u16, u32, u64, u128, usize); + generate_integer_equal_tests!(i8, i16, i32, i64, i128, isize); + } + + #[test] + fn ordering_equal() { + let a = cmp::Ordering::Equal; + let b = cmp::Ordering::Greater; + let c = a; + + assert_eq!(a.ct_eq(&b).unwrap_u8(), 0); + assert_eq!(a.ct_eq(&c).unwrap_u8(), 1); + } + + macro_rules! generate_greater_than_test { + ($ty: ty) => { + for _ in 0..100 { + let mut rng = OsRng.unwrap_err(); + let x = rng.next_u64() as $ty; + let y = rng.next_u64() as $ty; + let z = x.ct_gt(&y); + + if x < y { + assert!(z.unwrap_u8() == 0); + } else if x == y { + assert!(z.unwrap_u8() == 0); + } else if x > y { + assert!(z.unwrap_u8() == 1); + } + } + }; + } + + #[test] + fn greater_than_u8() { + generate_greater_than_test!(u8); + } + + #[test] + fn greater_than_u16() { + generate_greater_than_test!(u16); + } + + #[test] + fn greater_than_u32() { + generate_greater_than_test!(u32); + } + + #[test] + fn greater_than_u64() { + generate_greater_than_test!(u64); + } + + #[test] + fn greater_than_u128() { + generate_greater_than_test!(u128); + } + + #[test] + fn greater_than_ordering() { + assert_eq!( + cmp::Ordering::Less + .ct_gt(&cmp::Ordering::Greater) + .unwrap_u8(), + 0 + ); + assert_eq!( + cmp::Ordering::Greater + .ct_gt(&cmp::Ordering::Less) + .unwrap_u8(), + 1 + ); + } + + #[test] + /// Test that the two's compliment min and max, i.e. 0000...0001 < 1111...1110, + /// 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)); + + assert!(z.unwrap_u8() == 1); + } + + macro_rules! generate_less_than_test { + ($ty: ty) => { + for _ in 0..100 { + let mut rng = OsRng.unwrap_err(); + let x = rng.next_u64() as $ty; + let y = rng.next_u64() as $ty; + let z = x.ct_gt(&y); + + if x < y { + assert!(z.unwrap_u8() == 0); + } else if x == y { + assert!(z.unwrap_u8() == 0); + } else if x > y { + assert!(z.unwrap_u8() == 1); + } + } + }; + } + + #[test] + fn less_than_u8() { + generate_less_than_test!(u8); + } + + #[test] + fn less_than_u16() { + generate_less_than_test!(u16); + } + + #[test] + fn less_than_u32() { + generate_less_than_test!(u32); + } + + #[test] + fn less_than_u64() { + generate_less_than_test!(u64); + } + + #[test] + fn less_than_u128() { + generate_less_than_test!(u128); + } + + #[test] + fn less_than_ordering() { + assert_eq!( + cmp::Ordering::Greater + .ct_lt(&cmp::Ordering::Less) + .unwrap_u8(), + 0 + ); + assert_eq!( + cmp::Ordering::Less + .ct_lt(&cmp::Ordering::Greater) + .unwrap_u8(), + 1 + ); + } +} diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index 888b9d0..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1,432 +0,0 @@ -use std::cmp; - -use rand::rngs::OsRng; -use rand::RngCore; - -use subtle::*; - -#[test] -#[should_panic] -fn slices_equal_different_lengths() { - let a: [u8; 3] = [0, 0, 0]; - let b: [u8; 4] = [0, 0, 0, 0]; - - assert_eq!((&a).ct_eq(&b).unwrap_u8(), 1); -} - -#[test] -fn slices_equal() { - let a: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; - let b: [u8; 8] = [1, 2, 3, 4, 4, 3, 2, 1]; - - let a_eq_a = (&a).ct_eq(&a); - let a_eq_b = (&a).ct_eq(&b); - - assert_eq!(a_eq_a.unwrap_u8(), 1); - assert_eq!(a_eq_b.unwrap_u8(), 0); - - let c: [u8; 16] = [0u8; 16]; - - let a_eq_c = (&a).ct_eq(&c); - assert_eq!(a_eq_c.unwrap_u8(), 0); -} - -#[test] -fn conditional_assign_i32() { - let mut a: i32 = 5; - let b: i32 = 13; - - a.conditional_assign(&b, 0.into()); - assert_eq!(a, 5); - a.conditional_assign(&b, 1.into()); - assert_eq!(a, 13); -} - -#[test] -fn conditional_assign_i64() { - let mut c: i64 = 2343249123; - let d: i64 = 8723884895; - - c.conditional_assign(&d, 0.into()); - assert_eq!(c, 2343249123); - c.conditional_assign(&d, 1.into()); - assert_eq!(c, 8723884895); -} - -macro_rules! generate_integer_conditional_select_tests { - ($($t:ty)*) => ($( - let x: $t = 0; // all 0 bits - let y: $t = !0; // all 1 bits - - assert_eq!(<$t>::conditional_select(&x, &y, 0.into()), x); - assert_eq!(<$t>::conditional_select(&x, &y, 1.into()), y); - - let mut z = x; - let mut w = y; - - <$t>::conditional_swap(&mut z, &mut w, 0.into()); - assert_eq!(z, x); - assert_eq!(w, y); - <$t>::conditional_swap(&mut z, &mut w, 1.into()); - assert_eq!(z, y); - assert_eq!(w, x); - - z.conditional_assign(&x, 1.into()); - w.conditional_assign(&y, 0.into()); - assert_eq!(z, x); - assert_eq!(w, x); - )*) -} - -#[test] -fn integer_conditional_select() { - generate_integer_conditional_select_tests!(u8 u16 u32 u64); - generate_integer_conditional_select_tests!(i8 i16 i32 i64); - #[cfg(feature = "i128")] - generate_integer_conditional_select_tests!(i128 u128); -} - -#[test] -fn custom_conditional_select_i16() { - let x: i16 = 257; - let y: i16 = 514; - - assert_eq!(i16::conditional_select(&x, &y, 0.into()), 257); - assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514); -} - -#[test] -fn ordering_conditional_select() { - assert_eq!( - cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()), - cmp::Ordering::Less - ); - - assert_eq!( - cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()), - cmp::Ordering::Greater - ); -} - -macro_rules! generate_integer_equal_tests { - ($($t:ty),*) => ($( - let y: $t = 0; // all 0 bits - let z: $t = !0; // all 1 bits - - let x = z; - - assert_eq!(x.ct_eq(&y).unwrap_u8(), 0); - assert_eq!(x.ct_eq(&z).unwrap_u8(), 1); - assert_eq!(x.ct_ne(&y).unwrap_u8(), 1); - assert_eq!(x.ct_ne(&z).unwrap_u8(), 0); - )*) -} - -#[test] -fn integer_equal() { - generate_integer_equal_tests!(u8, u16, u32, u64); - generate_integer_equal_tests!(i8, i16, i32, i64); - #[cfg(feature = "i128")] - generate_integer_equal_tests!(i128, u128); - generate_integer_equal_tests!(isize, usize); -} - -#[test] -fn choice_into_bool() { - let choice_true: bool = Choice::from(1).into(); - - assert!(choice_true); - - let choice_false: bool = Choice::from(0).into(); - - assert!(!choice_false); -} - -#[test] -fn conditional_select_choice() { - let t = Choice::from(1); - let f = Choice::from(0); - - assert_eq!(bool::from(Choice::conditional_select(&t, &f, f)), true); - assert_eq!(bool::from(Choice::conditional_select(&t, &f, t)), false); - assert_eq!(bool::from(Choice::conditional_select(&f, &t, f)), false); - assert_eq!(bool::from(Choice::conditional_select(&f, &t, t)), true); -} - -#[test] -fn choice_equal() { - assert!(Choice::from(0).ct_eq(&Choice::from(0)).unwrap_u8() == 1); - assert!(Choice::from(0).ct_eq(&Choice::from(1)).unwrap_u8() == 0); - assert!(Choice::from(1).ct_eq(&Choice::from(0)).unwrap_u8() == 0); - assert!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8() == 1); -} - -#[test] -fn ordering_equal() { - let a = cmp::Ordering::Equal; - let b = cmp::Ordering::Greater; - let c = a; - - assert_eq!(a.ct_eq(&b).unwrap_u8(), 0); - assert_eq!(a.ct_eq(&c).unwrap_u8(), 1); -} - -#[test] -fn test_ctoption() { - let a = CtOption::new(10, Choice::from(1)); - let b = CtOption::new(9, Choice::from(1)); - let c = CtOption::new(10, Choice::from(0)); - let d = CtOption::new(9, Choice::from(0)); - - // Test is_some / is_none - assert!(bool::from(a.is_some())); - assert!(bool::from(!a.is_none())); - assert!(bool::from(b.is_some())); - assert!(bool::from(!b.is_none())); - assert!(bool::from(!c.is_some())); - assert!(bool::from(c.is_none())); - assert!(bool::from(!d.is_some())); - assert!(bool::from(d.is_none())); - - // Test unwrap for Some - assert_eq!(a.unwrap(), 10); - assert_eq!(b.unwrap(), 9); - - // Test equality - assert!(bool::from(a.ct_eq(&a))); - assert!(bool::from(!a.ct_eq(&b))); - assert!(bool::from(!a.ct_eq(&c))); - assert!(bool::from(!a.ct_eq(&d))); - - // Test equality of None with different - // dummy value - assert!(bool::from(c.ct_eq(&d))); - - // Test unwrap_or - assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or(2), 1); - assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or(2), 2); - - // Test unwrap_or_else - assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or_else(|| 2), 1); - assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or_else(|| 2), 2); - - // Test map - assert_eq!( - CtOption::new(1, Choice::from(1)) - .map(|v| { - assert_eq!(v, 1); - 2 - }) - .unwrap(), - 2 - ); - assert_eq!( - CtOption::new(1, Choice::from(0)) - .map(|_| 2) - .is_none() - .unwrap_u8(), - 1 - ); - - // Test and_then - assert_eq!( - CtOption::new(1, Choice::from(1)) - .and_then(|v| { - assert_eq!(v, 1); - CtOption::new(2, Choice::from(0)) - }) - .is_none() - .unwrap_u8(), - 1 - ); - assert_eq!( - CtOption::new(1, Choice::from(1)) - .and_then(|v| { - assert_eq!(v, 1); - CtOption::new(2, Choice::from(1)) - }) - .unwrap(), - 2 - ); - - assert_eq!( - CtOption::new(1, Choice::from(0)) - .and_then(|_| CtOption::new(2, Choice::from(0))) - .is_none() - .unwrap_u8(), - 1 - ); - assert_eq!( - CtOption::new(1, Choice::from(0)) - .and_then(|_| CtOption::new(2, Choice::from(1))) - .is_none() - .unwrap_u8(), - 1 - ); - - // Test or_else - assert_eq!( - CtOption::new(1, Choice::from(0)) - .or_else(|| CtOption::new(2, Choice::from(1))) - .unwrap(), - 2 - ); - assert_eq!( - CtOption::new(1, Choice::from(1)) - .or_else(|| CtOption::new(2, Choice::from(0))) - .unwrap(), - 1 - ); - assert_eq!( - CtOption::new(1, Choice::from(1)) - .or_else(|| CtOption::new(2, Choice::from(1))) - .unwrap(), - 1 - ); - assert!(bool::from( - CtOption::new(1, Choice::from(0)) - .or_else(|| CtOption::new(2, Choice::from(0))) - .is_none() - )); - - // 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); -} - -#[test] -#[should_panic] -fn unwrap_none_ctoption() { - // This test might fail (in release mode?) if the - // compiler decides to optimize it away. - CtOption::new(10, Choice::from(0)).unwrap(); -} - -macro_rules! generate_greater_than_test { - ($ty: ty) => { - for _ in 0..100 { - let x = OsRng.next_u64() as $ty; - let y = OsRng.next_u64() as $ty; - let z = x.ct_gt(&y); - - println!("x={}, y={}, z={:?}", x, y, z); - - if x < y { - assert!(z.unwrap_u8() == 0); - } else if x == y { - assert!(z.unwrap_u8() == 0); - } else if x > y { - assert!(z.unwrap_u8() == 1); - } - } - } -} - -#[test] -fn greater_than_u8() { - generate_greater_than_test!(u8); -} - -#[test] -fn greater_than_u16() { - generate_greater_than_test!(u16); -} - -#[test] -fn greater_than_u32() { - generate_greater_than_test!(u32); -} - -#[test] -fn greater_than_u64() { - generate_greater_than_test!(u64); -} - -#[cfg(feature = "i128")] -#[test] -fn greater_than_u128() { - generate_greater_than_test!(u128); -} - -#[test] -fn greater_than_ordering() { - assert_eq!(cmp::Ordering::Less.ct_gt(&cmp::Ordering::Greater).unwrap_u8(), 0); - assert_eq!(cmp::Ordering::Greater.ct_gt(&cmp::Ordering::Less).unwrap_u8(), 1); -} - -#[test] -/// Test that the two's compliment min and max, i.e. 0000...0001 < 1111...1110, -/// 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)); - - assert!(z.unwrap_u8() == 1); -} - -macro_rules! generate_less_than_test { - ($ty: ty) => { - for _ in 0..100 { - let x = OsRng.next_u64() as $ty; - let y = OsRng.next_u64() as $ty; - let z = x.ct_gt(&y); - - println!("x={}, y={}, z={:?}", x, y, z); - - if x < y { - assert!(z.unwrap_u8() == 0); - } else if x == y { - assert!(z.unwrap_u8() == 0); - } else if x > y { - assert!(z.unwrap_u8() == 1); - } - } - } -} - -#[test] -fn less_than_u8() { - generate_less_than_test!(u8); -} - -#[test] -fn less_than_u16() { - generate_less_than_test!(u16); -} - -#[test] -fn less_than_u32() { - generate_less_than_test!(u32); -} - -#[test] -fn less_than_u64() { - generate_less_than_test!(u64); -} - -#[cfg(feature = "i128")] -#[test] -fn less_than_u128() { - generate_less_than_test!(u128); -} - -#[test] -fn less_than_ordering() { - assert_eq!(cmp::Ordering::Greater.ct_lt(&cmp::Ordering::Less).unwrap_u8(), 0); - assert_eq!(cmp::Ordering::Less.ct_lt(&cmp::Ordering::Greater).unwrap_u8(), 1); -} - -#[test] -fn black_box_round_trip() { - let n = 42u64; - let black_box = BlackBox::new(n); - assert_eq!(n, black_box.get()); -}