Index: net/third_party/nss/ssl/ssl3con.c =================================================================== --- net/third_party/nss/ssl/ssl3con.c (revision 220594) +++ net/third_party/nss/ssl/ssl3con.c (working copy) @@ -3933,6 +3933,20 @@ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); return SECFailure; } + + /* A backup SHA-1 hash for a potential client auth signature. */ + if (!ss->sec.isServer) { + ss->ssl3.hs.md5 = PK11_CreateDigestContext(SEC_OID_SHA1); + if (ss->ssl3.hs.md5 == NULL) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + + if (PK11_DigestBegin(ss->ssl3.hs.md5) != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + } } else { /* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or * created successfully. */ @@ -4043,6 +4057,13 @@ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); return rv; } + if (ss->ssl3.hs.md5) { + rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return rv; + } + } } else { rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l); if (rv != SECSuccess) { @@ -4791,6 +4812,30 @@ return rv; } +static SECStatus +ssl3_ComputeBackupHandshakeHashes(sslSocket * ss, + SSL3Hashes * hashes) /* output goes here. */ +{ + SECStatus rv = SECSuccess; + + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); + PORT_Assert( ss->ssl3.hs.hashType == handshake_hash_single ); + + rv = PK11_DigestFinal(ss->ssl3.hs.md5, hashes->u.raw, &hashes->len, + sizeof(hashes->u.raw)); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + rv = SECFailure; + goto loser; + } + hashes->hashAlg = SEC_OID_SHA1; + +loser: + PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE); + ss->ssl3.hs.md5 = NULL; + return rv; +} + /* * SSL 2 based implementations pass in the initial outbound buffer * so that the handshake hash can contain the included information. @@ -6044,7 +6089,17 @@ SSL_GETPID(), ss->fd)); ssl_GetSpecReadLock(ss); - rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0); + /* In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the handshake hash + * function (SHA-256). If the server or the client does not support SHA-256 + * as a signature hash, we can either maintain a backup SHA-1 handshake + * hash or buffer all handshake messages. + */ + if (ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) { + rv = ssl3_ComputeBackupHandshakeHashes(ss, &hashes); + PORT_Assert(ss->ssl3.hs.md5 == NULL); + } else { + rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0); + } ssl_ReleaseSpecReadLock(ss); if (rv != SECSuccess) { goto done; /* err code was set by ssl3_ComputeHandshakeHashes */ @@ -6098,11 +6153,6 @@ if (rv != SECSuccess) { goto done; } - /* We always sign using the handshake hash function. It's possible that - * a server could support SHA-256 as the handshake hash but not as a - * signature hash. In that case we wouldn't be able to do client - * certificates with it. The alternative is to buffer all handshake - * messages. */ sigAndHash.hashAlg = hashes.hashAlg; rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash); @@ -6802,6 +6852,70 @@ } +/* + * Returns true if the client authentication key is an RSA or DSA key that + * may be able to sign only SHA-1 hashes. + */ +static PRBool +ssl3_ClientKeyPrefersSHA1(sslSocket *ss) +{ + SECKEYPublicKey *pubk; + PRBool prefer_sha1 = PR_FALSE; + +#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32) + /* If the key is in CAPI, assume conservatively that the CAPI service + * provider may be unable to sign SHA-256 hashes. + */ + 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; + } +#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */ + + /* If the key is a 1024-bit RSA or DSA key, assume conservatively that + * it may be unable to sign SHA-256 hashes. This is the case for older + * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and + * 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; + } + SECKEY_DestroyPublicKey(pubk); + return prefer_sha1; +} + +/* Destroys the backup handshake hash context if we don't need it. */ +static void +ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss, + const SECItem *algorithms) +{ + PRBool need_backup_hash = 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; + } + } + } + if (!need_backup_hash) { + PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE); + ss->ssl3.hs.md5 = NULL; + } +} + typedef struct dnameNode { struct dnameNode *next; SECItem name; @@ -6994,6 +7108,9 @@ } goto send_no_certificate; } + if (isTLS12) { + ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms); + } break; /* not an error */ } #endif /* NSS_PLATFORM_CLIENT_AUTH */ @@ -7029,6 +7146,9 @@ } goto send_no_certificate; } + if (isTLS12) { + ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms); + } break; /* not an error */ case SECFailure: @@ -7227,6 +7347,13 @@ (ss->ssl3.platformClientKey || ss->ssl3.clientPrivateKey != NULL); + if (!sendClientCert && + ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) { + /* Don't need the backup handshake hash. */ + PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE); + ss->ssl3.hs.md5 = NULL; + } + /* We must wait for the server's certificate to be authenticated before * sending the client certificate in order to disclosing the client * certificate to an attacker that does not have a valid cert for the Index: net/third_party/nss/ssl/sslimpl.h =================================================================== --- net/third_party/nss/ssl/sslimpl.h (revision 220594) +++ net/third_party/nss/ssl/sslimpl.h (working copy) @@ -838,6 +838,9 @@ * SSL 3.0 - TLS 1.1 use both |md5| and |sha|. |md5| is used for MD5 and * |sha| for SHA-1. * TLS 1.2 and later use only |sha|, for SHA-256. */ + /* NOTE: On the client side, TLS 1.2 and later use |md5| as a backup + * handshake hash for generating client auth signatures. Confusingly, the + * backup hash function is SHA-1. */ PK11Context * md5; PK11Context * sha;