From c0d3cfc3b76cbba259971e8d5ed5f96dfa06333a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 26 May 2017 21:56:11 +0000 Subject: [PATCH 1/4] Whitespace fix in subtle module. From 4ecf6ab326700569bff39be12a01e02b11ab8646 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 26 May 2017 22:37:36 +0000 Subject: [PATCH 2/4] Implement constant-time selection between two things. --- Cargo.toml | 6 ++- src/lib.rs | 4 ++ src/subtle.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) 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..7c98510 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,6 +65,72 @@ 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 + + One + + Copy + + Signed + + Sub + + BitAnd + + BitOr + + Not +{ + (!(choice - T::one()) & a) | ((choice - T::one()) & b) +} + /// Check equality of two bytes in constant time. /// /// # Return @@ -163,4 +244,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); + } } From 43481a9ff65135736ced32cb7cac4e8c5891b008 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 28 May 2017 02:35:48 +0000 Subject: [PATCH 3/4] Change the whitespace because Boats made fun of it on twitter. --- src/subtle.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/subtle.rs b/src/subtle.rs index 7c98510..a81b3e2 100644 --- a/src/subtle.rs +++ b/src/subtle.rs @@ -118,16 +118,9 @@ impl CTNegatable for T #[inline(always)] #[cfg(feature = "std")] pub fn conditional_select(a: T, b: T, choice: T) -> T - where T: PartialEq + - PartialOrd + - One + - Copy + - Signed + - Sub + - BitAnd + - BitOr + - Not -{ + where T: PartialEq + PartialOrd + Copy + + One + Signed + Sub + Not + + BitAnd + BitOr { (!(choice - T::one()) & a) | ((choice - T::one()) & b) } From 161c0cd96dc9ca65c7643063aedac3c256d53f36 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 29 May 2017 00:56:57 +0000 Subject: [PATCH 4/4] Add a doctest for subtle::bytes_equal(). --- src/subtle.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/subtle.rs b/src/subtle.rs index a81b3e2..b29b635 100644 --- a/src/subtle.rs +++ b/src/subtle.rs @@ -129,6 +129,20 @@ pub fn conditional_select(a: T, b: T, choice: T) -> T /// # 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;