package filter import "encoding/binary" // SipHash-2-4 constants. const ( sipC = 2 // compression rounds per message block sipD = 4 // finalization rounds ) // siphash24 computes the SipHash-2-4 MAC of msg under key and returns the // 64-bit tag. The key is interpreted as two little-endian uint64s, as required // by the reference implementation and BIP158. func siphash24(key [16]byte, msg []byte) uint64 { k0 := binary.LittleEndian.Uint64(key[0:8]) k1 := binary.LittleEndian.Uint64(key[8:16]) v0 := k0 ^ 0x736f6d6570736575 v1 := k1 ^ 0x646f72616e646f6d v2 := k0 ^ 0x6c7967656e657261 v3 := k1 ^ 0x7465646279746573 length := len(msg) // Process all complete 8-byte little-endian words. for len(msg) >= 8 { m := binary.LittleEndian.Uint64(msg[:8]) msg = msg[8:] v3 ^= m for i := 0; i < sipC; i++ { v0, v1, v2, v3 = sipRound(v0, v1, v2, v3) } v0 ^= m } // The last block contains the remaining 0..7 bytes followed by the total // message length modulo 256 in the top byte. var b uint64 for i, c := range msg { b |= uint64(c) << (8 * uint(i)) } b |= uint64(length) << 56 v3 ^= b for i := 0; i < sipC; i++ { v0, v1, v2, v3 = sipRound(v0, v1, v2, v3) } v0 ^= b v2 ^= 0xff for i := 0; i < sipD; i++ { v0, v1, v2, v3 = sipRound(v0, v1, v2, v3) } return v0 ^ v1 ^ v2 ^ v3 } // sipRound performs one SipHash round. The four state words are passed and // returned by value so the round can be inlined by the compiler. func sipRound(v0, v1, v2, v3 uint64) (uint64, uint64, uint64, uint64) { v0 += v1 v1 = v1<<13 | v1>>(64-13) v1 ^= v0 v0 = v0<<32 | v0>>(64-32) v2 += v3 v3 = v3<<16 | v3>>(64-16) v3 ^= v2 v0 += v3 v3 = v3<<21 | v3>>(64-21) v3 ^= v0 v2 += v1 v1 = v1<<17 | v1>>(64-17) v1 ^= v2 v2 = v2<<32 | v2>>(64-32) return v0, v1, v2, v3 }