1
1
//! Constant-time helper functions.
2
2
3
3
use super :: BoxedUint ;
4
- use crate :: Limb ;
5
- use subtle:: { Choice , ConditionallySelectable , CtOption } ;
4
+ use crate :: { ConstantTimeSelect , Limb } ;
5
+ use subtle:: { Choice , ConditionallySelectable } ;
6
6
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 {
15
11
debug_assert_eq ! ( a. bits_precision( ) , b. bits_precision( ) ) ;
16
12
let mut limbs = vec ! [ Limb :: ZERO ; a. nlimbs( ) ] . into_boxed_slice ( ) ;
17
13
@@ -22,161 +18,37 @@ impl BoxedUint {
22
18
Self { limbs }
23
19
}
24
20
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.
31
21
#[ inline]
32
- pub fn conditional_assign ( & mut self , other : & Self , choice : Choice ) {
22
+ fn ct_assign ( & mut self , other : & Self , choice : Choice ) {
33
23
debug_assert_eq ! ( self . bits_precision( ) , other. bits_precision( ) ) ;
34
24
35
25
for i in 0 ..self . nlimbs ( ) {
36
26
self . limbs [ i] . conditional_assign ( & other. limbs [ i] , choice) ;
37
27
}
38
28
}
39
29
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.
47
30
#[ 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 ) {
49
32
debug_assert_eq ! ( a. bits_precision( ) , b. bits_precision( ) ) ;
50
33
51
34
for i in 0 ..a. nlimbs ( ) {
52
35
Limb :: conditional_swap ( & mut a. limbs [ i] , & mut b. limbs [ i] , choice) ;
53
36
}
54
37
}
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
- }
107
38
}
108
39
109
40
#[ cfg( test) ]
110
41
mod tests {
111
42
use super :: BoxedUint ;
112
- use subtle:: { Choice , CtOption } ;
43
+ use crate :: ConstantTimeSelect ;
44
+ use subtle:: Choice ;
113
45
114
46
#[ test]
115
47
fn conditional_select ( ) {
116
48
let a = BoxedUint :: zero_with_precision ( 128 ) ;
117
49
let b = BoxedUint :: max ( 128 ) ;
118
50
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 ) ) ) ;
135
53
}
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 ) ;
182
54
}
0 commit comments