diff options
-rw-r--r-- | gtests/ssl_gtest/libssl_internals.c | 14 | ||||
-rw-r--r-- | gtests/ssl_gtest/libssl_internals.h | 2 | ||||
-rw-r--r-- | gtests/ssl_gtest/manifest.mn | 1 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_gtest.gyp | 1 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_keyupdate_unittest.cc | 119 | ||||
-rw-r--r-- | gtests/ssl_gtest/tls_connect.cc | 26 | ||||
-rw-r--r-- | gtests/ssl_gtest/tls_connect.h | 3 | ||||
-rw-r--r-- | lib/ssl/SSLerrs.h | 9 | ||||
-rw-r--r-- | lib/ssl/sslerr.h | 3 | ||||
-rw-r--r-- | lib/ssl/sslexp.h | 10 | ||||
-rw-r--r-- | lib/ssl/sslimpl.h | 4 | ||||
-rw-r--r-- | lib/ssl/sslsock.c | 1 | ||||
-rw-r--r-- | lib/ssl/tls13con.c | 284 | ||||
-rw-r--r-- | lib/ssl/tls13con.h | 3 |
14 files changed, 440 insertions, 40 deletions
diff --git a/gtests/ssl_gtest/libssl_internals.c b/gtests/ssl_gtest/libssl_internals.c index 1b2b71028..887d85278 100644 --- a/gtests/ssl_gtest/libssl_internals.c +++ b/gtests/ssl_gtest/libssl_internals.c @@ -362,3 +362,17 @@ SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) { void SSLInt_RolloverAntiReplay(void) { tls13_AntiReplayRollover(ssl_TimeUsec()); } + +SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch, + PRUint16 *writeEpoch) { + sslSocket *ss = ssl_FindSocket(fd); + if (!ss || !readEpoch || !writeEpoch) { + return SECFailure; + } + + ssl_GetSpecReadLock(ss); + *readEpoch = ss->ssl3.crSpec->epoch; + *writeEpoch = ss->ssl3.cwSpec->epoch; + ssl_ReleaseSpecReadLock(ss); + return SECSuccess; +} diff --git a/gtests/ssl_gtest/libssl_internals.h b/gtests/ssl_gtest/libssl_internals.h index e7ea9ef34..95d4afdaf 100644 --- a/gtests/ssl_gtest/libssl_internals.h +++ b/gtests/ssl_gtest/libssl_internals.h @@ -39,6 +39,8 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra); SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group); +SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch, + PRUint16 *writeEpoch); SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd, sslCipherSpecChangedFunc func, diff --git a/gtests/ssl_gtest/manifest.mn b/gtests/ssl_gtest/manifest.mn index 86d6ddd6f..5d893bab3 100644 --- a/gtests/ssl_gtest/manifest.mn +++ b/gtests/ssl_gtest/manifest.mn @@ -32,6 +32,7 @@ CPPSRCS = \ ssl_gtest.cc \ ssl_hrr_unittest.cc \ ssl_keylog_unittest.cc \ + ssl_keyupdate_unittest.cc \ ssl_loopback_unittest.cc \ ssl_misc_unittest.cc \ ssl_record_unittest.cc \ diff --git a/gtests/ssl_gtest/ssl_gtest.gyp b/gtests/ssl_gtest/ssl_gtest.gyp index 03cad2e89..e2a8d830a 100644 --- a/gtests/ssl_gtest/ssl_gtest.gyp +++ b/gtests/ssl_gtest/ssl_gtest.gyp @@ -33,6 +33,7 @@ 'ssl_gtest.cc', 'ssl_hrr_unittest.cc', 'ssl_keylog_unittest.cc', + 'ssl_keyupdate_unittest.cc', 'ssl_loopback_unittest.cc', 'ssl_misc_unittest.cc', 'ssl_record_unittest.cc', diff --git a/gtests/ssl_gtest/ssl_keyupdate_unittest.cc b/gtests/ssl_gtest/ssl_keyupdate_unittest.cc new file mode 100644 index 000000000..c2d03aa48 --- /dev/null +++ b/gtests/ssl_gtest/ssl_keyupdate_unittest.cc @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "secerr.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" + +extern "C" { +// This is not something that should make you happy. +#include "libssl_internals.h" +} + +#include "gtest_utils.h" +#include "scoped_ptrs.h" +#include "tls_connect.h" +#include "tls_filter.h" +#include "tls_parser.h" + +namespace nss_test { + +// All stream only tests; DTLS isn't supported yet. + +TEST_F(TlsConnectTest, KeyUpdateClient) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE)); + SendReceive(50); + SendReceive(60); + CheckEpochs(4, 3); +} + +TEST_F(TlsConnectTest, KeyUpdateClientRequestUpdate) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE)); + // SendReceive() only gives each peer one chance to read. This isn't enough + // when the read on one side generates another handshake message. A second + // read gives each peer an extra chance to consume the KeyUpdate. + SendReceive(50); + SendReceive(60); // Cumulative count. + CheckEpochs(4, 4); +} + +TEST_F(TlsConnectTest, KeyUpdateServer) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE)); + SendReceive(50); + SendReceive(60); + CheckEpochs(3, 4); +} + +TEST_F(TlsConnectTest, KeyUpdateServerRequestUpdate) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + SendReceive(50); + SendReceive(60); + CheckEpochs(4, 4); +} + +TEST_F(TlsConnectTest, KeyUpdateConsecutiveRequests) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + SendReceive(50); + SendReceive(60); + // The server should have updated twice, but the client should have declined + // to respond to the second request from the server, since it doesn't send + // anything in between those two requests. + CheckEpochs(4, 5); +} + +// Check that a local update can be immediately followed by a remotely triggered +// update even if there is no use of the keys. +TEST_F(TlsConnectTest, KeyUpdateLocalUpdateThenConsecutiveRequests) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + // This should trigger an update on the client. + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE)); + // The client should update for the first request. + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + // ...but not the second. + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + SendReceive(50); + SendReceive(60); + // Both should have updated twice. + CheckEpochs(5, 5); +} + +TEST_F(TlsConnectTest, KeyUpdateMultiple) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE)); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE)); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE)); + SendReceive(50); + SendReceive(60); + CheckEpochs(5, 6); +} + +// Both ask the other for an update, and both should react. +TEST_F(TlsConnectTest, KeyUpdateBothRequest) { + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + SendReceive(50); + SendReceive(60); + CheckEpochs(5, 5); +} + +} // namespace nss_test diff --git a/gtests/ssl_gtest/tls_connect.cc b/gtests/ssl_gtest/tls_connect.cc index c23dc3bd1..2eca87aea 100644 --- a/gtests/ssl_gtest/tls_connect.cc +++ b/gtests/ssl_gtest/tls_connect.cc @@ -165,6 +165,22 @@ void TlsConnectTestBase::CheckShares( EXPECT_EQ(shares.len(), i); } +void TlsConnectTestBase::CheckEpochs(uint16_t client_epoch, + uint16_t server_epoch) const { + uint16_t read_epoch = 0; + uint16_t write_epoch = 0; + + EXPECT_EQ(SECSuccess, + SSLInt_GetEpochs(client_->ssl_fd(), &read_epoch, &write_epoch)); + EXPECT_EQ(server_epoch, read_epoch); + EXPECT_EQ(client_epoch, write_epoch); + + EXPECT_EQ(SECSuccess, + SSLInt_GetEpochs(server_->ssl_fd(), &read_epoch, &write_epoch)); + EXPECT_EQ(client_epoch, read_epoch); + EXPECT_EQ(server_epoch, write_epoch); +} + void TlsConnectTestBase::ClearStats() { // Clear statistics. SSL3Statistics* stats = SSL_GetStatistics(); @@ -593,10 +609,12 @@ void TlsConnectTestBase::CheckSrtp() const { server_->CheckSrtp(); } -void TlsConnectTestBase::SendReceive() { - client_->SendData(50); - server_->SendData(50); - Receive(50); +void TlsConnectTestBase::SendReceive(size_t total) { + ASSERT_GT(total, client_->received_bytes()); + ASSERT_GT(total, server_->received_bytes()); + client_->SendData(total - server_->received_bytes()); + server_->SendData(total - client_->received_bytes()); + Receive(total); // Receive() is cumulative } // Do a first connection so we can do 0-RTT on the second one. diff --git a/gtests/ssl_gtest/tls_connect.h b/gtests/ssl_gtest/tls_connect.h index c1f23c2f2..c650dda1d 100644 --- a/gtests/ssl_gtest/tls_connect.h +++ b/gtests/ssl_gtest/tls_connect.h @@ -94,6 +94,7 @@ class TlsConnectTestBase : public ::testing::Test { std::function<void(SSLNamedGroup)> check_group); void CheckShares(const DataBuffer& shares, std::function<void(SSLNamedGroup)> check_group); + void CheckEpochs(uint16_t client_epoch, uint16_t server_epoch) const; void ConfigureVersion(uint16_t version); void SetExpectedVersion(uint16_t version); @@ -114,7 +115,7 @@ class TlsConnectTestBase : public ::testing::Test { void CheckAlpn(const std::string& val); void EnableSrtp(); void CheckSrtp() const; - void SendReceive(); + void SendReceive(size_t total = 50); void SetupForZeroRtt(); void SetupForResume(); void ZeroRttSendReceive( diff --git a/lib/ssl/SSLerrs.h b/lib/ssl/SSLerrs.h index 160204201..c95fe661a 100644 --- a/lib/ssl/SSLerrs.h +++ b/lib/ssl/SSLerrs.h @@ -531,3 +531,12 @@ ER3(SSL_ERROR_NO_TIMERS_ERROR, (SSL_ERROR_BASE + 167), ER3(SSL_ERROR_MISSING_COOKIE_EXTENSION, (SSL_ERROR_BASE + 168), "A second ClientHello was received without a cookie extension.") + +ER3(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, (SSL_ERROR_BASE + 169), + "SSL received an unexpected key update message.") + +ER3(SSL_ERROR_RX_MALFORMED_KEY_UPDATE, (SSL_ERROR_BASE + 170), + "SSL received a malformed key update message.") + +ER3(SSL_ERROR_TOO_MANY_KEY_UPDATES, (SSL_ERROR_BASE + 171), + "SSL attempted too many key updates.") diff --git a/lib/ssl/sslerr.h b/lib/ssl/sslerr.h index 00e580ab3..90815dd79 100644 --- a/lib/ssl/sslerr.h +++ b/lib/ssl/sslerr.h @@ -257,6 +257,9 @@ typedef enum { SSL_ERROR_NO_TIMERS_FOUND = (SSL_ERROR_BASE + 167), SSL_ERROR_MISSING_COOKIE_EXTENSION = (SSL_ERROR_BASE + 168), + SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE = (SSL_ERROR_BASE + 169), + SSL_ERROR_RX_MALFORMED_KEY_UPDATE = (SSL_ERROR_BASE + 170), + SSL_ERROR_TOO_MANY_KEY_UPDATES = (SSL_ERROR_BASE + 171), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; #endif /* NO_SECURITY_ERROR_ENUM */ diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h index c1b06e305..569add861 100644 --- a/lib/ssl/sslexp.h +++ b/lib/ssl/sslexp.h @@ -340,6 +340,16 @@ typedef SSLHelloRetryRequestAction(PR_CALLBACK *SSLHelloRetryRequestCallback)( SSLHelloRetryRequestCallback _cb, void *_arg), \ (fd, cb, arg)) +/* Update traffic keys (TLS 1.3 only). + * + * The |requestUpdate| flag determines whether to request an update from the + * remote peer. + */ +#define SSL_KeyUpdate(fd, requestUpdate) \ + SSL_EXPERIMENTAL_API("SSL_KeyUpdate", \ + (PRFileDesc * _fd, PRBool _requestUpdate), \ + (fd, requestUpdate)) + #define SSL_UseAltServerHelloType(fd, enable) \ SSL_DEPRECATED_EXPERIMENTAL_API diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index 57a03d30f..dee9aa20f 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -726,6 +726,10 @@ struct ssl3StateStr { ssl3CipherSpec *cwSpec; /* current write spec. */ ssl3CipherSpec *pwSpec; /* pending write spec. */ + /* This is true after the peer requests a key update; false after a key + * update is initiated locally. */ + PRBool peerRequestedKeyUpdate; + /* Internal callback for when we do a cipher suite change. Used for * debugging in TLS 1.3. This can only be set by non-public functions. */ sslCipherSpecChangedFunc changedCipherSpecFunc; diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c index 3ba10f4f7..4893cb9f9 100644 --- a/lib/ssl/sslsock.c +++ b/lib/ssl/sslsock.c @@ -3930,6 +3930,7 @@ struct { EXP(GetExtensionSupport), EXP(HelloRetryRequestCallback), EXP(InstallExtensionHooks), + EXP(KeyUpdate), EXP(SendSessionTicket), EXP(SetupAntiReplay), #endif diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index cc10aa80f..f481b6609 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -24,7 +24,7 @@ #include "tls13exthandle.h" #include "tls13hashstate.h" -static SECStatus tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type, +static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch, CipherSpecDirection install, PRBool deleteSecret); static SECStatus tls13_AESGCM( @@ -128,11 +128,6 @@ const char keylogLabelServerTrafficSecret[] = "SERVER_TRAFFIC_SECRET_0"; const char keylogLabelEarlyExporterSecret[] = "EARLY_EXPORTER_SECRET"; const char keylogLabelExporterSecret[] = "EXPORTER_SECRET"; -#define TRAFFIC_SECRET(ss, dir, name) ((ss->sec.isServer ^ \ - (dir == CipherSpecWrite)) \ - ? ss->ssl3.hs.client##name \ - : ss->ssl3.hs.server##name) - /* Belt and suspenders in case we ever add a TLS 1.4. */ PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <= SSL_LIBRARY_VERSION_TLS_1_3); @@ -582,6 +577,229 @@ loser: return SECFailure; } +static PRBool +tls13_UseServerSecret(sslSocket *ss, CipherSpecDirection direction) +{ + return ss->sec.isServer == (direction == CipherSpecWrite); +} + +static PK11SymKey ** +tls13_TrafficSecretRef(sslSocket *ss, CipherSpecDirection direction) +{ + if (tls13_UseServerSecret(ss, direction)) { + return &ss->ssl3.hs.serverTrafficSecret; + } + return &ss->ssl3.hs.clientTrafficSecret; +} + +SECStatus +tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction) +{ + PK11SymKey **secret; + PK11SymKey *updatedSecret; + PRUint16 epoch; + SECStatus rv; + + secret = tls13_TrafficSecretRef(ss, direction); + rv = tls13_HkdfExpandLabel(*secret, tls13_GetHash(ss), + NULL, 0, + kHkdfLabelApplicationTrafficSecret, + strlen(kHkdfLabelApplicationTrafficSecret), + tls13_GetHmacMechanism(ss), + tls13_GetHashSize(ss), + &updatedSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + PK11_FreeSymKey(*secret); + *secret = updatedSecret; + + ssl_GetSpecReadLock(ss); + if (direction == CipherSpecRead) { + epoch = ss->ssl3.crSpec->epoch; + } else { + epoch = ss->ssl3.cwSpec->epoch; + } + ssl_ReleaseSpecReadLock(ss); + + if (epoch == PR_UINT16_MAX) { + /* Good chance that this is an overflow from too many updates. */ + FATAL_ERROR(ss, SSL_ERROR_TOO_MANY_KEY_UPDATES, internal_error); + return SECFailure; + } + ++epoch; + + rv = tls13_SetCipherSpec(ss, epoch, direction, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + return SECSuccess; +} + +typedef enum { + update_not_requested = 0, + update_requested = 1 +} tls13KeyUpdateRequest; + +static SECStatus +tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: %s send key update, response %s", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + (request == update_requested) ? "requested" + : "not requested")); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (!ss->firstHsDone) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_LIBRARY_FAILURE, + idle_handshake); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Not supported. */ + if (IS_DTLS(ss)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ssl_GetXmitBufLock(ss); + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_key_update, 1); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + rv = ssl3_AppendHandshakeNumber(ss, request, 1); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + + rv = ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + goto loser; /* error code set by ssl3_FlushHandshake */ + } + ssl_ReleaseXmitBufLock(ss); + + rv = tls13_UpdateTrafficKeys(ss, CipherSpecWrite); + if (rv != SECSuccess) { + goto loser; /* error code set by tls13_UpdateTrafficKeys */ + } + + return SECSuccess; + +loser: + ssl_ReleaseXmitBufLock(ss); + return SECFailure; +} + +SECStatus +SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate) +{ + SECStatus rv; + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + ssl_GetSSL3HandshakeLock(ss); + rv = tls13_SendKeyUpdate(ss, requestUpdate ? update_requested : update_not_requested); + + /* Remember that we are the ones that initiated this KeyUpdate. */ + if (rv == SECSuccess) { + ss->ssl3.peerRequestedKeyUpdate = PR_FALSE; + } + ssl_ReleaseSSL3HandshakeLock(ss); + return rv; +} + +/* + * enum { + * update_not_requested(0), update_requested(1), (255) + * } KeyUpdateRequest; + * + * struct { + * KeyUpdateRequest request_update; + * } KeyUpdate; + */ +static SECStatus +tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length) +{ + SECStatus rv; + PRUint32 update; + + SSL_TRC(3, ("%d: TLS13[%d]: %s handle key update", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + PORT_Assert(ss->firstHsDone); + if (!ss->firstHsDone) { + FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, unexpected_message); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, + idle_handshake); + if (rv != SECSuccess) { + /* We should never be idle_handshake prior to firstHsDone. */ + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeNumber(ss, &update, 1, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Error code set already. */ + } + if (length != 0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error); + return SECFailure; + } + if (update != update_requested && + update != update_not_requested) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error); + return SECFailure; + } + + rv = tls13_UpdateTrafficKeys(ss, CipherSpecRead); + if (rv != SECSuccess) { + return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */ + } + + if (update == update_requested) { + PRBool sendUpdate; + if (ss->ssl3.peerRequestedKeyUpdate) { + /* Only send an update if we have sent with the current spec. This + * prevents us from being forced to crank forward pointlessly. */ + ssl_GetSpecReadLock(ss); + sendUpdate = ss->ssl3.cwSpec->seqNum > 0; + ssl_ReleaseSpecReadLock(ss); + } else { + sendUpdate = PR_TRUE; + } + if (sendUpdate) { + rv = tls13_SendKeyUpdate(ss, update_not_requested); + if (rv != SECSuccess) { + return SECFailure; /* Error already set. */ + } + } + ss->ssl3.peerRequestedKeyUpdate = PR_TRUE; + } + + return SECSuccess; +} + SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length) { @@ -619,6 +837,9 @@ tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length case ssl_hs_end_of_early_data: return tls13_HandleEndOfEarlyData(ss, b, length); + case ssl_hs_key_update: + return tls13_HandleKeyUpdate(ss, b, length); + default: FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message); return SECFailure; @@ -2233,8 +2454,7 @@ tls13_SendServerHelloSequence(sslSocket *ss) ssl_CipherSpecAddRef(ss->ssl3.crSpec); } if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { - rv = tls13_SetCipherSpec(ss, - TrafficKeyEarlyApplicationData, + rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData, CipherSpecRead, PR_TRUE); if (rv != SECSuccess) { LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); @@ -2899,33 +3119,30 @@ tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec, CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipherDef->calg); PK11SymKey **prkp = NULL; PK11SymKey *prk = NULL; - PRBool clientKey; + PRBool clientSecret; SECStatus rv; /* These labels are just used for debugging. */ static const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data"; static const char kHkdfPhaseHandshakeKeys[] = "handshake data"; static const char kHkdfPhaseApplicationDataKeys[] = "application data"; - if (ss->sec.isServer ^ (spec->direction == CipherSpecWrite)) { - clientKey = PR_TRUE; - } else { - clientKey = PR_FALSE; - } - PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + clientSecret = !tls13_UseServerSecret(ss, spec->direction); switch (type) { case TrafficKeyEarlyApplicationData: - PORT_Assert(clientKey); + PORT_Assert(clientSecret); prkp = &ss->ssl3.hs.clientEarlyTrafficSecret; spec->phase = kHkdfPhaseEarlyApplicationDataKeys; break; case TrafficKeyHandshake: - prkp = clientKey ? &ss->ssl3.hs.clientHsTrafficSecret : &ss->ssl3.hs.serverHsTrafficSecret; + prkp = clientSecret ? &ss->ssl3.hs.clientHsTrafficSecret + : &ss->ssl3.hs.serverHsTrafficSecret; spec->phase = kHkdfPhaseHandshakeKeys; break; case TrafficKeyApplicationData: - prkp = clientKey ? &ss->ssl3.hs.clientTrafficSecret : &ss->ssl3.hs.serverTrafficSecret; + prkp = clientSecret ? &ss->ssl3.hs.clientTrafficSecret + : &ss->ssl3.hs.serverTrafficSecret; spec->phase = kHkdfPhaseApplicationDataKeys; break; default: @@ -3062,11 +3279,16 @@ tls13_SetAlertCipherSpec(sslSocket *ss) return SECSuccess; } -/* Install a new cipher spec for this direction. */ +/* Install a new cipher spec for this direction. + * + * During the handshake, the values for |epoch| take values from the + * TrafficKeyType enum. Afterwards, key update increments them. + */ static SECStatus -tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type, +tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch, CipherSpecDirection direction, PRBool deleteSecret) { + TrafficKeyType type; SECStatus rv; ssl3CipherSpec *spec = NULL; ssl3CipherSpec **specp; @@ -3084,15 +3306,7 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type, if (!spec) { return SECFailure; } - - specp = (direction == CipherSpecRead) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec; - - /* We use the epoch for cipher suite identification, so increment - * it in both TLS and DTLS. */ - if ((*specp)->epoch == PR_UINT16_MAX) { - goto loser; - } - spec->epoch = (PRUint16)type; + spec->epoch = epoch; spec->seqNum = 0; if (IS_DTLS(ss)) { dtls_InitRecvdRecords(&spec->recvdRecords); @@ -3104,12 +3318,14 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type, goto loser; } + type = (TrafficKeyType)PR_MIN(TrafficKeyApplicationData, epoch); rv = tls13_DeriveTrafficKeys(ss, spec, type, deleteSecret); if (rv != SECSuccess) { goto loser; } /* Now that we've set almost everything up, finally cut over. */ + specp = (direction == CipherSpecRead) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec; ssl_GetSpecWriteLock(ss); ssl_CipherSpecRelease(*specp); /* May delete old cipher. */ *specp = spec; /* Overwrite. */ @@ -3953,7 +4169,7 @@ tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length) } rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, - CipherSpecRead, PR_TRUE); + CipherSpecRead, PR_FALSE); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); return SECFailure; @@ -4160,7 +4376,7 @@ tls13_SendClientSecondRound(sslSocket *ss) return SECFailure; } rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, - CipherSpecWrite, PR_TRUE); + CipherSpecWrite, PR_FALSE); if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; @@ -5007,11 +5223,11 @@ tls13_EncodeDraftVersion(SSL3ProtocolVersion version) /* Pick the highest version we support that is also advertised. */ SECStatus -tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supported_versions) +tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supportedVersions) { PRUint16 version; - /* Make a copy so we're nondestructive*/ - SECItem data = supported_versions->data; + /* Make a copy so we're nondestructive. */ + SECItem data = supportedVersions->data; SECItem versions; SECStatus rv; diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h index d4583ee2e..07f56f07d 100644 --- a/lib/ssl/tls13con.h +++ b/lib/ssl/tls13con.h @@ -101,13 +101,14 @@ SECStatus tls13_NegotiateVersion(sslSocket *ss, PRBool tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid); void tls13_AntiReplayRollover(PRTime now); + SECStatus SSLExp_SetupAntiReplay(PRTime window, unsigned int k, unsigned int bits); SECStatus SSLExp_HelloRetryRequestCallback(PRFileDesc *fd, SSLHelloRetryRequestCallback cb, void *arg); -SECStatus SSLExp_UseAltHandshakeType(PRFileDesc *fd, PRBool enable); +SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate); PRBool tls13_MaybeTls13(sslSocket *ss); void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec); |