|
| 1 | +// Copyright 2017 The Go Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +//+build !gccgo,!appengine |
| 6 | + |
| 7 | +package sha3 |
| 8 | + |
| 9 | +// This file contains code for using the 'compute intermediate |
| 10 | +// message digest' (KIMD) and 'compute last message digest' (KLMD) |
| 11 | +// instructions to compute SHA-3 and SHAKE hashes on IBM Z. |
| 12 | + |
| 13 | +import ( |
| 14 | + "hash" |
| 15 | +) |
| 16 | + |
| 17 | +// codes represent 7-bit KIMD/KLMD function codes as defined in |
| 18 | +// the Principles of Operation. |
| 19 | +type code uint64 |
| 20 | + |
| 21 | +const ( |
| 22 | + // function codes for KIMD/KLMD |
| 23 | + sha3_224 code = 32 |
| 24 | + sha3_256 = 33 |
| 25 | + sha3_384 = 34 |
| 26 | + sha3_512 = 35 |
| 27 | + shake_128 = 36 |
| 28 | + shake_256 = 37 |
| 29 | + nopad = 0x100 |
| 30 | +) |
| 31 | + |
| 32 | +// hasMSA6 reports whether the machine supports the SHA-3 and SHAKE function |
| 33 | +// codes, as defined in message-security-assist extension 6. |
| 34 | +func hasMSA6() bool |
| 35 | + |
| 36 | +// hasAsm caches the result of hasMSA6 (which might be expensive to call). |
| 37 | +var hasAsm = hasMSA6() |
| 38 | + |
| 39 | +// kimd is a wrapper for the 'compute intermediate message digest' instruction. |
| 40 | +// src must be a multiple of the rate for the given function code. |
| 41 | +//go:noescape |
| 42 | +func kimd(function code, chain *[200]byte, src []byte) |
| 43 | + |
| 44 | +// klmd is a wrapper for the 'compute last message digest' instruction. |
| 45 | +// src padding is handled by the instruction. |
| 46 | +//go:noescape |
| 47 | +func klmd(function code, chain *[200]byte, dst, src []byte) |
| 48 | + |
| 49 | +type asmState struct { |
| 50 | + a [200]byte // 1600 bit state |
| 51 | + buf []byte // care must be taken to ensure cap(buf) is a multiple of rate |
| 52 | + rate int // equivalent to block size |
| 53 | + storage [3072]byte // underlying storage for buf |
| 54 | + outputLen int // output length if fixed, 0 if not |
| 55 | + function code // KIMD/KLMD function code |
| 56 | + state spongeDirection // whether the sponge is absorbing or squeezing |
| 57 | +} |
| 58 | + |
| 59 | +func newAsmState(function code) *asmState { |
| 60 | + var s asmState |
| 61 | + s.function = function |
| 62 | + switch function { |
| 63 | + case sha3_224: |
| 64 | + s.rate = 144 |
| 65 | + s.outputLen = 28 |
| 66 | + case sha3_256: |
| 67 | + s.rate = 136 |
| 68 | + s.outputLen = 32 |
| 69 | + case sha3_384: |
| 70 | + s.rate = 104 |
| 71 | + s.outputLen = 48 |
| 72 | + case sha3_512: |
| 73 | + s.rate = 72 |
| 74 | + s.outputLen = 64 |
| 75 | + case shake_128: |
| 76 | + s.rate = 168 |
| 77 | + case shake_256: |
| 78 | + s.rate = 136 |
| 79 | + default: |
| 80 | + panic("sha3: unrecognized function code") |
| 81 | + } |
| 82 | + |
| 83 | + // limit s.buf size to a multiple of s.rate |
| 84 | + s.resetBuf() |
| 85 | + return &s |
| 86 | +} |
| 87 | + |
| 88 | +func (s *asmState) clone() *asmState { |
| 89 | + c := *s |
| 90 | + c.buf = c.storage[:len(s.buf):cap(s.buf)] |
| 91 | + return &c |
| 92 | +} |
| 93 | + |
| 94 | +// copyIntoBuf copies b into buf. It will panic if there is not enough space to |
| 95 | +// store all of b. |
| 96 | +func (s *asmState) copyIntoBuf(b []byte) { |
| 97 | + bufLen := len(s.buf) |
| 98 | + s.buf = s.buf[:len(s.buf)+len(b)] |
| 99 | + copy(s.buf[bufLen:], b) |
| 100 | +} |
| 101 | + |
| 102 | +// resetBuf points buf at storage, sets the length to 0 and sets cap to be a |
| 103 | +// multiple of the rate. |
| 104 | +func (s *asmState) resetBuf() { |
| 105 | + max := (cap(s.storage) / s.rate) * s.rate |
| 106 | + s.buf = s.storage[:0:max] |
| 107 | +} |
| 108 | + |
| 109 | +// Write (via the embedded io.Writer interface) adds more data to the running hash. |
| 110 | +// It never returns an error. |
| 111 | +func (s *asmState) Write(b []byte) (int, error) { |
| 112 | + if s.state != spongeAbsorbing { |
| 113 | + panic("sha3: write to sponge after read") |
| 114 | + } |
| 115 | + length := len(b) |
| 116 | + for len(b) > 0 { |
| 117 | + if len(s.buf) == 0 && len(b) >= cap(s.buf) { |
| 118 | + // Hash the data directly and push any remaining bytes |
| 119 | + // into the buffer. |
| 120 | + remainder := len(s.buf) % s.rate |
| 121 | + kimd(s.function, &s.a, b[:len(b)-remainder]) |
| 122 | + if remainder != 0 { |
| 123 | + s.copyIntoBuf(b[len(b)-remainder:]) |
| 124 | + } |
| 125 | + return length, nil |
| 126 | + } |
| 127 | + |
| 128 | + if len(s.buf) == cap(s.buf) { |
| 129 | + // flush the buffer |
| 130 | + kimd(s.function, &s.a, s.buf) |
| 131 | + s.buf = s.buf[:0] |
| 132 | + } |
| 133 | + |
| 134 | + // copy as much as we can into the buffer |
| 135 | + n := len(b) |
| 136 | + if len(b) > cap(s.buf)-len(s.buf) { |
| 137 | + n = cap(s.buf) - len(s.buf) |
| 138 | + } |
| 139 | + s.copyIntoBuf(b[:n]) |
| 140 | + b = b[n:] |
| 141 | + } |
| 142 | + return length, nil |
| 143 | +} |
| 144 | + |
| 145 | +// Read squeezes an arbitrary number of bytes from the sponge. |
| 146 | +func (s *asmState) Read(out []byte) (n int, err error) { |
| 147 | + n = len(out) |
| 148 | + |
| 149 | + // need to pad if we were absorbing |
| 150 | + if s.state == spongeAbsorbing { |
| 151 | + s.state = spongeSqueezing |
| 152 | + |
| 153 | + // write hash directly into out if possible |
| 154 | + if len(out)%s.rate == 0 { |
| 155 | + klmd(s.function, &s.a, out, s.buf) // len(out) may be 0 |
| 156 | + s.buf = s.buf[:0] |
| 157 | + return |
| 158 | + } |
| 159 | + |
| 160 | + // write hash into buffer |
| 161 | + max := cap(s.buf) |
| 162 | + if max > len(out) { |
| 163 | + max = (len(out)/s.rate)*s.rate + s.rate |
| 164 | + } |
| 165 | + klmd(s.function, &s.a, s.buf[:max], s.buf) |
| 166 | + s.buf = s.buf[:max] |
| 167 | + } |
| 168 | + |
| 169 | + for len(out) > 0 { |
| 170 | + // flush the buffer |
| 171 | + if len(s.buf) != 0 { |
| 172 | + c := copy(out, s.buf) |
| 173 | + out = out[c:] |
| 174 | + s.buf = s.buf[c:] |
| 175 | + continue |
| 176 | + } |
| 177 | + |
| 178 | + // write hash directly into out if possible |
| 179 | + if len(out)%s.rate == 0 { |
| 180 | + klmd(s.function|nopad, &s.a, out, nil) |
| 181 | + return |
| 182 | + } |
| 183 | + |
| 184 | + // write hash into buffer |
| 185 | + s.resetBuf() |
| 186 | + if cap(s.buf) > len(out) { |
| 187 | + s.buf = s.buf[:(len(out)/s.rate)*s.rate+s.rate] |
| 188 | + } |
| 189 | + klmd(s.function|nopad, &s.a, s.buf, nil) |
| 190 | + } |
| 191 | + return |
| 192 | +} |
| 193 | + |
| 194 | +// Sum appends the current hash to b and returns the resulting slice. |
| 195 | +// It does not change the underlying hash state. |
| 196 | +func (s *asmState) Sum(b []byte) []byte { |
| 197 | + if s.outputLen == 0 { |
| 198 | + panic("sha3: cannot call Sum on SHAKE functions") |
| 199 | + } |
| 200 | + |
| 201 | + // Copy the state to preserve the original. |
| 202 | + a := s.a |
| 203 | + |
| 204 | + // Hash the buffer. Note that we don't clear it because we |
| 205 | + // aren't updating the state. |
| 206 | + klmd(s.function, &a, nil, s.buf) |
| 207 | + return append(b, a[:s.outputLen]...) |
| 208 | +} |
| 209 | + |
| 210 | +// Reset resets the Hash to its initial state. |
| 211 | +func (s *asmState) Reset() { |
| 212 | + for i := range s.a { |
| 213 | + s.a[i] = 0 |
| 214 | + } |
| 215 | + s.resetBuf() |
| 216 | + s.state = spongeAbsorbing |
| 217 | +} |
| 218 | + |
| 219 | +// Size returns the number of bytes Sum will return. |
| 220 | +func (s *asmState) Size() int { |
| 221 | + return s.outputLen |
| 222 | +} |
| 223 | + |
| 224 | +// BlockSize returns the hash's underlying block size. |
| 225 | +// The Write method must be able to accept any amount |
| 226 | +// of data, but it may operate more efficiently if all writes |
| 227 | +// are a multiple of the block size. |
| 228 | +func (s *asmState) BlockSize() int { |
| 229 | + return s.rate |
| 230 | +} |
| 231 | + |
| 232 | +// Clone returns a copy of the ShakeHash in its current state. |
| 233 | +func (s *asmState) Clone() ShakeHash { |
| 234 | + return s.clone() |
| 235 | +} |
| 236 | + |
| 237 | +// new224Asm returns an assembly implementation of SHA3-224 if available, |
| 238 | +// otherwise it returns nil. |
| 239 | +func new224Asm() hash.Hash { |
| 240 | + if hasAsm { |
| 241 | + return newAsmState(sha3_224) |
| 242 | + } |
| 243 | + return nil |
| 244 | +} |
| 245 | + |
| 246 | +// new256Asm returns an assembly implementation of SHA3-256 if available, |
| 247 | +// otherwise it returns nil. |
| 248 | +func new256Asm() hash.Hash { |
| 249 | + if hasAsm { |
| 250 | + return newAsmState(sha3_256) |
| 251 | + } |
| 252 | + return nil |
| 253 | +} |
| 254 | + |
| 255 | +// new384Asm returns an assembly implementation of SHA3-384 if available, |
| 256 | +// otherwise it returns nil. |
| 257 | +func new384Asm() hash.Hash { |
| 258 | + if hasAsm { |
| 259 | + return newAsmState(sha3_384) |
| 260 | + } |
| 261 | + return nil |
| 262 | +} |
| 263 | + |
| 264 | +// new512Asm returns an assembly implementation of SHA3-512 if available, |
| 265 | +// otherwise it returns nil. |
| 266 | +func new512Asm() hash.Hash { |
| 267 | + if hasAsm { |
| 268 | + return newAsmState(sha3_512) |
| 269 | + } |
| 270 | + return nil |
| 271 | +} |
| 272 | + |
| 273 | +// newShake128Asm returns an assembly implementation of SHAKE-128 if available, |
| 274 | +// otherwise it returns nil. |
| 275 | +func newShake128Asm() ShakeHash { |
| 276 | + if hasAsm { |
| 277 | + return newAsmState(shake_128) |
| 278 | + } |
| 279 | + return nil |
| 280 | +} |
| 281 | + |
| 282 | +// newShake256Asm returns an assembly implementation of SHAKE-256 if available, |
| 283 | +// otherwise it returns nil. |
| 284 | +func newShake256Asm() ShakeHash { |
| 285 | + if hasAsm { |
| 286 | + return newAsmState(shake_256) |
| 287 | + } |
| 288 | + return nil |
| 289 | +} |
0 commit comments