commit ed04711f609bb39eb72811b84a0cc5e961bb089a Author: Andrew Ekstedt Date: Wed Dec 31 14:59:00 2014 -0800 Initial commit. diff --git a/const.go b/const.go new file mode 100644 index 0000000..71d9f29 --- /dev/null +++ b/const.go @@ -0,0 +1,3 @@ +package keccak +var RC = [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} +var rotc = [5][5]uint{[5]uint{0x0, 0x24, 0x3, 0x29, 0x12}, [5]uint{0x1, 0x2c, 0xa, 0x2d, 0x2}, [5]uint{0x3e, 0x6, 0x2b, 0xf, 0x3d}, [5]uint{0x1c, 0x37, 0x19, 0x15, 0x38}, [5]uint{0x1b, 0x14, 0x27, 0x8, 0xe}} diff --git a/genconst.go b/genconst.go new file mode 100644 index 0000000..5dc8d36 --- /dev/null +++ b/genconst.go @@ -0,0 +1,27 @@ +// +build ignore + +package main + +import "fmt" + +func main() { + //Round constants + var RC [24]uint64 + rc := uint8(1) + for i := 0; i < 24; i++ { + for j := 0; j <= 6; j++ { + RC[i] |= uint64(rc&1) << (1<>7) + } + } + fmt.Println("package keccak") + fmt.Printf("var RC = %#016v\n", RC) + + var rot [5][5]uint + x, y := 1, 0 + for i := 0; i < 24; i++ { + rot[x][y] = uint((i+1)*(i+2)/2)%64 + x, y = y, (2*x+3*y)%5 + } + fmt.Printf("var rotc = %#v\n", rot) +} diff --git a/keccak.go b/keccak.go new file mode 100644 index 0000000..d50300a --- /dev/null +++ b/keccak.go @@ -0,0 +1,70 @@ +package keccak + +// roundGeneric implements one round of the keccak-f[1600] permutation. +func roundGeneric(a [5][5]uint64) [5][5]uint64 { + a = theta(a) + a = rho(a) + a = pi(a) + a = chi(a) + // Iota + //a[0][0] ^= RC[i] + return a +} + +// Theta +func theta(a [5][5]uint64) [5][5]uint64 { + var c [5]uint64 + for x := range a { + c[x] = a[x][0] ^ a[x][1] ^ a[x][2] ^ a[x][3] ^ a[x][4] + } + for x := range a { + x0, x1 := (x+4)%5, (x+1)%5 + a[x][0] ^= c[x0] ^ rotl(c[x1], 1) + a[x][1] ^= c[x0] ^ rotl(c[x1], 1) + a[x][2] ^= c[x0] ^ rotl(c[x1], 1) + a[x][3] ^= c[x0] ^ rotl(c[x1], 1) + a[x][4] ^= c[x0] ^ rotl(c[x1], 1) + } + return a +} + +// Rho +func rho(a [5][5]uint64) [5][5]uint64 { + for x := range a { + for y := range a[0] { + a[x][y] = rotl(a[x][y], rotc[x][y]) + } + } + return a +} + +// Pi +func pi(a [5][5]uint64) [5][5]uint64 { + var b [5][5]uint64 + for x := range a { + for y := range a[0] { + x0 := y + y0 := (x*2 + y*3) % 5 + b[x0][y0] = a[x][y] + } + } + return b +} + +// Chi +func chi(a [5][5]uint64) [5][5]uint64 { + for y := range a[0] { + c := [5]uint64{a[0][y], a[1][y], a[2][y], a[3][y], a[4][y]} + a[0][y] ^= ^c[1] & c[2] + a[1][y] ^= ^c[2] & c[3] + a[2][y] ^= ^c[3] & c[4] + a[3][y] ^= ^c[4] & c[0] + a[4][y] ^= ^c[0] & c[1] + } + return a +} + +func rotl(a uint64, r uint) uint64 { + return a<>(64-r) +} + diff --git a/keccak_test.go b/keccak_test.go new file mode 100644 index 0000000..f75f824 --- /dev/null +++ b/keccak_test.go @@ -0,0 +1,29 @@ +package keccak + +import ( + "reflect" + "testing" +) + +var vector = []byte{0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, + 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, + 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, + 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70, +} + +func TestKeccak(t *testing.T) { + h := New() + sum := h.Sum(nil) + if !reflect.DeepEqual(sum, vector) { + t.Errorf("\"\": want % x, got % x", vector, sum) + } +} + +func Benchmark256(b *testing.B) { + var tmp [Size]byte + h := New() + b.SetBytes(BlockSize) + for i := 0; i < b.N; i ++ { + h.Sum(tmp[:]) + } +} diff --git a/sponge.go b/sponge.go new file mode 100644 index 0000000..41a2cd9 --- /dev/null +++ b/sponge.go @@ -0,0 +1,87 @@ +package keccak + +import "hash" + +const Size = 256/8 + +const BlockSize = 1600/8 - Size*2 + +// digest implements hash.Hash +type digest struct { + a [5][5]uint64 + buf [BlockSize]byte + len int +} + +func New() hash.Hash { + return &digest{} +} + +func (d *digest) Size() int { return Size } +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Reset() { + *d = digest{} +} + +func (d *digest) Write(b []byte) (int, error) { + written := len(b) + for len(b) > 0 { + n := copy(d.buf[d.len:], b) + d.len += n + b = b[n:] + if d.len == BlockSize { + d.flush() + } + } + return written, nil +} + +func (d *digest) flush() { + b := d.buf[:] +loop: + for y := range d.a[0] { + for x := range d.a { + if len(b) == 0 { + break loop + } + d.a[x][y] ^= uint64(b[0]) + uint64(b[1])<<8 + uint64(b[2])<<16 + uint64(b[3])<<24 + uint64(b[4])<<32 + uint64(b[5])<<40 + uint64(b[6])<<48 + uint64(b[7])<<56 + b = b[8:] + } + } + d.a = keccak(d.a) + d.len = 0 +} + +func keccak(a [5][5]uint64) [5][5]uint64 { + for i := 0; i < 24; i++ { + a = roundGeneric(a) + a[0][0] ^= RC[i] + } + return a +} + +func (d0 *digest) Sum(b []byte) []byte { + d := *d0 + d.buf[d.len] = 0x01 + for i := d.len+1; i < BlockSize; i++ { + d.buf[i] = 0 + } + d.buf[BlockSize-1] |= 0x80 + d.flush() + + b = le64enc(b, d.a[0][0]) + b = le64enc(b, d.a[1][0]) + b = le64enc(b, d.a[2][0]) + b = le64enc(b, d.a[3][0]) + return b +} + +func le64enc(b []byte, x uint64) []byte { + return append(b, byte(x), byte(x>>8), byte(x>>16), byte(x>>24), byte(x>>32), byte(x>>40), byte(x>>48), byte(x>>56)) +} + +func (d *digest) writeByte(b byte) { + var tmp = [1]byte{b} + d.Write(tmp[:]) +}