// 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/elliptic" "errors" "internal/cpu" "io" "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) // testingDisableKDSA forces the generic fallback path. It must only be set in tests. var testingDisableKDSA bool // 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 { 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 } 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 signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) { c := priv.Curve functionCode, blockSize, ok := canUseKDSA(c) if !ok { return nil, errNoAsm } for { var k *big.Int k, err = randFieldElement(c, csprng) if err != nil { return 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 // 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]) // 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 return encodeSignature(params[:blockSize], params[blockSize:2*blockSize]) case 1: // error return nil, errZeroParam case 2: // retry continue } panic("unreachable") } } func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error { c := pub.Curve functionCode, blockSize, ok := canUseKDSA(c) if !ok { return errNoAsm } r, s, err := parseSignature(sig) if err != nil { return err } if len(r) > blockSize || len(s) > blockSize { return errors.New("invalid signature") } // 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. 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]) if kdsa(functionCode, ¶ms) != 0 { return errors.New("invalid signature") } return nil }