summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtests/ssl_gtest/libssl_internals.c14
-rw-r--r--gtests/ssl_gtest/libssl_internals.h2
-rw-r--r--gtests/ssl_gtest/manifest.mn1
-rw-r--r--gtests/ssl_gtest/ssl_gtest.gyp1
-rw-r--r--gtests/ssl_gtest/ssl_keyupdate_unittest.cc119
-rw-r--r--gtests/ssl_gtest/tls_connect.cc26
-rw-r--r--gtests/ssl_gtest/tls_connect.h3
-rw-r--r--lib/ssl/SSLerrs.h9
-rw-r--r--lib/ssl/sslerr.h3
-rw-r--r--lib/ssl/sslexp.h10
-rw-r--r--lib/ssl/sslimpl.h4
-rw-r--r--lib/ssl/sslsock.c1
-rw-r--r--lib/ssl/tls13con.c284
-rw-r--r--lib/ssl/tls13con.h3
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);