crypto/tls: support crypto.MessageSigner private keys

Fixes #75656

Change-Id: I6bc71c80973765ef995d17b1450ea2026a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/724820
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicholas Husin <nsh@golang.org>
This commit is contained in:
yuhan6665
2026-09-08 21:33:46 -04:00
parent 2d3ccda21d
commit d1b0d4d0cd
8 changed files with 141 additions and 117 deletions
+41 -17
View File
@@ -18,9 +18,13 @@ import (
"slices"
)
// verifyHandshakeSignature verifies a signature against pre-hashed
// (if required) handshake contents.
// verifyHandshakeSignature verifies a signature against unhashed handshake contents.
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
if hashFunc != directSigning {
h := hashFunc.New()
h.Write(signed)
signed = h.Sum(nil)
}
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
@@ -61,6 +65,32 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c
return nil
}
// verifyLegacyHandshakeSignature verifies a TLS 1.0 and 1.1 signature against
// pre-hashed handshake contents.
func verifyLegacyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, hashed, sig []byte) error {
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
}
if !ecdsa.VerifyASN1(pubKey, hashed, sig) {
return errors.New("ECDSA verification failure")
}
case signaturePKCS1v15:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
}
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, hashed, sig); err != nil {
return err
}
default:
return errors.New("internal error: unknown signature type")
}
return nil
}
const (
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
@@ -77,21 +107,15 @@ var signaturePadding = []byte{
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
}
// signedMessage returns the pre-hashed (if necessary) message to be signed by
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
if sigHash == directSigning {
b := &bytes.Buffer{}
b.Write(signaturePadding)
io.WriteString(b, context)
b.Write(transcript.Sum(nil))
return b.Bytes()
}
h := sigHash.New()
h.Write(signaturePadding)
io.WriteString(h, context)
h.Write(transcript.Sum(nil))
return h.Sum(nil)
// signedMessage returns the (unhashed) message to be signed by certificate keys
// in TLS 1.3. See RFC 8446, Section 4.4.3.
func signedMessage(context string, transcript hash.Hash) []byte {
const maxSize = 64 /* signaturePadding */ + len(serverSignatureContext) + 512/8 /* SHA-512 */
b := bytes.NewBuffer(make([]byte, 0, maxSize))
b.Write(signaturePadding)
io.WriteString(b, context)
b.Write(transcript.Sum(nil))
return b.Bytes()
}
// typeAndHashFromSignatureScheme returns the corresponding signature type and
+6 -1
View File
@@ -1631,9 +1631,14 @@ var writerMutex sync.Mutex
type Certificate struct {
Certificate [][]byte
// PrivateKey contains the private key corresponding to the public key in
// Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey.
// Leaf. This must implement [crypto.Signer] with an RSA, ECDSA or Ed25519
// PublicKey.
//
// For a server up to TLS 1.2, it can also implement crypto.Decrypter with
// an RSA PublicKey.
//
// If it implements [crypto.MessageSigner], SignMessage will be used instead
// of Sign for TLS 1.2 and later.
PrivateKey crypto.PrivateKey
// SupportedSignatureAlgorithms is an optional list restricting what
// signature algorithms the PrivateKey can be used for.
+21 -15
View File
@@ -774,37 +774,43 @@ func (hs *clientHandshakeState) doFullHandshake() error {
return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
}
var sigType uint8
var sigHash crypto.Hash
if c.vers >= VersionTLS12 {
signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
sigType, sigHash, err := typeAndHashFromSignatureScheme(signatureAlgorithm)
if err != nil {
return c.sendAlert(alertInternalError)
}
certVerify.hasSignatureAlgorithm = true
certVerify.signatureAlgorithm = signatureAlgorithm
if hs.finishedHash.buffer == nil {
c.sendAlert(alertInternalError)
return errors.New("tls: internal error: did not keep handshake transcript for TLS 1.2")
}
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
certVerify.signature, err = crypto.SignMessage(key, c.config.rand(), hs.finishedHash.buffer, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
sigType, sigHash, err := legacyTypeAndHashFromPublicKey(key.Public())
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
}
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return err
signed := hs.finishedHash.hashForClientCertificate(sigType)
certVerify.signature, err = key.Sign(c.config.rand(), signed, sigHash)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
}
if _, err := hs.c.writeHandshakeRecord(certVerify, &hs.finishedHash); err != nil {
+3 -3
View File
@@ -664,7 +664,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
signed := signedMessage(serverSignatureContext, hs.transcript)
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
@@ -783,12 +783,12 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
signed := signedMessage(clientSignatureContext, hs.transcript)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
sig, err := crypto.SignMessage(cert.PrivateKey.(crypto.Signer), c.config.rand(), signed, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to sign handshake: " + err.Error())
+13 -5
View File
@@ -763,19 +763,27 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if err != nil {
return c.sendAlert(alertInternalError)
}
if hs.finishedHash.buffer == nil {
c.sendAlert(alertInternalError)
return errors.New("tls: internal error: did not keep handshake transcript for TLS 1.2")
}
if err := verifyHandshakeSignature(sigType, pub, sigHash, hs.finishedHash.buffer, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
signed := hs.finishedHash.hashForClientCertificate(sigType)
if err := verifyLegacyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
}
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
c.peerSigAlg = certVerify.signatureAlgorithm
if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
+3 -3
View File
@@ -948,12 +948,12 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
signed := signedMessage(serverSignatureContext, hs.transcript)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
sig, err := crypto.SignMessage(hs.cert.PrivateKey.(crypto.Signer), c.config.rand(), signed, signOpts)
if err != nil {
public := hs.cert.PrivateKey.(crypto.Signer).Public()
if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
@@ -1184,7 +1184,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
signed := signedMessage(clientSignatureContext, hs.transcript)
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
+51 -57
View File
@@ -127,25 +127,8 @@ func md5SHA1Hash(slices [][]byte) []byte {
}
// hashForServerKeyExchange hashes the given slices and returns their digest
// using the given hash function (for TLS 1.2) or using a default based on
// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
// do pre-hashing, it returns the concatenation of the slices.
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
if sigType == signatureEd25519 {
var signed []byte
for _, slice := range slices {
signed = append(signed, slice...)
}
return signed
}
if version >= VersionTLS12 {
h := hashFunc.New()
for _, slice := range slices {
h.Write(slice)
}
digest := h.Sum(nil)
return digest
}
// using a hash based on the sigType. It can only be used for TLS 1.0 and 1.1.
func hashForServerKeyExchange(sigType uint8, slices ...[]byte) []byte {
if sigType == signatureECDSA {
return sha1Hash(slices)
}
@@ -207,36 +190,41 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
}
var sigType uint8
var sigHash crypto.Hash
var sig []byte
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
if err != nil {
return nil, err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
sigType, sigHash, err := typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
if err != nil {
return nil, err
}
signed := slices.Concat(clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err = crypto.SignMessage(priv, config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
sigType, sigHash, err := legacyTypeAndHashFromPublicKey(priv.Public())
if err != nil {
return nil, err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := priv.Sign(config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
signed := hashForServerKeyExchange(sigType, clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
sig, err = priv.Sign(config.rand(), signed, sigHash)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
}
skx := new(serverKeyExchangeMsg)
@@ -296,6 +284,18 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if len(sig) < 2 {
return errServerKeyExchange
}
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {
return errServerKeyExchange
}
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
if !slices.Contains(clientHello.supportedCurves, ka.curveID) {
return errors.New("tls: server selected unoffered curve")
@@ -329,12 +329,6 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
var sigType uint8
var sigHash crypto.Hash
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {
return errServerKeyExchange
}
if !isSupportedSignatureAlgorithm(ka.signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
return errors.New("tls: certificate used with invalid signature algorithm")
}
@@ -342,25 +336,25 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if err != nil {
return err
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return errServerKeyExchange
}
signed := slices.Concat(clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
if err != nil {
return err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return errServerKeyExchange
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
if (sigType == signaturePKCS1v15) != ka.isRSA {
return errServerKeyExchange
}
signed := hashForServerKeyExchange(sigType, clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyLegacyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
}
return nil
}
+3 -16
View File
@@ -222,22 +222,9 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength)
}
// hashForClientCertificate returns the handshake messages so far, pre-hashed if
// necessary, suitable for signing by a TLS client certificate.
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash) []byte {
if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
}
if sigType == signatureEd25519 {
return h.buffer
}
if h.version >= VersionTLS12 {
hash := hashAlg.New()
hash.Write(h.buffer)
return hash.Sum(nil)
}
// hashForClientCertificate returns the handshake messages so far, pre-hashed,
// suitable for signing by a TLS 1.0 and 1.1 client certificate.
func (h finishedHash) hashForClientCertificate(sigType uint8) []byte {
if sigType == signatureECDSA {
return h.server.Sum(nil)