authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2021-06-06 16:37:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-06 16:37:58+02:00
logca6951ff7959b88573bfec7c7bf0465209f3263f
tree24c0a280b738747fabe98151c74f7f555b883489
parentfbd99319d1aa0a856d2b7e94a4446555931a6acb
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.crypto: timing-safe functions to add&subtract serialized integers (#8977)

This is useful to increment nonces and for scalar reduction. It will avoid code duplication in crypto/25519 and crypto/pcurves/*

1 files changed, 71 insertions(+), 4 deletions(-)

lib/std/crypto/utils.zig+71-4
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const debug = std.debug;2const debug = std.debug;
3const mem = std.mem;3const mem = std.mem;
4const random = std.crypto.random;
4const testing = std.testing;5const testing = std.testing;
56
6const Endian = std.builtin.Endian;7const Endian = std.builtin.Endian;
...@@ -77,6 +78,52 @@ pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: E...@@ -77,6 +78,52 @@ pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: E
77 return Order.lt;78 return Order.lt;
78}79}
7980
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.
83pub 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.
106pub 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/// Sets a slice to zeroes.127/// Sets a slice to zeroes.
81/// Prevents the store from being optimized out.128/// Prevents the store from being optimized out.
82pub fn secureZero(comptime T: type, s: []T) void {129pub fn secureZero(comptime T: type, s: []T) void {
...@@ -90,8 +137,8 @@ pub fn secureZero(comptime T: type, s: []T) void {...@@ -90,8 +137,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
90test "crypto.utils.timingSafeEql" {137test "crypto.utils.timingSafeEql" {
91 var a: [100]u8 = undefined;138 var a: [100]u8 = undefined;
92 var b: [100]u8 = undefined;139 var b: [100]u8 = undefined;
93 std.crypto.random.bytes(a[0..]);140 random.bytes(a[0..]);
94 std.crypto.random.bytes(b[0..]);141 random.bytes(b[0..]);
95 try testing.expect(!timingSafeEql([100]u8, a, b));142 try testing.expect(!timingSafeEql([100]u8, a, b));
96 mem.copy(u8, a[0..], b[0..]);143 mem.copy(u8, a[0..], b[0..]);
97 try testing.expect(timingSafeEql([100]u8, a, b));144 try testing.expect(timingSafeEql([100]u8, a, b));
...@@ -100,8 +147,8 @@ test "crypto.utils.timingSafeEql" {...@@ -100,8 +147,8 @@ test "crypto.utils.timingSafeEql" {
100test "crypto.utils.timingSafeEql (vectors)" {147test "crypto.utils.timingSafeEql (vectors)" {
101 var a: [100]u8 = undefined;148 var a: [100]u8 = undefined;
102 var b: [100]u8 = undefined;149 var b: [100]u8 = undefined;
103 std.crypto.random.bytes(a[0..]);150 random.bytes(a[0..]);
104 std.crypto.random.bytes(b[0..]);151 random.bytes(b[0..]);
105 const v1: std.meta.Vector(100, u8) = a;152 const v1: std.meta.Vector(100, u8) = a;
106 const v2: std.meta.Vector(100, u8) = b;153 const v2: std.meta.Vector(100, u8) = b;
107 try testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));154 try testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));
...@@ -122,6 +169,26 @@ test "crypto.utils.timingSafeCompare" {...@@ -122,6 +169,26 @@ test "crypto.utils.timingSafeCompare" {
122 try testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);169 try testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
123}170}
124171
172test "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
125test "crypto.utils.secureZero" {192test "crypto.utils.secureZero" {
126 var a = [_]u8{0xfe} ** 8;193 var a = [_]u8{0xfe} ** 8;
127 var b = [_]u8{0xfe} ** 8;194 var b = [_]u8{0xfe} ** 8;