--- /dev/null
+If [Certificate.PrivateKey] implements [crypto.MessageSigner], its SignMessage
+method is used instead of Sign in TLS 1.2 and later.
"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)
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"
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
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.
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)
}
tlssha1.Value() // ensure godebug is initialized
tlssha1.IncNonDefault()
}
+ 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 {
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)
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())
tlssha1.Value() // ensure godebug is initialized
tlssha1.IncNonDefault()
}
+ 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 {
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 &&
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)
}
// 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)
}
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
}
tlssha1.Value() // ensure godebug is initialized
tlssha1.IncNonDefault()
}
+ 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)
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")
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")
}
tlssha1.Value() // ensure godebug is initialized
tlssha1.IncNonDefault()
}
+ 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) != 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())
+ }
}
- 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())
- }
return nil
}
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)
}
check()
}
+
+func TestMessageSigner(t *testing.T) {
+ t.Run("TLSv10", func(t *testing.T) { testMessageSigner(t, VersionTLS10) })
+ t.Run("TLSv12", func(t *testing.T) { testMessageSigner(t, VersionTLS12) })
+ t.Run("TLSv13", func(t *testing.T) { testMessageSigner(t, VersionTLS13) })
+}
+
+func testMessageSigner(t *testing.T, version uint16) {
+ clientConfig, serverConfig := testConfig.Clone(), testConfig.Clone()
+ serverConfig.ClientAuth = RequireAnyClientCert
+ clientConfig.MinVersion = version
+ clientConfig.MaxVersion = version
+ serverConfig.MinVersion = version
+ serverConfig.MaxVersion = version
+ clientConfig.Certificates = []Certificate{{
+ Certificate: [][]byte{testRSACertificate},
+ PrivateKey: messageOnlySigner{testRSAPrivateKey},
+ }}
+ serverConfig.Certificates = []Certificate{{
+ Certificate: [][]byte{testRSACertificate},
+ PrivateKey: messageOnlySigner{testRSAPrivateKey},
+ }}
+
+ _, _, err := testHandshake(t, clientConfig, serverConfig)
+ if version < VersionTLS12 {
+ if err == nil {
+ t.Fatal("expected failure for TLS 1.0/1.1")
+ }
+ } else {
+ if err != nil {
+ t.Fatalf("unexpected failure: %s", err)
+ }
+ }
+
+ clientConfig.Certificates = []Certificate{{
+ Certificate: [][]byte{testECDSACertificate},
+ PrivateKey: messageOnlySigner{testECDSAPrivateKey},
+ }}
+ serverConfig.Certificates = []Certificate{{
+ Certificate: [][]byte{testECDSACertificate},
+ PrivateKey: messageOnlySigner{testECDSAPrivateKey},
+ }}
+
+ _, _, err = testHandshake(t, clientConfig, serverConfig)
+ if version < VersionTLS12 {
+ if err == nil {
+ t.Fatal("expected failure for TLS 1.0/1.1")
+ }
+ } else {
+ if err != nil {
+ t.Fatalf("unexpected failure: %s", err)
+ }
+ }
+}
+
+type messageOnlySigner struct{ crypto.Signer }
+
+func (s messageOnlySigner) Public() crypto.PublicKey {
+ return s.Signer.Public()
+}
+
+func (s messageOnlySigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+ return nil, errors.New("messageOnlySigner: Sign called")
+}
+
+func (s messageOnlySigner) SignMessage(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+ h := opts.HashFunc().New()
+ h.Write(msg)
+ digest := h.Sum(nil)
+ return s.Signer.Sign(rand, digest, opts)
+}