11//! Constant-time helper functions.
22
33use super :: BoxedUint ;
4- use crate :: Limb ;
5- use subtle:: { Choice , ConditionallySelectable , CtOption } ;
4+ use crate :: { ConstantTimeSelect , Limb } ;
5+ use subtle:: { Choice , ConditionallySelectable } ;
66
7- impl BoxedUint {
8- /// Conditionally select `a` or `b` in constant time depending on [`Choice`].
9- ///
10- /// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
11- /// this is an inherent function instead.
12- ///
13- /// Panics if `a` and `b` don't have the same precision.
14- pub fn conditional_select ( a : & Self , b : & Self , choice : Choice ) -> Self {
7+ /// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound
8+ impl ConstantTimeSelect for BoxedUint {
9+ #[ inline]
10+ fn ct_select ( a : & Self , b : & Self , choice : Choice ) -> Self {
1511 debug_assert_eq ! ( a. bits_precision( ) , b. bits_precision( ) ) ;
1612 let mut limbs = vec ! [ Limb :: ZERO ; a. nlimbs( ) ] . into_boxed_slice ( ) ;
1713
@@ -22,161 +18,37 @@ impl BoxedUint {
2218 Self { limbs }
2319 }
2420
25- /// Conditionally assign `other` to `self`, according to `choice`.
26- ///
27- /// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
28- /// this is an inherent function instead.
29- ///
30- /// Panics if `a` and `b` don't have the same precision.
3121 #[ inline]
32- pub fn conditional_assign ( & mut self , other : & Self , choice : Choice ) {
22+ fn ct_assign ( & mut self , other : & Self , choice : Choice ) {
3323 debug_assert_eq ! ( self . bits_precision( ) , other. bits_precision( ) ) ;
3424
3525 for i in 0 ..self . nlimbs ( ) {
3626 self . limbs [ i] . conditional_assign ( & other. limbs [ i] , choice) ;
3727 }
3828 }
3929
40- /// Conditionally swap `self` and `other` if `choice == 1`; otherwise,
41- /// reassign both unto themselves.
42- ///
43- /// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
44- /// this is an inherent function instead.
45- ///
46- /// Panics if `a` and `b` don't have the same precision.
4730 #[ inline]
48- pub fn conditional_swap ( a : & mut Self , b : & mut Self , choice : Choice ) {
31+ fn ct_swap ( a : & mut Self , b : & mut Self , choice : Choice ) {
4932 debug_assert_eq ! ( a. bits_precision( ) , b. bits_precision( ) ) ;
5033
5134 for i in 0 ..a. nlimbs ( ) {
5235 Limb :: conditional_swap ( & mut a. limbs [ i] , & mut b. limbs [ i] , choice) ;
5336 }
5437 }
55-
56- /// Conditional `map`: workaround which provides a [`CtOption::map`]-like API.
57- ///
58- /// Ensures both functions are called regardless of whether the first returns some/none with an
59- /// argument whose precision matches `self`. Note this still requires branching on the
60- /// intermediate [`CtOption`] value and therefore isn't fully constant time, but the best we can
61- /// do without upstream changes to `subtle` (see dalek-cryptography/subtle#94).
62- ///
63- /// Workaround due to `Copy` in [`ConditionallySelectable`] supertrait bounds.
64- pub fn conditional_map < C , F , T > ( & self , condition : C , f : F ) -> CtOption < T >
65- where
66- C : Fn ( & Self ) -> CtOption < Self > ,
67- F : Fn ( Self ) -> T ,
68- {
69- let conditional_val = condition ( self ) ;
70- let is_some = conditional_val. is_some ( ) ;
71-
72- let placeholder = Self :: zero_with_precision ( self . bits_precision ( ) ) ;
73- let value = Option :: < Self > :: from ( conditional_val) . unwrap_or ( placeholder) ;
74- debug_assert_eq ! ( self . bits_precision( ) , value. bits_precision( ) ) ;
75- CtOption :: new ( f ( value) , is_some)
76- }
77-
78- /// Conditional `and_then`: workaround which provides a [`CtOption::and_then`]-like API.
79- ///
80- /// Ensures both functions are called regardless of whether the first returns some/none with an
81- /// argument whose precision matches `self`. Note this still requires branching on the
82- /// intermediate [`CtOption`] value and therefore isn't fully constant time, but the best we can
83- /// do without upstream changes to `subtle` (see dalek-cryptography/subtle#94).
84- ///
85- /// Workaround due to `Copy` in [`ConditionallySelectable`] supertrait bounds.
86- pub fn conditional_and_then < C , F > ( & self , condition : C , f : F ) -> CtOption < Self >
87- where
88- C : Fn ( & Self ) -> CtOption < Self > ,
89- F : Fn ( Self ) -> CtOption < Self > ,
90- {
91- let conditional_val = condition ( self ) ;
92- let mut is_some = conditional_val. is_some ( ) ;
93-
94- let placeholder = Self :: zero_with_precision ( self . bits_precision ( ) ) ;
95- let value = Option :: < Self > :: from ( conditional_val) . unwrap_or ( placeholder) ;
96- debug_assert_eq ! ( self . bits_precision( ) , value. bits_precision( ) ) ;
97-
98- let conditional_val = f ( value) ;
99- is_some &= conditional_val. is_some ( ) ;
100-
101- let placeholder = Self :: zero_with_precision ( self . bits_precision ( ) ) ;
102- let value = Option :: from ( conditional_val) . unwrap_or ( placeholder) ;
103- debug_assert_eq ! ( self . bits_precision( ) , value. bits_precision( ) ) ;
104-
105- CtOption :: new ( value, is_some)
106- }
10738}
10839
10940#[ cfg( test) ]
11041mod tests {
11142 use super :: BoxedUint ;
112- use subtle:: { Choice , CtOption } ;
43+ use crate :: ConstantTimeSelect ;
44+ use subtle:: Choice ;
11345
11446 #[ test]
11547 fn conditional_select ( ) {
11648 let a = BoxedUint :: zero_with_precision ( 128 ) ;
11749 let b = BoxedUint :: max ( 128 ) ;
11850
119- assert_eq ! ( a, BoxedUint :: conditional_select( & a, & b, Choice :: from( 0 ) ) ) ;
120- assert_eq ! ( b, BoxedUint :: conditional_select( & a, & b, Choice :: from( 1 ) ) ) ;
121- }
122-
123- #[ test]
124- fn conditional_map_some ( ) {
125- let n = BoxedUint :: one ( ) ;
126-
127- let ret = n
128- . conditional_map (
129- |n| CtOption :: new ( n. clone ( ) , 1u8 . into ( ) ) ,
130- |n| n. wrapping_add ( & BoxedUint :: one ( ) ) ,
131- )
132- . unwrap ( ) ;
133-
134- assert_eq ! ( ret, BoxedUint :: from( 2u8 ) ) ;
51+ assert_eq ! ( a, BoxedUint :: ct_select( & a, & b, Choice :: from( 0 ) ) ) ;
52+ assert_eq ! ( b, BoxedUint :: ct_select( & a, & b, Choice :: from( 1 ) ) ) ;
13553 }
136-
137- #[ test]
138- fn conditional_map_none ( ) {
139- let n = BoxedUint :: one ( ) ;
140-
141- let ret = n. conditional_map (
142- |n| CtOption :: new ( n. clone ( ) , 0u8 . into ( ) ) ,
143- |n| n. wrapping_add ( & BoxedUint :: one ( ) ) ,
144- ) ;
145-
146- assert ! ( bool :: from( ret. is_none( ) ) ) ;
147- }
148-
149- #[ test]
150- fn conditional_and_then_all_some ( ) {
151- let n = BoxedUint :: one ( ) ;
152-
153- let ret = n
154- . conditional_and_then (
155- |n| CtOption :: new ( n. clone ( ) , 1u8 . into ( ) ) ,
156- |n| CtOption :: new ( n. wrapping_add ( & BoxedUint :: one ( ) ) , 1u8 . into ( ) ) ,
157- )
158- . unwrap ( ) ;
159-
160- assert_eq ! ( ret, BoxedUint :: from( 2u8 ) ) ;
161- }
162-
163- macro_rules! conditional_and_then_none_test {
164- ( $name: ident, $a: expr, $b: expr) => {
165- #[ test]
166- fn $name( ) {
167- let n = BoxedUint :: one( ) ;
168-
169- let ret = n. conditional_and_then(
170- |n| CtOption :: new( n. clone( ) , $a. into( ) ) ,
171- |n| CtOption :: new( n. wrapping_add( & BoxedUint :: one( ) ) , $b. into( ) ) ,
172- ) ;
173-
174- assert!( bool :: from( ret. is_none( ) ) ) ;
175- }
176- } ;
177- }
178-
179- conditional_and_then_none_test ! ( conditional_and_then_none_some, 0 , 1 ) ;
180- conditional_and_then_none_test ! ( conditional_and_then_some_none, 1 , 0 ) ;
181- conditional_and_then_none_test ! ( conditional_and_then_none_none, 0 , 0 ) ;
18254}
0 commit comments