Skip to content

Commit b22c585

Browse files
FiloSottilegopherbot
authored andcommitted
crypto/ecdsa: move s390x assembly to crypto/internal/fips/ecdsa
For #69536 Change-Id: I85088acb3da788f688f78efff39320bd517e617d Reviewed-on: https://go-review.googlesource.com/c/go/+/628679 Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 03f075b commit b22c585

File tree

10 files changed

+69
-119
lines changed

10 files changed

+69
-119
lines changed

src/crypto/ecdsa/ecdsa.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,6 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
219219
return nil, err
220220
}
221221

222-
if sig, err := signAsm(priv, csprng, hash); err != errNoAsm {
223-
return sig, err
224-
}
225-
226222
switch priv.Curve.Params() {
227223
case elliptic.P224().Params():
228224
return signFIPS(ecdsa.P224(), priv, csprng, hash)
@@ -346,10 +342,6 @@ func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
346342
}
347343
boring.UnreachableExceptTests()
348344

349-
if err := verifyAsm(pub, hash, sig); err != errNoAsm {
350-
return err == nil
351-
}
352-
353345
switch pub.Curve.Params() {
354346
case elliptic.P224().Params():
355347
return verifyFIPS(ecdsa.P224(), pub, hash, sig)

src/crypto/ecdsa/ecdsa_legacy.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@ var one = new(big.Int).SetInt64(1)
171171
// randFieldElement returns a random element of the order of the given
172172
// curve using the procedure given in FIPS 186-4, Appendix B.5.2.
173173
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
174-
// See randomPoint for notes on the algorithm. This has to match, or s390x
175-
// signatures will come out different from other architectures, which will
176-
// break TLS recorded tests.
177174
for {
178175
N := c.Params().N
179176
b := make([]byte, (N.BitLen()+7)/8)

src/crypto/ecdsa/ecdsa_noasm.go

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/crypto/ecdsa/ecdsa_s390x_test.go

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/crypto/ecdsa/ecdsa_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bufio"
99
"compress/bzip2"
1010
"crypto/elliptic"
11+
"crypto/internal/cryptotest"
1112
"crypto/rand"
1213
"crypto/sha1"
1314
"crypto/sha256"
@@ -37,9 +38,11 @@ func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
3738
}
3839
for _, test := range tests {
3940
curve := test.curve
40-
t.Run(test.name, func(t *testing.T) {
41-
t.Parallel()
42-
f(t, curve)
41+
cryptotest.TestAllImplementations(t, "ecdsa", func(t *testing.T) {
42+
t.Run(test.name, func(t *testing.T) {
43+
t.Parallel()
44+
f(t, curve)
45+
})
4346
})
4447
}
4548
}
@@ -184,6 +187,10 @@ func fromHex(s string) *big.Int {
184187
}
185188

186189
func TestVectors(t *testing.T) {
190+
cryptotest.TestAllImplementations(t, "ecdsa", testVectors)
191+
}
192+
193+
func testVectors(t *testing.T) {
187194
// This test runs the full set of NIST test vectors from
188195
// https://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
189196
//

src/crypto/internal/fips/ecdsa/ecdsa.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,10 @@ func Sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []by
256256
if priv.pub.curve != c.curve {
257257
return nil, errors.New("ecdsa: private key does not match curve")
258258
}
259+
return sign(c, priv, csprng, hash)
260+
}
259261

262+
func signGeneric[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
260263
// SEC 1, Version 2.0, Section 4.1.3
261264

262265
k, R, err := randomPoint(c, csprng)
@@ -358,7 +361,10 @@ func Verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature
358361
if pub.curve != c.curve {
359362
return errors.New("ecdsa: public key does not match curve")
360363
}
364+
return verify(c, pub, hash, sig)
365+
}
361366

367+
func verifyGeneric[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
362368
Q, err := c.newPoint().SetBytes(pub.q)
363369
if err != nil {
364370
return err
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2020 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+
//go:build !s390x || purego
6+
7+
package ecdsa
8+
9+
import "io"
10+
11+
func sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
12+
return signGeneric(c, priv, csprng, hash)
13+
}
14+
15+
func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
16+
return verifyGeneric(c, pub, hash, sig)
17+
}

src/crypto/ecdsa/ecdsa_s390x.go renamed to src/crypto/internal/fips/ecdsa/ecdsa_s390x.go

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
package ecdsa
88

99
import (
10-
"crypto/elliptic"
10+
"crypto/internal/fips/bigmod"
11+
"crypto/internal/fipsdeps/cpu"
12+
"crypto/internal/impl"
1113
"errors"
12-
"internal/cpu"
1314
"io"
14-
"math/big"
1515
)
1616

1717
// kdsa invokes the "compute digital signature authentication"
@@ -25,62 +25,47 @@ import (
2525
//go:noescape
2626
func kdsa(fc uint64, params *[4096]byte) (errn uint64)
2727

28-
// testingDisableKDSA forces the generic fallback path. It must only be set in tests.
29-
var testingDisableKDSA bool
28+
var supportsKDSA = cpu.S390XHasECDSA
29+
30+
func init() {
31+
// CP Assist for Cryptographic Functions (CPACF)
32+
// https://www.ibm.com/docs/en/zos/3.1.0?topic=icsf-cp-assist-cryptographic-functions-cpacf
33+
impl.Register("ecdsa", "CPACF", &supportsKDSA)
34+
}
3035

3136
// canUseKDSA checks if KDSA instruction is available, and if it is, it checks
3237
// the name of the curve to see if it matches the curves supported(P-256, P-384, P-521).
3338
// Then, based on the curve name, a function code and a block size will be assigned.
3439
// If KDSA instruction is not available or if the curve is not supported, canUseKDSA
3540
// will set ok to false.
36-
func canUseKDSA(c elliptic.Curve) (functionCode uint64, blockSize int, ok bool) {
37-
if testingDisableKDSA {
38-
return 0, 0, false
39-
}
40-
if !cpu.S390X.HasECDSA {
41+
func canUseKDSA(c curveID) (functionCode uint64, blockSize int, ok bool) {
42+
if !supportsKDSA {
4143
return 0, 0, false
4244
}
43-
switch c.Params().Name {
44-
case "P-256":
45+
switch c {
46+
case p256:
4547
return 1, 32, true
46-
case "P-384":
48+
case p384:
4749
return 2, 48, true
48-
case "P-521":
50+
case p521:
4951
return 3, 80, true
5052
}
5153
return 0, 0, false // A mismatch
5254
}
5355

54-
func hashToBytes(dst, hash []byte, c elliptic.Curve) {
55-
l := len(dst)
56-
if n := c.Params().N.BitLen(); n == l*8 {
57-
// allocation free path for curves with a length that is a whole number of bytes
58-
if len(hash) >= l {
59-
// truncate hash
60-
copy(dst, hash[:l])
61-
return
62-
}
63-
// pad hash with leading zeros
64-
p := l - len(hash)
65-
for i := 0; i < p; i++ {
66-
dst[i] = 0
67-
}
68-
copy(dst[p:], hash)
69-
return
70-
}
71-
// TODO(mundaym): avoid hashToInt call here
72-
hashToInt(hash, c).FillBytes(dst)
56+
func hashToBytes[P Point[P]](c *Curve[P], dst, hash []byte) {
57+
e := bigmod.NewNat()
58+
hashToNat(c, e, hash)
59+
copy(dst, e.Bytes(c.N))
7360
}
7461

75-
func signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
76-
c := priv.Curve
77-
functionCode, blockSize, ok := canUseKDSA(c)
62+
func sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
63+
functionCode, blockSize, ok := canUseKDSA(c.curve)
7864
if !ok {
79-
return nil, errNoAsm
65+
return signGeneric(c, priv, csprng, hash)
8066
}
8167
for {
82-
var k *big.Int
83-
k, err = randFieldElement(c, csprng)
68+
k, _, err := randomPoint(c, csprng)
8469
if err != nil {
8570
return nil, err
8671
}
@@ -109,36 +94,31 @@ func signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err e
10994
// Copy content into the parameter block. In the sign case,
11095
// we copy hashed message, private key and random number into
11196
// the parameter block.
112-
hashToBytes(params[2*blockSize:3*blockSize], hash, c)
113-
priv.D.FillBytes(params[3*blockSize : 4*blockSize])
114-
k.FillBytes(params[4*blockSize : 5*blockSize])
97+
hashToBytes(c, params[2*blockSize:3*blockSize], hash)
98+
copy(params[3*blockSize+blockSize-len(priv.d):], priv.d)
99+
copy(params[4*blockSize:5*blockSize], k.Bytes(c.N))
115100
// Convert verify function code into a sign function code by adding 8.
116101
// We also need to set the 'deterministic' bit in the function code, by
117102
// adding 128, in order to stop the instruction using its own random number
118103
// generator in addition to the random number we supply.
119104
switch kdsa(functionCode+136, &params) {
120105
case 0: // success
121-
return encodeSignature(params[:blockSize], params[blockSize:2*blockSize])
106+
return &Signature{R: params[:blockSize], S: params[blockSize : 2*blockSize]}, nil
122107
case 1: // error
123-
return nil, errZeroParam
108+
return nil, errors.New("zero parameter")
124109
case 2: // retry
125110
continue
126111
}
127-
panic("unreachable")
128112
}
129113
}
130114

131-
func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error {
132-
c := pub.Curve
133-
functionCode, blockSize, ok := canUseKDSA(c)
115+
func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
116+
functionCode, blockSize, ok := canUseKDSA(c.curve)
134117
if !ok {
135-
return errNoAsm
118+
return verifyGeneric(c, pub, hash, sig)
136119
}
137120

138-
r, s, err := parseSignature(sig)
139-
if err != nil {
140-
return err
141-
}
121+
r, s := sig.R, sig.S
142122
if len(r) > blockSize || len(s) > blockSize {
143123
return errors.New("invalid signature")
144124
}
@@ -169,9 +149,8 @@ func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error {
169149
// and public key y component into the parameter block.
170150
copy(params[0*blockSize+blockSize-len(r):], r)
171151
copy(params[1*blockSize+blockSize-len(s):], s)
172-
hashToBytes(params[2*blockSize:3*blockSize], hash, c)
173-
pub.X.FillBytes(params[3*blockSize : 4*blockSize])
174-
pub.Y.FillBytes(params[4*blockSize : 5*blockSize])
152+
hashToBytes(c, params[2*blockSize:3*blockSize], hash)
153+
copy(params[3*blockSize:5*blockSize], pub.q[1:]) // strip 0x04 prefix
175154
if kdsa(functionCode, &params) != 0 {
176155
return errors.New("invalid signature")
177156
}

src/crypto/internal/fipsdeps/cpu/cpu.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var S390XHasAES = cpu.S390X.HasAES
2323
var S390XHasAESCBC = cpu.S390X.HasAESCBC
2424
var S390XHasAESCTR = cpu.S390X.HasAESCTR
2525
var S390XHasAESGCM = cpu.S390X.HasAESGCM
26+
var S390XHasECDSA = cpu.S390X.HasECDSA
2627
var S390XHasGHASH = cpu.S390X.HasGHASH
2728
var S390XHasSHA256 = cpu.S390X.HasSHA256
2829
var S390XHasSHA3 = cpu.S390X.HasSHA3

0 commit comments

Comments
 (0)