diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index d9c83992c9..083813a799 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -21,6 +21,14 @@ const Crypto = struct { name: []const u8, }; +fn blackBox(x: anytype) void { + asm volatile ("" + : + : [x] "rm" (x) + : "memory" + ); +} + const hashes = [_]Crypto{ Crypto{ .ty = crypto.hash.Md5, .name = "md5" }, Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" }, @@ -46,6 +54,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 while (offset < bytes) : (offset += block.len) { h.update(block[0..]); } + blackBox(&h); const end = timer.read(); const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; @@ -67,19 +76,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..]); + blackBox(&mac); } const end = timer.read(); @@ -106,6 +116,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..]); + blackBox(&out); } } const end = timer.read(); @@ -118,7 +129,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; @@ -129,7 +140,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); + blackBox(&s); } } const end = timer.read(); @@ -140,6 +152,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; + } + blackBox(&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] @@ -198,29 +244,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) }); } } } diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 1a359ecfc7..02257c2a0b 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -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]; diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 9d139d6716..3c88e2ce11 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -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, }; @@ -229,7 +233,7 @@ pub const Aead = struct { /// 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, at: *[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); @@ -275,7 +279,7 @@ pub const Aead = struct { /// 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, at: [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);