Add tests for signature algorithm negotiation.
Change-Id: I5a263734560997b774014b5742877aa4b2940664 Reviewed-on: https://boringssl-review.googlesource.com/2289 Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
ec2f27dee1
commit
000800a306
@ -129,8 +129,12 @@ const (
|
|||||||
|
|
||||||
// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
|
// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
|
||||||
const (
|
const (
|
||||||
|
hashMD5 uint8 = 1
|
||||||
hashSHA1 uint8 = 2
|
hashSHA1 uint8 = 2
|
||||||
|
hashSHA224 uint8 = 3
|
||||||
hashSHA256 uint8 = 4
|
hashSHA256 uint8 = 4
|
||||||
|
hashSHA384 uint8 = 5
|
||||||
|
hashSHA512 uint8 = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
|
// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
|
||||||
@ -346,6 +350,11 @@ type Config struct {
|
|||||||
// protection profiles to offer in DTLS-SRTP.
|
// protection profiles to offer in DTLS-SRTP.
|
||||||
SRTPProtectionProfiles []uint16
|
SRTPProtectionProfiles []uint16
|
||||||
|
|
||||||
|
// SignatureAndHashes, if not nil, overrides the default set of
|
||||||
|
// supported signature and hash algorithms to advertise in
|
||||||
|
// CertificateRequest.
|
||||||
|
SignatureAndHashes []signatureAndHash
|
||||||
|
|
||||||
// Bugs specifies optional misbehaviour to be used for testing other
|
// Bugs specifies optional misbehaviour to be used for testing other
|
||||||
// implementations.
|
// implementations.
|
||||||
Bugs ProtocolBugs
|
Bugs ProtocolBugs
|
||||||
@ -541,6 +550,14 @@ type ProtocolBugs struct {
|
|||||||
// SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the
|
// SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the
|
||||||
// server sends in the ServerHello instead of the negotiated one.
|
// server sends in the ServerHello instead of the negotiated one.
|
||||||
SendSRTPProtectionProfile uint16
|
SendSRTPProtectionProfile uint16
|
||||||
|
|
||||||
|
// NoSignatureAndHashes, if true, causes the client to omit the
|
||||||
|
// signature and hashes extension.
|
||||||
|
//
|
||||||
|
// For a server, it will cause an empty list to be sent in the
|
||||||
|
// CertificateRequest message. None the less, the configured set will
|
||||||
|
// still be enforced.
|
||||||
|
NoSignatureAndHashes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) serverInit() {
|
func (c *Config) serverInit() {
|
||||||
@ -655,6 +672,20 @@ func (c *Config) getCertificateForName(name string) *Certificate {
|
|||||||
return &c.Certificates[0]
|
return &c.Certificates[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) signatureAndHashesForServer() []signatureAndHash {
|
||||||
|
if c != nil && c.SignatureAndHashes != nil {
|
||||||
|
return c.SignatureAndHashes
|
||||||
|
}
|
||||||
|
return supportedClientCertSignatureAlgorithms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) signatureAndHashesForClient() []signatureAndHash {
|
||||||
|
if c != nil && c.SignatureAndHashes != nil {
|
||||||
|
return c.SignatureAndHashes
|
||||||
|
}
|
||||||
|
return supportedSKXSignatureAlgorithms
|
||||||
|
}
|
||||||
|
|
||||||
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
|
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
|
||||||
// from the CommonName and SubjectAlternateName fields of each of the leaf
|
// from the CommonName and SubjectAlternateName fields of each of the leaf
|
||||||
// certificates.
|
// certificates.
|
||||||
@ -806,3 +837,12 @@ func initDefaultCipherSuites() {
|
|||||||
func unexpectedMessageError(wanted, got interface{}) error {
|
func unexpectedMessageError(wanted, got interface{}) error {
|
||||||
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
|
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSupportedSignatureAndHash(sigHash signatureAndHash, sigHashes []signatureAndHash) bool {
|
||||||
|
for _, s := range sigHashes {
|
||||||
|
if s == sigHash {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -129,8 +129,8 @@ NextCipherSuite:
|
|||||||
return errors.New("tls: short read from Rand: " + err.Error())
|
return errors.New("tls: short read from Rand: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if hello.vers >= VersionTLS12 {
|
if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
|
||||||
hello.signatureAndHashes = supportedSKXSignatureAlgorithms
|
hello.signatureAndHashes = c.config.signatureAndHashesForClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
var session *ClientSessionState
|
var session *ClientSessionState
|
||||||
|
@ -467,7 +467,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
|||||||
}
|
}
|
||||||
if c.vers >= VersionTLS12 {
|
if c.vers >= VersionTLS12 {
|
||||||
certReq.hasSignatureAndHash = true
|
certReq.hasSignatureAndHash = true
|
||||||
certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
|
if !config.Bugs.NoSignatureAndHashes {
|
||||||
|
certReq.signatureAndHashes = config.signatureAndHashesForServer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An empty list of certificateAuthorities signals to
|
// An empty list of certificateAuthorities signals to
|
||||||
@ -567,6 +569,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
|||||||
var signatureAndHash signatureAndHash
|
var signatureAndHash signatureAndHash
|
||||||
if certVerify.hasSignatureAndHash {
|
if certVerify.hasSignatureAndHash {
|
||||||
signatureAndHash = certVerify.signatureAndHash
|
signatureAndHash = certVerify.signatureAndHash
|
||||||
|
if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
|
||||||
|
return errors.New("tls: unsupported hash function for client certificate")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Before TLS 1.2 the signature algorithm was implicit
|
// Before TLS 1.2 the signature algorithm was implicit
|
||||||
// from the key type, and only one hash per signature
|
// from the key type, and only one hash per signature
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"errors"
|
||||||
@ -125,28 +124,20 @@ func md5SHA1Hash(slices [][]byte) []byte {
|
|||||||
return md5sha1
|
return md5sha1
|
||||||
}
|
}
|
||||||
|
|
||||||
// sha256Hash implements TLS 1.2's hash function.
|
|
||||||
func sha256Hash(slices [][]byte) []byte {
|
|
||||||
h := sha256.New()
|
|
||||||
for _, slice := range slices {
|
|
||||||
h.Write(slice)
|
|
||||||
}
|
|
||||||
return h.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashForServerKeyExchange hashes the given slices and returns their digest
|
// hashForServerKeyExchange hashes the given slices and returns their digest
|
||||||
// and the identifier of the hash function used. The hashFunc argument is only
|
// and the identifier of the hash function used. The hashFunc argument is only
|
||||||
// used for >= TLS 1.2 and precisely identifies the hash function to use.
|
// used for >= TLS 1.2 and precisely identifies the hash function to use.
|
||||||
func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
|
func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
|
||||||
if version >= VersionTLS12 {
|
if version >= VersionTLS12 {
|
||||||
switch hashFunc {
|
hash, err := lookupTLSHash(hashFunc)
|
||||||
case hashSHA256:
|
if err != nil {
|
||||||
return sha256Hash(slices), crypto.SHA256, nil
|
return nil, 0, err
|
||||||
case hashSHA1:
|
|
||||||
return sha1Hash(slices), crypto.SHA1, nil
|
|
||||||
default:
|
|
||||||
return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
|
|
||||||
}
|
}
|
||||||
|
h := hash.New()
|
||||||
|
for _, slice := range slices {
|
||||||
|
h.Write(slice)
|
||||||
|
}
|
||||||
|
return h.Sum(nil), hash, nil
|
||||||
}
|
}
|
||||||
if sigType == signatureECDSA {
|
if sigType == signatureECDSA {
|
||||||
return sha1Hash(slices), crypto.SHA1, nil
|
return sha1Hash(slices), crypto.SHA1, nil
|
||||||
@ -307,6 +298,10 @@ func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clie
|
|||||||
if len(sig) < 2 {
|
if len(sig) < 2 {
|
||||||
return errServerKeyExchange
|
return errServerKeyExchange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isSupportedSignatureAndHash(signatureAndHash{ka.sigType, tls12HashId}, config.signatureAndHashesForClient()) {
|
||||||
|
return errors.New("tls: unsupported hash function for ServerKeyExchange")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sigLen := int(sig[0])<<8 | int(sig[1])
|
sigLen := int(sig[0])<<8 | int(sig[1])
|
||||||
if sigLen+2 != len(sig) {
|
if sigLen+2 != len(sig) {
|
||||||
|
@ -185,6 +185,27 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lookupTLSHash looks up the corresponding crypto.Hash for a given
|
||||||
|
// TLS hash identifier.
|
||||||
|
func lookupTLSHash(hash uint8) (crypto.Hash, error) {
|
||||||
|
switch hash {
|
||||||
|
case hashMD5:
|
||||||
|
return crypto.MD5, nil
|
||||||
|
case hashSHA1:
|
||||||
|
return crypto.SHA1, nil
|
||||||
|
case hashSHA224:
|
||||||
|
return crypto.SHA224, nil
|
||||||
|
case hashSHA256:
|
||||||
|
return crypto.SHA256, nil
|
||||||
|
case hashSHA384:
|
||||||
|
return crypto.SHA384, nil
|
||||||
|
case hashSHA512:
|
||||||
|
return crypto.SHA512, nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("tls: unsupported hash algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
|
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
|
||||||
if version >= VersionTLS12 {
|
if version >= VersionTLS12 {
|
||||||
newHash := sha256.New
|
newHash := sha256.New
|
||||||
@ -331,11 +352,13 @@ func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash
|
|||||||
return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil
|
return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil
|
||||||
}
|
}
|
||||||
if h.version >= VersionTLS12 {
|
if h.version >= VersionTLS12 {
|
||||||
if signatureAndHash.hash != hashSHA256 {
|
hashAlg, err := lookupTLSHash(signatureAndHash.hash)
|
||||||
return nil, 0, errors.New("tls: unsupported hash function for client certificate")
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
digest := sha256.Sum256(h.buffer)
|
hash := hashAlg.New()
|
||||||
return digest[:], crypto.SHA256, nil
|
hash.Write(h.buffer)
|
||||||
|
return hash.Sum(nil), hashAlg, nil
|
||||||
}
|
}
|
||||||
if signatureAndHash.signature == signatureECDSA {
|
if signatureAndHash.signature == signatureECDSA {
|
||||||
return h.server.Sum(nil), crypto.SHA1, nil
|
return h.server.Sum(nil), crypto.SHA1, nil
|
||||||
|
@ -2030,6 +2030,114 @@ func addDTLSReplayTests() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testHashes = []struct {
|
||||||
|
name string
|
||||||
|
id uint8
|
||||||
|
}{
|
||||||
|
{"SHA1", hashSHA1},
|
||||||
|
{"SHA224", hashSHA224},
|
||||||
|
{"SHA256", hashSHA256},
|
||||||
|
{"SHA384", hashSHA384},
|
||||||
|
{"SHA512", hashSHA512},
|
||||||
|
}
|
||||||
|
|
||||||
|
func addSigningHashTests() {
|
||||||
|
// Make sure each hash works. Include some fake hashes in the list and
|
||||||
|
// ensure they're ignored.
|
||||||
|
for _, hash := range testHashes {
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
name: "SigningHash-ClientAuth-" + hash.name,
|
||||||
|
config: Config{
|
||||||
|
ClientAuth: RequireAnyClientCert,
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureRSA, 42},
|
||||||
|
{signatureRSA, hash.id},
|
||||||
|
{signatureRSA, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: []string{
|
||||||
|
"-cert-file", rsaCertificateFile,
|
||||||
|
"-key-file", rsaKeyFile,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
testType: serverTest,
|
||||||
|
name: "SigningHash-ServerKeyExchange-Sign-" + hash.name,
|
||||||
|
config: Config{
|
||||||
|
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureRSA, 42},
|
||||||
|
{signatureRSA, hash.id},
|
||||||
|
{signatureRSA, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that hash resolution takes the signature type into account.
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
name: "SigningHash-ClientAuth-SignatureType",
|
||||||
|
config: Config{
|
||||||
|
ClientAuth: RequireAnyClientCert,
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureECDSA, hashSHA512},
|
||||||
|
{signatureRSA, hashSHA384},
|
||||||
|
{signatureECDSA, hashSHA1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: []string{
|
||||||
|
"-cert-file", rsaCertificateFile,
|
||||||
|
"-key-file", rsaKeyFile,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
testType: serverTest,
|
||||||
|
name: "SigningHash-ServerKeyExchange-SignatureType",
|
||||||
|
config: Config{
|
||||||
|
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureECDSA, hashSHA512},
|
||||||
|
{signatureRSA, hashSHA384},
|
||||||
|
{signatureECDSA, hashSHA1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test that, if the list is missing, the peer falls back to SHA-1.
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
name: "SigningHash-ClientAuth-Fallback",
|
||||||
|
config: Config{
|
||||||
|
ClientAuth: RequireAnyClientCert,
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureRSA, hashSHA1},
|
||||||
|
},
|
||||||
|
Bugs: ProtocolBugs{
|
||||||
|
NoSignatureAndHashes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: []string{
|
||||||
|
"-cert-file", rsaCertificateFile,
|
||||||
|
"-key-file", rsaKeyFile,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
testCases = append(testCases, testCase{
|
||||||
|
testType: serverTest,
|
||||||
|
name: "SigningHash-ServerKeyExchange-Fallback",
|
||||||
|
config: Config{
|
||||||
|
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||||
|
SignatureAndHashes: []signatureAndHash{
|
||||||
|
{signatureRSA, hashSHA1},
|
||||||
|
},
|
||||||
|
Bugs: ProtocolBugs{
|
||||||
|
NoSignatureAndHashes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
|
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
@ -2088,6 +2196,7 @@ func main() {
|
|||||||
addExtendedMasterSecretTests()
|
addExtendedMasterSecretTests()
|
||||||
addRenegotiationTests()
|
addRenegotiationTests()
|
||||||
addDTLSReplayTests()
|
addDTLSReplayTests()
|
||||||
|
addSigningHashTests()
|
||||||
for _, async := range []bool{false, true} {
|
for _, async := range []bool{false, true} {
|
||||||
for _, splitHandshake := range []bool{false, true} {
|
for _, splitHandshake := range []bool{false, true} {
|
||||||
for _, protocol := range []protocol{tls, dtls} {
|
for _, protocol := range []protocol{tls, dtls} {
|
||||||
|
Loading…
Reference in New Issue
Block a user