From 54a112d7197ec320527614e7502a3243eab93b6e Mon Sep 17 00:00:00 2001 From: Ruixin Bao Date: Tue, 29 Sep 2020 15:55:19 -0400 Subject: [PATCH] crypto/ecdsa: use FillBytes on s390x MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Originally, zeroExtendAndCopy is used to pad src with leading zeros and copy the padded src into the destination. It is no longer needed after CL 230397 introduced FillBytes. We can simply use that and remove the zeroExtendAndCopy function. It is cleaner and reduces some allocation. In addition, this patch tries to avoid calling hashToInt function in both Sign and Verify function so some allocation is reduced. Benchmarks: name old alloc/op new alloc/op delta SignP256-8 1.60kB ± 0% 1.49kB ± 0% -7.23% (p=0.000 n=20+20) SignP384-8 1.74kB ± 0% 1.59kB ± 0% -8.50% (p=0.000 n=20+18) VerifyP256-8 176B ± 0% 0B -100.00% (p=0.000 n=20+20) KeyGeneration-8 640B ± 0% 640B ± 0% ~ (all equal) name old allocs/op new allocs/op delta SignP256-8 22.0 ± 0% 17.0 ± 0% -22.73% (p=0.000 n=20+20) SignP384-8 22.0 ± 0% 17.0 ± 0% -22.73% (p=0.000 n=20+20) VerifyP256-8 7.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) KeyGeneration-8 13.0 ± 0% 13.0 ± 0% ~ (all equal) Change-Id: Ic4c95191eded55deb3420d97db501689f3b173c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/232297 Reviewed-by: Michael Munday Run-TryBot: Michael Munday TryBot-Result: Go Bot Trust: Filippo Valsorda --- src/crypto/ecdsa/ecdsa_s390x.go | 59 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/crypto/ecdsa/ecdsa_s390x.go b/src/crypto/ecdsa/ecdsa_s390x.go index d8d2c716db..0a1d73e7a4 100644 --- a/src/crypto/ecdsa/ecdsa_s390x.go +++ b/src/crypto/ecdsa/ecdsa_s390x.go @@ -41,26 +41,29 @@ func canUseKDSA(c elliptic.Curve) (functionCode uint64, blockSize int, ok bool) 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 +func hashToBytes(dst, hash []byte, c elliptic.Curve) { + l := len(dst) + if n := c.Params().N.BitLen(); n == l*8 { + // allocation free path for curves with a length that is a whole number of bytes + if len(hash) >= l { + // truncate hash + copy(dst, hash[:l]) + return + } + // pad hash with leading zeros + p := l - len(hash) + for i := 0; i < p; i++ { + dst[i] = 0 + } + copy(dst[p:], hash) + return } - copy(dst[nz:size], src[:size-nz]) - return + // TODO(mundaym): avoid hashToInt call here + hashToInt(hash, c).FillBytes(dst) } 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) @@ -89,17 +92,12 @@ func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash // 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) - } - + // the parameter block. + hashToBytes(params[2*blockSize:3*blockSize], hash, c) + priv.D.FillBytes(params[3*blockSize : 4*blockSize]) + k.FillBytes(params[4*blockSize : 5*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 @@ -124,7 +122,6 @@ func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, 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) | @@ -149,13 +146,11 @@ func verify(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool { // 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) - } - + r.FillBytes(params[0*blockSize : 1*blockSize]) + s.FillBytes(params[1*blockSize : 2*blockSize]) + hashToBytes(params[2*blockSize:3*blockSize], hash, c) + pub.X.FillBytes(params[3*blockSize : 4*blockSize]) + pub.Y.FillBytes(params[4*blockSize : 5*blockSize]) return kdsa(functionCode, ¶ms) == 0 } return verifyGeneric(pub, c, hash, r, s) -- 2.48.1