diff options
Diffstat (limited to 'lib/std/crypto')
| -rw-r--r-- | lib/std/crypto/25519/field.zig | 2 | ||||
| -rw-r--r-- | lib/std/crypto/benchmark.zig | 2 | ||||
| -rw-r--r-- | lib/std/crypto/keccak_p.zig | 277 | ||||
| -rw-r--r-- | lib/std/crypto/sha3.zig | 299 |
4 files changed, 444 insertions, 136 deletions
diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index 1a786e0c32..66a50bee70 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -287,7 +287,7 @@ pub const Fe = struct { return _carry128(&r); } - inline fn _sq(a: Fe, comptime double: bool) Fe { + fn _sq(a: Fe, comptime double: bool) Fe { var ax: [5]u128 = undefined; var r: [5]u128 = undefined; comptime var i = 0; diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 71c22f2b4c..e6e0e1fc39 100644 --- a/lib/std/crypto/benchmark.zig +++ b/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" }, diff --git a/lib/std/crypto/keccak_p.zig b/lib/std/crypto/keccak_p.zig new file mode 100644 index 0000000000..10367caffd --- /dev/null +++ b/lib/std/crypto/keccak_p.zig @@ -0,0 +1,277 @@ +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 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; + + /// Maximum number of rounds for the given f parameter. + pub const max_rounds = 12 + 2 * math.log2(f / 25); + + // Round constants + const RC = rc: { + const RC64 = [_]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, + }; + var rc: [max_rounds]T = undefined; + for (&rc, RC64[0..max_rounds]) |*t, c| t.* = @truncate(T, c); + break :rc rc; + }; + + 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]; + comptime var rotc = 0; + inline for (0..24) |i| { + const x = PI[i]; + const tmp = st[x]; + rotc = (rotc + i + 1) % @bitSizeOf(T); + st[x] = math.rotl(T, last, rotc); + 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(max_rounds); + } + }; +} + +/// 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); + } + } + }; +} + +test "Keccak-f800" { + var st: KeccakF(800) = .{ + .st = .{ + 0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7, + 0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E, + 0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98, + 0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996, + }, + }; + st.permute(); + const expected: [25]u32 = .{ + 0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9, + 0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9, + 0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259, + 0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB, + }; + try std.testing.expectEqualSlices(u32, &st.st, &expected); +} diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index c2801a4709..985027f3ee 100644 --- a/lib/std/crypto/sha3.zig +++ b/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); } |
