master
1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4const math = std.math;
5const mem = std.mem;
6
7const kangarootwelve = @import("kangarootwelve.zig");
8
9const KeccakState = std.crypto.core.keccak.State;
10
11pub const Sha3_224 = Keccak(1600, 224, 0x06, 24);
12pub const Sha3_256 = Keccak(1600, 256, 0x06, 24);
13pub const Sha3_384 = Keccak(1600, 384, 0x06, 24);
14pub const Sha3_512 = Keccak(1600, 512, 0x06, 24);
15
16pub const Keccak256 = Keccak(1600, 256, 0x01, 24);
17pub const Keccak512 = Keccak(1600, 512, 0x01, 24);
18
19pub const Shake128 = Shake(128);
20pub const Shake256 = Shake(256);
21
22pub const CShake128 = CShake(128, null);
23pub const CShake256 = CShake(256, null);
24
25pub const KMac128 = KMac(128);
26pub const KMac256 = KMac(256);
27
28pub const TupleHash128 = TupleHash(128);
29pub const TupleHash256 = TupleHash(256);
30
31pub const KT128 = kangarootwelve.KT128;
32pub const KT256 = kangarootwelve.KT256;
33
34/// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level.
35/// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance.
36/// The delimiter is 0x1f by default, but can be changed for context-separation.
37/// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter.
38pub fn TurboShake128(delim: ?u7) type {
39 return TurboShake(128, delim);
40}
41
42/// TurboSHAKE256 is a XOF (a secure hash function with a variable output length), with a 256 bit security level.
43/// It is based on the same permutation as SHA3 and SHAKE256, but which much higher performance.
44/// The delimiter is 0x1f by default, but can be changed for context-separation.
45pub fn TurboShake256(comptime delim: ?u7) type {
46 return TurboShake(256, delim);
47}
48
49/// A generic Keccak hash function.
50pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim: u8, comptime rounds: u5) type {
51 comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
52
53 const State = KeccakState(f, output_bits * 2, rounds);
54
55 return struct {
56 const Self = @This();
57
58 st: State,
59
60 /// The output length, in bytes.
61 pub const digest_length = std.math.divCeil(comptime_int, output_bits, 8) catch unreachable;
62 /// The block length, or rate, in bytes.
63 pub const block_length = State.rate;
64 /// The delimiter can be overwritten in the options.
65 pub const Options = struct { delim: u8 = default_delim };
66
67 /// Initialize a Keccak hash function.
68 pub fn init(options: Options) Self {
69 return Self{ .st = .{ .delim = options.delim } };
70 }
71
72 /// Hash a slice of bytes.
73 pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void {
74 var st = Self.init(options);
75 st.update(bytes);
76 st.final(out);
77 }
78
79 /// Absorb a slice of bytes into the state.
80 pub fn update(self: *Self, bytes: []const u8) void {
81 self.st.absorb(bytes);
82 }
83
84 /// Return the hash of the absorbed bytes.
85 pub fn final(self: *Self, out: *[digest_length]u8) void {
86 self.st.pad();
87 self.st.squeeze(out[0..]);
88 }
89 };
90}
91
92/// The SHAKE extendable output hash function.
93pub fn Shake(comptime security_level: u11) type {
94 return ShakeLike(security_level, 0x1f, 24);
95}
96
97/// The TurboSHAKE extendable output hash function.
98/// It is based on the same permutation as SHA3 and SHAKE, but which much higher performance.
99/// The delimiter is 0x1f by default, but can be changed for context-separation.
100/// https://eprint.iacr.org/2023/342
101pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type {
102 comptime assert(security_level <= 256);
103 const d = delim orelse 0x1f;
104 comptime assert(d >= 0x01); // delimiter must be >= 1
105 return ShakeLike(security_level, d, 12);
106}
107
108fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type {
109 const f = 1600;
110 const State = KeccakState(f, security_level * 2, rounds);
111
112 return struct {
113 const Self = @This();
114
115 st: State,
116 buf: [State.rate]u8 = undefined,
117 offset: usize = 0,
118 padded: bool = false,
119
120 /// The recommended output length, in bytes.
121 pub const digest_length = security_level / 8 * 2;
122 /// The block length, or rate, in bytes.
123 pub const block_length = State.rate;
124 /// The delimiter can be overwritten in the options.
125 pub const Options = struct { delim: u8 = default_delim };
126
127 /// Initialize a SHAKE extensible hash function.
128 pub fn init(options: Options) Self {
129 return Self{ .st = .{ .delim = options.delim } };
130 }
131
132 /// Hash a slice of bytes.
133 /// `out` can be any length.
134 pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
135 var st = Self.init(options);
136 st.update(bytes);
137 st.squeeze(out);
138 }
139
140 /// Absorb a slice of bytes into the state.
141 pub fn update(self: *Self, bytes: []const u8) void {
142 self.st.absorb(bytes);
143 }
144
145 /// Squeeze a slice of bytes from the state.
146 /// `out` can be any length, and the function can be called multiple times.
147 pub fn squeeze(self: *Self, out_: []u8) void {
148 if (!self.padded) {
149 self.st.pad();
150 self.padded = true;
151 }
152 var out = out_;
153 if (self.offset > 0) {
154 const left = self.buf.len - self.offset;
155 if (left > 0) {
156 const n = @min(left, out.len);
157 @memcpy(out[0..n], self.buf[self.offset..][0..n]);
158 out = out[n..];
159 self.offset += n;
160 if (out.len == 0) {
161 return;
162 }
163 }
164 }
165 const full_blocks = out[0 .. out.len - out.len % State.rate];
166 if (full_blocks.len > 0) {
167 self.st.squeeze(full_blocks);
168 out = out[full_blocks.len..];
169 }
170 if (out.len > 0) {
171 self.st.squeeze(self.buf[0..]);
172 @memcpy(out[0..], self.buf[0..out.len]);
173 self.offset = out.len;
174 }
175 }
176
177 /// Return the hash of the absorbed bytes.
178 /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
179 pub fn final(self: *Self, out: []u8) void {
180 self.squeeze(out);
181 self.st.st.clear(0, State.rate);
182 }
183
184 /// Align the input to a block boundary.
185 pub fn fillBlock(self: *Self) void {
186 self.st.fillBlock();
187 }
188 };
189}
190
191/// The cSHAKE extendable output hash function.
192/// cSHAKE is similar to SHAKE, but in addition to the input message, it also takes an optional context (aka customization string).
193pub fn CShake(comptime security_level: u11, comptime fname: ?[]const u8) type {
194 return CShakeLike(security_level, 0x04, 24, fname);
195}
196
197fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5, comptime fname: ?[]const u8) type {
198 return struct {
199 const Shaker = ShakeLike(security_level, default_delim, rounds);
200 shaker: Shaker,
201
202 /// The recommended output length, in bytes.
203 pub const digest_length = Shaker.digest_length;
204 /// The block length, or rate, in bytes.
205 pub const block_length = Shaker.block_length;
206
207 /// cSHAKE options can include a context string.
208 pub const Options = struct { context: ?[]const u8 = null };
209
210 const Self = @This();
211
212 /// Initialize a SHAKE extensible hash function.
213 pub fn init(options: Options) Self {
214 if (fname == null and options.context == null) {
215 return Self{ .shaker = Shaker.init(.{ .delim = 0x1f }) };
216 }
217 var shaker = Shaker.init(.{});
218 comptime assert(Shaker.block_length % 8 == 0);
219 const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8);
220 shaker.update(encoded_rate_len.slice());
221 const encoded_zero = comptime NistLengthEncoding.encode(.left, 0);
222 if (fname) |name| {
223 const encoded_fname_len = comptime NistLengthEncoding.encode(.left, name.len);
224 const encoded_fname = comptime encoded_fname_len.slice() ++ name;
225 shaker.update(encoded_fname);
226 } else {
227 shaker.update(encoded_zero.slice());
228 }
229 if (options.context) |context| {
230 const encoded_context_len = NistLengthEncoding.encode(.left, context.len);
231 shaker.update(encoded_context_len.slice());
232 shaker.update(context);
233 } else {
234 shaker.update(encoded_zero.slice());
235 }
236 shaker.st.fillBlock();
237 return Self{ .shaker = shaker };
238 }
239
240 /// Hash a slice of bytes.
241 /// `out` can be any length.
242 pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
243 var st = Self.init(options);
244 st.update(bytes);
245 st.squeeze(out);
246 }
247
248 /// Absorb a slice of bytes into the state.
249 pub fn update(self: *Self, bytes: []const u8) void {
250 self.shaker.update(bytes);
251 }
252
253 /// Squeeze a slice of bytes from the state.
254 /// `out` can be any length, and the function can be called multiple times.
255 pub fn squeeze(self: *Self, out: []u8) void {
256 self.shaker.squeeze(out);
257 }
258
259 /// Return the hash of the absorbed bytes.
260 /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
261 pub fn final(self: *Self, out: []u8) void {
262 self.shaker.final(out);
263 }
264
265 /// Align the input to a block boundary.
266 pub fn fillBlock(self: *Self) void {
267 self.shaker.fillBlock();
268 }
269 };
270}
271
272/// The KMAC extendable output authentication function.
273/// KMAC is a keyed version of the cSHAKE function, with an optional context.
274/// It can be used as an SHA-3 based alternative to HMAC, as well as a generic keyed XoF (extendable output function).
275pub fn KMac(comptime security_level: u11) type {
276 return KMacLike(security_level, 0x04, 24);
277}
278
279fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type {
280 const CShaker = CShakeLike(security_level, default_delim, rounds, "KMAC");
281
282 return struct {
283 const Self = @This();
284
285 /// The recommended output length, in bytes.
286 pub const mac_length = CShaker.digest_length;
287 /// The minimum output length, in bytes.
288 pub const mac_length_min = 4;
289 /// The recommended key length, in bytes.
290 pub const key_length = security_level / 8;
291 /// The minimum key length, in bytes.
292 pub const key_length_min = 0;
293 /// The block length, or rate, in bytes.
294 pub const block_length = CShaker.block_length;
295
296 cshaker: CShaker,
297 xof_mode: bool = false,
298
299 /// KMAC options can include a context string.
300 pub const Options = struct {
301 context: ?[]const u8 = null,
302 };
303
304 /// Initialize a state for the KMAC function, with an optional context and an arbitrary-long key.
305 /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message.
306 /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory.
307 pub fn initWithOptions(key: []const u8, options: Options) Self {
308 var cshaker = CShaker.init(.{ .context = options.context });
309 const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8);
310 cshaker.update(encoded_rate_len.slice());
311 const encoded_key_len = NistLengthEncoding.encode(.left, key.len);
312 cshaker.update(encoded_key_len.slice());
313 cshaker.update(key);
314 cshaker.fillBlock();
315 return Self{
316 .cshaker = cshaker,
317 };
318 }
319
320 /// Initialize a state for the KMAC function.
321 /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message.
322 /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory.
323 pub fn init(key: []const u8) Self {
324 return initWithOptions(key, .{});
325 }
326
327 /// Add data to the state.
328 pub fn update(self: *Self, b: []const u8) void {
329 self.cshaker.update(b);
330 }
331
332 /// Return an authentication tag for the current state.
333 pub fn final(self: *Self, out: []u8) void {
334 const encoded_out_len = NistLengthEncoding.encode(.right, out.len);
335 self.update(encoded_out_len.slice());
336 self.cshaker.final(out);
337 }
338
339 /// Squeeze a slice of bytes from the state.
340 /// `out` can be any length, and the function can be called multiple times.
341 pub fn squeeze(self: *Self, out: []u8) void {
342 if (!self.xof_mode) {
343 const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0);
344 self.update(encoded_out_len.slice());
345 self.xof_mode = true;
346 }
347 self.cshaker.squeeze(out);
348 }
349
350 /// Return an authentication tag for a message and a key, with an optional context.
351 pub fn createWithOptions(out: []u8, msg: []const u8, key: []const u8, options: Options) void {
352 var ctx = Self.initWithOptions(key, options);
353 ctx.update(msg);
354 ctx.final(out);
355 }
356
357 /// Return an authentication tag for a message and a key.
358 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
359 var ctx = Self.init(key);
360 ctx.update(msg);
361 ctx.final(out);
362 }
363 };
364}
365
366/// The TupleHash extendable output hash function, with domain-separated inputs.
367/// TupleHash is a secure hash function with a variable output length, based on the cSHAKE function.
368/// It is designed for unambiguously hashing tuples of data.
369///
370/// With most hash functions, calling `update("A")` followed by `update("B")`is identical to `update("AB")`.
371/// With TupleHash, this is not the case: `update("A"); update("B")` is different from `update("AB")`.
372///
373/// Any number of inputs can be hashed, and the output depends on individual inputs and their order.
374pub fn TupleHash(comptime security_level: u11) type {
375 return TupleHashLike(security_level, 0x04, 24);
376}
377
378fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type {
379 const CShaker = CShakeLike(security_level, default_delim, rounds, "TupleHash");
380
381 return struct {
382 const Self = @This();
383
384 /// The output length, in bytes.
385 pub const digest_length = CShaker.digest_length;
386 /// The block length, or rate, in bytes.
387 pub const block_length = CShaker.block_length;
388
389 cshaker: CShaker,
390 xof_mode: bool = false,
391
392 /// TupleHash options can include a context string.
393 pub const Options = struct {
394 context: ?[]const u8 = null,
395 };
396
397 /// Initialize a state for the TupleHash function, with an optional context.
398 /// If the context is going to be reused, the structure can be initialized once, and cloned for each message.
399 /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory.
400 ///
401 /// A key can be optionally added to the context to create a keyed TupleHash function, similar to KMAC.
402 pub fn initWithOptions(options: Options) Self {
403 const cshaker = CShaker.init(.{ .context = options.context });
404 return Self{
405 .cshaker = cshaker,
406 };
407 }
408
409 /// Initialize a state for the MAC function.
410 pub fn init() Self {
411 return initWithOptions(.{});
412 }
413
414 /// Add data to the state, separated from previous updates.
415 pub fn update(self: *Self, b: []const u8) void {
416 const encoded_b_len = NistLengthEncoding.encode(.left, b.len);
417 self.cshaker.update(encoded_b_len.slice());
418 self.cshaker.update(b);
419 }
420
421 /// Return an authentication tag for the current state.
422 pub fn final(self: *Self, out: []u8) void {
423 const encoded_out_len = NistLengthEncoding.encode(.right, out.len);
424 self.cshaker.update(encoded_out_len.slice());
425 self.cshaker.final(out);
426 }
427
428 /// Align the input to a block boundary.
429 pub fn fillBlock(self: *Self) void {
430 self.cshaker.fillBlock();
431 }
432
433 /// Squeeze a slice of bytes from the state.
434 /// `out` can be any length, and the function can be called multiple times.
435 pub fn squeeze(self: *Self, out: []u8) void {
436 if (!self.xof_mode) {
437 const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0);
438 self.update(encoded_out_len.slice());
439 self.xof_mode = true;
440 }
441 self.cshaker.squeeze(out);
442 }
443 };
444}
445
446/// The NIST SP 800-185 encoded length format.
447pub const NistLengthEncoding = enum {
448 left,
449 right,
450
451 /// A length encoded according to NIST SP 800-185.
452 pub const Length = struct {
453 /// The size of the encoded value, in bytes.
454 len: usize = 0,
455 /// A buffer to store the encoded length.
456 buf: [@sizeOf(usize) + 1]u8 = undefined,
457
458 /// Return the encoded length as a slice.
459 pub fn slice(self: *const Length) []const u8 {
460 return self.buf[0..self.len];
461 }
462 };
463
464 /// Encode a length according to NIST SP 800-185.
465 pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length {
466 const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3;
467 const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable;
468
469 var res = Length{ .len = len_bytes + 1 };
470 if (encoding == .right) {
471 res.buf[len_bytes] = @intCast(len_bytes);
472 }
473 const end = if (encoding == .right) len_bytes - 1 else len_bytes;
474 res.buf[end] = @truncate(len << 3);
475 var len_ = len >> 5;
476 for (1..len_bytes) |i| {
477 res.buf[end - i] = @truncate(len_);
478 len_ >>= 8;
479 }
480 if (encoding == .left) {
481 res.buf[0] = @intCast(len_bytes);
482 }
483 return res;
484 }
485};
486
487const htest = @import("test.zig");
488
489test {
490 _ = kangarootwelve;
491}
492
493test "sha3-224 single" {
494 try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
495 try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
496 try htest.assertEqualHash(Sha3_224, "543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
497}
498
499test "sha3-224 streaming" {
500 var h = Sha3_224.init(.{});
501 var out: [28]u8 = undefined;
502
503 h.final(out[0..]);
504 try htest.assertEqual("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", out[0..]);
505
506 h = Sha3_224.init(.{});
507 h.update("abc");
508 h.final(out[0..]);
509 try htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]);
510
511 h = Sha3_224.init(.{});
512 h.update("a");
513 h.update("b");
514 h.update("c");
515 h.final(out[0..]);
516 try htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]);
517}
518
519test "sha3-256 single" {
520 try htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", "");
521 try htest.assertEqualHash(Sha3_256, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc");
522 try htest.assertEqualHash(Sha3_256, "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
523}
524
525test "sha3-256 streaming" {
526 var h = Sha3_256.init(.{});
527 var out: [32]u8 = undefined;
528
529 h.final(out[0..]);
530 try htest.assertEqual("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", out[0..]);
531
532 h = Sha3_256.init(.{});
533 h.update("abc");
534 h.final(out[0..]);
535 try htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]);
536
537 h = Sha3_256.init(.{});
538 h.update("a");
539 h.update("b");
540 h.update("c");
541 h.final(out[0..]);
542 try htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]);
543}
544
545test "sha3-256 aligned final" {
546 var block = [_]u8{0} ** Sha3_256.block_length;
547 var out: [Sha3_256.digest_length]u8 = undefined;
548
549 var h = Sha3_256.init(.{});
550 h.update(&block);
551 h.final(out[0..]);
552}
553
554test "sha3-384 single" {
555 const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
556 try htest.assertEqualHash(Sha3_384, h1, "");
557 const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25";
558 try htest.assertEqualHash(Sha3_384, h2, "abc");
559 const h3 = "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7";
560 try htest.assertEqualHash(Sha3_384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
561}
562
563test "sha3-384 streaming" {
564 var h = Sha3_384.init(.{});
565 var out: [48]u8 = undefined;
566
567 const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
568 h.final(out[0..]);
569 try htest.assertEqual(h1, out[0..]);
570
571 const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25";
572 h = Sha3_384.init(.{});
573 h.update("abc");
574 h.final(out[0..]);
575 try htest.assertEqual(h2, out[0..]);
576
577 h = Sha3_384.init(.{});
578 h.update("a");
579 h.update("b");
580 h.update("c");
581 h.final(out[0..]);
582 try htest.assertEqual(h2, out[0..]);
583}
584
585test "sha3-512 single" {
586 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25083
587
588 const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26";
589 try htest.assertEqualHash(Sha3_512, h1, "");
590 const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
591 try htest.assertEqualHash(Sha3_512, h2, "abc");
592 const h3 = "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185";
593 try htest.assertEqualHash(Sha3_512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
594}
595
596test "sha3-512 streaming" {
597 var h = Sha3_512.init(.{});
598 var out: [64]u8 = undefined;
599
600 const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26";
601 h.final(out[0..]);
602 try htest.assertEqual(h1, out[0..]);
603
604 const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
605 h = Sha3_512.init(.{});
606 h.update("abc");
607 h.final(out[0..]);
608 try htest.assertEqual(h2, out[0..]);
609
610 h = Sha3_512.init(.{});
611 h.update("a");
612 h.update("b");
613 h.update("c");
614 h.final(out[0..]);
615 try htest.assertEqual(h2, out[0..]);
616}
617
618test "sha3-512 aligned final" {
619 var block = [_]u8{0} ** Sha3_512.block_length;
620 var out: [Sha3_512.digest_length]u8 = undefined;
621
622 var h = Sha3_512.init(.{});
623 h.update(&block);
624 h.final(out[0..]);
625}
626
627test "keccak-256 single" {
628 try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
629 try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
630 try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
631}
632
633test "keccak-512 single" {
634 try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
635 try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
636 try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
637}
638
639test "SHAKE-128 single" {
640 var out: [10]u8 = undefined;
641 Shake128.hash("hello123", &out, .{});
642 try htest.assertEqual("1b85861510bc4d8e467d", &out);
643}
644
645test "SHAKE-128 multisqueeze" {
646 var out: [10]u8 = undefined;
647 var h = Shake128.init(.{});
648 h.update("hello123");
649 h.squeeze(out[0..4]);
650 h.squeeze(out[4..]);
651 try htest.assertEqual("1b85861510bc4d8e467d", &out);
652}
653
654test "SHAKE-128 multisqueeze with multiple blocks" {
655 var out: [100]u8 = undefined;
656 var out2: [100]u8 = undefined;
657
658 var h = Shake128.init(.{});
659 h.update("hello123");
660 h.squeeze(out[0..50]);
661 h.squeeze(out[50..]);
662
663 var h2 = Shake128.init(.{});
664 h2.update("hello123");
665 h2.squeeze(&out2);
666 try std.testing.expectEqualSlices(u8, &out, &out2);
667}
668
669test "SHAKE-256 single" {
670 var out: [10]u8 = undefined;
671 Shake256.hash("hello123", &out, .{});
672 try htest.assertEqual("ade612ba265f92de4a37", &out);
673}
674
675test "TurboSHAKE-128" {
676 var out: [32]u8 = undefined;
677 TurboShake(128, 0x06).hash("\xff", &out, .{});
678 try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out);
679}
680
681test "SHA-3 with streaming" {
682 var msg: [613]u8 = [613]u8{ 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79 };
683 var out: [Sha3_256.digest_length]u8 = undefined;
684
685 Sha3_256.hash(&msg, &out, .{});
686 try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out);
687
688 var h = Sha3_256.init(.{});
689 h.update(msg[0..64]);
690 h.update(msg[64..613]);
691 h.final(&out);
692 try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out);
693}
694
695test "cSHAKE-128 with no context nor function name" {
696 var out: [32]u8 = undefined;
697 CShake128.hash("hello123", &out, .{});
698 try htest.assertEqual("1b85861510bc4d8e467d6f8a92270533cbaa7ba5e06c2d2a502854bac468b8b9", &out);
699}
700
701test "cSHAKE-128 with context" {
702 var out: [32]u8 = undefined;
703 CShake128.hash("hello123", &out, .{ .context = "custom" });
704 try htest.assertEqual("7509fa13a6bd3e38ad5c6fac042142c233996e40ebffc86c276f108b3b19cc6a", &out);
705}
706
707test "cSHAKE-128 with context and function" {
708 var out: [32]u8 = undefined;
709 CShake(128, "function").hash("hello123", &out, .{ .context = "custom" });
710 try htest.assertEqual("ad7f4d7db2d96587fcd5047c65d37c368f5366e3afac60bb9b66b0bb95dfb675", &out);
711}
712
713test "cSHAKE-256" {
714 var out: [32]u8 = undefined;
715 CShake256.hash("hello123", &out, .{ .context = "custom" });
716 try htest.assertEqual("dabe027eb1a6cbe3a0542d0560eb4e6b39146dd72ae1bf89c970a61bd93b1813", &out);
717}
718
719test "KMAC-128 with empty key and message" {
720 var out: [KMac128.mac_length]u8 = undefined;
721 const key = "";
722 KMac128.create(&out, "", key);
723 try htest.assertEqual("5c135c615152fb4d9784dd1155f9b6034e013fd77165c327dfa4d36701983ef7", &out);
724}
725
726test "KMAC-128" {
727 var out: [KMac128.mac_length]u8 = undefined;
728 const key = "A KMAC secret key";
729 KMac128.create(&out, "hello123", key);
730 try htest.assertEqual("1fa1c0d761129a83f9a4299ca137674de8373a3cc437799ae4c129e651627f8e", &out);
731}
732
733test "KMAC-128 with a customization string" {
734 var out: [KMac128.mac_length]u8 = undefined;
735 const key = "A KMAC secret key";
736 KMac128.createWithOptions(&out, "hello123", key, .{ .context = "custom" });
737 try htest.assertEqual("c58c6d42dc00a27dfa8e7e08f8c9307cecb5d662ddb11b6c36057fc2e0e068ba", &out);
738}
739
740test "KMACXOF-128" {
741 const key = "A KMAC secret key";
742 var xof = KMac128.init(key);
743 xof.update("hello123");
744 var out: [50]u8 = undefined;
745 xof.squeeze(&out);
746 try htest.assertEqual("628c2fb870d294b3673ac82d9f0d651aae6a5bb8084ea8cd8343cb888d075b9053173200a71f301141069c3c0322527981f7", &out);
747 xof.squeeze(&out);
748 try htest.assertEqual("7b638e178cfdac5727a4ea7694efaa967a65a1d0034501855acff506b4158d187d5a18d668e67b43f2abf61144b20ed4c09f", &out);
749}
750
751test "KMACXOF-256" {
752 const key = "A KMAC secret key";
753 var xof = KMac256.init(key);
754 xof.update("hello123");
755 var out: [50]u8 = undefined;
756 xof.squeeze(&out);
757 try htest.assertEqual("23fc644bc2655ba6fde7b7c11f2804f22e8d8c6bd7db856268bf3370ce2362703f6c7e91916a1b8c116e60edfbcb25613054", &out);
758 xof.squeeze(&out);
759 try htest.assertEqual("ff97251020ff255ee65a1c1f5f78ebe904f61211c39f973f82fbce2b196b9f51c2cb12afe51549a0f1eaf7954e657ba11af3", &out);
760}
761
762test "TupleHash-128" {
763 var st = TupleHash128.init();
764 st.update("hello");
765 st.update("123");
766 var out: [32]u8 = undefined;
767 st.final(&out);
768 try htest.assertEqual("3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20", &out);
769}
770
771test "TupleHash-256" {
772 var st = TupleHash256.init();
773 st.update("hello");
774 st.update("123");
775 var out: [64]u8 = undefined;
776 st.final(&out);
777 try htest.assertEqual("2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289", &out);
778}