diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index 06992e0..cf7ef32 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -6973,14 +6973,27 @@ no_memory: /* no-memory error has already been set. */ /* - * Returns true if the client authentication key is an RSA or DSA key that - * may be able to sign only SHA-1 hashes. + * Returns the TLS signature algorithm for the client authentication key and + * whether it is an RSA or DSA key that may be able to sign only SHA-1 hashes. */ -static PRBool -ssl3_ClientKeyPrefersSHA1(sslSocket *ss) +static SECStatus +ssl3_ExtractClientKeyInfo(sslSocket *ss, + TLSSignatureAlgorithm *sigAlg, + PRBool *preferSha1) { + SECStatus rv = SECSuccess; SECKEYPublicKey *pubk; - PRBool prefer_sha1 = PR_FALSE; + + pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate); + if (pubk == NULL) { + rv = SECFailure; + goto done; + } + + rv = ssl3_TLSSignatureAlgorithmForKeyType(pubk->keyType, sigAlg); + if (rv != SECSuccess) { + goto done; + } #if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32) /* If the key is in CAPI, assume conservatively that the CAPI service @@ -6989,7 +7002,8 @@ ssl3_ClientKeyPrefersSHA1(sslSocket *ss) if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) { /* CAPI only supports RSA and DSA signatures, so we don't need to * check the key type. */ - return PR_TRUE; + *preferSha1 = PR_TRUE; + goto done; } #endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */ @@ -6999,38 +7013,61 @@ ssl3_ClientKeyPrefersSHA1(sslSocket *ss) * older, DSA key size is at most 1024 bits and the hash function must * be SHA-1. */ - pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate); - if (pubk == NULL) { - return PR_FALSE; - } if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) { - prefer_sha1 = SECKEY_PublicKeyStrength(pubk) <= 128; + *preferSha1 = SECKEY_PublicKeyStrength(pubk) <= 128; + } else { + *preferSha1 = PR_FALSE; } - SECKEY_DestroyPublicKey(pubk); - return prefer_sha1; + + done: + if (pubk) + SECKEY_DestroyPublicKey(pubk); + return rv; } -/* Destroys the backup handshake hash context if we don't need it. */ +/* Destroys the backup handshake hash context if we don't need it. Note that + * this function selects the hash algorithm for client authentication + * signatures; ssl3_SendCertificateVerify uses the presence of the backup hash + * to determine whether to use SHA-1 or SHA-256. */ static void ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss, const SECItem *algorithms) { - PRBool need_backup_hash = PR_FALSE; + SECStatus rv; + TLSSignatureAlgorithm sigAlg; + PRBool preferSha1; + PRBool supportsSha1 = PR_FALSE; + PRBool supportsSha256 = PR_FALSE; + PRBool needBackupHash = PR_FALSE; unsigned int i; PORT_Assert(ss->ssl3.hs.md5); - if (ssl3_ClientKeyPrefersSHA1(ss)) { - /* Use SHA-1 if the server supports it. */ - for (i = 0; i < algorithms->len; i += 2) { - if (algorithms->data[i] == tls_hash_sha1 && - (algorithms->data[i+1] == tls_sig_rsa || - algorithms->data[i+1] == tls_sig_dsa)) { - need_backup_hash = PR_TRUE; - break; + + /* Determine the key's signature algorithm and whether it prefers SHA-1. */ + rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1); + if (rv != SECSuccess) { + goto done; + } + + /* Determine the server's hash support for that signature algorithm. */ + for (i = 0; i < algorithms->len; i += 2) { + if (algorithms->data[i+1] == sigAlg) { + if (algorithms->data[i] == tls_hash_sha1) { + supportsSha1 = PR_TRUE; + } else if (algorithms->data[i] == tls_hash_sha256) { + supportsSha256 = PR_TRUE; } } } - if (!need_backup_hash) { + + /* If either the server does not support SHA-256 or the client key prefers + * SHA-1, leave the backup hash. */ + if (supportsSha1 && (preferSha1 || !supportsSha256)) { + needBackupHash = PR_TRUE; + } + +done: + if (!needBackupHash) { PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE); ss->ssl3.hs.md5 = NULL; }