mirror of
https://github.com/golang/go
synced 2024-11-18 11:55:01 -07:00
crypto/ecdsa: implement ecdsa on s390x for P256/P384/P521 using KDSA instruction
This CL revives CL 174437(also IBM CLA) and adds benchmarks and some simplifications. The original commit message is as follows: Utilize KDSA when available. This guarantees constant time operation on all three curves mentioned, and is faster than conventional assembly. Benchmarks: name old time/op new time/op delta SignP256-8 15.2µs ±14% 14.1µs ±18% ~ (p=0.356 n=9+10) SignP384-8 4.28ms ±26% 0.02ms ±30% -99.43% (p=0.000 n=10+10) VerifyP256-8 33.6µs ±13% 13.3µs ±38% -60.32% (p=0.000 n=9+10) name old alloc/op new alloc/op delta SignP256-8 2.16kB ± 0% 1.60kB ± 0% -25.63% (p=0.000 n=9+10) SignP384-8 1.75MB ± 0% 0.00MB ± 0% -99.90% (p=0.000 n=9+10) VerifyP256-8 1.08kB ± 0% 0.18kB ± 0% -83.70% (p=0.000 n=9+10) name old allocs/op new allocs/op delta SignP256-8 29.0 ± 0% 22.0 ± 0% -24.14% (p=0.000 n=10+10) SignP384-8 14.4k ± 0% 0.0k ± 0% -99.85% (p=0.000 n=9+10) VerifyP256-8 23.0 ± 0% 7.0 ± 0% -69.57% (p=0.000 n=10+10) Change-Id: Ifa1fc5917fa7592dd592affa7549147dbc9b4169 Reviewed-on: https://go-review.googlesource.com/c/go/+/228580 Run-TryBot: Michael Munday <mike.munday@ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Munday <mike.munday@ibm.com>
This commit is contained in:
parent
d646c035f9
commit
a637ee1970
@ -220,6 +220,10 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
|
||||
// See [NSA] 3.4.1
|
||||
c := priv.PublicKey.Curve
|
||||
return sign(priv, &csprng, c, hash)
|
||||
}
|
||||
|
||||
func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
|
||||
N := c.Params().N
|
||||
if N.Sign() == 0 {
|
||||
return nil, nil, errZeroParam
|
||||
@ -227,7 +231,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
var k, kInv *big.Int
|
||||
for {
|
||||
for {
|
||||
k, err = randFieldElement(c, csprng)
|
||||
k, err = randFieldElement(c, *csprng)
|
||||
if err != nil {
|
||||
r = nil
|
||||
return
|
||||
@ -281,9 +285,13 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
|
||||
return false
|
||||
}
|
||||
e := hashToInt(hash, c)
|
||||
return verify(pub, c, hash, r, s)
|
||||
}
|
||||
|
||||
func verifyGeneric(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
|
||||
e := hashToInt(hash, c)
|
||||
var w *big.Int
|
||||
N := c.Params().N
|
||||
if in, ok := c.(invertible); ok {
|
||||
w = in.Inverse(s)
|
||||
} else {
|
||||
|
21
src/crypto/ecdsa/ecdsa_noasm.go
Normal file
21
src/crypto/ecdsa/ecdsa_noasm.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
// +build !s390x
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
|
||||
return signGeneric(priv, csprng, c, hash)
|
||||
}
|
||||
|
||||
func verify(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
|
||||
return verifyGeneric(pub, c, hash, r, s)
|
||||
}
|
162
src/crypto/ecdsa/ecdsa_s390x.go
Normal file
162
src/crypto/ecdsa/ecdsa_s390x.go
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2020 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 ecdsa
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/elliptic"
|
||||
"internal/cpu"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// kdsa invokes the "compute digital signature authentication"
|
||||
// instruction with the given function code and 4096 byte
|
||||
// parameter block.
|
||||
//
|
||||
// The return value corresponds to the condition code set by the
|
||||
// instruction. Interrupted invocations are handled by the
|
||||
// function.
|
||||
//go:noescape
|
||||
func kdsa(fc uint64, params *[4096]byte) (errn uint64)
|
||||
|
||||
// canUseKDSA checks if KDSA instruction is available, and if it is, it checks
|
||||
// the name of the curve to see if it matches the curves supported(P-256, P-384, P-521).
|
||||
// Then, based on the curve name, a function code and a block size will be assigned.
|
||||
// If KDSA instruction is not available or if the curve is not supported, canUseKDSA
|
||||
// will set ok to false.
|
||||
func canUseKDSA(c elliptic.Curve) (functionCode uint64, blockSize int, ok bool) {
|
||||
if !cpu.S390X.HasECDSA {
|
||||
return 0, 0, false
|
||||
}
|
||||
switch c.Params().Name {
|
||||
case "P-256":
|
||||
return 1, 32, true
|
||||
case "P-384":
|
||||
return 2, 48, true
|
||||
case "P-521":
|
||||
return 3, 80, true
|
||||
}
|
||||
return 0, 0, false // A mismatch
|
||||
}
|
||||
|
||||
// zeroExtendAndCopy pads src with leading zeros until it has the size given.
|
||||
// It then copies the padded src into the dst. Bytes beyond size in dst are
|
||||
// not modified.
|
||||
func zeroExtendAndCopy(dst, src []byte, size int) {
|
||||
nz := size - len(src)
|
||||
if nz < 0 {
|
||||
panic("src is too long")
|
||||
}
|
||||
// the compiler should replace this loop with a memclr call
|
||||
z := dst[:nz]
|
||||
for i := range z {
|
||||
z[i] = 0
|
||||
}
|
||||
copy(dst[nz:size], src[:size-nz])
|
||||
return
|
||||
}
|
||||
|
||||
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
|
||||
if functionCode, blockSize, ok := canUseKDSA(c); ok {
|
||||
e := hashToInt(hash, c)
|
||||
for {
|
||||
var k *big.Int
|
||||
k, err = randFieldElement(c, *csprng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// The parameter block looks like the following for sign.
|
||||
// +---------------------+
|
||||
// | Signature(R) |
|
||||
// +---------------------+
|
||||
// | Signature(S) |
|
||||
// +---------------------+
|
||||
// | Hashed Message |
|
||||
// +---------------------+
|
||||
// | Private Key |
|
||||
// +---------------------+
|
||||
// | Random Number |
|
||||
// +---------------------+
|
||||
// | |
|
||||
// | ... |
|
||||
// | |
|
||||
// +---------------------+
|
||||
// The common components(signatureR, signatureS, hashedMessage, privateKey and
|
||||
// random number) each takes block size of bytes. The block size is different for
|
||||
// different curves and is set by canUseKDSA function.
|
||||
var params [4096]byte
|
||||
|
||||
startingOffset := 2 * blockSize // Set the starting location for copying
|
||||
// Copy content into the parameter block. In the sign case,
|
||||
// we copy hashed message, private key and random number into
|
||||
// the parameter block. Since those are consecutive components in the parameter
|
||||
// block, we use a for loop here.
|
||||
for i, v := range []*big.Int{e, priv.D, k} {
|
||||
startPosition := startingOffset + i*blockSize
|
||||
endPosition := startPosition + blockSize
|
||||
zeroExtendAndCopy(params[startPosition:endPosition], v.Bytes(), blockSize)
|
||||
}
|
||||
|
||||
// Convert verify function code into a sign function code by adding 8.
|
||||
// We also need to set the 'deterministic' bit in the function code, by
|
||||
// adding 128, in order to stop the instruction using its own random number
|
||||
// generator in addition to the random number we supply.
|
||||
switch kdsa(functionCode+136, ¶ms) {
|
||||
case 0: // success
|
||||
r = new(big.Int)
|
||||
r.SetBytes(params[:blockSize])
|
||||
s = new(big.Int)
|
||||
s.SetBytes(params[blockSize : 2*blockSize])
|
||||
return
|
||||
case 1: // error
|
||||
return nil, nil, errZeroParam
|
||||
case 2: // retry
|
||||
continue
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
return signGeneric(priv, csprng, c, hash)
|
||||
}
|
||||
|
||||
func verify(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
|
||||
if functionCode, blockSize, ok := canUseKDSA(c); ok {
|
||||
e := hashToInt(hash, c)
|
||||
// The parameter block looks like the following for verify:
|
||||
// +---------------------+
|
||||
// | Signature(R) |
|
||||
// +---------------------+
|
||||
// | Signature(S) |
|
||||
// +---------------------+
|
||||
// | Hashed Message |
|
||||
// +---------------------+
|
||||
// | Public Key X |
|
||||
// +---------------------+
|
||||
// | Public Key Y |
|
||||
// +---------------------+
|
||||
// | |
|
||||
// | ... |
|
||||
// | |
|
||||
// +---------------------+
|
||||
// The common components(signatureR, signatureS, hashed message, public key X,
|
||||
// and public key Y) each takes block size of bytes. The block size is different for
|
||||
// different curves and is set by canUseKDSA function.
|
||||
var params [4096]byte
|
||||
|
||||
// Copy content into the parameter block. In the verify case,
|
||||
// we copy signature (r), signature(s), hashed message, public key x component,
|
||||
// and public key y component into the parameter block.
|
||||
// Since those are consecutive components in the parameter block, we use a for loop here.
|
||||
for i, v := range []*big.Int{r, s, e, pub.X, pub.Y} {
|
||||
startPosition := i * blockSize
|
||||
endPosition := startPosition + blockSize
|
||||
zeroExtendAndCopy(params[startPosition:endPosition], v.Bytes(), blockSize)
|
||||
}
|
||||
|
||||
return kdsa(functionCode, ¶ms) == 0
|
||||
}
|
||||
return verifyGeneric(pub, c, hash, r, s)
|
||||
}
|
28
src/crypto/ecdsa/ecdsa_s390x.s
Normal file
28
src/crypto/ecdsa/ecdsa_s390x.s
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func kdsa(fc uint64, params *[4096]byte) (errn uint64)
|
||||
TEXT ·kdsa(SB), NOSPLIT|NOFRAME, $0-24
|
||||
MOVD fc+0(FP), R0 // function code
|
||||
MOVD params+8(FP), R1 // address parameter block
|
||||
|
||||
loop:
|
||||
WORD $0xB93A0008 // compute digital signature authentication
|
||||
BVS loop // branch back if interrupted
|
||||
BGT retry // signing unsuccessful, but retry with new CSPRN
|
||||
BLT error // condition code of 1 indicates a failure
|
||||
|
||||
success:
|
||||
MOVD $0, errn+16(FP) // return 0 - sign/verify was successful
|
||||
RET
|
||||
|
||||
error:
|
||||
MOVD $1, errn+16(FP) // return 1 - sign/verify failed
|
||||
RET
|
||||
|
||||
retry:
|
||||
MOVD $2, errn+16(FP) // return 2 - sign/verify was unsuccessful -- if sign, retry with new RN
|
||||
RET
|
33
src/crypto/ecdsa/ecdsa_s390x_test.go
Normal file
33
src/crypto/ecdsa/ecdsa_s390x_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
// +build s390x
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoAsm(t *testing.T) {
|
||||
curves := [...]elliptic.Curve{
|
||||
elliptic.P256(),
|
||||
elliptic.P384(),
|
||||
elliptic.P521(),
|
||||
}
|
||||
|
||||
for _, curve := range curves {
|
||||
// override the name of the curve to stop the assembly path being taken
|
||||
params := *curve.Params()
|
||||
name := params.Name
|
||||
params.Name = name + "_GENERIC_OVERRIDE"
|
||||
|
||||
testKeyGeneration(t, ¶ms, name)
|
||||
testSignAndVerify(t, ¶ms, name)
|
||||
testNonceSafety(t, ¶ms, name)
|
||||
testINDCCA(t, ¶ms, name)
|
||||
testNegativeInputs(t, ¶ms, name)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user