Implement constant-time selection between two things.

This commit is contained in:
Isis Lovecruft 2017-05-26 22:37:36 +00:00
parent c0d3cfc3b7
commit 4ecf6ab326
Failed to extract signature
3 changed files with 110 additions and 1 deletions

View file

@ -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

View file

@ -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;

View file

@ -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<T> 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<T>(a: T, b: T, choice: T) -> T
where T: PartialEq +
PartialOrd +
One +
Copy +
Signed +
Sub<T, Output = T> +
BitAnd<T, Output = T> +
BitOr<T, Output = T> +
Not<Output = T>
{
(!(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);
}
}