151 lines
3.9 KiB
Go
151 lines
3.9 KiB
Go
// 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)
|