diff --git a/Cargo.toml b/Cargo.toml index 500dffb..3ca54f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,10 @@ version = "0.4" # same version that digest depends on version = "^0.6" +[dependencies.num-traits] +optional = true +version = "^0.1" + [dev-dependencies.sha2] version = "0.4" @@ -45,7 +49,7 @@ version = "0.6" [features] nightly = ["radix_51"] default = ["std"] -std = ["rand"] +std = ["rand", "num-traits"] yolocrypto = [] bench = [] # Radix-51 arithmetic using u128 diff --git a/src/lib.rs b/src/lib.rs index 408d7ff..ac8522e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![cfg_attr(not(feature = "std"), feature(collections))] #![cfg_attr(feature = "nightly", feature(i128_type))] #![cfg_attr(feature = "bench", feature(test))] +#![cfg_attr(all(feature = "nightly", feature = "std"), feature(zero_one))] #![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing @@ -58,6 +59,9 @@ extern crate core; #[cfg(feature = "std")] extern crate rand; +#[cfg(feature = "std")] +extern crate num_traits; + #[cfg(not(feature = "std"))] extern crate collections; diff --git a/src/subtle.rs b/src/subtle.rs index 5d024bc..b29b635 100644 --- a/src/subtle.rs +++ b/src/subtle.rs @@ -11,8 +11,23 @@ //! Constant-time traits and utility functions. +#[cfg(feature = "std")] +use core::ops::BitAnd; +#[cfg(feature = "std")] +use core::ops::BitOr; +#[cfg(feature = "std")] +use core::ops::Not; +#[cfg(feature = "std")] +use core::ops::Sub; + use core::ops::Neg; +#[cfg(feature = "std")] +use num_traits::One; +#[cfg(feature = "std")] +use num_traits::Signed; + + /// Trait for items which can be conditionally assigned in constant time. pub trait CTAssignable { /// If `choice == 1u8`, assign `other` to `self`. @@ -50,11 +65,84 @@ impl CTNegatable for T } } +/// Select `a` if `choice == 1` or select `b` if `choice == 0`, in constant time. +/// +/// # Inputs +/// +/// * `a`, `b`, and `choice` must be types for which bitwise-AND, and +/// bitwise-OR, bitwise-complement, subtraction, multiplicative identity, +/// copying, partial equality, and partial order comparison are defined. +/// * `choice`: If `choice` is equal to the multiplicative identity of the type +/// (i.e. `1u8` for `u8`, etc.), then `a` is returned. If `choice` is equal +/// to the additive identity (i.e. `0u8` for `u8`, etc.) then `b` is returned. +/// +/// # Warning +/// +/// The behaviour of this function is undefined if `choice` is something other +/// than a multiplicative identity or additive identity (i.e. `1u8` or `0u8`). +/// +/// If you somehow manage to design a type which is not a signed integer, and +/// yet implements all the requisite trait bounds for this generic, it's your +/// problem if something breaks. +/// +/// # Examples +/// +/// This function should work for signed integer types: +/// +/// ``` +/// # extern crate curve25519_dalek; +/// # use curve25519_dalek::subtle::conditional_select; +/// # fn main() { +/// let a: i32 = 5; +/// let b: i32 = 13; +/// +/// assert!(conditional_select(a, b, 0) == 13); +/// assert!(conditional_select(a, b, 1) == 5); +/// +/// let c: i64 = 2343249123; +/// let d: i64 = 8723884895; +/// +/// assert!(conditional_select(c, d, 0) == d); +/// assert!(conditional_select(c, d, 1) == c); +/// # } +/// ``` +/// +/// It does not work with `i128`s, however, because the `num` crate doesn't +/// implement `num::traits::Signed` for `i128`. +/// +/// # TODO +/// +/// Once `#[feature(specialization)]` is finished, we should rewrite this. Or +/// find some other way to only implement it for types which we know work +/// correctly. +#[inline(always)] +#[cfg(feature = "std")] +pub fn conditional_select(a: T, b: T, choice: T) -> T + where T: PartialEq + PartialOrd + Copy + + One + Signed + Sub + Not + + BitAnd + BitOr { + (!(choice - T::one()) & a) | ((choice - T::one()) & b) +} + /// Check equality of two bytes in constant time. /// /// # Return /// /// Returns `1u8` if `a == b` and `0u8` otherwise. +/// +/// # Examples +/// +/// ``` +/// # extern crate curve25519_dalek; +/// # use curve25519_dalek::subtle::bytes_equal; +/// # fn main() { +/// let a: u8 = 0xDE; +/// let b: u8 = 0xAD; +/// +/// assert_eq!(bytes_equal(a, b), 0); +/// assert_eq!(bytes_equal(a, a), 1); +/// # } +/// ``` #[inline(always)] pub fn bytes_equal(a: u8, b: u8) -> u8 { let mut x: u8; @@ -163,4 +251,24 @@ mod test { assert!(arrays_equal(&a, &b) == 1); } + + #[test] + #[cfg(feature = "std")] + fn conditional_select_i32() { + let a: i32 = 5; + let b: i32 = 13; + + assert_eq!(conditional_select(a, b, 0), 13); + assert_eq!(conditional_select(a, b, 1), 5); + } + + #[test] + #[cfg(feature = "std")] + fn conditional_select_i64() { + let c: i64 = 2343249123; + let d: i64 = 8723884895; + + assert_eq!(conditional_select(c, d, 0), d); + assert_eq!(conditional_select(c, d, 1), c); + } }