@@ -9,7 +9,8 @@ use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign,
99/// This is used as a "belt-and-suspenders" defense in addition to mechanisms like
1010/// constant-time predication intrinsics provided by the `cmov` crate, and is never expected to be
1111/// the only line of defense.
12- #[ derive( Copy , Clone , Debug ) ]
12+ // TODO(tarcieri): remove `Eq`/`PartialEq` when `crypto-bigint` is updated
13+ #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
1314pub struct Choice ( u8 ) ;
1415
1516impl Choice {
@@ -59,6 +60,26 @@ impl Choice {
5960 core:: hint:: black_box ( self . 0 )
6061 }
6162
63+ /// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
64+ ///
65+ /// This does not apply `black_box` to the output.
66+ // TODO(tarcieri): deprecate/remove this in favor of `to_bool` when MSRV is Rust 1.86
67+ pub const fn to_bool_vartime ( self ) -> bool {
68+ self . 0 != 0
69+ }
70+
71+ /// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
72+ ///
73+ /// This does not apply `black_box` to the output.
74+ // TODO(tarcieri): deprecate/remove this in favor of `to_u8` when MSRV is Rust 1.86
75+ pub const fn to_u8_vartime ( self ) -> u8 {
76+ self . 0
77+ }
78+
79+ //
80+ // Bitwise ops
81+ //
82+
6283 /// Apply an `and` conditional to the given [`Choice`]s.
6384 #[ inline]
6485 pub const fn and ( self , rhs : Choice ) -> Choice {
@@ -83,6 +104,131 @@ impl Choice {
83104 // NOTE: assumes self.0 is `0` or `1` as checked in constructor
84105 Self ( self . 0 ^ 1 )
85106 }
107+
108+ //
109+ // Comparison ops
110+ //
111+
112+ /// `const fn` equality operation.
113+ #[ inline]
114+ pub const fn eq ( self , other : Self ) -> Self {
115+ Self :: ne ( self , other) . not ( )
116+ }
117+
118+ /// `const fn` not equal operation.
119+ #[ inline]
120+ pub const fn ne ( self , other : Self ) -> Self {
121+ Self :: xor ( self , other)
122+ }
123+
124+ //
125+ // `const fn` constructor methods
126+ //
127+
128+ /// Returns the truthy value if `x == y`, and the falsy value otherwise.
129+ #[ inline]
130+ pub const fn from_i64_eq ( x : i64 , y : i64 ) -> Self {
131+ Self :: from_u64_nonzero ( x as u64 ^ y as u64 ) . not ( )
132+ }
133+
134+ /// Returns the truthy value if `x == y`, and the falsy value otherwise.
135+ #[ inline]
136+ pub const fn from_u32_eq ( x : u32 , y : u32 ) -> Self {
137+ Self :: from_u32_nonzero ( x ^ y) . not ( )
138+ }
139+
140+ /// Returns the truthy value if `x <= y` and the falsy value otherwise.
141+ #[ inline]
142+ pub const fn from_u32_le ( x : u32 , y : u32 ) -> Self {
143+ // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
144+ let bit = ( ( ( !x) | y) & ( ( x ^ y) | !y. wrapping_sub ( x) ) ) >> ( u32:: BITS - 1 ) ;
145+ Self :: from_u32_lsb ( bit)
146+ }
147+
148+ /// Initialize from the least significant bit of a `u32`.
149+ #[ inline]
150+ pub const fn from_u32_lsb ( value : u32 ) -> Self {
151+ Self :: new ( ( value & 0x1 ) as u8 )
152+ }
153+
154+ /// Returns the truthy value if `x < y`, and the falsy value otherwise.
155+ #[ inline]
156+ pub const fn from_u32_lt ( x : u32 , y : u32 ) -> Self {
157+ // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
158+ let bit = ( ( ( !x) & y) | ( ( ( !x) | y) & x. wrapping_sub ( y) ) ) >> ( u32:: BITS - 1 ) ;
159+ Self :: from_u32_lsb ( bit)
160+ }
161+
162+ /// Returns the truthy value if `value != 0`, and the falsy value otherwise.
163+ #[ inline]
164+ pub const fn from_u32_nonzero ( value : u32 ) -> Self {
165+ Self :: from_u32_lsb ( ( value | value. wrapping_neg ( ) ) >> ( u32:: BITS - 1 ) )
166+ }
167+
168+ /// Initialize from the least significant bit of a `u64`.
169+ #[ inline]
170+ pub const fn from_u64_lsb ( value : u64 ) -> Self {
171+ Self :: new ( ( value & 0x1 ) as u8 )
172+ }
173+
174+ /// Returns the truthy value if `value != 0`, and the falsy value otherwise.
175+ #[ inline]
176+ pub const fn from_u64_nonzero ( value : u64 ) -> Self {
177+ Self :: from_u64_lsb ( ( value | value. wrapping_neg ( ) ) >> ( u64:: BITS - 1 ) )
178+ }
179+
180+ //
181+ // `const fn` predication methods
182+ //
183+
184+ /// `const fn` helper: return `b` if `self` is truthy, otherwise return `a`.
185+ ///
186+ /// Only use this instead of the [`CtSelect`] trait in the event you're in a `const fn` context
187+ /// and can't use the trait. The former will provide better constant-time assurances.
188+ #[ inline]
189+ pub const fn select_i64 ( self , a : i64 , b : i64 ) -> i64 {
190+ self . select_u64 ( a as u64 , b as u64 ) as i64
191+ }
192+
193+ /// `const fn` helper: return `b` if `self` is truthy, otherwise return `a`.
194+ ///
195+ /// Only use this instead of the [`CtSelect`] trait in the event you're in a `const fn` context
196+ /// and can't use the trait. The former will provide better constant-time assurances.
197+ #[ inline]
198+ pub const fn select_u32 ( self , a : u32 , b : u32 ) -> u32 {
199+ a ^ ( self . to_u32_mask ( ) & ( a ^ b) )
200+ }
201+
202+ /// `const fn` helper: return `b` if `self` is truthy, otherwise return `a`.
203+ ///
204+ /// Only use this instead of the [`CtSelect`] trait in the event you're in a `const fn` context
205+ /// and can't use the trait. The former will provide better constant-time assurances.
206+ #[ inline]
207+ pub const fn select_u64 ( self , a : u64 , b : u64 ) -> u64 {
208+ a ^ ( self . to_u64_mask ( ) & ( a ^ b) )
209+ }
210+
211+ /// Create a `u32` bitmask.
212+ ///
213+ /// # Returns
214+ /// - `0` for `Choice::FALSE`
215+ /// - `u32::MAX` for `Choice::TRUE`
216+ #[ inline]
217+ #[ allow( trivial_numeric_casts) ]
218+ pub ( crate ) const fn to_u32_mask ( self ) -> u32 {
219+ ( self . 0 as u32 & 1 ) . wrapping_neg ( )
220+ }
221+
222+ /// Create a `u64` bitmask.
223+ ///
224+ /// # Returns
225+ /// - `0` for `Choice::FALSE`
226+ /// - `u64::MAX` for `Choice::TRUE`
227+ #[ inline]
228+ #[ allow( trivial_numeric_casts) ]
229+ pub ( crate ) const fn to_u64_mask ( self ) -> u64 {
230+ ( self . 0 as u64 & 1 ) . wrapping_neg ( )
231+ }
86232}
87233
88234impl BitAnd for Choice {
@@ -253,4 +399,84 @@ mod tests {
253399 assert_eq ! ( Choice :: new( 0 ) . not( ) . to_u8( ) , 1 ) ;
254400 assert_eq ! ( Choice :: new( 1 ) . not( ) . to_u8( ) , 0 ) ;
255401 }
402+
403+ #[ test]
404+ fn from_i64_eq ( ) {
405+ assert_eq ! ( Choice :: from_i64_eq( 0 , 1 ) , Choice :: FALSE ) ;
406+ assert_eq ! ( Choice :: from_i64_eq( 1 , 1 ) , Choice :: TRUE ) ;
407+ }
408+
409+ #[ test]
410+ fn from_u32_eq ( ) {
411+ assert_eq ! ( Choice :: from_u32_eq( 0 , 1 ) , Choice :: FALSE ) ;
412+ assert_eq ! ( Choice :: from_u32_eq( 1 , 1 ) , Choice :: TRUE ) ;
413+ }
414+
415+ #[ test]
416+ fn from_u32_le ( ) {
417+ assert_eq ! ( Choice :: from_u32_le( 0 , 0 ) , Choice :: TRUE ) ;
418+ assert_eq ! ( Choice :: from_u32_le( 1 , 0 ) , Choice :: FALSE ) ;
419+ assert_eq ! ( Choice :: from_u32_le( 1 , 1 ) , Choice :: TRUE ) ;
420+ assert_eq ! ( Choice :: from_u32_le( 1 , 2 ) , Choice :: TRUE ) ;
421+ }
422+
423+ #[ test]
424+ fn from_u32_lsb ( ) {
425+ assert_eq ! ( Choice :: from_u32_lsb( 0 ) , Choice :: FALSE ) ;
426+ assert_eq ! ( Choice :: from_u32_lsb( 1 ) , Choice :: TRUE ) ;
427+ assert_eq ! ( Choice :: from_u32_lsb( 2 ) , Choice :: FALSE ) ;
428+ assert_eq ! ( Choice :: from_u32_lsb( 3 ) , Choice :: TRUE ) ;
429+ }
430+
431+ #[ test]
432+ fn from_u32_lt ( ) {
433+ assert_eq ! ( Choice :: from_u32_lt( 0 , 0 ) , Choice :: FALSE ) ;
434+ assert_eq ! ( Choice :: from_u32_lt( 1 , 0 ) , Choice :: FALSE ) ;
435+ assert_eq ! ( Choice :: from_u32_lt( 1 , 1 ) , Choice :: FALSE ) ;
436+ assert_eq ! ( Choice :: from_u32_lt( 1 , 2 ) , Choice :: TRUE ) ;
437+ }
438+
439+ #[ test]
440+ fn from_u32_nonzero ( ) {
441+ assert_eq ! ( Choice :: from_u32_nonzero( 0 ) , Choice :: FALSE ) ;
442+ assert_eq ! ( Choice :: from_u32_nonzero( 1 ) , Choice :: TRUE ) ;
443+ assert_eq ! ( Choice :: from_u32_nonzero( 2 ) , Choice :: TRUE ) ;
444+ }
445+
446+ #[ test]
447+ fn from_u64_lsb ( ) {
448+ assert_eq ! ( Choice :: from_u64_lsb( 0 ) , Choice :: FALSE ) ;
449+ assert_eq ! ( Choice :: from_u64_lsb( 1 ) , Choice :: TRUE ) ;
450+ }
451+
452+ #[ test]
453+ fn from_u64_nonzero ( ) {
454+ assert_eq ! ( Choice :: from_u64_nonzero( 0 ) , Choice :: FALSE ) ;
455+ assert_eq ! ( Choice :: from_u64_nonzero( 1 ) , Choice :: TRUE ) ;
456+ assert_eq ! ( Choice :: from_u64_nonzero( 2 ) , Choice :: TRUE ) ;
457+ }
458+
459+ #[ test]
460+ fn select_i64 ( ) {
461+ let a: i64 = 1 ;
462+ let b: i64 = 2 ;
463+ assert_eq ! ( Choice :: TRUE . select_i64( a, b) , b) ;
464+ assert_eq ! ( Choice :: FALSE . select_i64( a, b) , a) ;
465+ }
466+
467+ #[ test]
468+ fn select_u32 ( ) {
469+ let a: u32 = 1 ;
470+ let b: u32 = 2 ;
471+ assert_eq ! ( Choice :: TRUE . select_u32( a, b) , b) ;
472+ assert_eq ! ( Choice :: FALSE . select_u32( a, b) , a) ;
473+ }
474+
475+ #[ test]
476+ fn select_u64 ( ) {
477+ let a: u64 = 1 ;
478+ let b: u64 = 2 ;
479+ assert_eq ! ( Choice :: TRUE . select_u64( a, b) , b) ;
480+ assert_eq ! ( Choice :: FALSE . select_u64( a, b) , a) ;
481+ }
256482}
0 commit comments