// Hamadryad: SWIFFT-derived lattice hash. // Ring: Z_257[x]/(x^64 + 1), n=64, m=16. // Output: 448-bit (56 bytes) canonical hash, 48-bit (6 bytes) shard. package hamcrypto import "crypto/rand" const ( HamN = 64 HamM = 16 HamP = 257 HamR = 128 HamInputBits = HamM * HamN HamFullBits = HamN * 9 HamBits = HamN * 7 HamBytes = HamBits / 8 ShardBits = 48 ShardBytes = ShardBits / 8 ) type Hamadryad [HamBytes]byte type Shard [ShardBytes]byte // Disperse extracts the 48-bit Shard from a full Hamadryad. func (p *Hamadryad) Disperse() (s Shard) { copy(s[:], p[:ShardBytes]) return s } // hamKeys holds the m=16 fixed random polynomials in NTT form. var hamKeys [HamM][HamN]uint16 const hamInputBytes = HamInputBits / 8 // Single init for the crypto package: NTT tables + key generation. func main() { initNTTTables() initNTT27Tables() initHamKeys() initGnarlKeys() } func initHamKeys() { seed := uint64(0) seedStr := "hamadryad-swifft-v1-dendrite-kismet" for i := int32(0); i < int32(len(seedStr)); i++ { seed ^= uint64(seedStr[i]) << ((uint(i) * 7) % 64) } xorshift := func() (v uint64) { seed ^= seed << 13 seed ^= seed >> 7 seed ^= seed << 17 return seed } for i := int32(0); i < HamM; i++ { var poly [HamN]uint16 for j := int32(0); j < HamN; j++ { poly[j] = uint16(xorshift() % uint64(HamP)) } ntt64(&poly) hamKeys[i] = poly } } // hamCompress computes one SWIFFT compression: 128 bytes -> 64 coefficients in Z_257. func hamCompress(block *[hamInputBytes]byte) (acc [HamN]uint16) { for i := int32(0); i < HamM; i++ { var poly [HamN]uint16 base := i * (HamN / 8) for j := int32(0); j < HamN; j++ { byteIdx := base + j/8 bitIdx := uint(j % 8) if block[byteIdx]&(1<>8), byte(msgLenBits>>16), byte(msgLenBits>>24), byte(msgLenBits>>32), byte(msgLenBits>>40), byte(msgLenBits>>48), byte(msgLenBits>>56), ) // Process each 128-byte block. off := int32(0) for off < int32(len(padded)) { var block [hamInputBytes]byte copy(block[:], padded[off:off+hamInputBytes]) result := hamCompress(&block) for j := int32(0); j < HamN; j++ { chain[j] = mod257(int32(chain[j]) + int32(result[j])) } off += hamInputBytes } return packCoeffs(chain) } // Sum returns the coefficient-wise sum (mod 128) of two Hamadryad hashes. func (p *Hamadryad) Sum(other *Hamadryad) (result Hamadryad) { a := unpackCoeffs(*p) b := unpackCoeffs(*other) var res [HamN]uint16 for i := int32(0); i < HamN; i++ { res[i] = (a[i] + b[i]) % HamR } return packCoeffs(res) } // IsZero reports whether the Hamadryad hash is all zeros. func (p *Hamadryad) IsZero() (result bool) { for i := int32(0); i < HamBytes; i++ { if p[i] != 0 { return false } } return true } // RandomHamadryad generates a cryptographically random Hamadryad value. func RandomHamadryad() (p Hamadryad) { _, err := rand.Read(p[:]) if err != nil { panic("crypto/rand failed") } return p }