Source

sha3 / sha3.go

Full commit
// Copyright 2012 Eric Roshan-Eisner. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sha3 implements the SHA3 cryptographic hash function, Keccak
// (version 3.2). Implementation details are found at
// http://keccak.noekeon.org/
package sha3

import (
	"hash"
)

type keccak struct {
	state [25]uint64
	// digest size
	size      int
	blocksize int
	// buf[:n] holds buffered data awaiting a full block (n < blocksize)
	buf [200]byte
	n   int
	// closed is set when this is closed for writing.
	closed bool
}

func newSHA3(bitsize int) *keccak {
	size := bitsize / 8
	return &keccak{
		size:      size,
		blocksize: 200 - 2*size,
	}
}

// New224 returns a new hash.Hash computing the SHA-3 224-bit checksum.
func New224() hash.Hash { return newSHA3(224) }

// New256 returns a new hash.Hash computing the SHA-3 256-bit checksum.
func New256() hash.Hash { return newSHA3(256) }

// New384 returns a new hash.Hash computing the SHA-3 384-bit checksum.
func New384() hash.Hash { return newSHA3(384) }

// New512 returns a new hash.Hash computing the SHA-3 512-bit checksum.
func New512() hash.Hash { return newSHA3(512) }

func (d *keccak) Size() int {
	return d.size
}

func (d *keccak) BlockSize() int {
	return d.blocksize
}

func (d *keccak) Reset() {
	// Zero all other fields.
	*d = keccak{
		size:      d.size,
		blocksize: d.blocksize,
	}
}

// The internal state should be written to then read with calls to absorb,
// then exactly one call to pad, then calls to squeeze.

// absorb accepts a block of data with len exactly d.blocksize. It xor's this
// data to the front of d.state (little endian), then performs one
// KeccakF[1600] permutation.
func (d *keccak) absorb(chunk []byte) {
	for i := 0; i < d.blocksize; i += 8 {
		d.state[i/8] ^= readUint64(chunk[i:])
	}
	block(&d.state)
}

// pad fills the remaining bytes in the block with the 10*1 padding rule, then
// writes it out to the state.
func (d *keccak) pad() {
	if d.closed {
		panic("sha3: close after close")
	}
	d.buf[d.n] = 0x01
	for i := d.n + 1; i < len(d.buf); i++ {
		d.buf[i] = 0
	}
	d.buf[d.blocksize-1] |= 0x80

	for i := 0; i < d.blocksize; i += 8 {
		d.state[i/8] ^= readUint64(d.buf[i:])
	}
	d.closed = true
}

// squeeze performs the permutation to the state, then reads the first block
// of data from the state (little endian) to buf
func (d *keccak) squeeze() {
	if !d.closed {
		panic("sha3: Read before close")
	}
	block(&d.state)
	for i := 0; i < d.blocksize; i += 8 {
		writeUint64(d.buf[i:], d.state[i/8])
	}
}

// Write uses d.buf to buffer calls to d.absorb from arbitrary sized inputs.
func (d *keccak) Write(b []byte) (nn int, err error) {
	if d.closed {
		panic("sha3: Write after close")
	}
	nn = len(b)
	if d.n > 0 {
		// Add to buffer.
		if len(b)+d.n < d.blocksize {
			d.n += copy(d.buf[d.n:], b)
			return
		} else {
			copy(d.buf[d.n:d.blocksize], b)
			d.absorb(d.buf[:d.blocksize])
			b = b[d.blocksize-d.n:]
			d.n = 0
		}
	}

	for len(b) >= d.blocksize {
		d.absorb(b[:d.blocksize])
		b = b[d.blocksize:]
	}

	d.n = copy(d.buf[:], b)
	return
}

func (d0 *keccak) Sum(in []byte) []byte {
	// Work on a copy to keep the original unmodified.
	d := *d0
	d.pad()
	d.squeeze()
	return append(in, d.buf[:d.size]...)
}