Skip to content

Repository files navigation

sml-siphash

CI

SipHash-2-4, the add-rotate-xor pseudorandom function of Aumasson & Bernstein, "SipHash: a fast short-input PRF", in pure Standard ML.

No dependencies, no FFI, no threads, no clock, no randomness: the same 128-bit key and message always produce the same 64-bit output under MLton and Poly/ML. All internal state and arithmetic uses Word64.word explicitly — never the default Word type, whose width differs between MLton (32-bit) and Poly/ML (63-bit) and would otherwise silently diverge between compilers.

SipHash-2-4 runs 2 compression rounds per 8-byte message block and 4 finalization rounds. (SipHash-1-3 is not implemented; scope was kept to the 2-4 variant used almost everywhere SipHash appears, e.g. hash-DoS-resistant hash tables and OS PRFs.)

API

signature SIPHASH =
sig
  (* hash {k0, k1} msg: SipHash-2-4 of `msg` under 128-bit key (k0, k1). *)
  val hash : {k0 : Word64.word, k1 : Word64.word} -> string -> Word64.word

  (* hash the message as a hex-encoded (lowercase, 16 hex digits) string. *)
  val hashHex : {k0 : Word64.word, k1 : Word64.word} -> string -> string
end

The 128-bit key is split into two Word64.word halves k0/k1, matching the reference C implementation's little-endian key layout (k0 from key bytes 0..7, k1 from bytes 8..15, each read little-endian). The message is an SML string consumed byte-by-byte (Char.ord of each character, so binary messages are represented as strings of chars 0..255).

Quick start

val key = { k0 = 0wx0706050403020100 : Word64.word
          , k1 = 0wx0f0e0d0c0b0a0908 : Word64.word }
val h   = Siphash.hash key "hello"        (* : Word64.word *)
val hex = Siphash.hashHex key "hello"     (* "004fb3985767df81" *)

Example

Running examples/demo.sml with make example prints:

SipHash-2-4, key 000102030405060708090a0b0c0d0e0f:
  hash("") = 726fdb47dd0e0e31
  hash("a") = 2ba3e8e9a71148ca
  hash("hello") = 004fb3985767df81
  hash("sjqtentacles") = 1b014be91f4a492d

Same message, different keys (key sensitivity):
  key A -> 6a571e45426093e9
  key B -> 2163a21c114a0d9b

Build & test

Requires MLton and/or Poly/ML.

make test        # build + run the suite under MLton
make test-poly   # run the suite under Poly/ML
make all-tests   # both + byte-identical output gate
make example     # build + run the demo
make clean

Installing with smlpkg

smlpkg add github.com/sjqtentacles/sml-siphash
smlpkg sync

Reference lib/github.com/sjqtentacles/sml-siphash/siphash.mlb from your own .mlb (MLton / MLKit), or feed sources.mlb to tools/polybuild (Poly/ML).

Layout

sml.pkg                                       smlpkg manifest
Makefile                                      MLton + Poly/ML targets
.github/workflows/ci.yml                      CI: MLton + Poly/ML
lib/github.com/sjqtentacles/sml-siphash/
  siphash.sig    SIPHASH signature
  siphash.sml    SipHash-2-4 implementation
  sources.mlb    ordered source list
  siphash.mlb    public basis
examples/
  demo.sml       fixed-key hash walkthrough
test/
  harness.sml    shared assertion harness
  test.sml       canonical vectors + key/message sensitivity checks (18 checks)
  entry.sml / main.sml
tools/polybuild  Poly/ML build wrapper

Tests

18 deterministic checks, anchored on the canonical SipHash-2-4 conformance table (all 64 outputs, message lengths 0..63) from the reference C implementation's vectors.h — key 000102030405060708090a0b0c0d0e0f, message byte i has value i — the same table reproduced in the SipHash paper's appendix and used as the standard cross-implementation test by essentially every SipHash port. Also covers determinism, hex-encoding format, and key/message sensitivity. Run make all-tests to verify identical output under both compilers.

License

MIT. See LICENSE.

About

SipHash-2-4 keyed pseudorandom function in pure Standard ML

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages