Skip to content

Commit

Permalink
crypto/ecdsa: move s390x assembly to crypto/internal/fips/ecdsa
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
FiloSottile authored and gopherbot committed Nov 19, 2024
1 parent 03f075b commit b22c585
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 119 deletions.
8 changes: 0 additions & 8 deletions src/crypto/ecdsa/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,6 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
return nil, err
}

if sig, err := signAsm(priv, csprng, hash); err != errNoAsm {
return sig, err
}

switch priv.Curve.Params() {
case elliptic.P224().Params():
return signFIPS(ecdsa.P224(), priv, csprng, hash)
Expand Down Expand Up @@ -346,10 +342,6 @@ func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
}
boring.UnreachableExceptTests()

if err := verifyAsm(pub, hash, sig); err != errNoAsm {
return err == nil
}

switch pub.Curve.Params() {
case elliptic.P224().Params():
return verifyFIPS(ecdsa.P224(), pub, hash, sig)
Expand Down
3 changes: 0 additions & 3 deletions src/crypto/ecdsa/ecdsa_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,6 @@ var one = new(big.Int).SetInt64(1)
// randFieldElement returns a random element of the order of the given
// curve using the procedure given in FIPS 186-4, Appendix B.5.2.
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
// See randomPoint for notes on the algorithm. This has to match, or s390x
// signatures will come out different from other architectures, which will
// break TLS recorded tests.
for {
N := c.Params().N
b := make([]byte, (N.BitLen()+7)/8)
Expand Down
17 changes: 0 additions & 17 deletions src/crypto/ecdsa/ecdsa_noasm.go

This file was deleted.

32 changes: 0 additions & 32 deletions src/crypto/ecdsa/ecdsa_s390x_test.go

This file was deleted.

13 changes: 10 additions & 3 deletions src/crypto/ecdsa/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"bufio"
"compress/bzip2"
"crypto/elliptic"
"crypto/internal/cryptotest"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
Expand Down Expand Up @@ -37,9 +38,11 @@ func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
}
for _, test := range tests {
curve := test.curve
t.Run(test.name, func(t *testing.T) {
t.Parallel()
f(t, curve)
cryptotest.TestAllImplementations(t, "ecdsa", func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
f(t, curve)
})
})
}
}
Expand Down Expand Up @@ -184,6 +187,10 @@ func fromHex(s string) *big.Int {
}

func TestVectors(t *testing.T) {
cryptotest.TestAllImplementations(t, "ecdsa", testVectors)
}

func testVectors(t *testing.T) {
// This test runs the full set of NIST test vectors from
// https://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
//
Expand Down
6 changes: 6 additions & 0 deletions src/crypto/internal/fips/ecdsa/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,10 @@ func Sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []by
if priv.pub.curve != c.curve {
return nil, errors.New("ecdsa: private key does not match curve")
}
return sign(c, priv, csprng, hash)
}

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

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

func verifyGeneric[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
Q, err := c.newPoint().SetBytes(pub.q)
if err != nil {
return err
Expand Down
17 changes: 17 additions & 0 deletions src/crypto/internal/fips/ecdsa/ecdsa_noasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.

//go:build !s390x || purego

package ecdsa

import "io"

func sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
return signGeneric(c, priv, csprng, hash)
}

func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
return verifyGeneric(c, pub, hash, sig)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
package ecdsa

import (
"crypto/elliptic"
"crypto/internal/fips/bigmod"
"crypto/internal/fipsdeps/cpu"
"crypto/internal/impl"
"errors"
"internal/cpu"
"io"
"math/big"
)

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

// testingDisableKDSA forces the generic fallback path. It must only be set in tests.
var testingDisableKDSA bool
var supportsKDSA = cpu.S390XHasECDSA

func init() {
// CP Assist for Cryptographic Functions (CPACF)
// https://www.ibm.com/docs/en/zos/3.1.0?topic=icsf-cp-assist-cryptographic-functions-cpacf
impl.Register("ecdsa", "CPACF", &supportsKDSA)
}

// 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 testingDisableKDSA {
return 0, 0, false
}
if !cpu.S390X.HasECDSA {
func canUseKDSA(c curveID) (functionCode uint64, blockSize int, ok bool) {
if !supportsKDSA {
return 0, 0, false
}
switch c.Params().Name {
case "P-256":
switch c {
case p256:
return 1, 32, true
case "P-384":
case p384:
return 2, 48, true
case "P-521":
case p521:
return 3, 80, true
}
return 0, 0, false // A mismatch
}

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
}
// TODO(mundaym): avoid hashToInt call here
hashToInt(hash, c).FillBytes(dst)
func hashToBytes[P Point[P]](c *Curve[P], dst, hash []byte) {
e := bigmod.NewNat()
hashToNat(c, e, hash)
copy(dst, e.Bytes(c.N))
}

func signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
c := priv.Curve
functionCode, blockSize, ok := canUseKDSA(c)
func sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
functionCode, blockSize, ok := canUseKDSA(c.curve)
if !ok {
return nil, errNoAsm
return signGeneric(c, priv, csprng, hash)
}
for {
var k *big.Int
k, err = randFieldElement(c, csprng)
k, _, err := randomPoint(c, csprng)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -109,36 +94,31 @@ func signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err e
// Copy content into the parameter block. In the sign case,
// we copy hashed message, private key and random number into
// 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])
hashToBytes(c, params[2*blockSize:3*blockSize], hash)
copy(params[3*blockSize+blockSize-len(priv.d):], priv.d)
copy(params[4*blockSize:5*blockSize], k.Bytes(c.N))
// 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, &params) {
case 0: // success
return encodeSignature(params[:blockSize], params[blockSize:2*blockSize])
return &Signature{R: params[:blockSize], S: params[blockSize : 2*blockSize]}, nil
case 1: // error
return nil, errZeroParam
return nil, errors.New("zero parameter")
case 2: // retry
continue
}
panic("unreachable")
}
}

func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error {
c := pub.Curve
functionCode, blockSize, ok := canUseKDSA(c)
func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
functionCode, blockSize, ok := canUseKDSA(c.curve)
if !ok {
return errNoAsm
return verifyGeneric(c, pub, hash, sig)
}

r, s, err := parseSignature(sig)
if err != nil {
return err
}
r, s := sig.R, sig.S
if len(r) > blockSize || len(s) > blockSize {
return errors.New("invalid signature")
}
Expand Down Expand Up @@ -169,9 +149,8 @@ func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error {
// and public key y component into the parameter block.
copy(params[0*blockSize+blockSize-len(r):], r)
copy(params[1*blockSize+blockSize-len(s):], s)
hashToBytes(params[2*blockSize:3*blockSize], hash, c)
pub.X.FillBytes(params[3*blockSize : 4*blockSize])
pub.Y.FillBytes(params[4*blockSize : 5*blockSize])
hashToBytes(c, params[2*blockSize:3*blockSize], hash)
copy(params[3*blockSize:5*blockSize], pub.q[1:]) // strip 0x04 prefix
if kdsa(functionCode, &params) != 0 {
return errors.New("invalid signature")
}
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions src/crypto/internal/fipsdeps/cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var S390XHasAES = cpu.S390X.HasAES
var S390XHasAESCBC = cpu.S390X.HasAESCBC
var S390XHasAESCTR = cpu.S390X.HasAESCTR
var S390XHasAESGCM = cpu.S390X.HasAESGCM
var S390XHasECDSA = cpu.S390X.HasECDSA
var S390XHasGHASH = cpu.S390X.HasGHASH
var S390XHasSHA256 = cpu.S390X.HasSHA256
var S390XHasSHA3 = cpu.S390X.HasSHA3
Expand Down

0 comments on commit b22c585

Please sign in to comment.