From da7b6f8aafdfb074f4096d9714b6e6f26bf59f44 Mon Sep 17 00:00:00 2001 From: EKR Date: Tue, 1 Aug 2017 10:33:43 +1000 Subject: [PATCH] Bug 1386096 - Semi-stateless HelloRetryRequest, r=mt --HG-- branch : NSS_TLS13_DRAFT19_BRANCH --- gtests/ssl_gtest/ssl_custext_unittest.cc | 1 + lib/ssl/manifest.mn | 1 + lib/ssl/selfencrypt.c | 13 ++ lib/ssl/selfencrypt.h | 4 + lib/ssl/ssl.gyp | 1 + lib/ssl/ssl3con.c | 62 ++++++-- lib/ssl/ssl3ext.c | 13 ++ lib/ssl/ssl3ext.h | 15 +- lib/ssl/ssl3exthandle.c | 8 ++ lib/ssl/sslimpl.h | 15 +- lib/ssl/tls13con.c | 176 ++++++++++++++++------- lib/ssl/tls13con.h | 10 +- lib/ssl/tls13err.h | 28 ++++ lib/ssl/tls13exthandle.c | 77 +++++++++- lib/ssl/tls13exthandle.h | 9 ++ lib/ssl/tls13hashstate.c | 153 ++++++++++++++++++++ lib/ssl/tls13hashstate.h | 22 +++ 17 files changed, 531 insertions(+), 77 deletions(-) create mode 100644 lib/ssl/tls13err.h create mode 100644 lib/ssl/tls13hashstate.c create mode 100644 lib/ssl/tls13hashstate.h diff --git a/gtests/ssl_gtest/ssl_custext_unittest.cc b/gtests/ssl_gtest/ssl_custext_unittest.cc index d5485690cd..77a941f420 100644 --- a/gtests/ssl_gtest/ssl_custext_unittest.cc +++ b/gtests/ssl_gtest/ssl_custext_unittest.cc @@ -119,6 +119,7 @@ TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopServer) { TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterClient) { EnsureTlsSetup(); InstallManyWriters(client_, EmptyExtensionWriter); + InstallManyWriters(server_, EmptyExtensionWriter); Connect(); } diff --git a/lib/ssl/manifest.mn b/lib/ssl/manifest.mn index 0f43142890..928c710904 100644 --- a/lib/ssl/manifest.mn +++ b/lib/ssl/manifest.mn @@ -49,6 +49,7 @@ CSRCS = \ ssl3ecc.c \ tls13con.c \ tls13exthandle.c \ + tls13hashstate.c \ tls13hkdf.c \ tls13replay.c \ sslcert.c \ diff --git a/lib/ssl/selfencrypt.c b/lib/ssl/selfencrypt.c index 8c9e374b03..d00ddf7d9a 100644 --- a/lib/ssl/selfencrypt.c +++ b/lib/ssl/selfencrypt.c @@ -269,6 +269,19 @@ ssl_SelfEncryptUnprotectInt( } #endif +/* Predict the size of the encrypted data, including padding */ +SECStatus +ssl_SelfEncryptGetProtectedSize(unsigned int inLen, unsigned int *outLen) +{ + *outLen = SELF_ENCRYPT_KEY_NAME_LEN + + AES_BLOCK_SIZE + + 2 + + ((inLen / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE + /* Padded */ + SHA256_LENGTH; + + return SECSuccess; +} + SECStatus ssl_SelfEncryptProtect( sslSocket *ss, const PRUint8 *in, unsigned int inLen, diff --git a/lib/ssl/selfencrypt.h b/lib/ssl/selfencrypt.h index 5bc8e4348a..d65d3e5b27 100644 --- a/lib/ssl/selfencrypt.h +++ b/lib/ssl/selfencrypt.h @@ -11,6 +11,10 @@ #include "secmodt.h" +typedef struct sslSocketStr sslSocket; + +SECStatus ssl_SelfEncryptGetProtectedSize(unsigned int inLen, + unsigned int *outLen); SECStatus ssl_SelfEncryptProtect( sslSocket *ss, const PRUint8 *in, unsigned int inLen, PRUint8 *out, unsigned int *outLen, unsigned int maxOutLen); diff --git a/lib/ssl/ssl.gyp b/lib/ssl/ssl.gyp index 0870a29dce..c7f853245d 100644 --- a/lib/ssl/ssl.gyp +++ b/lib/ssl/ssl.gyp @@ -42,6 +42,7 @@ 'sslver.c', 'tls13con.c', 'tls13exthandle.c', + 'tls13hashstate.c', 'tls13hkdf.c', 'tls13replay.c', ], diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index 1cc53356cb..da472b1a19 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -52,7 +52,9 @@ static SECStatus ssl3_SendServerKeyExchange(sslSocket *ss); static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss, SECItem *suites, SECItem *comps, - sslSessionID *sid); + sslSessionID *sid, + const PRUint8 *msg, + unsigned int len); static SECStatus ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes, int *retErrCode); @@ -355,8 +357,7 @@ static const ssl3CipherSuiteDef cipher_suite_defs[] = {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_dhe_rsa, ssl_hash_none}, - -/* New TLS cipher suites */ + /* New TLS cipher suites */ {TLS_RSA_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_rsa, ssl_hash_none}, {TLS_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_rsa, ssl_hash_sha256}, {TLS_DHE_DSS_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_dhe_dss, ssl_hash_none}, @@ -4004,11 +4005,11 @@ ssl3_InitHandshakeHashes(sslSocket *ss) return SECFailure; } ss->ssl3.hs.hashType = handshake_hash_single; - if (PK11_DigestBegin(ss->ssl3.hs.sha) != SECSuccess) { ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); return SECFailure; } + } else { /* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or * created successfully. */ @@ -4931,6 +4932,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) unsigned numCompressionMethods; PRUint16 version; PRInt32 flags; + unsigned int cookieLen = ss->ssl3.hs.cookie.len; SSL_TRC(3, ("%d: SSL3[%d]: send %s ClientHello handshake", SSL_GETPID(), ss->fd, ssl_ClientHelloTypeName(type))); @@ -4949,6 +4951,9 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) * to maintain the handshake hashes. */ if (ss->ssl3.hs.helloRetry) { PORT_Assert(type == client_hello_retry); + /* This cookieLen applies to the cookie that appears in the DTLS + ClientHello, which isn't used in DTLS 1.3. */ + cookieLen = 0; } else { ssl3_InitState(ss); ssl3_RestartHandshakeHashes(ss); @@ -5208,7 +5213,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) 2 + num_suites * sizeof(ssl3CipherSuite) + 1 + numCompressionMethods; if (IS_DTLS(ss)) { - length += 1 + ss->ssl3.hs.cookie.len; + length += 1 + cookieLen; } if (extensionBuf.len) { @@ -5266,7 +5271,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) if (IS_DTLS(ss)) { rv = ssl3_AppendHandshakeVariable( - ss, ss->ssl3.hs.cookie.data, ss->ssl3.hs.cookie.len, 1); + ss, ss->ssl3.hs.cookie.data, cookieLen, 1); if (rv != SECSuccess) { goto loser; /* err set by ssl3_AppendHandshake* */ } @@ -8234,6 +8239,8 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) SECItem suites = { siBuffer, NULL, 0 }; SECItem comps = { siBuffer, NULL, 0 }; PRBool isTLS13; + const PRUint8 *savedMsg = b; + const PRUint32 savedLen = length; SSL_TRC(3, ("%d: SSL3[%d]: handle client_hello handshake", SSL_GETPID(), ss->fd)); @@ -8265,6 +8272,9 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) } } + /* We should always be in a fresh state. */ + SSL_ASSERT_HASHES_EMPTY(ss); + /* Get peer name of client */ rv = ssl_GetPeerInfo(ss); if (rv != SECSuccess) { @@ -8312,6 +8322,9 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) if (rv != SECSuccess) { goto loser; /* malformed */ } + if (cookieBytes.len != 0) { + goto loser; /* We never send cookies in DTLS 1.2. */ + } } /* Grab the list of cipher suites. */ @@ -8579,9 +8592,10 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) #endif if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - rv = tls13_HandleClientHelloPart2(ss, &suites, sid); + rv = tls13_HandleClientHelloPart2(ss, &suites, sid, savedMsg, savedLen); } else { - rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid); + rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid, + savedMsg, savedLen); } if (rv != SECSuccess) { errCode = PORT_GetError(); @@ -8601,7 +8615,9 @@ static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss, SECItem *suites, SECItem *comps, - sslSessionID *sid) + sslSessionID *sid, + const PRUint8 *msg, + unsigned int len) { PRBool haveSpecWriteLock = PR_FALSE; PRBool haveXmitBufLock = PR_FALSE; @@ -8611,6 +8627,13 @@ ssl3_HandleClientHelloPart2(sslSocket *ss, unsigned int i; int j; + rv = ssl_HashHandshakeMessage(ss, ssl_hs_client_hello, msg, len); + if (rv != SECSuccess) { + errCode = SEC_ERROR_LIBRARY_FAILURE; + desc = internal_error; + goto alert_loser; + } + /* If we already have a session for this client, be sure to pick the ** same cipher suite and compression method we picked before. ** This is not a loop, despite appearances. @@ -11571,8 +11594,9 @@ ssl3_FinishHandshake(sslSocket *ss) } SECStatus -ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, - const PRUint8 *b, PRUint32 length) +ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type, + PRUint32 dtlsSeq, + const PRUint8 *b, PRUint32 length) { PRUint8 hdr[4]; PRUint8 dtlsData[8]; @@ -11590,8 +11614,8 @@ ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, /* Extra data to simulate a complete DTLS handshake fragment */ if (IS_DTLS(ss)) { /* Sequence number */ - dtlsData[0] = MSB(ss->ssl3.hs.recvMessageSeq); - dtlsData[1] = LSB(ss->ssl3.hs.recvMessageSeq); + dtlsData[0] = MSB(dtlsSeq); + dtlsData[1] = LSB(dtlsSeq); /* Fragment offset */ dtlsData[2] = 0; @@ -11617,6 +11641,14 @@ ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, return SECSuccess; } +SECStatus +ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, + const PRUint8 *b, PRUint32 length) +{ + return ssl_HashHandshakeMessageInt(ss, type, ss->ssl3.hs.recvMessageSeq, + b, length); +} + /* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3 * handshake message. * Caller must hold Handshake and RecvBuf locks. @@ -11646,11 +11678,11 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length, * in the handshake hashes */ break; + /* Defer hashing of these messages until the message handlers. */ + case ssl_hs_client_hello: case ssl_hs_hello_retry_request: case ssl_hs_certificate_verify: case ssl_hs_finished: - /* Defer hashing of these messages until the message handlers - * we need to finalize the hashes there. */ break; default: diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c index fb9a145cde..dfee32fddb 100644 --- a/lib/ssl/ssl3ext.c +++ b/lib/ssl/ssl3ext.c @@ -14,6 +14,7 @@ #include "sslimpl.h" #include "sslproto.h" #include "ssl3exthandle.h" +#include "tls13err.h" #include "tls13exthandle.h" /* Callback function that handles a received extension. */ @@ -49,6 +50,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = { { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn }, { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn }, + { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn }, { 0, NULL } }; @@ -151,6 +153,12 @@ static const sslExtensionBuilder tls13_cert_req_senders[] = { { 0, NULL } }; +static const sslExtensionBuilder tls13_hrr_senders[] = { + { ssl_tls13_key_share_xtn, &tls13_ServerSendHrrKeyShareXtn }, + { ssl_tls13_cookie_xtn, &tls13_ServerSendHrrCookieXtn }, + { 0, NULL } +}; + static const struct { SSLExtensionType type; SSLExtensionSupport support; @@ -714,6 +722,11 @@ ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message) sender = ss->xtnData.encryptedExtensionsSenders; break; + case ssl_hs_hello_retry_request: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + sender = tls13_hrr_senders; + break; + default: PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); diff --git a/lib/ssl/ssl3ext.h b/lib/ssl/ssl3ext.h index 8f2722e65c..dac44af55d 100644 --- a/lib/ssl/ssl3ext.h +++ b/lib/ssl/ssl3ext.h @@ -86,12 +86,15 @@ struct TLSExtensionDataStr { PRUint16 dtlsSRTPCipherSuite; /* 0 if not selected */ - unsigned int lastXtnOffset; /* Where to insert padding. 0 = end. */ - SECItem pskBinder; /* The PSK binder for the first PSK (TLS 1.3) */ - unsigned int pskBinderPrefixLen; /* The length of the binder input. */ - PRCList remoteKeyShares; /* The other side's public keys (TLS 1.3) */ - /* This is used when deciding whether to accept early data. */ - PRUint32 ticketAge; + unsigned int lastXtnOffset; /* Where to insert padding. 0 = end. */ + PRCList remoteKeyShares; /* The other side's public keys (TLS 1.3) */ + + /* The following are used by a TLS 1.3 server. */ + SECItem pskBinder; /* The binder for the first PSK. */ + unsigned int pskBindersLen; /* The length of the binders. */ + PRUint32 ticketAge; /* Used to accept early data. */ + SECItem cookie; /* HRR Cookie. */ + const sslNamedGroupDef *selectedGroup; /* For HRR. */ }; typedef struct TLSExtensionStr { diff --git a/lib/ssl/ssl3exthandle.c b/lib/ssl/ssl3exthandle.c index ea9b49c30b..2911367cf9 100644 --- a/lib/ssl/ssl3exthandle.c +++ b/lib/ssl/ssl3exthandle.c @@ -971,6 +971,8 @@ ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData return SECSuccess; } +PR_STATIC_ASSERT((TLS_EX_SESS_TICKET_VERSION >> 8) == 1); + static SECStatus ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket, SessionTicket *parsedTicket) @@ -997,6 +999,12 @@ ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket, return SECFailure; } + /* All ticket versions start with 0x01, so check to see if this + * is a ticket or some other self-encrypted thing. */ + if ((temp >> 8) != 1) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; + } /* Skip the ticket if the version is wrong. This won't result in a * handshake failure, just a failure to resume. */ if (temp != TLS_EX_SESS_TICKET_VERSION) { diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index 6ffbaef76d..559b549d27 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -35,6 +35,7 @@ typedef struct sslSocketStr sslSocket; typedef struct ssl3CipherSpecStr ssl3CipherSpec; +typedef struct sslNamedGroupDefStr sslNamedGroupDef; #include "sslexp.h" #include "ssl3ext.h" #include "sslencode.h" @@ -162,7 +163,7 @@ typedef enum { ticket_allow_psk_sign_auth = 16 } TLS13SessionTicketFlags; -typedef struct { +struct sslNamedGroupDefStr { /* The name is the value that is encoded on the wire in TLS. */ SSLNamedGroup name; /* The number of bits in the group. */ @@ -174,7 +175,7 @@ typedef struct { SECOidTag oidTag; /* Assume that the group is always supported. */ PRBool assumeSupported; -} sslNamedGroupDef; +}; typedef struct sslConnectInfoStr sslConnectInfo; typedef struct sslGatherStr sslGather; @@ -869,6 +870,12 @@ typedef struct SSL3HandshakeStateStr { PRUint16 ticketNonce; /* A counter we use for tickets. */ } SSL3HandshakeState; +#define SSL_ASSERT_HASHES_EMPTY(ss) \ + do { \ + PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_unknown); \ + PORT_Assert(ss->ssl3.hs.messages.len == 0); \ + } while (0) + /* ** This is the "ssl3" struct, as in "ss->ssl3". ** note: @@ -1350,6 +1357,10 @@ extern void ssl3_RestartHandshakeHashes(sslSocket *ss); extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l); +SECStatus +ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type, + PRUint32 dtlsSeq, + const PRUint8 *b, PRUint32 length); SECStatus ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, const PRUint8 *b, PRUint32 length); diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 4f01b02632..87c27d5acb 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -20,7 +20,9 @@ #include "ssl3exthandle.h" #include "tls13hkdf.h" #include "tls13con.h" +#include "tls13err.h" #include "tls13exthandle.h" +#include "tls13hashstate.h" typedef enum { TrafficKeyClearText = 0, @@ -102,8 +104,6 @@ static SECStatus tls13_SendNewSessionTicket(sslSocket *ss, unsigned int appTokenLen); static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length); -static SECStatus tls13_ComputeHandshakeHashes(sslSocket *ss, - SSL3Hashes *hashes); static SECStatus tls13_ComputeEarlySecrets(sslSocket *ss); static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss); static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss); @@ -1265,7 +1265,9 @@ tls13_NegotiateAuthentication(sslSocket *ss) SECStatus tls13_HandleClientHelloPart2(sslSocket *ss, const SECItem *suites, - sslSessionID *sid) + sslSessionID *sid, + const PRUint8 *msg, + unsigned int len) { SECStatus rv; SSL3Statistics *ssl3stats = SSL_GetStatistics(); @@ -1294,9 +1296,36 @@ tls13_HandleClientHelloPart2(sslSocket *ss, } /* If we are going around again, then we should make sure that the cipher * suite selection doesn't change. That's a sign of client shennanigans. */ - if (ss->ssl3.hs.helloRetry && - ss->ssl3.hs.cipher_suite != previousCipherSuite) { - FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure); + if (ss->ssl3.hs.helloRetry) { + if (ss->ssl3.hs.cipher_suite != previousCipherSuite) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure); + goto loser; + } + if (!ss->xtnData.cookie.len) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure); + goto loser; + } + PRINT_BUF(50, (ss, "Client sent cookie", + ss->xtnData.cookie.data, ss->xtnData.cookie.len)); + + rv = tls13_RecoverHashState(ss, ss->xtnData.cookie.data, ss->xtnData.cookie.len); + if (rv != SECSuccess) { + goto loser; + } + } else { + if (ss->xtnData.cookie.len) { + /* Client shouldn't be sending a cookie if we're not doing HRR. + * TODO(ekr@rtfm.com): Remove this when we go to totally stateless. + */ + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure); + goto loser; + } + } + + /* Now merge the ClientHello into the hash state. */ + rv = ssl_HashHandshakeMessage(ss, ssl_hs_client_hello, msg, len); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } @@ -1401,8 +1430,11 @@ tls13_HandleClientHelloPart2(sslSocket *ss, if (ss->statelessResume) { SSL3Hashes hashes; - rv = tls13_ComputePskBinderHash(ss, ss->xtnData.pskBinderPrefixLen, - &hashes); + PORT_Assert(ss->ssl3.hs.messages.len > ss->xtnData.pskBindersLen); + rv = tls13_ComputePskBinderHash( + ss, + ss->ssl3.hs.messages.len - ss->xtnData.pskBindersLen, + &hashes); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; @@ -1485,80 +1517,114 @@ tls13_HandleClientHelloPart2(sslSocket *ss, return SECFailure; } -static SECStatus -tls13_SendHelloRetryRequest(sslSocket *ss, const sslNamedGroupDef *selectedGroup) +/* + * struct { + * ProtocolVersion server_version; + * CipherSuite cipher_suite; + * Extension extensions<2..2^16-1>; + * } HelloRetryRequest; + * + * Note: this function takes an empty buffer and returns + * a non-empty one on success, in which case the caller must + * eventually clean up. + */ +SECStatus +tls13_ConstructHelloRetryRequest(sslSocket *ss, + const sslNamedGroupDef *selectedGroup, + PRUint8 *cookie, unsigned int cookieLen, + sslBuffer *buffer) { SECStatus rv; + sslBuffer extensionsBuf = { NULL, 0, 0 }; + PORT_Assert(buffer->len == 0); - SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake", - SSL_GETPID(), ss->fd)); - - PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + rv = sslBuffer_AppendNumber(buffer, + tls13_EncodeDraftVersion(ss->version), 2); - /* We asked already, but made no progress. */ - if (ss->ssl3.hs.helloRetry) { - FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter); - return SECFailure; - } - - ssl_GetXmitBufLock(ss); - /* Reset the handshake hash. */ - rv = tls13_ReinjectHandshakeTranscript(ss); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_hello_retry_request, - 2 + /* version */ - 2 + /* cipher suite */ - 2 + /* extension length */ - 2 + /* group extension id */ - 2 + /* group extension length */ - 2 /* group */); + rv = sslBuffer_AppendNumber(buffer, ss->ssl3.hs.cipher_suite, 2); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - rv = ssl3_AppendHandshakeNumber( - ss, tls13_EncodeDraftVersion(ss->version), 2); + /* Note: cookie is pointing to a stack variable, so is only valid + * now. */ + ss->xtnData.selectedGroup = selectedGroup; + ss->xtnData.cookie.data = cookie; + ss->xtnData.cookie.len = cookieLen; + rv = ssl_ConstructExtensions(ss, &extensionsBuf, ssl_hs_hello_retry_request); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } + PORT_Assert(extensionsBuf.len > 0); /* These can't be empty. */ - rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.cipher_suite, 2); + /* Clean up cookie so we're not pointing at random memory. */ + ss->xtnData.cookie.data = NULL; + ss->xtnData.cookie.len = 0; + + rv = sslBuffer_AppendBufferVariable(buffer, &extensionsBuf, 2); + sslBuffer_Clear(&extensionsBuf); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - /* Length of extensions. */ - rv = ssl3_AppendHandshakeNumber(ss, 2 + 2 + 2, 2); + return SECSuccess; + +loser: + sslBuffer_Clear(buffer); + return SECFailure; +} + +static SECStatus +tls13_SendHelloRetryRequest(sslSocket *ss, const sslNamedGroupDef *selectedGroup) +{ + SECStatus rv; + unsigned int cookieLen; + PRUint8 cookie[1024]; + sslBuffer messageBuf = { NULL, 0, 0 }; + + SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* We asked already, but made no progress. */ + if (ss->ssl3.hs.helloRetry) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + + /* Compute the cookie we are going to need. */ + rv = tls13_MakeHrrCookie(ss, selectedGroup, + cookie, &cookieLen, sizeof(cookie)); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); - goto loser; + return SECFailure; } - /* Key share extension - currently the only reason we send this. */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); + /* Now build the body of the message. */ + rv = tls13_ConstructHelloRetryRequest(ss, selectedGroup, + cookie, cookieLen, &messageBuf); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); - goto loser; + return SECFailure; } - /* Key share extension length. */ - rv = ssl3_AppendHandshakeNumber(ss, 2, 2); + + /* And send it. */ + ssl_GetXmitBufLock(ss); + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_hello_retry_request, + messageBuf.len); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2); + rv = ssl3_AppendBufferToHandshake(ss, &messageBuf); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - + sslBuffer_Clear(&messageBuf); /* Done with messageBuf */ rv = ssl3_FlushHandshake(ss, 0); if (rv != SECSuccess) { goto loser; /* error code set by ssl3_FlushHandshake */ @@ -1573,9 +1639,14 @@ tls13_SendHelloRetryRequest(sslSocket *ss, const sslNamedGroupDef *selectedGroup ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_hrr; } + /* Restart the handshake hashes because we will refresh them when we + * get ClientHello2 again. */ + ssl3_RestartHandshakeHashes(ss); + return SECSuccess; loser: + sslBuffer_Clear(&messageBuf); ssl_ReleaseXmitBufLock(ss); return SECFailure; } @@ -2870,9 +2941,8 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type, return SECSuccess; } -static SECStatus -tls13_ComputeHandshakeHashes(sslSocket *ss, - SSL3Hashes *hashes) +SECStatus +tls13_ComputeHandshakeHashes(sslSocket *ss, SSL3Hashes *hashes) { SECStatus rv; PK11Context *ctx = NULL; @@ -2917,6 +2987,8 @@ tls13_ComputeHandshakeHashes(sslSocket *ss, ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); goto loser; } + + PRINT_BUF(10, (NULL, "Handshake hash", hashes->u.raw, hashes->len)); PORT_Assert(hashes->len == tls13_GetHashSize(ss)); PK11_DestroyContext(ctx, PR_TRUE); diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h index 66e4580dd1..685cec7300 100644 --- a/lib/ssl/tls13con.h +++ b/lib/ssl/tls13con.h @@ -49,6 +49,8 @@ unsigned int tls13_GetHashSize(const sslSocket *ss); CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss); SECStatus tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes, const PRUint8 *buf, unsigned int len); +SECStatus tls13_ComputeHandshakeHashes(sslSocket *ss, + SSL3Hashes *hashes); SECStatus tls13_DeriveSecretNullHash(sslSocket *ss, PK11SymKey *key, const char *label, unsigned int labelLen, @@ -64,10 +66,16 @@ PRBool tls13_PskSuiteEnabled(sslSocket *ss); SECStatus tls13_WriteExtensionsWithBinder(sslSocket *ss, sslBuffer *extensions); SECStatus tls13_HandleClientHelloPart2(sslSocket *ss, const SECItem *suites, - sslSessionID *sid); + sslSessionID *sid, + const PRUint8 *msg, + unsigned int len); SECStatus tls13_HandleServerHelloPart2(sslSocket *ss); SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length); +SECStatus tls13_ConstructHelloRetryRequest(sslSocket *ss, + const sslNamedGroupDef *selectedGroup, + PRUint8 *cookie, unsigned int cookieLen, + sslBuffer *buffer); SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, PRUint8 *b, PRUint32 length); void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry); diff --git a/lib/ssl/tls13err.h b/lib/ssl/tls13err.h new file mode 100644 index 0000000000..8cdeb12eb2 --- /dev/null +++ b/lib/ssl/tls13err.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __tls13err_h_ +#define __tls13err_h_ + +/* Use this instead of FATAL_ERROR when an alert isn't possible. */ +#define LOG_ERROR(ss, prError) \ + do { \ + SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \ + SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \ + PORT_SetError(prError); \ + } while (0) + +/* Log an error and generate an alert because something is irreparably wrong. */ +#define FATAL_ERROR(ss, prError, desc) \ + do { \ + LOG_ERROR(ss, prError); \ + tls13_FatalError(ss, prError, desc); \ + } while (0) + +void tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc); +#endif diff --git a/lib/ssl/tls13exthandle.c b/lib/ssl/tls13exthandle.c index 58a48c3559..7a32f8808c 100644 --- a/lib/ssl/tls13exthandle.c +++ b/lib/ssl/tls13exthandle.c @@ -517,7 +517,7 @@ tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData ++numIdentities; } - xtnData->pskBinderPrefixLen = ss->ssl3.hs.messages.len - data->len; + xtnData->pskBindersLen = data->len; /* Parse the binders list. */ rv = ssl3_ExtConsumeHandshakeVariable(ss, @@ -806,6 +806,37 @@ tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, return SECSuccess; } +SECStatus +tls13_ServerHandleCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, + SECItem *data) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension", + SSL_GETPID(), ss->fd)); + + rv = ssl3_ExtConsumeHandshakeVariable(ss, &xtnData->cookie, 2, + &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; + } + + if (xtnData->cookie.len == 0) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + if (data->len) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_cookie_xtn; + + return SECSuccess; +} + /* * enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode; * @@ -947,3 +978,47 @@ tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss, xtnData->certReqAuthorities.arena = NULL; return SECFailure; } + +SECStatus +tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + SECStatus rv; + + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + + /* In future, we may want to send HRRs w/o key_share but we don't + * currently do that. At that time, this assert can simply be + * removed. */ + PORT_Assert(xtnData->selectedGroup != NULL); + if (!xtnData->selectedGroup) { + return SECSuccess; + } + + rv = sslBuffer_AppendNumber(buf, xtnData->selectedGroup->name, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + *added = PR_TRUE; + return SECSuccess; +} + +SECStatus +tls13_ServerSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + SECStatus rv; + + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + PORT_Assert(xtnData->cookie.len > 0); + + rv = sslBuffer_AppendVariable(buf, + xtnData->cookie.data, xtnData->cookie.len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + *added = PR_TRUE; + return SECSuccess; +} diff --git a/lib/ssl/tls13exthandle.h b/lib/ssl/tls13exthandle.h index 0de2af57dd..b500de052c 100644 --- a/lib/ssl/tls13exthandle.h +++ b/lib/ssl/tls13exthandle.h @@ -72,5 +72,14 @@ SECStatus tls13_SendCertAuthoritiesXtn(const sslSocket *ss, SECStatus tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data); +SECStatus tls13_ServerHandleCookieXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data); +SECStatus tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added); +SECStatus tls13_ServerSendHrrCookieXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added); #endif diff --git a/lib/ssl/tls13hashstate.c b/lib/ssl/tls13hashstate.c new file mode 100644 index 0000000000..8012abe378 --- /dev/null +++ b/lib/ssl/tls13hashstate.c @@ -0,0 +1,153 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "pk11func.h" +#include "selfencrypt.h" +#include "ssl.h" +#include "sslt.h" +#include "sslencode.h" +#include "sslimpl.h" +#include "tls13con.h" +#include "tls13err.h" +#include "tls13hashstate.h" + +/* + * The cookie is structured as a self-encrypted structure with the + * inner value being. + * + * struct { + * uint8 indicator = 0xff; // To disambiguate from tickets. + * uint16 cipherSuite; // Selected cipher suite. + * uint16 keyShare; // Key share we requested (0 if none) + * opaque ch_hash[rest_of_buffer]; // H(ClientHello) + * } CookieInner; + */ +SECStatus +tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup, + PRUint8 *buf, unsigned int *len, unsigned int maxlen) +{ + SECStatus rv; + SSL3Hashes hashes; + PRUint8 encodedCookie[1024]; + SECItem cookieItem = { siBuffer, encodedCookie, sizeof(encodedCookie) }; + + PORT_Assert(sizeof(encodedCookie) >= maxlen); + + /* Encode header. */ + rv = ssl3_AppendNumberToItem(&cookieItem, 0xff, 1); + if (rv != SECSuccess) { + return SECFailure; + } + rv = ssl3_AppendNumberToItem(&cookieItem, ss->ssl3.hs.cipher_suite, 2); + if (rv != SECSuccess) { + return SECFailure; + } + rv = ssl3_AppendNumberToItem(&cookieItem, selectedGroup ? selectedGroup->name : 0, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Encode the hash state */ + rv = tls13_ComputeHandshakeHashes(ss, &hashes); + if (rv != SECSuccess) { + return SECFailure; + } + rv = ssl3_AppendToItem(&cookieItem, hashes.u.raw, hashes.len); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Encrypt right into the buffer. */ + rv = ssl_SelfEncryptProtect(ss, + encodedCookie, cookieItem.data - encodedCookie, + buf, len, maxlen); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +/* Recover the hash state from the cookie. */ +SECStatus +tls13_RecoverHashState(sslSocket *ss, + unsigned char *cookie, + unsigned int cookieLen) +{ + SECStatus rv; + unsigned char plaintext[1024]; + SECItem ptItem = { siBuffer, plaintext, 0 }; + sslBuffer messageBuf = { NULL, 0, 0 }; + PRUint32 sentinel; + PRUint32 cipherSuite; + PRUint32 group; + const sslNamedGroupDef *selectedGroup; + + rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen, + ptItem.data, &ptItem.len, sizeof(plaintext)); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Should start with 0xff. */ + rv = ssl3_ConsumeNumberFromItem(&ptItem, &sentinel, 1); + if ((rv != SECSuccess) || (sentinel != 0xff)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + /* The cipher suite should be the same or there are some shenanigans. */ + rv = ssl3_ConsumeNumberFromItem(&ptItem, &cipherSuite, 2); + if ((rv != SECSuccess) || cipherSuite != ss->ssl3.hs.cipher_suite) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + + /* The named group, if any. */ + rv = ssl3_ConsumeNumberFromItem(&ptItem, &group, 2); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + selectedGroup = ssl_LookupNamedGroup(group); + PORT_Assert(selectedGroup); + if (selectedGroup == NULL) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + + /* The remainder is the hash. */ + if (ptItem.len != tls13_GetHashSize(ss)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + + /* Now reinject the message. */ + SSL_ASSERT_HASHES_EMPTY(ss); + rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0, + ptItem.data, ptItem.len); + if (rv != SECSuccess) { + return SECFailure; + } + + /* And finally reinject the HRR. */ + rv = tls13_ConstructHelloRetryRequest(ss, selectedGroup, + cookie, cookieLen, + &messageBuf); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_hello_retry_request, 0, + messageBuf.buf, messageBuf.len); + sslBuffer_Clear(&messageBuf); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} diff --git a/lib/ssl/tls13hashstate.h b/lib/ssl/tls13hashstate.h new file mode 100644 index 0000000000..28d30d7e4d --- /dev/null +++ b/lib/ssl/tls13hashstate.h @@ -0,0 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __tls13hashstate_h_ +#define __tls13hashstate_h_ + +#include "ssl.h" +#include "sslt.h" +#include "sslimpl.h" + +SECStatus tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup, + PRUint8 *buf, unsigned int *len, unsigned int maxlen); +SECStatus tls13_GetHrrCookieLength(sslSocket *ss, unsigned int *length); +SECStatus tls13_RecoverHashState(sslSocket *ss, + unsigned char *cookie, + unsigned int cookieLen); +#endif