SipHash is a keyed, cryptographically-secure pseudorandom function (PRF) optimized for short inputs. It was designed by Jean‑Philippe Aumasson and Daniel J. Bernstein in 2012 to defend against hash-flooding (denial-of-service) attacks in hash tables.
Compact, high-performance SipHash-2-4 implementation and a small DI-ready service for .NET 8.
- Language: C# 12
- Target: .NET 8
This library exposes:
SipHashCore— a pure static implementation of SipHash-2-4.SipHashService— a singleton-friendly service that stores the two 64-bit keys in pinned memory and exposesComputeHash(ReadOnlySpan<byte>).SipHashOptions— simple options POCO for keys.ServiceCollectionExtensions.AddSipHash— helper to register the service with the Microsoft DI container.
The implementation is optimized for speed (span-based APIs, Unsafe.ReadUnaligned, AggressiveInlining). Keys are stored using PinnedMemory<ulong> to avoid GC relocation; review PinnedMemory behavior for zeroing/locking guarantees in your security model.
Install from NuGet
dotnet add package SipHash- Configure keys in DI (recommended):
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SipHash;
var host = Host.CreateDefaultBuilder()
.ConfigureServices((ctx, services) =>
{
services.Configure<SipHashOptions>(opts =>
{
opts.K0 = 0x0123456789abcdefUL;
opts.K1 = 0xfedcba9876543210UL;
});
services.AddSipHash(); // registers SipHashService as singleton
})
.Build();
// resolve & use
var svc = host.Services.GetRequiredService<SipHashService>();
byte[] data = System.Text.Encoding.UTF8.GetBytes("hello");
ulong tag = svc.ComputeHash(data);- Use directly (no DI):
ulong k0 = 0x0123456789abcdefUL;
ulong k1 = 0xfedcba9876543210UL;
byte[] data = System.Text.Encoding.UTF8.GetBytes("hello");
ulong tag = SipHashCore.Hash(k0, k1, data);Note: SipHashCore.Hash accepts any ReadOnlySpan<byte> (byte[] or Memory).
-
SipHashService : IDisposable
- Constructor:
SipHashService(IOptions<SipHashOptions> options)— validates presence of K0 and K1 and pins them in memory. ulong ComputeHash(ReadOnlySpan<byte> data)— computes SipHash-2-4 tag for the given data. ThrowsObjectDisposedExceptionif disposed.void Dispose()— disposes pinned memory. After disposal, service methods throw.
- Constructor:
-
SipHashCore (static)
static ulong Hash(ulong k0, ulong k1, ReadOnlySpan<byte> msg)— pure function implementing SipHash-2-4. Uses little-endian packing.
-
SipHashOptions
ulong? K0 { get; set; }ulong? K1 { get; set; }
-
ServiceCollectionExtensions
IServiceCollection AddSipHash(this IServiceCollection services)— registersSipHashServiceas a singleton using configuredSipHashOptions.
ComputeHash uses local stack state and reads keys from pinned memory. As implemented, SipHashService is safe for concurrent callers provided:
PinnedMemory<ulong>.Read(int)is thread-safe for concurrent reads.- No mutation of keys occurs after construction.
- Keys are pinned to avoid relocation, but this does not prevent process memory disclosure (core dumps, swap, memory scanners). Treat keys as sensitive.
- Review
PinnedMemorybehavior for whether it zeros memory on dispose; current constructor useszero: falseso initial allocation is not zeroed. If you require explicit zeroing, ensurePinnedMemoryis configured accordingly or zero keys manually before dispose. - Register the service as singleton only if the keys' lifetime aligns with application lifetime and DI usage.
- The code implements SipHash-2-4 (2 compression rounds, 4 finalization rounds).
- The implementation assumes little-endian byte order when packing/unpacking 64-bit words (typical for x86/x64). If targeting big-endian platforms, validate behavior.
- For maximum performance the implementation uses
Unsafe.ReadUnalignedandMemoryMarshal.
byte[] data = System.Text.Encoding.UTF8.GetBytes("example");
ulong tag = SipHashCore.Hash(k0, k1, data);
string hex = tag.ToString("x16");Contributions are welcome. Open issues or PRs with focused changes: tests, API improvements, or security-hardening (zeroing keys, secure memory).