From 1c425d479c495d266c23876887198a54e82e7078 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 3 Oct 2011 12:22:24 -0400 Subject: [PATCH] cachedinfo.patch --- mozilla/security/nss/lib/ssl/fnv1a64.c | 72 +++++++++ mozilla/security/nss/lib/ssl/manifest.mn | 1 + mozilla/security/nss/lib/ssl/ssl.h | 26 +++ mozilla/security/nss/lib/ssl/ssl3con.c | 221 +++++++++++++++++++------ mozilla/security/nss/lib/ssl/ssl3ext.c | 258 ++++++++++++++++++++++++++++++ mozilla/security/nss/lib/ssl/sslauth.c | 40 +++++ mozilla/security/nss/lib/ssl/sslimpl.h | 33 ++++- mozilla/security/nss/lib/ssl/sslsock.c | 11 ++ mozilla/security/nss/lib/ssl/sslt.h | 3 +- 9 files changed, 611 insertions(+), 54 deletions(-) create mode 100644 mozilla/security/nss/lib/ssl/fnv1a64.c diff --git a/mozilla/security/nss/lib/ssl/fnv1a64.c b/mozilla/security/nss/lib/ssl/fnv1a64.c new file mode 100644 index 0000000..c7c4b08 --- /dev/null +++ b/mozilla/security/nss/lib/ssl/fnv1a64.c @@ -0,0 +1,72 @@ +/* + * FNV1A64 Hash + * http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Langley, Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* $Id: fnv1a64.c,v 1.0 2010/08/09 13:00:00 agl%google.com Exp $ */ + +#include "prtypes.h" +#include "prnetdb.h" + +/* Older versions of Visual C++ don't support the 'ull' suffix. */ +#ifdef _MSC_VER +static const PRUint64 FNV1A64_OFFSET_BASIS = 14695981039346656037ui64; +static const PRUint64 FNV1A64_PRIME = 1099511628211ui64; +#else +static const PRUint64 FNV1A64_OFFSET_BASIS = 14695981039346656037ull; +static const PRUint64 FNV1A64_PRIME = 1099511628211ull; +#endif + +void FNV1A64_Init(PRUint64* digest) { + *digest = FNV1A64_OFFSET_BASIS; +} + +void FNV1A64_Update(PRUint64* digest, const unsigned char *data, + unsigned int length) { + unsigned int i; + + for (i = 0; i < length; i++) { + *digest ^= data[i]; + *digest *= FNV1A64_PRIME; + } +} + +void FNV1A64_Final(PRUint64 *digest) { + *digest = PR_htonll(*digest); +} diff --git a/mozilla/security/nss/lib/ssl/manifest.mn b/mozilla/security/nss/lib/ssl/manifest.mn index 8451229..f09d770 100644 --- a/mozilla/security/nss/lib/ssl/manifest.mn +++ b/mozilla/security/nss/lib/ssl/manifest.mn @@ -51,6 +51,7 @@ MAPFILE = $(OBJDIR)/ssl.def CSRCS = \ derive.c \ + fnv1a64.c \ prelib.c \ ssl3con.c \ ssl3gthr.c \ diff --git a/mozilla/security/nss/lib/ssl/ssl.h b/mozilla/security/nss/lib/ssl/ssl.h index 221fe2d..3a22b45 100644 --- a/mozilla/security/nss/lib/ssl/ssl.h +++ b/mozilla/security/nss/lib/ssl/ssl.h @@ -140,6 +140,8 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd); /* bits. The advantage of False Start is that it saves a round trip for */ /* client-speaks-first protocols when performing a full handshake. */ #define SSL_ENABLE_OCSP_STAPLING 23 /* Request OCSP stapling (client) */ +#define SSL_ENABLE_CACHED_INFO 24 /* Enable TLS cached information */ + /* extension, off by default. */ #ifdef SSL_DEPRECATED_FUNCTION /* Old deprecated function names */ @@ -256,6 +258,12 @@ SSL_IMPORT SECStatus SSL_SecurityStatus(PRFileDesc *fd, int *on, char **cipher, #define SSL_SECURITY_STATUS_FORTEZZA 3 /* NO LONGER SUPPORTED */ /* +** Returns true if the server's Certificate message contained a hash of the +** certificate chain due to the TLS cached info extension. +*/ +SSL_IMPORT PRBool SSL_CertChainDigestReceived(PRFileDesc *fd); + +/* ** Return the certificate for our SSL peer. If the client calls this ** it will always return the server's certificate. If the server calls ** this, it may return NULL if client authentication is not enabled or @@ -275,6 +283,13 @@ SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd); SSL_IMPORT SECStatus SSL_PeerCertificateChain( PRFileDesc *fd, CERTCertificate **certs, unsigned int *certs_size); +/* +** Set the predicted cert chain to be used in the cached info extension. +*/ +SSL_IMPORT SECStatus SSL_SetPredictedPeerCertificates(PRFileDesc *fd, + CERTCertificate **certs, + unsigned int len); + /* SSL_GetStapledOCSPResponse returns the OCSP response that was provided by * the TLS server. The resulting data is copied to |out_data|. On entry, |*len| * must contain the size of |out_data|. On exit, |*len| will contain the size @@ -405,6 +420,17 @@ SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, void *arg); /* + ** Set the predicted chain of certificates for the peer. This is used for the + ** TLS Cached Info extension. Note that the SSL_ENABLE_CACHED_INFO option must + ** be set for this to occur. + ** + ** This function takes a reference to each of the given certificates. + */ + SSL_IMPORT SECStatus SSL_SetPredictedPeerCertificates( + PRFileDesc *fd, CERTCertificate **certs, + unsigned int numCerts); + +/* ** Configure SSL socket for running a secure server. Needs the ** certificate for the server and the servers private key. The arguments ** are copied. diff --git a/mozilla/security/nss/lib/ssl/ssl3con.c b/mozilla/security/nss/lib/ssl/ssl3con.c index ca2793f..dd99962 100644 --- a/mozilla/security/nss/lib/ssl/ssl3con.c +++ b/mozilla/security/nss/lib/ssl/ssl3con.c @@ -5145,7 +5145,6 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) ssl3_CopyPeerCertsFromSID(ss, sid); } - /* NULL value for PMS signifies re-use of the old MS */ rv = ssl3_InitPendingCipherSpec(ss, NULL); if (rv != SECSuccess) { @@ -7715,6 +7714,69 @@ ssl3_SendCertificate(sslSocket *ss) } } + if (ss->ssl3.cachedInfoCertChainDigestReceived) { + /* Compute hash. */ + PRUint64 certChainHash; + int i; + FNV1A64_Init(&certChainHash); + for (i = 0; i < certChain->len; i++) { + unsigned int certLen = certChain->certs[i].len; + unsigned char certLenArray[3] = { + certLen >> 16, + certLen >> 8, + certLen + }; + FNV1A64_Update(&certChainHash, certLenArray, sizeof(certLenArray)); + FNV1A64_Update(&certChainHash, certChain->certs[i].data, certLen); + } + FNV1A64_Final(&certChainHash); + + /* Both |&certChainHash| and |ss->ssl3.certChainDigest| should be in + * network byte order since both are computed with the FNV1A64 hash, + * which calls the function htonll. + */ + if (memcmp(&certChainHash, ss->ssl3.certChainDigest, + sizeof(certChainHash)) == 0) { + /* The client correctly predicted the certificate chain. */ + + /* Handshake type: certificate. */ + rv = ssl3_AppendHandshakeNumber(ss, certificate, 1); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + /* Handshake message length. */ + rv = ssl3_AppendHandshakeNumber(ss, 15, 3); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + /* CertChainLen(3) + ASN.1CertLen(3) + DigestLen(1) + Digest(8) */ + rv = ssl3_AppendHandshakeNumber(ss, 12, 3); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + /* ASN.1CertLen(3) + DigestLen(1) + Digest(8) */ + rv = ssl3_AppendHandshakeNumber(ss, 9, 3); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + /* Digest Length Byte */ + rv = ssl3_AppendHandshakeNumber(ss, sizeof(certChainHash), 1); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + /* Digest */ + rv = ssl3_AppendHandshake(ss, &certChainHash, + sizeof(certChainHash)); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + + return SECSuccess; + } + } + + /* Send the entire certificate as usual. */ + rv = ssl3_AppendHandshakeHeader(ss, certificate, len + 3); if (rv != SECSuccess) { return rv; /* err set by AppendHandshake. */ @@ -7869,7 +7931,6 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) PRInt32 size; SECStatus rv; PRBool isServer = (PRBool)(!!ss->sec.isServer); - PRBool trusted = PR_FALSE; PRBool isTLS; SSL3AlertDescription desc = bad_certificate; int errCode = SSL_ERROR_RX_MALFORMED_CERTIFICATE; @@ -7929,35 +7990,46 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) goto loser; /* don't send alerts on memory errors */ } - /* First get the peer cert. */ - remaining -= 3; - if (remaining < 0) - goto decode_loser; + if (length == 12 && ssl3_ExtensionNegotiated(ss, ssl_cached_info_xtn)) { + /* We are dealing with a certificate_chain digest */ + int i; - size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length); - if (size <= 0) - goto loser; /* fatal alert already sent by ConsumeHandshake. */ + ss->ssl3.cachedInfoCertChainDigestReceived = PR_TRUE; - if (remaining < size) - goto decode_loser; + /* Make sure the digests match. */ + if (memcmp(b + 4, ss->ssl3.certChainDigest, 8)) { + desc = handshake_failure; + goto alert_loser; + } - certItem.data = b; - certItem.len = size; - b += size; - length -= size; - remaining -= size; + /* First get the peer cert. */ + if (ss->ssl3.predictedCertChain[0] == NULL) { + desc = handshake_failure; + goto alert_loser; + } + ss->sec.peerCert = CERT_DupCertificate(ss->ssl3.predictedCertChain[0]); - ss->sec.peerCert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, - PR_FALSE, PR_TRUE); - if (ss->sec.peerCert == NULL) { - /* We should report an alert if the cert was bad, but not if the - * problem was just some local problem, like memory error. - */ - goto ambiguous_err; - } + /* Now get all of the CA certs. */ + ss->ssl3.peerCertChain = NULL; + for (i = 1; ss->ssl3.predictedCertChain[i] != NULL; i++) { + c = PORT_ArenaNew(arena, ssl3CertNode); + if (c == NULL) { + goto loser; /* don't send alerts on memory errors */ + } + c->cert = CERT_DupCertificate(ss->ssl3.predictedCertChain[i]); + c->next = NULL; + if (lastCert) { + lastCert->next = c; + } else { + ss->ssl3.peerCertChain = c; + } + lastCert = c; + } + } else { + /* We are dealing with a regular certificate message */ + ss->ssl3.cachedInfoCertChainDigestReceived = PR_FALSE; - /* Now get all of the CA certs. */ - while (remaining > 0) { + /* First get the peer cert. */ remaining -= 3; if (remaining < 0) goto decode_loser; @@ -7971,35 +8043,63 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) certItem.data = b; certItem.len = size; - b += size; + b += size; length -= size; remaining -= size; - c = PORT_ArenaNew(arena, ssl3CertNode); - if (c == NULL) { - goto loser; /* don't send alerts on memory errors */ - } - - c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, - PR_FALSE, PR_TRUE); - if (c->cert == NULL) { + ss->sec.peerCert = CERT_NewTempCertificate(ss->dbHandle, &certItem, + NULL, PR_FALSE, PR_TRUE); + if (ss->sec.peerCert == NULL) { + /* We should report an alert if the cert was bad, but not if the + * problem was just some local problem, like memory error. + */ goto ambiguous_err; } - if (c->cert->trust) - trusted = PR_TRUE; + /* Now get all of the CA certs. */ + while (remaining > 0) { + remaining -= 3; + if (remaining < 0) + goto decode_loser; - c->next = NULL; - if (lastCert) { - lastCert->next = c; - } else { - certs = c; + size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length); + if (size <= 0) + goto loser; /* fatal alert already sent by ConsumeHandshake. */ + + if (remaining < size) + goto decode_loser; + + certItem.data = b; + certItem.len = size; + b += size; + length -= size; + remaining -= size; + + c = PORT_ArenaNew(arena, ssl3CertNode); + if (c == NULL) { + goto loser; /* don't send alerts on memory errors */ + } + + c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, + PR_FALSE, PR_TRUE); + if (c->cert == NULL) { + goto ambiguous_err; + } + + c->next = NULL; + if (lastCert) { + lastCert->next = c; + } else { + certs = c; + } + lastCert = c; } - lastCert = c; - } - if (remaining != 0) - goto decode_loser; + if (remaining != 0) + goto decode_loser; + + ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; + } SECKEY_UpdateCertPQG(ss->sec.peerCert); @@ -8019,8 +8119,6 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) /* someone will handle this connection asynchronously*/ SSL_DBG(("%d: SSL3[%d]: go to async cert handler", SSL_GETPID(), ss->fd)); - ss->ssl3.peerCertChain = certs; - certs = NULL; ssl_SetAlwaysBlock(ss); goto cert_block; } @@ -8045,7 +8143,7 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) } ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); - ssl3_CopyPeerCertsToSID(certs, ss->sec.ci.sid); + ssl3_CopyPeerCertsToSID(ss->ssl3.peerCertChain, ss->sec.ci.sid); if (!ss->sec.isServer) { /* set the server authentication and key exchange types and sizes @@ -8090,8 +8188,6 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length) } } - ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; - cert_block: if (ss->sec.isServer) { ss->ssl3.hs.ws = wait_client_key; @@ -8161,7 +8257,10 @@ alert_loser: (void)SSL3_SendAlert(ss, alert_fatal, desc); loser: - ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; + if (ss->ssl3.peerCertChain == NULL) { + ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; + } + PORT_Assert(certs == NULL); ssl3_CleanupPeerCerts(ss); if (ss->sec.peerCert != NULL) { @@ -9647,6 +9746,21 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache) return rv; } +static void +ssl3_CleanupPredictedPeerCertificates(sslSocket *ss) { + unsigned int i; + + if (!ss->ssl3.predictedCertChain) + return; + + for (i = 0; ss->ssl3.predictedCertChain[i]; i++) { + CERT_DestroyCertificate(ss->ssl3.predictedCertChain[i]); + } + + PORT_Free(ss->ssl3.predictedCertChain); + ss->ssl3.predictedCertChain = NULL; +} + /* Called from ssl_DestroySocketContents() in sslsock.c */ void ssl3_DestroySSL3Info(sslSocket *ss) @@ -9666,6 +9780,9 @@ ssl3_DestroySSL3Info(sslSocket *ss) ss->ssl3.clientCertChain = NULL; } + if (ss->ssl3.predictedCertChain != NULL) + ssl3_CleanupPredictedPeerCertificates(ss); + /* clean up handshake */ if (ss->opt.bypassPKCS11) { SHA1_DestroyContext((SHA1Context *)ss->ssl3.hs.sha_cx, PR_FALSE); diff --git a/mozilla/security/nss/lib/ssl/ssl3ext.c b/mozilla/security/nss/lib/ssl/ssl3ext.c index 4e3d9cc..17898fb 100644 --- a/mozilla/security/nss/lib/ssl/ssl3ext.c +++ b/mozilla/security/nss/lib/ssl/ssl3ext.c @@ -236,6 +236,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, { ssl_next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, + { ssl_cached_info_xtn, &ssl3_ServerHandleCachedInfoXtn }, { -1, NULL } }; @@ -247,6 +248,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, { ssl_next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, + { ssl_cached_info_xtn, &ssl3_ClientHandleCachedInfoXtn }, { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, { -1, NULL } }; @@ -272,6 +274,7 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { #endif { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn }, + { ssl_cached_info_xtn, &ssl3_ClientSendCachedInfoXtn }, { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn } /* any extra entries will appear as { 0, NULL } */ }; @@ -676,6 +679,261 @@ ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, return SECSuccess; } +/* ssl3_ClientSendCachedInfoXtn builds the cached_info extension on the + * client side. */ +PRInt32 +ssl3_ClientSendCachedInfoXtn(sslSocket * ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + PRBool send_empty; + CERTCertificate ** predictedCertChain; + + if (!ss->opt.enableCachedInfo) + return 0; + + predictedCertChain = ss->ssl3.predictedCertChain; + send_empty = (predictedCertChain == NULL); + + /* minimum extension: + * extension_type (2-bytes) + + * length(extension_data) (2-bytes) + + * length(cached_info) (2-bytes) + + */ + extension_length = send_empty ? 6 : 16; + + if (append && maxBytes >= extension_length) { + SECStatus rv; + + /* ExtensionType */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_cached_info_xtn, 2); + if (rv != SECSuccess) + return -1; + /* Extension Length */ + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + return -1; + if (send_empty) { + /* Cached Information Length */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + } else { + PRUint64 certChainHash; + int i; + PRUint8* digestPtr = (PRUint8*) &certChainHash; + + /* Cached Information Length */ + rv = ssl3_AppendHandshakeNumber(ss, 10, 2); + if (rv != SECSuccess) + return -1; + /* Cached Information Type */ + rv = ssl3_AppendHandshakeNumber(ss, 1 /* certificate_chain */, 1); + if (rv != SECSuccess) + return -1; + /* hash length */ + rv = ssl3_AppendHandshakeNumber(ss, 8, 1); + if (rv != SECSuccess) + return -1; + /* hash */ + FNV1A64_Init(&certChainHash); + for (i = 0; predictedCertChain[i] != NULL; i++) { + unsigned int certLen = predictedCertChain[i]->derCert.len; + unsigned char certLenArray[3] = { + certLen >> 16, + certLen >> 8, + certLen + }; + FNV1A64_Update(&certChainHash, certLenArray, 3); + FNV1A64_Update(&certChainHash, + predictedCertChain[i]->derCert.data, certLen); + } + FNV1A64_Final(&certChainHash); + rv = ssl3_AppendHandshake(ss, &certChainHash, 8); + if (rv != SECSuccess) + return -1; + for (i = 0; i < 8; i++) { + ss->ssl3.certChainDigest[i] = digestPtr[i]; + } + } + + } else if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_cached_info_xtn; + return extension_length; +} + +SECStatus +ssl3_ServerHandleCachedInfoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + SECStatus rv; + unsigned char *cached_info = data->data; + unsigned int remaining_len; + + /* Ignore the extension if it isn't enabled. */ + if (!ss->opt.enableCachedInfo) + return SECSuccess; + + if (data->len < 2) + return SECFailure; + remaining_len = (cached_info[0] << 8) | cached_info[1]; + if (remaining_len > 2048 || remaining_len != data->len - 2) + return SECFailure; + cached_info += 2; + + /* Handle reconnaissance case. */ + if (remaining_len == 0) { + /* The client supports information caching, but provides no information + * about what information types it supports */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, + ssl3_ServerSendCachedInfoXtn); + return rv; + } + + /* Iterate over the CachedObjects and pick the first item of type + * certificate_chain, while ignoring everything else. */ + while (remaining_len >= 2) { + unsigned char cached_object_type = *cached_info++; + unsigned int cached_object_length = *cached_info++; + remaining_len -= 2; + if (remaining_len < cached_object_length) + return SECFailure; + if (cached_object_length != 8) /* The digest must be present. */ + return SECFailure; + if (cached_object_type == cached_info_certificate_chain && + !ss->ssl3.cachedInfoCertChainDigestReceived) { + ss->ssl3.cachedInfoCertChainDigestReceived = PR_TRUE; + memcpy(ss->ssl3.certChainDigest, cached_info, 8); + } + remaining_len -= cached_object_length; + cached_info += cached_object_length; + } + + if (remaining_len != 0) + return SECFailure; + + if (ss->ssl3.cachedInfoCertChainDigestReceived) { + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, + ssl3_ServerSendCachedInfoXtn); + return SECSuccess; + } + + return SECSuccess; +} + +/* ssl3_ServerSendCachedInfoXtn builds the cached_info extension on the + * server side. */ +PRInt32 +ssl3_ServerSendCachedInfoXtn(sslSocket * ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length = 2 /* extension type */ + + 2 /* extension length */ + + 2 /* cached_info length */ + + 1 /* CachedInformationType */ + + 1 /* hash value length (0) */; + SECStatus rv; + + PORT_Assert(ss->opt.enableCachedInfo); + + if (append && maxBytes >= extension_length) { + /* ExtensionType */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_cached_info_xtn, 2); + if (rv != SECSuccess) + return -1; + /* Extension Length */ + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + return -1; + /* Cached Information Length */ + rv = ssl3_AppendHandshakeNumber(ss, 2, 2); + if (rv != SECSuccess) + return -1; + /* Cached Information Type */ + rv = ssl3_AppendHandshakeNumber(ss, 1 /* certificate_chain */, 1); + if (rv != SECSuccess) + return -1; + /* hash length */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 1); + if (rv != SECSuccess) + return -1; + } else if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + return extension_length; +} + +SECStatus +ssl3_ClientHandleCachedInfoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + unsigned char * cached_info = data->data; + unsigned int remaining_cached_info_length; + PRBool has_correct_cert_chain = PR_FALSE; + + /* If we didn't request this extension, then the server may not echo it. */ + if (!ss->opt.enableCachedInfo) + return SECFailure; + + if (data->len == 0) { + /* The server supports information caching, but provides no information + * about what information types it supports */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; + } + + if (data->len < 2) + return SECFailure; + remaining_cached_info_length = (cached_info[0] << 8) | cached_info[1]; + if (remaining_cached_info_length != data->len - 2) + return SECFailure; + cached_info += 2; + while (remaining_cached_info_length >= 2) { + /* The server supports only those CachedInformationType types that are + * identified by a present CachedObject */ + unsigned char cached_object_type; + unsigned int cached_object_length; + unsigned char cached_object_digest[8]; + cached_object_type = *cached_info++; + cached_object_length = *cached_info++; + remaining_cached_info_length -= 2; + if (remaining_cached_info_length < cached_object_length) + return SECFailure; + if (cached_object_length != 0 && cached_object_length != 8) + return SECFailure; + remaining_cached_info_length -= cached_object_length; + if (cached_object_type == cached_info_certificate_chain) { + if (cached_object_length == 0) + has_correct_cert_chain = PR_TRUE; + else { /* Hashes must match */ + int i; + for (i = 0; i < 8; i++) + cached_object_digest[i] = *cached_info++; + if (!memcmp(cached_object_digest, ss->ssl3.certChainDigest, 8)) + has_correct_cert_chain = PR_TRUE; + } + } + } + + if (remaining_cached_info_length != 0) + return SECFailure; + + if (has_correct_cert_chain) { + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; + } + + return SECFailure; +} + /* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the * client side. See RFC 4366 section 3.6. */ PRInt32 diff --git a/mozilla/security/nss/lib/ssl/sslauth.c b/mozilla/security/nss/lib/ssl/sslauth.c index df40f30..fcd15ca 100644 --- a/mozilla/security/nss/lib/ssl/sslauth.c +++ b/mozilla/security/nss/lib/ssl/sslauth.c @@ -95,6 +95,46 @@ SSL_PeerCertificateChain(PRFileDesc *fd, CERTCertificate **certs, return SECSuccess; } +SECStatus +SSL_SetPredictedPeerCertificates(PRFileDesc *fd, CERTCertificate **certs, + unsigned int numCerts) +{ + sslSocket *ss; + unsigned int i; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetPredictedPeerCertificates", + SSL_GETPID(), fd)); + return SECFailure; + } + + ss->ssl3.predictedCertChain = + PORT_NewArray(CERTCertificate*, numCerts + 1); + if (!ss->ssl3.predictedCertChain) + return SECFailure; /* error code was set */ + for (i = 0; i < numCerts; i++) + ss->ssl3.predictedCertChain[i] = CERT_DupCertificate(certs[i]); + ss->ssl3.predictedCertChain[numCerts] = NULL; + + return SECSuccess; +} + +PRBool +SSL_CertChainDigestReceived(PRFileDesc *fd) +{ + sslSocket *ss; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_CertChainDigestReceived", + SSL_GETPID(), fd)); + return SECFailure; + } + + return ss->ssl3.cachedInfoCertChainDigestReceived; +} + /* NEED LOCKS IN HERE. */ CERTCertificate * SSL_LocalCertificate(PRFileDesc *fd) diff --git a/mozilla/security/nss/lib/ssl/sslimpl.h b/mozilla/security/nss/lib/ssl/sslimpl.h index 8e2bd14..f1e9a3e 100644 --- a/mozilla/security/nss/lib/ssl/sslimpl.h +++ b/mozilla/security/nss/lib/ssl/sslimpl.h @@ -340,6 +340,7 @@ typedef struct sslOptionsStr { unsigned int requireSafeNegotiation : 1; /* 22 */ unsigned int enableFalseStart : 1; /* 23 */ unsigned int enableOCSPStapling : 1; /* 24 */ + unsigned int enableCachedInfo : 1; /* 25 */ } sslOptions; typedef enum { sslHandshakingUndetermined = 0, @@ -754,6 +755,11 @@ struct TLSExtensionDataStr { PRUint32 sniNameArrSize; }; +typedef enum { + cached_info_certificate_chain = 1, + cached_info_trusted_cas = 2 +} TLSCachedInfoType; + /* ** This is the "hs" member of the "ssl3" struct. ** This entire struct is protected by ssl3HandshakeLock @@ -832,6 +838,14 @@ struct ssl3StateStr { CERTCertificateList *clientCertChain; /* used by client */ PRBool sendEmptyCert; /* used by client */ + /* TLS Cached Info Extension */ + CERTCertificate ** predictedCertChain; + /* An array terminated with a NULL. */ + PRUint8 certChainDigest[8]; + /* Used in cached info extension. Stored in network + * byte order. */ + PRBool cachedInfoCertChainDigestReceived; + int policy; /* This says what cipher suites we can do, and should * be either SSL_ALLOWED or SSL_RESTRICTED @@ -839,7 +853,10 @@ struct ssl3StateStr { PRArenaPool * peerCertArena; /* These are used to keep track of the peer CA */ void * peerCertChain; - /* chain while we are trying to validate it. */ + /* Chain while we are trying to validate it. This + * does not include the leaf cert. It is actually a + * linked list of ssl3CertNode structs. + */ CERTDistNames * ca_list; /* used by server. trusted CAs for this socket. */ PRBool initialized; @@ -1524,6 +1541,10 @@ extern SECStatus ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +extern SECStatus ssl3_ServerHandleCachedInfoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +extern SECStatus ssl3_ClientHandleCachedInfoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, @@ -1545,6 +1566,10 @@ extern PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket *ss, PRBool append, */ extern PRInt32 ssl3_SendServerNameXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); +extern PRInt32 ssl3_ClientSendCachedInfoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +extern PRInt32 ssl3_ServerSendCachedInfoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); /* Assigns new cert, cert chain and keys to ss->serverCerts * struct. If certChain is NULL, tries to find one. Aborts if @@ -1648,6 +1673,12 @@ SECStatus SSL_DisableDefaultExportCipherSuites(void); SECStatus SSL_DisableExportCipherSuites(PRFileDesc * fd); PRBool SSL_IsExportCipherSuite(PRUint16 cipherSuite); +/********************** FNV hash *********************/ + +void FNV1A64_Init(PRUint64 *digest); +void FNV1A64_Update(PRUint64 *digest, const unsigned char *data, + unsigned int length); +void FNV1A64_Final(PRUint64 *digest); #ifdef TRACE #define SSL_TRACE(msg) ssl_Trace msg diff --git a/mozilla/security/nss/lib/ssl/sslsock.c b/mozilla/security/nss/lib/ssl/sslsock.c index 4c4df3f..3d89d86 100644 --- a/mozilla/security/nss/lib/ssl/sslsock.c +++ b/mozilla/security/nss/lib/ssl/sslsock.c @@ -186,6 +186,7 @@ static sslOptions ssl_defaults = { PR_FALSE, /* requireSafeNegotiation */ PR_FALSE, /* enableFalseStart */ PR_FALSE, /* enableOCSPStapling */ + PR_FALSE, /* enableCachedInfo */ }; sslSessionIDLookupFunc ssl_sid_lookup; @@ -743,6 +744,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on) ss->opt.enableOCSPStapling = on; break; + case SSL_ENABLE_CACHED_INFO: + ss->opt.enableCachedInfo = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -808,6 +813,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn) on = ss->opt.requireSafeNegotiation; break; case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break; case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break; + case SSL_ENABLE_CACHED_INFO: on = ss->opt.enableCachedInfo; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -862,6 +868,7 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn) case SSL_ENABLE_OCSP_STAPLING: on = ssl_defaults.enableOCSPStapling; break; + case SSL_ENABLE_CACHED_INFO: on = ssl_defaults.enableCachedInfo; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -1013,6 +1020,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on) ssl_defaults.enableOCSPStapling = on; break; + case SSL_ENABLE_CACHED_INFO: + ssl_defaults.enableCachedInfo = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; diff --git a/mozilla/security/nss/lib/ssl/sslt.h b/mozilla/security/nss/lib/ssl/sslt.h index 917c093..bca7496 100644 --- a/mozilla/security/nss/lib/ssl/sslt.h +++ b/mozilla/security/nss/lib/ssl/sslt.h @@ -205,9 +205,10 @@ typedef enum { #endif ssl_session_ticket_xtn = 35, ssl_next_proto_neg_xtn = 13172, + ssl_cached_info_xtn = 13173, ssl_renegotiation_info_xtn = 0xff01 /* experimental number */ } SSLExtensionType; -#define SSL_MAX_EXTENSIONS 7 +#define SSL_MAX_EXTENSIONS 8 #endif /* __sslt_h_ */