| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 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 | |
| ... | @@ -43,44 +44,37 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool { | ... | @@ -43,44 +44,37 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool { |
| 43 | | 44 | |
| 44 | /// Compare two integers serialized as arrays of the same size, in constant time. | 45 | /// Compare two integers serialized as arrays of the same size, in constant time. |
| 45 | /// Returns .lt if a<b, .gt if a>b and .eq if a=b | 46 | /// Returns .lt if a<b, .gt if a>b and .eq if a=b |
| 46 | pub fn timingSafeCompare(comptime T: type, a: T, b: T, endian: Endian) Order { | 47 | pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order { |
| 47 | switch (@typeInfo(T)) { | 48 | debug.assert(a.len == b.len); |
| 48 | .Array => |info| { | 49 | const bits = switch (@typeInfo(T)) { |
| 49 | const C = info.child; | 50 | .Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits, |
| 50 | const bits = switch (@typeInfo(C)) { | 51 | else => @compileError("Elements to be compared must be integers"), |
| 51 | .Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits, | 52 | }; |
| 52 | else => @compileError("Elements to be compared must be integers"), | 53 | comptime const Cext = std.meta.Int(.unsigned, bits + 1); |
| 53 | }; | 54 | var gt: T = 0; |
| 54 | comptime const Cext = std.meta.Int(.unsigned, bits + 1); | 55 | var eq: T = 1; |
| 55 | var gt: C = 0; | 56 | if (endian == .Little) { |
| 56 | var eq: C = 1; | 57 | var i = a.len; |
| 57 | if (endian == .Little) { | 58 | while (i != 0) { |
| 58 | var i = a.len; | 59 | i -= 1; |
| 59 | while (i != 0) { | 60 | const x1 = a[i]; |
| 60 | i -= 1; | 61 | const x2 = b[i]; |
| 61 | const x1 = a[i]; | 62 | gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; |
| 62 | const x2 = b[i]; | 63 | eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); |
| 63 | gt |= @truncate(C, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; | 64 | } |
| 64 | eq &= @truncate(C, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); | 65 | } else { |
| 65 | } | 66 | for (a) |x1, i| { |
| 66 | } else { | 67 | const x2 = b[i]; |
| 67 | for (a) |x1, i| { | 68 | gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; |
| 68 | const x2 = b[i]; | 69 | eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); |
| 69 | gt |= @truncate(C, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; | 70 | } |
| 70 | eq &= @truncate(C, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); | 71 | } |
| 71 | } | 72 | if (gt != 0) { |
| 72 | } | 73 | return Order.gt; |
| 73 | if (gt != 0) { | 74 | } else if (eq != 0) { |
| 74 | return Order.gt; | 75 | return Order.eq; |
| 75 | } else if (eq != 0) { | | |
| 76 | return Order.eq; | | |
| 77 | } | | |
| 78 | return Order.lt; | | |
| 79 | }, | | |
| 80 | else => { | | |
| 81 | @compileError("Only arrays can be compared"); | | |
| 82 | }, | | |
| 83 | } | 76 | } |
| | 77 | return Order.lt; |
| 84 | } | 78 | } |
| 85 | | 79 | |
| 86 | /// Sets a slice to zeroes. | 80 | /// Sets a slice to zeroes. |
| ... | @@ -118,14 +112,14 @@ test "crypto.utils.timingSafeEql (vectors)" { | ... | @@ -118,14 +112,14 @@ test "crypto.utils.timingSafeEql (vectors)" { |
| 118 | test "crypto.utils.timingSafeCompare" { | 112 | test "crypto.utils.timingSafeCompare" { |
| 119 | var a = [_]u8{10} ** 32; | 113 | var a = [_]u8{10} ** 32; |
| 120 | var b = [_]u8{10} ** 32; | 114 | var b = [_]u8{10} ** 32; |
| 121 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Big), .eq); | 115 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq); |
| 122 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Little), .eq); | 116 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq); |
| 123 | a[31] = 1; | 117 | a[31] = 1; |
| 124 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Big), .lt); | 118 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt); |
| 125 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Little), .lt); | 119 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); |
| 126 | a[0] = 20; | 120 | a[0] = 20; |
| 127 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Big), .gt); | 121 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt); |
| 128 | testing.expectEqual(timingSafeCompare([32]u8, a, b, .Little), .lt); | 122 | testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); |
| 129 | } | 123 | } |
| 130 | | 124 | |
| 131 | test "crypto.utils.secureZero" { | 125 | test "crypto.utils.secureZero" { |