| // Copyright 2017 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. |
| |
| //go:build gc && !purego |
| // +build gc,!purego |
| |
| package sha3 |
| |
| // This file contains code for using the 'compute intermediate |
| // message digest' (KIMD) and 'compute last message digest' (KLMD) |
| // instructions to compute SHA-3 and SHAKE hashes on IBM Z. |
| |
| import ( |
| "hash" |
| |
| "golang.org/x/sys/cpu" |
| ) |
| |
| // codes represent 7-bit KIMD/KLMD function codes as defined in |
| // the Principles of Operation. |
| type code uint64 |
| |
| const ( |
| // function codes for KIMD/KLMD |
| sha3_224 code = 32 |
| sha3_256 = 33 |
| sha3_384 = 34 |
| sha3_512 = 35 |
| shake_128 = 36 |
| shake_256 = 37 |
| nopad = 0x100 |
| ) |
| |
| // kimd is a wrapper for the 'compute intermediate message digest' instruction. |
| // src must be a multiple of the rate for the given function code. |
| //go:noescape |
| func kimd(function code, chain *[200]byte, src []byte) |
| |
| // klmd is a wrapper for the 'compute last message digest' instruction. |
| // src padding is handled by the instruction. |
| //go:noescape |
| func klmd(function code, chain *[200]byte, dst, src []byte) |
| |
| type asmState struct { |
| a [200]byte // 1600 bit state |
| buf []byte // care must be taken to ensure cap(buf) is a multiple of rate |
| rate int // equivalent to block size |
| storage [3072]byte // underlying storage for buf |
| outputLen int // output length if fixed, 0 if not |
| function code // KIMD/KLMD function code |
| state spongeDirection // whether the sponge is absorbing or squeezing |
| } |
| |
| func newAsmState(function code) *asmState { |
| var s asmState |
| s.function = function |
| switch function { |
| case sha3_224: |
| s.rate = 144 |
| s.outputLen = 28 |
| case sha3_256: |
| s.rate = 136 |
| s.outputLen = 32 |
| case sha3_384: |
| s.rate = 104 |
| s.outputLen = 48 |
| case sha3_512: |
| s.rate = 72 |
| s.outputLen = 64 |
| case shake_128: |
| s.rate = 168 |
| case shake_256: |
| s.rate = 136 |
| default: |
| panic("sha3: unrecognized function code") |
| } |
| |
| // limit s.buf size to a multiple of s.rate |
| s.resetBuf() |
| return &s |
| } |
| |
| func (s *asmState) clone() *asmState { |
| c := *s |
| c.buf = c.storage[:len(s.buf):cap(s.buf)] |
| return &c |
| } |
| |
| // copyIntoBuf copies b into buf. It will panic if there is not enough space to |
| // store all of b. |
| func (s *asmState) copyIntoBuf(b []byte) { |
| bufLen := len(s.buf) |
| s.buf = s.buf[:len(s.buf)+len(b)] |
| copy(s.buf[bufLen:], b) |
| } |
| |
| // resetBuf points buf at storage, sets the length to 0 and sets cap to be a |
| // multiple of the rate. |
| func (s *asmState) resetBuf() { |
| max := (cap(s.storage) / s.rate) * s.rate |
| s.buf = s.storage[:0:max] |
| } |
| |
| // Write (via the embedded io.Writer interface) adds more data to the running hash. |
| // It never returns an error. |
| func (s *asmState) Write(b []byte) (int, error) { |
| if s.state != spongeAbsorbing { |
| panic("sha3: write to sponge after read") |
| } |
| length := len(b) |
| for len(b) > 0 { |
| if len(s.buf) == 0 && len(b) >= cap(s.buf) { |
| // Hash the data directly and push any remaining bytes |
| // into the buffer. |
| remainder := len(b) % s.rate |
| kimd(s.function, &s.a, b[:len(b)-remainder]) |
| if remainder != 0 { |
| s.copyIntoBuf(b[len(b)-remainder:]) |
| } |
| return length, nil |
| } |
| |
| if len(s.buf) == cap(s.buf) { |
| // flush the buffer |
| kimd(s.function, &s.a, s.buf) |
| s.buf = s.buf[:0] |
| } |
| |
| // copy as much as we can into the buffer |
| n := len(b) |
| if len(b) > cap(s.buf)-len(s.buf) { |
| n = cap(s.buf) - len(s.buf) |
| } |
| s.copyIntoBuf(b[:n]) |
| b = b[n:] |
| } |
| return length, nil |
| } |
| |
| // Read squeezes an arbitrary number of bytes from the sponge. |
| func (s *asmState) Read(out []byte) (n int, err error) { |
| n = len(out) |
| |
| // need to pad if we were absorbing |
| if s.state == spongeAbsorbing { |
| s.state = spongeSqueezing |
| |
| // write hash directly into out if possible |
| if len(out)%s.rate == 0 { |
| klmd(s.function, &s.a, out, s.buf) // len(out) may be 0 |
| s.buf = s.buf[:0] |
| return |
| } |
| |
| // write hash into buffer |
| max := cap(s.buf) |
| if max > len(out) { |
| max = (len(out)/s.rate)*s.rate + s.rate |
| } |
| klmd(s.function, &s.a, s.buf[:max], s.buf) |
| s.buf = s.buf[:max] |
| } |
| |
| for len(out) > 0 { |
| // flush the buffer |
| if len(s.buf) != 0 { |
| c := copy(out, s.buf) |
| out = out[c:] |
| s.buf = s.buf[c:] |
| continue |
| } |
| |
| // write hash directly into out if possible |
| if len(out)%s.rate == 0 { |
| klmd(s.function|nopad, &s.a, out, nil) |
| return |
| } |
| |
| // write hash into buffer |
| s.resetBuf() |
| if cap(s.buf) > len(out) { |
| s.buf = s.buf[:(len(out)/s.rate)*s.rate+s.rate] |
| } |
| klmd(s.function|nopad, &s.a, s.buf, nil) |
| } |
| return |
| } |
| |
| // Sum appends the current hash to b and returns the resulting slice. |
| // It does not change the underlying hash state. |
| func (s *asmState) Sum(b []byte) []byte { |
| if s.outputLen == 0 { |
| panic("sha3: cannot call Sum on SHAKE functions") |
| } |
| |
| // Copy the state to preserve the original. |
| a := s.a |
| |
| // Hash the buffer. Note that we don't clear it because we |
| // aren't updating the state. |
| klmd(s.function, &a, nil, s.buf) |
| return append(b, a[:s.outputLen]...) |
| } |
| |
| // Reset resets the Hash to its initial state. |
| func (s *asmState) Reset() { |
| for i := range s.a { |
| s.a[i] = 0 |
| } |
| s.resetBuf() |
| s.state = spongeAbsorbing |
| } |
| |
| // Size returns the number of bytes Sum will return. |
| func (s *asmState) Size() int { |
| return s.outputLen |
| } |
| |
| // BlockSize returns the hash's underlying block size. |
| // The Write method must be able to accept any amount |
| // of data, but it may operate more efficiently if all writes |
| // are a multiple of the block size. |
| func (s *asmState) BlockSize() int { |
| return s.rate |
| } |
| |
| // Clone returns a copy of the ShakeHash in its current state. |
| func (s *asmState) Clone() ShakeHash { |
| return s.clone() |
| } |
| |
| // new224Asm returns an assembly implementation of SHA3-224 if available, |
| // otherwise it returns nil. |
| func new224Asm() hash.Hash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(sha3_224) |
| } |
| return nil |
| } |
| |
| // new256Asm returns an assembly implementation of SHA3-256 if available, |
| // otherwise it returns nil. |
| func new256Asm() hash.Hash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(sha3_256) |
| } |
| return nil |
| } |
| |
| // new384Asm returns an assembly implementation of SHA3-384 if available, |
| // otherwise it returns nil. |
| func new384Asm() hash.Hash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(sha3_384) |
| } |
| return nil |
| } |
| |
| // new512Asm returns an assembly implementation of SHA3-512 if available, |
| // otherwise it returns nil. |
| func new512Asm() hash.Hash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(sha3_512) |
| } |
| return nil |
| } |
| |
| // newShake128Asm returns an assembly implementation of SHAKE-128 if available, |
| // otherwise it returns nil. |
| func newShake128Asm() ShakeHash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(shake_128) |
| } |
| return nil |
| } |
| |
| // newShake256Asm returns an assembly implementation of SHAKE-256 if available, |
| // otherwise it returns nil. |
| func newShake256Asm() ShakeHash { |
| if cpu.S390X.HasSHA3 { |
| return newAsmState(shake_256) |
| } |
| return nil |
| } |