Index: net/third_party/nss/ssl/ssl3con.c |
=================================================================== |
--- net/third_party/nss/ssl/ssl3con.c (revision 199250) |
+++ net/third_party/nss/ssl/ssl3con.c (working copy) |
@@ -15,6 +15,7 @@ |
#include "keyhi.h" |
#include "secder.h" |
#include "secitem.h" |
+#include "sechash.h" |
#include "sslimpl.h" |
#include "sslproto.h" |
@@ -800,15 +801,101 @@ |
return rv; |
} |
+#define SSL_MAX_RSA_ASN1_PREFIX 20 |
+ |
+/* ssl3_GetPKCS1v15ASN1Data sets |out| and |out_len| to point to a buffer that |
+ * contains ASN.1 data that should be prepended to a hash of the given type in |
+ * order to create a structure that is valid for use in a PKCS#1 v1.5 RSA |
+ * signature. |out_len| will not be set to a value greater than |
+ * SSL_MAX_RSA_ASN1_PREFIX. */ |
+static SECStatus |
+ssl3_GetPKCS1v15ASN1Data(SECOidTag hashAlg, |
+ const SSL3Opaque** out, unsigned int *out_len) |
+{ |
+ /* These are ASN1 DER structures: |
+ * DigestInfo ::= SEQUENCE { |
+ * digestAlgorithm AlgorithmIdentifier, |
+ * digest OCTET STRING |
+ * } |
+ */ |
+ static const unsigned char kMD5[] = { |
+ 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, |
+ 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, |
+ 0x04, 0x10 |
+ }; |
+ static const unsigned char kSHA1[] = { |
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, |
+ 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 |
+ }; |
+ static const unsigned char kSHA224[] = { |
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, |
+ 0x00, 0x04, 0x1c |
+ }; |
+ static const unsigned char kSHA256[] = { |
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, |
+ 0x00, 0x04, 0x20 |
+ }; |
+ static const unsigned char kSHA384[] = { |
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, |
+ 0x00, 0x04, 0x30 |
+ }; |
+ static const unsigned char kSHA512[] = { |
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, |
+ 0x00, 0x04, 0x40 |
+ }; |
+ |
+ switch (hashAlg) { |
+ case SEC_OID_UNKNOWN: |
+ *out_len = 0; |
+ break; |
+ case SEC_OID_MD5: |
+ *out = kMD5; |
+ *out_len = sizeof(kMD5); |
+ break; |
+ case SEC_OID_SHA1: |
+ *out = kSHA1; |
+ *out_len = sizeof(kSHA1); |
+ break; |
+ case SEC_OID_SHA224: |
+ *out = kSHA224; |
+ *out_len = sizeof(kSHA224); |
+ break; |
+ case SEC_OID_SHA256: |
+ *out = kSHA256; |
+ *out_len = sizeof(kSHA256); |
+ break; |
+ case SEC_OID_SHA384: |
+ *out = kSHA384; |
+ *out_len = sizeof(kSHA384); |
+ break; |
+ case SEC_OID_SHA512: |
+ *out = kSHA512; |
+ *out_len = sizeof(kSHA512); |
+ break; |
+ default: |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ |
+ return SECSuccess; |
+} |
+ |
/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */ |
SECStatus |
ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf, |
PRBool isTLS) |
{ |
- SECStatus rv = SECFailure; |
- PRBool doDerEncode = PR_FALSE; |
- int signatureLen; |
- SECItem hashItem; |
+ SECStatus rv = SECFailure; |
+ PRBool doDerEncode = PR_FALSE; |
+ int signatureLen; |
+ SECItem hashItem; |
+ unsigned int len; |
+ const SSL3Opaque* asn1Prefix; |
+ SSL3Opaque asn1AndHash[HASH_LENGTH_MAX + SSL_MAX_RSA_ASN1_PREFIX]; |
buf->data = NULL; |
signatureLen = PK11_SignatureLen(key); |
@@ -824,19 +911,44 @@ |
switch (key->keyType) { |
case rsaKey: |
- hashItem.data = hash->md5; |
- hashItem.len = sizeof(SSL3Hashes); |
+ rv = ssl3_GetPKCS1v15ASN1Data(hash->hashAlg, &asn1Prefix, &len); |
+ if (rv != SECSuccess) { |
+ return rv; |
+ } |
+ if (len + hash->len > sizeof(asn1AndHash)) { |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+ memcpy(asn1AndHash, asn1Prefix, len); |
+ memcpy(asn1AndHash + len, hash->u.raw, hash->len); |
+ len += hash->len; |
+ hashItem.data = asn1AndHash; |
+ hashItem.len = len; |
break; |
case dsaKey: |
doDerEncode = isTLS; |
- hashItem.data = hash->sha; |
- hashItem.len = sizeof(hash->sha); |
+ /* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash. |
+ * In that case, we use just the SHA1 part. */ |
+ if (hash->hashAlg == SEC_OID_UNKNOWN) { |
+ hashItem.data = hash->u.s.sha; |
+ hashItem.len = sizeof(hash->u.s.sha); |
+ } else { |
+ hashItem.data = hash->u.raw; |
+ hashItem.len = hash->len; |
+ } |
break; |
#ifdef NSS_ENABLE_ECC |
case ecKey: |
doDerEncode = PR_TRUE; |
- hashItem.data = hash->sha; |
- hashItem.len = sizeof(hash->sha); |
+ /* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash. |
+ * In that case, we use just the SHA1 part. */ |
+ if (hash->hashAlg == SEC_OID_UNKNOWN) { |
+ hashItem.data = hash->u.s.sha; |
+ hashItem.len = sizeof(hash->u.s.sha); |
+ } else { |
+ hashItem.data = hash->u.raw; |
+ hashItem.len = hash->len; |
+ } |
break; |
#endif /* NSS_ENABLE_ECC */ |
default: |
@@ -879,9 +991,9 @@ |
SECItem * signature = NULL; |
SECStatus rv; |
SECItem hashItem; |
-#ifdef NSS_ENABLE_ECC |
+ const SSL3Opaque* asn1Prefix; |
+ SSL3Opaque asn1AndHash[HASH_LENGTH_MAX + SSL_MAX_RSA_ASN1_PREFIX]; |
unsigned int len; |
-#endif /* NSS_ENABLE_ECC */ |
PRINT_BUF(60, (NULL, "check signed hashes", |
@@ -895,12 +1007,30 @@ |
switch (key->keyType) { |
case rsaKey: |
- hashItem.data = hash->md5; |
- hashItem.len = sizeof(SSL3Hashes); |
+ rv = ssl3_GetPKCS1v15ASN1Data(hash->hashAlg, &asn1Prefix, &len); |
+ if (rv != SECSuccess) { |
+ return rv; |
+ } |
+ if (len + hash->len > sizeof(asn1AndHash)) { |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+ memcpy(asn1AndHash, asn1Prefix, len); |
+ memcpy(asn1AndHash + len, hash->u.raw, hash->len); |
+ len += hash->len; |
+ hashItem.data = asn1AndHash; |
+ hashItem.len = len; |
break; |
case dsaKey: |
- hashItem.data = hash->sha; |
- hashItem.len = sizeof(hash->sha); |
+ /* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash. |
+ * In that case, we use just the SHA1 part. */ |
+ if (hash->hashAlg == SEC_OID_UNKNOWN) { |
+ hashItem.data = hash->u.s.sha; |
+ hashItem.len = sizeof(hash->u.s.sha); |
+ } else { |
+ hashItem.data = hash->u.raw; |
+ hashItem.len = hash->len; |
+ } |
/* Allow DER encoded DSA signatures in SSL 3.0 */ |
if (isTLS || buf->len != SECKEY_SignatureLen(key)) { |
signature = DSAU_DecodeDerSig(buf); |
@@ -914,8 +1044,15 @@ |
#ifdef NSS_ENABLE_ECC |
case ecKey: |
- hashItem.data = hash->sha; |
- hashItem.len = sizeof(hash->sha); |
+ /* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash. |
+ * In that case, we use just the SHA1 part. */ |
+ if (hash->hashAlg == SEC_OID_UNKNOWN) { |
+ hashItem.data = hash->u.s.sha; |
+ hashItem.len = sizeof(hash->u.s.sha); |
+ } else { |
+ hashItem.data = hash->u.raw; |
+ hashItem.len = hash->len; |
+ } |
/* |
* ECDSA signatures always encode the integers r and s |
* using ASN (unlike DSA where ASN encoding is used |
@@ -961,33 +1098,66 @@ |
/* Called from ssl3_ComputeExportRSAKeyHash |
* ssl3_ComputeDHKeyHash |
* which are called from ssl3_HandleServerKeyExchange. |
+ * |
+ * hashAlg: either the OID for a hash function or SEC_OID_UNKNOWN to specify |
+ * the pre-1.2, MD5/SHA1 combination hash. |
*/ |
SECStatus |
-ssl3_ComputeCommonKeyHash(PRUint8 * hashBuf, unsigned int bufLen, |
- SSL3Hashes *hashes, PRBool bypassPKCS11) |
+ssl3_ComputeCommonKeyHash(SECOidTag hashAlg, |
+ PRUint8 * hashBuf, unsigned int bufLen, |
+ SSL3Hashes *hashes, PRBool bypassPKCS11) |
{ |
SECStatus rv = SECSuccess; |
#ifndef NO_PKCS11_BYPASS |
if (bypassPKCS11) { |
- MD5_HashBuf (hashes->md5, hashBuf, bufLen); |
- SHA1_HashBuf(hashes->sha, hashBuf, bufLen); |
+ if (hashAlg == SEC_OID_UNKNOWN) { |
+ MD5_HashBuf (hashes->u.s.md5, hashBuf, bufLen); |
+ SHA1_HashBuf(hashes->u.s.sha, hashBuf, bufLen); |
+ hashes->len = MD5_LENGTH + SHA1_LENGTH; |
+ } else if (hashAlg == SEC_OID_SHA1) { |
+ SHA1_HashBuf(hashes->u.raw, hashBuf, bufLen); |
+ hashes->len = SHA1_LENGTH; |
+ } else if (hashAlg == SEC_OID_SHA256) { |
+ SHA256_HashBuf(hashes->u.raw, hashBuf, bufLen); |
+ hashes->len = SHA256_LENGTH; |
+ } else { |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
} else |
#endif |
{ |
- rv = PK11_HashBuf(SEC_OID_MD5, hashes->md5, hashBuf, bufLen); |
- if (rv != SECSuccess) { |
- ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
- rv = SECFailure; |
- goto done; |
- } |
+ if (hashAlg == SEC_OID_UNKNOWN) { |
+ rv = PK11_HashBuf(SEC_OID_MD5, hashes->u.s.md5, hashBuf, bufLen); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
+ rv = SECFailure; |
+ goto done; |
+ } |
- rv = PK11_HashBuf(SEC_OID_SHA1, hashes->sha, hashBuf, bufLen); |
- if (rv != SECSuccess) { |
- ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
- rv = SECFailure; |
+ rv = PK11_HashBuf(SEC_OID_SHA1, hashes->u.s.sha, hashBuf, bufLen); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
+ rv = SECFailure; |
+ } |
+ hashes->len = MD5_LENGTH + SHA1_LENGTH; |
+ } else { |
+ hashes->len = HASH_ResultLenByOidTag(hashAlg); |
+ if (hashes->len > sizeof(hashes->u.raw)) { |
+ ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); |
+ rv = SECFailure; |
+ } else { |
+ rv = PK11_HashBuf(hashAlg, hashes->u.raw, hashBuf, bufLen); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); |
+ rv = SECFailure; |
+ } |
+ } |
} |
} |
+ hashes->hashAlg = hashAlg; |
+ |
done: |
return rv; |
} |
@@ -997,7 +1167,8 @@ |
** ssl3_HandleServerKeyExchange. |
*/ |
static SECStatus |
-ssl3_ComputeExportRSAKeyHash(SECItem modulus, SECItem publicExponent, |
+ssl3_ComputeExportRSAKeyHash(SECOidTag hashAlg, |
+ SECItem modulus, SECItem publicExponent, |
SSL3Random *client_rand, SSL3Random *server_rand, |
SSL3Hashes *hashes, PRBool bypassPKCS11) |
{ |
@@ -1033,11 +1204,15 @@ |
pBuf += publicExponent.len; |
PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen); |
- rv = ssl3_ComputeCommonKeyHash(hashBuf, bufLen, hashes, bypassPKCS11); |
+ rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes, bypassPKCS11); |
PRINT_BUF(95, (NULL, "RSAkey hash: ", hashBuf, bufLen)); |
- PRINT_BUF(95, (NULL, "RSAkey hash: MD5 result", hashes->md5, MD5_LENGTH)); |
- PRINT_BUF(95, (NULL, "RSAkey hash: SHA1 result", hashes->sha, SHA1_LENGTH)); |
+ if (hashAlg == SEC_OID_UNKNOWN) { |
+ PRINT_BUF(95, (NULL, "RSAkey hash: MD5 result", hashes->u.s.md5, MD5_LENGTH)); |
+ PRINT_BUF(95, (NULL, "RSAkey hash: SHA1 result", hashes->u.s.sha, SHA1_LENGTH)); |
+ } else { |
+ PRINT_BUF(95, (NULL, "RSAkey hash: ", hashes->u.raw, hashes->len)); |
+ } |
if (hashBuf != buf && hashBuf != NULL) |
PORT_Free(hashBuf); |
@@ -1047,9 +1222,10 @@ |
/* Caller must set hiLevel error code. */ |
/* Called from ssl3_HandleServerKeyExchange. */ |
static SECStatus |
-ssl3_ComputeDHKeyHash(SECItem dh_p, SECItem dh_g, SECItem dh_Ys, |
- SSL3Random *client_rand, SSL3Random *server_rand, |
- SSL3Hashes *hashes, PRBool bypassPKCS11) |
+ssl3_ComputeDHKeyHash(SECOidTag hashAlg, |
+ SECItem dh_p, SECItem dh_g, SECItem dh_Ys, |
+ SSL3Random *client_rand, SSL3Random *server_rand, |
+ SSL3Hashes *hashes, PRBool bypassPKCS11) |
{ |
PRUint8 * hashBuf; |
PRUint8 * pBuf; |
@@ -1088,11 +1264,15 @@ |
pBuf += dh_Ys.len; |
PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen); |
- rv = ssl3_ComputeCommonKeyHash(hashBuf, bufLen, hashes, bypassPKCS11); |
+ rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes, bypassPKCS11); |
PRINT_BUF(95, (NULL, "DHkey hash: ", hashBuf, bufLen)); |
- PRINT_BUF(95, (NULL, "DHkey hash: MD5 result", hashes->md5, MD5_LENGTH)); |
- PRINT_BUF(95, (NULL, "DHkey hash: SHA1 result", hashes->sha, SHA1_LENGTH)); |
+ if (hashAlg == SEC_OID_UNKNOWN) { |
+ PRINT_BUF(95, (NULL, "DHkey hash: MD5 result", hashes->u.s.md5, MD5_LENGTH)); |
+ PRINT_BUF(95, (NULL, "DHkey hash: SHA1 result", hashes->u.s.sha, SHA1_LENGTH)); |
+ } else { |
+ PRINT_BUF(95, (NULL, "DHkey hash: ", hashes->u.raw, hashes->len)); |
+ } |
if (hashBuf != buf && hashBuf != NULL) |
PORT_Free(hashBuf); |
@@ -3190,6 +3370,8 @@ |
unsigned char * sr = (unsigned char *)&ss->ssl3.hs.server_random; |
PRBool isTLS = (PRBool)(kea_def->tls_keygen || |
(pwSpec->version > SSL_LIBRARY_VERSION_3_0)); |
+ PRBool isTLS12= |
+ (PRBool)(isTLS && pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
/* |
* Whenever isDH is true, we need to use CKM_TLS_MASTER_KEY_DERIVE_DH |
* which, unlike CKM_TLS_MASTER_KEY_DERIVE, converts arbitrary size |
@@ -3208,7 +3390,12 @@ |
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); |
PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec); |
- if (isTLS) { |
+ if (isTLS12) { |
+ if(isDH) master_derive = CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256; |
+ else master_derive = CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256; |
+ key_derive = CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256; |
+ keyFlags = CKF_SIGN | CKF_VERIFY; |
+ } else if (isTLS) { |
if(isDH) master_derive = CKM_TLS_MASTER_KEY_DERIVE_DH; |
else master_derive = CKM_TLS_MASTER_KEY_DERIVE; |
key_derive = CKM_TLS_KEY_AND_MAC_DERIVE; |
@@ -3366,6 +3553,8 @@ |
unsigned char * sr = (unsigned char *)&ss->ssl3.hs.server_random; |
PRBool isTLS = (PRBool)(kea_def->tls_keygen || |
(pwSpec->version > SSL_LIBRARY_VERSION_3_0)); |
+ PRBool isTLS12= |
+ (PRBool)(isTLS && pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
/* following variables used in PKCS11 path */ |
const ssl3BulkCipherDef *cipher_def = pwSpec->cipher_def; |
PK11SlotInfo * slot = NULL; |
@@ -3423,7 +3612,9 @@ |
params.data = (unsigned char *)&key_material_params; |
params.len = sizeof(key_material_params); |
- if (isTLS) { |
+ if (isTLS12) { |
+ key_derive = CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256; |
+ } else if (isTLS) { |
key_derive = CKM_TLS_KEY_AND_MAC_DERIVE; |
} else { |
key_derive = CKM_SSL3_KEY_AND_MAC_DERIVE; |
@@ -3480,19 +3671,64 @@ |
return SECFailure; |
} |
+/* ssl3_InitTLS12HandshakeHash creates a handshake hash function for TLS 1.2, |
+ * if needed, and hashes in any buffered messages in ss->ssl3.hs.messages. */ |
+static SECStatus |
+ssl3_InitTLS12HandshakeHash(sslSocket *ss) |
+{ |
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2 && |
+ ss->ssl3.hs.tls12_handshake_hash == NULL) { |
+ /* If we ever support ciphersuites where the PRF hash isn't SHA-256 |
+ * then this will need to be updated. */ |
+ ss->ssl3.hs.tls12_handshake_hash = |
+ PK11_CreateDigestContext(SEC_OID_SHA256); |
+ if (!ss->ssl3.hs.tls12_handshake_hash || |
+ SECSuccess != PK11_DigestBegin(ss->ssl3.hs.tls12_handshake_hash)) { |
+ ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ } |
+ |
+ if (ss->ssl3.hs.tls12_handshake_hash && ss->ssl3.hs.messages.len > 0) { |
+ if (SECSuccess != PK11_DigestOp( |
+ ss->ssl3.hs.tls12_handshake_hash, |
+ ss->ssl3.hs.messages.buf, |
+ ss->ssl3.hs.messages.len)) { |
+ ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ } |
+ |
+ if (ss->ssl3.hs.messages.buf && !ss->opt.bypassPKCS11) { |
+ PORT_Free(ss->ssl3.hs.messages.buf); |
+ ss->ssl3.hs.messages.buf = NULL; |
+ ss->ssl3.hs.messages.len = 0; |
+ ss->ssl3.hs.messages.space = 0; |
+ } |
+ |
+ return SECSuccess; |
+} |
+ |
static SECStatus |
ssl3_RestartHandshakeHashes(sslSocket *ss) |
{ |
SECStatus rv = SECSuccess; |
+ ss->ssl3.hs.messages.len = 0; |
#ifndef NO_PKCS11_BYPASS |
if (ss->opt.bypassPKCS11) { |
- ss->ssl3.hs.messages.len = 0; |
MD5_Begin((MD5Context *)ss->ssl3.hs.md5_cx); |
SHA1_Begin((SHA1Context *)ss->ssl3.hs.sha_cx); |
} else |
#endif |
{ |
+ if (ss->ssl3.hs.tls12_handshake_hash) { |
+ rv = PK11_DigestBegin(ss->ssl3.hs.tls12_handshake_hash); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); |
+ return rv; |
+ } |
+ } |
rv = PK11_DigestBegin(ss->ssl3.hs.md5); |
if (rv != SECSuccess) { |
ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
@@ -3519,25 +3755,21 @@ |
* that the master secret will wind up in ... |
*/ |
SSL_TRC(30,("%d: SSL3[%d]: start handshake hashes", SSL_GETPID(), ss->fd)); |
-#ifndef NO_PKCS11_BYPASS |
- if (ss->opt.bypassPKCS11) { |
- PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space); |
- ss->ssl3.hs.messages.buf = NULL; |
- ss->ssl3.hs.messages.space = 0; |
- } else |
-#endif |
- { |
- ss->ssl3.hs.md5 = md5 = PK11_CreateDigestContext(SEC_OID_MD5); |
- ss->ssl3.hs.sha = sha = PK11_CreateDigestContext(SEC_OID_SHA1); |
- if (md5 == NULL) { |
- ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
- goto loser; |
- } |
- if (sha == NULL) { |
- ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
- goto loser; |
- } |
+ PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space); |
+ ss->ssl3.hs.messages.buf = NULL; |
+ ss->ssl3.hs.messages.space = 0; |
+ |
+ ss->ssl3.hs.md5 = md5 = PK11_CreateDigestContext(SEC_OID_MD5); |
+ ss->ssl3.hs.sha = sha = PK11_CreateDigestContext(SEC_OID_SHA1); |
+ ss->ssl3.hs.tls12_handshake_hash = NULL; |
+ if (md5 == NULL) { |
+ ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
+ goto loser; |
} |
+ if (sha == NULL) { |
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
+ goto loser; |
+ } |
if (SECSuccess == ssl3_RestartHandshakeHashes(ss)) { |
return SECSuccess; |
} |
@@ -3574,6 +3806,17 @@ |
PRINT_BUF(90, (NULL, "MD5 & SHA handshake hash input:", b, l)); |
+ if ((ss->version == 0 || ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) && |
+ !ss->opt.bypassPKCS11 && |
+ ss->ssl3.hs.tls12_handshake_hash == NULL) { |
+ /* For TLS 1.2 connections we need to buffer the handshake messages |
+ * until we have established which PRF hash function to use. */ |
+ rv = sslBuffer_Append(&ss->ssl3.hs.messages, b, l); |
+ if (rv != SECSuccess) { |
+ return rv; |
+ } |
+ } |
+ |
#ifndef NO_PKCS11_BYPASS |
if (ss->opt.bypassPKCS11) { |
MD5_Update((MD5Context *)ss->ssl3.hs.md5_cx, b, l); |
@@ -3584,16 +3827,20 @@ |
return rv; |
} |
#endif |
- rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l); |
- if (rv != SECSuccess) { |
- ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
- return rv; |
+ if (ss->ssl3.hs.tls12_handshake_hash) { |
+ rv = PK11_DigestOp(ss->ssl3.hs.tls12_handshake_hash, b, l); |
+ } else { |
+ rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
+ return rv; |
+ } |
+ rv = PK11_DigestOp(ss->ssl3.hs.sha, b, l); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
+ return rv; |
+ } |
} |
- rv = PK11_DigestOp(ss->ssl3.hs.sha, b, l); |
- if (rv != SECSuccess) { |
- ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
- return rv; |
- } |
return rv; |
} |
@@ -3744,6 +3991,25 @@ |
return rv; /* error code set by AppendHandshake, if applicable. */ |
} |
+/* ssl3_AppendSignatureAndHashAlgorithm appends the serialisation of |
+ * |sigAndHash| to the current handshake message. */ |
+SECStatus |
+ssl3_AppendSignatureAndHashAlgorithm( |
+ sslSocket *ss, const SSL3SignatureAndHashAlgorithm* sigAndHash) |
+{ |
+ unsigned char serialized[2]; |
+ |
+ serialized[0] = ssl3_OIDToTLSHashFunction(sigAndHash->hashAlg); |
+ if (serialized[0] == 0) { |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ |
+ serialized[1] = sigAndHash->sigAlg; |
+ |
+ return ssl3_AppendHandshake(ss, serialized, sizeof(serialized)); |
+} |
+ |
/************************************************************************** |
* Consume Handshake functions. |
* |
@@ -3850,6 +4116,145 @@ |
return SECSuccess; |
} |
+/* tls12HashOIDMap contains the mapping between TLS hash identifiers and the |
+ * SECOidTag used internally by NSS. */ |
+static const struct { |
+ int tlsHash; |
+ SECOidTag oid; |
+} tls12HashOIDMap[] = { |
+ { tls_hash_md5, SEC_OID_MD5 }, |
+ { tls_hash_sha1, SEC_OID_SHA1 }, |
+ { tls_hash_sha224, SEC_OID_SHA224 }, |
+ { tls_hash_sha256, SEC_OID_SHA256 }, |
+ { tls_hash_sha384, SEC_OID_SHA384 }, |
+ { tls_hash_sha512, SEC_OID_SHA512 } |
+}; |
+ |
+/* ssl3_TLSHashFunctionToOID converts a TLS hash identifier into an OID value. |
+ * If the hash is not recognised, SEC_OID_UNKNOWN is returned. |
+ * |
+ * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ |
+SECOidTag ssl3_TLSHashFunctionToOID(int hashFunc) |
+{ |
+ size_t i; |
+ |
+ for (i = 0; i < sizeof(tls12HashOIDMap) / sizeof(tls12HashOIDMap[0]); i++) { |
+ if (hashFunc == tls12HashOIDMap[i].tlsHash) { |
+ return tls12HashOIDMap[i].oid; |
+ } |
+ } |
+ return SEC_OID_UNKNOWN; |
+} |
+ |
+/* ssl3_OIDToTLSHashFunction converts an OID to a TLS hash function identifier. |
+ * If the hash is not recognised, zero is returned. |
+ * |
+ * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ |
+int ssl3_OIDToTLSHashFunction(SECOidTag oid) |
+{ |
+ size_t i; |
+ |
+ for (i = 0; i < sizeof(tls12HashOIDMap) / sizeof(tls12HashOIDMap[0]); i++) { |
+ if (oid == tls12HashOIDMap[i].oid) { |
+ return tls12HashOIDMap[i].tlsHash; |
+ } |
+ } |
+ return 0; |
+} |
+ |
+/* ssl3_TLSSignatureAlgorithmForKeyType returns the TLS signature algorithm |
+ * identifier for a given KeyType. */ |
+static SECStatus ssl3_TLSSignatureAlgorithmForKeyType( |
+ KeyType keyType, TLS12SignatureAlgorithm *out) |
+{ |
+ switch (keyType) { |
+ case rsaKey: |
+ *out = tls_sig_rsa; |
+ return SECSuccess; |
+ case dsaKey: |
+ *out = tls_sig_dsa; |
+ return SECSuccess; |
+ case ecKey: |
+ *out = tls_sig_ecdsa; |
+ return SECSuccess; |
+ } |
+ PORT_SetError(SEC_ERROR_INVALID_KEY); |
+ return SECFailure; |
+} |
+ |
+/* ssl3_TLSSignatureAlgorithmForKey returns the TLS 1.2 signature algorithm |
+ * identifier for the given private key. */ |
+static SECStatus ssl3_TLSSignatureAlgorithmForPrivateKey( |
+ SECKEYPrivateKey *key, TLS12SignatureAlgorithm *out) |
+{ |
+ return ssl3_TLSSignatureAlgorithmForKeyType(key->keyType, out); |
+} |
+ |
+/* ssl3_TLSSignatureAlgorithmForCert returns the TLS 1.2 signature algorithm |
+ * identifier for the given certificate. */ |
+static SECStatus ssl3_TLSSignatureAlgorithmForCertificate( |
+ CERTCertificate *cert, TLS12SignatureAlgorithm *out) |
+{ |
+ SECKEYPublicKey *key; |
+ |
+ key = CERT_ExtractPublicKey(cert); |
+ if (key == NULL) { |
+ ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); |
+ return SECFailure; |
+ } |
+ |
+ return ssl3_TLSSignatureAlgorithmForKeyType(key->keyType, out); |
+} |
+ |
+/* ssl3_CheckSignatureAndHashAlgorithmConsistency checks that the signature |
+ * algorithm identifier in |sigAndHash| is consistent with the public key in |
+ * |cert|. If so, SECSuccess is returned. Otherwise, PORT_SetError is called |
+ * and SECFailure is returned. */ |
+SECStatus ssl3_CheckSignatureAndHashAlgorithmConsistency( |
+ const SSL3SignatureAndHashAlgorithm *sigAndHash, CERTCertificate* cert) |
+{ |
+ SECStatus rv; |
+ TLS12SignatureAlgorithm sigAlg; |
+ |
+ rv = ssl3_TLSSignatureAlgorithmForCertificate(cert, &sigAlg); |
+ if (rv != SECSuccess) { |
+ return SECFailure; |
+ } |
+ if (sigAlg != sigAndHash->sigAlg) { |
+ PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); |
+ return SECFailure; |
+ } |
+ return SECSuccess; |
+} |
+ |
+/* ssl3_ConsumeSignatureAndHashAlgorithm reads a SignatureAndHashAlgorithm |
+ * structure from |b| and puts the resulting value into |out|. |b| and |length| |
+ * are updated accordingly. |
+ * |
+ * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ |
+SECStatus |
+ssl3_ConsumeSignatureAndHashAlgorithm(sslSocket *ss, |
+ SSL3Opaque **b, |
+ PRUint32 *length, |
+ SSL3SignatureAndHashAlgorithm *out) { |
+ unsigned char bytes[2]; |
+ SECStatus rv; |
+ |
+ rv = ssl3_ConsumeHandshake(ss, bytes, sizeof(bytes), b, length); |
+ if (rv != SECSuccess) { |
+ return rv; |
+ } |
+ |
+ out->hashAlg = ssl3_TLSHashFunctionToOID(bytes[0]); |
+ if (out->hashAlg == SEC_OID_UNKNOWN) { |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ |
+ out->sigAlg = bytes[1]; |
+ return SECSuccess; |
+} |
+ |
/************************************************************************** |
* end of Consume Handshake functions. |
**************************************************************************/ |
@@ -3876,6 +4281,7 @@ |
SSL3Opaque sha_inner[MAX_MAC_LENGTH]; |
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); |
+ hashes->hashAlg = SEC_OID_UNKNOWN; |
#ifndef NO_PKCS11_BYPASS |
if (ss->opt.bypassPKCS11) { |
@@ -3939,9 +4345,9 @@ |
MD5_Update(md5cx, mac_pad_2, mac_defs[mac_md5].pad_size); |
MD5_Update(md5cx, md5_inner, MD5_LENGTH); |
} |
- MD5_End(md5cx, hashes->md5, &outLength, MD5_LENGTH); |
+ MD5_End(md5cx, hashes->u.s.md5, &outLength, MD5_LENGTH); |
- PRINT_BUF(60, (NULL, "MD5 outer: result", hashes->md5, MD5_LENGTH)); |
+ PRINT_BUF(60, (NULL, "MD5 outer: result", hashes->u.s.md5, MD5_LENGTH)); |
if (!isTLS) { |
PRINT_BUF(95, (NULL, "SHA outer: MAC Pad 2", mac_pad_2, |
@@ -3953,16 +4359,58 @@ |
SHA1_Update(shacx, mac_pad_2, mac_defs[mac_sha].pad_size); |
SHA1_Update(shacx, sha_inner, SHA1_LENGTH); |
} |
- SHA1_End(shacx, hashes->sha, &outLength, SHA1_LENGTH); |
+ SHA1_End(shacx, hashes->u.s.sha, &outLength, SHA1_LENGTH); |
- PRINT_BUF(60, (NULL, "SHA outer: result", hashes->sha, SHA1_LENGTH)); |
+ PRINT_BUF(60, (NULL, "SHA outer: result", hashes->u.s.sha, SHA1_LENGTH)); |
+ hashes->len = MD5_LENGTH + SHA1_LENGTH; |
rv = SECSuccess; |
#undef md5cx |
#undef shacx |
} else |
#endif |
- { |
+ if (ss->ssl3.hs.tls12_handshake_hash) { |
+ PK11Context *h; |
+ unsigned int stateLen; |
+ unsigned char stackBuf[1024]; |
+ unsigned char *stateBuf = NULL; |
+ |
+ if (!spec->master_secret) { |
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); |
+ return SECFailure; |
+ } |
+ |
+ h = ss->ssl3.hs.tls12_handshake_hash; |
+ stateBuf = PK11_SaveContextAlloc(h, stackBuf, |
+ sizeof(stackBuf), &stateLen); |
+ if (stateBuf == NULL) { |
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); |
+ goto tls12Loser; |
+ } |
+ rv |= PK11_DigestFinal(h, hashes->u.raw, &hashes->len, |
+ sizeof(hashes->u.raw)); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); |
+ rv = SECFailure; |
+ goto tls12Loser; |
+ } |
+ /* If we ever support ciphersuites where the PRF hash isn't SHA-256 |
+ * then this will need to be updated. */ |
+ hashes->hashAlg = SEC_OID_SHA256; |
+ rv = SECSuccess; |
+ |
+tls12Loser: |
+ if (stateBuf) { |
+ if (PK11_RestoreContext(ss->ssl3.hs.tls12_handshake_hash, stateBuf, |
+ stateLen) != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); |
+ rv = SECFailure; |
+ } |
+ if (stateBuf != stackBuf) { |
+ PORT_ZFree(stateBuf, stateLen); |
+ } |
+ } |
+ } else { |
/* compute hases with PKCS11 */ |
PK11Context * md5; |
PK11Context * sha = NULL; |
@@ -4051,7 +4499,7 @@ |
rv |= PK11_DigestOp(md5, mac_pad_2, mac_defs[mac_md5].pad_size); |
rv |= PK11_DigestOp(md5, md5_inner, MD5_LENGTH); |
} |
- rv |= PK11_DigestFinal(md5, hashes->md5, &outLength, MD5_LENGTH); |
+ rv |= PK11_DigestFinal(md5, hashes->u.s.md5, &outLength, MD5_LENGTH); |
PORT_Assert(rv != SECSuccess || outLength == MD5_LENGTH); |
if (rv != SECSuccess) { |
ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); |
@@ -4059,7 +4507,7 @@ |
goto loser; |
} |
- PRINT_BUF(60, (NULL, "MD5 outer: result", hashes->md5, MD5_LENGTH)); |
+ PRINT_BUF(60, (NULL, "MD5 outer: result", hashes->u.s.md5, MD5_LENGTH)); |
if (!isTLS) { |
PRINT_BUF(95, (NULL, "SHA outer: MAC Pad 2", mac_pad_2, |
@@ -4071,7 +4519,7 @@ |
rv |= PK11_DigestOp(sha, mac_pad_2, mac_defs[mac_sha].pad_size); |
rv |= PK11_DigestOp(sha, sha_inner, SHA1_LENGTH); |
} |
- rv |= PK11_DigestFinal(sha, hashes->sha, &outLength, SHA1_LENGTH); |
+ rv |= PK11_DigestFinal(sha, hashes->u.s.sha, &outLength, SHA1_LENGTH); |
PORT_Assert(rv != SECSuccess || outLength == SHA1_LENGTH); |
if (rv != SECSuccess) { |
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); |
@@ -4079,8 +4527,9 @@ |
goto loser; |
} |
- PRINT_BUF(60, (NULL, "SHA outer: result", hashes->sha, SHA1_LENGTH)); |
+ PRINT_BUF(60, (NULL, "SHA outer: result", hashes->u.s.sha, SHA1_LENGTH)); |
+ hashes->len = MD5_LENGTH + SHA1_LENGTH; |
rv = SECSuccess; |
loser: |
@@ -5343,8 +5792,11 @@ |
{ |
SECStatus rv = SECFailure; |
PRBool isTLS; |
+ PRBool isTLS12; |
SECItem buf = {siBuffer, NULL, 0}; |
SSL3Hashes hashes; |
+ unsigned int len; |
+ SSL3SignatureAndHashAlgorithm sigAndHash; |
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
@@ -5360,6 +5812,7 @@ |
} |
isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0); |
+ isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
if (ss->ssl3.platformClientKey) { |
#ifdef NSS_PLATFORM_CLIENT_AUTH |
rv = ssl3_PlatformSignHashes( |
@@ -5385,6 +5838,11 @@ |
sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot); |
sid->u.ssl3.clAuthValid = PR_TRUE; |
PK11_FreeSlot(slot); |
+ |
+ if (isTLS12) { |
+ rv = ssl3_TLSSignatureAlgorithmForPrivateKey( |
+ ss->ssl3.clientPrivateKey, &sigAndHash.sigAlg); |
+ } |
} |
SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); |
ss->ssl3.clientPrivateKey = NULL; |
@@ -5393,10 +5851,25 @@ |
goto done; /* err code was set by ssl3_SignHashes */ |
} |
- rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, buf.len + 2); |
+ len = buf.len + 2 + (isTLS12 ? 2 : 0); |
+ |
+ rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, len); |
if (rv != SECSuccess) { |
goto done; /* error code set by AppendHandshake */ |
} |
+ if (isTLS12) { |
+ /* 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); |
+ if (rv != SECSuccess) { |
+ goto done; /* err set by AppendHandshake. */ |
+ } |
+ } |
rv = ssl3_AppendHandshakeVariable(ss, buf.data, buf.len, 2); |
if (rv != SECSuccess) { |
goto done; /* error code set by AppendHandshake */ |
@@ -5504,6 +5977,12 @@ |
} |
isTLS = (ss->version > SSL_LIBRARY_VERSION_3_0); |
+ rv = ssl3_InitTLS12HandshakeHash(ss); |
+ if (rv != SECSuccess) { |
+ desc = internal_error; |
+ goto alert_loser; |
+ } |
+ |
rv = ssl3_ConsumeHandshake( |
ss, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH, &b, &length); |
if (rv != SECSuccess) { |
@@ -5840,7 +6319,10 @@ |
SSL3AlertDescription desc = illegal_parameter; |
SSL3Hashes hashes; |
SECItem signature = {siBuffer, NULL, 0}; |
+ SSL3SignatureAndHashAlgorithm sigAndHash; |
+ sigAndHash.hashAlg = SEC_OID_UNKNOWN; |
+ |
SSL_TRC(3, ("%d: SSL3[%d]: handle server_key_exchange handshake", |
SSL_GETPID(), ss->fd)); |
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
@@ -5874,6 +6356,17 @@ |
if (rv != SECSuccess) { |
goto loser; /* malformed. */ |
} |
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { |
+ rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length, &sigAndHash); |
+ if (rv != SECSuccess) { |
+ goto loser; /* malformed or unsupported. */ |
+ } |
+ rv = ssl3_CheckSignatureAndHashAlgorithmConsistency( |
+ &sigAndHash, ss->sec.peerCert); |
+ if (rv != SECSuccess) { |
+ goto loser; |
+ } |
+ } |
rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length); |
if (rv != SECSuccess) { |
goto loser; /* malformed. */ |
@@ -5891,7 +6384,7 @@ |
/* |
* check to make sure the hash is signed by right guy |
*/ |
- rv = ssl3_ComputeExportRSAKeyHash(modulus, exponent, |
+ rv = ssl3_ComputeExportRSAKeyHash(sigAndHash.hashAlg, modulus, exponent, |
&ss->ssl3.hs.client_random, |
&ss->ssl3.hs.server_random, |
&hashes, ss->opt.bypassPKCS11); |
@@ -5964,6 +6457,17 @@ |
} |
if (dh_Ys.len > dh_p.len || !ssl3_BigIntGreaterThanOne(&dh_Ys)) |
goto alert_loser; |
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { |
+ rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length, &sigAndHash); |
+ if (rv != SECSuccess) { |
+ goto loser; /* malformed or unsupported. */ |
+ } |
+ rv = ssl3_CheckSignatureAndHashAlgorithmConsistency( |
+ &sigAndHash, ss->sec.peerCert); |
+ if (rv != SECSuccess) { |
+ goto loser; |
+ } |
+ } |
rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length); |
if (rv != SECSuccess) { |
goto loser; /* malformed. */ |
@@ -5985,7 +6489,7 @@ |
/* |
* check to make sure the hash is signed by right guy |
*/ |
- rv = ssl3_ComputeDHKeyHash(dh_p, dh_g, dh_Ys, |
+ rv = ssl3_ComputeDHKeyHash(sigAndHash.hashAlg, dh_p, dh_g, dh_Ys, |
&ss->ssl3.hs.client_random, |
&ss->ssl3.hs.server_random, |
&hashes, ss->opt.bypassPKCS11); |
@@ -6862,6 +7366,12 @@ |
goto alert_loser; |
} |
+ rv = ssl3_InitTLS12HandshakeHash(ss); |
+ if (rv != SECSuccess) { |
+ desc = internal_error; |
+ goto alert_loser; |
+ } |
+ |
/* grab the client random data. */ |
rv = ssl3_ConsumeHandshake( |
ss, &ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH, &b, &length); |
@@ -7703,6 +8213,12 @@ |
ss->sec.ci.sid = sid; |
/* do not worry about memory leak of sid since it now belongs to ci */ |
+ rv = ssl3_InitTLS12HandshakeHash(ss); |
+ if (rv != SECSuccess) { |
+ desc = internal_error; |
+ goto alert_loser; |
+ } |
+ |
/* We have to update the handshake hashes before we can send stuff */ |
rv = ssl3_UpdateHandshakeHashes(ss, buffer, length); |
if (rv != SECSuccess) { |
@@ -7851,7 +8367,83 @@ |
return SECSuccess; |
} |
+/* ssl3_PickSignatureHashFunction selects a hash function to use when signing |
+ * elements of the handshake. Prior to TLS 1.2, the MD5/SHA1 combination is |
+ * always used. With TLS 1.2, a client may advertise its support for signature |
+ * and hash combinations. */ |
+SECStatus |
+ssl3_PickSignatureHashFunction(sslSocket *ss, |
+ SSL3SignatureAndHashAlgorithm* out) |
+{ |
+ TLS12SignatureAlgorithm sigAlg; |
+ unsigned int i, j; |
+ /* hashPreference expresses our preferences for hash functions, most |
+ * preferable first. */ |
+ static const PRUint8 hashPreference[] = { |
+ tls_hash_sha256, |
+ tls_hash_sha384, |
+ tls_hash_sha512, |
+ tls_hash_sha1, |
+ }; |
+ switch (ss->ssl3.hs.kea_def->kea) { |
+ case kea_rsa: |
+ case kea_rsa_export: |
+ case kea_rsa_export_1024: |
+ case kea_dhe_rsa: |
+ case kea_dhe_rsa_export: |
+ case kea_rsa_fips: |
+ case kea_ecdh_rsa: |
+ case kea_ecdhe_rsa: |
+ sigAlg = tls_sig_rsa; |
+ break; |
+ case kea_dh_dss: |
+ case kea_dh_dss_export: |
+ case kea_dhe_dss: |
+ case kea_dhe_dss_export: |
+ sigAlg = tls_sig_dsa; |
+ break; |
+ case kea_ecdh_ecdsa: |
+ case kea_ecdhe_ecdsa: |
+ sigAlg = tls_sig_ecdsa; |
+ break; |
+ default: |
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); |
+ return SECFailure; |
+ } |
+ out->sigAlg = sigAlg; |
+ |
+ if (ss->version <= SSL_LIBRARY_VERSION_TLS_1_1) { |
+ /* SEC_OID_UNKNOWN means the MD5/SHA1 combo hash used in TLS 1.1 and |
+ * prior. */ |
+ out->hashAlg = SEC_OID_UNKNOWN; |
+ return SECSuccess; |
+ } |
+ |
+ if (ss->ssl3.hs.numClientSigAndHash == 0) { |
+ /* If the client didn't provide any signature_algorithms extension then |
+ * we can assume that they support SHA-1: |
+ * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ |
+ out->hashAlg = SEC_OID_SHA1; |
+ return SECSuccess; |
+ } |
+ |
+ |
+ for (i = 0; i < sizeof(hashPreference); i++) { |
+ for (j = 0; j < ss->ssl3.hs.numClientSigAndHash; j++) { |
+ const SSL3SignatureAndHashAlgorithm* sh = |
+ &ss->ssl3.hs.clientSigAndHash[j]; |
+ if (sh->sigAlg == sigAlg && sh->hashAlg == hashPreference[i]) { |
+ *out = *sh; |
+ return SECSuccess; |
+ } |
+ } |
+ } |
+ |
+ return SECFailure; |
+} |
+ |
+ |
static SECStatus |
ssl3_SendServerKeyExchange(sslSocket *ss) |
{ |
@@ -7862,6 +8454,7 @@ |
SECItem signed_hash = {siBuffer, NULL, 0}; |
SSL3Hashes hashes; |
SECKEYPublicKey * sdPub; /* public key for step-down */ |
+ SSL3SignatureAndHashAlgorithm sigAndHash; |
SSL_TRC(3, ("%d: SSL3[%d]: send server_key_exchange handshake", |
SSL_GETPID(), ss->fd)); |
@@ -7869,6 +8462,11 @@ |
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
+ if (SECSuccess != ssl3_PickSignatureHashFunction(ss, &sigAndHash)) { |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ |
switch (kea_def->exchKeyType) { |
case kt_rsa: |
/* Perform SSL Step-Down here. */ |
@@ -7878,7 +8476,8 @@ |
PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); |
return SECFailure; |
} |
- rv = ssl3_ComputeExportRSAKeyHash(sdPub->u.rsa.modulus, |
+ rv = ssl3_ComputeExportRSAKeyHash(sigAndHash.hashAlg, |
+ sdPub->u.rsa.modulus, |
sdPub->u.rsa.publicExponent, |
&ss->ssl3.hs.client_random, |
&ss->ssl3.hs.server_random, |
@@ -7921,6 +8520,13 @@ |
goto loser; /* err set by AppendHandshake. */ |
} |
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { |
+ rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash); |
+ if (rv != SECSuccess) { |
+ goto loser; /* err set by AppendHandshake. */ |
+ } |
+ } |
+ |
rv = ssl3_AppendHandshakeVariable(ss, signed_hash.data, |
signed_hash.len, 2); |
if (rv != SECSuccess) { |
@@ -8046,6 +8652,7 @@ |
int errCode = SSL_ERROR_RX_MALFORMED_CERT_VERIFY; |
SSL3AlertDescription desc = handshake_failure; |
PRBool isTLS; |
+ SSL3SignatureAndHashAlgorithm sigAndHash; |
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_verify handshake", |
SSL_GETPID(), ss->fd)); |
@@ -8058,6 +8665,26 @@ |
goto alert_loser; |
} |
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { |
+ rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length, |
+ &sigAndHash); |
+ if (rv != SECSuccess) { |
+ goto loser; /* malformed or unsupported. */ |
+ } |
+ rv = ssl3_CheckSignatureAndHashAlgorithmConsistency( |
+ &sigAndHash, ss->sec.peerCert); |
+ if (rv != SECSuccess) { |
+ goto loser; |
+ } |
+ |
+ /* We only support CertificateVerify messages that use the handshake |
+ * hash. */ |
+ if (sigAndHash.hashAlg != hashes->hashAlg) { |
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_FUNCTION); |
+ return SECFailure; |
+ } |
+ } |
+ |
rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length); |
if (rv != SECSuccess) { |
goto loser; /* malformed. */ |
@@ -9173,8 +9800,8 @@ |
label = isServer ? "server finished" : "client finished"; |
len = 15; |
- rv = ssl3_TLSPRFWithMasterSecret(spec, label, len, hashes->md5, |
- sizeof *hashes, tlsFinished->verify_data, |
+ rv = ssl3_TLSPRFWithMasterSecret(spec, label, len, hashes->u.raw, |
+ hashes->len, tlsFinished->verify_data, |
sizeof tlsFinished->verify_data); |
return rv; |
@@ -9193,9 +9820,13 @@ |
if (spec->master_secret && !spec->bypassCiphers) { |
SECItem param = {siBuffer, NULL, 0}; |
+ CK_MECHANISM_TYPE mech = CKM_TLS_PRF_GENERAL; |
+ if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_2) { |
+ mech = CKM_NSS_TLS_PRF_GENERAL_SHA256; |
+ } |
PK11Context *prf_context = |
- PK11_CreateContextBySymKey(CKM_TLS_PRF_GENERAL, CKA_SIGN, |
- spec->master_secret, ¶m); |
+ PK11_CreateContextBySymKey(mech, CKA_SIGN, spec->master_secret, |
+ ¶m); |
unsigned int retLen; |
if (!prf_context) |
@@ -9529,15 +10160,19 @@ |
if (rv != SECSuccess) |
goto fail; /* err set by AppendHandshake. */ |
} else { |
- if (isServer) |
- ss->ssl3.hs.finishedMsgs.sFinished[1] = hashes; |
- else |
- ss->ssl3.hs.finishedMsgs.sFinished[0] = hashes; |
- ss->ssl3.hs.finishedBytes = sizeof hashes; |
- rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof hashes); |
+ unsigned char *ssl3FinishedStorage; |
+ |
+ if (isServer) { |
+ ssl3FinishedStorage = &ss->ssl3.hs.finishedMsgs.sFinished[1][0]; |
+ } else { |
+ ssl3FinishedStorage = &ss->ssl3.hs.finishedMsgs.sFinished[0][0]; |
+ } |
+ memcpy(ssl3FinishedStorage, hashes.u.raw, hashes.len); |
+ ss->ssl3.hs.finishedBytes = hashes.len; |
+ rv = ssl3_AppendHandshakeHeader(ss, finished, hashes.len); |
if (rv != SECSuccess) |
goto fail; /* err set by AppendHandshake. */ |
- rv = ssl3_AppendHandshake(ss, &hashes, sizeof hashes); |
+ rv = ssl3_AppendHandshake(ss, hashes.u.raw, hashes.len); |
if (rv != SECSuccess) |
goto fail; /* err set by AppendHandshake. */ |
} |
@@ -9686,18 +10321,22 @@ |
return SECFailure; |
} |
} else { |
- if (length != sizeof(SSL3Hashes)) { |
+ unsigned char *ssl3FinishedStorage; |
+ |
+ if (length != hashes->len) { |
(void)ssl3_IllegalParameter(ss); |
PORT_SetError(SSL_ERROR_RX_MALFORMED_FINISHED); |
return SECFailure; |
} |
- if (!isServer) |
- ss->ssl3.hs.finishedMsgs.sFinished[1] = *hashes; |
- else |
- ss->ssl3.hs.finishedMsgs.sFinished[0] = *hashes; |
- ss->ssl3.hs.finishedBytes = sizeof *hashes; |
- if (0 != NSS_SecureMemcmp(hashes, b, length)) { |
+ if (!isServer) { |
+ ssl3FinishedStorage = &ss->ssl3.hs.finishedMsgs.sFinished[1][0]; |
+ } else { |
+ ssl3FinishedStorage = &ss->ssl3.hs.finishedMsgs.sFinished[0][0]; |
+ } |
+ memcpy(ssl3FinishedStorage, hashes->u.raw, hashes->len); |
+ ss->ssl3.hs.finishedBytes = hashes->len; |
+ if (0 != NSS_SecureMemcmp(hashes->u.raw, b, length)) { |
(void)ssl3_HandshakeFailure(ss); |
PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); |
return SECFailure; |
@@ -11286,6 +11925,12 @@ |
if (ss->ssl3.hs.sha) { |
PK11_DestroyContext(ss->ssl3.hs.sha,PR_TRUE); |
} |
+ if (ss->ssl3.hs.tls12_handshake_hash) { |
+ PK11_DestroyContext(ss->ssl3.hs.tls12_handshake_hash,PR_TRUE); |
+ } |
+ if (ss->ssl3.hs.clientSigAndHash) { |
+ PORT_Free(ss->ssl3.hs.clientSigAndHash); |
+ } |
if (ss->ssl3.hs.messages.buf) { |
PORT_Free(ss->ssl3.hs.messages.buf); |
ss->ssl3.hs.messages.buf = NULL; |