Skip to content

Commit

Permalink
std.crypto: timing-safe functions to add&subtract serialized integers (
Browse files Browse the repository at this point in the history
…#8977)

This is useful to increment nonces and for scalar reduction.

It will avoid code duplication in crypto/25519 and crypto/pcurves/*
  • Loading branch information
jedisct1 authored Jun 6, 2021
1 parent fbd9931 commit ca6951f
Showing 1 changed file with 71 additions and 4 deletions.
75 changes: 71 additions & 4 deletions lib/std/crypto/utils.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("../std.zig");
const debug = std.debug;
const mem = std.mem;
const random = std.crypto.random;
const testing = std.testing;

const Endian = std.builtin.Endian;
Expand Down Expand Up @@ -77,6 +78,52 @@ pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: E
return Order.lt;
}

/// Add two integers serialized as arrays of the same size, in constant time.
/// The result is stored into `result`, and `true` is returned if an overflow occurred.
pub fn timingSafeAdd(comptime T: type, a: []const T, b: []const T, result: []T, endian: Endian) bool {
const len = a.len;
debug.assert(len == b.len and len == result.len);
var carry: u1 = 0;
if (endian == .Little) {
var i: usize = 0;
while (i < len) : (i += 1) {
const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i]));
carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i]));
}
} else {
var i: usize = len;
while (i != 0) {
i -= 1;
const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i]));
carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i]));
}
}
return @bitCast(bool, carry);
}

/// Subtract two integers serialized as arrays of the same size, in constant time.
/// The result is stored into `result`, and `true` is returned if an underflow occurred.
pub fn timingSafeSub(comptime T: type, a: []const T, b: []const T, result: []T, endian: Endian) bool {
const len = a.len;
debug.assert(len == b.len and len == result.len);
var borrow: u1 = 0;
if (endian == .Little) {
var i: usize = 0;
while (i < len) : (i += 1) {
const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i]));
borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i]));
}
} else {
var i: usize = len;
while (i != 0) {
i -= 1;
const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i]));
borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i]));
}
}
return @bitCast(bool, borrow);
}

/// Sets a slice to zeroes.
/// Prevents the store from being optimized out.
pub fn secureZero(comptime T: type, s: []T) void {
Expand All @@ -90,8 +137,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
test "crypto.utils.timingSafeEql" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
std.crypto.random.bytes(a[0..]);
std.crypto.random.bytes(b[0..]);
random.bytes(a[0..]);
random.bytes(b[0..]);
try testing.expect(!timingSafeEql([100]u8, a, b));
mem.copy(u8, a[0..], b[0..]);
try testing.expect(timingSafeEql([100]u8, a, b));
Expand All @@ -100,8 +147,8 @@ test "crypto.utils.timingSafeEql" {
test "crypto.utils.timingSafeEql (vectors)" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
std.crypto.random.bytes(a[0..]);
std.crypto.random.bytes(b[0..]);
random.bytes(a[0..]);
random.bytes(b[0..]);
const v1: std.meta.Vector(100, u8) = a;
const v2: std.meta.Vector(100, u8) = b;
try testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));
Expand All @@ -122,6 +169,26 @@ test "crypto.utils.timingSafeCompare" {
try testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
}

test "crypto.utils.timingSafe{Add,Sub}" {
const len = 32;
var a: [len]u8 = undefined;
var b: [len]u8 = undefined;
var c: [len]u8 = undefined;
const zero = [_]u8{0} ** len;
var iterations: usize = 100;
while (iterations != 0) : (iterations -= 1) {
random.bytes(&a);
random.bytes(&b);
const endian = if (iterations % 2 == 0) Endian.Big else Endian.Little;
_ = timingSafeSub(u8, &a, &b, &c, endian); // a-b
_ = timingSafeAdd(u8, &c, &b, &c, endian); // (a-b)+b
try testing.expectEqualSlices(u8, &c, &a);
const borrow = timingSafeSub(u8, &c, &a, &c, endian); // ((a-b)+b)-a
try testing.expectEqualSlices(u8, &c, &zero);
try testing.expectEqual(borrow, false);
}
}

test "crypto.utils.secureZero" {
var a = [_]u8{0xfe} ** 8;
var b = [_]u8{0xfe} ** 8;
Expand Down

0 comments on commit ca6951f

Please sign in to comment.