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}