summaryrefslogtreecommitdiffstats
path: root/net/third_party
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-25 14:12:07 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-25 14:12:07 +0000
commit4922601438eda94cfabb30ee6cafb24dda74dd0b (patch)
tree95105eac6f739fee8ba8386d1925614d96ca3fff /net/third_party
parent437aa8af89f4a8ad08d33c5501eb8dd4685007a6 (diff)
downloadchromium_src-4922601438eda94cfabb30ee6cafb24dda74dd0b.zip
chromium_src-4922601438eda94cfabb30ee6cafb24dda74dd0b.tar.gz
chromium_src-4922601438eda94cfabb30ee6cafb24dda74dd0b.tar.bz2
NSS: Add Snap Start support
This patch adds NSS support for Snap Start, an experimental TLS extension for zero round trip handshakes. This change does not enable Snap Start. BUG=none TEST=External unittests (not in tree) git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57325 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/third_party')
-rw-r--r--net/third_party/nss/ssl.gyp2
-rw-r--r--net/third_party/nss/ssl/fnv1a64.c72
-rw-r--r--net/third_party/nss/ssl/snapstart.c985
-rw-r--r--net/third_party/nss/ssl/ssl.def10
-rw-r--r--net/third_party/nss/ssl/ssl.h52
-rw-r--r--net/third_party/nss/ssl/ssl3con.c115
-rw-r--r--net/third_party/nss/ssl/ssl3ext.c5
-rw-r--r--net/third_party/nss/ssl/sslimpl.h47
-rw-r--r--net/third_party/nss/ssl/sslsock.c10
-rw-r--r--net/third_party/nss/ssl/sslt.h16
10 files changed, 1299 insertions, 15 deletions
diff --git a/net/third_party/nss/ssl.gyp b/net/third_party/nss/ssl.gyp
index 3166be8..a804e92 100644
--- a/net/third_party/nss/ssl.gyp
+++ b/net/third_party/nss/ssl.gyp
@@ -28,11 +28,13 @@
'ssl/authcert.c',
'ssl/cmpcert.c',
'ssl/derive.c',
+ 'ssl/fnv1a64.c',
'ssl/nsskea.c',
'ssl/os2_err.c',
'ssl/os2_err.h',
'ssl/preenc.h',
'ssl/prelib.c',
+ 'ssl/snapstart.c',
'ssl/ssl.h',
'ssl/ssl3con.c',
'ssl/ssl3ecc.c',
diff --git a/net/third_party/nss/ssl/fnv1a64.c b/net/third_party/nss/ssl/fnv1a64.c
new file mode 100644
index 0000000..c7c4b08
--- /dev/null
+++ b/net/third_party/nss/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/net/third_party/nss/ssl/snapstart.c b/net/third_party/nss/ssl/snapstart.c
new file mode 100644
index 0000000..4999ae2
--- /dev/null
+++ b/net/third_party/nss/ssl/snapstart.c
@@ -0,0 +1,985 @@
+/*
+ * 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.
+** 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, 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];
+ SECItem extensions;
+ 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_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;
+
+ 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, 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);
+ }
+
+ 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);
+
+ /* 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) {
+ /* This is a no-op handler for when the server echos our Snap Start
+ * extension. The work of saving the ServerHello is done in
+ * ssl3_HandleServerHello, where its contents are available. */
+ 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;
+ }
+
+ *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/net/third_party/nss/ssl/ssl.def b/net/third_party/nss/ssl/ssl.def
index a1f4b51..effc35d 100644
--- a/net/third_party/nss/ssl/ssl.def
+++ b/net/third_party/nss/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/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h
index d87ae56..8217d2e 100644
--- a/net/third_party/nss/ssl/ssl.h
+++ b/net/third_party/nss/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/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
index 050223a..13d91507 100644
--- a/net/third_party/nss/ssl/ssl3con.c
+++ b/net/third_party/nss/ssl/ssl3con.c
@@ -1166,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);
@@ -2791,6 +2791,16 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
SSL_TRC(3, ("%d: SSL3[%d] Set Current Read Cipher Suite to Pending",
SSL_GETPID(), ss->fd ));
+ if (ss->ssl3.hs.snapStartType == snap_start_resume) {
+ /* If the server sent us a ChangeCipherSpec message then our Snap Start
+ * resume handshake was successful and we need to switch our current
+ * write cipher spec to reflect the ChangeCipherSpec message embedded
+ * in the ClientHello that the server has now processed. */
+ ssl3_DestroyCipherSpec(ss->ssl3.cwSpec, PR_TRUE/*freeSrvName*/);
+ ss->ssl3.cwSpec = ss->ssl3.pwSpec;
+ ss->ssl3.pwSpec = NULL;
+ }
+
/* If we are really through with the old cipher prSpec
* (Both the read and write sides have changed) destroy it.
*/
@@ -3094,7 +3104,7 @@ loser:
return SECFailure;
}
-static SECStatus
+SECStatus
ssl3_RestartHandshakeHashes(sslSocket *ss)
{
SECStatus rv = SECSuccess;
@@ -4029,7 +4039,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;
}
@@ -4971,6 +4992,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. */
@@ -4982,6 +5011,15 @@ 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);
+ }
+
temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (temp < 0) {
goto loser; /* alert has been sent */
@@ -7559,6 +7597,15 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
return SECFailure;
}
+ if (ss->ssl3.hs.snapStartType == snap_start_full) {
+ /* 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);
+ }
+
session_ticket.received_timestamp = ssl_Time();
if (length < 4) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
@@ -8313,6 +8360,21 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags)
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:
@@ -8438,12 +8500,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)) {
@@ -8474,8 +8545,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
@@ -8652,8 +8724,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);
@@ -9554,6 +9631,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);
@@ -9571,6 +9657,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/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c
index d291be6..648389f 100644
--- a/net/third_party/nss/ssl/ssl3ext.c
+++ b/net/third_party/nss/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 } */
};
diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h
index 5fe45d2..487d658 100644
--- a/net/third_party/nss/ssl/sslimpl.h
+++ b/net/third_party/nss/ssl/sslimpl.h
@@ -340,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,
@@ -755,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
@@ -793,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 */
@@ -825,6 +842,16 @@ 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;
+ /* 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
@@ -1264,6 +1291,8 @@ extern void ssl_FreeSID(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);
@@ -1436,6 +1465,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);
@@ -1456,6 +1488,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);
@@ -1505,6 +1538,7 @@ 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);
@@ -1557,6 +1591,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
@@ -1660,6 +1701,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/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
index ca0d714..2898b88 100644
--- a/net/third_party/nss/ssl/sslsock.c
+++ b/net/third_party/nss/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/net/third_party/nss/ssl/sslt.h b/net/third_party/nss/ssl/sslt.h
index f6e0b62..68cbf87 100644
--- a/net/third_party/nss/ssl/sslt.h
+++ b/net/third_party/nss/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_ */