1
1
const std = @import ("../std.zig" );
2
2
const debug = std .debug ;
3
3
const mem = std .mem ;
4
+ const random = std .crypto .random ;
4
5
const testing = std .testing ;
5
6
6
7
const Endian = std .builtin .Endian ;
@@ -77,6 +78,52 @@ pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: E
77
78
return Order .lt ;
78
79
}
79
80
81
+ /// Add two integers serialized as arrays of the same size, in constant time.
82
+ /// The result is stored into `result`, and `true` is returned if an overflow occurred.
83
+ pub fn timingSafeAdd (comptime T : type , a : []const T , b : []const T , result : []T , endian : Endian ) bool {
84
+ const len = a .len ;
85
+ debug .assert (len == b .len and len == result .len );
86
+ var carry : u1 = 0 ;
87
+ if (endian == .Little ) {
88
+ var i : usize = 0 ;
89
+ while (i < len ) : (i += 1 ) {
90
+ const tmp = @boolToInt (@addWithOverflow (u8 , a [i ], b [i ], & result [i ]));
91
+ carry = tmp | @boolToInt (@addWithOverflow (u8 , result [i ], carry , & result [i ]));
92
+ }
93
+ } else {
94
+ var i : usize = len ;
95
+ while (i != 0 ) {
96
+ i -= 1 ;
97
+ const tmp = @boolToInt (@addWithOverflow (u8 , a [i ], b [i ], & result [i ]));
98
+ carry = tmp | @boolToInt (@addWithOverflow (u8 , result [i ], carry , & result [i ]));
99
+ }
100
+ }
101
+ return @bitCast (bool , carry );
102
+ }
103
+
104
+ /// Subtract two integers serialized as arrays of the same size, in constant time.
105
+ /// The result is stored into `result`, and `true` is returned if an underflow occurred.
106
+ pub fn timingSafeSub (comptime T : type , a : []const T , b : []const T , result : []T , endian : Endian ) bool {
107
+ const len = a .len ;
108
+ debug .assert (len == b .len and len == result .len );
109
+ var borrow : u1 = 0 ;
110
+ if (endian == .Little ) {
111
+ var i : usize = 0 ;
112
+ while (i < len ) : (i += 1 ) {
113
+ const tmp = @boolToInt (@subWithOverflow (u8 , a [i ], b [i ], & result [i ]));
114
+ borrow = tmp | @boolToInt (@subWithOverflow (u8 , result [i ], borrow , & result [i ]));
115
+ }
116
+ } else {
117
+ var i : usize = len ;
118
+ while (i != 0 ) {
119
+ i -= 1 ;
120
+ const tmp = @boolToInt (@subWithOverflow (u8 , a [i ], b [i ], & result [i ]));
121
+ borrow = tmp | @boolToInt (@subWithOverflow (u8 , result [i ], borrow , & result [i ]));
122
+ }
123
+ }
124
+ return @bitCast (bool , borrow );
125
+ }
126
+
80
127
/// Sets a slice to zeroes.
81
128
/// Prevents the store from being optimized out.
82
129
pub fn secureZero (comptime T : type , s : []T ) void {
@@ -90,8 +137,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
90
137
test "crypto.utils.timingSafeEql" {
91
138
var a : [100 ]u8 = undefined ;
92
139
var b : [100 ]u8 = undefined ;
93
- std . crypto . random .bytes (a [0.. ]);
94
- std . crypto . random .bytes (b [0.. ]);
140
+ random .bytes (a [0.. ]);
141
+ random .bytes (b [0.. ]);
95
142
try testing .expect (! timingSafeEql ([100 ]u8 , a , b ));
96
143
mem .copy (u8 , a [0.. ], b [0.. ]);
97
144
try testing .expect (timingSafeEql ([100 ]u8 , a , b ));
@@ -100,8 +147,8 @@ test "crypto.utils.timingSafeEql" {
100
147
test "crypto.utils.timingSafeEql (vectors)" {
101
148
var a : [100 ]u8 = undefined ;
102
149
var b : [100 ]u8 = undefined ;
103
- std . crypto . random .bytes (a [0.. ]);
104
- std . crypto . random .bytes (b [0.. ]);
150
+ random .bytes (a [0.. ]);
151
+ random .bytes (b [0.. ]);
105
152
const v1 : std .meta .Vector (100 , u8 ) = a ;
106
153
const v2 : std .meta .Vector (100 , u8 ) = b ;
107
154
try testing .expect (! timingSafeEql (std .meta .Vector (100 , u8 ), v1 , v2 ));
@@ -122,6 +169,26 @@ test "crypto.utils.timingSafeCompare" {
122
169
try testing .expectEqual (timingSafeCompare (u8 , & a , & b , .Little ), .lt );
123
170
}
124
171
172
+ test "crypto.utils.timingSafe{Add,Sub}" {
173
+ const len = 32 ;
174
+ var a : [len ]u8 = undefined ;
175
+ var b : [len ]u8 = undefined ;
176
+ var c : [len ]u8 = undefined ;
177
+ const zero = [_ ]u8 {0 } ** len ;
178
+ var iterations : usize = 100 ;
179
+ while (iterations != 0 ) : (iterations -= 1 ) {
180
+ random .bytes (& a );
181
+ random .bytes (& b );
182
+ const endian = if (iterations % 2 == 0 ) Endian .Big else Endian .Little ;
183
+ _ = timingSafeSub (u8 , & a , & b , & c , endian ); // a-b
184
+ _ = timingSafeAdd (u8 , & c , & b , & c , endian ); // (a-b)+b
185
+ try testing .expectEqualSlices (u8 , & c , & a );
186
+ const borrow = timingSafeSub (u8 , & c , & a , & c , endian ); // ((a-b)+b)-a
187
+ try testing .expectEqualSlices (u8 , & c , & zero );
188
+ try testing .expectEqual (borrow , false );
189
+ }
190
+ }
191
+
125
192
test "crypto.utils.secureZero" {
126
193
var a = [_ ]u8 {0xfe } ** 8 ;
127
194
var b = [_ ]u8 {0xfe } ** 8 ;
0 commit comments