Skip to content

Commit 3a8e7ca

Browse files
committed
Add ConstantTimeSelect and ConstantTimeClone traits
`ConstantTimeSelect` is intended as a replacement to `ConditionallySelectable`, which is preserved but deprecated. It replaces the previous `Copy` bound with a bound on a new `ConstantTimeClone` marker trait, which allows the trait to be impl'd for heap-allocated types. No existing impls of `ConditionallySelectable` have been removed, however a blanket impl of `ConstantTimeSelect` for `T: ConditionallySelectable` has been added, allowing the two traits to interoperate and for `ConstantTimeSelect` to work on all types which currently impl `ConditionallySelectable`. `ConstantTimeClone` likewise has a blanket impl for all types which impl `Copy`. `CtOption`'s combinator methods have been changed to bound on `ConstantTimeSelect` which unlocks using them with heap-allocated types, which otherwise is a major limitation. In theory these changes are all backwards compatible due to the blanket impl, which should allow all types which previously worked to continue to do so. Closes dalek-cryptography#63, dalek-cryptography#94
1 parent 6b6a81a commit 3a8e7ca

File tree

2 files changed

+153
-54
lines changed

2 files changed

+153
-54
lines changed

src/lib.rs

+125-26
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ impl From<u8> for Choice {
258258
}
259259
}
260260

261+
/// Marker trait for types whose [`Clone`] impl operates in constant-time.
262+
pub trait ConstantTimeClone: Clone {}
263+
264+
impl<T: Copy> ConstantTimeClone for T {}
265+
261266
/// An `Eq`-like trait that produces a `Choice` instead of a `bool`.
262267
///
263268
/// # Example
@@ -397,6 +402,89 @@ impl ConstantTimeEq for cmp::Ordering {
397402
///
398403
/// This trait also provides generic implementations of conditional
399404
/// assignment and conditional swaps.
405+
pub trait ConstantTimeSelect: ConstantTimeClone {
406+
/// Select `a` or `b` according to `choice`.
407+
///
408+
/// # Returns
409+
///
410+
/// * `a` if `choice == Choice(0)`;
411+
/// * `b` if `choice == Choice(1)`.
412+
///
413+
/// This function should execute in constant time.
414+
///
415+
/// # Example
416+
///
417+
/// ```
418+
/// use subtle::ConstantTimeSelect;
419+
/// #
420+
/// # fn main() {
421+
/// let x: u8 = 13;
422+
/// let y: u8 = 42;
423+
///
424+
/// let z = u8::ct_select(&x, &y, 0.into());
425+
/// assert_eq!(z, x);
426+
/// let z = u8::ct_select(&x, &y, 1.into());
427+
/// assert_eq!(z, y);
428+
/// # }
429+
/// ```
430+
fn ct_select(a: &Self, b: &Self, choice: Choice) -> Self;
431+
432+
/// Conditionally assign `other` to `self`, according to `choice`.
433+
///
434+
/// This function should execute in constant time.
435+
///
436+
/// # Example
437+
///
438+
/// ```
439+
/// use subtle::ConstantTimeSelect;
440+
/// #
441+
/// # fn main() {
442+
/// let mut x: u8 = 13;
443+
/// let mut y: u8 = 42;
444+
///
445+
/// x.ct_assign(&y, 0.into());
446+
/// assert_eq!(x, 13);
447+
/// x.ct_assign(&y, 1.into());
448+
/// assert_eq!(x, 42);
449+
/// # }
450+
/// ```
451+
#[inline]
452+
fn ct_assign(&mut self, other: &Self, choice: Choice) {
453+
*self = Self::ct_select(self, other, choice);
454+
}
455+
456+
/// Conditionally swap `self` and `other` if `choice == 1`; otherwise,
457+
/// reassign both unto themselves.
458+
///
459+
/// This function should execute in constant time.
460+
///
461+
/// # Example
462+
///
463+
/// ```
464+
/// use subtle::ConstantTimeSelect;
465+
/// #
466+
/// # fn main() {
467+
/// let mut x: u8 = 13;
468+
/// let mut y: u8 = 42;
469+
///
470+
/// u8::ct_swap(&mut x, &mut y, 0.into());
471+
/// assert_eq!(x, 13);
472+
/// assert_eq!(y, 42);
473+
/// u8::ct_swap(&mut x, &mut y, 1.into());
474+
/// assert_eq!(x, 42);
475+
/// assert_eq!(y, 13);
476+
/// # }
477+
/// ```
478+
#[inline]
479+
fn ct_swap(a: &mut Self, b: &mut Self, choice: Choice) {
480+
let t: Self = a.clone();
481+
a.ct_assign(&b, choice);
482+
b.ct_assign(&t, choice);
483+
}
484+
}
485+
486+
/// Deprecated legacy equivalent of [`ConstantTimeSelect`]: please migrate to the new trait.
487+
#[deprecated(since = "2.6.0", note = "use ConstantTimeSelect instead")]
400488
pub trait ConditionallySelectable: Copy {
401489
/// Select `a` or `b` according to `choice`.
402490
///
@@ -422,7 +510,6 @@ pub trait ConditionallySelectable: Copy {
422510
/// assert_eq!(z, y);
423511
/// # }
424512
/// ```
425-
#[inline]
426513
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self;
427514

428515
/// Conditionally assign `other` to `self`, according to `choice`.
@@ -479,6 +566,24 @@ pub trait ConditionallySelectable: Copy {
479566
}
480567
}
481568

569+
#[allow(deprecated)]
570+
impl<T: ConditionallySelectable> ConstantTimeSelect for T {
571+
#[inline]
572+
fn ct_select(a: &Self, b: &Self, choice: Choice) -> Self {
573+
Self::conditional_select(a, b, choice)
574+
}
575+
576+
#[inline]
577+
fn ct_assign(&mut self, other: &Self, choice: Choice) {
578+
Self::conditional_assign(self, other, choice)
579+
}
580+
581+
#[inline]
582+
fn ct_swap(a: &mut Self, b: &mut Self, choice: Choice) {
583+
Self::conditional_swap(a, b, choice)
584+
}
585+
}
586+
482587
macro_rules! to_signed_int {
483588
(u8) => {
484589
i8
@@ -514,6 +619,7 @@ macro_rules! to_signed_int {
514619

515620
macro_rules! generate_integer_conditional_select {
516621
($($t:tt)*) => ($(
622+
#[allow(deprecated)]
517623
impl ConditionallySelectable for $t {
518624
#[inline]
519625
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
@@ -559,6 +665,7 @@ generate_integer_conditional_select!(u128 i128);
559665
///
560666
/// Given this, it's possible to operate on orderings as if they're integers,
561667
/// which allows leveraging conditional masking for predication.
668+
#[allow(deprecated)]
562669
impl ConditionallySelectable for cmp::Ordering {
563670
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
564671
let a = *a as i8;
@@ -571,6 +678,7 @@ impl ConditionallySelectable for cmp::Ordering {
571678
}
572679
}
573680

681+
#[allow(deprecated)]
574682
impl ConditionallySelectable for Choice {
575683
#[inline]
576684
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
@@ -579,6 +687,7 @@ impl ConditionallySelectable for Choice {
579687
}
580688

581689
#[cfg(feature = "const-generics")]
690+
#[allow(deprecated)]
582691
impl<T, const N: usize> ConditionallySelectable for [T; N]
583692
where
584693
T: ConditionallySelectable,
@@ -613,6 +722,7 @@ pub trait ConditionallyNegatable {
613722
fn conditional_negate(&mut self, choice: Choice);
614723
}
615724

725+
#[allow(deprecated)]
616726
impl<T> ConditionallyNegatable for T
617727
where
618728
T: ConditionallySelectable,
@@ -710,9 +820,9 @@ impl<T> CtOption<T> {
710820
#[inline]
711821
pub fn unwrap_or(self, def: T) -> T
712822
where
713-
T: ConditionallySelectable,
823+
T: ConstantTimeSelect,
714824
{
715-
T::conditional_select(&def, &self.value, self.is_some)
825+
T::ct_select(&def, &self.value, self.is_some)
716826
}
717827

718828
/// This returns the underlying value if it is `Some`
@@ -723,10 +833,10 @@ impl<T> CtOption<T> {
723833
#[inline]
724834
pub fn unwrap_or_else<F>(self, f: F) -> T
725835
where
726-
T: ConditionallySelectable,
836+
T: ConstantTimeSelect,
727837
F: FnOnce() -> T,
728838
{
729-
T::conditional_select(&f(), &self.value, self.is_some)
839+
T::ct_select(&f(), &self.value, self.is_some)
730840
}
731841

732842
/// Returns a true `Choice` if this value is `Some`.
@@ -752,17 +862,9 @@ impl<T> CtOption<T> {
752862
#[inline]
753863
pub fn map<U, F>(self, f: F) -> CtOption<U>
754864
where
755-
T: Default + ConditionallySelectable,
756865
F: FnOnce(T) -> U,
757866
{
758-
CtOption::new(
759-
f(T::conditional_select(
760-
&T::default(),
761-
&self.value,
762-
self.is_some,
763-
)),
764-
self.is_some,
765-
)
867+
CtOption::new(f(self.value), self.is_some)
766868
}
767869

768870
/// Returns a `None` value if the option is `None`, otherwise
@@ -775,34 +877,31 @@ impl<T> CtOption<T> {
775877
#[inline]
776878
pub fn and_then<U, F>(self, f: F) -> CtOption<U>
777879
where
778-
T: Default + ConditionallySelectable,
779880
F: FnOnce(T) -> CtOption<U>,
780881
{
781-
let mut tmp = f(T::conditional_select(
782-
&T::default(),
783-
&self.value,
784-
self.is_some,
785-
));
786-
tmp.is_some &= self.is_some;
787-
788-
tmp
882+
let mut ret = f(self.value);
883+
ret.is_some &= self.is_some;
884+
ret
789885
}
790886

791887
/// Returns `self` if it contains a value, and otherwise returns the result of
792888
/// calling `f`. The provided function `f` is always called.
793889
#[inline]
794890
pub fn or_else<F>(self, f: F) -> CtOption<T>
795891
where
796-
T: ConditionallySelectable,
892+
T: ConstantTimeSelect,
797893
F: FnOnce() -> CtOption<T>,
798894
{
799-
let is_none = self.is_none();
895+
let mut is_some = self.is_some();
800896
let f = f();
801897

802-
Self::conditional_select(&self, &f, is_none)
898+
let value = T::ct_select(&f.value, &self.value, is_some);
899+
is_some |= f.is_some();
900+
CtOption::new(value, is_some)
803901
}
804902
}
805903

904+
#[allow(deprecated)]
806905
impl<T: ConditionallySelectable> ConditionallySelectable for CtOption<T> {
807906
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
808907
CtOption::new(

tests/mod.rs

+28-28
Original file line numberDiff line numberDiff line change
@@ -32,78 +32,78 @@ fn slices_equal() {
3232
}
3333

3434
#[test]
35-
fn conditional_assign_i32() {
35+
fn ct_assign_i32() {
3636
let mut a: i32 = 5;
3737
let b: i32 = 13;
3838

39-
a.conditional_assign(&b, 0.into());
39+
a.ct_assign(&b, 0.into());
4040
assert_eq!(a, 5);
41-
a.conditional_assign(&b, 1.into());
41+
a.ct_assign(&b, 1.into());
4242
assert_eq!(a, 13);
4343
}
4444

4545
#[test]
46-
fn conditional_assign_i64() {
46+
fn ct_assign_i64() {
4747
let mut c: i64 = 2343249123;
4848
let d: i64 = 8723884895;
4949

50-
c.conditional_assign(&d, 0.into());
50+
c.ct_assign(&d, 0.into());
5151
assert_eq!(c, 2343249123);
52-
c.conditional_assign(&d, 1.into());
52+
c.ct_assign(&d, 1.into());
5353
assert_eq!(c, 8723884895);
5454
}
5555

56-
macro_rules! generate_integer_conditional_select_tests {
56+
macro_rules! generate_integer_ct_select_tests {
5757
($($t:ty)*) => ($(
5858
let x: $t = 0; // all 0 bits
5959
let y: $t = !0; // all 1 bits
6060

61-
assert_eq!(<$t>::conditional_select(&x, &y, 0.into()), 0);
62-
assert_eq!(<$t>::conditional_select(&x, &y, 1.into()), y);
61+
assert_eq!(<$t>::ct_select(&x, &y, 0.into()), 0);
62+
assert_eq!(<$t>::ct_select(&x, &y, 1.into()), y);
6363

6464
let mut z = x;
6565
let mut w = y;
6666

67-
<$t>::conditional_swap(&mut z, &mut w, 0.into());
67+
<$t>::ct_swap(&mut z, &mut w, 0.into());
6868
assert_eq!(z, x);
6969
assert_eq!(w, y);
70-
<$t>::conditional_swap(&mut z, &mut w, 1.into());
70+
<$t>::ct_swap(&mut z, &mut w, 1.into());
7171
assert_eq!(z, y);
7272
assert_eq!(w, x);
7373

74-
z.conditional_assign(&x, 1.into());
75-
w.conditional_assign(&y, 0.into());
74+
z.ct_assign(&x, 1.into());
75+
w.ct_assign(&y, 0.into());
7676
assert_eq!(z, x);
7777
assert_eq!(w, x);
7878
)*)
7979
}
8080

8181
#[test]
82-
fn integer_conditional_select() {
83-
generate_integer_conditional_select_tests!(u8 u16 u32 u64);
84-
generate_integer_conditional_select_tests!(i8 i16 i32 i64);
82+
fn integer_ct_select() {
83+
generate_integer_ct_select_tests!(u8 u16 u32 u64);
84+
generate_integer_ct_select_tests!(i8 i16 i32 i64);
8585
#[cfg(feature = "i128")]
86-
generate_integer_conditional_select_tests!(i128 u128);
86+
generate_integer_ct_select_tests!(i128 u128);
8787
}
8888

8989
#[test]
90-
fn custom_conditional_select_i16() {
90+
fn custom_ct_select_i16() {
9191
let x: i16 = 257;
9292
let y: i16 = 514;
9393

94-
assert_eq!(i16::conditional_select(&x, &y, 0.into()), 257);
95-
assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514);
94+
assert_eq!(i16::ct_select(&x, &y, 0.into()), 257);
95+
assert_eq!(i16::ct_select(&x, &y, 1.into()), 514);
9696
}
9797

9898
#[test]
99-
fn ordering_conditional_select() {
99+
fn ordering_ct_select() {
100100
assert_eq!(
101-
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()),
101+
cmp::Ordering::ct_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()),
102102
cmp::Ordering::Less
103103
);
104104

105105
assert_eq!(
106-
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()),
106+
cmp::Ordering::ct_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()),
107107
cmp::Ordering::Greater
108108
);
109109
}
@@ -143,14 +143,14 @@ fn choice_into_bool() {
143143
}
144144

145145
#[test]
146-
fn conditional_select_choice() {
146+
fn ct_select_choice() {
147147
let t = Choice::from(1);
148148
let f = Choice::from(0);
149149

150-
assert_eq!(bool::from(Choice::conditional_select(&t, &f, f)), true);
151-
assert_eq!(bool::from(Choice::conditional_select(&t, &f, t)), false);
152-
assert_eq!(bool::from(Choice::conditional_select(&f, &t, f)), false);
153-
assert_eq!(bool::from(Choice::conditional_select(&f, &t, t)), true);
150+
assert_eq!(bool::from(Choice::ct_select(&t, &f, f)), true);
151+
assert_eq!(bool::from(Choice::ct_select(&t, &f, t)), false);
152+
assert_eq!(bool::from(Choice::ct_select(&f, &t, f)), false);
153+
assert_eq!(bool::from(Choice::ct_select(&f, &t, t)), true);
154154
}
155155

156156
#[test]

0 commit comments

Comments
 (0)