Member-only story
The TupleHash: A NIST Standard
Meet the Zig implementation
In maths, a tuple is a way to represent a value with an ordered list of values – and which is immutable. So let’s look at the concept of a TupleHash, and applies this concept.
NIST has driven standards for cryptography for decades and is now looking to the future with the publication of a draft entitled “Transitioning the Use of Cryptographic Algorithms and Key Lengths” [here]:
Within this document, NIST approved the use of TupleHash and ParallelHash within hashing methods:
The TupleHash produces a variable-length hash function for tuples of byte strings and preserves the semantics of each string. It uses SHA-3 as its base and is standardised in NIST SP 800–185. Overall, it can use a 128-bit and a 256-bit version.
TupleHash
TupleHash focuses on creating a sequence of strings and respects their order. If we have two strings that are concatenated, such as “bob” and “alice”, then SHA-256 will produce the same hash:
SHA-256(“bob” || “alice”) = SHA-256(“bo” || “balice”)
With TupleHash, we would get:
Tuple(“bob” || “alice”) != Tuple(“bo” || “balice”)
For example, if we have the strings of “bob”, “alice” and “eve”, we get different hashes:
String1=bob, String2=alice, String3=eve
TupleHash128: 718983f6d81800b22d19c34f30dbd8da
TupleHash256: ec5b7f75bb15cd453d317fcce49c56bc
String1=bob, String2=alic, String3=eeve
TupleHash128: f7b9682d52ef43a41811ab7c05e734ac
TupleHash256: f4677ea94c322eb2bbfa3c0b917bcaca
We can use the cryptography library in Python to implement the Tuple hash [here]:
const std = @import("std");
const hash = @import("std").crypto.hash;
pub fn main() !void {
var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
// Get the command-line arguments
var m1: []const u8 = undefined;
var m2: []const u8 = undefined;
const args = try std.process.argsAlloc(std.heap.page_allocator);
defer std.process.argsFree(std.heap.page_allocator, args);
// Check if there are any arguments
if (args.len > 1) {
m1 = args[1];
}
if (args.len > 2) {
m2 = args[2];
}
var tuple128 = hash.sha3.TupleHash128.init();
var tuple256 = hash.sha3.TupleHash256.init();
tuple128.update(m1);
tuple128.update(m2);
tuple256.update(m1);
tuple256.update(m2);
var out1: [32]u8 = undefined;
var out2: [64]u8 = undefined;
tuple128.final(&out1);
tuple256.final(&out2);
try stdout.print("\nMessage 1: {s} \n", .{m1});
try stdout.print("Message 2: {s} \n", .{m2});
try stdout.print("\nTuple128: {x} \n", .{out1});
try stdout.print("\nTuple256: {x} \n", .{out2});
try stdout.flush();
}and then we can run the file zig_tuple.exe. A sample run for “hello” and “123 is” [here]:
Message 1: hello
Message 2: 123
Tuple128: 3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20
Tuple256: 2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289and then to prove the hash is different, for “hello1” and “23”, we get a different output hash [here]:
Message 1: hello1
Message 2: 23
Tuple128: ed235d3686b59ba8f8ec82189a7d65489e647b053495862f9267ccad9167cdd9
Tuple256: 08df6d3fd7883179b39c7f325738a5a880f134e8fb534a53025d40df213c9129812b77d307614bd243f32930174dad3075e5cb59687eccafad30346a9153d314Here are other Zig implementations: