crypto/ecdsa: fix s390x assembly with P-521

I had incorrectly assumed that the blocksize was always the same as the
curve field size. This is true of P-256 and P-384, but not P-521.

Fixes #70660
Fixes #70771

Change-Id: Idb6b510fcd3dd42d9b1e6cf42c1bb92e0ce8bd07
Reviewed-on: https://go-review.googlesource.com/c/go/+/636015
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
Filippo Valsorda 2024-12-13 16:59:20 +01:00 committed by Gopher Robot
parent 08725f9de2
commit c4f356dd86
2 changed files with 44 additions and 13 deletions

View file

@ -21,7 +21,7 @@ import (
type PrivateKey struct { type PrivateKey struct {
pub PublicKey pub PublicKey
d []byte // bigmod.(*Nat).Bytes output (fixed length) d []byte // bigmod.(*Nat).Bytes output (same length as the curve order)
} }
func (priv *PrivateKey) Bytes() []byte { func (priv *PrivateKey) Bytes() []byte {
@ -262,7 +262,7 @@ func randomPoint[P Point[P]](c *Curve[P], generate func([]byte) error) (k *bigmo
var testingOnlyRejectionSamplingLooped func() var testingOnlyRejectionSamplingLooped func()
// Signature is an ECDSA signature, where r and s are represented as big-endian // Signature is an ECDSA signature, where r and s are represented as big-endian
// fixed-length byte slices. // byte slices of the same length as the curve order.
type Signature struct { type Signature struct {
R, S []byte R, S []byte
} }

View file

@ -47,15 +47,34 @@ func canUseKDSA(c curveID) (functionCode uint64, blockSize int, ok bool) {
case p384: case p384:
return 2, 48, true return 2, 48, true
case p521: case p521:
// Note that the block size doesn't match the field size for P-521.
return 3, 80, true return 3, 80, true
} }
return 0, 0, false // A mismatch return 0, 0, false // A mismatch
} }
func hashToBytes[P Point[P]](c *Curve[P], dst, hash []byte) { func hashToBytes[P Point[P]](c *Curve[P], hash []byte) []byte {
e := bigmod.NewNat() e := bigmod.NewNat()
hashToNat(c, e, hash) hashToNat(c, e, hash)
copy(dst, e.Bytes(c.N)) return e.Bytes(c.N)
}
func appendBlock(p []byte, blocksize int, b []byte) []byte {
if len(b) > blocksize {
panic("ecdsa: internal error: appendBlock input larger than block")
}
padding := blocksize - len(b)
p = append(p, make([]byte, padding)...)
return append(p, b...)
}
func trimBlock(p []byte, size int) ([]byte, error) {
for _, b := range p[:len(p)-size] {
if b != 0 {
return nil, errors.New("ecdsa: internal error: KDSA produced invalid signature")
}
}
return p[len(p)-size:], nil
} }
func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte) (*Signature, error) { func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte) (*Signature, error) {
@ -95,17 +114,27 @@ func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte
// Copy content into the parameter block. In the sign case, // Copy content into the parameter block. In the sign case,
// we copy hashed message, private key and random number into // we copy hashed message, private key and random number into
// the parameter block. // the parameter block. We skip the signature slots.
hashToBytes(c, params[2*blockSize:3*blockSize], hash) p := params[:2*blockSize]
copy(params[3*blockSize+blockSize-len(priv.d):], priv.d) p = appendBlock(p, blockSize, hashToBytes(c, hash))
copy(params[4*blockSize:5*blockSize], k.Bytes(c.N)) p = appendBlock(p, blockSize, priv.d)
p = appendBlock(p, blockSize, k.Bytes(c.N))
// Convert verify function code into a sign function code by adding 8. // 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 // 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 // adding 128, in order to stop the instruction using its own random number
// generator in addition to the random number we supply. // generator in addition to the random number we supply.
switch kdsa(functionCode+136, &params) { switch kdsa(functionCode+136, &params) {
case 0: // success case 0: // success
return &Signature{R: params[:blockSize], S: params[blockSize : 2*blockSize]}, nil elementSize := (c.N.BitLen() + 7) / 8
r, err := trimBlock(params[:blockSize], elementSize)
if err != nil {
return nil, err
}
s, err := trimBlock(params[blockSize:2*blockSize], elementSize)
if err != nil {
return nil, err
}
return &Signature{R: r, S: s}, nil
case 1: // error case 1: // error
return nil, errors.New("zero parameter") return nil, errors.New("zero parameter")
case 2: // retry case 2: // retry
@ -149,10 +178,12 @@ func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature
// Copy content into the parameter block. In the verify case, // Copy content into the parameter block. In the verify case,
// we copy signature (r), signature(s), hashed message, public key x component, // we copy signature (r), signature(s), hashed message, public key x component,
// and public key y component into the parameter block. // and public key y component into the parameter block.
copy(params[0*blockSize+blockSize-len(r):], r) p := params[:0]
copy(params[1*blockSize+blockSize-len(s):], s) p = appendBlock(p, blockSize, r)
hashToBytes(c, params[2*blockSize:3*blockSize], hash) p = appendBlock(p, blockSize, s)
copy(params[3*blockSize:5*blockSize], pub.q[1:]) // strip 0x04 prefix p = appendBlock(p, blockSize, hashToBytes(c, hash))
p = appendBlock(p, blockSize, pub.q[1:1+len(pub.q)/2])
p = appendBlock(p, blockSize, pub.q[1+len(pub.q)/2:])
if kdsa(functionCode, &params) != 0 { if kdsa(functionCode, &params) != 0 {
return errors.New("invalid signature") return errors.New("invalid signature")
} }