From 90888045cca576a08c7172a29ae662e1f568ab73 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 7 Mar 2023 00:31:51 +0100 Subject: [PATCH] std.crypto.hash.sha3: add TurboSHAKE TurboSHAKE is a round-reduced version of SHAKE which is being standardized by CFRG. It offers the same practical security as SHA3 and SHAKE. Its security builds up on the scrutiny that Keccak has received since its publication. Unlike SHA3 (but not unlike SHAKE), the output can be of any size. TurboSHAKE also offers higher performance - It is nearly twice as fast as SHAKE. sha3-256: 571 MiB/s sha3-512: 316 MiB/s shake-128: 723 MiB/s turboshake-128: 1334 MiB/s Why it matters: TurboSHAKE is the fastest secure hash function we'll have in Zig when compiling for the `baseline` CPU. Since we already had SHAKE, TurboSHAKE is a trivial addition, and it will immediately benefit from the further optimizations we'll do to Keccak. --- lib/std/crypto/benchmark.zig | 2 ++ lib/std/crypto/keccak_p.zig | 4 ++-- lib/std/crypto/sha3.zig | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index e6e0e1fc3930..47ca24aa6651 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -27,6 +27,8 @@ const hashes = [_]Crypto{ 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.sha3.TurboShake128(null), .name = "turboshake-128" }, + Crypto{ .ty = crypto.hash.sha3.TurboShake256(null), .name = "turboshake-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 index 10367caffd34..cef3b0cce4c8 100644 --- a/lib/std/crypto/keccak_p.zig +++ b/lib/std/crypto/keccak_p.zig @@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type { /// 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) { + while (i < RC.len - RC.len % 3) : (i += 3) { self.round(RC[i]); self.round(RC[i + 1]); self.round(RC[i + 2]); } - while (i < rounds) : (i += 1) { + while (i < RC.len) : (i += 1) { self.round(RC[i]); } } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 985027f3eee3..6fc4977f0b21 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); pub const Shake128 = Shake(128); pub const Shake256 = Shake(256); +/// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. +/// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. +/// The delimiter is 0x01 by default, but can be changed for context-separation. +pub fn TurboShake128(comptime delim: ?u8) type { + return TurboShake(128, delim); +} + +/// TurboSHAKE256 is a XOF (a secure hash function with a variable output length), with a 256 bit security level. +/// It is based on the same permutation as SHA3 and SHAKE256, but which much higher performance. +/// The delimiter is 0x01 by default, but can be changed for context-separation. +pub fn TurboShake256(comptime delim: ?u8) type { + return TurboShake(256, delim); +} + /// 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 @@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co /// The SHAKE extendable output hash function. pub fn Shake(comptime security_level: u11) type { + return ShakeLike(security_level, 0x1f, 24); +} + +/// The TurboSHAKE extendable output hash function. +/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/ +pub fn TurboShake(comptime security_level: u11, comptime delim: ?u8) type { + return ShakeLike(security_level, delim orelse 0x01, 12); +} + +fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { const f = 1600; - const rounds = 24; - const State = KeccakState(f, security_level * 2, 0x1f, rounds); + const State = KeccakState(f, security_level * 2, delim, rounds); return struct { const Self = @This(); @@ -348,3 +371,9 @@ test "SHAKE-256 single" { Shake256.hash("hello123", &out, .{}); try htest.assertEqual("ade612ba265f92de4a37", &out); } + +test "TurboSHAKE-128" { + var out: [32]u8 = undefined; + TurboShake(128, 0x06).hash("\xff", &out, .{}); + try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out); +}