Skip to content

Commit

Permalink
Merge pull request #6164 from jedisct1/cryptobench
Browse files Browse the repository at this point in the history
Improve crypto benchmarks
  • Loading branch information
andrewrk authored Aug 26, 2020
2 parents 3abf9e1 + ad18078 commit 091d693
Show file tree
Hide file tree
Showing 17 changed files with 150 additions and 114 deletions.
72 changes: 59 additions & 13 deletions lib/std/crypto/benchmark.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const builtin = @import("builtin");
const std = @import("std");
const mem = std.mem;
const time = std.time;
const Timer = time.Timer;
const crypto = std.crypto;
Expand Down Expand Up @@ -46,6 +47,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
while (offset < bytes) : (offset += block.len) {
h.update(block[0..]);
}
mem.doNotOptimizeAway(&h);
const end = timer.read();

const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
Expand All @@ -67,19 +69,20 @@ const macs = [_]Crypto{
};

pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
std.debug.assert(64 >= Mac.mac_length and 32 >= Mac.minimum_key_length);

var in: [1 * MiB]u8 = undefined;
var in: [512 * KiB]u8 = undefined;
prng.random.bytes(in[0..]);

var key: [64]u8 = undefined;
const key_length = if (Mac.minimum_key_length == 0) 32 else Mac.minimum_key_length;
var key: [key_length]u8 = undefined;
prng.random.bytes(key[0..]);

var mac: [Mac.mac_length]u8 = undefined;
var offset: usize = 0;
var timer = try Timer.start();
const start = timer.lap();
while (offset < bytes) : (offset += in.len) {
Mac.create(key[0..], in[0..], key[0..]);
Mac.create(mac[0..], in[0..], key[0..]);
mem.doNotOptimizeAway(&mac);
}
const end = timer.read();

Expand All @@ -106,6 +109,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
var i: usize = 0;
while (i < exchange_count) : (i += 1) {
_ = DhKeyExchange.create(out[0..], out[0..], in[0..]);
mem.doNotOptimizeAway(&out);
}
}
const end = timer.read();
Expand All @@ -118,7 +122,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c

const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};

pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
var seed: [Signature.seed_length]u8 = undefined;
prng.random.bytes(seed[0..]);
const msg = [_]u8{0} ** 64;
Expand All @@ -129,7 +133,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
{
var i: usize = 0;
while (i < signatures_count) : (i += 1) {
_ = try Signature.sign(&msg, key_pair, null);
const s = try Signature.sign(&msg, key_pair, null);
mem.doNotOptimizeAway(&s);
}
}
const end = timer.read();
Expand All @@ -140,6 +145,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
return throughput;
}

const aeads = [_]Crypto{
Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
};

pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
var in: [512 * KiB]u8 = undefined;
prng.random.bytes(in[0..]);

var tag: [Aead.tag_length]u8 = undefined;

var key: [Aead.key_length]u8 = undefined;
prng.random.bytes(key[0..]);

var nonce: [Aead.nonce_length]u8 = undefined;
prng.random.bytes(nonce[0..]);

var offset: usize = 0;
var timer = try Timer.start();
const start = timer.lap();
while (offset < bytes) : (offset += in.len) {
Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
}
mem.doNotOptimizeAway(&in);
const end = timer.read();

const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
const throughput = @floatToInt(u64, 2 * bytes / elapsed_s);

return throughput;
}

fn usage() void {
std.debug.warn(
\\throughput_test [options]
Expand Down Expand Up @@ -198,29 +237,36 @@ pub fn main() !void {

inline for (hashes) |H| {
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
}
}

inline for (macs) |M| {
if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
}
}

inline for (exchanges) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
}
}

inline for (signatures) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkSignatures(E.ty, mode(1000));
try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });
const throughput = try benchmarkSignature(E.ty, mode(1000));
try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
}
}

inline for (aeads) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
}
}
}
26 changes: 13 additions & 13 deletions lib/std/crypto/chacha20.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
}

// The chacha family of ciphers are based on the salsa family.
fn chacha20Core(x: []u32, input: [16]u32) void {
inline fn chacha20Core(x: []u32, input: [16]u32) void {
for (x) |_, i|
x[i] = input[i];

Expand Down Expand Up @@ -744,26 +744,26 @@ pub const Chacha20Poly1305 = struct {
pub const key_length = 32;

/// c: ciphertext: output buffer should be of size m.len
/// at: authentication tag: output MAC
/// tag: authentication tag: output MAC
/// m: message
/// ad: Associated Data
/// npub: public nonce
/// k: private key
pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
assert(c.len == m.len);
return chacha20poly1305SealDetached(c, at, m, ad, k, npub);
return chacha20poly1305SealDetached(c, tag, m, ad, k, npub);
}

/// m: message: output buffer should be of size c.len
/// c: ciphertext
/// at: authentication tag
/// tag: authentication tag
/// ad: Associated Data
/// npub: public nonce
/// k: private key
/// NOTE: the check of the authentication tag is currently not done in constant time
pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
assert(c.len == m.len);
return try chacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
return try chacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
}
};

Expand All @@ -773,26 +773,26 @@ pub const XChacha20Poly1305 = struct {
pub const key_length = 32;

/// c: ciphertext: output buffer should be of size m.len
/// at: authentication tag: output MAC
/// tag: authentication tag: output MAC
/// m: message
/// ad: Associated Data
/// npub: public nonce
/// k: private key
pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
assert(c.len == m.len);
return xchacha20poly1305SealDetached(c, at, m, ad, k, npub);
return xchacha20poly1305SealDetached(c, tag, m, ad, k, npub);
}

/// m: message: output buffer should be of size c.len
/// c: ciphertext
/// at: authentication tag
/// tag: authentication tag
/// ad: Associated Data
/// npub: public nonce
/// k: private key
/// NOTE: the check of the authentication tag is currently not done in constant time
pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
assert(c.len == m.len);
return try xchacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
return try xchacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
}
};

Expand Down
58 changes: 31 additions & 27 deletions lib/std/crypto/gimli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,14 @@ test "hash" {
}

pub const Aead = struct {
pub const tag_length = State.RATE;
pub const nonce_length = 16;
pub const key_length = 32;

/// ad: Associated Data
/// npub: public nonce
/// k: private key
fn init(ad: []const u8, npub: [16]u8, k: [32]u8) State {
fn init(ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) State {
var state = State{
.data = undefined,
};
Expand Down Expand Up @@ -224,12 +228,12 @@ pub const Aead = struct {
}

/// c: ciphertext: output buffer should be of size m.len
/// at: authentication tag: output MAC
/// tag: authentication tag: output MAC
/// m: message
/// ad: Associated Data
/// npub: public nonce
/// k: private key
pub fn encrypt(c: []u8, at: *[State.RATE]u8, m: []const u8, ad: []const u8, npub: [16]u8, k: [32]u8) void {
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
assert(c.len == m.len);

var state = Aead.init(ad, npub, k);
Expand Down Expand Up @@ -265,17 +269,17 @@ pub const Aead = struct {

// After the final non-full block of plaintext, the first 16 bytes
// of the state are output as an authentication tag.
std.mem.copy(u8, at, buf[0..State.RATE]);
std.mem.copy(u8, tag, buf[0..State.RATE]);
}

/// m: message: output buffer should be of size c.len
/// c: ciphertext
/// at: authentication tag
/// tag: authentication tag
/// ad: Associated Data
/// npub: public nonce
/// k: private key
/// NOTE: the check of the authentication tag is currently not done in constant time
pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {
pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
assert(c.len == m.len);

var state = Aead.init(ad, npub, k);
Expand Down Expand Up @@ -308,7 +312,7 @@ pub const Aead = struct {
// After the final non-full block of plaintext, the first 16 bytes
// of the state are the authentication tag.
// TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776
if (!mem.eql(u8, buf[0..State.RATE], &at)) {
if (!mem.eql(u8, buf[0..State.RATE], &tag)) {
@memset(m.ptr, undefined, m.len);
return error.InvalidMessage;
}
Expand All @@ -328,13 +332,13 @@ test "cipher" {
const pt: [0]u8 = undefined;

var ct: [pt.len]u8 = undefined;
var at: [16]u8 = undefined;
Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
var tag: [16]u8 = undefined;
Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
htest.assertEqual("", &ct);
htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &at);
htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &tag);

var pt2: [pt.len]u8 = undefined;
try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
testing.expectEqualSlices(u8, &pt, &pt2);
}
{ // test vector (34) from NIST KAT submission.
Expand All @@ -343,13 +347,13 @@ test "cipher" {
try std.fmt.hexToBytes(&pt, "00");

var ct: [pt.len]u8 = undefined;
var at: [16]u8 = undefined;
Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
var tag: [16]u8 = undefined;
Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
htest.assertEqual("7F", &ct);
htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &at);
htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &tag);

var pt2: [pt.len]u8 = undefined;
try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
testing.expectEqualSlices(u8, &pt, &pt2);
}
{ // test vector (106) from NIST KAT submission.
Expand All @@ -359,13 +363,13 @@ test "cipher" {
try std.fmt.hexToBytes(&pt, "000102");

var ct: [pt.len]u8 = undefined;
var at: [16]u8 = undefined;
Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
var tag: [16]u8 = undefined;
Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
htest.assertEqual("484D35", &ct);
htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &at);
htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &tag);

var pt2: [pt.len]u8 = undefined;
try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
testing.expectEqualSlices(u8, &pt, &pt2);
}
{ // test vector (790) from NIST KAT submission.
Expand All @@ -375,13 +379,13 @@ test "cipher" {
try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");

var ct: [pt.len]u8 = undefined;
var at: [16]u8 = undefined;
Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
var tag: [16]u8 = undefined;
Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
htest.assertEqual("6815B4A0ECDAD01596EAD87D9E690697475D234C6A13D1", &ct);
htest.assertEqual("DFE23F1642508290D68245279558B2FB", &at);
htest.assertEqual("DFE23F1642508290D68245279558B2FB", &tag);

var pt2: [pt.len]u8 = undefined;
try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
testing.expectEqualSlices(u8, &pt, &pt2);
}
{ // test vector (1057) from NIST KAT submission.
Expand All @@ -390,13 +394,13 @@ test "cipher" {
try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");

var ct: [pt.len]u8 = undefined;
var at: [16]u8 = undefined;
Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
var tag: [16]u8 = undefined;
Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
htest.assertEqual("7F8A2CF4F52AA4D6B2E74105C30A2777B9D0C8AEFDD555DE35861BD3011F652F", &ct);
htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &at);
htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &tag);

var pt2: [pt.len]u8 = undefined;
try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
testing.expectEqualSlices(u8, &pt, &pt2);
}
}
Loading

0 comments on commit 091d693

Please sign in to comment.