| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| | 4 | const random = std.crypto.random; |
| 4 | const testing = std.testing; | 5 | const testing = std.testing; |
| 5 | | 6 | |
| 6 | const Endian = std.builtin.Endian; | 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,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 | } |
| 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 | /// 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. |
| 82 | pub fn secureZero(comptime T: type, s: []T) void { | 129 | pub 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 { |
| 90 | test "crypto.utils.timingSafeEql" { | 137 | test "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" { |
| 100 | test "crypto.utils.timingSafeEql (vectors)" { | 147 | test "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 | } |
| 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 | test "crypto.utils.secureZero" { | 192 | test "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; |