| author | |
| committer | |
| log | 2bd2a8ea3430b92f0c41d602d12982f776a9a524 |
| tree | f720b52a676b7a3a743589094736dc91e59fa25f |
| parent | bc88ef2dc331733e69ed895b7a815ba0a6996dc3 |
| parent | 8b50d10a846005f221bf910b3d8928915c126c4c |
| signature |
Add poly1305 and x25519 crypto primitives12 files changed, 1209 insertions(+), 203 deletions(-)
CMakeLists.txt+3-1| ... | ... | @@ -446,13 +446,15 @@ set(ZIG_STD_FILES |
| 446 | 446 | "c/windows.zig" |
| 447 | 447 | "coff.zig" |
| 448 | 448 | "crypto/blake2.zig" |
| 449 | "crypto/chacha20.zig" | |
| 449 | 450 | "crypto/hmac.zig" |
| 450 | 451 | "crypto/index.zig" |
| 451 | 452 | "crypto/md5.zig" |
| 452 | 453 | "crypto/sha1.zig" |
| 453 | 454 | "crypto/sha2.zig" |
| 454 | 455 | "crypto/sha3.zig" |
| 455 | "crypto/chacha20.zig" | |
| 456 | "crypto/poly1305.zig" | |
| 457 | "crypto/x25519.zig" | |
| 456 | 458 | "cstr.zig" |
| 457 | 459 | "debug/failing_allocator.zig" |
| 458 | 460 | "debug/index.zig" |
std/crypto/blake2.zig+8-8| ... | ... | @@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256); |
| 34 | 34 | fn Blake2s(comptime out_len: usize) type { |
| 35 | 35 | return struct { |
| 36 | 36 | 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; | |
| 39 | 39 | |
| 40 | 40 | const iv = [8]u32{ |
| 41 | 41 | 0x6A09E667, |
| ... | ... | @@ -250,8 +250,8 @@ test "blake2s256 streaming" { |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | 252 | test "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; | |
| 255 | 255 | |
| 256 | 256 | var h = Blake2s256.init(); |
| 257 | 257 | h.update(block); |
| ... | ... | @@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512); |
| 267 | 267 | fn Blake2b(comptime out_len: usize) type { |
| 268 | 268 | return struct { |
| 269 | 269 | 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; | |
| 272 | 272 | |
| 273 | 273 | const iv = [8]u64{ |
| 274 | 274 | 0x6a09e667f3bcc908, |
| ... | ... | @@ -483,8 +483,8 @@ test "blake2b512 streaming" { |
| 483 | 483 | } |
| 484 | 484 | |
| 485 | 485 | test "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; | |
| 488 | 488 | |
| 489 | 489 | var h = Blake2b512.init(); |
| 490 | 490 | h.update(block); |
std/crypto/chacha20.zig+30-27| ... | ... | @@ -32,24 +32,28 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { |
| 32 | 32 | x[i] = input[i]; |
| 33 | 33 | |
| 34 | 34 | 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), | |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | 45 | comptime var j: usize = 0; |
| 46 | 46 | inline while (j < 20) : (j += 2) { |
| 47 | 47 | // two-round cycles |
| 48 | 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)); | |
| 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)); | |
| 53 | 57 | } |
| 54 | 58 | } |
| 55 | 59 | |
| ... | ... | @@ -166,9 +170,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32] |
| 166 | 170 | var remaining_blocks: u32 = @intCast(u32, (in.len / big_block)); |
| 167 | 171 | var i: u32 = 0; |
| 168 | 172 | 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. | |
| 172 | 175 | cursor += big_block; |
| 173 | 176 | } |
| 174 | 177 | } |
| ... | ... | @@ -199,16 +202,16 @@ test "crypto.chacha20 test vector sunscreen" { |
| 199 | 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 | 203 | var result: [114]u8 = undefined; |
| 201 | 204 | 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, | |
| 206 | 209 | }; |
| 207 | 210 | 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 | }; | |
| 212 | 215 | |
| 213 | 216 | chaCha20IETF(result[0..], input[0..], 1, key, nonce); |
| 214 | 217 | assert(mem.eql(u8, expected_result, result)); |
| ... | ... | @@ -248,7 +251,7 @@ test "crypto.chacha20 test vector 1" { |
| 248 | 251 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 249 | 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 }; | |
| 252 | 255 | |
| 253 | 256 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); |
| 254 | 257 | assert(mem.eql(u8, expected_result, result)); |
| ... | ... | @@ -282,7 +285,7 @@ test "crypto.chacha20 test vector 2" { |
| 282 | 285 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 283 | 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 }; | |
| 286 | 289 | |
| 287 | 290 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); |
| 288 | 291 | assert(mem.eql(u8, expected_result, result)); |
| ... | ... | @@ -316,7 +319,7 @@ test "crypto.chacha20 test vector 3" { |
| 316 | 319 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 317 | 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 }; | |
| 320 | 323 | |
| 321 | 324 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); |
| 322 | 325 | assert(mem.eql(u8, expected_result, result)); |
| ... | ... | @@ -350,7 +353,7 @@ test "crypto.chacha20 test vector 4" { |
| 350 | 353 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 351 | 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 }; | |
| 354 | 357 | |
| 355 | 358 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); |
| 356 | 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 | 7 | pub const HmacSha1 = Hmac(crypto.Sha1); |
| 8 | 8 | pub const HmacSha256 = Hmac(crypto.Sha256); |
| 9 | 9 | |
| 10 | pub fn Hmac(comptime H: type) type { | |
| 10 | pub fn Hmac(comptime Hash: type) type { | |
| 11 | 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; | |
| 13 | 15 | |
| 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; | |
| 18 | 30 | |
| 19 | 31 | // 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); | |
| 26 | 38 | } else { |
| 27 | mem.copy(u8, scratch[0..], key); | |
| 39 | mem.copy(u8, ctx.scratch[0..], key); | |
| 28 | 40 | } |
| 29 | 41 | |
| 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; | |
| 33 | 44 | } |
| 34 | 45 | |
| 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; | |
| 38 | 48 | } |
| 39 | 49 | |
| 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); | |
| 45 | 61 | |
| 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]); | |
| 50 | 67 | } |
| 51 | 68 | }; |
| 52 | 69 | } |
| ... | ... | @@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type { |
| 54 | 71 | const htest = @import("test.zig"); |
| 55 | 72 | |
| 56 | 73 | test "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..], "", ""); | |
| 59 | 76 | htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]); |
| 60 | 77 | |
| 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 | 79 | htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]); |
| 63 | 80 | } |
| 64 | 81 | |
| 65 | 82 | test "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..], "", ""); | |
| 68 | 85 | htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]); |
| 69 | 86 | |
| 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 | 88 | htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]); |
| 72 | 89 | } |
| 73 | 90 | |
| 74 | 91 | test "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..], "", ""); | |
| 77 | 94 | htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]); |
| 78 | 95 | |
| 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 | 97 | htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]); |
| 81 | 98 | } |
std/crypto/index.zig+10-5| ... | ... | @@ -21,19 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512; |
| 21 | 21 | |
| 22 | 22 | const hmac = @import("hmac.zig"); |
| 23 | 23 | pub const HmacMd5 = hmac.HmacMd5; |
| 24 | pub const HmacSha1 = hmac.Sha1; | |
| 25 | pub const HmacSha256 = hmac.Sha256; | |
| 24 | pub const HmacSha1 = hmac.HmacSha1; | |
| 25 | pub const HmacSha256 = hmac.HmacSha256; | |
| 26 | 26 | |
| 27 | 27 | const import_chaCha20 = @import("chacha20.zig"); |
| 28 | 28 | pub const chaCha20IETF = import_chaCha20.chaCha20IETF; |
| 29 | 29 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; |
| 30 | 30 | |
| 31 | pub const Poly1305 = @import("poly1305.zig").Poly1305; | |
| 32 | pub const X25519 = @import("x25519.zig").X25519; | |
| 33 | ||
| 31 | 34 | test "crypto" { |
| 35 | _ = @import("blake2.zig"); | |
| 36 | _ = @import("chacha20.zig"); | |
| 37 | _ = @import("hmac.zig"); | |
| 32 | 38 | _ = @import("md5.zig"); |
| 39 | _ = @import("poly1305.zig"); | |
| 33 | 40 | _ = @import("sha1.zig"); |
| 34 | 41 | _ = @import("sha2.zig"); |
| 35 | 42 | _ = @import("sha3.zig"); |
| 36 | _ = @import("blake2.zig"); | |
| 37 | _ = @import("hmac.zig"); | |
| 38 | _ = @import("chacha20.zig"); | |
| 43 | _ = @import("x25519.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 | 29 | |
| 30 | 30 | pub const Md5 = struct { |
| 31 | 31 | const Self = this; |
| 32 | const block_size = 64; | |
| 33 | const digest_size = 16; | |
| 32 | const block_length = 64; | |
| 33 | const digest_length = 16; | |
| 34 | 34 | |
| 35 | 35 | s: [4]u32, |
| 36 | 36 | // Streaming Cache |
| ... | ... | @@ -271,8 +271,8 @@ test "md5 streaming" { |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | test "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; | |
| 276 | 276 | |
| 277 | 277 | var h = Md5.init(); |
| 278 | 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 | ||
| 5 | const std = @import("../index.zig"); | |
| 6 | const builtin = @import("builtin"); | |
| 7 | ||
| 8 | const Endian = builtin.Endian; | |
| 9 | const readInt = std.mem.readInt; | |
| 10 | const writeInt = std.mem.writeInt; | |
| 11 | ||
| 12 | pub 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 | ||
| 222 | test "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 | 26 | |
| 27 | 27 | pub const Sha1 = struct { |
| 28 | 28 | const Self = this; |
| 29 | const block_size = 64; | |
| 30 | const digest_size = 20; | |
| 29 | const block_length = 64; | |
| 30 | const digest_length = 20; | |
| 31 | 31 | |
| 32 | 32 | s: [5]u32, |
| 33 | 33 | // Streaming Cache |
| ... | ... | @@ -292,8 +292,8 @@ test "sha1 streaming" { |
| 292 | 292 | } |
| 293 | 293 | |
| 294 | 294 | test "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; | |
| 297 | 297 | |
| 298 | 298 | var h = Sha1.init(); |
| 299 | 299 | h.update(block); |
std/crypto/sha2.zig+8-8| ... | ... | @@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params); |
| 78 | 78 | fn Sha2_32(comptime params: Sha2Params32) type { |
| 79 | 79 | return struct { |
| 80 | 80 | 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; | |
| 83 | 83 | |
| 84 | 84 | s: [8]u32, |
| 85 | 85 | // Streaming Cache |
| ... | ... | @@ -338,8 +338,8 @@ test "sha256 streaming" { |
| 338 | 338 | } |
| 339 | 339 | |
| 340 | 340 | test "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; | |
| 343 | 343 | |
| 344 | 344 | var h = Sha256.init(); |
| 345 | 345 | h.update(block); |
| ... | ... | @@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params); |
| 419 | 419 | fn Sha2_64(comptime params: Sha2Params64) type { |
| 420 | 420 | return struct { |
| 421 | 421 | 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; | |
| 424 | 424 | |
| 425 | 425 | s: [8]u64, |
| 426 | 426 | // Streaming Cache |
| ... | ... | @@ -715,8 +715,8 @@ test "sha512 streaming" { |
| 715 | 715 | } |
| 716 | 716 | |
| 717 | 717 | test "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; | |
| 720 | 720 | |
| 721 | 721 | var h = Sha512.init(); |
| 722 | 722 | h.update(block); |
std/crypto/sha3.zig+15-88| ... | ... | @@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06); |
| 13 | 13 | fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 14 | 14 | return struct { |
| 15 | 15 | 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; | |
| 18 | 18 | |
| 19 | 19 | s: [200]u8, |
| 20 | 20 | offset: usize, |
| ... | ... | @@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | const 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, | |
| 114 | 96 | }; |
| 115 | 97 | |
| 116 | 98 | const 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, | |
| 141 | 100 | }; |
| 142 | 101 | |
| 143 | 102 | const 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, | |
| 168 | 104 | }; |
| 169 | 105 | |
| 170 | 106 | const 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, | |
| 181 | 108 | }; |
| 182 | 109 | |
| 183 | 110 | fn keccak_f(comptime F: usize, d: []u8) void { |
| ... | ... | @@ -297,8 +224,8 @@ test "sha3-256 streaming" { |
| 297 | 224 | } |
| 298 | 225 | |
| 299 | 226 | test "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; | |
| 302 | 229 | |
| 303 | 230 | var h = Sha3_256.init(); |
| 304 | 231 | h.update(block); |
| ... | ... | @@ -368,8 +295,8 @@ test "sha3-512 streaming" { |
| 368 | 295 | } |
| 369 | 296 | |
| 370 | 297 | test "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; | |
| 373 | 300 | |
| 374 | 301 | var h = Sha3_512.init(); |
| 375 | 302 | 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 | ||
| 1 | const builtin = @import("builtin"); | |
| 8 | 2 | const std = @import("std"); |
| 9 | 3 | const time = std.os.time; |
| 10 | 4 | const Timer = time.Timer; |
| 11 | const HashFunction = @import("md5.zig").Md5; | |
| 5 | const crypto = @import("index.zig"); | |
| 12 | 6 | |
| 13 | const MiB = 1024 * 1024; | |
| 14 | const BytesToHash = 1024 * MiB; | |
| 7 | const KiB = 1024; | |
| 8 | const MiB = 1024 * KiB; | |
| 15 | 9 | |
| 16 | pub 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; | |
| 10 | var prng = std.rand.DefaultPrng.init(0); | |
| 20 | 11 | |
| 21 | var block: [HashFunction.block_size]u8 = undefined; | |
| 22 | std.mem.set(u8, block[0..], 0); | |
| 12 | const Crypto = struct { | |
| 13 | ty: type, | |
| 14 | name: []const u8, | |
| 15 | }; | |
| 23 | 16 | |
| 24 | var h = HashFunction.init(); | |
| 25 | var offset: usize = 0; | |
| 17 | const 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 | ||
| 28 | pub 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..]); | |
| 26 | 33 | |
| 34 | var offset: usize = 0; | |
| 27 | 35 | var timer = try Timer.start(); |
| 28 | 36 | const start = timer.lap(); |
| 29 | while (offset < BytesToHash) : (offset += block.len) { | |
| 37 | while (offset < bytes) : (offset += block.len) { | |
| 30 | 38 | h.update(block[0..]); |
| 31 | 39 | } |
| 32 | 40 | const end = timer.read(); |
| 33 | 41 | |
| 34 | 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 | ||
| 48 | const 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 | ||
| 55 | pub 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 | ||
| 78 | const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }}; | |
| 79 | ||
| 80 | pub 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 | } | |
| 36 | 105 | |
| 37 | try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); | |
| 106 | fn 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 | ||
| 118 | fn 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. | |
| 123 | fn 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 | ||
| 131 | pub 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 | ||
| 5 | const std = @import("../index.zig"); | |
| 6 | const builtin = @import("builtin"); | |
| 7 | ||
| 8 | const Endian = builtin.Endian; | |
| 9 | const readInt = std.mem.readInt; | |
| 10 | const writeInt = std.mem.writeInt; | |
| 11 | ||
| 12 | // Based on Supercop's ref10 implementation. | |
| 13 | pub 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. | |
| 124 | fn 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 | |
| 139 | const 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 | ||
| 576 | test "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 | ||
| 588 | test "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 | ||
| 600 | test "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 | ||
| 619 | test "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 | ||
| 643 | test "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 | } |