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)
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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} {
|
||||
|
Loading…
Reference in New Issue
Block a user