Skip to content

Commit 18241c2

Browse files
committed
[WIP] subtle: breaking changes for a hypothetical v3.0
This commit contains a minimal set of proposed breaking changes, as an alternative to trying to make additive changes which may be "subtly" breaking: - Add `ConstantTimeClone` trait and use it as `ConditionallySelectable`'s supertrait bound (alternative to #118): this makes it possible to implement `ConditionallySelectable` on heap-allocated types (fixes #94) - Remove `Default` bound on `CtOption::map` and `CtOption::and_then`: allows using these methods with types that don't impl `Default` and avoids potential timing sidechannels if there might be timing variability in `Default` types (fixes #63)
1 parent f1f8e53 commit 18241c2

File tree

2 files changed

+26
-29
lines changed

2 files changed

+26
-29
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name = "subtle"
55
# - update html_root_url
66
# - update README if necessary by semver
77
# - if any updates were made to the README, also update the module documentation in src/lib.rs
8-
version = "2.6.0"
8+
version = "3.0.0-pre"
99
edition = "2018"
1010
authors = ["Isis Lovecruft <[email protected]>",
1111
"Henry de Valence <[email protected]>"]

src/lib.rs

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl Choice {
126126
///
127127
/// This function only exists as an **escape hatch** for the rare case
128128
/// where it's not possible to use one of the `subtle`-provided
129+
/// where it's not possible to use one of the `subtle`-provided
129130
/// trait impls.
130131
///
131132
/// **To convert a `Choice` to a `bool`, use the `From` implementation instead.**
@@ -383,14 +384,19 @@ impl ConstantTimeEq for cmp::Ordering {
383384
}
384385
}
385386

387+
/// Marker trait for types whose [`Clone`] impl operates in constant-time.
388+
pub trait ConstantTimeClone: Clone {}
389+
390+
impl<T: Copy> ConstantTimeClone for T {}
391+
386392
/// A type which can be conditionally selected in constant time.
387393
///
388394
/// This trait also provides generic implementations of conditional
389395
/// assignment and conditional swaps.
390396
//
391397
// #[inline] is specified on these function prototypes to signify that they
392398
#[allow(unused_attributes)] // should be in the actual implementation
393-
pub trait ConditionallySelectable: Copy {
399+
pub trait ConditionallySelectable: ConstantTimeClone {
394400
/// Select `a` or `b` according to `choice`.
395401
///
396402
/// # Returns
@@ -467,7 +473,7 @@ pub trait ConditionallySelectable: Copy {
467473
/// ```
468474
#[inline]
469475
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
470-
let t: Self = *a;
476+
let t: Self = a.clone();
471477
a.conditional_assign(&b, choice);
472478
b.conditional_assign(&t, choice);
473479
}
@@ -575,7 +581,7 @@ impl ConditionallySelectable for Choice {
575581
#[cfg(feature = "const-generics")]
576582
impl<T, const N: usize> ConditionallySelectable for [T; N]
577583
where
578-
T: ConditionallySelectable,
584+
T: ConditionallySelectable + Copy,
579585
{
580586
#[inline]
581587
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
@@ -741,49 +747,34 @@ impl<T> CtOption<T> {
741747

742748
/// Returns a `None` value if the option is `None`, otherwise
743749
/// returns a `CtOption` enclosing the value of the provided closure.
744-
/// The closure is given the enclosed value or, if the option is
745-
/// `None`, it is provided a dummy value computed using
746-
/// `Default::default()`.
750+
/// The closure is given the enclosed value.
747751
///
748752
/// This operates in constant time, because the provided closure
749753
/// is always called.
750754
#[inline]
751755
pub fn map<U, F>(self, f: F) -> CtOption<U>
752756
where
753-
T: Default + ConditionallySelectable,
757+
T: ConditionallySelectable,
754758
F: FnOnce(T) -> U,
755759
{
756-
CtOption::new(
757-
f(T::conditional_select(
758-
&T::default(),
759-
&self.value,
760-
self.is_some,
761-
)),
762-
self.is_some,
763-
)
760+
CtOption::new(f(self.value), self.is_some)
764761
}
765762

766763
/// Returns a `None` value if the option is `None`, otherwise
767764
/// returns the result of the provided closure. The closure is
768-
/// given the enclosed value or, if the option is `None`, it
769-
/// is provided a dummy value computed using `Default::default()`.
765+
/// given the enclosed value.
770766
///
771767
/// This operates in constant time, because the provided closure
772768
/// is always called.
773769
#[inline]
774770
pub fn and_then<U, F>(self, f: F) -> CtOption<U>
775771
where
776-
T: Default + ConditionallySelectable,
772+
T: ConditionallySelectable,
777773
F: FnOnce(T) -> CtOption<U>,
778774
{
779-
let mut tmp = f(T::conditional_select(
780-
&T::default(),
781-
&self.value,
782-
self.is_some,
783-
));
784-
tmp.is_some &= self.is_some;
785-
786-
tmp
775+
let mut ret = f(self.value);
776+
ret.is_some &= self.is_some;
777+
ret
787778
}
788779

789780
/// Returns `self` if it contains a value, and otherwise returns the result of
@@ -797,7 +788,10 @@ impl<T> CtOption<T> {
797788
let is_none = self.is_none();
798789
let f = f();
799790

800-
Self::conditional_select(&self, &f, is_none)
791+
CtOption::new(
792+
T::conditional_select(&self.value, &f.value, is_none),
793+
Choice::conditional_select(&self.is_some, &f.is_some, is_none),
794+
)
801795
}
802796

803797
/// Convert the `CtOption<T>` wrapper into an `Option<T>`, depending on whether
@@ -817,7 +811,10 @@ impl<T> CtOption<T> {
817811
}
818812
}
819813

820-
impl<T: ConditionallySelectable> ConditionallySelectable for CtOption<T> {
814+
impl<T> ConditionallySelectable for CtOption<T>
815+
where
816+
T: ConditionallySelectable + Copy
817+
{
821818
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
822819
CtOption::new(
823820
T::conditional_select(&a.value, &b.value, choice),

0 commit comments

Comments
 (0)