11// -*- mode: rust; -*-
22//
33// 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
55// See LICENSE for licensing information.
66//
77// Authors:
8787#[ macro_use]
8888extern crate std;
8989
90+ #[ cfg( test) ]
91+ extern crate rand;
92+
93+ use core:: cmp:: Ordering ;
9094use core:: ops:: { BitAnd , BitAndAssign , BitOr , BitOrAssign , BitXor , BitXorAssign , Neg , Not } ;
9195use core:: option:: Option ;
9296
@@ -111,6 +115,11 @@ use core::option::Option;
111115pub struct Choice ( u8 ) ;
112116
113117impl Choice {
118+ /// Create an instance in `const` context.
119+ pub const fn of_bool ( of : bool ) -> Self {
120+ Self ( of as u8 )
121+ }
122+
114123 /// Unwrap the `Choice` wrapper to reveal the underlying `u8`.
115124 ///
116125 /// # Note
@@ -236,7 +245,7 @@ impl From<u8> for Choice {
236245 }
237246}
238247
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`.
240249///
241250/// # Example
242251///
@@ -257,7 +266,6 @@ pub trait ConstantTimeEq {
257266 ///
258267 /// * `Choice(1u8)` if `self == other`;
259268 /// * `Choice(0u8)` if `self != other`.
260- #[ inline]
261269 fn ct_eq ( & self , other : & Self ) -> Choice ;
262270}
263271
@@ -380,7 +388,6 @@ pub trait ConditionallySelectable: Copy {
380388 /// assert_eq!(z, y);
381389 /// # }
382390 /// ```
383- #[ inline]
384391 fn conditional_select ( a : & Self , b : & Self , choice : Choice ) -> Self ;
385392
386393 /// Conditionally assign `other` to `self`, according to `choice`.
@@ -530,7 +537,6 @@ pub trait ConditionallyNegatable {
530537 /// unchanged.
531538 ///
532539 /// This function should execute in constant time.
533- #[ inline]
534540 fn conditional_negate ( & mut self , choice : Choice ) ;
535541}
536542
@@ -801,7 +807,7 @@ macro_rules! generate_unsigned_integer_greater {
801807 Choice :: from( ( bit & 1 ) as u8 )
802808 }
803809 }
804- }
810+ } ;
805811}
806812
807813generate_unsigned_integer_greater ! ( u8 , 8 ) ;
@@ -813,7 +819,7 @@ generate_unsigned_integer_greater!(u128, 128);
813819
814820/// A type which can be compared in some manner and be determined to be less
815821/// than another of the same type.
816- pub trait ConstantTimeLess : ConstantTimeEq + ConstantTimeGreater {
822+ pub trait ConstantTimeLess : ConstantTimeGreater {
817823 /// Determine whether `self < other`.
818824 ///
819825 /// The bitwise-NOT of the return value of this function should be usable to
@@ -852,7 +858,7 @@ pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
852858 /// ```
853859 #[ inline]
854860 fn ct_lt ( & self , other : & Self ) -> Choice {
855- ! self . ct_gt ( other ) & ! self . ct_eq ( other )
861+ other . ct_gt ( self )
856862 }
857863}
858864
@@ -862,3 +868,146 @@ impl ConstantTimeLess for u32 {}
862868impl ConstantTimeLess for u64 { }
863869#[ cfg( feature = "i128" ) ]
864870impl 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