| ... | ... | @@ -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 | |
| 9 | const std = @import("../std.zig"); |
| 10 | const assert = std.debug.assert; |
| 11 | const math = std.math; |
| 12 | const 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. |
| 20 | pub 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 | |
| 178 | const htest = @import("test.zig"); |
| 179 | |
| 180 | test "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 | } |