ipld-eth-server/vendor/github.com/gxed/hashland/keccakpg/keccak.go

225 lines
3.9 KiB
Go

// Package keccak implements the Keccak (SHA-3) hash algorithm.
// http://keccak.noekeon.org.
package keccakpg
import (
_ "fmt"
"hash"
)
const stdRounds = 24
var roundConstants = []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,
}
var rotationConstants = [24]uint{
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 piLane = [24]uint{
10, 7, 11, 17, 18, 3, 5, 16,
8, 21, 24, 4, 15, 23, 19, 13,
12, 2, 20, 14, 22, 9, 6, 1,
}
type keccak struct {
S [25]uint64
size int
blockSize int
rounds int
buf []byte
}
func newKeccak(bitlen, rounds int) hash.Hash {
var h keccak
h.size = bitlen / 8
h.blockSize = (200 - 2*h.size)
h.rounds = rounds
if rounds != stdRounds {
//fmt.Printf("keccak: warning non standard number of rounds %d vs %d\n", rounds, stdRounds)
}
return &h
}
func NewCustom(bits, rounds int) hash.Hash {
return newKeccak(bits, rounds)
}
func New160() hash.Hash {
return newKeccak(160, stdRounds)
}
func New224() hash.Hash {
return newKeccak(224, stdRounds)
}
func New256() hash.Hash {
return newKeccak(256, stdRounds)
}
func New384() hash.Hash {
return newKeccak(384, stdRounds)
}
func New512() hash.Hash {
return newKeccak(512, stdRounds)
}
func (k *keccak) Write(b []byte) (int, error) {
n := len(b)
if len(k.buf) > 0 {
x := k.blockSize - len(k.buf)
if x > len(b) {
x = len(b)
}
k.buf = append(k.buf, b[:x]...)
b = b[x:]
if len(k.buf) < k.blockSize {
return n, nil
}
k.f(k.buf)
k.buf = nil
}
for len(b) >= k.blockSize {
k.f(b[:k.blockSize])
b = b[k.blockSize:]
}
k.buf = b
return n, nil
}
func (k0 *keccak) Sum(b []byte) []byte {
k := *k0
last := k.pad(k.buf)
k.f(last)
buf := make([]byte, len(k.S)*8)
for i := range k.S {
putUint64le(buf[i*8:], k.S[i])
}
return append(b, buf[:k.size]...)
}
func (k *keccak) Reset() {
for i := range k.S {
k.S[i] = 0
}
k.buf = nil
}
func (k *keccak) Size() int {
return k.size
}
func (k *keccak) BlockSize() int {
return k.blockSize
}
func rotl64(x uint64, n uint) uint64 {
return (x << n) | (x >> (64 - n))
}
func (k *keccak) f(block []byte) {
if len(block) != k.blockSize {
panic("f() called with invalid block size")
}
for i := 0; i < k.blockSize/8; i++ {
k.S[i] ^= uint64le(block[i*8:])
}
for r := 0; r < k.rounds; r++ {
var bc [5]uint64
// theta
for i := range bc {
bc[i] = k.S[i] ^ k.S[5+i] ^ k.S[10+i] ^ k.S[15+i] ^ k.S[20+i]
}
for i := range bc {
t := bc[(i+4)%5] ^ rotl64(bc[(i+1)%5], 1)
for j := 0; j < len(k.S); j += 5 {
k.S[i+j] ^= t
}
}
// rho phi
temp := k.S[1]
for i := range piLane {
j := piLane[i]
temp2 := k.S[j]
k.S[j] = rotl64(temp, rotationConstants[i])
temp = temp2
}
// chi
for j := 0; j < len(k.S); j += 5 {
for i := range bc {
bc[i] = k.S[j+i]
}
for i := range bc {
k.S[j+i] ^= (^bc[(i+1)%5]) & bc[(i+2)%5]
}
}
// iota
k.S[0] ^= roundConstants[r]
}
}
func (k *keccak) pad(block []byte) []byte {
padded := make([]byte, k.blockSize)
copy(padded, k.buf)
padded[len(k.buf)] = 0x01
padded[len(padded)-1] |= 0x80
return padded
}
func uint64le(v []byte) uint64 {
return uint64(v[0]) |
uint64(v[1])<<8 |
uint64(v[2])<<16 |
uint64(v[3])<<24 |
uint64(v[4])<<32 |
uint64(v[5])<<40 |
uint64(v[6])<<48 |
uint64(v[7])<<56
}
func putUint64le(v []byte, x uint64) {
v[0] = byte(x)
v[1] = byte(x >> 8)
v[2] = byte(x >> 16)
v[3] = byte(x >> 24)
v[4] = byte(x >> 32)
v[5] = byte(x >> 40)
v[6] = byte(x >> 48)
v[7] = byte(x >> 56)
}