authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2023-03-02 07:13:40+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-02 06:13:40+00:00
log28364166e83ed52a7053029d5d7b33ad956d804d
treeb377f5a0b9967f81faa1e2c9f35a56977b9fc05a
parentdb8217f9a080f7c645a6448640a9af65f3944818
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

crypto.hash.sha3: make permutation generic and public, add SHAKE (#14756)

Make the Keccak permutation public, as it's useful for more than SHA-3 (kMAC, SHAKE, TurboSHAKE, TupleHash, etc). Our Keccak implementation was accepting f as a comptime parameter, but always used 64-bit words and 200 byte states, so it actually didn't work with anything besides f=1600. That has been fixed. The ability to use reduced-round versions was also added in order to support M14 and K12. The state was constantly converted back and forth between bytes and words, even though only a part of the state is actually used for absorbing and squeezing bytes. It was changed to something similar to the other permutations we have, so we can avoid extra copies, and eventually add vectorized implementations. In addition, the SHAKE extendable output function (XOF) was added (SHAKE128, SHAKE256). It is required by newer schemes, such as the Kyber post-quantum key exchange mechanism, whose implementation is currently blocked by SHAKE missing from our standard library. Breaking change: `Keccak_256` and `Keccak_512` were renamed to `Keccak256` and `Keccak512` for consistency with all other hash functions.

4 files changed, 419 insertions(+), 135 deletions(-)

lib/std/crypto.zig+2
...@@ -47,6 +47,8 @@ pub const auth = struct {...@@ -47,6 +47,8 @@ pub const auth = struct {
47/// Core functions, that should rarely be used directly by applications.47/// Core functions, that should rarely be used directly by applications.
48pub const core = struct {48pub const core = struct {
49 pub const aes = @import("crypto/aes.zig");49 pub const aes = @import("crypto/aes.zig");
50 pub const keccak = @import("crypto/keccak_p.zig");
51
50 pub const Ascon = @import("crypto/ascon.zig").State;52 pub const Ascon = @import("crypto/ascon.zig").State;
51 pub const Gimli = @import("crypto/gimli.zig").State;53 pub const Gimli = @import("crypto/gimli.zig").State;
52 pub const Xoodoo = @import("crypto/xoodoo.zig").State;54 pub const Xoodoo = @import("crypto/xoodoo.zig").State;
lib/std/crypto/benchmark.zig+2
...@@ -25,6 +25,8 @@ const hashes = [_]Crypto{...@@ -25,6 +25,8 @@ const hashes = [_]Crypto{
25 Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },25 Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },
26 Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },26 Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },
27 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },27 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
28 Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
29 Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },
28 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },30 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
29 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },31 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
30 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },32 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
lib/std/crypto/keccak_p.zig created+251
...@@ -0,0 +1,251 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const math = std.math;
4const mem = std.mem;
5
6/// The Keccak-f permutation.
7pub fn KeccakF(comptime f: u11) type {
8 comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid bit size
9 const T = std.meta.Int(.unsigned, f / 25);
10 const Block = [25]T;
11
12 const RC = [_]u64{
13 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
14 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
15 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
16 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
17 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
18 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
19 };
20
21 const RHO = [_]u6{
22 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
23 };
24
25 const PI = [_]u5{
26 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
27 };
28
29 return struct {
30 const Self = @This();
31
32 /// Number of bytes in the state.
33 pub const block_bytes = f / 8;
34
35 st: Block = [_]T{0} ** 25,
36
37 /// Initialize the state from a slice of bytes.
38 pub fn init(bytes: [block_bytes]u8) Self {
39 var self: Self = undefined;
40 inline for (&self.st, 0..) |*r, i| {
41 r.* = mem.readIntLittle(T, bytes[@sizeOf(T) * i ..][0..@sizeOf(T)]);
42 }
43 return self;
44 }
45
46 /// A representation of the state as bytes. The byte order is architecture-dependent.
47 pub fn asBytes(self: *Self) *[block_bytes]u8 {
48 return mem.asBytes(&self.st);
49 }
50
51 /// Byte-swap the entire state if the architecture doesn't match the required endianness.
52 pub fn endianSwap(self: *Self) void {
53 for (&self.st) |*w| {
54 w.* = mem.littleTooNative(T, w.*);
55 }
56 }
57
58 /// Set bytes starting at the beginning of the state.
59 pub fn setBytes(self: *Self, bytes: []const u8) void {
60 var i: usize = 0;
61 while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
62 self.st[i / @sizeOf(T)] = mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
63 }
64 if (i < bytes.len) {
65 var padded = [_]u8{0} ** @sizeOf(T);
66 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
67 self.st[i / @sizeOf(T)] = mem.readIntLittle(T, padded[0..]);
68 }
69 }
70
71 /// XOR a byte into the state at a given offset.
72 pub fn addByte(self: *Self, byte: u8, offset: usize) void {
73 const z = @sizeOf(T) * @truncate(math.Log2Int(T), offset % @sizeOf(T));
74 self.st[offset / @sizeOf(T)] ^= @as(T, byte) << z;
75 }
76
77 /// XOR bytes into the beginning of the state.
78 pub fn addBytes(self: *Self, bytes: []const u8) void {
79 var i: usize = 0;
80 while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
81 self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
82 }
83 if (i < bytes.len) {
84 var padded = [_]u8{0} ** @sizeOf(T);
85 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
86 self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, padded[0..]);
87 }
88 }
89
90 /// Extract the first bytes of the state.
91 pub fn extractBytes(self: *Self, out: []u8) void {
92 var i: usize = 0;
93 while (i + @sizeOf(T) <= out.len) : (i += @sizeOf(T)) {
94 mem.writeIntLittle(T, out[i..][0..@sizeOf(T)], self.st[i / @sizeOf(T)]);
95 }
96 if (i < out.len) {
97 var padded = [_]u8{0} ** @sizeOf(T);
98 mem.writeIntLittle(T, padded[0..], self.st[i / @sizeOf(T)]);
99 mem.copy(u8, out[i..], padded[0 .. out.len - i]);
100 }
101 }
102
103 /// XOR the first bytes of the state into a slice of bytes.
104 pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
105 assert(out.len == in.len);
106
107 var i: usize = 0;
108 while (i + @sizeOf(T) <= in.len) : (i += @sizeOf(T)) {
109 const x = mem.readIntNative(T, in[i..][0..@sizeOf(T)]) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
110 mem.writeIntNative(T, out[i..][0..@sizeOf(T)], x);
111 }
112 if (i < in.len) {
113 var padded = [_]u8{0} ** @sizeOf(T);
114 mem.copy(u8, padded[0 .. in.len - i], in[i..]);
115 const x = mem.readIntNative(T, &padded) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
116 mem.writeIntNative(T, &padded, x);
117 mem.copy(u8, out[i..], padded[0 .. in.len - i]);
118 }
119 }
120
121 /// Set the words storing the bytes of a given range to zero.
122 pub fn clear(self: *Self, from: usize, to: usize) void {
123 mem.set(T, self.st[from / @sizeOf(T) .. (to + @sizeOf(T) - 1) / @sizeOf(T)], 0);
124 }
125
126 /// Clear the entire state, disabling compiler optimizations.
127 pub fn secureZero(self: *Self) void {
128 std.crypto.utils.secureZero(T, &self.st);
129 }
130
131 inline fn round(self: *Self, rc: T) void {
132 const st = &self.st;
133
134 // theta
135 var t = [_]T{0} ** 5;
136 inline for (0..5) |i| {
137 inline for (0..5) |j| {
138 t[i] ^= st[j * 5 + i];
139 }
140 }
141 inline for (0..5) |i| {
142 inline for (0..5) |j| {
143 st[j * 5 + i] ^= t[(i + 4) % 5] ^ math.rotl(T, t[(i + 1) % 5], 1);
144 }
145 }
146
147 // rho+pi
148 var last = st[1];
149 inline for (0..24) |i| {
150 const x = PI[i];
151 const tmp = st[x];
152 st[x] = math.rotl(T, last, RHO[i]);
153 last = tmp;
154 }
155 inline for (0..5) |i| {
156 inline for (0..5) |j| {
157 t[j] = st[i * 5 + j];
158 }
159 inline for (0..5) |j| {
160 st[i * 5 + j] = t[j] ^ (~t[(j + 1) % 5] & t[(j + 2) % 5]);
161 }
162 }
163
164 // iota
165 st[0] ^= rc;
166 }
167
168 /// Apply a (possibly) reduced-round permutation to the state.
169 pub fn permuteR(self: *Self, comptime rounds: u5) void {
170 var i = RC.len - rounds;
171 while (i < rounds - rounds % 3) : (i += 3) {
172 self.round(RC[i]);
173 self.round(RC[i + 1]);
174 self.round(RC[i + 2]);
175 }
176 while (i < rounds) : (i += 1) {
177 self.round(RC[i]);
178 }
179 }
180
181 /// Apply a full-round permutation to the state.
182 pub fn permute(self: *Self) void {
183 self.permuteR(comptime 12 + 2 * math.log2(f / 25));
184 }
185 };
186}
187
188/// A generic Keccak-P state.
189pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type {
190 comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size
191 comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size
192
193 return struct {
194 const Self = @This();
195
196 /// The block length, or rate, in bytes.
197 pub const rate = KeccakF(f).block_bytes - capacity / 8;
198 /// Keccak does not have any options.
199 pub const Options = struct {};
200
201 offset: usize = 0,
202 buf: [rate]u8 = undefined,
203
204 st: KeccakF(f) = .{},
205
206 /// Absorb a slice of bytes into the sponge.
207 pub fn absorb(self: *Self, bytes_: []const u8) void {
208 var bytes = bytes_;
209 if (self.offset > 0) {
210 const left = math.min(rate - self.offset, bytes.len);
211 mem.copy(u8, self.buf[self.offset..], bytes[0..left]);
212 self.offset += left;
213 if (self.offset == rate) {
214 self.offset = 0;
215 self.st.addBytes(self.buf[0..]);
216 self.st.permuteR(rounds);
217 }
218 if (left == bytes.len) return;
219 bytes = bytes[left..];
220 }
221 while (bytes.len >= rate) {
222 self.st.addBytes(bytes[0..rate]);
223 self.st.permuteR(rounds);
224 bytes = bytes[rate..];
225 }
226 if (bytes.len > 0) {
227 self.st.addBytes(bytes[0..]);
228 self.offset = bytes.len;
229 }
230 }
231
232 /// Mark the end of the input.
233 pub fn pad(self: *Self) void {
234 self.st.addBytes(self.buf[0..self.offset]);
235 self.st.addByte(delim, self.offset);
236 self.st.addByte(0x80, rate - 1);
237 self.st.permuteR(rounds);
238 self.offset = 0;
239 }
240
241 /// Squeeze a slice of bytes from the sponge.
242 pub fn squeeze(self: *Self, out: []u8) void {
243 var i: usize = 0;
244 while (i < out.len) : (i += rate) {
245 const left = math.min(rate, out.len - i);
246 self.st.extractBytes(out[i..][0..left]);
247 self.st.permuteR(rounds);
248 }
249 }
250 };
251}
lib/std/crypto/sha3.zig+164-135
...@@ -1,84 +1,63 @@...@@ -1,84 +1,63 @@
1const std = @import("../std.zig");1const std = @import("std");
2const mem = std.mem;2const assert = std.debug.assert;
3const math = std.math;3const math = std.math;
4const debug = std.debug;4const mem = std.mem;
5const htest = @import("test.zig");5
6const KeccakState = std.crypto.core.keccak.State;
7
8pub const Sha3_224 = Keccak(1600, 224, 0x06, 24);
9pub const Sha3_256 = Keccak(1600, 256, 0x06, 24);
10pub const Sha3_384 = Keccak(1600, 384, 0x06, 24);
11pub const Sha3_512 = Keccak(1600, 512, 0x06, 24);
12
13pub const Keccak256 = Keccak(1600, 256, 0x01, 24);
14pub const Keccak512 = Keccak(1600, 512, 0x01, 24);
15pub const Keccak_256 = @compileError("Deprecated: use `Keccak256` instead");
16pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");
17
18pub const Shake128 = Shake(128);
19pub const Shake256 = Shake(256);
20
21/// A generic Keccak hash function.
22pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
23 comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
624
7pub const Sha3_224 = Keccak(224, 0x06);25 const State = KeccakState(f, output_bits * 2, delim, rounds);
8pub const Sha3_256 = Keccak(256, 0x06);
9pub const Sha3_384 = Keccak(384, 0x06);
10pub const Sha3_512 = Keccak(512, 0x06);
11pub const Keccak_256 = Keccak(256, 0x01);
12pub const Keccak_512 = Keccak(512, 0x01);
1326
14fn Keccak(comptime bits: usize, comptime delim: u8) type {
15 return struct {27 return struct {
16 const Self = @This();28 const Self = @This();
29
30 st: State = .{},
31
17 /// The output length, in bytes.32 /// The output length, in bytes.
18 pub const digest_length = bits / 8;33 pub const digest_length = output_bits / 8;
19 /// The block length, or rate, in bytes.34 /// The block length, or rate, in bytes.
20 pub const block_length = 200 - bits / 4;35 pub const block_length = State.rate;
21 /// Keccak does not have any options.36 /// Keccak does not have any options.
22 pub const Options = struct {};37 pub const Options = struct {};
2338
24 s: [200]u8,39 /// Initialize a Keccak hash function.
25 offset: usize,
26
27 pub fn init(options: Options) Self {40 pub fn init(options: Options) Self {
28 _ = options;41 _ = options;
29 return Self{ .s = [_]u8{0} ** 200, .offset = 0 };42 return Self{};
30 }43 }
3144
32 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {45 /// Hash a slice of bytes.
33 var d = Self.init(options);46 pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void {
34 d.update(b);47 var st = Self.init(options);
35 d.final(out);48 st.update(bytes);
49 st.final(out);
36 }50 }
3751
38 pub fn update(d: *Self, b: []const u8) void {52 /// Absorb a slice of bytes into the state.
39 var ip: usize = 0;53 pub fn update(self: *Self, bytes: []const u8) void {
40 var len = b.len;54 self.st.absorb(bytes);
41 var rate = block_length - d.offset;
42 var offset = d.offset;
43
44 // absorb
45 while (len >= rate) {
46 for (d.s[offset .. offset + rate], 0..) |*r, i|
47 r.* ^= b[ip..][i];
48
49 keccakF(1600, &d.s);
50
51 ip += rate;
52 len -= rate;
53 rate = block_length;
54 offset = 0;
55 }
56
57 for (d.s[offset .. offset + len], 0..) |*r, i|
58 r.* ^= b[ip..][i];
59
60 d.offset = offset + len;
61 }55 }
6256
63 pub fn final(d: *Self, out: *[digest_length]u8) void {57 /// Return the hash of the absorbed bytes.
64 // padding58 pub fn final(self: *Self, out: *[digest_length]u8) void {
65 d.s[d.offset] ^= delim;59 self.st.pad();
66 d.s[block_length - 1] ^= 0x80;60 self.st.squeeze(out[0..]);
67
68 keccakF(1600, &d.s);
69
70 // squeeze
71 var op: usize = 0;
72 var len: usize = bits / 8;
73
74 while (len >= block_length) {
75 mem.copy(u8, out[op..], d.s[0..block_length]);
76 keccakF(1600, &d.s);
77 op += block_length;
78 len -= block_length;
79 }
80
81 mem.copy(u8, out[op..], d.s[0..len]);
82 }61 }
8362
84 pub const Error = error{};63 pub const Error = error{};
...@@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {...@@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
95 };74 };
96}75}
9776
98const RC = [_]u64{77/// The SHAKE extendable output hash function.
99 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,78pub fn Shake(comptime security_level: u11) type {
100 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,79 const f = 1600;
101 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,80 const rounds = 24;
102 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,81 const State = KeccakState(f, security_level * 2, 0x1f, rounds);
103 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
104 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
105};
106
107const ROTC = [_]usize{
108 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
109};
110
111const PIL = [_]usize{
112 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
113};
114
115const M5 = [_]usize{
116 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
117};
118
119fn keccakF(comptime F: usize, d: *[F / 8]u8) void {
120 const B = F / 25;
121 const no_rounds = comptime x: {
122 break :x 12 + 2 * math.log2(B);
123 };
12482
125 var s = [_]u64{0} ** 25;83 return struct {
126 var t = [_]u64{0} ** 1;84 const Self = @This();
127 var c = [_]u64{0} ** 5;
12885
129 for (&s, 0..) |*r, i| {86 st: State = .{},
130 r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);87 buf: [State.rate]u8 = undefined,
131 }88 offset: usize = 0,
89 padded: bool = false,
13290
133 for (RC[0..no_rounds]) |round| {91 /// The recommended output length, in bytes.
134 // theta92 pub const digest_length = security_level / 2;
135 comptime var x: usize = 0;93 /// The block length, or rate, in bytes.
136 inline while (x < 5) : (x += 1) {94 pub const block_length = State.rate;
137 c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];95 /// Keccak does not have any options.
96 pub const Options = struct {};
97
98 /// Initialize a SHAKE extensible hash function.
99 pub fn init(options: Options) Self {
100 _ = options;
101 return Self{};
138 }102 }
139 x = 0;103
140 inline while (x < 5) : (x += 1) {104 /// Hash a slice of bytes.
141 t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1));105 /// `out` can be any length.
142 comptime var y: usize = 0;106 pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
143 inline while (y < 5) : (y += 1) {107 var st = Self.init(options);
144 s[x + y * 5] ^= t[0];108 st.update(bytes);
145 }109 st.squeeze(out);
146 }110 }
147111
148 // rho+pi112 /// Absorb a slice of bytes into the state.
149 t[0] = s[1];113 pub fn update(self: *Self, bytes: []const u8) void {
150 x = 0;114 self.st.absorb(bytes);
151 inline while (x < 24) : (x += 1) {
152 c[0] = s[PIL[x]];
153 s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]);
154 t[0] = c[0];
155 }115 }
156116
157 // chi117 /// Squeeze a slice of bytes from the state.
158 comptime var y: usize = 0;118 /// `out` can be any length, and the function can be called multiple times.
159 inline while (y < 5) : (y += 1) {119 pub fn squeeze(self: *Self, out_: []u8) void {
160 x = 0;120 if (!self.padded) {
161 inline while (x < 5) : (x += 1) {121 self.st.pad();
162 c[x] = s[x + y * 5];122 self.padded = true;
123 }
124 var out = out_;
125 if (self.offset > 0) {
126 const left = self.buf.len - self.offset;
127 if (left > 0) {
128 const n = math.min(left, out.len);
129 mem.copy(u8, out[0..n], self.buf[self.offset..][0..n]);
130 out = out[n..];
131 self.offset += n;
132 if (out.len == 0) {
133 return;
134 }
135 }
163 }136 }
164 x = 0;137 const full_blocks = out[0 .. out.len - out.len % State.rate];
165 inline while (x < 5) : (x += 1) {138 if (full_blocks.len > 0) {
166 s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]);139 self.st.squeeze(full_blocks);
140 out = out[full_blocks.len..];
141 }
142 if (out.len > 0) {
143 self.st.squeeze(self.buf[0..]);
144 mem.copy(u8, out[0..], self.buf[0..out.len]);
145 self.offset = out.len;
167 }146 }
168 }147 }
169148
170 // iota149 /// Return the hash of the absorbed bytes.
171 s[0] ^= round;150 /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
172 }151 pub fn final(self: *Self, out: []u8) void {
152 self.squeeze(out);
153 self.st.st.clear(0, State.rate);
154 }
173155
174 for (s, 0..) |r, i| {156 pub const Error = error{};
175 mem.writeIntLittle(u64, d[8 * i ..][0..8], r);157 pub const Writer = std.io.Writer(*Self, Error, write);
176 }158
159 fn write(self: *Self, bytes: []const u8) Error!usize {
160 self.update(bytes);
161 return bytes.len;
162 }
163
164 pub fn writer(self: *Self) Writer {
165 return .{ .context = self };
166 }
167 };
177}168}
178169
170const htest = @import("test.zig");
171
179test "sha3-224 single" {172test "sha3-224 single" {
180 try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");173 try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
181 try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");174 try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
...@@ -309,13 +302,49 @@ test "sha3-512 aligned final" {...@@ -309,13 +302,49 @@ test "sha3-512 aligned final" {
309}302}
310303
311test "keccak-256 single" {304test "keccak-256 single" {
312 try htest.assertEqualHash(Keccak_256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");305 try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
313 try htest.assertEqualHash(Keccak_256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");306 try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
314 try htest.assertEqualHash(Keccak_256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");307 try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
315}308}
316309
317test "keccak-512 single" {310test "keccak-512 single" {
318 try htest.assertEqualHash(Keccak_512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");311 try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
319 try htest.assertEqualHash(Keccak_512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");312 try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
320 try htest.assertEqualHash(Keccak_512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");313 try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
314}
315
316test "SHAKE-128 single" {
317 var out: [10]u8 = undefined;
318 Shake128.hash("hello123", &out, .{});
319 try htest.assertEqual("1b85861510bc4d8e467d", &out);
320}
321
322test "SHAKE-128 multisqueeze" {
323 var out: [10]u8 = undefined;
324 var h = Shake128.init(.{});
325 h.update("hello123");
326 h.squeeze(out[0..4]);
327 h.squeeze(out[4..]);
328 try htest.assertEqual("1b85861510bc4d8e467d", &out);
329}
330
331test "SHAKE-128 multisqueeze with multiple blocks" {
332 var out: [100]u8 = undefined;
333 var out2: [100]u8 = undefined;
334
335 var h = Shake128.init(.{});
336 h.update("hello123");
337 h.squeeze(out[0..50]);
338 h.squeeze(out[50..]);
339
340 var h2 = Shake128.init(.{});
341 h2.update("hello123");
342 h2.squeeze(&out2);
343 try std.testing.expectEqualSlices(u8, &out, &out2);
344}
345
346test "SHAKE-256 single" {
347 var out: [10]u8 = undefined;
348 Shake256.hash("hello123", &out, .{});
349 try htest.assertEqual("ade612ba265f92de4a37", &out);
321}350}