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:
David Benjamin 2014-11-14 01:43:59 -05:00 committed by Adam Langley
parent ec2f27dee1
commit 000800a306
6 changed files with 196 additions and 24 deletions

View File

@ -129,8 +129,12 @@ const (
// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
const (
hashMD5 uint8 = 1
hashSHA1 uint8 = 2
hashSHA224 uint8 = 3
hashSHA256 uint8 = 4
hashSHA384 uint8 = 5
hashSHA512 uint8 = 6
)
// 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.
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
// implementations.
Bugs ProtocolBugs
@ -541,6 +550,14 @@ type ProtocolBugs struct {
// SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the
// server sends in the ServerHello instead of the negotiated one.
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() {
@ -655,6 +672,20 @@ func (c *Config) getCertificateForName(name string) *Certificate {
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
// from the CommonName and SubjectAlternateName fields of each of the leaf
// certificates.
@ -806,3 +837,12 @@ func initDefaultCipherSuites() {
func unexpectedMessageError(wanted, got interface{}) error {
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
}

View File

@ -129,8 +129,8 @@ NextCipherSuite:
return errors.New("tls: short read from Rand: " + err.Error())
}
if hello.vers >= VersionTLS12 {
hello.signatureAndHashes = supportedSKXSignatureAlgorithms
if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
hello.signatureAndHashes = c.config.signatureAndHashesForClient()
}
var session *ClientSessionState

View File

@ -467,7 +467,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAndHash = true
certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
if !config.Bugs.NoSignatureAndHashes {
certReq.signatureAndHashes = config.signatureAndHashesForServer()
}
}
// An empty list of certificateAuthorities signals to
@ -567,6 +569,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
var signatureAndHash signatureAndHash
if certVerify.hasSignatureAndHash {
signatureAndHash = certVerify.signatureAndHash
if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
return errors.New("tls: unsupported hash function for client certificate")
}
} else {
// Before TLS 1.2 the signature algorithm was implicit
// from the key type, and only one hash per signature

View File

@ -12,7 +12,6 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"errors"
@ -125,28 +124,20 @@ func md5SHA1Hash(slices [][]byte) []byte {
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
// 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.
func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
if version >= VersionTLS12 {
switch hashFunc {
case hashSHA256:
return sha256Hash(slices), crypto.SHA256, nil
case hashSHA1:
return sha1Hash(slices), crypto.SHA1, nil
default:
return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
hash, err := lookupTLSHash(hashFunc)
if err != nil {
return nil, 0, err
}
h := hash.New()
for _, slice := range slices {
h.Write(slice)
}
return h.Sum(nil), hash, nil
}
if sigType == signatureECDSA {
return sha1Hash(slices), crypto.SHA1, nil
@ -307,6 +298,10 @@ func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clie
if len(sig) < 2 {
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])
if sigLen+2 != len(sig) {

View File

@ -185,6 +185,27 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie
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 {
if version >= VersionTLS12 {
newHash := sha256.New
@ -331,11 +352,13 @@ func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash
return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil
}
if h.version >= VersionTLS12 {
if signatureAndHash.hash != hashSHA256 {
return nil, 0, errors.New("tls: unsupported hash function for client certificate")
hashAlg, err := lookupTLSHash(signatureAndHash.hash)
if err != nil {
return nil, 0, err
}
digest := sha256.Sum256(h.buffer)
return digest[:], crypto.SHA256, nil
hash := hashAlg.New()
hash.Write(h.buffer)
return hash.Sum(nil), hashAlg, nil
}
if signatureAndHash.signature == signatureECDSA {
return h.server.Sum(nil), crypto.SHA1, nil

View File

@ -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) {
defer wg.Done()
@ -2088,6 +2196,7 @@ func main() {
addExtendedMasterSecretTests()
addRenegotiationTests()
addDTLSReplayTests()
addSigningHashTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {