authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-09-30 01:12:37+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-10-01 02:04:30+02:00
log58873ed3f90325cee5c442169d609d02c71fd05a
tree2b2ece424022580ae43f6ca40cb9da116eb579e8
parent4eb390b157fcc047a707ad0a2a522911c2269cd6

std/crypto: add GHASH implementation

GHASH is required to implement AES-GCM. Optimized implementations for CPUs with instructions for carry-less multiplication will be added next.

3 files changed, 197 insertions(+), 0 deletions(-)

lib/std/crypto.zig+1
...@@ -35,6 +35,7 @@ pub const aead = struct {...@@ -35,6 +35,7 @@ pub const aead = struct {
35/// MAC functions requiring single-use secret keys.35/// MAC functions requiring single-use secret keys.
36pub const onetimeauth = struct {36pub const onetimeauth = struct {
37 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;37 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
38 pub const Ghash = @import("crypto/ghash.zig").Ghash;
38};39};
3940
40/// A password hashing function derives a uniform key from low-entropy input material such as passwords.41/// A password hashing function derives a uniform key from low-entropy input material such as passwords.
lib/std/crypto/benchmark.zig+1
...@@ -57,6 +57,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64...@@ -57,6 +57,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
57}57}
5858
59const macs = [_]Crypto{59const macs = [_]Crypto{
60 Crypto{ .ty = crypto.onetimeauth.Ghash, .name = "ghash" },
60 Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" },61 Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" },
61 Crypto{ .ty = crypto.auth.hmac.HmacMd5, .name = "hmac-md5" },62 Crypto{ .ty = crypto.auth.hmac.HmacMd5, .name = "hmac-md5" },
62 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },63 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
lib/std/crypto/ghash.zig created+195
...@@ -0,0 +1,195 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// Adapted from BearSSL's ctmul64 implementation originally written by Thomas Pornin <pornin@bolet.org>
8
9const std = @import("../std.zig");
10const assert = std.debug.assert;
11const math = std.math;
12const mem = std.mem;
13
14/// GHASH is a universal hash function that features multiplication
15/// by a fixed parameter within a Galois field.
16///
17/// It is not a general purpose hash function - The key must be secret, unpredictable and never reused.
18///
19/// GHASH is typically used to compute the authentication tag in the AES-GCM construction.
20pub const Ghash = struct {
21 pub const block_size: usize = 16;
22 pub const mac_length = 16;
23 pub const minimum_key_length = 16;
24
25 y0: u64 = 0,
26 y1: u64 = 0,
27 h0: u64,
28 h1: u64,
29 h2: u64,
30 h0r: u64,
31 h1r: u64,
32 h2r: u64,
33
34 leftover: usize = 0,
35 buf: [block_size]u8 align(16) = undefined,
36
37 pub fn init(key: []const u8) Ghash {
38 assert(key.len >= minimum_key_length);
39 const h1 = mem.readIntBig(u64, key[0..8]);
40 const h0 = mem.readIntBig(u64, key[8..16]);
41 const h1r = @bitReverse(u64, h1);
42 const h0r = @bitReverse(u64, h0);
43 const h2 = h0 ^ h1;
44 const h2r = h0r ^ h1r;
45
46 return Ghash{
47 .h0 = h0,
48 .h1 = h1,
49 .h2 = h2,
50 .h0r = h0r,
51 .h1r = h1r,
52 .h2r = h2r,
53 };
54 }
55
56 fn bmul(x: u64, y: u64) u64 {
57 const x0 = x & 0x1111111111111111;
58 const x1 = x & 0x2222222222222222;
59 const x2 = x & 0x4444444444444444;
60 const x3 = x & 0x8888888888888888;
61 const y0 = y & 0x1111111111111111;
62 const y1 = y & 0x2222222222222222;
63 const y2 = y & 0x4444444444444444;
64 const y3 = y & 0x8888888888888888;
65 var z0 = (x0 *% y0) ^ (x1 *% y3) ^ (x2 *% y2) ^ (x3 *% y1);
66 var z1 = (x0 *% y1) ^ (x1 *% y0) ^ (x2 *% y3) ^ (x3 *% y2);
67 var z2 = (x0 *% y2) ^ (x1 *% y1) ^ (x2 *% y0) ^ (x3 *% y3);
68 var z3 = (x0 *% y3) ^ (x1 *% y2) ^ (x2 *% y1) ^ (x3 *% y0);
69 z0 &= 0x1111111111111111;
70 z1 &= 0x2222222222222222;
71 z2 &= 0x4444444444444444;
72 z3 &= 0x8888888888888888;
73
74 return z0 | z1 | z2 | z3;
75 }
76
77 fn blocks(st: *Ghash, msg: []const u8) void {
78 assert(msg.len % 16 == 0); // GHASH blocks() expects full blocks
79 var y1 = st.y1;
80 var y0 = st.y0;
81
82 var i: usize = 0;
83 while (i + 16 <= msg.len) : (i += 16) {
84 y1 ^= mem.readIntBig(u64, msg[i..][0..8]);
85 y0 ^= mem.readIntBig(u64, msg[i..][8..16]);
86
87 const y1r = @bitReverse(u64, y1);
88 const y0r = @bitReverse(u64, y0);
89 const y2 = y0 ^ y1;
90 const y2r = y0r ^ y1r;
91
92 const z0 = bmul(y0, st.h0);
93 const z1 = bmul(y1, st.h1);
94 var z2 = bmul(y2, st.h2);
95 var z0h = bmul(y0r, st.h0r);
96 var z1h = bmul(y1r, st.h1r);
97 var z2h = bmul(y2r, st.h2r);
98 z2 ^= z0 ^ z1;
99 z2h ^= z0h ^ z1h;
100 z0h = @bitReverse(u64, z0h) >> 1;
101 z1h = @bitReverse(u64, z1h) >> 1;
102 z2h = @bitReverse(u64, z2h) >> 1;
103
104 var v3 = z1h;
105 var v2 = z1 ^ z2h;
106 var v1 = z0h ^ z2;
107 var v0 = z0;
108
109 v3 = (v3 << 1) | (v2 >> 63);
110 v2 = (v2 << 1) | (v1 >> 63);
111 v1 = (v1 << 1) | (v0 >> 63);
112 v0 = (v0 << 1);
113
114 v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
115 v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
116 y1 = v3 ^ v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
117 y0 = v2 ^ (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
118 }
119 st.y1 = y1;
120 st.y0 = y0;
121 }
122
123 pub fn update(st: *Ghash, m: []const u8) void {
124 var mb = m;
125
126 if (st.leftover > 0) {
127 const want = math.min(block_size - st.leftover, mb.len);
128 const mc = mb[0..want];
129 for (mc) |x, i| {
130 st.buf[st.leftover + i] = x;
131 }
132 mb = mb[want..];
133 st.leftover += want;
134 if (st.leftover > block_size) {
135 return;
136 }
137 st.blocks(&st.buf);
138 st.leftover = 0;
139 }
140 if (mb.len >= block_size) {
141 const want = mb.len & ~(block_size - 1);
142 st.blocks(mb[0..want]);
143 mb = mb[want..];
144 }
145 if (mb.len > 0) {
146 for (mb) |x, i| {
147 st.buf[st.leftover + i] = x;
148 }
149 st.leftover += mb.len;
150 }
151 }
152
153 pub fn final(st: *Ghash, out: []u8) void {
154 assert(out.len >= mac_length);
155 if (st.leftover > 0) {
156 var i = st.leftover;
157 while (i < block_size) : (i += 1) {
158 st.buf[i] = 0;
159 }
160 st.blocks(&st.buf);
161 }
162 mem.writeIntBig(u64, out[0..8], st.y1);
163 mem.writeIntBig(u64, out[8..16], st.y0);
164
165 mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Ghash)]);
166 }
167
168 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
169 std.debug.assert(out.len >= mac_length);
170 std.debug.assert(key.len >= minimum_key_length);
171
172 var st = Ghash.init(key);
173 st.update(msg);
174 st.final(out);
175 }
176};
177
178const htest = @import("test.zig");
179
180test "ghash" {
181 const key = [_]u8{0x42} ** 16;
182 const m = [_]u8{0x69} ** 256;
183
184 var st = Ghash.init(&key);
185 st.update(&m);
186 var out: [16]u8 = undefined;
187 st.final(&out);
188 htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out);
189
190 st = Ghash.init(&key);
191 st.update(m[0..100]);
192 st.update(m[100..]);
193 st.final(&out);
194 htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out);
195}