Commit 28364166e8
Changed files (4)
lib
lib/std/crypto/benchmark.zig
@@ -25,6 +25,8 @@ const hashes = [_]Crypto{
Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },
Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },
Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
+ Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
+ Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },
Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
lib/std/crypto/keccak_p.zig
@@ -0,0 +1,251 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const math = std.math;
+const mem = std.mem;
+
+/// The Keccak-f permutation.
+pub fn KeccakF(comptime f: u11) type {
+ comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid bit size
+ const T = std.meta.Int(.unsigned, f / 25);
+ const Block = [25]T;
+
+ const RC = [_]u64{
+ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
+ 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+ 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
+ 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
+ 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
+ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
+ };
+
+ const RHO = [_]u6{
+ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
+ };
+
+ const PI = [_]u5{
+ 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
+ };
+
+ return struct {
+ const Self = @This();
+
+ /// Number of bytes in the state.
+ pub const block_bytes = f / 8;
+
+ st: Block = [_]T{0} ** 25,
+
+ /// Initialize the state from a slice of bytes.
+ pub fn init(bytes: [block_bytes]u8) Self {
+ var self: Self = undefined;
+ inline for (&self.st, 0..) |*r, i| {
+ r.* = mem.readIntLittle(T, bytes[@sizeOf(T) * i ..][0..@sizeOf(T)]);
+ }
+ return self;
+ }
+
+ /// A representation of the state as bytes. The byte order is architecture-dependent.
+ pub fn asBytes(self: *Self) *[block_bytes]u8 {
+ return mem.asBytes(&self.st);
+ }
+
+ /// Byte-swap the entire state if the architecture doesn't match the required endianness.
+ pub fn endianSwap(self: *Self) void {
+ for (&self.st) |*w| {
+ w.* = mem.littleTooNative(T, w.*);
+ }
+ }
+
+ /// Set bytes starting at the beginning of the state.
+ pub fn setBytes(self: *Self, bytes: []const u8) void {
+ var i: usize = 0;
+ while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
+ self.st[i / @sizeOf(T)] = mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
+ }
+ if (i < bytes.len) {
+ var padded = [_]u8{0} ** @sizeOf(T);
+ mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
+ self.st[i / @sizeOf(T)] = mem.readIntLittle(T, padded[0..]);
+ }
+ }
+
+ /// XOR a byte into the state at a given offset.
+ pub fn addByte(self: *Self, byte: u8, offset: usize) void {
+ const z = @sizeOf(T) * @truncate(math.Log2Int(T), offset % @sizeOf(T));
+ self.st[offset / @sizeOf(T)] ^= @as(T, byte) << z;
+ }
+
+ /// XOR bytes into the beginning of the state.
+ pub fn addBytes(self: *Self, bytes: []const u8) void {
+ var i: usize = 0;
+ while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
+ self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
+ }
+ if (i < bytes.len) {
+ var padded = [_]u8{0} ** @sizeOf(T);
+ mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
+ self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, padded[0..]);
+ }
+ }
+
+ /// Extract the first bytes of the state.
+ pub fn extractBytes(self: *Self, out: []u8) void {
+ var i: usize = 0;
+ while (i + @sizeOf(T) <= out.len) : (i += @sizeOf(T)) {
+ mem.writeIntLittle(T, out[i..][0..@sizeOf(T)], self.st[i / @sizeOf(T)]);
+ }
+ if (i < out.len) {
+ var padded = [_]u8{0} ** @sizeOf(T);
+ mem.writeIntLittle(T, padded[0..], self.st[i / @sizeOf(T)]);
+ mem.copy(u8, out[i..], padded[0 .. out.len - i]);
+ }
+ }
+
+ /// XOR the first bytes of the state into a slice of bytes.
+ pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
+ assert(out.len == in.len);
+
+ var i: usize = 0;
+ while (i + @sizeOf(T) <= in.len) : (i += @sizeOf(T)) {
+ const x = mem.readIntNative(T, in[i..][0..@sizeOf(T)]) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
+ mem.writeIntNative(T, out[i..][0..@sizeOf(T)], x);
+ }
+ if (i < in.len) {
+ var padded = [_]u8{0} ** @sizeOf(T);
+ mem.copy(u8, padded[0 .. in.len - i], in[i..]);
+ const x = mem.readIntNative(T, &padded) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
+ mem.writeIntNative(T, &padded, x);
+ mem.copy(u8, out[i..], padded[0 .. in.len - i]);
+ }
+ }
+
+ /// Set the words storing the bytes of a given range to zero.
+ pub fn clear(self: *Self, from: usize, to: usize) void {
+ mem.set(T, self.st[from / @sizeOf(T) .. (to + @sizeOf(T) - 1) / @sizeOf(T)], 0);
+ }
+
+ /// Clear the entire state, disabling compiler optimizations.
+ pub fn secureZero(self: *Self) void {
+ std.crypto.utils.secureZero(T, &self.st);
+ }
+
+ inline fn round(self: *Self, rc: T) void {
+ const st = &self.st;
+
+ // theta
+ var t = [_]T{0} ** 5;
+ inline for (0..5) |i| {
+ inline for (0..5) |j| {
+ t[i] ^= st[j * 5 + i];
+ }
+ }
+ inline for (0..5) |i| {
+ inline for (0..5) |j| {
+ st[j * 5 + i] ^= t[(i + 4) % 5] ^ math.rotl(T, t[(i + 1) % 5], 1);
+ }
+ }
+
+ // rho+pi
+ var last = st[1];
+ inline for (0..24) |i| {
+ const x = PI[i];
+ const tmp = st[x];
+ st[x] = math.rotl(T, last, RHO[i]);
+ last = tmp;
+ }
+ inline for (0..5) |i| {
+ inline for (0..5) |j| {
+ t[j] = st[i * 5 + j];
+ }
+ inline for (0..5) |j| {
+ st[i * 5 + j] = t[j] ^ (~t[(j + 1) % 5] & t[(j + 2) % 5]);
+ }
+ }
+
+ // iota
+ st[0] ^= rc;
+ }
+
+ /// Apply a (possibly) reduced-round permutation to the state.
+ pub fn permuteR(self: *Self, comptime rounds: u5) void {
+ var i = RC.len - rounds;
+ while (i < rounds - rounds % 3) : (i += 3) {
+ self.round(RC[i]);
+ self.round(RC[i + 1]);
+ self.round(RC[i + 2]);
+ }
+ while (i < rounds) : (i += 1) {
+ self.round(RC[i]);
+ }
+ }
+
+ /// Apply a full-round permutation to the state.
+ pub fn permute(self: *Self) void {
+ self.permuteR(comptime 12 + 2 * math.log2(f / 25));
+ }
+ };
+}
+
+/// A generic Keccak-P state.
+pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type {
+ comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size
+ comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size
+
+ return struct {
+ const Self = @This();
+
+ /// The block length, or rate, in bytes.
+ pub const rate = KeccakF(f).block_bytes - capacity / 8;
+ /// Keccak does not have any options.
+ pub const Options = struct {};
+
+ offset: usize = 0,
+ buf: [rate]u8 = undefined,
+
+ st: KeccakF(f) = .{},
+
+ /// Absorb a slice of bytes into the sponge.
+ pub fn absorb(self: *Self, bytes_: []const u8) void {
+ var bytes = bytes_;
+ if (self.offset > 0) {
+ const left = math.min(rate - self.offset, bytes.len);
+ mem.copy(u8, self.buf[self.offset..], bytes[0..left]);
+ self.offset += left;
+ if (self.offset == rate) {
+ self.offset = 0;
+ self.st.addBytes(self.buf[0..]);
+ self.st.permuteR(rounds);
+ }
+ if (left == bytes.len) return;
+ bytes = bytes[left..];
+ }
+ while (bytes.len >= rate) {
+ self.st.addBytes(bytes[0..rate]);
+ self.st.permuteR(rounds);
+ bytes = bytes[rate..];
+ }
+ if (bytes.len > 0) {
+ self.st.addBytes(bytes[0..]);
+ self.offset = bytes.len;
+ }
+ }
+
+ /// Mark the end of the input.
+ pub fn pad(self: *Self) void {
+ self.st.addBytes(self.buf[0..self.offset]);
+ self.st.addByte(delim, self.offset);
+ self.st.addByte(0x80, rate - 1);
+ self.st.permuteR(rounds);
+ self.offset = 0;
+ }
+
+ /// Squeeze a slice of bytes from the sponge.
+ pub fn squeeze(self: *Self, out: []u8) void {
+ var i: usize = 0;
+ while (i < out.len) : (i += rate) {
+ const left = math.min(rate, out.len - i);
+ self.st.extractBytes(out[i..][0..left]);
+ self.st.permuteR(rounds);
+ }
+ }
+ };
+}
lib/std/crypto/sha3.zig
@@ -1,84 +1,63 @@
-const std = @import("../std.zig");
-const mem = std.mem;
+const std = @import("std");
+const assert = std.debug.assert;
const math = std.math;
-const debug = std.debug;
-const htest = @import("test.zig");
+const mem = std.mem;
+
+const KeccakState = std.crypto.core.keccak.State;
+
+pub const Sha3_224 = Keccak(1600, 224, 0x06, 24);
+pub const Sha3_256 = Keccak(1600, 256, 0x06, 24);
+pub const Sha3_384 = Keccak(1600, 384, 0x06, 24);
+pub const Sha3_512 = Keccak(1600, 512, 0x06, 24);
+
+pub const Keccak256 = Keccak(1600, 256, 0x01, 24);
+pub const Keccak512 = Keccak(1600, 512, 0x01, 24);
+pub const Keccak_256 = @compileError("Deprecated: use `Keccak256` instead");
+pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");
+
+pub const Shake128 = Shake(128);
+pub const Shake256 = Shake(256);
+
+/// A generic Keccak hash function.
+pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
+ comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
-pub const Sha3_224 = Keccak(224, 0x06);
-pub const Sha3_256 = Keccak(256, 0x06);
-pub const Sha3_384 = Keccak(384, 0x06);
-pub const Sha3_512 = Keccak(512, 0x06);
-pub const Keccak_256 = Keccak(256, 0x01);
-pub const Keccak_512 = Keccak(512, 0x01);
+ const State = KeccakState(f, output_bits * 2, delim, rounds);
-fn Keccak(comptime bits: usize, comptime delim: u8) type {
return struct {
const Self = @This();
+
+ st: State = .{},
+
/// The output length, in bytes.
- pub const digest_length = bits / 8;
+ pub const digest_length = output_bits / 8;
/// The block length, or rate, in bytes.
- pub const block_length = 200 - bits / 4;
+ pub const block_length = State.rate;
/// Keccak does not have any options.
pub const Options = struct {};
- s: [200]u8,
- offset: usize,
-
+ /// Initialize a Keccak hash function.
pub fn init(options: Options) Self {
_ = options;
- return Self{ .s = [_]u8{0} ** 200, .offset = 0 };
+ return Self{};
}
- pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
- var d = Self.init(options);
- d.update(b);
- d.final(out);
+ /// Hash a slice of bytes.
+ pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void {
+ var st = Self.init(options);
+ st.update(bytes);
+ st.final(out);
}
- pub fn update(d: *Self, b: []const u8) void {
- var ip: usize = 0;
- var len = b.len;
- var rate = block_length - d.offset;
- var offset = d.offset;
-
- // absorb
- while (len >= rate) {
- for (d.s[offset .. offset + rate], 0..) |*r, i|
- r.* ^= b[ip..][i];
-
- keccakF(1600, &d.s);
-
- ip += rate;
- len -= rate;
- rate = block_length;
- offset = 0;
- }
-
- for (d.s[offset .. offset + len], 0..) |*r, i|
- r.* ^= b[ip..][i];
-
- d.offset = offset + len;
+ /// Absorb a slice of bytes into the state.
+ pub fn update(self: *Self, bytes: []const u8) void {
+ self.st.absorb(bytes);
}
- pub fn final(d: *Self, out: *[digest_length]u8) void {
- // padding
- d.s[d.offset] ^= delim;
- d.s[block_length - 1] ^= 0x80;
-
- keccakF(1600, &d.s);
-
- // squeeze
- var op: usize = 0;
- var len: usize = bits / 8;
-
- while (len >= block_length) {
- mem.copy(u8, out[op..], d.s[0..block_length]);
- keccakF(1600, &d.s);
- op += block_length;
- len -= block_length;
- }
-
- mem.copy(u8, out[op..], d.s[0..len]);
+ /// Return the hash of the absorbed bytes.
+ pub fn final(self: *Self, out: *[digest_length]u8) void {
+ self.st.pad();
+ self.st.squeeze(out[0..]);
}
pub const Error = error{};
@@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
};
}
-const RC = [_]u64{
- 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
- 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
- 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
- 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
- 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
- 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
-};
-
-const ROTC = [_]usize{
- 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
-};
-
-const PIL = [_]usize{
- 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
-};
-
-const M5 = [_]usize{
- 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
-};
-
-fn keccakF(comptime F: usize, d: *[F / 8]u8) void {
- const B = F / 25;
- const no_rounds = comptime x: {
- break :x 12 + 2 * math.log2(B);
- };
+/// The SHAKE extendable output hash function.
+pub fn Shake(comptime security_level: u11) type {
+ const f = 1600;
+ const rounds = 24;
+ const State = KeccakState(f, security_level * 2, 0x1f, rounds);
- var s = [_]u64{0} ** 25;
- var t = [_]u64{0} ** 1;
- var c = [_]u64{0} ** 5;
+ return struct {
+ const Self = @This();
- for (&s, 0..) |*r, i| {
- r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);
- }
+ st: State = .{},
+ buf: [State.rate]u8 = undefined,
+ offset: usize = 0,
+ padded: bool = false,
- for (RC[0..no_rounds]) |round| {
- // theta
- comptime var x: usize = 0;
- inline while (x < 5) : (x += 1) {
- c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
+ /// The recommended output length, in bytes.
+ pub const digest_length = security_level / 2;
+ /// The block length, or rate, in bytes.
+ pub const block_length = State.rate;
+ /// Keccak does not have any options.
+ pub const Options = struct {};
+
+ /// Initialize a SHAKE extensible hash function.
+ pub fn init(options: Options) Self {
+ _ = options;
+ return Self{};
}
- x = 0;
- inline while (x < 5) : (x += 1) {
- t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1));
- comptime var y: usize = 0;
- inline while (y < 5) : (y += 1) {
- s[x + y * 5] ^= t[0];
- }
+
+ /// Hash a slice of bytes.
+ /// `out` can be any length.
+ pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
+ var st = Self.init(options);
+ st.update(bytes);
+ st.squeeze(out);
}
- // rho+pi
- t[0] = s[1];
- x = 0;
- inline while (x < 24) : (x += 1) {
- c[0] = s[PIL[x]];
- s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]);
- t[0] = c[0];
+ /// Absorb a slice of bytes into the state.
+ pub fn update(self: *Self, bytes: []const u8) void {
+ self.st.absorb(bytes);
}
- // chi
- comptime var y: usize = 0;
- inline while (y < 5) : (y += 1) {
- x = 0;
- inline while (x < 5) : (x += 1) {
- c[x] = s[x + y * 5];
+ /// Squeeze a slice of bytes from the state.
+ /// `out` can be any length, and the function can be called multiple times.
+ pub fn squeeze(self: *Self, out_: []u8) void {
+ if (!self.padded) {
+ self.st.pad();
+ self.padded = true;
+ }
+ var out = out_;
+ if (self.offset > 0) {
+ const left = self.buf.len - self.offset;
+ if (left > 0) {
+ const n = math.min(left, out.len);
+ mem.copy(u8, out[0..n], self.buf[self.offset..][0..n]);
+ out = out[n..];
+ self.offset += n;
+ if (out.len == 0) {
+ return;
+ }
+ }
}
- x = 0;
- inline while (x < 5) : (x += 1) {
- s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]);
+ const full_blocks = out[0 .. out.len - out.len % State.rate];
+ if (full_blocks.len > 0) {
+ self.st.squeeze(full_blocks);
+ out = out[full_blocks.len..];
+ }
+ if (out.len > 0) {
+ self.st.squeeze(self.buf[0..]);
+ mem.copy(u8, out[0..], self.buf[0..out.len]);
+ self.offset = out.len;
}
}
- // iota
- s[0] ^= round;
- }
+ /// Return the hash of the absorbed bytes.
+ /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
+ pub fn final(self: *Self, out: []u8) void {
+ self.squeeze(out);
+ self.st.st.clear(0, State.rate);
+ }
- for (s, 0..) |r, i| {
- mem.writeIntLittle(u64, d[8 * i ..][0..8], r);
- }
+ pub const Error = error{};
+ pub const Writer = std.io.Writer(*Self, Error, write);
+
+ fn write(self: *Self, bytes: []const u8) Error!usize {
+ self.update(bytes);
+ return bytes.len;
+ }
+
+ pub fn writer(self: *Self) Writer {
+ return .{ .context = self };
+ }
+ };
}
+const htest = @import("test.zig");
+
test "sha3-224 single" {
try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
@@ -309,13 +302,49 @@ test "sha3-512 aligned final" {
}
test "keccak-256 single" {
- try htest.assertEqualHash(Keccak_256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
- try htest.assertEqualHash(Keccak_256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
- try htest.assertEqualHash(Keccak_256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+ try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
+ try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
+ try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "keccak-512 single" {
- try htest.assertEqualHash(Keccak_512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
- try htest.assertEqualHash(Keccak_512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
- try htest.assertEqualHash(Keccak_512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+ try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
+ try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
+ try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+}
+
+test "SHAKE-128 single" {
+ var out: [10]u8 = undefined;
+ Shake128.hash("hello123", &out, .{});
+ try htest.assertEqual("1b85861510bc4d8e467d", &out);
+}
+
+test "SHAKE-128 multisqueeze" {
+ var out: [10]u8 = undefined;
+ var h = Shake128.init(.{});
+ h.update("hello123");
+ h.squeeze(out[0..4]);
+ h.squeeze(out[4..]);
+ try htest.assertEqual("1b85861510bc4d8e467d", &out);
+}
+
+test "SHAKE-128 multisqueeze with multiple blocks" {
+ var out: [100]u8 = undefined;
+ var out2: [100]u8 = undefined;
+
+ var h = Shake128.init(.{});
+ h.update("hello123");
+ h.squeeze(out[0..50]);
+ h.squeeze(out[50..]);
+
+ var h2 = Shake128.init(.{});
+ h2.update("hello123");
+ h2.squeeze(&out2);
+ try std.testing.expectEqualSlices(u8, &out, &out2);
+}
+
+test "SHAKE-256 single" {
+ var out: [10]u8 = undefined;
+ Shake256.hash("hello123", &out, .{});
+ try htest.assertEqual("ade612ba265f92de4a37", &out);
}
lib/std/crypto.zig
@@ -47,6 +47,8 @@ pub const auth = struct {
/// Core functions, that should rarely be used directly by applications.
pub const core = struct {
pub const aes = @import("crypto/aes.zig");
+ pub const keccak = @import("crypto/keccak_p.zig");
+
pub const Ascon = @import("crypto/ascon.zig").State;
pub const Gimli = @import("crypto/gimli.zig").State;
pub const Xoodoo = @import("crypto/xoodoo.zig").State;