Skip to content

Commit c67166b

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 #63, #94
1 parent 6b6a81a commit c67166b

File tree

2 files changed

+128
-26
lines changed

2 files changed

+128
-26
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

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// TODO(tarcieri): test non-deprecated API
2+
#![allow(deprecated)]
3+
14
use std::cmp;
25

36
use rand::rngs::OsRng;

0 commit comments

Comments
 (0)