Skip to content

Commit dccd99e

Browse files
committed
sha3: add optimized implementation for s390x
Message-security-assist extension 6 adds support for the SHA-3 and SHAKE algorithms. This CL allows the sha3 package to use these new features. name old speed new speed delta PermutationFunction 328MB/s ± 0% 385MB/s ± 0% +17.28% (p=0.000 n=9+10) Sha3_512_MTU 108MB/s ± 0% 2011MB/s ± 0% +1768.56% (p=0.000 n=10+10) Sha3_384_MTU 149MB/s ± 0% 2437MB/s ± 0% +1534.22% (p=0.000 n=10+10) Sha3_256_MTU 185MB/s ± 0% 2739MB/s ± 0% +1379.93% (p=0.000 n=10+10) Sha3_224_MTU 195MB/s ± 0% 2782MB/s ± 0% +1326.05% (p=0.000 n=10+10) Shake128_MTU 225MB/s ± 0% 4436MB/s ± 0% +1873.18% (p=0.000 n=9+9) Shake256_MTU 209MB/s ± 0% 4521MB/s ± 0% +2059.86% (p=0.000 n=8+10) Shake256_16x 188MB/s ± 0% 1366MB/s ± 0% +624.70% (p=0.000 n=9+10) Shake256_1MiB 212MB/s ± 0% 5861MB/s ± 0% +2666.67% (p=0.000 n=10+10) Sha3_512_1MiB 116MB/s ± 0% 4328MB/s ± 0% +3628.33% (p=0.000 n=10+10) Change-Id: I8ebc503ca2b9eda2ebb361dffdbfe79dd97e1975 Reviewed-on: https://go-review.googlesource.com/59391 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1 parent 719079d commit dccd99e

File tree

6 files changed

+420
-6
lines changed

6 files changed

+420
-6
lines changed

sha3/hashes.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,42 @@ import (
1515
// New224 creates a new SHA3-224 hash.
1616
// Its generic security strength is 224 bits against preimage attacks,
1717
// and 112 bits against collision attacks.
18-
func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} }
18+
func New224() hash.Hash {
19+
if h := new224Asm(); h != nil {
20+
return h
21+
}
22+
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
23+
}
1924

2025
// New256 creates a new SHA3-256 hash.
2126
// Its generic security strength is 256 bits against preimage attacks,
2227
// and 128 bits against collision attacks.
23-
func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} }
28+
func New256() hash.Hash {
29+
if h := new256Asm(); h != nil {
30+
return h
31+
}
32+
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
33+
}
2434

2535
// New384 creates a new SHA3-384 hash.
2636
// Its generic security strength is 384 bits against preimage attacks,
2737
// and 192 bits against collision attacks.
28-
func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} }
38+
func New384() hash.Hash {
39+
if h := new384Asm(); h != nil {
40+
return h
41+
}
42+
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
43+
}
2944

3045
// New512 creates a new SHA3-512 hash.
3146
// Its generic security strength is 512 bits against preimage attacks,
3247
// and 256 bits against collision attacks.
33-
func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} }
48+
func New512() hash.Hash {
49+
if h := new512Asm(); h != nil {
50+
return h
51+
}
52+
return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
53+
}
3454

3555
// Sum224 returns the SHA3-224 digest of the data.
3656
func Sum224(data []byte) (digest [28]byte) {

sha3/hashes_generic.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 !s390x
6+
7+
package sha3
8+
9+
import (
10+
"hash"
11+
)
12+
13+
// new224Asm returns an assembly implementation of SHA3-224 if available,
14+
// otherwise it returns nil.
15+
func new224Asm() hash.Hash { return nil }
16+
17+
// new256Asm returns an assembly implementation of SHA3-256 if available,
18+
// otherwise it returns nil.
19+
func new256Asm() hash.Hash { return nil }
20+
21+
// new384Asm returns an assembly implementation of SHA3-384 if available,
22+
// otherwise it returns nil.
23+
func new384Asm() hash.Hash { return nil }
24+
25+
// new512Asm returns an assembly implementation of SHA3-512 if available,
26+
// otherwise it returns nil.
27+
func new512Asm() hash.Hash { return nil }

sha3/sha3_s390x.go

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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

Comments
 (0)