diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 13:13:40 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 13:13:40 +0000 |
commit | db54628ea7875c034c153a71fee1c7c9ab83f8ed (patch) | |
tree | 1773bf23ef6e57f2a2cd4f7d3c587f698a801ab1 /net/third_party | |
parent | d55864e9e69f5fd4fd98b9482ed5fc461fbfb584 (diff) | |
download | chromium_src-db54628ea7875c034c153a71fee1c7c9ab83f8ed.zip chromium_src-db54628ea7875c034c153a71fee1c7c9ab83f8ed.tar.gz chromium_src-db54628ea7875c034c153a71fee1c7c9ab83f8ed.tar.bz2 |
net: add patch file for Snap Start support in NSS
Add a file in the patches/ directory which contains the contents of
our current Snap Start patch.
No code changes.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/3922002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63206 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/third_party')
-rw-r--r-- | net/third_party/nss/README.chromium | 4 | ||||
-rw-r--r-- | net/third_party/nss/patches/snapstart.patch | 2131 |
2 files changed, 2135 insertions, 0 deletions
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium index ce137bd..db082e5 100644 --- a/net/third_party/nss/README.chromium +++ b/net/third_party/nss/README.chromium @@ -36,5 +36,9 @@ Patches: http://crbug.com/51694 https://bugzilla.mozilla.org/show_bug.cgi?id=587234 + * Add Snap Start support + patches/snapstart.patch + http://tools.ietf.org/html/draft-agl-tls-snapstart-00 + The ssl/bodge directory contains files taken from the NSS repo that we required for building libssl outside of its usual build environment. diff --git a/net/third_party/nss/patches/snapstart.patch b/net/third_party/nss/patches/snapstart.patch new file mode 100644 index 0000000..f9c05c8 --- /dev/null +++ b/net/third_party/nss/patches/snapstart.patch @@ -0,0 +1,2131 @@ +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/snapstart.c b/mozilla/security/nss/lib/ssl/snapstart.c +new file mode 100644 +index 0000000..ca2cafa +--- /dev/null ++++ b/mozilla/security/nss/lib/ssl/snapstart.c +@@ -0,0 +1,1062 @@ ++/* ++ * TLS Snap Start ++ * ++ * ***** 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: ssl3snap.c,v 1.0 2010/08/09 13:00:00 agl%google.com Exp $ */ ++ ++ ++/* TODO(agl): Refactor ssl3_CompressMACEncryptRecord so that it can write to ++** |sendBuf| directly and fix ssl3_AppendSnapStartHandshakeRecord and ++** ssl3_AppendSnapStartApplicationData. ++*/ ++ ++/* TODO(agl): Add support for snap starting with compression. */ ++ ++/* TODO(agl): Free snapStartApplicationData as soon as the handshake has ++** completed. ++*/ ++ ++#include "pk11pub.h" ++#include "ssl.h" ++#include "sslimpl.h" ++#include "sslproto.h" ++ ++static unsigned int GetBE16(const void *in) ++{ ++ const unsigned char *p = in; ++ return ((unsigned) p[0]) << 8 | ++ p[1]; ++} ++ ++static unsigned int GetBE24(const void *in) ++{ ++ const unsigned char *p = in; ++ return ((unsigned) p[0]) << 16 | ++ ((unsigned) p[1]) << 8 | ++ p[2]; ++} ++ ++static void PutBE16(void *out, unsigned int value) ++{ ++ unsigned char *p = out; ++ p[0] = value >> 8; ++ p[1] = value; ++} ++ ++static void PutBE24(void *out, unsigned int value) ++{ ++ unsigned char *p = out; ++ p[0] = value >> 16; ++ p[1] = value >> 8; ++ p[2] = value; ++} ++ ++/* ssl3_ForEachExtension calls a callback for each TLS extension in |extensions| ++** extensions: points to a block of extensions which includes the two prefix ++** length bytes ++** is_resuming: if true, certain extensions will be omitted ++** f: a function which is called with the data of each extension, which ++** includes the four type and length bytes at the beginning. ++*/ ++static PRBool ++ssl3_ForEachExtension(const SECItem *extensions, PRBool is_resuming, ++ void (*f) (const unsigned char *data, unsigned int length, ++ void *ctx), ++ void *ctx) { ++ unsigned int extensions_len, offset; ++ ++ if (extensions->len == 0) ++ return PR_TRUE; ++ ++ if (extensions->len < 2) ++ goto loser; ++ ++ extensions_len = GetBE16(extensions->data); ++ offset = 2; ++ ++ if (extensions->len != 2 + extensions_len) ++ goto loser; ++ ++ while (extensions_len) { ++ unsigned int extension_num, extension_len; ++ ++ if (extensions->len - offset < 4) ++ goto loser; ++ ++ extension_num = GetBE16(extensions->data + offset); ++ extension_len = GetBE16(extensions->data + offset + 2); ++ ++ if (extensions->len - offset < 4 + extension_len) ++ goto loser; ++ ++ /* When resuming, the server will omit some extensions from the ++ * previous non-resume ServerHello. */ ++ if (!is_resuming || ++ (extension_num != ssl_server_name_xtn && ++ extension_num != ssl_session_ticket_xtn)) { ++ f(extensions->data + offset, 4 + extension_len, ctx); ++ } ++ ++ offset += 4 + extension_len; ++ extensions_len -= 4 + extension_len; ++ } ++ ++ return PR_TRUE; ++ ++loser: ++ PORT_SetError(SEC_ERROR_INPUT_LEN); ++ return PR_FALSE; ++} ++ ++static void ++ssl3_AccumlateLengths(const unsigned char *data, unsigned int length, void *ptr) ++{ ++ unsigned int *sum = (unsigned int *) ptr; ++ *sum += length; ++} ++ ++/* ssl3_PredictServerResponse predicts the contents of the server's ++** ServerHello...ServerHelloDone (inclusive) and progressively calls a callback ++** with the contents of those messages. ++** previous_server_hello: the contents of a previous ServerHello from the ++** server where the 'random' field has been replaced with our suggested ++** server random. ++** is_resuming: if false, Certificate and ServerHelloDone messages will be ++** predicted ++** hashUpdate: a callback which is called repeated with the contents of the ++** predicted messages. ++*/ ++static PRBool ++ssl3_PredictServerResponse( ++ sslSocket *ss, SECItem *previous_server_hello, PRBool is_resuming, ++ void (*hashUpdate) (const unsigned char *data, unsigned int length, ++ void *ctx), ++ void *ctx) { ++ unsigned int old_session_id_length, old_extensions_len; ++ unsigned int extensions_len, server_hello_len; ++ unsigned char session_id_len, header[4]; ++ SECItem extensions; ++ ++ /* Keep the structure of a ServerHello in mind when reading the following: ++ * ++ * struct ServerHello { ++ * uint16_t version; ++ * uint8_t random[32]; ++ * uint8_t session_id_len; ++ * uint8_t session_id[session_id_len]; ++ * uint16_t cipher ++ * uint8_t compression ++ * ++ * // Optional: ++ * uint16_t extensions_len; ++ * struct Extension { ++ * uint16_t num; ++ * uint16_t len; ++ * uint8_t payload[len]; ++ * } ++ */ ++ ++ /* 38 bytes is the shortest possible ServerHello with a zero-length ++ * session_id and no extensions. */ ++ if (previous_server_hello->len < 38) { ++ PORT_SetError(SEC_ERROR_INPUT_LEN); ++ return PR_FALSE; ++ } ++ ++ /* First we need to figure out the length of the predicted ServerHello. Any ++ * session id in |previous_server_hello| needs to be removed ++ * (or replaced). */ ++ old_session_id_length = previous_server_hello->data[34]; ++ ++ extensions.len = 0; ++ ++ if (previous_server_hello->len >= 35 + old_session_id_length + 3 + 2) { ++ /* Extensions present */ ++ unsigned int offset = 35 + old_session_id_length + 3; ++ extensions.data = previous_server_hello->data + offset; ++ extensions.len = previous_server_hello->len - offset; ++ } ++ ++ /* Sum the lengths of all the extensions that we wish to include */ ++ extensions_len = 0; ++ if (!ssl3_ForEachExtension(&extensions, is_resuming, ssl3_AccumlateLengths, ++ &extensions_len)) { ++ return PR_FALSE; ++ } ++ ++ old_extensions_len = ++ (previous_server_hello->len - 35 - old_session_id_length - 3 - 2); ++ ++ session_id_len = 0; ++ if (ss->sec.ci.sid) ++ session_id_len = ss->sec.ci.sid->u.ssl3.sessionIDLength; ++ server_hello_len = previous_server_hello->len + ++ session_id_len - old_session_id_length + ++ extensions_len - old_extensions_len; ++ ++ header[0] = server_hello; ++ PutBE24(header + 1, server_hello_len); ++ hashUpdate(header, 4, ctx); ++ ++ hashUpdate(previous_server_hello->data, 34, ctx); ++ hashUpdate(&session_id_len, sizeof(session_id_len), ctx); ++ if (session_id_len) ++ hashUpdate(ss->sec.ci.sid->u.ssl3.sessionID, session_id_len, ctx); ++ hashUpdate(previous_server_hello->data + 35 + old_session_id_length, 3, ++ ctx); ++ ++ if (extensions.len) { ++ PutBE16(header, extensions_len); ++ hashUpdate(header, 2, ctx); ++ ++ if (!ssl3_ForEachExtension(&extensions, is_resuming, hashUpdate, ctx)) ++ return PR_FALSE; ++ } ++ ++ if (!is_resuming) { ++ unsigned int certificate_message_len = 3, i; ++ for (i = 0; ss->ssl3.predictedCertChain[i]; i++) { ++ certificate_message_len += 3; ++ certificate_message_len += ++ ss->ssl3.predictedCertChain[i]->derCert.len; ++ } ++ ++ header[0] = certificate; ++ PutBE24(header + 1, certificate_message_len); ++ hashUpdate(header, 4, ctx); ++ ++ PutBE24(header, certificate_message_len - 3); ++ hashUpdate(header, 3, ctx); ++ ++ for (i = 0; ss->ssl3.predictedCertChain[i]; i++) { ++ unsigned int len = ss->ssl3.predictedCertChain[i]->derCert.len; ++ PutBE24(header, len); ++ hashUpdate(header, 3, ctx); ++ hashUpdate(ss->ssl3.predictedCertChain[i]->derCert.data, len, ctx); ++ } ++ ++ header[0] = server_hello_done; ++ header[1] = header[2] = header[3] = 0; ++ hashUpdate(header, 4, ctx); ++ } ++ ++ return PR_TRUE; ++} ++ ++/* ssl3_SnapStartHash is called with the contents of the server's predicted ++ * response and updates both the Finished hash and an FNV641a hash. */ ++static void ++ssl3_SnapStartHash(const unsigned char *data, unsigned int len, void *ctx) ++{ ++ SECStatus rv; ++ void **ptrs = (void **) ctx; ++ sslSocket *ss = ptrs[0]; ++ PRUint64 *fnv = ptrs[1]; ++ ++ FNV1A64_Update(fnv, data, len); ++ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *) data, len); ++ if (rv != SECSuccess) ++ PR_Assert("rv == SECSuccess", __FILE__, __LINE__); ++} ++ ++static PRInt32 ++ssl3_SendEmptySnapStartXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) ++{ ++ SECStatus rv; ++ ++ if (maxBytes < 4) ++ return 0; ++ ++ if (append) { ++ rv = ssl3_AppendHandshakeNumber(ss, ssl_snap_start_xtn, 2); ++ if (rv != SECSuccess) ++ return -1; ++ rv = ssl3_AppendHandshakeNumber(ss, 0 /* empty extension */, 2); ++ if (rv != SECSuccess) ++ return -1; ++ if (!ss->sec.isServer) { ++ TLSExtensionData *xtnData = &ss->xtnData; ++ xtnData->advertised[xtnData->numAdvertised++] = ssl_snap_start_xtn; ++ } ++ } ++ return 4; ++} ++ ++static SECStatus ++ssl3_BufferEnsure(sslBuffer *buf, unsigned int extra_bytes) ++{ ++ if (buf->space < buf->len + extra_bytes) ++ return sslBuffer_Grow(buf, buf->len + extra_bytes); ++ return SECSuccess; ++} ++ ++/* ssl3_AppendSnapStartRecordHeader appends a 5 byte TLS record header to the ++ * sendBuf of the given sslSocket. */ ++static SECStatus ++ssl3_AppendSnapStartRecordHeader(sslSocket *ss, SSL3ContentType type, ++ unsigned int len) ++{ ++ SECStatus rv; ++ ++ rv = ssl3_BufferEnsure(&ss->sec.ci.sendBuf, 5); ++ if (rv != SECSuccess) ++ return rv; ++ ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len + 0] = type; ++ PutBE16(&ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len + 1], ss->version); ++ PutBE16(&ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len + 3], len); ++ ss->sec.ci.sendBuf.len += 5; ++ return SECSuccess; ++} ++ ++/* ssl3_AppendSnapStartHandshakeRecord appends a (possibly encrypted) record to ++** the sendBuf of the given sslSocket. ++** f: a function which will append the bytes of the record (not including the ++** 5 byte header) to the sendBuf. ++*/ ++static SECStatus ++ssl3_AppendSnapStartHandshakeRecord(sslSocket *ss, SECStatus (*f) (sslSocket*), ++ PRBool encrypt) ++{ ++ SECStatus rv; ++ unsigned int record_offset, record_len; ++ ++ /* ssl3_CompressMACEncryptRecord will deal with the record header if we are ++ * encrypting. */ ++ if (!encrypt) { ++ /* The zero length argument here is a dummy value. We write the real ++ * length once we know it, below. */ ++ rv = ssl3_AppendSnapStartRecordHeader(ss, content_handshake, 0); ++ if (rv != SECSuccess) ++ return rv; ++ } ++ ++ record_offset = ss->sec.ci.sendBuf.len; ++ rv = f(ss); ++ if (rv != SECSuccess) ++ return rv; ++ record_len = ss->sec.ci.sendBuf.len - record_offset; ++ if (!encrypt) { ++ PutBE16(&ss->sec.ci.sendBuf.buf[record_offset - 2], record_len); ++ } else { ++ /* ssl3_CompressMACEncryptRecord writes to |ss->sec.writeBuf| ++ * so we copy it back to |ss->sec.ci.sendBuf| */ ++ /* TODO(agl): the buffer copy here is a bodge. See TODO at the top of ++ * the file. */ ++ rv = ssl3_CompressMACEncryptRecord( ++ ss, content_handshake, ++ &ss->sec.ci.sendBuf.buf[record_offset], record_len); ++ if (rv != SECSuccess) ++ return rv; ++ ss->sec.ci.sendBuf.len -= record_len; ++ ssl3_BufferEnsure(&ss->sec.ci.sendBuf, ss->sec.writeBuf.len); ++ memcpy(&ss->sec.ci.sendBuf.buf[record_offset], ss->sec.writeBuf.buf, ++ ss->sec.writeBuf.len); ++ ss->sec.ci.sendBuf.len += ss->sec.writeBuf.len; ++ ss->sec.writeBuf.len = 0; ++ } ++ ++ return SECSuccess; ++} ++ ++/* ssl3_AppendSnapStartApplicationData appends an encrypted Application Data ++** record the sendBuf of the given sslSocket. ++*/ ++static SECStatus ssl3_AppendSnapStartApplicationData( ++ sslSocket *ss, const SSL3Opaque *data, unsigned int len) ++{ ++ SECStatus rv; ++ ++ /* TODO(agl): the buffer copy here is a bodge. See TODO at the top of the ++ * file. */ ++ rv = ssl3_CompressMACEncryptRecord(ss, content_application_data, data, len); ++ if (rv != SECSuccess) ++ return rv; ++ rv = ssl3_BufferEnsure(&ss->sec.ci.sendBuf, ss->sec.writeBuf.len); ++ if (rv != SECSuccess) ++ return rv; ++ memcpy(&ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len], ++ ss->sec.writeBuf.buf, ss->sec.writeBuf.len); ++ ss->sec.ci.sendBuf.len += ss->sec.writeBuf.len; ++ ss->sec.writeBuf.len = 0; ++ ++ return SECSuccess; ++} ++ ++static SECStatus ssl3_SendSnapStartFinished(sslSocket *ss) ++{ ++ /* We use ssl_SEND_FLAG_NO_FLUSH here because this finished message is ++ * going to end up in the middle of the Snap Start extension. So ++ * transmitting |sendBuf| at this point would result in an incomplete ++ * ClientHello. */ ++ return ssl3_SendFinished(ss, ssl_SEND_FLAG_NO_FLUSH); ++} ++ ++/* ssl3_FindOrbit is called for each extension in a ServerHello message. It ++** tests for a Snap Start extension and records the server's orbit when found. ++** data: the extension data (including the four type and length bytes) ++** length: the length, in bytes, of |data| ++** ptr: a pointer to a uint8_t[9]. The orbit, if found, is copied into the ++** first 8 bytes and then the ninth byte is set to one. ++*/ ++static void ++ssl3_FindOrbit(const unsigned char *data, unsigned int length, void *ptr) ++{ ++ unsigned char *orbit = (unsigned char *) ptr; ++ ++ unsigned int extension_num = GetBE16(data); ++ if (extension_num == ssl_snap_start_xtn && ++ length == 4 + 8 /* orbit */ + 2 /* snap start cipher suite */) { ++ memcpy(orbit, data + 4, 8); ++ /* A last byte of 1 indicates that the previous eight are valid. */ ++ orbit[8] = 1; ++ } ++} ++ ++/* ssl3_CanSnapStart returns true if we are able to perform Snap Start on ++** the given socket. ++** extensions: on successful return, this is filled in with the contents of ++** the server's predicted extensions. This points within ++** |ss->ssl3.serverHelloPredictionData|. ++** resuming: PR_TRUE iff we wish to attempt a Snap Start resume ++** out_orbit: if this function returns PR_TRUE, then |out_orbit| is filled ++** with the server's predicted orbit value. ++** The |hs.cipher_suite|, |hs.cipher_def| and |hs.compression| fields of |ss| ++** are set to match the predicted ServerHello on successful exit (and may still ++** be modified on failure). ++*/ ++static PRBool ++ssl3_CanSnapStart(sslSocket *ss, SECItem *extensions, PRBool resuming, ++ unsigned char out_orbit[8]) { ++ const unsigned char *server_hello; ++ unsigned int server_hello_len, session_id_len, cipher_suite_offset; ++ unsigned int extensions_offset, cipher_suite, compression_method; ++ unsigned char orbit[9]; ++ SECStatus rv; ++ SSL3ProtocolVersion version; ++ ++ /* If we don't have the information needed then we can't perform a Snap ++ * Start. */ ++ if (!ss->ssl3.predictedCertChain || !ss->ssl3.serverHelloPredictionData.data) ++ return PR_FALSE; ++ ++ /* When the sizes of the fields in the ClientHello are calculated, they'll ++ * take the length of the Snap Start extension to be zero, so currently ++ * it's as if this extension didn't exist, which is the state that we ++ * need. */ ++ ++ server_hello = ss->ssl3.serverHelloPredictionData.data; ++ server_hello_len = ss->ssl3.serverHelloPredictionData.len; ++ ++ if (server_hello_len < 2 + 32 + 1) ++ return PR_FALSE; ++ session_id_len = server_hello[2 + 32]; ++ cipher_suite_offset = 2 + 32 + 1 + session_id_len; ++ if (server_hello_len < cipher_suite_offset + 3) ++ return PR_FALSE; ++ extensions_offset = cipher_suite_offset + 3; ++ ++ version = (SSL3ProtocolVersion) GetBE16(server_hello); ++ if (MSB(version) < MSB(SSL_LIBRARY_VERSION_3_0)) ++ return PR_FALSE; ++ rv = ssl3_NegotiateVersion(ss, version); ++ if (rv != SECSuccess) ++ return PR_FALSE; ++ ++ cipher_suite = GetBE16(&server_hello[cipher_suite_offset]); ++ ss->ssl3.hs.cipher_suite = (ssl3CipherSuite)cipher_suite; ++ ss->ssl3.hs.suite_def = ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite); ++ if (!ss->ssl3.hs.suite_def) ++ return PR_FALSE; ++ compression_method = server_hello[cipher_suite_offset + 2]; ++ if (compression_method != ssl_compression_null) { ++ /* TODO(agl): support compression. */ ++ return PR_FALSE; ++ } ++ ss->ssl3.hs.compression = ssl_compression_null; ++ ++ extensions->data = (unsigned char *) server_hello + extensions_offset; ++ extensions->len = server_hello_len - extensions_offset; ++ ++ /* The last byte is used to indictate that the previous eight are valid. */ ++ orbit[8] = 0; ++ if (!ssl3_ForEachExtension(extensions, resuming, ssl3_FindOrbit, orbit)) ++ return PR_FALSE; ++ ++ if (!orbit[8]) ++ return PR_FALSE; ++ ++ memcpy(out_orbit, orbit, 8); ++ ++ return PR_TRUE; ++} ++ ++/* ssl3_UpdateClientHelloLengths rewrites the handshake header length and ++** extensions length in the ClientHello to reflect the addition of the Snap ++** Start extension. ++** snap_start_extension_len_offset: the number of bytes of the ClientHello to ++** skip in order to find the embedded length of the Snap Start extension. ++*/ ++static void ++ssl3_UpdateClientHelloLengths(sslSocket *ss, ++ unsigned int snap_start_extension_len_offset, ++ sslSessionID *sid) { ++ unsigned int extension_length, old_length, new_length, new_session_id_len; ++ unsigned int offset, ciphers_length, compressions_len; ++ ++ extension_length = ++ ss->sec.ci.sendBuf.len - snap_start_extension_len_offset - 2; ++ PutBE16(&ss->sec.ci.sendBuf.buf[snap_start_extension_len_offset], ++ extension_length); ++ ++ /* The length in the handshake header is short by extension_length + 4 ++ * bytes. */ ++ old_length = GetBE24(&ss->sec.ci.sendBuf.buf[1]); ++ new_length = old_length + extension_length + 4; ++ PutBE24(&ss->sec.ci.sendBuf.buf[1], new_length); ++ ++ /* The length of the extensions block is similarly wrong. */ ++ new_session_id_len = 0; ++ if (sid) ++ new_session_id_len = sid->u.ssl3.sessionIDLength; ++ offset = 4 + 2 + 32 + 1 + new_session_id_len; ++ ciphers_length = GetBE16(&ss->sec.ci.sendBuf.buf[offset]); ++ offset += 2 + ciphers_length; ++ compressions_len = ss->sec.ci.sendBuf.buf[offset]; ++ offset += 1 + compressions_len; ++ old_length = GetBE16(&ss->sec.ci.sendBuf.buf[offset]); ++ new_length = old_length + extension_length + 4; ++ PutBE16(&ss->sec.ci.sendBuf.buf[offset], new_length); ++} ++ ++/* ssl3_FindServerNPNExtension is a callback function for ssl3_ForEachExtension. ++ * It looks for a Next Protocol Negotiation and saves the payload of the ++ * extension in the given SECItem */ ++static void ++ssl3_FindServerNPNExtension(const unsigned char* data, unsigned int length, ++ void *ctx) ++{ ++ SECItem *server_npn_extension = (SECItem*) ctx; ++ ++ unsigned int extension_num = GetBE16(data); ++ if (extension_num == ssl_next_proto_neg_xtn && length >= 4) { ++ server_npn_extension->data = (unsigned char*)data + 4; ++ server_npn_extension->len = length - 4; ++ } ++} ++ ++/* ssl3_MaybeWriteNextProtocol deals with the interaction of Next Protocol ++ * Negotiation and Snap Start. It's called just before we serialise the embedded ++ * Finished message in the extension. At this point, if NPN is enabled, we have ++ * to include a NextProtocol message. */ ++static SECStatus ++ssl3_MaybeWriteNextProtocol(sslSocket *ss, SECItem *server_hello_extensions) ++{ ++ PRUint16 i16; ++ SECItem server_npn_extension; ++ ++ for (i16 = 0; i16 < ss->xtnData.numAdvertised; i16++) { ++ if (ss->xtnData.advertised[i16] == ssl_next_proto_neg_xtn) ++ break; ++ } ++ ++ if (i16 == ss->xtnData.numAdvertised) { ++ /* We didn't send an NPN extension, so no need to do anything here. */ ++ return SECSuccess; ++ } ++ ++ memset(&server_npn_extension, 0, sizeof(server_npn_extension)); ++ ++ ssl3_ForEachExtension( ++ server_hello_extensions, PR_FALSE /* is_resuming: value doesn't matter ++ in this case */, ssl3_FindServerNPNExtension, &server_npn_extension); ++ ++ if (server_npn_extension.data == NULL) { ++ /* We predicted that the server doesn't support NPN, so nothing to do ++ * here. */ ++ return SECSuccess; ++ } ++ ++ ssl3_ClientHandleNextProtoNegoXtn(ss, ssl_next_proto_neg_xtn, ++ &server_npn_extension); ++ ++ if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NO_SUPPORT) { ++ /* The server's predicted NPN extension was malformed. We're didn't pick ++ * a protocol so we won't send a NextProtocol message. However, this is ++ * probably fatal to the connection. */ ++ return SECSuccess; ++ } ++ ++ return ssl3_AppendSnapStartHandshakeRecord(ss, ssl3_SendNextProto, ++ PR_TRUE /* encrypt */); ++} ++ ++/* ssl3_SendSnapStartXtn appends a Snap Start extension. It assumes that the ++ * inchoate ClientHello is in |ss->sec.ci.sendBuf|. */ ++PRInt32 ++ssl3_SendSnapStartXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) ++{ ++ unsigned char orbit[8]; ++ PRBool resuming = PR_FALSE; ++ unsigned char suggested_server_random[32]; ++ SECStatus rv; ++ PRUint64 fnv; ++ /* The context for |ssl3_SnapStartHash|. The first pointer points to |ss| ++ * and the second to the running FNV1A64 hash of the predicted server ++ * response. */ ++ void *ctx[2]; ++ unsigned int snap_start_extension_len_offset, original_sendbuf_len; ++ ssl3CipherSpec *temp; ++ sslSessionID *sid; ++ SECItem server_extensions; ++ ++ if (!ss->opt.enableSnapStart) ++ return 0; ++ ++ original_sendbuf_len = ss->sec.ci.sendBuf.len; ++ ++ /* This function is called twice for each ClientHello emitted. The first ++ * time around is to calculate the sizes of the extension (|append| is ++ * false). The second time around is to actually write out the bytes ++ * (|append| is true). ++ * ++ * We always return 0 bytes in each case because we want to be able to hash ++ * the inchoate ClientHello as if this extension was missing: that's why ++ * it's important that this always be the last extension serialised. */ ++ sid = ss->sec.ci.sid; ++ ++ if (!ss->opt.enableSessionTickets || ss->sec.isServer) ++ return 0; ++ ++ /* If we are sending a SessionTicket then the first time around ++ * ticketTimestampVerified will be true but it's reset after serialising ++ * the session ticket extension, so we have ++ * |clientSentNonEmptySessionTicket|. */ ++ if (ss->xtnData.clientSentNonEmptySessionTicket) { ++ resuming = PR_TRUE; ++ } else if (sid->u.ssl3.sessionTicket.ticket.data && ++ ss->xtnData.ticketTimestampVerified) { ++ resuming = PR_TRUE; ++ } ++ ++ if (!ssl3_CanSnapStart(ss, &server_extensions, resuming, orbit)) ++ return ssl3_SendEmptySnapStartXtn(ss, append, maxBytes); ++ ++ /* At this point we are happy that we are going to send a non-empty Snap ++ * Start extension. If we are still calculating length then we lie and ++ * return 0 so that everything is set up as if the extension didn't exist ++ * when this function is called again later. */ ++ ++ if (!append) ++ return 0; ++ ++ /* |ss->sec.ci.sendBuf.buf| contains the inchoate ClientHello. This copies ++ * the ClientHello's gmt_unix_time into the suggested server random. */ ++ memcpy(suggested_server_random, ss->sec.ci.sendBuf.buf + 6, 4); ++ memcpy(suggested_server_random + 4, orbit, 8); ++ rv = PK11_GenerateRandom(suggested_server_random + 12, 20); ++ if (rv != SECSuccess) ++ goto loser; ++ memcpy(ss->ssl3.serverHelloPredictionData.data + 2, suggested_server_random, ++ 32); ++ ++ memcpy(&ss->ssl3.hs.server_random, suggested_server_random, 32); ++ ++ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); ++ rv = ssl3_SetupPendingCipherSpec(ss); ++ if (rv != SECSuccess) ++ goto loser; ++ ss->ssl3.hs.isResuming = resuming; ++ ++ FNV1A64_Init(&fnv); ++ ctx[0] = ss; ++ ctx[1] = &fnv; ++ ++ if (!ssl3_PredictServerResponse(ss, &ss->ssl3.serverHelloPredictionData, ++ resuming, ssl3_SnapStartHash, ctx)) { ++ /* It's not a fatal error if the predicted ServerHello was invalid. */ ++ return 0; ++ } ++ FNV1A64_Final(&fnv); ++ ++ /* Now we grow the send buffer to accomodate the extension type and length, ++ * orbit, suggested random and predicted server response hash without ++ * calling ssl3_AppendHandshake (which would also update the Finished ++ * hash). */ ++ if (ssl3_BufferEnsure(&ss->sec.ci.sendBuf, 4 + 8 + 20 + 8) != SECSuccess) ++ goto loser; ++ ++ PutBE16(&ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len], ++ ssl_snap_start_xtn); ++ ss->sec.ci.sendBuf.len += 2; ++ /* Skip over the length for now. */ ++ snap_start_extension_len_offset = ss->sec.ci.sendBuf.len; ++ ss->sec.ci.sendBuf.len += 2; ++ memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, orbit, 8); ++ ss->sec.ci.sendBuf.len += 8; ++ memcpy(&ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len], ++ suggested_server_random + 12, 20); ++ ss->sec.ci.sendBuf.len += 20; ++ memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, &fnv, 8); ++ ss->sec.ci.sendBuf.len += 8; ++ ++ if (!resuming) { ++ /* Write ClientKeyExchange */ ++ ss->sec.peerCert = ++ CERT_DupCertificate(ss->ssl3.predictedCertChain[0]); ++ rv = ssl3_AppendSnapStartHandshakeRecord( ++ ss, ssl3_SendClientKeyExchange, PR_FALSE /* do not encrypt */); ++ if (rv != SECSuccess) ++ goto loser; ++ } else { ++ SSL3Finished hashes; ++ TLSFinished tlsFinished; ++ unsigned char hdr[4]; ++ ++ rv = ssl3_SetupMasterSecretFromSessionID(ss); ++ if (rv == SECFailure) ++ goto loser; ++ ++ if (sid->peerCert != NULL) { ++ ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); ++ ssl3_CopyPeerCertsFromSID(ss, sid); ++ } ++ ++ rv = ssl3_InitPendingCipherSpec(ss, NULL /* re-use master secret */); ++ if (rv != SECSuccess) ++ goto loser; ++ ++ /* Need to add the server's predicted Finished message to our handshake ++ * hash in order to be able to produce our own Finished message. */ ++ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, ++ 0 /* only for SSL3 */); ++ if (rv != SECSuccess) ++ goto loser; ++ ++ rv = ssl3_ComputeTLSFinished(ss->ssl3.pwSpec, PR_TRUE /* isServer */, ++ &hashes, &tlsFinished); ++ if (rv != SECSuccess) ++ goto loser; ++ ++ hdr[0] = (unsigned char) finished; ++ hdr[1] = hdr[2] = 0; ++ hdr[3] = sizeof(tlsFinished.verify_data); ++ ssl3_UpdateHandshakeHashes(ss, hdr, sizeof(hdr)); ++ ssl3_UpdateHandshakeHashes(ss, tlsFinished.verify_data, ++ sizeof(tlsFinished.verify_data)); ++ ++ /* Store the Finished message so that we can verify it later */ ++ memcpy(&ss->ssl3.hs.finishedMsgs.tFinished[1], tlsFinished.verify_data, ++ sizeof(tlsFinished.verify_data)); ++ } ++ ++ /* Write ChangeCipherSpec */ ++ rv = ssl3_AppendSnapStartRecordHeader(ss, content_change_cipher_spec, 1); ++ if (rv != SECSuccess) ++ goto loser; ++ rv = ssl3_BufferEnsure(&ss->sec.ci.sendBuf, 1); ++ if (rv != SECSuccess) ++ goto loser; ++ ss->sec.ci.sendBuf.buf[ss->sec.ci.sendBuf.len] = change_cipher_spec_choice; ++ ss->sec.ci.sendBuf.len++; ++ ++ /* We swap |cwSpec| and |pwSpec| temporarily in order to encrypt some ++ * records before switching them back so that the whole ClientHello doesn't ++ * get encrypted. */ ++ ssl_GetSpecWriteLock(ss); ++ temp = ss->ssl3.cwSpec; ++ ss->ssl3.cwSpec = ss->ssl3.pwSpec; ++ ss->ssl3.pwSpec = temp; ++ ss->ssl3.cwSpec->write_seq_num.high = 0; ++ ss->ssl3.cwSpec->write_seq_num.low = 0; ++ ssl_ReleaseSpecWriteLock(ss); ++ ++ rv = ssl3_MaybeWriteNextProtocol(ss, &server_extensions); ++ if (rv != SECSuccess) ++ goto loser; ++ ++ /* Write Finished */ ++ rv = ssl3_AppendSnapStartHandshakeRecord(ss, ssl3_SendSnapStartFinished, ++ PR_TRUE /* encrypt */); ++ if (rv != SECSuccess) ++ goto loser; ++ ++ /* Write application data */ ++ if (ss->ssl3.snapStartApplicationData.data) { ++ rv = ssl3_AppendSnapStartApplicationData( ++ ss, ss->ssl3.snapStartApplicationData.data, ++ ss->ssl3.snapStartApplicationData.len); ++ if (rv != SECSuccess) ++ goto loser; ++ } ++ ++ /* Revert the write cipher spec because the ClientHello will get encrypted ++ * with it otherwise. */ ++ ssl_GetSpecWriteLock(ss); ++ temp = ss->ssl3.cwSpec; ++ ss->ssl3.cwSpec = ss->ssl3.pwSpec; ++ ss->ssl3.pwSpec = temp; ++ ssl_ReleaseSpecWriteLock(ss); ++ ++ /* Update the lengths in the ClientHello to reflect this extension. */ ++ ssl3_UpdateClientHelloLengths(ss, snap_start_extension_len_offset, sid); ++ ++ /* Keep a copy of the ClientHello around so that we can hash it in the case ++ * the the Snap Start handshake is rejected. */ ++ ++ if (SECITEM_AllocItem(NULL, &ss->ssl3.hs.origClientHello, ++ ss->sec.ci.sendBuf.len) == NULL) { ++ goto loser; ++ } ++ memcpy(ss->ssl3.hs.origClientHello.data, ss->sec.ci.sendBuf.buf, ++ ss->sec.ci.sendBuf.len); ++ ss->ssl3.hs.origClientHello.len = ss->sec.ci.sendBuf.len; ++ ++ ss->xtnData.advertised[ss->xtnData.numAdvertised++] = ssl_snap_start_xtn; ++ ++ if (resuming) { ++ ss->ssl3.hs.snapStartType = snap_start_resume; ++ } else { ++ ss->ssl3.hs.snapStartType = snap_start_full; ++ } ++ ++ return 0; ++ ++loser: ++ /* In the case of an error we revert the length of the sendBuf to remove ++ * any partial data that we may have appended. */ ++ ss->sec.ci.sendBuf.len = original_sendbuf_len; ++ return -1; ++} ++ ++SECStatus ssl3_ClientHandleSnapStartXtn(sslSocket *ss, PRUint16 ex_type, ++ SECItem *data) { ++ /* The work of saving the ServerHello is done in ssl3_HandleServerHello, ++ * where its contents are available. Here we renognise that the saved ++ * ServerHello message contains a Snap Start extension and mark it as ++ * valid. */ ++ ss->ssl3.serverHelloPredictionDataValid = PR_TRUE; ++ 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; ++} ++ ++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; ++} ++ ++SECStatus ++SSL_GetPredictedServerHelloData(PRFileDesc *fd, const unsigned char **data, ++ unsigned int *data_len) ++{ ++ sslSocket *ss; ++ ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetPredictedServerHelloData", ++ SSL_GETPID(), fd)); ++ *data = NULL; ++ *data_len = 0; ++ return SECFailure; ++ } ++ ++ if (!ss->ssl3.serverHelloPredictionDataValid) { ++ *data = NULL; ++ *data_len = 0; ++ } else { ++ *data = ss->ssl3.serverHelloPredictionData.data; ++ *data_len = ss->ssl3.serverHelloPredictionData.len; ++ } ++ return SECSuccess; ++} ++ ++SECStatus ++SSL_SetPredictedServerHelloData(PRFileDesc *fd, const unsigned char *data, ++ unsigned int data_len) ++{ ++ sslSocket *ss; ++ ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetPredictedServerHelloData", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ ++ if (ss->ssl3.serverHelloPredictionData.data) ++ SECITEM_FreeItem(&ss->ssl3.serverHelloPredictionData, PR_FALSE); ++ if (!SECITEM_AllocItem(NULL, &ss->ssl3.serverHelloPredictionData, data_len)) ++ return SECFailure; ++ memcpy(ss->ssl3.serverHelloPredictionData.data, data, data_len); ++ return SECSuccess; ++} ++ ++SECStatus ++SSL_SetSnapStartApplicationData(PRFileDesc *fd, const unsigned char *data, ++ unsigned int data_len) ++{ ++ sslSocket *ss; ++ ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetSnapStartApplicationData", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ ++ if (ss->ssl3.snapStartApplicationData.data) ++ SECITEM_FreeItem(&ss->ssl3.snapStartApplicationData, PR_FALSE); ++ if (!SECITEM_AllocItem(NULL, &ss->ssl3.snapStartApplicationData, data_len)) ++ return SECFailure; ++ memcpy(ss->ssl3.snapStartApplicationData.data, data, data_len); ++ return SECSuccess; ++} ++ ++SECStatus ++SSL_GetSnapStartResult(PRFileDesc *fd, SSLSnapStartResult *result) ++{ ++ sslSocket *ss; ++ ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetSnapStartResult", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ ++ switch (ss->ssl3.hs.snapStartType) { ++ case snap_start_full: ++ *result = SSL_SNAP_START_FULL; ++ break; ++ case snap_start_recovery: ++ *result = SSL_SNAP_START_RECOVERY; ++ break; ++ case snap_start_resume: ++ *result = SSL_SNAP_START_RESUME; ++ break; ++ case snap_start_resume_recovery: ++ *result = SSL_SNAP_START_RESUME_RECOVERY; ++ break; ++ default: ++ PORT_Assert(ss->ssl3.hs.snapStartType == snap_start_none); ++ *result = SSL_SNAP_START_NONE; ++ break; ++ } ++ ++ return SECSuccess; ++} ++ ++/* Called form ssl3_HandleServerHello in the case that we sent a Snap Start ++** ClientHello but received a ServerHello in reply. ++*/ ++SECStatus ++ssl3_ResetForSnapStartRecovery(sslSocket *ss, SSL3Opaque *b, PRUint32 length) ++{ ++ SECStatus rv; ++ PRUint8 hdr[4]; ++ ++ ss->ssl3.hs.ws = wait_server_hello; ++ ++ /* Need to reset the Finished hashes to include the full ClientHello ++ * message. */ ++ ++ rv = ssl3_RestartHandshakeHashes(ss); ++ if (rv != SECSuccess) ++ return rv; ++ rv = ssl3_UpdateHandshakeHashes(ss, ss->ssl3.hs.origClientHello.data, ++ ss->ssl3.hs.origClientHello.len); ++ SECITEM_FreeItem(&ss->ssl3.hs.origClientHello, PR_FALSE); ++ if (rv != SECSuccess) ++ return rv; ++ ++ hdr[0] = (PRUint8)server_hello; ++ hdr[1] = (PRUint8)(length >> 16); ++ hdr[2] = (PRUint8)(length >> 8); ++ hdr[3] = (PRUint8)(length ); ++ ++ rv = ssl3_UpdateHandshakeHashes(ss, hdr, sizeof(hdr)); ++ if (rv != SECSuccess) ++ return rv; ++ rv = ssl3_UpdateHandshakeHashes(ss, b, length); ++ if (rv != SECSuccess) ++ return rv; ++ ++ if (ss->ssl3.hs.snapStartType == snap_start_full) { ++ ss->ssl3.hs.snapStartType = snap_start_recovery; ++ } else { ++ ss->ssl3.hs.snapStartType = snap_start_resume_recovery; ++ } ++ ++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_TRUE/*freeSrvName*/); ++ ++ return SECSuccess; ++} +diff --git a/mozilla/security/nss/lib/ssl/ssl.def b/mozilla/security/nss/lib/ssl/ssl.def +index a1f4b51..effc35d 100644 +--- a/mozilla/security/nss/lib/ssl/ssl.def ++++ b/mozilla/security/nss/lib/ssl/ssl.def +@@ -159,3 +159,13 @@ SSL_SetNextProtoNego; + ;+ local: + ;+*; + ;+}; ++;+NSS_3.13 { # NSS 3.13 release ++;+ global: ++SSL_GetPredictedServerHelloData; ++SSL_GetSnapStartResult; ++SSL_SetPredictedPeerCertificates; ++SSL_SetPredictedServerHelloData; ++SSL_SetSnapStartApplicationData; ++;+ local: ++;+*; ++;+}; +diff --git a/mozilla/security/nss/lib/ssl/ssl.h b/mozilla/security/nss/lib/ssl/ssl.h +index d87ae56..8217d2e 100644 +--- a/mozilla/security/nss/lib/ssl/ssl.h ++++ b/mozilla/security/nss/lib/ssl/ssl.h +@@ -139,6 +139,15 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd); + /* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */ + /* 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_SNAP_START 23 /* Enable SSL snap start (off by */ ++ /* default, applies only to */ ++ /* clients). Snap start is a way */ ++/* of performing TLS handshakes with no round trips. The client's entire */ ++/* handshake is included in the first handshake message, along with */ ++/* optional application data. In order to do this, information from a */ ++/* previous connection to the same server is required. See */ ++/* SSL_GetPredictedServerHelloData, SSL_SetPredictedPeerCertificates and */ ++/* SSL_SetSnapStartApplicationData. */ + + #ifdef SSL_DEPRECATED_FUNCTION + /* Old deprecated function names */ +@@ -376,6 +385,49 @@ 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 Snap Start extension. Note that the SSL_ENABLE_SNAP_START 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); ++ ++/* ++** Get the data needed to predict the server's hello message in the future. On ++** return, |*data| will either be NULL (in which case no data is available and ++** |*data_len| will be zero) or it will point to a buffer within the internal ++** data of |fd| and |*data_len| will contain the number of bytes available. If ++** non-NULL, |*data| will persist at least until the next handshake on |fd|. ++*/ ++SSL_IMPORT SECStatus SSL_GetPredictedServerHelloData( ++ PRFileDesc *fd, const unsigned char **data, ++ unsigned int *data_len); ++ ++/* ++** Set the predicted server hello data. This is used for the TLS Snap Start ++** extension. Note that the SSL_ENABLE_SNAP_START option must be set for this ++** to occur. ++*/ ++SSL_IMPORT SECStatus SSL_SetPredictedServerHelloData( ++ PRFileDesc *fd, const unsigned char *data, unsigned int data_len); ++ ++/* Set the application data which will be transmitted in a Snap Start ++** handshake. If the Snap Start handshake fails, this data will be ++* retransmitted automatically. */ ++SSL_IMPORT SECStatus SSL_SetSnapStartApplicationData( ++ PRFileDesc *fd, const unsigned char *data, unsigned int data_len); ++ ++/* Get the result of a Snap Start handshake. It's valid to call then even if ++** SSL_ENABLE_SNAP_START hasn't been set, although the result will always be ++** SSL_SNAP_START_NONE. ++*/ ++SSL_IMPORT SECStatus SSL_GetSnapStartResult(PRFileDesc* socket, ++ SSLSnapStartResult* result); ++ ++/* + ** 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 64c3452..9ab2a1c 100644 +--- a/mozilla/security/nss/lib/ssl/ssl3con.c ++++ b/mozilla/security/nss/lib/ssl/ssl3con.c +@@ -72,7 +72,8 @@ + #endif + + static void ssl3_CleanupPeerCerts(sslSocket *ss); +-static void ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid); ++static void ssl3_CopyPeerCertsToSID(ssl3CertNode *certs, ++ sslSessionID *sid); + static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec, + PK11SlotInfo * serverKeySlot); + static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms); +@@ -82,14 +83,10 @@ static SECStatus ssl3_InitState( sslSocket *ss); + static SECStatus ssl3_SendCertificate( sslSocket *ss); + static SECStatus ssl3_SendEmptyCertificate( sslSocket *ss); + static SECStatus ssl3_SendCertificateRequest(sslSocket *ss); +-static SECStatus ssl3_SendNextProto( sslSocket *ss); +-static SECStatus ssl3_SendFinished( sslSocket *ss, PRInt32 flags); + static SECStatus ssl3_SendServerHello( sslSocket *ss); + static SECStatus ssl3_SendServerHelloDone( sslSocket *ss); + static SECStatus ssl3_SendServerKeyExchange( sslSocket *ss); + static SECStatus ssl3_NewHandshakeHashes( sslSocket *ss); +-static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss, unsigned char *b, +- unsigned int l); + + static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen, + int maxOutputLen, const unsigned char *input, +@@ -583,7 +580,7 @@ void SSL_AtomicIncrementLong(long * x) + + /* return pointer to ssl3CipherSuiteDef for suite, or NULL */ + /* XXX This does a linear search. A binary search would be better. */ +-static const ssl3CipherSuiteDef * ++const ssl3CipherSuiteDef * + ssl_LookupCipherSuiteDef(ssl3CipherSuite suite) + { + int cipher_suite_def_len = +@@ -1169,7 +1166,7 @@ ssl3_CleanupKeyMaterial(ssl3KeyMaterial *mat) + ** ssl3_DestroySSL3Info + ** Caller must hold SpecWriteLock. + */ +-static void ++void + ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName) + { + PRBool freeit = (PRBool)(!spec->bypassCiphers); +@@ -1211,7 +1208,7 @@ ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName) + ** Caller must hold the ssl3 handshake lock. + ** Acquires & releases SpecWriteLock. + */ +-static SECStatus ++SECStatus + ssl3_SetupPendingCipherSpec(sslSocket *ss) + { + ssl3CipherSpec * pwSpec; +@@ -2039,7 +2036,7 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid) { + return isPresent; + } + +-static SECStatus ++SECStatus + ssl3_CompressMACEncryptRecord(sslSocket * ss, + SSL3ContentType type, + const SSL3Opaque * pIn, +@@ -3097,7 +3094,7 @@ loser: + return SECFailure; + } + +-static SECStatus ++SECStatus + ssl3_RestartHandshakeHashes(sslSocket *ss) + { + SECStatus rv = SECSuccess; +@@ -3175,7 +3172,7 @@ loser: + ** ssl3_HandleHandshakeMessage() + ** Caller must hold the ssl3Handshake lock. + */ +-static SECStatus ++SECStatus + ssl3_UpdateHandshakeHashes(sslSocket *ss, unsigned char *b, unsigned int l) + { + SECStatus rv = SECSuccess; +@@ -3434,7 +3431,7 @@ ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRInt32 bytes, + * Caller must hold a read or write lock on the Spec R/W lock. + * (There is presently no way to assert on a Read lock.) + */ +-static SECStatus ++SECStatus + ssl3_ComputeHandshakeHashes(sslSocket * ss, + ssl3CipherSpec *spec, /* uses ->master_secret */ + SSL3Hashes * hashes, /* output goes here. */ +@@ -4032,7 +4029,18 @@ ssl3_SendClientHello(sslSocket *ss) + return rv; /* error code set by ssl3_FlushHandshake */ + } + +- ss->ssl3.hs.ws = wait_server_hello; ++ switch (ss->ssl3.hs.snapStartType) { ++ case snap_start_full: ++ ss->ssl3.hs.ws = wait_new_session_ticket; ++ break; ++ case snap_start_resume: ++ ss->ssl3.hs.ws = wait_change_cipher; ++ break; ++ default: ++ ss->ssl3.hs.ws = wait_server_hello; ++ break; ++ } ++ + return rv; + } + +@@ -4723,7 +4731,7 @@ loser: + + + /* Called from ssl3_HandleServerHelloDone(). */ +-static SECStatus ++SECStatus + ssl3_SendClientKeyExchange(sslSocket *ss) + { + SECKEYPublicKey * serverKey = NULL; +@@ -4862,6 +4870,94 @@ done: + return rv; + } + ++/* Called from ssl3_HandleServerHello to set up the master secret in ++ * ss->ssl3.pwSpec and the auth algorithm and kea type in ss->sec in the case ++ * of a successful session resumption. */ ++SECStatus ssl3_SetupMasterSecretFromSessionID(sslSocket* ss) { ++ sslSessionID *sid = ss->sec.ci.sid; ++ ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec; ++ SECItem wrappedMS; /* wrapped master secret. */ ++ ++ ss->sec.authAlgorithm = sid->authAlgorithm; ++ ss->sec.authKeyBits = sid->authKeyBits; ++ ss->sec.keaType = sid->keaType; ++ ss->sec.keaKeyBits = sid->keaKeyBits; ++ ++ /* 3 cases here: ++ * a) key is wrapped (implies using PKCS11) ++ * b) key is unwrapped, but we're still using PKCS11 ++ * c) key is unwrapped, and we're bypassing PKCS11. ++ */ ++ if (sid->u.ssl3.keys.msIsWrapped) { ++ PK11SlotInfo *slot; ++ PK11SymKey * wrapKey; /* wrapping key */ ++ CK_FLAGS keyFlags = 0; ++ ++ if (ss->opt.bypassPKCS11) { ++ /* we cannot restart a non-bypass session in a ++ ** bypass socket. ++ */ ++ return SECFailure; ++ } ++ /* unwrap master secret with PKCS11 */ ++ slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID, ++ sid->u.ssl3.masterSlotID); ++ if (slot == NULL) { ++ return SECFailure; ++ } ++ if (!PK11_IsPresent(slot)) { ++ PK11_FreeSlot(slot); ++ return SECFailure; ++ } ++ wrapKey = PK11_GetWrapKey(slot, sid->u.ssl3.masterWrapIndex, ++ sid->u.ssl3.masterWrapMech, ++ sid->u.ssl3.masterWrapSeries, ++ ss->pkcs11PinArg); ++ PK11_FreeSlot(slot); ++ if (wrapKey == NULL) { ++ return SECFailure; ++ } ++ ++ if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */ ++ keyFlags = CKF_SIGN | CKF_VERIFY; ++ } ++ ++ wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; ++ wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; ++ pwSpec->master_secret = ++ PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech, ++ NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE, ++ CKA_DERIVE, sizeof(SSL3MasterSecret), keyFlags); ++ PK11_FreeSymKey(wrapKey); ++ if (pwSpec->master_secret == NULL) { ++ return SECFailure; ++ } ++ } else if (ss->opt.bypassPKCS11) { ++ /* MS is not wrapped */ ++ wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; ++ wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; ++ memcpy(pwSpec->raw_master_secret, wrappedMS.data, wrappedMS.len); ++ pwSpec->msItem.data = pwSpec->raw_master_secret; ++ pwSpec->msItem.len = wrappedMS.len; ++ } else { ++ /* We CAN restart a bypass session in a non-bypass socket. */ ++ /* need to import the raw master secret to session object */ ++ PK11SlotInfo *slot = PK11_GetInternalSlot(); ++ wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; ++ wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; ++ pwSpec->master_secret = ++ PK11_ImportSymKey(slot, CKM_SSL3_MASTER_KEY_DERIVE, ++ PK11_OriginUnwrap, CKA_ENCRYPT, ++ &wrappedMS, NULL); ++ PK11_FreeSlot(slot); ++ if (pwSpec->master_secret == NULL) { ++ return SECFailure; ++ } ++ } ++ ++ return SECSuccess; ++} ++ + /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete + * ssl3 ServerHello message. + * Caller must hold Handshake and RecvBuf locks. +@@ -4886,6 +4982,14 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); + ++ if (ss->ssl3.hs.snapStartType == snap_start_full || ++ ss->ssl3.hs.snapStartType == snap_start_resume) { ++ /* Snap Start handshake was rejected. */ ++ rv = ssl3_ResetForSnapStartRecovery(ss, b, length); ++ if (rv != SECSuccess) ++ return rv; ++ } ++ + rv = ssl3_InitState(ss); + if (rv != SECSuccess) { + errCode = PORT_GetError(); /* ssl3_InitState has set the error code. */ +@@ -4897,6 +5001,21 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + goto alert_loser; + } + ++ if (!ss->ssl3.serverHelloPredictionData.data) { ++ /* If this allocation fails it will only stop the application from ++ * recording the ServerHello information and performing future Snap ++ * Starts. */ ++ if (SECITEM_AllocItem(NULL, &ss->ssl3.serverHelloPredictionData, ++ length)) ++ memcpy(ss->ssl3.serverHelloPredictionData.data, b, length); ++ /* ss->ssl3.serverHelloPredictionDataValid is still false at this ++ * point. We have to record the contents of the ServerHello here ++ * because we don't have a pointer to the whole message when handling ++ * the extensions. However, we wait until the Snap Start extenion ++ * handler to recognise that the server supports Snap Start and to set ++ * serverHelloPredictionDataValid. */ ++ } ++ + temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); + if (temp < 0) { + goto loser; /* alert has been sent */ +@@ -5037,118 +5156,40 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + + if (sid_match && + sid->version == ss->version && +- sid->u.ssl3.cipherSuite == ss->ssl3.hs.cipher_suite) do { +- ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec; +- +- SECItem wrappedMS; /* wrapped master secret. */ ++ sid->u.ssl3.cipherSuite == ss->ssl3.hs.cipher_suite) { ++ rv = ssl3_SetupMasterSecretFromSessionID(ss); ++ /* Failure of ssl3_SetupMasterSecretFromSessionID not considered an ++ * error. Continue with a full handshake. */ ++ if (rv == SECSuccess) { ++ /* Got a Match */ ++ SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits ); + +- ss->sec.authAlgorithm = sid->authAlgorithm; +- ss->sec.authKeyBits = sid->authKeyBits; +- ss->sec.keaType = sid->keaType; +- ss->sec.keaKeyBits = sid->keaKeyBits; ++ /* If we sent a session ticket, then this is a stateless resume. */ ++ if (sid->version > SSL_LIBRARY_VERSION_3_0 && ++ sid->u.ssl3.sessionTicket.ticket.data != NULL) ++ SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes ); + +- /* 3 cases here: +- * a) key is wrapped (implies using PKCS11) +- * b) key is unwrapped, but we're still using PKCS11 +- * c) key is unwrapped, and we're bypassing PKCS11. +- */ +- if (sid->u.ssl3.keys.msIsWrapped) { +- PK11SlotInfo *slot; +- PK11SymKey * wrapKey; /* wrapping key */ +- CK_FLAGS keyFlags = 0; ++ if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) ++ ss->ssl3.hs.ws = wait_new_session_ticket; ++ else ++ ss->ssl3.hs.ws = wait_change_cipher; + +- if (ss->opt.bypassPKCS11) { +- /* we cannot restart a non-bypass session in a +- ** bypass socket. +- */ +- break; +- } +- /* unwrap master secret with PKCS11 */ +- slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID, +- sid->u.ssl3.masterSlotID); +- if (slot == NULL) { +- break; /* not considered an error. */ +- } +- if (!PK11_IsPresent(slot)) { +- PK11_FreeSlot(slot); +- break; /* not considered an error. */ +- } +- wrapKey = PK11_GetWrapKey(slot, sid->u.ssl3.masterWrapIndex, +- sid->u.ssl3.masterWrapMech, +- sid->u.ssl3.masterWrapSeries, +- ss->pkcs11PinArg); +- PK11_FreeSlot(slot); +- if (wrapKey == NULL) { +- break; /* not considered an error. */ +- } ++ ss->ssl3.hs.isResuming = PR_TRUE; + +- if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */ +- keyFlags = CKF_SIGN | CKF_VERIFY; ++ /* copy the peer cert from the SID */ ++ if (sid->peerCert != NULL) { ++ ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); ++ ssl3_CopyPeerCertsFromSID(ss, sid); + } + +- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; +- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; +- pwSpec->master_secret = +- PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech, +- NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE, +- CKA_DERIVE, sizeof(SSL3MasterSecret), keyFlags); +- errCode = PORT_GetError(); +- PK11_FreeSymKey(wrapKey); +- if (pwSpec->master_secret == NULL) { +- break; /* errorCode set just after call to UnwrapSymKey. */ +- } +- } else if (ss->opt.bypassPKCS11) { +- /* MS is not wrapped */ +- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; +- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; +- memcpy(pwSpec->raw_master_secret, wrappedMS.data, wrappedMS.len); +- pwSpec->msItem.data = pwSpec->raw_master_secret; +- pwSpec->msItem.len = wrappedMS.len; +- } else { +- /* We CAN restart a bypass session in a non-bypass socket. */ +- /* need to import the raw master secret to session object */ +- PK11SlotInfo *slot = PK11_GetInternalSlot(); +- wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; +- wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; +- pwSpec->master_secret = +- PK11_ImportSymKey(slot, CKM_SSL3_MASTER_KEY_DERIVE, +- PK11_OriginUnwrap, CKA_ENCRYPT, +- &wrappedMS, NULL); +- PK11_FreeSlot(slot); +- if (pwSpec->master_secret == NULL) { +- break; ++ /* NULL value for PMS signifies re-use of the old MS */ ++ rv = ssl3_InitPendingCipherSpec(ss, NULL); ++ if (rv != SECSuccess) { ++ goto alert_loser; + } ++ return SECSuccess; + } +- +- /* Got a Match */ +- SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits ); +- +- /* If we sent a session ticket, then this is a stateless resume. */ +- if (sid->version > SSL_LIBRARY_VERSION_3_0 && +- sid->u.ssl3.sessionTicket.ticket.data != NULL) +- SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes ); +- +- if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) +- ss->ssl3.hs.ws = wait_new_session_ticket; +- else +- ss->ssl3.hs.ws = wait_change_cipher; +- +- ss->ssl3.hs.isResuming = PR_TRUE; +- +- /* copy the peer cert from the SID */ +- if (sid->peerCert != NULL) { +- ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); +- ssl3_CopyPeerCertsFromSID(ss, sid); +- } +- +- +- /* NULL value for PMS signifies re-use of the old MS */ +- rv = ssl3_InitPendingCipherSpec(ss, NULL); +- if (rv != SECSuccess) { +- goto alert_loser; /* err code was set */ +- } +- return SECSuccess; +- } while (0); ++ } + + if (sid_match) + SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_not_ok ); +@@ -6116,7 +6157,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + * ticket extension, but sent an empty ticket. + */ + if (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) || +- ss->xtnData.emptySessionTicket) { ++ ss->xtnData.serverReceivedEmptySessionTicket) { + if (sidBytes.len > 0 && !ss->opt.noCache) { + SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x", + SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0], +@@ -7569,6 +7610,12 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + return SECFailure; /* malformed */ + } + ++ if (ss->sec.ci.sid->peerCert == NULL) { ++ ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); ++ ssl3_CopyPeerCertsToSID((ssl3CertNode *)ss->ssl3.peerCertChain, ++ ss->sec.ci.sid); ++ } ++ + rv = ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &session_ticket); + if (rv != SECSuccess) { + (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure); +@@ -7760,7 +7807,7 @@ ssl3_CleanupPeerCerts(sslSocket *ss) + ss->ssl3.peerCertChain = NULL; + } + +-static void ++void + ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid) + { + PRArenaPool *arena; +@@ -8163,7 +8210,7 @@ ssl3_RestartHandshakeAfterServerCert(sslSocket *ss) + return rv; + } + +-static SECStatus ++SECStatus + ssl3_ComputeTLSFinished(ssl3CipherSpec *spec, + PRBool isServer, + const SSL3Finished * hashes, +@@ -8211,7 +8258,7 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec, + + /* called from ssl3_HandleServerHelloDone + */ +-static SECStatus ++SECStatus + ssl3_SendNextProto(sslSocket *ss) + { + SECStatus rv; +@@ -8247,7 +8294,7 @@ ssl3_SendNextProto(sslSocket *ss) + * ssl3_HandleClientHello + * ssl3_HandleFinished + */ +-static SECStatus ++SECStatus + ssl3_SendFinished(sslSocket *ss, PRInt32 flags) + { + ssl3CipherSpec *cwSpec; +@@ -8300,10 +8347,27 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags) + if (rv != SECSuccess) + goto fail; /* err set by AppendHandshake. */ + } +- rv = ssl3_FlushHandshake(ss, flags); +- if (rv != SECSuccess) { +- goto fail; /* error code set by ssl3_FlushHandshake */ ++ if ((flags & ssl_SEND_FLAG_NO_FLUSH) == 0) { ++ rv = ssl3_FlushHandshake(ss, flags); ++ if (rv != SECSuccess) { ++ goto fail; /* error code set by ssl3_FlushHandshake */ ++ } + } ++ ++ if ((ss->ssl3.hs.snapStartType == snap_start_recovery || ++ ss->ssl3.hs.snapStartType == snap_start_resume_recovery) && ++ ss->ssl3.snapStartApplicationData.data) { ++ /* In the event that the server ignored the application data in our ++ * snap start extension, we need to retransmit it now. */ ++ PRInt32 sent = ssl3_SendRecord(ss, content_application_data, ++ ss->ssl3.snapStartApplicationData.data, ++ ss->ssl3.snapStartApplicationData.len, ++ flags); ++ SECITEM_FreeItem(&ss->ssl3.snapStartApplicationData, PR_FALSE); ++ if (sent < 0) ++ return (SECStatus)sent; /* error code set by ssl3_SendRecord */ ++ } ++ + return SECSuccess; + + fail: +@@ -8420,6 +8484,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, + return SECFailure; + } + ++ if (ss->ssl3.hs.snapStartType == snap_start_full || ++ ss->ssl3.hs.snapStartType == snap_start_resume) { ++ /* Snap Start handshake was successful. Switch the cipher spec. */ ++ ssl_GetSpecWriteLock(ss); ++ ssl3_DestroyCipherSpec(ss->ssl3.cwSpec, PR_TRUE/*freeSrvName*/); ++ ss->ssl3.cwSpec = ss->ssl3.pwSpec; ++ ss->ssl3.pwSpec = NULL; ++ ssl_ReleaseSpecWriteLock(ss); ++ } ++ + isTLS = (PRBool)(ss->ssl3.crSpec->version > SSL_LIBRARY_VERSION_3_0); + if (isTLS) { + TLSFinished tlsFinished; +@@ -8429,12 +8503,21 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, + PORT_SetError(SSL_ERROR_RX_MALFORMED_FINISHED); + return SECFailure; + } +- rv = ssl3_ComputeTLSFinished(ss->ssl3.crSpec, !isServer, +- hashes, &tlsFinished); +- if (!isServer) +- ss->ssl3.hs.finishedMsgs.tFinished[1] = tlsFinished; +- else +- ss->ssl3.hs.finishedMsgs.tFinished[0] = tlsFinished; ++ ++ if (ss->ssl3.hs.snapStartType == snap_start_resume) { ++ /* In this case we have already advanced the Finished hash past the ++ * server's verify_data because we needed to predict the server's ++ * Finished message in order to compute our own (which includes ++ * it). When we did this, we stored a copy in tFinished[1]. */ ++ tlsFinished = ss->ssl3.hs.finishedMsgs.tFinished[1]; ++ } else { ++ rv = ssl3_ComputeTLSFinished(ss->ssl3.crSpec, !isServer, ++ hashes, &tlsFinished); ++ if (!isServer) ++ ss->ssl3.hs.finishedMsgs.tFinished[1] = tlsFinished; ++ else ++ ss->ssl3.hs.finishedMsgs.tFinished[0] = tlsFinished; ++ } + ss->ssl3.hs.finishedBytes = sizeof tlsFinished; + if (rv != SECSuccess || + 0 != NSS_SecureMemcmp(&tlsFinished, b, length)) { +@@ -8465,8 +8548,9 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, + + ssl_GetXmitBufLock(ss); /*************************************/ + +- if ((isServer && !ss->ssl3.hs.isResuming) || +- (!isServer && ss->ssl3.hs.isResuming)) { ++ if (ss->ssl3.hs.snapStartType != snap_start_resume && ++ ((isServer && !ss->ssl3.hs.isResuming) || ++ (!isServer && ss->ssl3.hs.isResuming))) { + PRInt32 flags = 0; + + /* Send a NewSessionTicket message if the client sent us +@@ -8582,7 +8666,10 @@ xmit_loser: + ss->ssl3.hs.ws = idle_handshake; + + /* Do the handshake callback for sslv3 here, if we cannot false start. */ +- if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) { ++ if (ss->handshakeCallback != NULL && ++ (!ssl3_CanFalseStart(ss) || ++ ss->ssl3.hs.snapStartType == snap_start_full || ++ ss->ssl3.hs.snapStartType == snap_start_resume)) { + (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); + } + +@@ -8643,8 +8730,13 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length) + return rv; + } + } +- /* We should not include hello_request messages in the handshake hashes */ +- if (ss->ssl3.hs.msg_type != hello_request) { ++ /* We should not include hello_request messages in the handshake hashes. ++ * Likewise, for Finished messages from the server during a Snap Start ++ * resume, we have already predicted and included the message in our ++ * Finished hash. */ ++ if (ss->ssl3.hs.msg_type != hello_request && ++ !(ss->ssl3.hs.msg_type == finished && ++ ss->ssl3.hs.snapStartType == snap_start_resume)) { + rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char*) hdr, 4); + if (rv != SECSuccess) return rv; /* err code already set. */ + rv = ssl3_UpdateHandshakeHashes(ss, b, length); +@@ -9545,6 +9637,15 @@ ssl3_DestroySSL3Info(sslSocket *ss) + ss->ssl3.clientCertChain = NULL; + } + ++ if (ss->ssl3.predictedCertChain != NULL) ++ ssl3_CleanupPredictedPeerCertificates(ss); ++ ++ if (ss->ssl3.serverHelloPredictionData.data) ++ SECITEM_FreeItem(&ss->ssl3.serverHelloPredictionData, PR_FALSE); ++ ++ if (ss->ssl3.snapStartApplicationData.data) ++ SECITEM_FreeItem(&ss->ssl3.snapStartApplicationData, PR_FALSE); ++ + /* clean up handshake */ + if (ss->opt.bypassPKCS11) { + SHA1_DestroyContext((SHA1Context *)ss->ssl3.hs.sha_cx, PR_FALSE); +@@ -9562,6 +9663,9 @@ ssl3_DestroySSL3Info(sslSocket *ss) + ss->ssl3.hs.messages.len = 0; + ss->ssl3.hs.messages.space = 0; + } ++ if (ss->ssl3.hs.origClientHello.data) { ++ SECITEM_FreeItem(&ss->ssl3.hs.origClientHello, PR_FALSE); ++ } + + /* free the SSL3Buffer (msg_body) */ + PORT_Free(ss->ssl3.hs.msg_body.buf); +diff --git a/mozilla/security/nss/lib/ssl/ssl3ext.c b/mozilla/security/nss/lib/ssl/ssl3ext.c +index fbd5a91..a7ae062 100644 +--- a/mozilla/security/nss/lib/ssl/ssl3ext.c ++++ b/mozilla/security/nss/lib/ssl/ssl3ext.c +@@ -247,6 +247,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_snap_start_xtn, &ssl3_ClientHandleSnapStartXtn }, + { -1, NULL } + }; + +@@ -270,7 +271,9 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { + { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, + #endif + { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, +- { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn } ++ { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn }, ++ { ssl_snap_start_xtn, &ssl3_SendSnapStartXtn } ++ /* NOTE: The Snap Start sender MUST be the last extension in the list. */ + /* any extra entries will appear as { 0, NULL } */ + }; + +@@ -298,7 +301,7 @@ ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type) { + xtnData->numNegotiated, ex_type); + } + +-static PRBool ++PRBool + ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type) { + TLSExtensionData *xtnData = &ss->xtnData; + return arrayContainsExtension(xtnData->advertised, +@@ -515,6 +518,8 @@ ssl3_SendSessionTicketXtn( + rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data, + session_ticket->ticket.len, 2); + ss->xtnData.ticketTimestampVerified = PR_FALSE; ++ if (!ss->sec.isServer) ++ ss->xtnData.clientSentNonEmptySessionTicket = PR_TRUE; + } else { + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + } +@@ -573,7 +578,7 @@ ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) + + SECStatus + ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, +- SECItem *data) ++ SECItem *data) + { + unsigned int i, j; + SECStatus rv; +@@ -1021,7 +1026,7 @@ ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, + * instead of terminating the current connection. + */ + if (data->len == 0) { +- ss->xtnData.emptySessionTicket = PR_TRUE; ++ ss->xtnData.serverReceivedEmptySessionTicket = PR_TRUE; + } else { + int i; + SECItem extension_data; +diff --git a/mozilla/security/nss/lib/ssl/sslimpl.h b/mozilla/security/nss/lib/ssl/sslimpl.h +index fe7ac7a..f708696 100644 +--- a/mozilla/security/nss/lib/ssl/sslimpl.h ++++ b/mozilla/security/nss/lib/ssl/sslimpl.h +@@ -278,6 +278,7 @@ struct sslSocketOpsStr { + /* Flags interpreted by ssl send functions. */ + #define ssl_SEND_FLAG_FORCE_INTO_BUFFER 0x40000000 + #define ssl_SEND_FLAG_NO_BUFFER 0x20000000 ++#define ssl_SEND_FLAG_NO_FLUSH 0x10000000 + #define ssl_SEND_FLAG_MASK 0x7f000000 + + /* +@@ -339,6 +340,7 @@ typedef struct sslOptionsStr { + unsigned int enableRenegotiation : 2; /* 20-21 */ + unsigned int requireSafeNegotiation : 1; /* 22 */ + unsigned int enableFalseStart : 1; /* 23 */ ++ unsigned int enableSnapStart : 1; /* 24 */ + } sslOptions; + + typedef enum { sslHandshakingUndetermined = 0, +@@ -743,7 +745,8 @@ struct TLSExtensionDataStr { + + /* SessionTicket Extension related data. */ + PRBool ticketTimestampVerified; +- PRBool emptySessionTicket; ++ PRBool serverReceivedEmptySessionTicket; ++ PRBool clientSentNonEmptySessionTicket; + + /* SNI Extension related data + * Names data is not coppied from the input buffer. It can not be +@@ -753,6 +756,14 @@ struct TLSExtensionDataStr { + PRUint32 sniNameArrSize; + }; + ++typedef enum { ++ snap_start_none = 0, ++ snap_start_full, ++ snap_start_recovery, ++ snap_start_resume, ++ snap_start_resume_recovery ++} TLSSnapStartType; ++ + /* + ** This is the "hs" member of the "ssl3" struct. + ** This entire struct is protected by ssl3HandshakeLock +@@ -791,6 +802,14 @@ const ssl3CipherSuiteDef *suite_def; + SSL3Hashes sFinished[2]; + SSL3Opaque data[72]; + } finishedMsgs; ++ ++ TLSSnapStartType snapStartType; ++ /* When we perform a Snap Start handshake, we hash our ClientHello as if ++ * the Snap Start extension wasn't included. However, if the server rejects ++ * our Snap Start attempt, then it will hash the whole ClientHello. Thus we ++ * store the original ClientHello that we sent in case we need to reset our ++ * Finished hash to cover it. */ ++ SECItem origClientHello; + #ifdef NSS_ENABLE_ECC + PRUint32 negotiatedECCurves; /* bit mask */ + #endif /* NSS_ENABLE_ECC */ +@@ -823,6 +842,17 @@ struct ssl3StateStr { + CERTCertificateList *clientCertChain; /* used by client */ + PRBool sendEmptyCert; /* used by client */ + ++ /* TLS Snap Start: */ ++ CERTCertificate ** predictedCertChain; ++ /* An array terminated with a NULL. */ ++ SECItem serverHelloPredictionData; ++ PRBool serverHelloPredictionDataValid; ++ /* data needed to predict the ServerHello from ++ * this server. */ ++ SECItem snapStartApplicationData; ++ /* the application data to include in the Snap ++ * Start extension. */ ++ + int policy; + /* This says what cipher suites we can do, and should + * be either SSL_ALLOWED or SSL_RESTRICTED +@@ -1258,10 +1288,13 @@ extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server); + extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, + const char *peerID, const char *urlSvrName); + extern void ssl_FreeSID(sslSessionID *sid); ++extern void ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid); + + extern int ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in, + int len, int flags); + ++extern SECStatus ssl3_RestartHandshakeHashes(sslSocket *ss); ++ + extern PRBool ssl_FdIsBlocking(PRFileDesc *fd); + + extern PRBool ssl_SocketIsBlocking(sslSocket *ss); +@@ -1434,6 +1467,9 @@ ECName ssl3_GetCurveWithECKeyStrength(PRUint32 curvemsk, int requiredECCbits); + + #endif /* NSS_ENABLE_ECC */ + ++extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket* ss, unsigned char *b, ++ unsigned int l); ++ + extern SECStatus ssl3_CipherPrefSetDefault(ssl3CipherSuite which, PRBool on); + extern SECStatus ssl3_CipherPrefGetDefault(ssl3CipherSuite which, PRBool *on); + extern SECStatus ssl2_CipherPrefSetDefault(PRInt32 which, PRBool enabled); +@@ -1454,6 +1490,7 @@ extern void ssl3_InitSocketPolicy(sslSocket *ss); + + extern SECStatus ssl3_ConstructV2CipherSpecsHack(sslSocket *ss, + unsigned char *cs, int *size); ++extern void ssl3_DestroyCipherSpec(ssl3CipherSpec* spec, PRBool freeSrvName); + + extern SECStatus ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache); + +@@ -1503,6 +1540,31 @@ extern SECStatus ssl3_VerifySignedHashes(SSL3Hashes *hash, + extern SECStatus ssl3_CacheWrappedMasterSecret(sslSocket *ss, + sslSessionID *sid, ssl3CipherSpec *spec, + SSL3KEAType effectiveExchKeyType); ++extern void ssl3_CleanupPredictedPeerCertificates(sslSocket *ss); ++extern const ssl3CipherSuiteDef* ssl_LookupCipherSuiteDef(ssl3CipherSuite suite); ++extern SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss); ++extern SECStatus ssl3_SendClientKeyExchange(sslSocket *ss); ++extern SECStatus ssl3_SendNextProto(sslSocket *ss); ++extern SECStatus ssl3_SendFinished(sslSocket *ss, PRInt32 flags); ++extern SECStatus ssl3_CompressMACEncryptRecord ++ (sslSocket * ss, ++ SSL3ContentType type, ++ const SSL3Opaque * pIn, ++ PRUint32 contentLen); ++extern PRBool ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type); ++extern SECStatus ssl3_SetupMasterSecretFromSessionID(sslSocket* ss); ++extern SECStatus ssl3_ComputeHandshakeHashes( ++ sslSocket * ss, ++ ssl3CipherSpec *spec, /* uses ->master_secret */ ++ SSL3Hashes * hashes, /* output goes here. */ ++ PRUint32 sender); ++extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket* ss, unsigned char *b, ++ unsigned int l); ++extern SECStatus ssl3_ComputeTLSFinished( ++ ssl3CipherSpec *spec, ++ PRBool isServer, ++ const SSL3Finished * hashes, ++ TLSFinished * tlsFinished); + + /* Functions that handle ClientHello and ServerHello extensions. */ + extern SECStatus ssl3_HandleServerNameXtn(sslSocket * ss, +@@ -1532,6 +1594,13 @@ extern PRInt32 ssl3_SendSessionTicketXtn(sslSocket *ss, PRBool append, + */ + extern PRInt32 ssl3_SendServerNameXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); ++extern PRInt32 ssl3_SendSnapStartXtn(sslSocket *ss, PRBool append, ++ PRUint32 maxBytes); ++extern SECStatus ssl3_ClientHandleSnapStartXtn(sslSocket *ss, PRUint16 ex_type, ++ SECItem *data); ++ ++extern SECStatus ssl3_ResetForSnapStartRecovery(sslSocket *ss, ++ SSL3Opaque *b, PRUint32 length); + + /* Assigns new cert, cert chain and keys to ss->serverCerts + * struct. If certChain is NULL, tries to find one. Aborts if +@@ -1635,6 +1704,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 ca0d714..2898b88 100644 +--- a/mozilla/security/nss/lib/ssl/sslsock.c ++++ b/mozilla/security/nss/lib/ssl/sslsock.c +@@ -738,6 +738,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on) + ss->opt.enableFalseStart = on; + break; + ++ case SSL_ENABLE_SNAP_START: ++ ss->opt.enableSnapStart = on; ++ break; ++ + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; +@@ -802,6 +806,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn) + case SSL_REQUIRE_SAFE_NEGOTIATION: + on = ss->opt.requireSafeNegotiation; break; + case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break; ++ case SSL_ENABLE_SNAP_START: on = ss->opt.enableSnapStart; break; + + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); +@@ -853,6 +858,7 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn) + on = ssl_defaults.requireSafeNegotiation; + break; + case SSL_ENABLE_FALSE_START: on = ssl_defaults.enableFalseStart; break; ++ case SSL_ENABLE_SNAP_START: on = ssl_defaults.enableSnapStart; break; + + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); +@@ -1000,6 +1006,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on) + ssl_defaults.enableFalseStart = on; + break; + ++ case SSL_ENABLE_SNAP_START: ++ ssl_defaults.enableSnapStart = 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 f6e0b62..68cbf87 100644 +--- a/mozilla/security/nss/lib/ssl/sslt.h ++++ b/mozilla/security/nss/lib/ssl/sslt.h +@@ -204,9 +204,23 @@ typedef enum { + #endif + ssl_session_ticket_xtn = 35, + ssl_next_proto_neg_xtn = 13172, ++ ssl_snap_start_xtn = 13174, + ssl_renegotiation_info_xtn = 0xff01 /* experimental number */ + } SSLExtensionType; + +-#define SSL_MAX_EXTENSIONS 6 ++#define SSL_MAX_EXTENSIONS 7 ++ ++typedef enum { ++ /* No Snap Start handshake was attempted. */ ++ SSL_SNAP_START_NONE = 0, ++ /* A Snap Start full handshake was completed. */ ++ SSL_SNAP_START_FULL = 1, ++ /* A Snap Start full handshake was attempted, but failed. */ ++ SSL_SNAP_START_RECOVERY = 2, ++ /* A Snap Start resume handshake was completed. */ ++ SSL_SNAP_START_RESUME = 3, ++ /* A Snap Start resume handshake was attempted, but failed. */ ++ SSL_SNAP_START_RESUME_RECOVERY = 4 ++} SSLSnapStartResult; + + #endif /* __sslt_h_ */ |