// Package keccak implements the legacy Keccak-256 hash used by TRON and // Ethereum. The Go standard library only exposes FIPS-202 SHA3/SHAKE, which // differ in the domain-separation padding byte (0x06 vs 0x01), so the Keccak // permutation is implemented directly here. package keccak import ( "encoding/binary" "hash" ) // rate is the Keccak-256 sponge rate in bytes (1600 - 2*256 bits). const rate = 136 var roundConstants = [24]uint64{ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, } // rotation offsets and pi lane permutation for the rho/pi steps. var rhoOffsets = [24]int{1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44} var piLanes = [24]int{10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1} func rotl(x uint64, n int) uint64 { return (x << n) | (x >> (64 - n)) } // permute applies the Keccak-f[1600] permutation in place. func permute(a *[25]uint64) { for round := 0; round < 24; round++ { // theta var c [5]uint64 for i := 0; i < 5; i++ { c[i] = a[i] ^ a[i+5] ^ a[i+10] ^ a[i+15] ^ a[i+20] } for i := 0; i < 5; i++ { d := c[(i+4)%5] ^ rotl(c[(i+1)%5], 1) for j := 0; j < 25; j += 5 { a[j+i] ^= d } } // rho and pi last := a[1] for i := 0; i < 24; i++ { j := piLanes[i] last, a[j] = a[j], rotl(last, rhoOffsets[i]) } // chi for j := 0; j < 25; j += 5 { var row [5]uint64 copy(row[:], a[j:j+5]) for i := 0; i < 5; i++ { a[j+i] = row[i] ^ (^row[(i+1)%5] & row[(i+2)%5]) } } // iota a[0] ^= roundConstants[round] } } // State is a streaming Keccak-256 sponge state. type State struct { a [25]uint64 buf [rate]byte n int } // New returns a fresh Keccak-256 hash state. func New() *State { return &State{} } func (s *State) absorbBlock(b *[rate]byte) { for i := 0; i < rate/8; i++ { s.a[i] ^= binary.LittleEndian.Uint64(b[i*8:]) } permute(&s.a) } // Write absorbs data into the sponge. It never returns an error. func (s *State) Write(p []byte) (int, error) { total := len(p) for len(p) > 0 { n := copy(s.buf[s.n:], p) s.n += n p = p[n:] if s.n == rate { s.absorbBlock(&s.buf) s.n = 0 } } return total, nil } // Sum256 returns the Keccak-256 digest without disturbing the state. func (s *State) Sum256() [32]byte { clone := *s var out [32]byte clone.finalize(&out) return out } // Sum appends the current digest to b, implementing hash.Hash. func (s *State) Sum(b []byte) []byte { d := s.Sum256() return append(b, d[:]...) } // Reset clears the state to its initial value. func (s *State) Reset() { s.a = [25]uint64{} s.buf = [rate]byte{} s.n = 0 } // Size returns the digest length in bytes. func (s *State) Size() int { return 32 } // BlockSize returns the sponge rate in bytes. func (s *State) BlockSize() int { return rate } func (s *State) finalize(out *[32]byte) { var block [rate]byte copy(block[:], s.buf[:s.n]) block[s.n] ^= 0x01 block[rate-1] ^= 0x80 s.absorbBlock(&block) for i := 0; i < 4; i++ { binary.LittleEndian.PutUint64(out[i*8:], s.a[i]) } } // Hash256 returns the Keccak-256 digest of data. func Hash256(data []byte) [32]byte { h := New() h.Write(data) return h.Sum256() } // Sum256 is a package-level convenience matching crypto/sha256.Sum256. func Sum256(data []byte) [32]byte { return Hash256(data) } // compile-time assurance that State satisfies hash.Hash. var _ hash.Hash = (*State)(nil)