authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-04 10:34:46-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-09-04 10:34:46-04:00
log2bd2a8ea3430b92f0c41d602d12982f776a9a524
treef720b52a676b7a3a743589094736dc91e59fa25f
parentbc88ef2dc331733e69ed895b7a815ba0a6996dc3
parent8b50d10a846005f221bf910b3d8928915c126c4c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1441 from ziglang/poly1305-x25519

Add poly1305 and x25519 crypto primitives

12 files changed, 1209 insertions(+), 203 deletions(-)

CMakeLists.txt+3-1
...@@ -446,13 +446,15 @@ set(ZIG_STD_FILES...@@ -446,13 +446,15 @@ set(ZIG_STD_FILES
446 "c/windows.zig"446 "c/windows.zig"
447 "coff.zig"447 "coff.zig"
448 "crypto/blake2.zig"448 "crypto/blake2.zig"
449 "crypto/chacha20.zig"
449 "crypto/hmac.zig"450 "crypto/hmac.zig"
450 "crypto/index.zig"451 "crypto/index.zig"
451 "crypto/md5.zig"452 "crypto/md5.zig"
452 "crypto/sha1.zig"453 "crypto/sha1.zig"
453 "crypto/sha2.zig"454 "crypto/sha2.zig"
454 "crypto/sha3.zig"455 "crypto/sha3.zig"
455 "crypto/chacha20.zig"456 "crypto/poly1305.zig"
457 "crypto/x25519.zig"
456 "cstr.zig"458 "cstr.zig"
457 "debug/failing_allocator.zig"459 "debug/failing_allocator.zig"
458 "debug/index.zig"460 "debug/index.zig"
std/crypto/blake2.zig+8-8
...@@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256);...@@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256);
34fn Blake2s(comptime out_len: usize) type {34fn Blake2s(comptime out_len: usize) type {
35 return struct {35 return struct {
36 const Self = this;36 const Self = this;
37 const block_size = 64;37 const block_length = 64;
38 const digest_size = out_len / 8;38 const digest_length = out_len / 8;
3939
40 const iv = [8]u32{40 const iv = [8]u32{
41 0x6A09E667,41 0x6A09E667,
...@@ -250,8 +250,8 @@ test "blake2s256 streaming" {...@@ -250,8 +250,8 @@ test "blake2s256 streaming" {
250}250}
251251
252test "blake2s256 aligned final" {252test "blake2s256 aligned final" {
253 var block = []u8{0} ** Blake2s256.block_size;253 var block = []u8{0} ** Blake2s256.block_length;
254 var out: [Blake2s256.digest_size]u8 = undefined;254 var out: [Blake2s256.digest_length]u8 = undefined;
255255
256 var h = Blake2s256.init();256 var h = Blake2s256.init();
257 h.update(block);257 h.update(block);
...@@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512);...@@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512);
267fn Blake2b(comptime out_len: usize) type {267fn Blake2b(comptime out_len: usize) type {
268 return struct {268 return struct {
269 const Self = this;269 const Self = this;
270 const block_size = 128;270 const block_length = 128;
271 const digest_size = out_len / 8;271 const digest_length = out_len / 8;
272272
273 const iv = [8]u64{273 const iv = [8]u64{
274 0x6a09e667f3bcc908,274 0x6a09e667f3bcc908,
...@@ -483,8 +483,8 @@ test "blake2b512 streaming" {...@@ -483,8 +483,8 @@ test "blake2b512 streaming" {
483}483}
484484
485test "blake2b512 aligned final" {485test "blake2b512 aligned final" {
486 var block = []u8{0} ** Blake2b512.block_size;486 var block = []u8{0} ** Blake2b512.block_length;
487 var out: [Blake2b512.digest_size]u8 = undefined;487 var out: [Blake2b512.digest_length]u8 = undefined;
488488
489 var h = Blake2b512.init();489 var h = Blake2b512.init();
490 h.update(block);490 h.update(block);
std/crypto/chacha20.zig+30-27
...@@ -32,24 +32,28 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {...@@ -32,24 +32,28 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
32 x[i] = input[i];32 x[i] = input[i];
3333
34 const rounds = comptime []QuarterRound{34 const rounds = comptime []QuarterRound{
35 Rp( 0, 4, 8,12),35 Rp(0, 4, 8, 12),
36 Rp( 1, 5, 9,13),36 Rp(1, 5, 9, 13),
37 Rp( 2, 6,10,14),37 Rp(2, 6, 10, 14),
38 Rp( 3, 7,11,15),38 Rp(3, 7, 11, 15),
39 Rp( 0, 5,10,15),39 Rp(0, 5, 10, 15),
40 Rp( 1, 6,11,12),40 Rp(1, 6, 11, 12),
41 Rp( 2, 7, 8,13),41 Rp(2, 7, 8, 13),
42 Rp( 3, 4, 9,14),42 Rp(3, 4, 9, 14),
43 };43 };
4444
45 comptime var j: usize = 0;45 comptime var j: usize = 0;
46 inline while (j < 20) : (j += 2) {46 inline while (j < 20) : (j += 2) {
47 // two-round cycles47 // two-round cycles
48 inline for (rounds) |r| {48 inline for (rounds) |r| {
49 x[r.a] +%= x[r.b]; x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16));49 x[r.a] +%= x[r.b];
50 x[r.c] +%= x[r.d]; x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12));50 x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16));
51 x[r.a] +%= x[r.b]; x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8));51 x[r.c] +%= x[r.d];
52 x[r.c] +%= x[r.d]; x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7));52 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12));
53 x[r.a] +%= x[r.b];
54 x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8));
55 x[r.c] +%= x[r.d];
56 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7));
53 }57 }
54 }58 }
5559
...@@ -166,9 +170,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]...@@ -166,9 +170,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
166 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));170 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
167 var i: u32 = 0;171 var i: u32 = 0;
168 while (remaining_blocks > 0) : (remaining_blocks -= 1) {172 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
169 chaCha20_internal(out[cursor..cursor + big_block], in[cursor..cursor + big_block], k, c);173 chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
170 c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't174 c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
171 // know about this.
172 cursor += big_block;175 cursor += big_block;
173 }176 }
174 }177 }
...@@ -199,16 +202,16 @@ test "crypto.chacha20 test vector sunscreen" {...@@ -199,16 +202,16 @@ test "crypto.chacha20 test vector sunscreen" {
199 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";202 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
200 var result: [114]u8 = undefined;203 var result: [114]u8 = undefined;
201 const key = []u8{204 const key = []u8{
202 0, 1, 2, 3, 4, 5, 6, 7,205 0, 1, 2, 3, 4, 5, 6, 7,
203 8, 9,10,11,12,13,14,15,206 8, 9, 10, 11, 12, 13, 14, 15,
204 16,17,18,19,20,21,22,23,207 16, 17, 18, 19, 20, 21, 22, 23,
205 24,25,26,27,28,29,30,31,208 24, 25, 26, 27, 28, 29, 30, 31,
206 };209 };
207 const nonce = []u8{210 const nonce = []u8{
208 0, 0, 0, 0,211 0, 0, 0, 0,
209 0, 0, 0, 0x4a,212 0, 0, 0, 0x4a,
210 0, 0, 0, 0,213 0, 0, 0, 0,
211 };214 };
212215
213 chaCha20IETF(result[0..], input[0..], 1, key, nonce);216 chaCha20IETF(result[0..], input[0..], 1, key, nonce);
214 assert(mem.eql(u8, expected_result, result));217 assert(mem.eql(u8, expected_result, result));
...@@ -248,7 +251,7 @@ test "crypto.chacha20 test vector 1" {...@@ -248,7 +251,7 @@ test "crypto.chacha20 test vector 1" {
248 0, 0, 0, 0, 0, 0, 0, 0,251 0, 0, 0, 0, 0, 0, 0, 0,
249 0, 0, 0, 0, 0, 0, 0, 0,252 0, 0, 0, 0, 0, 0, 0, 0,
250 };253 };
251 const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 0};254 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
252255
253 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);256 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
254 assert(mem.eql(u8, expected_result, result));257 assert(mem.eql(u8, expected_result, result));
...@@ -282,7 +285,7 @@ test "crypto.chacha20 test vector 2" {...@@ -282,7 +285,7 @@ test "crypto.chacha20 test vector 2" {
282 0, 0, 0, 0, 0, 0, 0, 0,285 0, 0, 0, 0, 0, 0, 0, 0,
283 0, 0, 0, 0, 0, 0, 0, 1,286 0, 0, 0, 0, 0, 0, 0, 1,
284 };287 };
285 const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 0};288 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
286289
287 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);290 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
288 assert(mem.eql(u8, expected_result, result));291 assert(mem.eql(u8, expected_result, result));
...@@ -316,7 +319,7 @@ test "crypto.chacha20 test vector 3" {...@@ -316,7 +319,7 @@ test "crypto.chacha20 test vector 3" {
316 0, 0, 0, 0, 0, 0, 0, 0,319 0, 0, 0, 0, 0, 0, 0, 0,
317 0, 0, 0, 0, 0, 0, 0, 0,320 0, 0, 0, 0, 0, 0, 0, 0,
318 };321 };
319 const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 1};322 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
320323
321 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);324 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
322 assert(mem.eql(u8, expected_result, result));325 assert(mem.eql(u8, expected_result, result));
...@@ -350,7 +353,7 @@ test "crypto.chacha20 test vector 4" {...@@ -350,7 +353,7 @@ test "crypto.chacha20 test vector 4" {
350 0, 0, 0, 0, 0, 0, 0, 0,353 0, 0, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0,354 0, 0, 0, 0, 0, 0, 0, 0,
352 };355 };
353 const nonce = []u8{1, 0, 0, 0, 0, 0, 0, 0};356 const nonce = []u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
354357
355 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);358 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
356 assert(mem.eql(u8, expected_result, result));359 assert(mem.eql(u8, expected_result, result));
std/crypto/hmac.zig+54-37
...@@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5);...@@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5);
7pub const HmacSha1 = Hmac(crypto.Sha1);7pub const HmacSha1 = Hmac(crypto.Sha1);
8pub const HmacSha256 = Hmac(crypto.Sha256);8pub const HmacSha256 = Hmac(crypto.Sha256);
99
10pub fn Hmac(comptime H: type) type {10pub fn Hmac(comptime Hash: type) type {
11 return struct {11 return struct {
12 const digest_size = H.digest_size;12 const Self = this;
13 pub const mac_length = Hash.digest_length;
14 pub const minimum_key_length = 0;
1315
14 pub fn hash(output: []u8, key: []const u8, message: []const u8) void {16 o_key_pad: [Hash.block_length]u8,
15 debug.assert(output.len >= H.digest_size);17 i_key_pad: [Hash.block_length]u8,
16 debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption18 scratch: [Hash.block_length]u8,
17 var scratch: [H.block_size]u8 = undefined;19 hash: Hash,
20
21 // HMAC(k, m) = H(o_key_pad | H(i_key_pad | msg)) where | is concatenation
22 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
23 var ctx = Self.init(key);
24 ctx.update(msg);
25 ctx.final(out[0..]);
26 }
27
28 pub fn init(key: []const u8) Self {
29 var ctx: Self = undefined;
1830
19 // Normalize key length to block size of hash31 // Normalize key length to block size of hash
20 if (key.len > H.block_size) {32 if (key.len > Hash.block_length) {
21 H.hash(key, scratch[0..H.digest_size]);33 Hash.hash(key, ctx.scratch[0..mac_length]);
22 mem.set(u8, scratch[H.digest_size..H.block_size], 0);34 mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0);
23 } else if (key.len < H.block_size) {35 } else if (key.len < Hash.block_length) {
24 mem.copy(u8, scratch[0..key.len], key);36 mem.copy(u8, ctx.scratch[0..key.len], key);
25 mem.set(u8, scratch[key.len..H.block_size], 0);37 mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0);
26 } else {38 } else {
27 mem.copy(u8, scratch[0..], key);39 mem.copy(u8, ctx.scratch[0..], key);
28 }40 }
2941
30 var o_key_pad: [H.block_size]u8 = undefined;42 for (ctx.o_key_pad) |*b, i| {
31 for (o_key_pad) |*b, i| {43 b.* = ctx.scratch[i] ^ 0x5c;
32 b.* = scratch[i] ^ 0x5c;
33 }44 }
3445
35 var i_key_pad: [H.block_size]u8 = undefined;46 for (ctx.i_key_pad) |*b, i| {
36 for (i_key_pad) |*b, i| {47 b.* = ctx.scratch[i] ^ 0x36;
37 b.* = scratch[i] ^ 0x36;
38 }48 }
3949
40 // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation50 ctx.hash = Hash.init();
41 var hmac = H.init();51 ctx.hash.update(ctx.i_key_pad[0..]);
42 hmac.update(i_key_pad[0..]);52 return ctx;
43 hmac.update(message);53 }
44 hmac.final(scratch[0..H.digest_size]);54
55 pub fn update(ctx: *Self, msg: []const u8) void {
56 ctx.hash.update(msg);
57 }
58
59 pub fn final(ctx: *Self, out: []u8) void {
60 debug.assert(Hash.block_length >= out.len and out.len >= mac_length);
4561
46 hmac.reset();62 ctx.hash.final(ctx.scratch[0..mac_length]);
47 hmac.update(o_key_pad[0..]);63 ctx.hash.reset();
48 hmac.update(scratch[0..H.digest_size]);64 ctx.hash.update(ctx.o_key_pad[0..]);
49 hmac.final(output[0..H.digest_size]);65 ctx.hash.update(ctx.scratch[0..mac_length]);
66 ctx.hash.final(out[0..mac_length]);
50 }67 }
51 };68 };
52}69}
...@@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type {...@@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type {
54const htest = @import("test.zig");71const htest = @import("test.zig");
5572
56test "hmac md5" {73test "hmac md5" {
57 var out: [crypto.Md5.digest_size]u8 = undefined;74 var out: [HmacMd5.mac_length]u8 = undefined;
58 HmacMd5.hash(out[0..], "", "");75 HmacMd5.create(out[0..], "", "");
59 htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);76 htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
6077
61 HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");78 HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
62 htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);79 htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
63}80}
6481
65test "hmac sha1" {82test "hmac sha1" {
66 var out: [crypto.Sha1.digest_size]u8 = undefined;83 var out: [HmacSha1.mac_length]u8 = undefined;
67 HmacSha1.hash(out[0..], "", "");84 HmacSha1.create(out[0..], "", "");
68 htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);85 htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
6986
70 HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");87 HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
71 htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);88 htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
72}89}
7390
74test "hmac sha256" {91test "hmac sha256" {
75 var out: [crypto.Sha256.digest_size]u8 = undefined;92 var out: [HmacSha256.mac_length]u8 = undefined;
76 HmacSha256.hash(out[0..], "", "");93 HmacSha256.create(out[0..], "", "");
77 htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);94 htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
7895
79 HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");96 HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
80 htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);97 htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
81}98}
std/crypto/index.zig+10-5
...@@ -21,19 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512;...@@ -21,19 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512;
2121
22const hmac = @import("hmac.zig");22const hmac = @import("hmac.zig");
23pub const HmacMd5 = hmac.HmacMd5;23pub const HmacMd5 = hmac.HmacMd5;
24pub const HmacSha1 = hmac.Sha1;24pub const HmacSha1 = hmac.HmacSha1;
25pub const HmacSha256 = hmac.Sha256;25pub const HmacSha256 = hmac.HmacSha256;
2626
27const import_chaCha20 = @import("chacha20.zig");27const import_chaCha20 = @import("chacha20.zig");
28pub const chaCha20IETF = import_chaCha20.chaCha20IETF;28pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
29pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;29pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
3030
31pub const Poly1305 = @import("poly1305.zig").Poly1305;
32pub const X25519 = @import("x25519.zig").X25519;
33
31test "crypto" {34test "crypto" {
35 _ = @import("blake2.zig");
36 _ = @import("chacha20.zig");
37 _ = @import("hmac.zig");
32 _ = @import("md5.zig");38 _ = @import("md5.zig");
39 _ = @import("poly1305.zig");
33 _ = @import("sha1.zig");40 _ = @import("sha1.zig");
34 _ = @import("sha2.zig");41 _ = @import("sha2.zig");
35 _ = @import("sha3.zig");42 _ = @import("sha3.zig");
36 _ = @import("blake2.zig");43 _ = @import("x25519.zig");
37 _ = @import("hmac.zig");
38 _ = @import("chacha20.zig");
39}44}
std/crypto/md5.zig+4-4
...@@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar...@@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar
2929
30pub const Md5 = struct {30pub const Md5 = struct {
31 const Self = this;31 const Self = this;
32 const block_size = 64;32 const block_length = 64;
33 const digest_size = 16;33 const digest_length = 16;
3434
35 s: [4]u32,35 s: [4]u32,
36 // Streaming Cache36 // Streaming Cache
...@@ -271,8 +271,8 @@ test "md5 streaming" {...@@ -271,8 +271,8 @@ test "md5 streaming" {
271}271}
272272
273test "md5 aligned final" {273test "md5 aligned final" {
274 var block = []u8{0} ** Md5.block_size;274 var block = []u8{0} ** Md5.block_length;
275 var out: [Md5.digest_size]u8 = undefined;275 var out: [Md5.digest_length]u8 = undefined;
276276
277 var h = Md5.init();277 var h = Md5.init();
278 h.update(block);278 h.update(block);
std/crypto/poly1305.zig created+233
...@@ -0,0 +1,233 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../index.zig");
6const builtin = @import("builtin");
7
8const Endian = builtin.Endian;
9const readInt = std.mem.readInt;
10const writeInt = std.mem.writeInt;
11
12pub const Poly1305 = struct {
13 const Self = this;
14
15 pub const mac_length = 16;
16 pub const minimum_key_length = 32;
17
18 // constant multiplier (from the secret key)
19 r: [4]u32,
20 // accumulated hash
21 h: [5]u32,
22 // chunk of the message
23 c: [5]u32,
24 // random number added at the end (from the secret key)
25 pad: [4]u32,
26 // How many bytes are there in the chunk.
27 c_idx: usize,
28
29 fn secureZero(self: *Self) void {
30 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Poly1305)]);
31 }
32
33 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
34 std.debug.assert(out.len >= mac_length);
35 std.debug.assert(key.len >= minimum_key_length);
36
37 var ctx = Poly1305.init(key);
38 ctx.update(msg);
39 ctx.final(out);
40 }
41
42 // Initialize the MAC context.
43 // - key.len is sufficient size.
44 pub fn init(key: []const u8) Self {
45 var ctx: Poly1305 = undefined;
46
47 // Initial hash is zero
48 {
49 var i: usize = 0;
50 while (i < 5) : (i += 1) {
51 ctx.h[i] = 0;
52 }
53 }
54 // add 2^130 to every input block
55 ctx.c[4] = 1;
56 polyClearC(&ctx);
57
58 // load r and pad (r has some of its bits cleared)
59 {
60 var i: usize = 0;
61 while (i < 1) : (i += 1) {
62 ctx.r[0] = readInt(key[0..4], u32, Endian.Little) & 0x0fffffff;
63 }
64 }
65 {
66 var i: usize = 1;
67 while (i < 4) : (i += 1) {
68 ctx.r[i] = readInt(key[i * 4 .. i * 4 + 4], u32, Endian.Little) & 0x0ffffffc;
69 }
70 }
71 {
72 var i: usize = 0;
73 while (i < 4) : (i += 1) {
74 ctx.pad[i] = readInt(key[i * 4 + 16 .. i * 4 + 16 + 4], u32, Endian.Little);
75 }
76 }
77
78 return ctx;
79 }
80
81 // h = (h + c) * r
82 // preconditions:
83 // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
84 // ctx->c <= 1_ffffffff_ffffffff_ffffffff_ffffffff
85 // ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff
86 // Postcondition:
87 // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
88 fn polyBlock(ctx: *Self) void {
89 // s = h + c, without carry propagation
90 const s0 = u64(ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe
91 const s1 = u64(ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe
92 const s2 = u64(ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe
93 const s3 = u64(ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe
94 const s4 = u64(ctx.h[4]) + ctx.c[4]; // s4 <= 5
95
96 // Local all the things!
97 const r0 = ctx.r[0]; // r0 <= 0fffffff
98 const r1 = ctx.r[1]; // r1 <= 0ffffffc
99 const r2 = ctx.r[2]; // r2 <= 0ffffffc
100 const r3 = ctx.r[3]; // r3 <= 0ffffffc
101 const rr0 = (r0 >> 2) * 5; // rr0 <= 13fffffb // lose 2 bits...
102 const rr1 = (r1 >> 2) + r1; // rr1 <= 13fffffb // rr1 == (r1 >> 2) * 5
103 const rr2 = (r2 >> 2) + r2; // rr2 <= 13fffffb // rr1 == (r2 >> 2) * 5
104 const rr3 = (r3 >> 2) + r3; // rr3 <= 13fffffb // rr1 == (r3 >> 2) * 5
105
106 // (h + c) * r, without carry propagation
107 const x0 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0; //<=97ffffe007fffff8
108 const x1 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1; //<=8fffffe20ffffff6
109 const x2 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2; //<=87ffffe417fffff4
110 const x3 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3; //<=7fffffe61ffffff2
111 const x4 = s4 * (r0 & 3); // ...recover 2 bits //<= f
112
113 // partial reduction modulo 2^130 - 5
114 const _u5 = @truncate(u32, x4 + (x3 >> 32)); // u5 <= 7ffffff5
115 const _u0 = (_u5 >> 2) * 5 + (x0 & 0xffffffff);
116 const _u1 = (_u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32);
117 const _u2 = (_u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32);
118 const _u3 = (_u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32);
119 const _u4 = (_u3 >> 32) + (_u5 & 3);
120
121 // Update the hash
122 ctx.h[0] = @truncate(u32, _u0); // u0 <= 1_9ffffff0
123 ctx.h[1] = @truncate(u32, _u1); // u1 <= 1_97ffffe0
124 ctx.h[2] = @truncate(u32, _u2); // u2 <= 1_8fffffe2
125 ctx.h[3] = @truncate(u32, _u3); // u3 <= 1_87ffffe4
126 ctx.h[4] = @truncate(u32, _u4); // u4 <= 4
127 }
128
129 // (re-)initializes the input counter and input buffer
130 fn polyClearC(ctx: *Self) void {
131 ctx.c[0] = 0;
132 ctx.c[1] = 0;
133 ctx.c[2] = 0;
134 ctx.c[3] = 0;
135 ctx.c_idx = 0;
136 }
137
138 fn polyTakeInput(ctx: *Self, input: u8) void {
139 const word = ctx.c_idx >> 2;
140 const byte = ctx.c_idx & 3;
141 ctx.c[word] |= std.math.shl(u32, input, byte * 8);
142 ctx.c_idx += 1;
143 }
144
145 fn polyUpdate(ctx: *Self, msg: []const u8) void {
146 for (msg) |b| {
147 polyTakeInput(ctx, b);
148 if (ctx.c_idx == 16) {
149 polyBlock(ctx);
150 polyClearC(ctx);
151 }
152 }
153 }
154
155 fn alignTo(x: usize, block_size: usize) usize {
156 return ((~x) +% 1) & (block_size - 1);
157 }
158
159 // Feed data into the MAC context.
160 pub fn update(ctx: *Self, msg: []const u8) void {
161 // Align ourselves with block boundaries
162 const alignm = std.math.min(alignTo(ctx.c_idx, 16), msg.len);
163 polyUpdate(ctx, msg[0..alignm]);
164
165 var nmsg = msg[alignm..];
166
167 // Process the msg block by block
168 const nb_blocks = nmsg.len >> 4;
169 var i: usize = 0;
170 while (i < nb_blocks) : (i += 1) {
171 ctx.c[0] = readInt(nmsg[0..4], u32, Endian.Little);
172 ctx.c[1] = readInt(nmsg[4..8], u32, Endian.Little);
173 ctx.c[2] = readInt(nmsg[8..12], u32, Endian.Little);
174 ctx.c[3] = readInt(nmsg[12..16], u32, Endian.Little);
175 polyBlock(ctx);
176 nmsg = nmsg[16..];
177 }
178 if (nb_blocks > 0) {
179 polyClearC(ctx);
180 }
181
182 // remaining bytes
183 polyUpdate(ctx, nmsg[0..]);
184 }
185
186 // Finalize the MAC and output into buffer provided by caller.
187 pub fn final(ctx: *Self, out: []u8) void {
188 // Process the last block (if any)
189 if (ctx.c_idx != 0) {
190 // move the final 1 according to remaining input length
191 // (We may add less than 2^130 to the last input block)
192 ctx.c[4] = 0;
193 polyTakeInput(ctx, 1);
194 // one last hash update
195 polyBlock(ctx);
196 }
197
198 // check if we should subtract 2^130-5 by performing the
199 // corresponding carry propagation.
200 const _u0 = u64(5) + ctx.h[0]; // <= 1_00000004
201 const _u1 = (_u0 >> 32) + ctx.h[1]; // <= 1_00000000
202 const _u2 = (_u1 >> 32) + ctx.h[2]; // <= 1_00000000
203 const _u3 = (_u2 >> 32) + ctx.h[3]; // <= 1_00000000
204 const _u4 = (_u3 >> 32) + ctx.h[4]; // <= 5
205 // u4 indicates how many times we should subtract 2^130-5 (0 or 1)
206
207 // h + pad, minus 2^130-5 if u4 exceeds 3
208 const uu0 = (_u4 >> 2) * 5 + ctx.h[0] + ctx.pad[0]; // <= 2_00000003
209 const uu1 = (uu0 >> 32) + ctx.h[1] + ctx.pad[1]; // <= 2_00000000
210 const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000
211 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000
212
213 writeInt(out[0..], @truncate(u32, uu0), Endian.Little);
214 writeInt(out[4..], @truncate(u32, uu1), Endian.Little);
215 writeInt(out[8..], @truncate(u32, uu2), Endian.Little);
216 writeInt(out[12..], @truncate(u32, uu3), Endian.Little);
217
218 ctx.secureZero();
219 }
220};
221
222test "poly1305 rfc7439 vector1" {
223 const expected_mac = "\xa8\x06\x1d\xc1\x30\x51\x36\xc6\xc2\x2b\x8b\xaf\x0c\x01\x27\xa9";
224
225 const msg = "Cryptographic Forum Research Group";
226 const key = "\x85\xd6\xbe\x78\x57\x55\x6d\x33\x7f\x44\x52\xfe\x42\xd5\x06\xa8" ++
227 "\x01\x03\x80\x8a\xfb\x0d\xb2\xfd\x4a\xbf\xf6\xaf\x41\x49\xf5\x1b";
228
229 var mac: [16]u8 = undefined;
230 Poly1305.create(mac[0..], msg, key);
231
232 std.debug.assert(std.mem.eql(u8, mac, expected_mac));
233}
std/crypto/sha1.zig+4-4
...@@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {...@@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
2626
27pub const Sha1 = struct {27pub const Sha1 = struct {
28 const Self = this;28 const Self = this;
29 const block_size = 64;29 const block_length = 64;
30 const digest_size = 20;30 const digest_length = 20;
3131
32 s: [5]u32,32 s: [5]u32,
33 // Streaming Cache33 // Streaming Cache
...@@ -292,8 +292,8 @@ test "sha1 streaming" {...@@ -292,8 +292,8 @@ test "sha1 streaming" {
292}292}
293293
294test "sha1 aligned final" {294test "sha1 aligned final" {
295 var block = []u8{0} ** Sha1.block_size;295 var block = []u8{0} ** Sha1.block_length;
296 var out: [Sha1.digest_size]u8 = undefined;296 var out: [Sha1.digest_length]u8 = undefined;
297297
298 var h = Sha1.init();298 var h = Sha1.init();
299 h.update(block);299 h.update(block);
std/crypto/sha2.zig+8-8
...@@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params);...@@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params);
78fn Sha2_32(comptime params: Sha2Params32) type {78fn Sha2_32(comptime params: Sha2Params32) type {
79 return struct {79 return struct {
80 const Self = this;80 const Self = this;
81 const block_size = 64;81 const block_length = 64;
82 const digest_size = params.out_len / 8;82 const digest_length = params.out_len / 8;
8383
84 s: [8]u32,84 s: [8]u32,
85 // Streaming Cache85 // Streaming Cache
...@@ -338,8 +338,8 @@ test "sha256 streaming" {...@@ -338,8 +338,8 @@ test "sha256 streaming" {
338}338}
339339
340test "sha256 aligned final" {340test "sha256 aligned final" {
341 var block = []u8{0} ** Sha256.block_size;341 var block = []u8{0} ** Sha256.block_length;
342 var out: [Sha256.digest_size]u8 = undefined;342 var out: [Sha256.digest_length]u8 = undefined;
343343
344 var h = Sha256.init();344 var h = Sha256.init();
345 h.update(block);345 h.update(block);
...@@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params);...@@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params);
419fn Sha2_64(comptime params: Sha2Params64) type {419fn Sha2_64(comptime params: Sha2Params64) type {
420 return struct {420 return struct {
421 const Self = this;421 const Self = this;
422 const block_size = 128;422 const block_length = 128;
423 const digest_size = params.out_len / 8;423 const digest_length = params.out_len / 8;
424424
425 s: [8]u64,425 s: [8]u64,
426 // Streaming Cache426 // Streaming Cache
...@@ -715,8 +715,8 @@ test "sha512 streaming" {...@@ -715,8 +715,8 @@ test "sha512 streaming" {
715}715}
716716
717test "sha512 aligned final" {717test "sha512 aligned final" {
718 var block = []u8{0} ** Sha512.block_size;718 var block = []u8{0} ** Sha512.block_length;
719 var out: [Sha512.digest_size]u8 = undefined;719 var out: [Sha512.digest_length]u8 = undefined;
720720
721 var h = Sha512.init();721 var h = Sha512.init();
722 h.update(block);722 h.update(block);
std/crypto/sha3.zig+15-88
...@@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06);...@@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06);
13fn Keccak(comptime bits: usize, comptime delim: u8) type {13fn Keccak(comptime bits: usize, comptime delim: u8) type {
14 return struct {14 return struct {
15 const Self = this;15 const Self = this;
16 const block_size = 200;16 const block_length = 200;
17 const digest_size = bits / 8;17 const digest_length = bits / 8;
1818
19 s: [200]u8,19 s: [200]u8,
20 offset: usize,20 offset: usize,
...@@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {...@@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
87}87}
8888
89const RC = []const u64{89const RC = []const u64{
90 0x0000000000000001,90 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
91 0x0000000000008082,91 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
92 0x800000000000808a,92 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
93 0x8000000080008000,93 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
94 0x000000000000808b,94 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
95 0x0000000080000001,95 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
96 0x8000000080008081,
97 0x8000000000008009,
98 0x000000000000008a,
99 0x0000000000000088,
100 0x0000000080008009,
101 0x000000008000000a,
102 0x000000008000808b,
103 0x800000000000008b,
104 0x8000000000008089,
105 0x8000000000008003,
106 0x8000000000008002,
107 0x8000000000000080,
108 0x000000000000800a,
109 0x800000008000000a,
110 0x8000000080008081,
111 0x8000000000008080,
112 0x0000000080000001,
113 0x8000000080008008,
114};96};
11597
116const ROTC = []const usize{98const ROTC = []const usize{
117 1,99 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
118 3,
119 6,
120 10,
121 15,
122 21,
123 28,
124 36,
125 45,
126 55,
127 2,
128 14,
129 27,
130 41,
131 56,
132 8,
133 25,
134 43,
135 62,
136 18,
137 39,
138 61,
139 20,
140 44,
141};100};
142101
143const PIL = []const usize{102const PIL = []const usize{
144 10,103 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
145 7,
146 11,
147 17,
148 18,
149 3,
150 5,
151 16,
152 8,
153 21,
154 24,
155 4,
156 15,
157 23,
158 19,
159 13,
160 12,
161 2,
162 20,
163 14,
164 22,
165 9,
166 6,
167 1,
168};104};
169105
170const M5 = []const usize{106const M5 = []const usize{
171 0,107 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
172 1,
173 2,
174 3,
175 4,
176 0,
177 1,
178 2,
179 3,
180 4,
181};108};
182109
183fn keccak_f(comptime F: usize, d: []u8) void {110fn keccak_f(comptime F: usize, d: []u8) void {
...@@ -297,8 +224,8 @@ test "sha3-256 streaming" {...@@ -297,8 +224,8 @@ test "sha3-256 streaming" {
297}224}
298225
299test "sha3-256 aligned final" {226test "sha3-256 aligned final" {
300 var block = []u8{0} ** Sha3_256.block_size;227 var block = []u8{0} ** Sha3_256.block_length;
301 var out: [Sha3_256.digest_size]u8 = undefined;228 var out: [Sha3_256.digest_length]u8 = undefined;
302229
303 var h = Sha3_256.init();230 var h = Sha3_256.init();
304 h.update(block);231 h.update(block);
...@@ -368,8 +295,8 @@ test "sha3-512 streaming" {...@@ -368,8 +295,8 @@ test "sha3-512 streaming" {
368}295}
369296
370test "sha3-512 aligned final" {297test "sha3-512 aligned final" {
371 var block = []u8{0} ** Sha3_512.block_size;298 var block = []u8{0} ** Sha3_512.block_length;
372 var out: [Sha3_512.digest_size]u8 = undefined;299 var out: [Sha3_512.digest_length]u8 = undefined;
373300
374 var h = Sha3_512.init();301 var h = Sha3_512.init();
375 h.update(block);302 h.update(block);
std/crypto/throughput_test.zig+176-21
...@@ -1,38 +1,193 @@...@@ -1,38 +1,193 @@
1// Modify the HashFunction variable to the one wanted to test.1const builtin = @import("builtin");
2//
3// ```
4// zig build-exe --release-fast throughput_test.zig
5// ./throughput_test
6// ```
7
8const std = @import("std");2const std = @import("std");
9const time = std.os.time;3const time = std.os.time;
10const Timer = time.Timer;4const Timer = time.Timer;
11const HashFunction = @import("md5.zig").Md5;5const crypto = @import("index.zig");
126
13const MiB = 1024 * 1024;7const KiB = 1024;
14const BytesToHash = 1024 * MiB;8const MiB = 1024 * KiB;
159
16pub fn main() !void {10var prng = std.rand.DefaultPrng.init(0);
17 var stdout_file = try std.io.getStdOut();
18 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
19 const stdout = &stdout_out_stream.stream;
2011
21 var block: [HashFunction.block_size]u8 = undefined;12const Crypto = struct {
22 std.mem.set(u8, block[0..], 0);13 ty: type,
14 name: []const u8,
15};
2316
24 var h = HashFunction.init();17const hashes = []Crypto{
25 var offset: usize = 0;18 Crypto{ .ty = crypto.Md5, .name = "md5" },
19 Crypto{ .ty = crypto.Sha1, .name = "sha1" },
20 Crypto{ .ty = crypto.Sha256, .name = "sha256" },
21 Crypto{ .ty = crypto.Sha512, .name = "sha512" },
22 Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" },
23 Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" },
24 Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" },
25 Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" },
26};
27
28pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {
29 var h = Hash.init();
30
31 var block: [Hash.digest_length]u8 = undefined;
32 prng.random.bytes(block[0..]);
2633
34 var offset: usize = 0;
27 var timer = try Timer.start();35 var timer = try Timer.start();
28 const start = timer.lap();36 const start = timer.lap();
29 while (offset < BytesToHash) : (offset += block.len) {37 while (offset < bytes) : (offset += block.len) {
30 h.update(block[0..]);38 h.update(block[0..]);
31 }39 }
32 const end = timer.read();40 const end = timer.read();
3341
34 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;42 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
35 const throughput = @floatToInt(u64, BytesToHash / elapsed_s);43 const throughput = @floatToInt(u64, bytes / elapsed_s);
44
45 return throughput;
46}
47
48const macs = []Crypto{
49 Crypto{ .ty = crypto.Poly1305, .name = "poly1305" },
50 Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" },
51 Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" },
52 Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
53};
54
55pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
56 std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
57
58 var in: [1 * MiB]u8 = undefined;
59 prng.random.bytes(in[0..]);
60
61 var key: [32]u8 = undefined;
62 prng.random.bytes(key[0..]);
63
64 var offset: usize = 0;
65 var timer = try Timer.start();
66 const start = timer.lap();
67 while (offset < bytes) : (offset += in.len) {
68 Mac.create(key[0..], in[0..], key);
69 }
70 const end = timer.read();
71
72 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
73 const throughput = @floatToInt(u64, bytes / elapsed_s);
74
75 return throughput;
76}
77
78const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
79
80pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {
81 std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
82
83 var in: [DhKeyExchange.minimum_key_length]u8 = undefined;
84 prng.random.bytes(in[0..]);
85
86 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
87 prng.random.bytes(out[0..]);
88
89 var offset: usize = 0;
90 var timer = try Timer.start();
91 const start = timer.lap();
92 {
93 var i: usize = 0;
94 while (i < exchange_count) : (i += 1) {
95 _ = DhKeyExchange.create(out[0..], out, in);
96 }
97 }
98 const end = timer.read();
99
100 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
101 const throughput = @floatToInt(u64, exchange_count / elapsed_s);
102
103 return throughput;
104}
36105
37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));106fn usage() void {
107 std.debug.warn(
108 \\throughput_test [options]
109 \\
110 \\Options:
111 \\ --filter [test-name]
112 \\ --seed [int]
113 \\ --help
114 \\
115 );
116}
117
118fn mode(comptime x: comptime_int) comptime_int {
119 return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
120}
121
122// TODO(#1358): Replace with builtin formatted padding when available.
123fn printPad(stdout: var, s: []const u8) !void {
124 var i: usize = 0;
125 while (i < 12 - s.len) : (i += 1) {
126 try stdout.print(" ");
127 }
128 try stdout.print("{}", s);
129}
130
131pub fn main() !void {
132 var stdout_file = try std.io.getStdOut();
133 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
134 const stdout = &stdout_out_stream.stream;
135
136 var buffer: [1024]u8 = undefined;
137 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
138 const args = try std.os.argsAlloc(&fixed.allocator);
139
140 var filter: ?[]u8 = "";
141
142 var i: usize = 1;
143 while (i < args.len) : (i += 1) {
144 if (std.mem.eql(u8, args[i], "--seed")) {
145 i += 1;
146 if (i == args.len) {
147 usage();
148 std.os.exit(1);
149 }
150
151 const seed = try std.fmt.parseUnsigned(u32, args[i], 10);
152 prng.seed(seed);
153 } else if (std.mem.eql(u8, args[i], "--filter")) {
154 i += 1;
155 if (i == args.len) {
156 usage();
157 std.os.exit(1);
158 }
159
160 filter = args[i];
161 } else if (std.mem.eql(u8, args[i], "--help")) {
162 usage();
163 return;
164 } else {
165 usage();
166 std.os.exit(1);
167 }
168 }
169
170 inline for (hashes) |H| {
171 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
172 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
173 try printPad(stdout, H.name);
174 try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
175 }
176 }
177
178 inline for (macs) |M| {
179 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
180 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
181 try printPad(stdout, M.name);
182 try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
183 }
184 }
185
186 inline for (exchanges) |E| {
187 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
188 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
189 try printPad(stdout, E.name);
190 try stdout.print(": {} exchanges/s\n", throughput);
191 }
192 }
38}193}
std/crypto/x25519.zig created+664
...@@ -0,0 +1,664 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../index.zig");
6const builtin = @import("builtin");
7
8const Endian = builtin.Endian;
9const readInt = std.mem.readInt;
10const writeInt = std.mem.writeInt;
11
12// Based on Supercop's ref10 implementation.
13pub const X25519 = struct {
14 pub const secret_length = 32;
15 pub const minimum_key_length = 32;
16
17 fn trimScalar(s: []u8) void {
18 s[0] &= 248;
19 s[31] &= 127;
20 s[31] |= 64;
21 }
22
23 fn scalarBit(s: []const u8, i: usize) i32 {
24 return (s[i >> 3] >> @intCast(u3, i & 7)) & 1;
25 }
26
27 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
28 std.debug.assert(out.len >= secret_length);
29 std.debug.assert(private_key.len >= minimum_key_length);
30 std.debug.assert(public_key.len >= minimum_key_length);
31
32 var storage: [7]Fe = undefined;
33 var x1 = &storage[0];
34 var x2 = &storage[1];
35 var z2 = &storage[2];
36 var x3 = &storage[3];
37 var z3 = &storage[4];
38 var t0 = &storage[5];
39 var t1 = &storage[6];
40
41 // computes the scalar product
42 Fe.fromBytes(x1, public_key);
43
44 // restrict the possible scalar values
45 var e: [32]u8 = undefined;
46 for (e[0..]) |_, i| {
47 e[i] = private_key[i];
48 }
49 trimScalar(e[0..]);
50
51 // computes the actual scalar product (the result is in x2 and z2)
52
53 // Montgomery ladder
54 // In projective coordinates, to avoid divisons: x = X / Z
55 // We don't care about the y coordinate, it's only 1 bit of information
56 Fe.init1(x2);
57 Fe.init0(z2); // "zero" point
58 Fe.copy(x3, x1);
59 Fe.init1(z3);
60
61 var swap: i32 = 0;
62 var pos: isize = 254;
63 while (pos >= 0) : (pos -= 1) {
64 // constant time conditional swap before ladder step
65 const b = scalarBit(e, @intCast(usize, pos));
66 swap ^= b; // xor trick avoids swapping at the end of the loop
67 Fe.cswap(x2, x3, swap);
68 Fe.cswap(z2, z3, swap);
69 swap = b; // anticipates one last swap after the loop
70
71 // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
72 // with differential addition
73 Fe.sub(t0, x3, z3);
74 Fe.sub(t1, x2, z2);
75 Fe.add(x2, x2, z2);
76 Fe.add(z2, x3, z3);
77 Fe.mul(z3, t0, x2);
78 Fe.mul(z2, z2, t1);
79 Fe.sq(t0, t1);
80 Fe.sq(t1, x2);
81 Fe.add(x3, z3, z2);
82 Fe.sub(z2, z3, z2);
83 Fe.mul(x2, t1, t0);
84 Fe.sub(t1, t1, t0);
85 Fe.sq(z2, z2);
86 Fe.mulSmall(z3, t1, 121666);
87 Fe.sq(x3, x3);
88 Fe.add(t0, t0, z3);
89 Fe.mul(z3, x1, z2);
90 Fe.mul(z2, t1, t0);
91 }
92
93 // last swap is necessary to compensate for the xor trick
94 // Note: after this swap, P3 == P2 + P1.
95 Fe.cswap(x2, x3, swap);
96 Fe.cswap(z2, z3, swap);
97
98 // normalises the coordinates: x == X / Z
99 Fe.invert(z2, z2);
100 Fe.mul(x2, x2, z2);
101 Fe.toBytes(out, x2);
102
103 x1.secureZero();
104 x2.secureZero();
105 x3.secureZero();
106 t0.secureZero();
107 t1.secureZero();
108 z2.secureZero();
109 z3.secureZero();
110 std.mem.secureZero(u8, e[0..]);
111
112 // Returns false if the output is all zero
113 // (happens with some malicious public keys)
114 return !zerocmp(u8, out);
115 }
116
117 pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool {
118 var base_point = []u8{9} ++ []u8{0} ** 31;
119 return create(public_key, private_key, base_point);
120 }
121};
122
123// Constant time compare to zero.
124fn zerocmp(comptime T: type, a: []const T) bool {
125 var s: T = 0;
126 for (a) |b| {
127 s |= b;
128 }
129 return s == 0;
130}
131
132////////////////////////////////////
133/// Arithmetic modulo 2^255 - 19 ///
134////////////////////////////////////
135// Taken from Supercop's ref10 implementation.
136// A bit bigger than TweetNaCl, over 4 times faster.
137
138// field element
139const Fe = struct {
140 b: [10]i32,
141
142 fn secureZero(self: *Fe) void {
143 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]);
144 }
145
146 fn init0(h: *Fe) void {
147 for (h.b) |*e| {
148 e.* = 0;
149 }
150 }
151
152 fn init1(h: *Fe) void {
153 for (h.b[1..]) |*e| {
154 e.* = 0;
155 }
156 h.b[0] = 1;
157 }
158
159 fn copy(h: *Fe, f: *const Fe) void {
160 for (h.b) |_, i| {
161 h.b[i] = f.b[i];
162 }
163 }
164
165 fn neg(h: *Fe, f: *const Fe) void {
166 for (h.b) |_, i| {
167 h.b[i] = -f.b[i];
168 }
169 }
170
171 fn add(h: *Fe, f: *const Fe, g: *const Fe) void {
172 for (h.b) |_, i| {
173 h.b[i] = f.b[i] + g.b[i];
174 }
175 }
176
177 fn sub(h: *Fe, f: *const Fe, g: *const Fe) void {
178 for (h.b) |_, i| {
179 h.b[i] = f.b[i] - g.b[i];
180 }
181 }
182
183 fn cswap(f: *Fe, g: *Fe, b: i32) void {
184 for (f.b) |_, i| {
185 const x = (f.b[i] ^ g.b[i]) & -b;
186 f.b[i] ^= x;
187 g.b[i] ^= x;
188 }
189 }
190
191 fn ccopy(f: *Fe, g: *const Fe, b: i32) void {
192 for (f.b) |_, i| {
193 const x = (f.b[i] ^ g.b[i]) & -b;
194 f.b[i] ^= x;
195 }
196 }
197
198 inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void {
199 const j = (i + 1) % 10;
200
201 c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1);
202 t[j] += c[i] * mult;
203 t[i] -= c[i] * (i64(1) << (shift + 1));
204 }
205
206 fn carry1(h: *Fe, t: []i64) void {
207 var c: [10]i64 = undefined;
208
209 var sc = c[0..];
210 var st = t[0..];
211
212 carryRound(sc, st, 9, 24, 19);
213 carryRound(sc, st, 1, 24, 1);
214 carryRound(sc, st, 3, 24, 1);
215 carryRound(sc, st, 5, 24, 1);
216 carryRound(sc, st, 7, 24, 1);
217 carryRound(sc, st, 0, 25, 1);
218 carryRound(sc, st, 2, 25, 1);
219 carryRound(sc, st, 4, 25, 1);
220 carryRound(sc, st, 6, 25, 1);
221 carryRound(sc, st, 8, 25, 1);
222
223 for (h.b) |_, i| {
224 h.b[i] = @intCast(i32, t[i]);
225 }
226 }
227
228 fn carry2(h: *Fe, t: []i64) void {
229 var c: [10]i64 = undefined;
230
231 var sc = c[0..];
232 var st = t[0..];
233
234 carryRound(sc, st, 0, 25, 1);
235 carryRound(sc, st, 4, 25, 1);
236 carryRound(sc, st, 1, 24, 1);
237 carryRound(sc, st, 5, 24, 1);
238 carryRound(sc, st, 2, 25, 1);
239 carryRound(sc, st, 6, 25, 1);
240 carryRound(sc, st, 3, 24, 1);
241 carryRound(sc, st, 7, 24, 1);
242 carryRound(sc, st, 4, 25, 1);
243 carryRound(sc, st, 8, 25, 1);
244 carryRound(sc, st, 9, 24, 19);
245 carryRound(sc, st, 0, 25, 1);
246
247 for (h.b) |_, i| {
248 h.b[i] = @intCast(i32, t[i]);
249 }
250 }
251
252 fn fromBytes(h: *Fe, s: []const u8) void {
253 std.debug.assert(s.len >= 32);
254
255 var t: [10]i64 = undefined;
256
257 t[0] = readInt(s[0..4], u32, Endian.Little);
258 t[1] = readInt(s[4..7], u32, Endian.Little) << 6;
259 t[2] = readInt(s[7..10], u32, Endian.Little) << 5;
260 t[3] = readInt(s[10..13], u32, Endian.Little) << 3;
261 t[4] = readInt(s[13..16], u32, Endian.Little) << 2;
262 t[5] = readInt(s[16..20], u32, Endian.Little);
263 t[6] = readInt(s[20..23], u32, Endian.Little) << 7;
264 t[7] = readInt(s[23..26], u32, Endian.Little) << 5;
265 t[8] = readInt(s[26..29], u32, Endian.Little) << 4;
266 t[9] = (readInt(s[29..32], u32, Endian.Little) & 0x7fffff) << 2;
267
268 carry1(h, t[0..]);
269 }
270
271 fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void {
272 var t: [10]i64 = undefined;
273
274 for (t[0..]) |_, i| {
275 t[i] = i64(f.b[i]) * g;
276 }
277
278 carry1(h, t[0..]);
279 }
280
281 fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void {
282 const f = f1.b;
283 const g = g1.b;
284
285 var F: [10]i32 = undefined;
286 var G: [10]i32 = undefined;
287
288 F[1] = f[1] * 2;
289 F[3] = f[3] * 2;
290 F[5] = f[5] * 2;
291 F[7] = f[7] * 2;
292 F[9] = f[9] * 2;
293
294 G[1] = g[1] * 19;
295 G[2] = g[2] * 19;
296 G[3] = g[3] * 19;
297 G[4] = g[4] * 19;
298 G[5] = g[5] * 19;
299 G[6] = g[6] * 19;
300 G[7] = g[7] * 19;
301 G[8] = g[8] * 19;
302 G[9] = g[9] * 19;
303
304 // t's become h
305 var t: [10]i64 = undefined;
306
307 t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]);
308 t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]);
309 t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]);
310 t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]);
311 t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]);
312 t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]);
313 t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]);
314 t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]);
315 t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]);
316 t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]);
317
318 carry2(h, t[0..]);
319 }
320
321 // we could use Fe.mul() for this, but this is significantly faster
322 fn sq(h: *Fe, fz: *const Fe) void {
323 const f0 = fz.b[0];
324 const f1 = fz.b[1];
325 const f2 = fz.b[2];
326 const f3 = fz.b[3];
327 const f4 = fz.b[4];
328 const f5 = fz.b[5];
329 const f6 = fz.b[6];
330 const f7 = fz.b[7];
331 const f8 = fz.b[8];
332 const f9 = fz.b[9];
333
334 const f0_2 = f0 * 2;
335 const f1_2 = f1 * 2;
336 const f2_2 = f2 * 2;
337 const f3_2 = f3 * 2;
338 const f4_2 = f4 * 2;
339 const f5_2 = f5 * 2;
340 const f6_2 = f6 * 2;
341 const f7_2 = f7 * 2;
342 const f5_38 = f5 * 38;
343 const f6_19 = f6 * 19;
344 const f7_38 = f7 * 38;
345 const f8_19 = f8 * 19;
346 const f9_38 = f9 * 38;
347
348 var t: [10]i64 = undefined;
349
350 t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38);
351 t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19);
352 t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19);
353 t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38);
354 t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38);
355 t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19);
356 t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19);
357 t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38);
358 t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38);
359 t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2);
360
361 carry2(h, t[0..]);
362 }
363
364 fn sq2(h: *Fe, f: *const Fe) void {
365 Fe.sq(h, f);
366 Fe.mul_small(h, h, 2);
367 }
368
369 // This could be simplified, but it would be slower
370 fn invert(out: *Fe, z: *const Fe) void {
371 var i: usize = undefined;
372
373 var t: [4]Fe = undefined;
374 var t0 = &t[0];
375 var t1 = &t[1];
376 var t2 = &t[2];
377 var t3 = &t[3];
378
379 Fe.sq(t0, z);
380 Fe.sq(t1, t0);
381 Fe.sq(t1, t1);
382 Fe.mul(t1, z, t1);
383 Fe.mul(t0, t0, t1);
384
385 Fe.sq(t2, t0);
386 Fe.mul(t1, t1, t2);
387
388 Fe.sq(t2, t1);
389 i = 1;
390 while (i < 5) : (i += 1) Fe.sq(t2, t2);
391 Fe.mul(t1, t2, t1);
392
393 Fe.sq(t2, t1);
394 i = 1;
395 while (i < 10) : (i += 1) Fe.sq(t2, t2);
396 Fe.mul(t2, t2, t1);
397
398 Fe.sq(t3, t2);
399 i = 1;
400 while (i < 20) : (i += 1) Fe.sq(t3, t3);
401 Fe.mul(t2, t3, t2);
402
403 Fe.sq(t2, t2);
404 i = 1;
405 while (i < 10) : (i += 1) Fe.sq(t2, t2);
406 Fe.mul(t1, t2, t1);
407
408 Fe.sq(t2, t1);
409 i = 1;
410 while (i < 50) : (i += 1) Fe.sq(t2, t2);
411 Fe.mul(t2, t2, t1);
412
413 Fe.sq(t3, t2);
414 i = 1;
415 while (i < 100) : (i += 1) Fe.sq(t3, t3);
416 Fe.mul(t2, t3, t2);
417
418 Fe.sq(t2, t2);
419 i = 1;
420 while (i < 50) : (i += 1) Fe.sq(t2, t2);
421 Fe.mul(t1, t2, t1);
422
423 Fe.sq(t1, t1);
424 i = 1;
425 while (i < 5) : (i += 1) Fe.sq(t1, t1);
426 Fe.mul(out, t1, t0);
427
428 t0.secureZero();
429 t1.secureZero();
430 t2.secureZero();
431 t3.secureZero();
432 }
433
434 // This could be simplified, but it would be slower
435 fn pow22523(out: *Fe, z: *const Fe) void {
436 var i: usize = undefined;
437
438 var t: [3]Fe = undefined;
439 var t0 = &t[0];
440 var t1 = &t[1];
441 var t2 = &t[2];
442
443 Fe.sq(t0, z);
444 Fe.sq(t1, t0);
445 Fe.sq(t1, t1);
446 Fe.mul(t1, z, t1);
447 Fe.mul(t0, t0, t1);
448
449 Fe.sq(t0, t0);
450 Fe.mul(t0, t1, t0);
451
452 Fe.sq(t1, t0);
453 i = 1;
454 while (i < 5) : (i += 1) Fe.sq(t1, t1);
455 Fe.mul(t0, t1, t0);
456
457 Fe.sq(t1, t0);
458 i = 1;
459 while (i < 10) : (i += 1) Fe.sq(t1, t1);
460 Fe.mul(t1, t1, t0);
461
462 Fe.sq(t2, t1);
463 i = 1;
464 while (i < 20) : (i += 1) Fe.sq(t2, t2);
465 Fe.mul(t1, t2, t1);
466
467 Fe.sq(t1, t1);
468 i = 1;
469 while (i < 10) : (i += 1) Fe.sq(t1, t1);
470 Fe.mul(t0, t1, t0);
471
472 Fe.sq(t1, t0);
473 i = 1;
474 while (i < 50) : (i += 1) Fe.sq(t1, t1);
475 Fe.mul(t1, t1, t0);
476
477 Fe.sq(t2, t1);
478 i = 1;
479 while (i < 100) : (i += 1) Fe.sq(t2, t2);
480 Fe.mul(t1, t2, t1);
481
482 Fe.sq(t1, t1);
483 i = 1;
484 while (i < 50) : (i += 1) Fe.sq(t1, t1);
485 Fe.mul(t0, t1, t0);
486
487 Fe.sq(t0, t0);
488 i = 1;
489 while (i < 2) : (i += 1) Fe.sq(t0, t0);
490 Fe.mul(out, t0, z);
491
492 t0.secureZero();
493 t1.secureZero();
494 t2.secureZero();
495 }
496
497 inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void {
498 c[i] = t[i] >> shift;
499 if (i + 1 < 10) {
500 t[i + 1] += c[i];
501 }
502 t[i] -= c[i] * (i32(1) << shift);
503 }
504
505 fn toBytes(s: []u8, h: *const Fe) void {
506 std.debug.assert(s.len >= 32);
507
508 var t: [10]i64 = undefined;
509 for (h.b[0..]) |_, i| {
510 t[i] = h.b[i];
511 }
512
513 var q = (19 * t[9] + ((i32(1) << 24))) >> 25;
514 {
515 var i: usize = 0;
516 while (i < 5) : (i += 1) {
517 q += t[2 * i];
518 q >>= 26;
519 q += t[2 * i + 1];
520 q >>= 25;
521 }
522 }
523 t[0] += 19 * q;
524
525 var c: [10]i64 = undefined;
526
527 var st = t[0..];
528 var sc = c[0..];
529
530 toBytesRound(sc, st, 0, 26);
531 toBytesRound(sc, st, 1, 25);
532 toBytesRound(sc, st, 2, 26);
533 toBytesRound(sc, st, 3, 25);
534 toBytesRound(sc, st, 4, 26);
535 toBytesRound(sc, st, 5, 25);
536 toBytesRound(sc, st, 6, 26);
537 toBytesRound(sc, st, 7, 25);
538 toBytesRound(sc, st, 8, 26);
539 toBytesRound(sc, st, 9, 25);
540
541 var ut: [10]u32 = undefined;
542 for (ut[0..]) |_, i| {
543 ut[i] = @bitCast(u32, @intCast(i32, t[i]));
544 }
545
546 writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little);
547 writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little);
548 writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little);
549 writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little);
550 writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little);
551 writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little);
552 writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little);
553 writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little);
554
555 std.mem.secureZero(i64, t[0..]);
556 }
557
558 // Parity check. Returns 0 if even, 1 if odd
559 fn isNegative(f: *const Fe) bool {
560 var s: [32]u8 = undefined;
561 Fe.toBytes(s[0..], f);
562 const isneg = s[0] & 1;
563 s.secureZero();
564 return isneg;
565 }
566
567 fn isNonZero(f: *const Fe) bool {
568 var s: [32]u8 = undefined;
569 Fe.toBytes(s[0..], f);
570 const isnonzero = zerocmp(u8, s[0..]);
571 s.secureZero();
572 return isneg;
573 }
574};
575
576test "x25519 rfc7748 vector1" {
577 const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
578 const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
579
580 const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52";
581
582 var output: [32]u8 = undefined;
583
584 std.debug.assert(X25519.create(output[0..], secret_key, public_key));
585 std.debug.assert(std.mem.eql(u8, output, expected_output));
586}
587
588test "x25519 rfc7748 vector2" {
589 const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d";
590 const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93";
591
592 const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57";
593
594 var output: [32]u8 = undefined;
595
596 std.debug.assert(X25519.create(output[0..], secret_key, public_key));
597 std.debug.assert(std.mem.eql(u8, output, expected_output));
598}
599
600test "x25519 rfc7748 one iteration" {
601 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
602 const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79";
603
604 var k: [32]u8 = initial_value;
605 var u: [32]u8 = initial_value;
606
607 var i: usize = 0;
608 while (i < 1) : (i += 1) {
609 var output: [32]u8 = undefined;
610 std.debug.assert(X25519.create(output[0..], k, u));
611
612 std.mem.copy(u8, u[0..], k[0..]);
613 std.mem.copy(u8, k[0..], output[0..]);
614 }
615
616 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
617}
618
619test "x25519 rfc7748 1,000 iterations" {
620 // These iteration tests are slow so we always skip them. Results have been verified.
621 if (true) {
622 return error.SkipZigTest;
623 }
624
625 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
626 const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51";
627
628 var k: [32]u8 = initial_value;
629 var u: [32]u8 = initial_value;
630
631 var i: usize = 0;
632 while (i < 1000) : (i += 1) {
633 var output: [32]u8 = undefined;
634 std.debug.assert(X25519.create(output[0..], k, u));
635
636 std.mem.copy(u8, u[0..], k[0..]);
637 std.mem.copy(u8, k[0..], output[0..]);
638 }
639
640 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
641}
642
643test "x25519 rfc7748 1,000,000 iterations" {
644 if (true) {
645 return error.SkipZigTest;
646 }
647
648 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
649 const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24";
650
651 var k: [32]u8 = initial_value;
652 var u: [32]u8 = initial_value;
653
654 var i: usize = 0;
655 while (i < 1000000) : (i += 1) {
656 var output: [32]u8 = undefined;
657 std.debug.assert(X25519.create(output[0..], k, u));
658
659 std.mem.copy(u8, u[0..], k[0..]);
660 std.mem.copy(u8, k[0..], output[0..]);
661 }
662
663 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
664}