2017-11-22 18:25:20 +00:00
|
|
|
// Copyright 2017 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 tls
|
|
|
|
|
|
|
|
|
|
import (
|
2019-05-16 19:13:29 -04:00
|
|
|
"bytes"
|
2017-11-22 18:25:20 +00:00
|
|
|
"crypto"
|
|
|
|
|
"crypto/ecdsa"
|
2019-05-16 19:13:29 -04:00
|
|
|
"crypto/ed25519"
|
2018-11-02 00:57:30 -04:00
|
|
|
"crypto/elliptic"
|
2017-11-22 18:25:20 +00:00
|
|
|
"crypto/rsa"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2018-11-01 01:01:09 -04:00
|
|
|
"hash"
|
|
|
|
|
"io"
|
2025-02-19 12:29:31 +01:00
|
|
|
"slices"
|
2017-11-22 18:25:20 +00:00
|
|
|
)
|
|
|
|
|
|
2025-11-26 21:11:35 +01:00
|
|
|
// verifyHandshakeSignature verifies a signature against unhashed handshake contents.
|
2019-05-16 19:13:29 -04:00
|
|
|
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
|
2025-11-26 21:11:35 +01:00
|
|
|
if hashFunc != directSigning {
|
|
|
|
|
h := hashFunc.New()
|
|
|
|
|
h.Write(signed)
|
|
|
|
|
signed = h.Sum(nil)
|
|
|
|
|
}
|
2017-11-22 18:25:20 +00:00
|
|
|
switch sigType {
|
|
|
|
|
case signatureECDSA:
|
|
|
|
|
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
|
|
|
|
if !ok {
|
2019-10-29 16:46:26 -04:00
|
|
|
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
|
2017-11-22 18:25:20 +00:00
|
|
|
}
|
2020-02-24 17:23:19 -05:00
|
|
|
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
|
2019-10-29 16:46:26 -04:00
|
|
|
return errors.New("ECDSA verification failure")
|
2017-11-22 18:25:20 +00:00
|
|
|
}
|
2019-05-16 19:13:29 -04:00
|
|
|
case signatureEd25519:
|
|
|
|
|
pubKey, ok := pubkey.(ed25519.PublicKey)
|
|
|
|
|
if !ok {
|
2019-10-29 16:46:26 -04:00
|
|
|
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
|
2019-05-16 19:13:29 -04:00
|
|
|
}
|
|
|
|
|
if !ed25519.Verify(pubKey, signed, sig) {
|
2019-10-29 16:46:26 -04:00
|
|
|
return errors.New("Ed25519 verification failure")
|
2019-05-16 19:13:29 -04:00
|
|
|
}
|
2017-11-22 19:27:20 +00:00
|
|
|
case signaturePKCS1v15:
|
2017-11-22 18:25:20 +00:00
|
|
|
pubKey, ok := pubkey.(*rsa.PublicKey)
|
|
|
|
|
if !ok {
|
2019-10-29 16:46:26 -04:00
|
|
|
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
2017-11-22 18:25:20 +00:00
|
|
|
}
|
2019-05-16 19:13:29 -04:00
|
|
|
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
|
2017-11-22 18:25:20 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2017-11-22 19:27:20 +00:00
|
|
|
case signatureRSAPSS:
|
|
|
|
|
pubKey, ok := pubkey.(*rsa.PublicKey)
|
|
|
|
|
if !ok {
|
2019-10-29 16:46:26 -04:00
|
|
|
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
2017-11-22 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
2019-05-16 19:13:29 -04:00
|
|
|
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
|
2017-11-22 19:27:20 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2017-11-22 18:25:20 +00:00
|
|
|
default:
|
2019-10-29 16:46:26 -04:00
|
|
|
return errors.New("internal error: unknown signature type")
|
2017-11-22 18:25:20 +00:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-11-01 01:01:09 -04:00
|
|
|
|
2025-11-26 21:11:35 +01:00
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 01:01:09 -04:00
|
|
|
const (
|
|
|
|
|
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
|
|
|
|
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var signaturePadding = []byte{
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 21:11:35 +01:00
|
|
|
// 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()
|
2018-11-01 01:01:09 -04:00
|
|
|
}
|
2018-11-02 00:57:30 -04:00
|
|
|
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
// typeAndHashFromSignatureScheme returns the corresponding signature type and
|
|
|
|
|
// crypto.Hash for a given TLS SignatureScheme.
|
|
|
|
|
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
|
|
|
|
|
switch signatureAlgorithm {
|
|
|
|
|
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
|
|
|
|
|
sigType = signaturePKCS1v15
|
|
|
|
|
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
|
|
|
|
|
sigType = signatureRSAPSS
|
|
|
|
|
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
|
|
|
|
|
sigType = signatureECDSA
|
|
|
|
|
case Ed25519:
|
|
|
|
|
sigType = signatureEd25519
|
|
|
|
|
default:
|
2019-11-21 13:52:18 -05:00
|
|
|
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
}
|
|
|
|
|
switch signatureAlgorithm {
|
|
|
|
|
case PKCS1WithSHA1, ECDSAWithSHA1:
|
|
|
|
|
hash = crypto.SHA1
|
|
|
|
|
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
|
|
|
|
hash = crypto.SHA256
|
|
|
|
|
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
|
|
|
|
hash = crypto.SHA384
|
|
|
|
|
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
|
|
|
|
hash = crypto.SHA512
|
|
|
|
|
case Ed25519:
|
|
|
|
|
hash = directSigning
|
|
|
|
|
default:
|
2019-11-21 13:52:18 -05:00
|
|
|
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
}
|
|
|
|
|
return sigType, hash, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
|
|
|
|
|
// a given public key used with TLS 1.0 and 1.1, before the introduction of
|
|
|
|
|
// signature algorithm negotiation.
|
|
|
|
|
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
|
|
|
|
|
switch pub.(type) {
|
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
|
return signaturePKCS1v15, crypto.MD5SHA1, nil
|
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
|
return signatureECDSA, crypto.SHA1, nil
|
|
|
|
|
case ed25519.PublicKey:
|
|
|
|
|
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
|
|
|
|
|
// but it requires holding on to a handshake transcript to do a
|
|
|
|
|
// full signature, and not even OpenSSL bothers with the
|
|
|
|
|
// complexity, so we can't even test it properly.
|
|
|
|
|
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
|
|
|
|
|
default:
|
|
|
|
|
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 02:14:18 -05:00
|
|
|
var rsaSignatureSchemes = []struct {
|
|
|
|
|
scheme SignatureScheme
|
|
|
|
|
minModulusBytes int
|
|
|
|
|
}{
|
|
|
|
|
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
|
|
|
|
// emLen >= hLen + sLen + 2
|
2025-05-23 18:04:36 +02:00
|
|
|
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2},
|
|
|
|
|
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2},
|
|
|
|
|
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2},
|
2020-07-08 09:40:33 -04:00
|
|
|
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
2019-11-04 02:14:18 -05:00
|
|
|
// emLen >= len(prefix) + hLen + 11
|
2025-05-23 18:04:36 +02:00
|
|
|
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11},
|
|
|
|
|
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11},
|
|
|
|
|
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11},
|
|
|
|
|
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11},
|
2019-11-04 02:14:18 -05:00
|
|
|
}
|
|
|
|
|
|
2025-05-23 20:28:36 +02:00
|
|
|
func signatureSchemesForPublicKey(version uint16, pub crypto.PublicKey) []SignatureScheme {
|
|
|
|
|
switch pub := pub.(type) {
|
2018-11-02 00:57:30 -04:00
|
|
|
case *ecdsa.PublicKey:
|
2025-05-23 20:28:36 +02:00
|
|
|
if version < VersionTLS13 {
|
2018-11-29 01:38:07 -05:00
|
|
|
// In TLS 1.2 and earlier, ECDSA algorithms are not
|
|
|
|
|
// constrained to a single curve.
|
2025-05-23 20:28:36 +02:00
|
|
|
return []SignatureScheme{
|
2018-11-29 01:38:07 -05:00
|
|
|
ECDSAWithP256AndSHA256,
|
|
|
|
|
ECDSAWithP384AndSHA384,
|
|
|
|
|
ECDSAWithP521AndSHA512,
|
|
|
|
|
ECDSAWithSHA1,
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-29 02:30:26 -05:00
|
|
|
switch pub.Curve {
|
2018-11-02 00:57:30 -04:00
|
|
|
case elliptic.P256():
|
2025-05-23 20:28:36 +02:00
|
|
|
return []SignatureScheme{ECDSAWithP256AndSHA256}
|
2018-11-02 00:57:30 -04:00
|
|
|
case elliptic.P384():
|
2025-05-23 20:28:36 +02:00
|
|
|
return []SignatureScheme{ECDSAWithP384AndSHA384}
|
2018-11-02 00:57:30 -04:00
|
|
|
case elliptic.P521():
|
2025-05-23 20:28:36 +02:00
|
|
|
return []SignatureScheme{ECDSAWithP521AndSHA512}
|
2018-11-02 00:57:30 -04:00
|
|
|
default:
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
case *rsa.PublicKey:
|
2019-11-04 02:14:18 -05:00
|
|
|
size := pub.Size()
|
2025-05-23 20:28:36 +02:00
|
|
|
sigAlgs := make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
2019-11-04 02:14:18 -05:00
|
|
|
for _, candidate := range rsaSignatureSchemes {
|
2025-05-23 18:04:36 +02:00
|
|
|
if size >= candidate.minModulusBytes {
|
2019-11-04 02:14:18 -05:00
|
|
|
sigAlgs = append(sigAlgs, candidate.scheme)
|
2018-11-29 01:38:07 -05:00
|
|
|
}
|
2018-11-02 00:57:30 -04:00
|
|
|
}
|
2025-05-23 20:28:36 +02:00
|
|
|
return sigAlgs
|
2019-05-16 19:13:29 -04:00
|
|
|
case ed25519.PublicKey:
|
2025-05-23 20:28:36 +02:00
|
|
|
return []SignatureScheme{Ed25519}
|
2018-11-02 00:57:30 -04:00
|
|
|
default:
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-29 02:30:26 -05:00
|
|
|
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
|
|
|
|
|
// that works with the selected certificate. It's only called for protocol
|
|
|
|
|
// versions that support signature algorithms, so TLS 1.2 and 1.3.
|
|
|
|
|
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
|
2025-05-23 20:28:36 +02:00
|
|
|
priv, ok := c.PrivateKey.(crypto.Signer)
|
|
|
|
|
if !ok {
|
|
|
|
|
return 0, unsupportedCertificateError(c)
|
|
|
|
|
}
|
|
|
|
|
supportedAlgs := signatureSchemesForPublicKey(vers, priv.Public())
|
|
|
|
|
if c.SupportedSignatureAlgorithms != nil {
|
|
|
|
|
supportedAlgs = slices.DeleteFunc(supportedAlgs, func(sigAlg SignatureScheme) bool {
|
|
|
|
|
return !isSupportedSignatureAlgorithm(sigAlg, c.SupportedSignatureAlgorithms)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// Filter out any unsupported signature algorithms, for example due to
|
|
|
|
|
// FIPS 140-3 policy, tlssha1=0, or protocol version.
|
|
|
|
|
supportedAlgs = slices.DeleteFunc(supportedAlgs, func(sigAlg SignatureScheme) bool {
|
|
|
|
|
return isDisabledSignatureAlgorithm(vers, sigAlg, false)
|
|
|
|
|
})
|
2019-11-03 21:04:48 -05:00
|
|
|
if len(supportedAlgs) == 0 {
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
return 0, unsupportedCertificateError(c)
|
|
|
|
|
}
|
|
|
|
|
if len(peerAlgs) == 0 && vers == VersionTLS12 {
|
|
|
|
|
// For TLS 1.2, if the client didn't send signature_algorithms then we
|
|
|
|
|
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
2025-03-15 15:12:39 +01:00
|
|
|
// RFC 9155 made signature_algorithms mandatory in TLS 1.2, and we gated
|
|
|
|
|
// it behind the tlssha1 GODEBUG setting.
|
|
|
|
|
if tlssha1.Value() != "1" {
|
|
|
|
|
return 0, errors.New("tls: missing signature_algorithms from TLS 1.2 peer")
|
|
|
|
|
}
|
crypto/tls: refactor certificate and signature algorithm logic
This refactors a lot of the certificate support logic to make it cleaner
and reusable where possible. These changes will make the following CLs
much simpler.
In particular, the heavily overloaded pickSignatureAlgorithm is gone.
That function used to cover both signing and verifying side, would work
both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned
sigalg, type and hash.
Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have
effectively completely different logic. TLS 1.0/1.1 simply use
legacyTypeAndHashFromPublicKey as they employ a fixed hash function and
signature algorithm for each public key type. TLS 1.2 is instead routed
through selectSignatureScheme (on the signing side) or
isSupportedSignatureAlgorithm (on the verifying side) and
typeAndHashFromSignatureScheme, like TLS 1.3.
On the signing side, signatureSchemesForCertificate was already version
aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just
had to learn the Section 7.4.1.4.1 defaults for a missing
signature_algorithms to replace pickSignatureAlgorithm.
On the verifying side, pickSignatureAlgorithm was also checking the
public key type, while isSupportedSignatureAlgorithm +
typeAndHashFromSignatureScheme are not, but that check was redundant
with the one in verifyHandshakeSignature.
There should be no major change in behavior so far. A few minor changes
came from the refactor: we now correctly require signature_algorithms in
TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the
client didn't send signature_algorithms; and we don't send
ec_points_format in the ServerHello (a compatibility measure) if we are
not doing ECDHE anyway because there are no mutually supported curves.
The tests also got simpler because they test simpler functions. The
caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the
transcript tests.
Updates #32426
Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577
Reviewed-on: https://go-review.googlesource.com/c/go/+/205061
Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
|
|
|
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
|
|
|
|
|
}
|
|
|
|
|
// Pick signature scheme in the peer's preference order, as our
|
|
|
|
|
// preference order is not configurable.
|
|
|
|
|
for _, preferredAlg := range peerAlgs {
|
|
|
|
|
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
|
|
|
|
return preferredAlg, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-29 02:30:26 -05:00
|
|
|
// unsupportedCertificateError returns a helpful error for certificates with
|
|
|
|
|
// an unsupported private key.
|
|
|
|
|
func unsupportedCertificateError(cert *Certificate) error {
|
|
|
|
|
switch cert.PrivateKey.(type) {
|
|
|
|
|
case rsa.PrivateKey, ecdsa.PrivateKey:
|
|
|
|
|
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
|
|
|
|
|
cert.PrivateKey, cert.PrivateKey)
|
2019-05-16 19:13:29 -04:00
|
|
|
case *ed25519.PrivateKey:
|
|
|
|
|
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
|
2018-11-29 02:30:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signer, ok := cert.PrivateKey.(crypto.Signer)
|
|
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
|
|
|
|
|
cert.PrivateKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch pub := signer.Public().(type) {
|
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
|
switch pub.Curve {
|
|
|
|
|
case elliptic.P256():
|
|
|
|
|
case elliptic.P384():
|
|
|
|
|
case elliptic.P521():
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
|
|
|
|
|
}
|
|
|
|
|
case *rsa.PublicKey:
|
2019-11-04 02:14:18 -05:00
|
|
|
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
|
2019-05-16 19:13:29 -04:00
|
|
|
case ed25519.PublicKey:
|
2018-11-29 02:30:26 -05:00
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-03 21:04:48 -05:00
|
|
|
if cert.SupportedSignatureAlgorithms != nil {
|
|
|
|
|
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-29 02:30:26 -05:00
|
|
|
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
|
|
|
|
|
}
|