Files
kisstron/keccak/keccak.go
T
2026-09-19 18:26:07 +03:00

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)