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
446446 "c/windows.zig"
447447 "coff.zig"
448448 "crypto/blake2.zig"
449 "crypto/chacha20.zig"
449450 "crypto/hmac.zig"
450451 "crypto/index.zig"
451452 "crypto/md5.zig"
452453 "crypto/sha1.zig"
453454 "crypto/sha2.zig"
454455 "crypto/sha3.zig"
455 "crypto/chacha20.zig"
456 "crypto/poly1305.zig"
457 "crypto/x25519.zig"
456458 "cstr.zig"
457459 "debug/failing_allocator.zig"
458460 "debug/index.zig"
std/crypto/blake2.zig+8-8
......@@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256);
3434fn Blake2s(comptime out_len: usize) type {
3535 return struct {
3636 const Self = this;
37 const block_size = 64;
38 const digest_size = out_len / 8;
37 const block_length = 64;
38 const digest_length = out_len / 8;
3939
4040 const iv = [8]u32{
4141 0x6A09E667,
......@@ -250,8 +250,8 @@ test "blake2s256 streaming" {
250250}
251251
252252test "blake2s256 aligned final" {
253 var block = []u8{0} ** Blake2s256.block_size;
254 var out: [Blake2s256.digest_size]u8 = undefined;
253 var block = []u8{0} ** Blake2s256.block_length;
254 var out: [Blake2s256.digest_length]u8 = undefined;
255255
256256 var h = Blake2s256.init();
257257 h.update(block);
......@@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512);
267267fn Blake2b(comptime out_len: usize) type {
268268 return struct {
269269 const Self = this;
270 const block_size = 128;
271 const digest_size = out_len / 8;
270 const block_length = 128;
271 const digest_length = out_len / 8;
272272
273273 const iv = [8]u64{
274274 0x6a09e667f3bcc908,
......@@ -483,8 +483,8 @@ test "blake2b512 streaming" {
483483}
484484
485485test "blake2b512 aligned final" {
486 var block = []u8{0} ** Blake2b512.block_size;
487 var out: [Blake2b512.digest_size]u8 = undefined;
486 var block = []u8{0} ** Blake2b512.block_length;
487 var out: [Blake2b512.digest_length]u8 = undefined;
488488
489489 var h = Blake2b512.init();
490490 h.update(block);
std/crypto/chacha20.zig+30-27
......@@ -32,24 +32,28 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
3232 x[i] = input[i];
3333
3434 const rounds = comptime []QuarterRound{
35 Rp( 0, 4, 8,12),
36 Rp( 1, 5, 9,13),
37 Rp( 2, 6,10,14),
38 Rp( 3, 7,11,15),
39 Rp( 0, 5,10,15),
40 Rp( 1, 6,11,12),
41 Rp( 2, 7, 8,13),
42 Rp( 3, 4, 9,14),
35 Rp(0, 4, 8, 12),
36 Rp(1, 5, 9, 13),
37 Rp(2, 6, 10, 14),
38 Rp(3, 7, 11, 15),
39 Rp(0, 5, 10, 15),
40 Rp(1, 6, 11, 12),
41 Rp(2, 7, 8, 13),
42 Rp(3, 4, 9, 14),
4343 };
4444
4545 comptime var j: usize = 0;
4646 inline while (j < 20) : (j += 2) {
4747 // two-round cycles
4848 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));
50 x[r.c] +%= x[r.d]; x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12));
51 x[r.a] +%= x[r.b]; x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8));
52 x[r.c] +%= x[r.d]; x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7));
49 x[r.a] +%= x[r.b];
50 x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16));
51 x[r.c] +%= x[r.d];
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));
5357 }
5458 }
5559
......@@ -166,9 +170,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
166170 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
167171 var i: u32 = 0;
168172 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
169 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't
171 // know about this.
173 chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
174 c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
172175 cursor += big_block;
173176 }
174177 }
......@@ -199,16 +202,16 @@ test "crypto.chacha20 test vector sunscreen" {
199202 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.";
200203 var result: [114]u8 = undefined;
201204 const key = []u8{
202 0, 1, 2, 3, 4, 5, 6, 7,
203 8, 9,10,11,12,13,14,15,
204 16,17,18,19,20,21,22,23,
205 24,25,26,27,28,29,30,31,
205 0, 1, 2, 3, 4, 5, 6, 7,
206 8, 9, 10, 11, 12, 13, 14, 15,
207 16, 17, 18, 19, 20, 21, 22, 23,
208 24, 25, 26, 27, 28, 29, 30, 31,
206209 };
207210 const nonce = []u8{
208 0, 0, 0, 0,
209 0, 0, 0, 0x4a,
210 0, 0, 0, 0,
211 };
211 0, 0, 0, 0,
212 0, 0, 0, 0x4a,
213 0, 0, 0, 0,
214 };
212215
213216 chaCha20IETF(result[0..], input[0..], 1, key, nonce);
214217 assert(mem.eql(u8, expected_result, result));
......@@ -248,7 +251,7 @@ test "crypto.chacha20 test vector 1" {
248251 0, 0, 0, 0, 0, 0, 0, 0,
249252 0, 0, 0, 0, 0, 0, 0, 0,
250253 };
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
253256 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
254257 assert(mem.eql(u8, expected_result, result));
......@@ -282,7 +285,7 @@ test "crypto.chacha20 test vector 2" {
282285 0, 0, 0, 0, 0, 0, 0, 0,
283286 0, 0, 0, 0, 0, 0, 0, 1,
284287 };
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
287290 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
288291 assert(mem.eql(u8, expected_result, result));
......@@ -316,7 +319,7 @@ test "crypto.chacha20 test vector 3" {
316319 0, 0, 0, 0, 0, 0, 0, 0,
317320 0, 0, 0, 0, 0, 0, 0, 0,
318321 };
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
321324 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
322325 assert(mem.eql(u8, expected_result, result));
......@@ -350,7 +353,7 @@ test "crypto.chacha20 test vector 4" {
350353 0, 0, 0, 0, 0, 0, 0, 0,
351354 0, 0, 0, 0, 0, 0, 0, 0,
352355 };
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
355358 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
356359 assert(mem.eql(u8, expected_result, result));
std/crypto/hmac.zig+54-37
......@@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5);
77pub const HmacSha1 = Hmac(crypto.Sha1);
88pub const HmacSha256 = Hmac(crypto.Sha256);
99
10pub fn Hmac(comptime H: type) type {
10pub fn Hmac(comptime Hash: type) type {
1111 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 {
15 debug.assert(output.len >= H.digest_size);
16 debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
17 var scratch: [H.block_size]u8 = undefined;
16 o_key_pad: [Hash.block_length]u8,
17 i_key_pad: [Hash.block_length]u8,
18 scratch: [Hash.block_length]u8,
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
1931 // Normalize key length to block size of hash
20 if (key.len > H.block_size) {
21 H.hash(key, scratch[0..H.digest_size]);
22 mem.set(u8, scratch[H.digest_size..H.block_size], 0);
23 } else if (key.len < H.block_size) {
24 mem.copy(u8, scratch[0..key.len], key);
25 mem.set(u8, scratch[key.len..H.block_size], 0);
32 if (key.len > Hash.block_length) {
33 Hash.hash(key, ctx.scratch[0..mac_length]);
34 mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0);
35 } else if (key.len < Hash.block_length) {
36 mem.copy(u8, ctx.scratch[0..key.len], key);
37 mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0);
2638 } else {
27 mem.copy(u8, scratch[0..], key);
39 mem.copy(u8, ctx.scratch[0..], key);
2840 }
2941
30 var o_key_pad: [H.block_size]u8 = undefined;
31 for (o_key_pad) |*b, i| {
32 b.* = scratch[i] ^ 0x5c;
42 for (ctx.o_key_pad) |*b, i| {
43 b.* = ctx.scratch[i] ^ 0x5c;
3344 }
3445
35 var i_key_pad: [H.block_size]u8 = undefined;
36 for (i_key_pad) |*b, i| {
37 b.* = scratch[i] ^ 0x36;
46 for (ctx.i_key_pad) |*b, i| {
47 b.* = ctx.scratch[i] ^ 0x36;
3848 }
3949
40 // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation
41 var hmac = H.init();
42 hmac.update(i_key_pad[0..]);
43 hmac.update(message);
44 hmac.final(scratch[0..H.digest_size]);
50 ctx.hash = Hash.init();
51 ctx.hash.update(ctx.i_key_pad[0..]);
52 return ctx;
53 }
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();
47 hmac.update(o_key_pad[0..]);
48 hmac.update(scratch[0..H.digest_size]);
49 hmac.final(output[0..H.digest_size]);
62 ctx.hash.final(ctx.scratch[0..mac_length]);
63 ctx.hash.reset();
64 ctx.hash.update(ctx.o_key_pad[0..]);
65 ctx.hash.update(ctx.scratch[0..mac_length]);
66 ctx.hash.final(out[0..mac_length]);
5067 }
5168 };
5269}
......@@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type {
5471const htest = @import("test.zig");
5572
5673test "hmac md5" {
57 var out: [crypto.Md5.digest_size]u8 = undefined;
58 HmacMd5.hash(out[0..], "", "");
74 var out: [HmacMd5.mac_length]u8 = undefined;
75 HmacMd5.create(out[0..], "", "");
5976 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");
6279 htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
6380}
6481
6582test "hmac sha1" {
66 var out: [crypto.Sha1.digest_size]u8 = undefined;
67 HmacSha1.hash(out[0..], "", "");
83 var out: [HmacSha1.mac_length]u8 = undefined;
84 HmacSha1.create(out[0..], "", "");
6885 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");
7188 htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
7289}
7390
7491test "hmac sha256" {
75 var out: [crypto.Sha256.digest_size]u8 = undefined;
76 HmacSha256.hash(out[0..], "", "");
92 var out: [HmacSha256.mac_length]u8 = undefined;
93 HmacSha256.create(out[0..], "", "");
7794 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");
8097 htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
8198}
std/crypto/index.zig+10-5
......@@ -21,19 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512;
2121
2222const hmac = @import("hmac.zig");
2323pub const HmacMd5 = hmac.HmacMd5;
24pub const HmacSha1 = hmac.Sha1;
25pub const HmacSha256 = hmac.Sha256;
24pub const HmacSha1 = hmac.HmacSha1;
25pub const HmacSha256 = hmac.HmacSha256;
2626
2727const import_chaCha20 = @import("chacha20.zig");
2828pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
2929pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
3030
31pub const Poly1305 = @import("poly1305.zig").Poly1305;
32pub const X25519 = @import("x25519.zig").X25519;
33
3134test "crypto" {
35 _ = @import("blake2.zig");
36 _ = @import("chacha20.zig");
37 _ = @import("hmac.zig");
3238 _ = @import("md5.zig");
39 _ = @import("poly1305.zig");
3340 _ = @import("sha1.zig");
3441 _ = @import("sha2.zig");
3542 _ = @import("sha3.zig");
36 _ = @import("blake2.zig");
37 _ = @import("hmac.zig");
38 _ = @import("chacha20.zig");
43 _ = @import("x25519.zig");
3944}
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
2929
3030pub const Md5 = struct {
3131 const Self = this;
32 const block_size = 64;
33 const digest_size = 16;
32 const block_length = 64;
33 const digest_length = 16;
3434
3535 s: [4]u32,
3636 // Streaming Cache
......@@ -271,8 +271,8 @@ test "md5 streaming" {
271271}
272272
273273test "md5 aligned final" {
274 var block = []u8{0} ** Md5.block_size;
275 var out: [Md5.digest_size]u8 = undefined;
274 var block = []u8{0} ** Md5.block_length;
275 var out: [Md5.digest_length]u8 = undefined;
276276
277277 var h = Md5.init();
278278 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 {
2626
2727pub const Sha1 = struct {
2828 const Self = this;
29 const block_size = 64;
30 const digest_size = 20;
29 const block_length = 64;
30 const digest_length = 20;
3131
3232 s: [5]u32,
3333 // Streaming Cache
......@@ -292,8 +292,8 @@ test "sha1 streaming" {
292292}
293293
294294test "sha1 aligned final" {
295 var block = []u8{0} ** Sha1.block_size;
296 var out: [Sha1.digest_size]u8 = undefined;
295 var block = []u8{0} ** Sha1.block_length;
296 var out: [Sha1.digest_length]u8 = undefined;
297297
298298 var h = Sha1.init();
299299 h.update(block);
std/crypto/sha2.zig+8-8
......@@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params);
7878fn Sha2_32(comptime params: Sha2Params32) type {
7979 return struct {
8080 const Self = this;
81 const block_size = 64;
82 const digest_size = params.out_len / 8;
81 const block_length = 64;
82 const digest_length = params.out_len / 8;
8383
8484 s: [8]u32,
8585 // Streaming Cache
......@@ -338,8 +338,8 @@ test "sha256 streaming" {
338338}
339339
340340test "sha256 aligned final" {
341 var block = []u8{0} ** Sha256.block_size;
342 var out: [Sha256.digest_size]u8 = undefined;
341 var block = []u8{0} ** Sha256.block_length;
342 var out: [Sha256.digest_length]u8 = undefined;
343343
344344 var h = Sha256.init();
345345 h.update(block);
......@@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params);
419419fn Sha2_64(comptime params: Sha2Params64) type {
420420 return struct {
421421 const Self = this;
422 const block_size = 128;
423 const digest_size = params.out_len / 8;
422 const block_length = 128;
423 const digest_length = params.out_len / 8;
424424
425425 s: [8]u64,
426426 // Streaming Cache
......@@ -715,8 +715,8 @@ test "sha512 streaming" {
715715}
716716
717717test "sha512 aligned final" {
718 var block = []u8{0} ** Sha512.block_size;
719 var out: [Sha512.digest_size]u8 = undefined;
718 var block = []u8{0} ** Sha512.block_length;
719 var out: [Sha512.digest_length]u8 = undefined;
720720
721721 var h = Sha512.init();
722722 h.update(block);
std/crypto/sha3.zig+15-88
......@@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06);
1313fn Keccak(comptime bits: usize, comptime delim: u8) type {
1414 return struct {
1515 const Self = this;
16 const block_size = 200;
17 const digest_size = bits / 8;
16 const block_length = 200;
17 const digest_length = bits / 8;
1818
1919 s: [200]u8,
2020 offset: usize,
......@@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
8787}
8888
8989const RC = []const u64{
90 0x0000000000000001,
91 0x0000000000008082,
92 0x800000000000808a,
93 0x8000000080008000,
94 0x000000000000808b,
95 0x0000000080000001,
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,
90 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
91 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
92 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
93 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
94 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
95 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
11496};
11597
11698const ROTC = []const usize{
117 1,
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,
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,
141100};
142101
143102const PIL = []const usize{
144 10,
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,
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,
168104};
169105
170106const M5 = []const usize{
171 0,
172 1,
173 2,
174 3,
175 4,
176 0,
177 1,
178 2,
179 3,
180 4,
107 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
181108};
182109
183110fn keccak_f(comptime F: usize, d: []u8) void {
......@@ -297,8 +224,8 @@ test "sha3-256 streaming" {
297224}
298225
299226test "sha3-256 aligned final" {
300 var block = []u8{0} ** Sha3_256.block_size;
301 var out: [Sha3_256.digest_size]u8 = undefined;
227 var block = []u8{0} ** Sha3_256.block_length;
228 var out: [Sha3_256.digest_length]u8 = undefined;
302229
303230 var h = Sha3_256.init();
304231 h.update(block);
......@@ -368,8 +295,8 @@ test "sha3-512 streaming" {
368295}
369296
370297test "sha3-512 aligned final" {
371 var block = []u8{0} ** Sha3_512.block_size;
372 var out: [Sha3_512.digest_size]u8 = undefined;
298 var block = []u8{0} ** Sha3_512.block_length;
299 var out: [Sha3_512.digest_length]u8 = undefined;
373300
374301 var h = Sha3_512.init();
375302 h.update(block);
std/crypto/throughput_test.zig+176-21
......@@ -1,38 +1,193 @@
1// Modify the HashFunction variable to the one wanted to test.
2//
3// ```
4// zig build-exe --release-fast throughput_test.zig
5// ./throughput_test
6// ```
7
1const builtin = @import("builtin");
82const std = @import("std");
93const time = std.os.time;
104const Timer = time.Timer;
11const HashFunction = @import("md5.zig").Md5;
5const crypto = @import("index.zig");
126
13const MiB = 1024 * 1024;
14const BytesToHash = 1024 * MiB;
7const KiB = 1024;
8const MiB = 1024 * KiB;
159
16pub fn main() !void {
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;
10var prng = std.rand.DefaultPrng.init(0);
2011
21 var block: [HashFunction.block_size]u8 = undefined;
22 std.mem.set(u8, block[0..], 0);
12const Crypto = struct {
13 ty: type,
14 name: []const u8,
15};
2316
24 var h = HashFunction.init();
25 var offset: usize = 0;
17const hashes = []Crypto{
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;
2735 var timer = try Timer.start();
2836 const start = timer.lap();
29 while (offset < BytesToHash) : (offset += block.len) {
37 while (offset < bytes) : (offset += block.len) {
3038 h.update(block[0..]);
3139 }
3240 const end = timer.read();
3341
3442 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 }
38193}
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}