1
1
// -*- mode: rust; -*-
2
2
//
3
3
// This file is part of subtle, part of the dalek cryptography project.
4
- // Copyright (c) 2016-2018 isis lovecruft, Henry de Valence
4
+ // Copyright (c) 2016-2022 isis lovecruft, Henry de Valence
5
5
// See LICENSE for licensing information.
6
6
//
7
7
// Authors:
87
87
#[ macro_use]
88
88
extern crate std;
89
89
90
+ #[ cfg( test) ]
91
+ extern crate rand;
92
+
93
+ use core:: cmp:: Ordering ;
90
94
use core:: ops:: { BitAnd , BitAndAssign , BitOr , BitOrAssign , BitXor , BitXorAssign , Neg , Not } ;
91
95
use core:: option:: Option ;
92
96
@@ -111,6 +115,11 @@ use core::option::Option;
111
115
pub struct Choice ( u8 ) ;
112
116
113
117
impl Choice {
118
+ /// Create an instance in `const` context.
119
+ pub const fn of_bool ( of : bool ) -> Self {
120
+ Self ( of as u8 )
121
+ }
122
+
114
123
/// Unwrap the `Choice` wrapper to reveal the underlying `u8`.
115
124
///
116
125
/// # Note
@@ -236,7 +245,7 @@ impl From<u8> for Choice {
236
245
}
237
246
}
238
247
239
- /// An `Eq`-like trait that produces a `Choice` instead of a `bool`.
248
+ /// An [ `Eq`] -like trait that produces a `Choice` instead of a `bool`.
240
249
///
241
250
/// # Example
242
251
///
@@ -257,7 +266,6 @@ pub trait ConstantTimeEq {
257
266
///
258
267
/// * `Choice(1u8)` if `self == other`;
259
268
/// * `Choice(0u8)` if `self != other`.
260
- #[ inline]
261
269
fn ct_eq ( & self , other : & Self ) -> Choice ;
262
270
}
263
271
@@ -380,7 +388,6 @@ pub trait ConditionallySelectable: Copy {
380
388
/// assert_eq!(z, y);
381
389
/// # }
382
390
/// ```
383
- #[ inline]
384
391
fn conditional_select ( a : & Self , b : & Self , choice : Choice ) -> Self ;
385
392
386
393
/// Conditionally assign `other` to `self`, according to `choice`.
@@ -530,7 +537,6 @@ pub trait ConditionallyNegatable {
530
537
/// unchanged.
531
538
///
532
539
/// This function should execute in constant time.
533
- #[ inline]
534
540
fn conditional_negate ( & mut self , choice : Choice ) ;
535
541
}
536
542
@@ -801,7 +807,7 @@ macro_rules! generate_unsigned_integer_greater {
801
807
Choice :: from( ( bit & 1 ) as u8 )
802
808
}
803
809
}
804
- }
810
+ } ;
805
811
}
806
812
807
813
generate_unsigned_integer_greater ! ( u8 , 8 ) ;
@@ -813,7 +819,7 @@ generate_unsigned_integer_greater!(u128, 128);
813
819
814
820
/// A type which can be compared in some manner and be determined to be less
815
821
/// than another of the same type.
816
- pub trait ConstantTimeLess : ConstantTimeEq + ConstantTimeGreater {
822
+ pub trait ConstantTimeLess : ConstantTimeGreater {
817
823
/// Determine whether `self < other`.
818
824
///
819
825
/// The bitwise-NOT of the return value of this function should be usable to
@@ -852,7 +858,7 @@ pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
852
858
/// ```
853
859
#[ inline]
854
860
fn ct_lt ( & self , other : & Self ) -> Choice {
855
- ! self . ct_gt ( other ) & ! self . ct_eq ( other )
861
+ other . ct_gt ( self )
856
862
}
857
863
}
858
864
@@ -862,3 +868,146 @@ impl ConstantTimeLess for u32 {}
862
868
impl ConstantTimeLess for u64 { }
863
869
#[ cfg( feature = "i128" ) ]
864
870
impl ConstantTimeLess for u128 { }
871
+
872
+ /// A [`PartialOrd`][core::cmp::PartialOrd]-like trait for constant-time comparisons.
873
+ ///
874
+ /// This trait is automatically implemented for types supporting the "equals", "less", and
875
+ /// "greater" comparisons.
876
+ ///
877
+ /// # Example
878
+ ///
879
+ /// ```
880
+ /// use std::cmp::Ordering;
881
+ /// use subtle::{ConstantTimePartialOrd, CtOption};
882
+ /// let x: u8 = 5;
883
+ /// let y: u8 = 13;
884
+ ///
885
+ /// assert_eq!(x.ct_partial_cmp(&x).unwrap(), Ordering::Equal);
886
+ /// assert_eq!(x.ct_partial_cmp(&y).unwrap(), Ordering::Less);
887
+ /// assert_eq!(y.ct_partial_cmp(&x).unwrap(), Ordering::Greater);
888
+ /// ```
889
+ pub trait ConstantTimePartialOrd {
890
+ /// This method returns an ordering between `self` and `other`, if it exists.
891
+ ///
892
+ /// This method should execute in constant time.
893
+ fn ct_partial_cmp ( & self , other : & Self ) -> CtOption < Ordering > ;
894
+ }
895
+
896
+ impl ConstantTimeEq for Ordering {
897
+ /// Use our `#[repr(i8)]` to get a `ct_eq()` implementation without relying on any `match`es.
898
+ ///
899
+ /// This also means `CtOption<Ordering>` implements `ConstantTimeEq`.
900
+ #[ inline]
901
+ fn ct_eq ( & self , other : & Self ) -> Choice {
902
+ let a = * self as i8 ;
903
+ let b = * other as i8 ;
904
+ a. ct_eq ( & b)
905
+ }
906
+ }
907
+
908
+ /// Select among `N + 1` results given `N` logical values, of which at most one should be true.
909
+ ///
910
+ /// This method requires a whole set of logical checks to be performed before evaluating their
911
+ /// result, and uses a lookup table to avoid branching in a `match` expression.
912
+ ///
913
+ ///```
914
+ /// use subtle::index_mutually_exclusive_logical_results;
915
+ ///
916
+ /// let r = [0xA, 0xB, 0xC];
917
+ ///
918
+ /// let a = index_mutually_exclusive_logical_results(&r, [0.into(), 0.into()]);
919
+ /// assert_eq!(*a, 0xA);
920
+ /// let b = index_mutually_exclusive_logical_results(&r, [1.into(), 0.into()]);
921
+ /// assert_eq!(*b, 0xB);
922
+ /// let c = index_mutually_exclusive_logical_results(&r, [0.into(), 1.into()]);
923
+ /// assert_eq!(*c, 0xC);
924
+ ///```
925
+ pub fn index_mutually_exclusive_logical_results < T , const N : usize > (
926
+ results : & [ T ] ,
927
+ logicals : [ Choice ; N ] ,
928
+ ) -> & T {
929
+ assert_eq ! ( results. len( ) , N + 1 ) ;
930
+ let combined_result: u8 = logicals. iter ( ) . enumerate ( ) . fold ( 0u8 , |x, ( i, choice) | {
931
+ x + ( ( i as u8 ) + 1 ) * choice. unwrap_u8 ( )
932
+ } ) ;
933
+ results
934
+ . get ( combined_result as usize )
935
+ . expect ( "multiple inconsistent mutually exclusive logical operations returned true" )
936
+ }
937
+
938
+ impl < T : ConstantTimeGreater + ConstantTimeLess + ConstantTimeEq > ConstantTimePartialOrd for T {
939
+ /// We do not assume a total ordering for `T`, so we have to individually check "less than",
940
+ /// "equal", and "greater". This also respects non-default implementations of `ct_lt()`.
941
+ fn ct_partial_cmp ( & self , other : & Self ) -> CtOption < Ordering > {
942
+ let is_eq = self . ct_eq ( other) ;
943
+ let is_lt = self . ct_lt ( other) ;
944
+ let is_gt = self . ct_gt ( other) ;
945
+
946
+ static PARTIAL_ORDERS : [ CtOption < Ordering > ; 4 ] = [
947
+ CtOption {
948
+ value : Ordering :: Equal ,
949
+ is_some : Choice :: of_bool ( false ) ,
950
+ } ,
951
+ CtOption {
952
+ value : Ordering :: Equal ,
953
+ is_some : Choice :: of_bool ( true ) ,
954
+ } ,
955
+ CtOption {
956
+ value : Ordering :: Less ,
957
+ is_some : Choice :: of_bool ( true ) ,
958
+ } ,
959
+ CtOption {
960
+ value : Ordering :: Greater ,
961
+ is_some : Choice :: of_bool ( true ) ,
962
+ } ,
963
+ ] ;
964
+ * index_mutually_exclusive_logical_results ( & PARTIAL_ORDERS , [ is_eq, is_lt, is_gt] )
965
+ }
966
+ }
967
+
968
+ /// An [`Ord`][core::cmp::Ord]-like trait for constant-time comparisons.
969
+ ///
970
+ /// This trait can be automatically implemented for types supporting the "equals" and "greater"
971
+ /// comparisons.
972
+ ///
973
+ /// # Example
974
+ ///
975
+ /// ```
976
+ /// use std::cmp::Ordering;
977
+ /// use subtle::ConstantTimeOrd;
978
+ /// let x: u8 = 5;
979
+ /// let y: u8 = 13;
980
+ ///
981
+ /// assert_eq!(x.ct_cmp(&x), Ordering::Equal);
982
+ /// assert_eq!(x.ct_cmp(&y), Ordering::Less);
983
+ /// assert_eq!(y.ct_cmp(&x), Ordering::Greater);
984
+ /// ```
985
+ pub trait ConstantTimeOrd : ConstantTimeEq + ConstantTimeGreater {
986
+ /// This method returns an ordering between `self` and other`.
987
+ ///
988
+ /// Although this method should never need to be overridden, it is exposed as a default method
989
+ /// here to force types to explicitly implement this trait. This ensures that types which are
990
+ /// *only* partially orderable do not pick up an incorrect `ConstantTimeOrd` impl just by
991
+ /// implementing the pairwise comparison operations. Contrast this with
992
+ /// [`ConstantTimePartialOrd`], which is automatically implemented for all types implementing
993
+ /// [`ConstantTimeGreater`], [`ConstantTimeLess`], and [`ConstantTimeEq`].
994
+ ///
995
+ /// Here we assume a total ordering for `T`, so we need to check only "equal" and "greater", and
996
+ /// can assume "less" if both `ct_eq()` and `ct_gt()` are false.
997
+ ///
998
+ /// This method should execute in constant time.
999
+ fn ct_cmp ( & self , other : & Self ) -> Ordering {
1000
+ let is_gt = self . ct_gt ( other) ;
1001
+ let is_eq = self . ct_eq ( other) ;
1002
+
1003
+ static ORDERS : [ Ordering ; 3 ] = [ Ordering :: Less , Ordering :: Greater , Ordering :: Equal ] ;
1004
+ * index_mutually_exclusive_logical_results ( & ORDERS , [ is_gt, is_eq] )
1005
+ }
1006
+ }
1007
+
1008
+ impl ConstantTimeOrd for u8 { }
1009
+ impl ConstantTimeOrd for u16 { }
1010
+ impl ConstantTimeOrd for u32 { }
1011
+ impl ConstantTimeOrd for u64 { }
1012
+ #[ cfg( feature = "i128" ) ]
1013
+ impl ConstantTimeOrd for u128 { }
0 commit comments