authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 17:30:31-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-26 17:30:31-04:00
log091d693c5381a17d1de01ab372481f629b560c7c
tree2977e9fa1fcbbc58057684adf86ceb0291e16ef2
parent3abf9e1457ed9332474ab211eb8d996d594a33c3
parentad18078d53b448fc18a100dcebca88fe05a1e3ac
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6164 from jedisct1/cryptobench

Improve crypto benchmarks

17 files changed, 150 insertions(+), 114 deletions(-)

lib/std/crypto/benchmark.zig+59-13
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
77
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const std = @import("std");9const std = @import("std");
10const mem = std.mem;
10const time = std.time;11const time = std.time;
11const Timer = time.Timer;12const Timer = time.Timer;
12const crypto = std.crypto;13const crypto = std.crypto;
...@@ -46,6 +47,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64...@@ -46,6 +47,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
46 while (offset < bytes) : (offset += block.len) {47 while (offset < bytes) : (offset += block.len) {
47 h.update(block[0..]);48 h.update(block[0..]);
48 }49 }
50 mem.doNotOptimizeAway(&h);
49 const end = timer.read();51 const end = timer.read();
5052
51 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;53 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
...@@ -67,19 +69,20 @@ const macs = [_]Crypto{...@@ -67,19 +69,20 @@ const macs = [_]Crypto{
67};69};
6870
69pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {71pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
70 std.debug.assert(64 >= Mac.mac_length and 32 >= Mac.minimum_key_length);72 var in: [512 * KiB]u8 = undefined;
71
72 var in: [1 * MiB]u8 = undefined;
73 prng.random.bytes(in[0..]);73 prng.random.bytes(in[0..]);
7474
75 var key: [64]u8 = undefined;75 const key_length = if (Mac.minimum_key_length == 0) 32 else Mac.minimum_key_length;
76 var key: [key_length]u8 = undefined;
76 prng.random.bytes(key[0..]);77 prng.random.bytes(key[0..]);
7778
79 var mac: [Mac.mac_length]u8 = undefined;
78 var offset: usize = 0;80 var offset: usize = 0;
79 var timer = try Timer.start();81 var timer = try Timer.start();
80 const start = timer.lap();82 const start = timer.lap();
81 while (offset < bytes) : (offset += in.len) {83 while (offset < bytes) : (offset += in.len) {
82 Mac.create(key[0..], in[0..], key[0..]);84 Mac.create(mac[0..], in[0..], key[0..]);
85 mem.doNotOptimizeAway(&mac);
83 }86 }
84 const end = timer.read();87 const end = timer.read();
8588
...@@ -106,6 +109,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c...@@ -106,6 +109,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
106 var i: usize = 0;109 var i: usize = 0;
107 while (i < exchange_count) : (i += 1) {110 while (i < exchange_count) : (i += 1) {
108 _ = DhKeyExchange.create(out[0..], out[0..], in[0..]);111 _ = DhKeyExchange.create(out[0..], out[0..], in[0..]);
112 mem.doNotOptimizeAway(&out);
109 }113 }
110 }114 }
111 const end = timer.read();115 const end = timer.read();
...@@ -118,7 +122,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c...@@ -118,7 +122,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
118122
119const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};123const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
120124
121pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {125pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
122 var seed: [Signature.seed_length]u8 = undefined;126 var seed: [Signature.seed_length]u8 = undefined;
123 prng.random.bytes(seed[0..]);127 prng.random.bytes(seed[0..]);
124 const msg = [_]u8{0} ** 64;128 const msg = [_]u8{0} ** 64;
...@@ -129,7 +133,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun...@@ -129,7 +133,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
129 {133 {
130 var i: usize = 0;134 var i: usize = 0;
131 while (i < signatures_count) : (i += 1) {135 while (i < signatures_count) : (i += 1) {
132 _ = try Signature.sign(&msg, key_pair, null);136 const s = try Signature.sign(&msg, key_pair, null);
137 mem.doNotOptimizeAway(&s);
133 }138 }
134 }139 }
135 const end = timer.read();140 const end = timer.read();
...@@ -140,6 +145,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun...@@ -140,6 +145,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
140 return throughput;145 return throughput;
141}146}
142147
148const aeads = [_]Crypto{
149 Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
150 Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
151 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
152};
153
154pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
155 var in: [512 * KiB]u8 = undefined;
156 prng.random.bytes(in[0..]);
157
158 var tag: [Aead.tag_length]u8 = undefined;
159
160 var key: [Aead.key_length]u8 = undefined;
161 prng.random.bytes(key[0..]);
162
163 var nonce: [Aead.nonce_length]u8 = undefined;
164 prng.random.bytes(nonce[0..]);
165
166 var offset: usize = 0;
167 var timer = try Timer.start();
168 const start = timer.lap();
169 while (offset < bytes) : (offset += in.len) {
170 Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
171 Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
172 }
173 mem.doNotOptimizeAway(&in);
174 const end = timer.read();
175
176 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
177 const throughput = @floatToInt(u64, 2 * bytes / elapsed_s);
178
179 return throughput;
180}
181
143fn usage() void {182fn usage() void {
144 std.debug.warn(183 std.debug.warn(
145 \\throughput_test [options]184 \\throughput_test [options]
...@@ -198,29 +237,36 @@ pub fn main() !void {...@@ -198,29 +237,36 @@ pub fn main() !void {
198237
199 inline for (hashes) |H| {238 inline for (hashes) |H| {
200 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {239 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
201 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));240 const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
202 try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });241 try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
203 }242 }
204 }243 }
205244
206 inline for (macs) |M| {245 inline for (macs) |M| {
207 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {246 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
208 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));247 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
209 try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });248 try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
210 }249 }
211 }250 }
212251
213 inline for (exchanges) |E| {252 inline for (exchanges) |E| {
214 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {253 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
215 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));254 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
216 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });255 try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
217 }256 }
218 }257 }
219258
220 inline for (signatures) |E| {259 inline for (signatures) |E| {
221 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {260 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
222 const throughput = try benchmarkSignatures(E.ty, mode(1000));261 const throughput = try benchmarkSignature(E.ty, mode(1000));
223 try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });262 try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
263 }
264 }
265
266 inline for (aeads) |E| {
267 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
268 const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
269 try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
224 }270 }
225 }271 }
226}272}
lib/std/crypto/chacha20.zig+13-13
...@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {...@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
47}47}
4848
49// The chacha family of ciphers are based on the salsa family.49// The chacha family of ciphers are based on the salsa family.
50fn chacha20Core(x: []u32, input: [16]u32) void {50inline fn chacha20Core(x: []u32, input: [16]u32) void {
51 for (x) |_, i|51 for (x) |_, i|
52 x[i] = input[i];52 x[i] = input[i];
5353
...@@ -744,26 +744,26 @@ pub const Chacha20Poly1305 = struct {...@@ -744,26 +744,26 @@ pub const Chacha20Poly1305 = struct {
744 pub const key_length = 32;744 pub const key_length = 32;
745745
746 /// c: ciphertext: output buffer should be of size m.len746 /// c: ciphertext: output buffer should be of size m.len
747 /// at: authentication tag: output MAC747 /// tag: authentication tag: output MAC
748 /// m: message748 /// m: message
749 /// ad: Associated Data749 /// ad: Associated Data
750 /// npub: public nonce750 /// npub: public nonce
751 /// k: private key751 /// k: private key
752 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {752 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
753 assert(c.len == m.len);753 assert(c.len == m.len);
754 return chacha20poly1305SealDetached(c, at, m, ad, k, npub);754 return chacha20poly1305SealDetached(c, tag, m, ad, k, npub);
755 }755 }
756756
757 /// m: message: output buffer should be of size c.len757 /// m: message: output buffer should be of size c.len
758 /// c: ciphertext758 /// c: ciphertext
759 /// at: authentication tag759 /// tag: authentication tag
760 /// ad: Associated Data760 /// ad: Associated Data
761 /// npub: public nonce761 /// npub: public nonce
762 /// k: private key762 /// k: private key
763 /// NOTE: the check of the authentication tag is currently not done in constant time763 /// NOTE: the check of the authentication tag is currently not done in constant time
764 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {764 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
765 assert(c.len == m.len);765 assert(c.len == m.len);
766 return try chacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);766 return try chacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
767 }767 }
768};768};
769769
...@@ -773,26 +773,26 @@ pub const XChacha20Poly1305 = struct {...@@ -773,26 +773,26 @@ pub const XChacha20Poly1305 = struct {
773 pub const key_length = 32;773 pub const key_length = 32;
774774
775 /// c: ciphertext: output buffer should be of size m.len775 /// c: ciphertext: output buffer should be of size m.len
776 /// at: authentication tag: output MAC776 /// tag: authentication tag: output MAC
777 /// m: message777 /// m: message
778 /// ad: Associated Data778 /// ad: Associated Data
779 /// npub: public nonce779 /// npub: public nonce
780 /// k: private key780 /// k: private key
781 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {781 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
782 assert(c.len == m.len);782 assert(c.len == m.len);
783 return xchacha20poly1305SealDetached(c, at, m, ad, k, npub);783 return xchacha20poly1305SealDetached(c, tag, m, ad, k, npub);
784 }784 }
785785
786 /// m: message: output buffer should be of size c.len786 /// m: message: output buffer should be of size c.len
787 /// c: ciphertext787 /// c: ciphertext
788 /// at: authentication tag788 /// tag: authentication tag
789 /// ad: Associated Data789 /// ad: Associated Data
790 /// npub: public nonce790 /// npub: public nonce
791 /// k: private key791 /// k: private key
792 /// NOTE: the check of the authentication tag is currently not done in constant time792 /// NOTE: the check of the authentication tag is currently not done in constant time
793 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {793 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
794 assert(c.len == m.len);794 assert(c.len == m.len);
795 return try xchacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);795 return try xchacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
796 }796 }
797};797};
798798
lib/std/crypto/gimli.zig+31-27
...@@ -180,10 +180,14 @@ test "hash" {...@@ -180,10 +180,14 @@ test "hash" {
180}180}
181181
182pub const Aead = struct {182pub const Aead = struct {
183 pub const tag_length = State.RATE;
184 pub const nonce_length = 16;
185 pub const key_length = 32;
186
183 /// ad: Associated Data187 /// ad: Associated Data
184 /// npub: public nonce188 /// npub: public nonce
185 /// k: private key189 /// k: private key
186 fn init(ad: []const u8, npub: [16]u8, k: [32]u8) State {190 fn init(ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) State {
187 var state = State{191 var state = State{
188 .data = undefined,192 .data = undefined,
189 };193 };
...@@ -224,12 +228,12 @@ pub const Aead = struct {...@@ -224,12 +228,12 @@ pub const Aead = struct {
224 }228 }
225229
226 /// c: ciphertext: output buffer should be of size m.len230 /// c: ciphertext: output buffer should be of size m.len
227 /// at: authentication tag: output MAC231 /// tag: authentication tag: output MAC
228 /// m: message232 /// m: message
229 /// ad: Associated Data233 /// ad: Associated Data
230 /// npub: public nonce234 /// npub: public nonce
231 /// k: private key235 /// k: private key
232 pub fn encrypt(c: []u8, at: *[State.RATE]u8, m: []const u8, ad: []const u8, npub: [16]u8, k: [32]u8) void {236 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
233 assert(c.len == m.len);237 assert(c.len == m.len);
234238
235 var state = Aead.init(ad, npub, k);239 var state = Aead.init(ad, npub, k);
...@@ -265,17 +269,17 @@ pub const Aead = struct {...@@ -265,17 +269,17 @@ pub const Aead = struct {
265269
266 // After the final non-full block of plaintext, the first 16 bytes270 // After the final non-full block of plaintext, the first 16 bytes
267 // of the state are output as an authentication tag.271 // of the state are output as an authentication tag.
268 std.mem.copy(u8, at, buf[0..State.RATE]);272 std.mem.copy(u8, tag, buf[0..State.RATE]);
269 }273 }
270274
271 /// m: message: output buffer should be of size c.len275 /// m: message: output buffer should be of size c.len
272 /// c: ciphertext276 /// c: ciphertext
273 /// at: authentication tag277 /// tag: authentication tag
274 /// ad: Associated Data278 /// ad: Associated Data
275 /// npub: public nonce279 /// npub: public nonce
276 /// k: private key280 /// k: private key
277 /// NOTE: the check of the authentication tag is currently not done in constant time281 /// NOTE: the check of the authentication tag is currently not done in constant time
278 pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {282 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
279 assert(c.len == m.len);283 assert(c.len == m.len);
280284
281 var state = Aead.init(ad, npub, k);285 var state = Aead.init(ad, npub, k);
...@@ -308,7 +312,7 @@ pub const Aead = struct {...@@ -308,7 +312,7 @@ pub const Aead = struct {
308 // After the final non-full block of plaintext, the first 16 bytes312 // After the final non-full block of plaintext, the first 16 bytes
309 // of the state are the authentication tag.313 // of the state are the authentication tag.
310 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776314 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776
311 if (!mem.eql(u8, buf[0..State.RATE], &at)) {315 if (!mem.eql(u8, buf[0..State.RATE], &tag)) {
312 @memset(m.ptr, undefined, m.len);316 @memset(m.ptr, undefined, m.len);
313 return error.InvalidMessage;317 return error.InvalidMessage;
314 }318 }
...@@ -328,13 +332,13 @@ test "cipher" {...@@ -328,13 +332,13 @@ test "cipher" {
328 const pt: [0]u8 = undefined;332 const pt: [0]u8 = undefined;
329333
330 var ct: [pt.len]u8 = undefined;334 var ct: [pt.len]u8 = undefined;
331 var at: [16]u8 = undefined;335 var tag: [16]u8 = undefined;
332 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);336 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
333 htest.assertEqual("", &ct);337 htest.assertEqual("", &ct);
334 htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &at);338 htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &tag);
335339
336 var pt2: [pt.len]u8 = undefined;340 var pt2: [pt.len]u8 = undefined;
337 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);341 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
338 testing.expectEqualSlices(u8, &pt, &pt2);342 testing.expectEqualSlices(u8, &pt, &pt2);
339 }343 }
340 { // test vector (34) from NIST KAT submission.344 { // test vector (34) from NIST KAT submission.
...@@ -343,13 +347,13 @@ test "cipher" {...@@ -343,13 +347,13 @@ test "cipher" {
343 try std.fmt.hexToBytes(&pt, "00");347 try std.fmt.hexToBytes(&pt, "00");
344348
345 var ct: [pt.len]u8 = undefined;349 var ct: [pt.len]u8 = undefined;
346 var at: [16]u8 = undefined;350 var tag: [16]u8 = undefined;
347 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);351 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
348 htest.assertEqual("7F", &ct);352 htest.assertEqual("7F", &ct);
349 htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &at);353 htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &tag);
350354
351 var pt2: [pt.len]u8 = undefined;355 var pt2: [pt.len]u8 = undefined;
352 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);356 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
353 testing.expectEqualSlices(u8, &pt, &pt2);357 testing.expectEqualSlices(u8, &pt, &pt2);
354 }358 }
355 { // test vector (106) from NIST KAT submission.359 { // test vector (106) from NIST KAT submission.
...@@ -359,13 +363,13 @@ test "cipher" {...@@ -359,13 +363,13 @@ test "cipher" {
359 try std.fmt.hexToBytes(&pt, "000102");363 try std.fmt.hexToBytes(&pt, "000102");
360364
361 var ct: [pt.len]u8 = undefined;365 var ct: [pt.len]u8 = undefined;
362 var at: [16]u8 = undefined;366 var tag: [16]u8 = undefined;
363 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);367 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
364 htest.assertEqual("484D35", &ct);368 htest.assertEqual("484D35", &ct);
365 htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &at);369 htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &tag);
366370
367 var pt2: [pt.len]u8 = undefined;371 var pt2: [pt.len]u8 = undefined;
368 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);372 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
369 testing.expectEqualSlices(u8, &pt, &pt2);373 testing.expectEqualSlices(u8, &pt, &pt2);
370 }374 }
371 { // test vector (790) from NIST KAT submission.375 { // test vector (790) from NIST KAT submission.
...@@ -375,13 +379,13 @@ test "cipher" {...@@ -375,13 +379,13 @@ test "cipher" {
375 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");379 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
376380
377 var ct: [pt.len]u8 = undefined;381 var ct: [pt.len]u8 = undefined;
378 var at: [16]u8 = undefined;382 var tag: [16]u8 = undefined;
379 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);383 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
380 htest.assertEqual("6815B4A0ECDAD01596EAD87D9E690697475D234C6A13D1", &ct);384 htest.assertEqual("6815B4A0ECDAD01596EAD87D9E690697475D234C6A13D1", &ct);
381 htest.assertEqual("DFE23F1642508290D68245279558B2FB", &at);385 htest.assertEqual("DFE23F1642508290D68245279558B2FB", &tag);
382386
383 var pt2: [pt.len]u8 = undefined;387 var pt2: [pt.len]u8 = undefined;
384 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);388 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
385 testing.expectEqualSlices(u8, &pt, &pt2);389 testing.expectEqualSlices(u8, &pt, &pt2);
386 }390 }
387 { // test vector (1057) from NIST KAT submission.391 { // test vector (1057) from NIST KAT submission.
...@@ -390,13 +394,13 @@ test "cipher" {...@@ -390,13 +394,13 @@ test "cipher" {
390 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");394 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
391395
392 var ct: [pt.len]u8 = undefined;396 var ct: [pt.len]u8 = undefined;
393 var at: [16]u8 = undefined;397 var tag: [16]u8 = undefined;
394 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);398 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
395 htest.assertEqual("7F8A2CF4F52AA4D6B2E74105C30A2777B9D0C8AEFDD555DE35861BD3011F652F", &ct);399 htest.assertEqual("7F8A2CF4F52AA4D6B2E74105C30A2777B9D0C8AEFDD555DE35861BD3011F652F", &ct);
396 htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &at);400 htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &tag);
397401
398 var pt2: [pt.len]u8 = undefined;402 var pt2: [pt.len]u8 = undefined;
399 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);403 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
400 testing.expectEqualSlices(u8, &pt, &pt2);404 testing.expectEqualSlices(u8, &pt, &pt2);
401 }405 }
402}406}
lib/std/math.zig+3-28
...@@ -5,6 +5,7 @@...@@ -5,6 +5,7 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std.zig");6const std = @import("std.zig");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const mem = std.mem;
8const testing = std.testing;9const testing = std.testing;
910
10/// Euler's number (e)11/// Euler's number (e)
...@@ -108,34 +109,8 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {...@@ -108,34 +109,8 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
108 return fabs(x - y) < epsilon;109 return fabs(x - y) < epsilon;
109}110}
110111
111// TODO: Hide the following in an internal module.112pub fn doNotOptimizeAway(value: anytype) void {
112pub fn forceEval(value: anytype) void {113 mem.doNotOptimizeAway(value);
113 const T = @TypeOf(value);
114 switch (T) {
115 f16 => {
116 var x: f16 = undefined;
117 const p = @ptrCast(*volatile f16, &x);
118 p.* = x;
119 },
120 f32 => {
121 var x: f32 = undefined;
122 const p = @ptrCast(*volatile f32, &x);
123 p.* = x;
124 },
125 f64 => {
126 var x: f64 = undefined;
127 const p = @ptrCast(*volatile f64, &x);
128 p.* = x;
129 },
130 f128 => {
131 var x: f128 = undefined;
132 const p = @ptrCast(*volatile f128, &x);
133 p.* = x;
134 },
135 else => {
136 @compileError("forceEval not implemented for " ++ @typeName(T));
137 },
138 }
139}114}
140115
141pub fn raiseInvalid() void {116pub fn raiseInvalid() void {
lib/std/math/asinh.zig+2-2
...@@ -56,7 +56,7 @@ fn asinh32(x: f32) f32 {...@@ -56,7 +56,7 @@ fn asinh32(x: f32) f32 {
56 }56 }
57 // |x| < 0x1p-12, inexact if x != 057 // |x| < 0x1p-12, inexact if x != 0
58 else {58 else {
59 math.forceEval(x + 0x1.0p120);59 math.doNotOptimizeAway(x + 0x1.0p120);
60 }60 }
6161
62 return if (s != 0) -rx else rx;62 return if (s != 0) -rx else rx;
...@@ -87,7 +87,7 @@ fn asinh64(x: f64) f64 {...@@ -87,7 +87,7 @@ fn asinh64(x: f64) f64 {
87 }87 }
88 // |x| < 0x1p-12, inexact if x != 088 // |x| < 0x1p-12, inexact if x != 0
89 else {89 else {
90 math.forceEval(x + 0x1.0p120);90 math.doNotOptimizeAway(x + 0x1.0p120);
91 }91 }
9292
93 return if (s != 0) -rx else rx;93 return if (s != 0) -rx else rx;
lib/std/math/atan.zig+2-2
...@@ -72,7 +72,7 @@ fn atan32(x_: f32) f32 {...@@ -72,7 +72,7 @@ fn atan32(x_: f32) f32 {
72 // |x| < 2^(-12)72 // |x| < 2^(-12)
73 if (ix < 0x39800000) {73 if (ix < 0x39800000) {
74 if (ix < 0x00800000) {74 if (ix < 0x00800000) {
75 math.forceEval(x * x);75 math.doNotOptimizeAway(x * x);
76 }76 }
77 return x;77 return x;
78 }78 }
...@@ -170,7 +170,7 @@ fn atan64(x_: f64) f64 {...@@ -170,7 +170,7 @@ fn atan64(x_: f64) f64 {
170 // |x| < 2^(-27)170 // |x| < 2^(-27)
171 if (ix < 0x3E400000) {171 if (ix < 0x3E400000) {
172 if (ix < 0x00100000) {172 if (ix < 0x00100000) {
173 math.forceEval(@floatCast(f32, x));173 math.doNotOptimizeAway(@floatCast(f32, x));
174 }174 }
175 return x;175 return x;
176 }176 }
lib/std/math/atanh.zig+2-2
...@@ -45,7 +45,7 @@ fn atanh_32(x: f32) f32 {...@@ -45,7 +45,7 @@ fn atanh_32(x: f32) f32 {
45 if (u < 0x3F800000 - (32 << 23)) {45 if (u < 0x3F800000 - (32 << 23)) {
46 // underflow46 // underflow
47 if (u < (1 << 23)) {47 if (u < (1 << 23)) {
48 math.forceEval(y * y);48 math.doNotOptimizeAway(y * y);
49 }49 }
50 }50 }
51 // |x| < 0.551 // |x| < 0.5
...@@ -74,7 +74,7 @@ fn atanh_64(x: f64) f64 {...@@ -74,7 +74,7 @@ fn atanh_64(x: f64) f64 {
74 if (e < 0x3FF - 32) {74 if (e < 0x3FF - 32) {
75 // underflow75 // underflow
76 if (e == 0) {76 if (e == 0) {
77 math.forceEval(@floatCast(f32, y));77 math.doNotOptimizeAway(@floatCast(f32, y));
78 }78 }
79 }79 }
80 // |x| < 0.580 // |x| < 0.5
lib/std/math/ceil.zig+4-4
...@@ -47,14 +47,14 @@ fn ceil32(x: f32) f32 {...@@ -47,14 +47,14 @@ fn ceil32(x: f32) f32 {
47 if (u & m == 0) {47 if (u & m == 0) {
48 return x;48 return x;
49 }49 }
50 math.forceEval(x + 0x1.0p120);50 math.doNotOptimizeAway(x + 0x1.0p120);
51 if (u >> 31 == 0) {51 if (u >> 31 == 0) {
52 u += m;52 u += m;
53 }53 }
54 u &= ~m;54 u &= ~m;
55 return @bitCast(f32, u);55 return @bitCast(f32, u);
56 } else {56 } else {
57 math.forceEval(x + 0x1.0p120);57 math.doNotOptimizeAway(x + 0x1.0p120);
58 if (u >> 31 != 0) {58 if (u >> 31 != 0) {
59 return -0.0;59 return -0.0;
60 } else {60 } else {
...@@ -79,7 +79,7 @@ fn ceil64(x: f64) f64 {...@@ -79,7 +79,7 @@ fn ceil64(x: f64) f64 {
79 }79 }
8080
81 if (e <= 0x3FF - 1) {81 if (e <= 0x3FF - 1) {
82 math.forceEval(y);82 math.doNotOptimizeAway(y);
83 if (u >> 63 != 0) {83 if (u >> 63 != 0) {
84 return -0.0;84 return -0.0;
85 } else {85 } else {
...@@ -106,7 +106,7 @@ fn ceil128(x: f128) f128 {...@@ -106,7 +106,7 @@ fn ceil128(x: f128) f128 {
106 }106 }
107107
108 if (e <= 0x3FFF - 1) {108 if (e <= 0x3FFF - 1) {
109 math.forceEval(y);109 math.doNotOptimizeAway(y);
110 if (u >> 127 != 0) {110 if (u >> 127 != 0) {
111 return -0.0;111 return -0.0;
112 } else {112 } else {
lib/std/math/exp.zig+4-4
...@@ -56,7 +56,7 @@ fn exp32(x_: f32) f32 {...@@ -56,7 +56,7 @@ fn exp32(x_: f32) f32 {
56 return x * 0x1.0p127;56 return x * 0x1.0p127;
57 }57 }
58 if (sign != 0) {58 if (sign != 0) {
59 math.forceEval(-0x1.0p-149 / x); // overflow59 math.doNotOptimizeAway(-0x1.0p-149 / x); // overflow
60 // x <= -103.97208460 // x <= -103.972084
61 if (hx >= 0x42CFF1B5) {61 if (hx >= 0x42CFF1B5) {
62 return 0;62 return 0;
...@@ -88,7 +88,7 @@ fn exp32(x_: f32) f32 {...@@ -88,7 +88,7 @@ fn exp32(x_: f32) f32 {
88 hi = x;88 hi = x;
89 lo = 0;89 lo = 0;
90 } else {90 } else {
91 math.forceEval(0x1.0p127 + x); // inexact91 math.doNotOptimizeAway(0x1.0p127 + x); // inexact
92 return 1 + x;92 return 1 + x;
93 }93 }
9494
...@@ -139,7 +139,7 @@ fn exp64(x_: f64) f64 {...@@ -139,7 +139,7 @@ fn exp64(x_: f64) f64 {
139 }139 }
140 if (x < -708.39641853226410622) {140 if (x < -708.39641853226410622) {
141 // underflow if x != -inf141 // underflow if x != -inf
142 // math.forceEval(@as(f32, -0x1.0p-149 / x));142 // math.doNotOptimizeAway(@as(f32, -0x1.0p-149 / x));
143 if (x < -745.13321910194110842) {143 if (x < -745.13321910194110842) {
144 return 0;144 return 0;
145 }145 }
...@@ -172,7 +172,7 @@ fn exp64(x_: f64) f64 {...@@ -172,7 +172,7 @@ fn exp64(x_: f64) f64 {
172 lo = 0;172 lo = 0;
173 } else {173 } else {
174 // inexact if x != 0174 // inexact if x != 0
175 // math.forceEval(0x1.0p1023 + x);175 // math.doNotOptimizeAway(0x1.0p1023 + x);
176 return 1 + x;176 return 1 + x;
177 }177 }
178178
lib/std/math/exp2.zig+2-2
...@@ -70,7 +70,7 @@ fn exp2_32(x: f32) f32 {...@@ -70,7 +70,7 @@ fn exp2_32(x: f32) f32 {
70 // x < -12670 // x < -126
71 if (u >= 0x80000000) {71 if (u >= 0x80000000) {
72 if (u >= 0xC3160000 or u & 0x000FFFF != 0) {72 if (u >= 0xC3160000 or u & 0x000FFFF != 0) {
73 math.forceEval(-0x1.0p-149 / x);73 math.doNotOptimizeAway(-0x1.0p-149 / x);
74 }74 }
75 // x <= -15075 // x <= -150
76 if (u >= 0x3160000) {76 if (u >= 0x3160000) {
...@@ -393,7 +393,7 @@ fn exp2_64(x: f64) f64 {...@@ -393,7 +393,7 @@ fn exp2_64(x: f64) f64 {
393 if (ux >> 63 != 0) {393 if (ux >> 63 != 0) {
394 // underflow394 // underflow
395 if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) {395 if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) {
396 math.forceEval(@floatCast(f32, -0x1.0p-149 / x));396 math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x));
397 }397 }
398 if (x <= -1075) {398 if (x <= -1075) {
399 return 0;399 return 0;
lib/std/math/expm1.zig+2-2
...@@ -106,7 +106,7 @@ fn expm1_32(x_: f32) f32 {...@@ -106,7 +106,7 @@ fn expm1_32(x_: f32) f32 {
106 // |x| < 2^(-25)106 // |x| < 2^(-25)
107 else if (hx < 0x33000000) {107 else if (hx < 0x33000000) {
108 if (hx < 0x00800000) {108 if (hx < 0x00800000) {
109 math.forceEval(x * x);109 math.doNotOptimizeAway(x * x);
110 }110 }
111 return x;111 return x;
112 } else {112 } else {
...@@ -237,7 +237,7 @@ fn expm1_64(x_: f64) f64 {...@@ -237,7 +237,7 @@ fn expm1_64(x_: f64) f64 {
237 // |x| < 2^(-54)237 // |x| < 2^(-54)
238 else if (hx < 0x3C900000) {238 else if (hx < 0x3C900000) {
239 if (hx < 0x00100000) {239 if (hx < 0x00100000) {
240 math.forceEval(@floatCast(f32, x));240 math.doNotOptimizeAway(@floatCast(f32, x));
241 }241 }
242 return x;242 return x;
243 } else {243 } else {
lib/std/math/floor.zig+6-6
...@@ -50,13 +50,13 @@ fn floor16(x: f16) f16 {...@@ -50,13 +50,13 @@ fn floor16(x: f16) f16 {
50 if (u & m == 0) {50 if (u & m == 0) {
51 return x;51 return x;
52 }52 }
53 math.forceEval(x + 0x1.0p120);53 math.doNotOptimizeAway(x + 0x1.0p120);
54 if (u >> 15 != 0) {54 if (u >> 15 != 0) {
55 u += m;55 u += m;
56 }56 }
57 return @bitCast(f16, u & ~m);57 return @bitCast(f16, u & ~m);
58 } else {58 } else {
59 math.forceEval(x + 0x1.0p120);59 math.doNotOptimizeAway(x + 0x1.0p120);
60 if (u >> 15 == 0) {60 if (u >> 15 == 0) {
61 return 0.0;61 return 0.0;
62 } else {62 } else {
...@@ -84,13 +84,13 @@ fn floor32(x: f32) f32 {...@@ -84,13 +84,13 @@ fn floor32(x: f32) f32 {
84 if (u & m == 0) {84 if (u & m == 0) {
85 return x;85 return x;
86 }86 }
87 math.forceEval(x + 0x1.0p120);87 math.doNotOptimizeAway(x + 0x1.0p120);
88 if (u >> 31 != 0) {88 if (u >> 31 != 0) {
89 u += m;89 u += m;
90 }90 }
91 return @bitCast(f32, u & ~m);91 return @bitCast(f32, u & ~m);
92 } else {92 } else {
93 math.forceEval(x + 0x1.0p120);93 math.doNotOptimizeAway(x + 0x1.0p120);
94 if (u >> 31 == 0) {94 if (u >> 31 == 0) {
95 return 0.0;95 return 0.0;
96 } else {96 } else {
...@@ -115,7 +115,7 @@ fn floor64(x: f64) f64 {...@@ -115,7 +115,7 @@ fn floor64(x: f64) f64 {
115 }115 }
116116
117 if (e <= 0x3FF - 1) {117 if (e <= 0x3FF - 1) {
118 math.forceEval(y);118 math.doNotOptimizeAway(y);
119 if (u >> 63 != 0) {119 if (u >> 63 != 0) {
120 return -1.0;120 return -1.0;
121 } else {121 } else {
...@@ -142,7 +142,7 @@ fn floor128(x: f128) f128 {...@@ -142,7 +142,7 @@ fn floor128(x: f128) f128 {
142 }142 }
143143
144 if (e <= 0x3FFF - 1) {144 if (e <= 0x3FFF - 1) {
145 math.forceEval(y);145 math.doNotOptimizeAway(y);
146 if (u >> 127 != 0) {146 if (u >> 127 != 0) {
147 return -1.0;147 return -1.0;
148 } else {148 } else {
lib/std/math/log1p.zig+1-1
...@@ -62,7 +62,7 @@ fn log1p_32(x: f32) f32 {...@@ -62,7 +62,7 @@ fn log1p_32(x: f32) f32 {
62 if ((ix << 1) < (0x33800000 << 1)) {62 if ((ix << 1) < (0x33800000 << 1)) {
63 // underflow if subnormal63 // underflow if subnormal
64 if (ix & 0x7F800000 == 0) {64 if (ix & 0x7F800000 == 0) {
65 math.forceEval(x * x);65 math.doNotOptimizeAway(x * x);
66 }66 }
67 return x;67 return x;
68 }68 }
lib/std/math/round.zig+3-3
...@@ -43,7 +43,7 @@ fn round32(x_: f32) f32 {...@@ -43,7 +43,7 @@ fn round32(x_: f32) f32 {
43 x = -x;43 x = -x;
44 }44 }
45 if (e < 0x7F - 1) {45 if (e < 0x7F - 1) {
46 math.forceEval(x + math.f32_toint);46 math.doNotOptimizeAway(x + math.f32_toint);
47 return 0 * @bitCast(f32, u);47 return 0 * @bitCast(f32, u);
48 }48 }
4949
...@@ -76,7 +76,7 @@ fn round64(x_: f64) f64 {...@@ -76,7 +76,7 @@ fn round64(x_: f64) f64 {
76 x = -x;76 x = -x;
77 }77 }
78 if (e < 0x3ff - 1) {78 if (e < 0x3ff - 1) {
79 math.forceEval(x + math.f64_toint);79 math.doNotOptimizeAway(x + math.f64_toint);
80 return 0 * @bitCast(f64, u);80 return 0 * @bitCast(f64, u);
81 }81 }
8282
...@@ -109,7 +109,7 @@ fn round128(x_: f128) f128 {...@@ -109,7 +109,7 @@ fn round128(x_: f128) f128 {
109 x = -x;109 x = -x;
110 }110 }
111 if (e < 0x3FFF - 1) {111 if (e < 0x3FFF - 1) {
112 math.forceEval(x + math.f64_toint);112 math.doNotOptimizeAway(x + math.f64_toint);
113 return 0 * @bitCast(f128, u);113 return 0 * @bitCast(f128, u);
114 }114 }
115115
lib/std/math/tanh.zig+2-2
...@@ -67,7 +67,7 @@ fn tanh32(x: f32) f32 {...@@ -67,7 +67,7 @@ fn tanh32(x: f32) f32 {
67 }67 }
68 // |x| is subnormal68 // |x| is subnormal
69 else {69 else {
70 math.forceEval(x * x);70 math.doNotOptimizeAway(x * x);
71 t = x;71 t = x;
72 }72 }
7373
...@@ -112,7 +112,7 @@ fn tanh64(x: f64) f64 {...@@ -112,7 +112,7 @@ fn tanh64(x: f64) f64 {
112 }112 }
113 // |x| is subnormal113 // |x| is subnormal
114 else {114 else {
115 math.forceEval(@floatCast(f32, x));115 math.doNotOptimizeAway(@floatCast(f32, x));
116 t = x;116 t = x;
117 }117 }
118118
lib/std/math/trunc.zig+3-3
...@@ -46,7 +46,7 @@ fn trunc32(x: f32) f32 {...@@ -46,7 +46,7 @@ fn trunc32(x: f32) f32 {
46 if (u & m == 0) {46 if (u & m == 0) {
47 return x;47 return x;
48 } else {48 } else {
49 math.forceEval(x + 0x1p120);49 math.doNotOptimizeAway(x + 0x1p120);
50 return @bitCast(f32, u & ~m);50 return @bitCast(f32, u & ~m);
51 }51 }
52}52}
...@@ -67,7 +67,7 @@ fn trunc64(x: f64) f64 {...@@ -67,7 +67,7 @@ fn trunc64(x: f64) f64 {
67 if (u & m == 0) {67 if (u & m == 0) {
68 return x;68 return x;
69 } else {69 } else {
70 math.forceEval(x + 0x1p120);70 math.doNotOptimizeAway(x + 0x1p120);
71 return @bitCast(f64, u & ~m);71 return @bitCast(f64, u & ~m);
72 }72 }
73}73}
...@@ -88,7 +88,7 @@ fn trunc128(x: f128) f128 {...@@ -88,7 +88,7 @@ fn trunc128(x: f128) f128 {
88 if (u & m == 0) {88 if (u & m == 0) {
89 return x;89 return x;
90 } else {90 } else {
91 math.forceEval(x + 0x1p120);91 math.doNotOptimizeAway(x + 0x1p120);
92 return @bitCast(f128, u & ~m);92 return @bitCast(f128, u & ~m);
93 }93 }
94}94}
lib/std/mem.zig+11
...@@ -2158,6 +2158,17 @@ pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T {...@@ -2158,6 +2158,17 @@ pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T {
2158 return alignBackwardGeneric(T, addr + (alignment - 1), alignment);2158 return alignBackwardGeneric(T, addr + (alignment - 1), alignment);
2159}2159}
21602160
2161/// Force an evaluation of the expression; this tries to prevent
2162/// the compiler from optimizing the computation away even if the
2163/// result eventually gets discarded.
2164pub fn doNotOptimizeAway(val: anytype) void {
2165 asm volatile (""
2166 :
2167 : [val] "rm" (val)
2168 : "memory"
2169 );
2170}
2171
2161test "alignForward" {2172test "alignForward" {
2162 testing.expect(alignForward(1, 1) == 1);2173 testing.expect(alignForward(1, 1) == 1);
2163 testing.expect(alignForward(2, 1) == 2);2174 testing.expect(alignForward(2, 1) == 2);