| ... | @@ -1,7 +1,11 @@ | ... | @@ -1,7 +1,11 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| | 2 | const debug = std.debug; |
| 2 | const mem = std.mem; | 3 | const mem = std.mem; |
| 3 | const testing = std.testing; | 4 | const testing = std.testing; |
| 4 | | 5 | |
| | 6 | const Endian = std.builtin.Endian; |
| | 7 | const Order = std.math.Order; |
| | 8 | |
| 5 | /// Compares two arrays in constant time (for a given length) and returns whether they are equal. | 9 | /// Compares two arrays in constant time (for a given length) and returns whether they are equal. |
| 6 | /// This function was designed to compare short cryptographic secrets (MACs, signatures). | 10 | /// This function was designed to compare short cryptographic secrets (MACs, signatures). |
| 7 | /// For all other applications, use mem.eql() instead. | 11 | /// For all other applications, use mem.eql() instead. |
| ... | @@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool { | ... | @@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool { |
| 38 | } | 42 | } |
| 39 | } | 43 | } |
| 40 | | 44 | |
| | 45 | /// Compare two integers serialized as arrays of the same size, in constant time. |
| | 46 | /// Returns .lt if a<b, .gt if a>b and .eq if a=b |
| | 47 | pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order { |
| | 48 | debug.assert(a.len == b.len); |
| | 49 | const bits = switch (@typeInfo(T)) { |
| | 50 | .Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits, |
| | 51 | else => @compileError("Elements to be compared must be integers"), |
| | 52 | }; |
| | 53 | comptime const Cext = std.meta.Int(.unsigned, bits + 1); |
| | 54 | var gt: T = 0; |
| | 55 | var eq: T = 1; |
| | 56 | if (endian == .Little) { |
| | 57 | var i = a.len; |
| | 58 | while (i != 0) { |
| | 59 | i -= 1; |
| | 60 | const x1 = a[i]; |
| | 61 | const x2 = b[i]; |
| | 62 | gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; |
| | 63 | eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); |
| | 64 | } |
| | 65 | } else { |
| | 66 | for (a) |x1, i| { |
| | 67 | const x2 = b[i]; |
| | 68 | gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; |
| | 69 | eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); |
| | 70 | } |
| | 71 | } |
| | 72 | if (gt != 0) { |
| | 73 | return Order.gt; |
| | 74 | } else if (eq != 0) { |
| | 75 | return Order.eq; |
| | 76 | } |
| | 77 | return Order.lt; |
| | 78 | } |
| | 79 | |
| 41 | /// Sets a slice to zeroes. | 80 | /// Sets a slice to zeroes. |
| 42 | /// Prevents the store from being optimized out. | 81 | /// Prevents the store from being optimized out. |
| 43 | pub fn secureZero(comptime T: type, s: []T) void { | 82 | pub fn secureZero(comptime T: type, s: []T) void { |
| ... | @@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" { | ... | @@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" { |
| 70 | testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3)); | 109 | testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3)); |
| 71 | } | 110 | } |
| 72 | | 111 | |
| | 112 | test "crypto.utils.timingSafeCompare" { |
| | 113 | var a = [_]u8{10} ** 32; |
| | 114 | var b = [_]u8{10} ** 32; |
| | 115 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq); |
| | 116 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq); |
| | 117 | a[31] = 1; |
| | 118 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt); |
| | 119 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); |
| | 120 | a[0] = 20; |
| | 121 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt); |
| | 122 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); |
| | 123 | } |
| | 124 | |
| 73 | test "crypto.utils.secureZero" { | 125 | test "crypto.utils.secureZero" { |
| 74 | var a = [_]u8{0xfe} ** 8; | 126 | var a = [_]u8{0xfe} ** 8; |
| 75 | var b = [_]u8{0xfe} ** 8; | 127 | var b = [_]u8{0xfe} ** 8; |