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_ */