Commit e919744c7a
Changed files (5)
lib
std
lib/std/crypto/benchmark.zig
@@ -60,6 +60,10 @@ const macs = [_]Crypto{
Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" },
Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" },
+ Crypto{ .ty = crypto.auth.siphash.SipHash64(2, 4), .name = "siphash-2-4" },
+ Crypto{ .ty = crypto.auth.siphash.SipHash64(1, 3), .name = "siphash-1-3" },
+ Crypto{ .ty = crypto.auth.siphash.SipHash128(2, 4), .name = "siphash128-2-4" },
+ Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
};
pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
lib/std/hash/siphash.zig → lib/std/crypto/siphash.zig
@@ -3,25 +3,39 @@
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-// Siphash
//
-// SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance
-// against hash flooding DoS attacks.
+// SipHash is a moderately fast pseudorandom function, returning a 64-bit or 128-bit tag for an arbitrary long input.
+//
+// Typical use cases include:
+// - protection against against DoS attacks for hash tables and bloom filters
+// - authentication of short-lived messages in online protocols
//
// https://131002.net/siphash/
-
const std = @import("../std.zig");
const assert = std.debug.assert;
const testing = std.testing;
const math = std.math;
const mem = std.mem;
-const Endian = std.builtin.Endian;
-
+/// SipHash function with 64-bit output.
+///
+/// Recommended parameters are:
+/// - (c_rounds=2, d_rounds=4) standard parameters.
+/// - (c_rounds=1, d_rounds=2) reduced-round function. Faster, no known implications on its practical security level.
+///
+/// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary.
+/// And due to its small output size, collisions in SipHash64 can be found with an exhaustive search.
pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
return SipHash(u64, c_rounds, d_rounds);
}
+/// SipHash function with 128-bit output.
+///
+/// Recommended parameters are:
+/// - (c_rounds=2, d_rounds=4) standard parameters.
+/// - (c_rounds=1, d_rounds=2) reduced-round function. Faster, no known implications on its practical security level.
+///
+/// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary.
pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
return SipHash(u128, c_rounds, d_rounds);
}
@@ -146,30 +160,31 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round
d.v2 = math.rotl(u64, d.v2, @as(u64, 32));
}
- pub fn hash(key: []const u8, input: []const u8) T {
- const aligned_len = input.len - (input.len % 8);
-
+ pub fn hash(msg: []const u8, key: []const u8) T {
+ const aligned_len = msg.len - (msg.len % 8);
var c = Self.init(key);
- @call(.{ .modifier = .always_inline }, c.update, .{input[0..aligned_len]});
- return @call(.{ .modifier = .always_inline }, c.final, .{input[aligned_len..]});
+ @call(.{ .modifier = .always_inline }, c.update, .{msg[0..aligned_len]});
+ return @call(.{ .modifier = .always_inline }, c.final, .{msg[aligned_len..]});
}
};
}
-pub fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
+fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
assert(T == u64 or T == u128);
assert(c_rounds > 0 and d_rounds > 0);
return struct {
const State = SipHashStateless(T, c_rounds, d_rounds);
const Self = @This();
- const digest_size = 64;
- const block_size = 64;
+ pub const minimum_key_length = 16;
+ pub const mac_length = @sizeOf(T);
+ pub const block_length = 8;
state: State,
buf: [8]u8,
buf_len: usize,
+ /// Initialize a state for a SipHash function
pub fn init(key: []const u8) Self {
return Self{
.state = State.init(key),
@@ -178,6 +193,7 @@ pub fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: us
};
}
+ /// Add data to the state
pub fn update(self: *Self, b: []const u8) void {
var off: usize = 0;
@@ -196,12 +212,27 @@ pub fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: us
self.buf_len += @intCast(u8, b[off + aligned_len ..].len);
}
- pub fn final(self: *Self) T {
+ /// Return an authentication tag for the current state
+ pub fn final(self: *Self, out: []u8) void {
+ std.debug.assert(out.len >= mac_length);
+ mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len]));
+ }
+
+ /// Return an authentication tag for a message and a key
+ pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
+ var ctx = Self.init(key);
+ ctx.update(msg);
+ ctx.final(out[0..]);
+ }
+
+ /// Return an authentication tag for the current state, as an integer
+ pub fn finalInt(self: *Self) T {
return self.state.final(self.buf[0..self.buf_len]);
}
- pub fn hash(key: []const u8, input: []const u8) T {
- return State.hash(key, input);
+ /// Return an authentication tag for a message and a key, as an integer
+ pub fn toInt(msg: []const u8, key: []const u8) T {
+ return State.hash(msg, key);
}
};
}
@@ -284,8 +315,9 @@ test "siphash64-2-4 sanity" {
for (vectors) |vector, i| {
buffer[i] = @intCast(u8, i);
- const expected = mem.readIntLittle(u64, &vector);
- testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected);
+ var out: [siphash.mac_length]u8 = undefined;
+ siphash.create(&out, buffer[0..i], test_key);
+ testing.expectEqual(out, vector);
}
}
@@ -363,8 +395,9 @@ test "siphash128-2-4 sanity" {
for (vectors) |vector, i| {
buffer[i] = @intCast(u8, i);
- const expected = mem.readIntLittle(u128, &vector);
- testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected);
+ var out: [siphash.mac_length]u8 = undefined;
+ siphash.create(&out, buffer[0..i], test_key[0..]);
+ testing.expectEqual(out, vector);
}
}
@@ -379,14 +412,14 @@ test "iterative non-divisible update" {
var end: usize = 9;
while (end < buf.len) : (end += 9) {
- const non_iterative_hash = Siphash.hash(key, buf[0..end]);
+ const non_iterative_hash = Siphash.toInt(buf[0..end], key[0..]);
- var wy = Siphash.init(key);
+ var siphash = Siphash.init(key);
var i: usize = 0;
while (i < end) : (i += 7) {
- wy.update(buf[i..std.math.min(i + 7, end)]);
+ siphash.update(buf[i..std.math.min(i + 7, end)]);
}
- const iterative_hash = wy.final();
+ const iterative_hash = siphash.finalInt();
std.testing.expectEqual(iterative_hash, non_iterative_hash);
}
lib/std/hash/benchmark.zig
@@ -25,24 +25,12 @@ const Hash = struct {
init_u64: ?u64 = null,
};
-const siphash_key = "0123456789abcdef";
-
const hashes = [_]Hash{
Hash{
.ty = hash.Wyhash,
.name = "wyhash",
.init_u64 = 0,
},
- Hash{
- .ty = hash.SipHash64(1, 3),
- .name = "siphash(1,3)",
- .init_u8s = siphash_key,
- },
- Hash{
- .ty = hash.SipHash64(2, 4),
- .name = "siphash(2,4)",
- .init_u8s = siphash_key,
- },
Hash{
.ty = hash.Fnv1a_64,
.name = "fnv1a",
lib/std/crypto.zig
@@ -18,6 +18,7 @@ pub const hash = struct {
/// Authentication (MAC) functions.
pub const auth = struct {
pub const hmac = @import("crypto/hmac.zig");
+ pub const siphash = @import("crypto/siphash.zig");
};
/// Authenticated Encryption with Associated Data
@@ -80,6 +81,7 @@ test "crypto" {
_ = @import("crypto/sha1.zig");
_ = @import("crypto/sha2.zig");
_ = @import("crypto/sha3.zig");
+ _ = @import("crypto/siphash.zig");
_ = @import("crypto/25519/curve25519.zig");
_ = @import("crypto/25519/ed25519.zig");
_ = @import("crypto/25519/edwards25519.zig");
lib/std/hash.zig
@@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32;
pub const Fnv1a_64 = fnv.Fnv1a_64;
pub const Fnv1a_128 = fnv.Fnv1a_128;
-const siphash = @import("hash/siphash.zig");
+const siphash = @import("crypto/siphash.zig");
pub const SipHash64 = siphash.SipHash64;
pub const SipHash128 = siphash.SipHash128;
@@ -42,7 +42,6 @@ test "hash" {
_ = @import("hash/auto_hash.zig");
_ = @import("hash/crc.zig");
_ = @import("hash/fnv.zig");
- _ = @import("hash/siphash.zig");
_ = @import("hash/murmur.zig");
_ = @import("hash/cityhash.zig");
_ = @import("hash/wyhash.zig");