// Copyright 2014 The Go Authors. 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 import ( "crypto/subtle" "encoding/binary" "errors" "unsafe" "golang.org/x/sys/cpu" ) // spongeDirection indicates the direction bytes are flowing through the sponge. type spongeDirection int const ( // spongeAbsorbing indicates that the sponge is absorbing input. spongeAbsorbing spongeDirection = iota // spongeSqueezing indicates that the sponge is being squeezed. spongeSqueezing ) type state struct { a [1600 / 8]byte // main state of the hash // a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR // into before running the permutation. If squeezing, it's the remaining // output to produce before running the permutation. n, rate int // dsbyte contains the "domain separation" bits and the first bit of // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the // SHA-3 and SHAKE functions by appending bitstrings to the message. // Using a little-endian bit-ordering convention, these are "01" for SHA-3 // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the // padding rule from section 5.1 is applied to pad the message to a multiple // of the rate, which involves adding a "1" bit, zero or more "0" bits, and // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, // giving 00000110b (0x06) and 00011111b (0x1f). // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and // Extendable-Output Functions (May 2014)" dsbyte byte outputLen int // the default output size in bytes state spongeDirection // whether the sponge is absorbing or squeezing } // BlockSize returns the rate of sponge underlying this hash function. func (d *state) BlockSize() int { return d.rate } // Size returns the output size of the hash function in bytes. func (d *state) Size() int { return d.outputLen } // Reset clears the internal state by zeroing the sponge state and // the buffer indexes, and setting Sponge.state to absorbing. func (d *state) Reset() { // Zero the permutation's state. for i := range d.a { d.a[i] = 0 } d.state = spongeAbsorbing d.n = 0 } func (d *state) clone() *state { ret := *d return &ret } // permute applies the KeccakF-1600 permutation. func (d *state) permute() { var a *[25]uint64 if cpu.IsBigEndian { a = new([25]uint64) for i := range a { a[i] = binary.LittleEndian.Uint64(d.a[i*8:]) } } else { a = (*[25]uint64)(unsafe.Pointer(&d.a)) } keccakF1600(a) d.n = 0 if cpu.IsBigEndian { for i := range a { binary.LittleEndian.PutUint64(d.a[i*8:], a[i]) } } } // pads appends the domain separation bits in dsbyte, applies // the multi-bitrate 10..1 padding rule, and permutes the state. func (d *state) padAndPermute() { // Pad with this instance's domain-separator bits. We know that there's // at least one byte of space in the sponge because, if it were full, // permute would have been called to empty it. dsbyte also contains the // first one bit for the padding. See the comment in the state struct. d.a[d.n] ^= d.dsbyte // This adds the final one bit for the padding. Because of the way that // bits are numbered from the LSB upwards, the final bit is the MSB of // the last byte. d.a[d.rate-1] ^= 0x80 // Apply the permutation d.permute() d.state = spongeSqueezing } // Write absorbs more data into the hash's state. It panics if any // output has already been read. func (d *state) Write(p []byte) (n int, err error) { if d.state != spongeAbsorbing { panic("sha3: Write after Read") } n = len(p) for len(p) > 0 { x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p) d.n += x p = p[x:] // If the sponge is full, apply the permutation. if d.n == d.rate { d.permute() } } return } // Read squeezes an arbitrary number of bytes from the sponge. func (d *state) Read(out []byte) (n int, err error) { // If we're still absorbing, pad and apply the permutation. if d.state == spongeAbsorbing { d.padAndPermute() } n = len(out) // Now, do the squeezing. for len(out) > 0 { // Apply the permutation if we've squeezed the sponge dry. if d.n == d.rate { d.permute() } x := copy(out, d.a[d.n:d.rate]) d.n += x out = out[x:] } return } // Sum applies padding to the hash state and then squeezes out the desired // number of output bytes. It panics if any output has already been read. func (d *state) Sum(in []byte) []byte { if d.state != spongeAbsorbing { panic("sha3: Sum after Read") } // Make a copy of the original hash so that caller can keep writing // and summing. dup := d.clone() hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation dup.Read(hash) return append(in, hash...) } const ( magicSHA3 = "sha\x08" magicShake = "sha\x09" magicCShake = "sha\x0a" magicKeccak = "sha\x0b" // magic || rate || main state || n || sponge direction marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1 ) func (d *state) MarshalBinary() ([]byte, error) { return d.AppendBinary(make([]byte, 0, marshaledSize)) } func (d *state) AppendBinary(b []byte) ([]byte, error) { switch d.dsbyte { case dsbyteSHA3: b = append(b, magicSHA3...) case dsbyteShake: b = append(b, magicShake...) case dsbyteCShake: b = append(b, magicCShake...) case dsbyteKeccak: b = append(b, magicKeccak...) default: panic("unknown dsbyte") } // rate is at most 168, and n is at most rate. b = append(b, byte(d.rate)) b = append(b, d.a[:]...) b = append(b, byte(d.n), byte(d.state)) return b, nil } func (d *state) UnmarshalBinary(b []byte) error { if len(b) != marshaledSize { return errors.New("sha3: invalid hash state") } magic := string(b[:len(magicSHA3)]) b = b[len(magicSHA3):] switch { case magic == magicSHA3 && d.dsbyte == dsbyteSHA3: case magic == magicShake && d.dsbyte == dsbyteShake: case magic == magicCShake && d.dsbyte == dsbyteCShake: case magic == magicKeccak && d.dsbyte == dsbyteKeccak: default: return errors.New("sha3: invalid hash state identifier") } rate := int(b[0]) b = b[1:] if rate != d.rate { return errors.New("sha3: invalid hash state function") } copy(d.a[:], b) b = b[len(d.a):] n, state := int(b[0]), spongeDirection(b[1]) if n > d.rate { return errors.New("sha3: invalid hash state") } d.n = n if state != spongeAbsorbing && state != spongeSqueezing { return errors.New("sha3: invalid hash state") } d.state = state return nil }