aboutsummaryrefslogtreecommitdiff
path: root/sha3/legacy_hash.go
diff options
context:
space:
mode:
Diffstat (limited to 'sha3/legacy_hash.go')
-rw-r--r--sha3/legacy_hash.go263
1 files changed, 263 insertions, 0 deletions
diff --git a/sha3/legacy_hash.go b/sha3/legacy_hash.go
new file mode 100644
index 0000000..b878453
--- /dev/null
+++ b/sha3/legacy_hash.go
@@ -0,0 +1,263 @@
+// 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
+
+// This implementation is only used for NewLegacyKeccak256 and
+// NewLegacyKeccak512, which are not implemented by crypto/sha3.
+// All other functions in this package are wrappers around crypto/sha3.
+
+import (
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "hash"
+ "unsafe"
+
+ "golang.org/x/sys/cpu"
+)
+
+const (
+ dsbyteKeccak = 0b00000001
+
+ // rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
+ // bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
+ rateK256 = (1600 - 256) / 8
+ rateK512 = (1600 - 512) / 8
+ rateK1024 = (1600 - 1024) / 8
+)
+
+// NewLegacyKeccak256 creates a new Keccak-256 hash.
+//
+// Only use this function if you require compatibility with an existing cryptosystem
+// that uses non-standard padding. All other users should use New256 instead.
+func NewLegacyKeccak256() hash.Hash {
+ return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
+}
+
+// NewLegacyKeccak512 creates a new Keccak-512 hash.
+//
+// Only use this function if you require compatibility with an existing cryptosystem
+// that uses non-standard padding. All other users should use New512 instead.
+func NewLegacyKeccak512() hash.Hash {
+ return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
+}
+
+// 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 (
+ magicKeccak = "sha\x0b"
+ // magic || rate || main state || n || sponge direction
+ marshaledSize = len(magicKeccak) + 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 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(magicKeccak)])
+ b = b[len(magicKeccak):]
+ switch {
+ 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
+}