summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc1501
1 files changed, 1501 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
new file mode 100644
index 0000000000..00c755be63
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
@@ -0,0 +1,1501 @@
+/* -*- 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 "nss_scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+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(TlsConnectStreamTls13, KeyUpdateTooEarly_Client) {
+ StartConnect();
+ auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
+ server_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+ ExpectAlert(client_, kTlsAlertUnexpectedMessage);
+ client_->Handshake();
+ client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
+ server_->Handshake();
+ server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13, KeyUpdateTooEarly_Server) {
+ StartConnect();
+ auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
+ client_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+ client_->Handshake();
+ ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ server_->Handshake();
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
+ client_->Handshake();
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+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);
+}
+
+// If the sequence number exceeds the number of writes before an automatic
+// update (currently 3/4 of the max records for the cipher suite), then the
+// stack should send an update automatically (but not request one).
+TEST_F(TlsConnectTest, KeyUpdateAutomaticOnWrite) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+
+ // Set this to one below the write threshold.
+ uint64_t threshold = (0x5aULL << 28) * 3 / 4;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ // This should be OK.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ // This should cause the client to update.
+ client_->SendData(20);
+ server_->ReadBytes();
+
+ SendReceive(100);
+ CheckEpochs(4, 3);
+}
+
+// If the sequence number exceeds a certain number of reads (currently 7/8 of
+// the max records for the cipher suite), then the stack should send AND request
+// an update automatically. However, the sender (client) will be above its
+// automatic update threshold, so the KeyUpdate - that it sends with the old
+// cipher spec - will exceed the receiver (server) automatic update threshold.
+// The receiver gets a packet with a sequence number over its automatic read
+// update threshold. Even though the sender has updated, the code that checks
+// the sequence numbers at the receiver doesn't know this and it will request an
+// update. This causes two updates: one from the sender (without requesting a
+// response) and one from the receiver (which does request a response).
+TEST_F(TlsConnectTest, KeyUpdateAutomaticOnRead) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+
+ // Move to right at the read threshold. Unlike the write test, we can't send
+ // packets because that would cause the client to update, which would spoil
+ // the test.
+ uint64_t threshold = ((0x5aULL << 28) * 7 / 8) + 1;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ // This should cause the client to update, but not early enough to prevent the
+ // server from updating also.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ // Need two SendReceive() calls to ensure that the update that the server
+ // requested is properly generated and consumed.
+ SendReceive(70);
+ SendReceive(80);
+ CheckEpochs(5, 4);
+}
+
+// Filter to modify KeyUpdate message. Takes as an input which byte and what
+// value to install.
+class TLSKeyUpdateDamager : public TlsRecordFilter {
+ public:
+ TLSKeyUpdateDamager(const std::shared_ptr<TlsAgent>& a, size_t byte,
+ uint8_t val)
+ : TlsRecordFilter(a), offset_(byte), value_(val) {}
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ if (!header.is_protected()) {
+ return KEEP;
+ }
+ uint16_t protection_epoch;
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ TlsRecordHeader out_header;
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ if (inner_content_type != ssl_ct_handshake) {
+ return KEEP;
+ }
+
+ if (plaintext.data()[0] != ssl_hs_key_update) {
+ return KEEP;
+ }
+
+ if (offset_ >= plaintext.len()) {
+ ADD_FAILURE() << "TLSKeyUpdateDamager: the input (offset_) is out "
+ "of the range (the expected len is equal to "
+ << plaintext.len() << ")." << std::endl;
+ return KEEP;
+ }
+
+ plaintext.data()[offset_] = value_;
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintext, &ciphertext, &out_header);
+ if (!ok) {
+ ADD_FAILURE() << "Unable to protect the plaintext using "
+ << protection_epoch << "epoch. " << std::endl;
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+
+ protected:
+ size_t offset_;
+ uint8_t value_;
+};
+
+// The next tests check the behaviour in case of malformed KeyUpdate.
+// The first test, TLSKeyUpdateWrongValueForUpdateRequested,
+// modifies the 4th byte (KeyUpdate) to have the incorrect value.
+// The last tests check the incorrect values of the length.
+
+// RFC 8446: 4. Handshake Protocol
+// struct {
+// HandshakeType msg_type; handshake type
+// uint24 length; remaining bytes in message
+// select (Handshake.msg_type) {
+// case key_update: KeyUpdate; (4th byte)
+// };
+// } Handshake;
+
+TEST_F(TlsConnectStreamTls13, TLSKeyUpdateWrongValueForUpdateRequested) {
+ EnsureTlsSetup();
+ // This test is setting the update_requested to be equal to 2
+ // Whereas the allowed values are [0, 1].
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 4, 2);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+
+ filter->Enable();
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+
+ ExpectAlert(server_, kTlsAlertDecodeError);
+ client_->ExpectReceiveAlert(kTlsAlertDecodeError);
+
+ server_->ExpectReadWriteError();
+ client_->ExpectReadWriteError();
+ server_->ReadBytes();
+ client_->ReadBytes();
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_KEY_UPDATE);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+
+ // Even if the client has updated his writing key,
+ client_->CheckEpochs(3, 4);
+ // the server has not.
+ server_->CheckEpochs(3, 3);
+}
+
+TEST_F(TlsConnectStreamTls13, TLSKeyUpdateWrongValueForLength_MessageTooLong) {
+ EnsureTlsSetup();
+ // the first byte of the length was replaced with 0xff.
+ // The message now is too long.
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 1, 0xff);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+
+ filter->Enable();
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+
+ ExpectAlert(server_, kTlsAlertDecodeError);
+ client_->ExpectReceiveAlert(kTlsAlertDecodeError);
+
+ server_->ExpectReadWriteError();
+ client_->ExpectReadWriteError();
+ server_->ReadBytes();
+ client_->ReadBytes();
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+
+ // Even if the client has updated his writing key,
+ client_->CheckEpochs(3, 4);
+ // the server has not.
+ server_->CheckEpochs(3, 3);
+}
+
+TEST_F(TlsConnectStreamTls13, TLSKeyUpdateWrongValueForLength_MessageTooShort) {
+ EnsureTlsSetup();
+ // Changing the value of length of the KU message to be shorter than the
+ // correct one.
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 0x3, 0x00);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+
+ filter->Enable();
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+
+ ExpectAlert(server_, kTlsAlertDecodeError);
+ client_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ client_->SendData(10);
+ server_->ReadBytes();
+}
+
+// DTLS1.3 tests
+
+// The KeyUpdate in DTLS1.3 workflow (with the update_requested set):
+
+// Client(P1) is asking for KeyUpdate
+// Here the second parameter states whether the P1 requires update_requested
+// (RFC9147, Section 8).
+// EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(),
+// PR_FALSE));
+
+// The server (P2) receives the KeyUpdate request and processes it.
+// server_->ReadBytes();
+
+// P2 sends ACK.
+// SSLInt_SendImmediateACK(server_->ssl_fd());
+
+// P1 receives ACK and finished the KeyUpdate:
+// client_->ReadBytes();
+
+// This function sends and proceeds KeyUpdate explained above (assuming
+// updateRequested == PR_FALSE) For the explantation of the updateRequested look
+// at the test DTLSKeyUpdateClientUpdateRequestedSucceed.*/
+static void SendAndProcessKU(const std::shared_ptr<TlsAgent>& sender,
+ const std::shared_ptr<TlsAgent>& receiver,
+ bool updateRequested) {
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(sender->ssl_fd(), updateRequested));
+ receiver->ReadBytes();
+ // It takes some time to send an ack message, so here we send it immediately
+ SSLInt_SendImmediateACK(receiver->ssl_fd());
+ sender->ReadBytes();
+ if (updateRequested) {
+ SSLInt_SendImmediateACK(sender->ssl_fd());
+ receiver->ReadBytes();
+ }
+}
+
+// This test checks that after the execution of KeyUpdate started by the client,
+// the writing client/reading server key epoch was incremented.
+// RFC 9147. Section 4.
+// However, this value is set [...] of the connection epoch,
+// which is an [...] counter incremented on every KeyUpdate.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientKUSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ // Client starts KeyUpdate
+ // The updateRequested is not requested.
+ SendAndProcessKU(client_, server_, PR_FALSE);
+ // The KeyUpdate is finished, and the client writing spec/the server reading
+ // spec is incremented.
+ CheckEpochs(4, 3);
+ // Check that we can send/receive data after KeyUpdate.
+ SendReceive(50);
+}
+
+// This test checks that only one KeyUpdate is possible at the same time.
+// RFC 9147 Section 5.8.4
+// In contrast, implementations MUST NOT send KeyUpdate, NewConnectionId, or
+// RequestConnectionId messages if an earlier message of the same type has not
+// yet been acknowledged.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientKUTwiceOnceIgnored) {
+ Connect();
+ CheckEpochs(3, 3);
+ // Client sends a key update message.
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // The second key update message will be ignored as there is KeyUpdate in
+ // progress.
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // For the workflow see ssl_KeyUpdate_unittest.cc:SendAndProcessKU.
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // As only one KeyUpdate was executed, the key epoch was incremented only
+ // once.
+ CheckEpochs(4, 3);
+ SendReceive(50);
+}
+
+// This test checks the same as the test DTLSKeyUpdateClientKeyUpdateSucceed,
+// except that the server sends KeyUpdate.
+TEST_F(TlsConnectDatagram13, DTLSKU_ServerKUSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(server_, client_, PR_FALSE);
+ CheckEpochs(3, 4);
+ SendReceive(50);
+}
+
+// This test checks the same as the test
+// DTLSKeyUpdateClientKeyUpdateTwiceOnceIgnored, except that the server sends
+// KeyUpdate.
+TEST_F(TlsConnectDatagram13, DTLSKU_PreviousKUNotYetACKed) {
+ Connect();
+ CheckEpochs(3, 3);
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+ // The second key update message will be ignored
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ server_->ReadBytes();
+
+ CheckEpochs(3, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that if we receive two KeyUpdates, one will be ignored
+TEST_F(TlsConnectDatagram13, DTLSKU_TwiceReceivedOnceIgnored) {
+ Connect();
+ CheckEpochs(3, 3);
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(server_);
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+
+ // Here we check that there was no KeyUpdate happened
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ server_->ReadBytes();
+ CheckEpochs(3, 3);
+
+ DataBuffer d = filter->ReturnRecorded();
+ // Sending the recorded KeyUpdate
+ server_->SendDirect(d);
+ // Sending the KeyUpdate again
+ server_->SendDirect(d);
+
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ server_->ReadBytes();
+
+ // We observe that only one KeyUpdate has happened
+ CheckEpochs(3, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// The KeyUpdate in DTLS1.3 workflow (with the update_requested set):
+
+// Client(P1) is asking for KeyUpdate
+// EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
+
+// The server (P2) receives and processes the KeyUpdate request
+// At the same time, P2 sends its own KeyUpdate request (due to update_requested
+// was set)
+// server_->ReadBytes();
+
+// P1 receives the ACK and finalizes the KeyUpdate.
+// SSLInt_SendImmediateACK(server_->ssl_fd());
+
+// P1 receives the KeyUpdate request and processes it.
+// client_->ReadBytes();
+
+// P2 receives the ACK and finalizes the KeyUpdate.
+// SSLInt_SendImmediateACK(client_->ssl_fd());
+// server_->ReadBytes();
+
+// This test checks that after the KeyUpdate (with update requested set)
+// both client w/r and server w/r key epochs were incremented.
+TEST_F(TlsConnectDatagram13, DTLSKU_UpdateRequestedSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ // Here the second parameter sets the update_requested to true.
+ SendAndProcessKU(client_, server_, PR_TRUE);
+ // As there were two KeyUpdates executed (one by a client, another one by a
+ // server) Both of the keys were modified.
+ CheckEpochs(4, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that after two KeyUpdates (with update requested set)
+// the keys epochs were incremented twice.
+TEST_F(TlsConnectDatagram13, DTLSKU_UpdateRequestedTwiceSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(client_, server_, PR_TRUE);
+ // The KeyUpdate is finished, so both of the epochs got incremented.
+ CheckEpochs(4, 4);
+ SendAndProcessKU(client_, server_, PR_TRUE);
+ // The second KeyUpdate is finished, so finally the epochs were incremented
+ // twice.
+ CheckEpochs(5, 5);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks the same as the test DTLSKeyUpdateUpdateRequestedSucceed,
+// except that the server sends KeyUpdate.
+TEST_F(TlsConnectDatagram13, DTLSKU_ServerUpdateRequestedSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(server_, client_, PR_TRUE);
+ CheckEpochs(4, 4);
+ SendReceive(50);
+}
+
+// This test checks that after two KeyUpdates (with update requested set)
+// the keys epochs were incremented twice.
+TEST_F(TlsConnectDatagram13, DTLSKU_ServerUpdateRequestedTwiceSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(server_, client_, PR_TRUE);
+ // The KeyUpdate is finished, so both of the epochs got incremented.
+ CheckEpochs(4, 4);
+
+ // Server sends another KeyUpdate
+ SendAndProcessKU(server_, client_, PR_TRUE);
+ // The second KeyUpdate is finished, so finally the epochs were incremented
+ // twice.
+ CheckEpochs(5, 5);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that both client and server can send the KeyUpdate in
+// consequence.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientServerConseqSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(client_, server_, PR_FALSE);
+ // As the server initiated KeyUpdate and did not request an update_request,
+ // Only the server writing/client reading key epoch was incremented.
+ CheckEpochs(4, 3);
+ SendAndProcessKU(server_, client_, PR_FALSE);
+ // Now the client initiated KeyUpdate and did not request an update_request,
+ // so now both of epochs got incremented.
+ CheckEpochs(4, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that both client and server can send the KeyUpdate in
+// consequence. Compared to the DTLSKeyUpdateClientServerConseqSucceed TV, this
+// time both parties set update_requested to be true.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientServerUpdateRequestedBothSucceed) {
+ Connect();
+ CheckEpochs(3, 3);
+ SendAndProcessKU(client_, server_, PR_TRUE);
+ SendAndProcessKU(server_, client_, PR_TRUE);
+ // The second KeyUpdate (update_request = True) increments again the epochs
+ // of both keys.
+ CheckEpochs(5, 5);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that if there is an ongoing KeyUpdate, the one started
+// durint the KU is not going to be executed.
+TEST_F(TlsConnectDatagram13, DTLSKU_KUInTheMiddleIsRejected) {
+ Connect();
+ CheckEpochs(3, 3);
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ // Here a client starts KeyUpdate at the same time as the ongoing KeyUpdate
+ // This KeyUpdate will not execute
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // As there was only one KeyUpdate executed, both keys got incremented only
+ // once.
+ CheckEpochs(4, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// DTLS1.3 KeyUpdate - Immediate Send Tests.
+
+// The expected behaviour of the protocol:
+// P1 starts initiates KeyUpdate
+// P2 receives KeyUpdate
+// And this moment, P2 will update the reading key to n
+// But P2 will be accepting the keys from the previous epoch until a new message
+// encrypted with the epoch n arrives.
+
+// This test checks that when a client sent KeyUpdate, but the KeyUpdate message
+// was not yet received, client can still send data.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientImmediateSend) {
+ Connect();
+ // Client has initiated KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server has not yet received it, client is trying to send some additional
+ // data.
+ CheckEpochs(3, 3);
+ client_->SendData(10);
+ // Server successfully receives it.
+ WAIT_(server_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, server_->received_bytes());
+ SendReceive(50);
+}
+
+// This test checks that when a client sent KeyUpdate, but the KeyUpdate message
+// was not yet received, it can still receive data.
+TEST_F(TlsConnectDatagram13, DTLSKU_ServerImmediateSend) {
+ Connect();
+ // Client has initiated KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // The server can successfully send data.
+ CheckEpochs(3, 3);
+ server_->SendData(10);
+ WAIT_(client_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, client_->received_bytes());
+ SendReceive(50);
+}
+
+// This test checks that when a client sent KeyUpdate,
+// the server has not yet sent an ACK and the client has not yet ACKed
+// KeyUpdate, both parties can exchange data.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientImmediateSendAfterServerRead) {
+ Connect();
+ // Client has initiated KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server receives KeyUpdate
+ server_->ReadBytes();
+ // Client can send data before the server sending ACK and client receiving
+ // * ACK messages
+ // Only server keys got updated.
+ server_->CheckEpochs(4, 3);
+ client_->CheckEpochs(3, 3);
+ client_->SendData(10);
+ WAIT_(server_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, server_->received_bytes());
+ // Server can send data
+ server_->SendData(10);
+ WAIT_(client_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, client_->received_bytes());
+ SendReceive(50);
+}
+
+// This test checks that when a client sent KeyUpdate, but has not yet ACKed it,
+// both parties can exchange data.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientImmediateSendAfterServerReadAndACK) {
+ Connect();
+ CheckEpochs(3, 3);
+ // Client has initiated KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server receives KeyUpdate
+ server_->ReadBytes();
+ // Server sends ACK
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ // Client can send data before he has received KeyUpdate
+ // Only server keys got updated.
+ server_->CheckEpochs(4, 3);
+ client_->CheckEpochs(3, 3);
+ client_->SendData(10);
+ WAIT_(server_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, server_->received_bytes());
+ // Server can send data
+ server_->SendData(10);
+ WAIT_(client_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, client_->received_bytes());
+ SendReceive(50);
+}
+
+// This test checks that the client writing epoch is updated only
+// when the client has received the ACK.
+// RFC 9147. Section 8
+// As with other handshake messages with no built-in response, KeyUpdates MUST
+// be acknowledged.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientWritingEpochUpdatedAfterReceivedACK) {
+ Connect();
+ // Previous epoch
+ CheckEpochs(3, 3);
+ // Client sends a KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server updates his reading key
+ server_->ReadBytes();
+ // Now the server has a reading key = 4
+ server_->CheckEpochs(4, 3);
+ // But the client has a writing key = 3
+ client_->CheckEpochs(3, 3);
+
+ // Client sends a data, but using the old (3) keys
+ client_->SendData(10);
+ WAIT_(server_->received_bytes() == 10, 2000);
+ ASSERT_EQ((size_t)10, server_->received_bytes());
+
+ server_->CheckEpochs(4, 3);
+ client_->CheckEpochs(3, 3);
+
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+
+ CheckEpochs(4, 3);
+ SendReceive(50);
+}
+
+// DTLS1.3 KeyUpdate - Testing the border conditions
+// (i.e. the cases where we reached the highest epoch).
+
+// This test checks that the maximum epoch will not be exceeded on KeyUpdate.
+// RFC 9147. Section 8.
+// In order to provide an extra margin of security,
+// sending implementations MUST NOT allow the epoch to exceed 2^48-1.
+
+// Here we use the maximum as 2^16,
+// See bug https://bugzilla.mozilla.org/show_bug.cgi?id=1809872
+// When the bug is solved, the constant is to be replaced with 2^48 as
+// required by RFC.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientMaxEpochReached) {
+ Connect();
+ CheckEpochs(3, 3);
+ PRUint64 max_epoch_type = (0x1ULL << 16) - 1;
+
+ // We assign the maximum possible epochs
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteEpochNum(client_->ssl_fd(), max_epoch_type));
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceReadEpochNum(server_->ssl_fd(), max_epoch_type));
+ CheckEpochs(max_epoch_type, 3);
+ // Upon trying to execute KeyUpdate, we return a SECFailure.
+ EXPECT_EQ(SECFailure, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ SendReceive(50);
+}
+
+// This test checks the compliance with the RFC 9147 stating the behaviour
+// reaching the max epoch: RFC 9147 Section 8. If a sending implementation
+// receives a KeyUpdate with request_update set to "update_requested", it MUST
+// NOT send its own KeyUpdate if that would cause it to exceed these limits and
+// SHOULD instead ignore the "update_requested" flag.
+TEST_F(TlsConnectDatagram13, DTLSKU_ClientMaxEpochReachedUpdateRequested) {
+ Connect();
+ CheckEpochs(3, 3);
+
+ PRUint64 max_epoch_type = (0x1ULL << 16) - 1;
+
+ // We assign the maximum possible epochs - 1.
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteEpochNum(client_->ssl_fd(), max_epoch_type));
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceReadEpochNum(server_->ssl_fd(), max_epoch_type));
+
+ CheckEpochs(max_epoch_type, 3);
+ // Once we call KeyUpdate with update requested
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // Only one key (that has not reached the maximum epoch) was updated.
+ CheckEpochs(max_epoch_type, 4);
+ SendReceive(50);
+}
+
+// DTLS1.3 KeyUpdate - Automatic update tests
+
+// RFC 9147 Section 4.5.3.
+// Implementations SHOULD NOT protect more records than allowed by the limit
+// specified for the negotiated AEAD.
+// Implementations SHOULD initiate a key update before reaching this limit.
+
+// These two tests check that the KeyUpdate is automatically called upon
+// reaching the reading/writing limit.
+TEST_F(TlsConnectDatagram13, DTLSKU_AutomaticOnWrite) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+ CheckEpochs(3, 3);
+
+ // Set this to one below the write threshold.
+ uint64_t threshold = 0x438000000;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ // This should be OK.
+ client_->SendData(10);
+ server_->ReadBytes();
+
+ // This should cause the client to update.
+ client_->SendData(15);
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+
+ // The client key epoch was incremented.
+ CheckEpochs(4, 3);
+ // Checking that we still can send/receive data.
+ SendReceive(100);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_AutomaticOnRead) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+ CheckEpochs(3, 3);
+
+ // Set this to one below the read threshold.
+ uint64_t threshold = 0x4ec000000 - 1;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(client_);
+ client_->SendData(10);
+ DataBuffer d = filter->ReturnRecorded();
+
+ client_->SendDirect(d);
+ // This message will cause the server to start KeyUpdate with updateRequested
+ // = 1.
+ server_->ReadBytes();
+
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ SSLInt_SendImmediateACK(client_->ssl_fd());
+ server_->ReadBytes();
+
+ // Both keys got updated.
+ CheckEpochs(4, 4);
+ // Checking that we still can send/receive data.
+ SendReceive(100);
+}
+
+// The test describes the situation when there was a request
+// to execute an automatic KU, but the server has not responded.
+TEST_F(TlsConnectDatagram13, DTLSKU_CanSendBeforeThreshold) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+ CheckEpochs(3, 3);
+
+ uint64_t threshold = 0x5a0000000 - 2;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ size_t received_bytes = server_->received_bytes();
+ // We still can send a message
+ client_->SendData(15);
+
+ // We can not send a message anymore
+ client_->ExpectReadWriteError();
+ client_->SendData(105);
+
+ server_->ReadBytes();
+ // And it was not received.
+ ASSERT_EQ((size_t)received_bytes + 15, server_->received_bytes());
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_DiscardAfterThreshold) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);
+ CheckEpochs(3, 3);
+
+ uint64_t threshold = 0x5a0000000 - 3;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
+ EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));
+
+ size_t received_bytes = server_->received_bytes();
+
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(client_);
+ client_->SendData(30);
+ DataBuffer d = filter->ReturnRecorded();
+
+ client_->SendDirect(d);
+ client_->SendDirect(d);
+
+ server_->ReadBytes();
+ // Only one message was received.
+ ASSERT_EQ((size_t)received_bytes + 30, server_->received_bytes());
+}
+
+// DTLS1.3 KeyUpdate - Managing previous epoch messages
+// RFC 9147 Section 8.
+// Due to the possibility of an ACK message for a KeyUpdate being lost
+// and thereby preventing the sender of the KeyUpdate from updating its
+// keying material, receivers MUST retain the pre-update keying material
+// until receipt and successful decryption of a message using the new
+// keys.
+
+// This test checks that message encrypted with the key n-1 will be accepted
+// after KeyUpdate is executed, but before the message n has arrived.
+TEST_F(TlsConnectDatagram13, DTLSKU_PreviousEpochIsAcceptedBeforeNew) {
+ size_t len = 10;
+
+ Connect();
+ // Client starts KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server receives KeyUpdate and sends ACK
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ // Client has not yet received the ACK, so the writing key epoch has not
+ // changed
+ client_->CheckEpochs(3, 3);
+ server_->CheckEpochs(4, 3);
+
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(client_);
+
+ // Here the message previousEpochMessageBuffer contains a message
+ // encrypted with the client 3rd epoch key, m1 = enc(message, key_3)
+ client_->SendData(len);
+ DataBuffer d = filter->ReturnRecorded();
+
+ // Client has received the ACK
+ client_->ReadBytes();
+ // Now he updates the writing Key to 4
+ client_->CheckEpochs(3, 4);
+ server_->CheckEpochs(4, 3);
+
+ // And now we resend the message m1 and successfully receive it
+ client_->SendDirect(d);
+ WAIT_(server_->received_bytes() == len, 2000);
+ ASSERT_EQ(len, server_->received_bytes());
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// This test checks that message encrypted with the key n-2 will not be accepted
+// after KeyUpdate is executed, but before the message n has arrived.
+TEST_F(TlsConnectDatagram13, DTLSKU_2EpochsAgoIsRejected) {
+ size_t len = 10;
+
+ Connect();
+ CheckEpochs(3, 3);
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(client_);
+ client_->SendData(len);
+ DataBuffer d = filter->ReturnRecorded();
+ client_->ResetSentBytes();
+
+ SendAndProcessKU(client_, server_, PR_FALSE);
+ SendAndProcessKU(client_, server_, PR_FALSE);
+
+ // Executing 2 KeyUpdates, so the client writing key is equal to 5 now
+ CheckEpochs(5, 3);
+ // And now we resend the message m1 encrypted with the key n-2 (3)
+ client_->SendDirect(d);
+ server_->ReadBytes();
+ // Server has still received just legal_message_len of bytes (not the
+ // previousEpochLen + legal_message_len)
+ ASSERT_EQ((size_t)0, server_->received_bytes());
+ // Checking that we still can send/receive data.
+ SendReceive(60);
+}
+
+// This test checks that that message encrypted with the key n-1 will be
+// rejected after KeyUpdate is executed, and after the message n has arrived.
+TEST_F(TlsConnectDatagram13, DTLSKU_PreviousEpochIsAcceptedAfterNew) {
+ size_t len = 30;
+ size_t legal_message_len = 20;
+
+ Connect();
+ // Client starts KeyUpdate
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ // Server receives KeyUpdate and sends ACK
+ server_->ReadBytes();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ // Client has not yet received the ACK, so the writing key epoch has not
+ // changed
+ client_->CheckEpochs(3, 3);
+ server_->CheckEpochs(4, 3);
+
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(client_);
+
+ // Here the message previousEpochMessageBuffer contains a message
+ // encrypted with the client 3rd epoch key, m1 = enc(message, key_3)
+ client_->SendData(len);
+ DataBuffer d = filter->ReturnRecorded();
+ client_->ResetSentBytes();
+
+ // Client has received the ACK
+ client_->ReadBytes();
+ client_->CheckEpochs(3, 4);
+ server_->CheckEpochs(4, 3);
+
+ // At this moment, a client will send a message with the new key
+ SendReceive(legal_message_len);
+ // As soon as it's received, the server will forbid the messaged from the
+ // previous epochs
+ server_->ReadBytes();
+
+ // If a message from the previous epoch arrives to the server (m1, the key_3
+ // was used to encrypt it)
+ client_->SendDirect(d);
+ // it will be silently dropped
+ server_->ReadBytes();
+ // Server has still received just legal_message_len of bytes (not the
+ // previousEpochLen + legal_message_len)
+ ASSERT_EQ((size_t)legal_message_len, server_->received_bytes());
+ // Checking that we still can send/receive data.
+ SendReceive(50);
+}
+
+// DTLS Epoch reconstruction test
+// RFC 9147 Section 8. 4.2.2. Reconstructing the Sequence Number and Epoch
+
+// This test checks that the epoch reconstruction is correct.
+// The function under testing is dtlscon.c::dtls_ReadEpoch.
+// We only consider the case when dtls_IsDtls13Ciphertext is true.
+
+typedef struct sslKeyUpdateReadEpochTVStr {
+ // The current epoch
+ DTLSEpoch epoch;
+ // Only two-bit epoch here
+ PRUint8 header;
+ DTLSEpoch expected_reconstructed_epoch;
+} sslKeyUpdateReadEpochTV_t;
+
+static const sslKeyUpdateReadEpochTV_t sslKeyUpdateReadEpochTV[26] = {
+ {0x1, 0x1, 0x1},
+
+ {0x2, 0x1, 0x1},
+ {0x2, 0x2, 0x2},
+
+ {0x3, 0x3, 0x3},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x1, 0x1},
+
+ {0x4, 0x0, 0x4}, // the difference (diff) between the reconstructed and
+ // the current epoch is equal to 0
+ {0x4, 0x1, 0x1}, // diff == 3
+ {0x4, 0x2, 0x2}, // diff == 2
+ {0x4, 0x3, 0x3}, // diff == 1
+
+ {0x5, 0x0, 0x4}, // diff == 1
+ {0x5, 0x1, 0x5}, // diff == 0
+ {0x5, 0x2, 0x2}, // diff == 3
+ {0x5, 0x3, 0x3}, // diff == 2
+
+ {0x6, 0x0, 0x4},
+ {0x6, 0x1, 0x5},
+ {0x6, 0x2, 0x6},
+ {0x6, 0x3, 0x3},
+
+ {0x7, 0x0, 0x4},
+ {0x7, 0x1, 0x5},
+ {0x7, 0x2, 0x6},
+ {0x7, 0x3, 0x7},
+
+ {0x8, 0x0, 0x8},
+ {0x8, 0x1, 0x5},
+ {0x8, 0x2, 0x6},
+ {0x8, 0x3, 0x7},
+
+ // Starting from here the pattern (starting from 4) repeats:
+ // if a current epoch is equal to n,
+ // the difference will behave as for n % 4 + 4.
+ // For example, if the current epoch is equal to 9, then
+ // the difference between the reconstructed epoch and the current one
+ // will be the same as for the 5th epoch.
+};
+
+TEST_F(TlsConnectDatagram13, DTLS_EpochReconstruction) {
+ PRUint8 header[5] = {0};
+ header[0] = 0x20;
+ DTLSEpoch epoch;
+
+ for (size_t i = 0; i < 26; i++) {
+ epoch = sslKeyUpdateReadEpochTV[i].epoch;
+ header[0] = (header[0] & 0xfc) | (sslKeyUpdateReadEpochTV[i].header & 0x3);
+ // ReadEpoch (dtlscon.c#1339) uses only spec->version and spec->epoch.
+ ASSERT_EQ(sslKeyUpdateReadEpochTV[i].expected_reconstructed_epoch,
+ dtls_ReadEpoch(SSL_LIBRARY_VERSION_TLS_1_3, epoch, header));
+ }
+}
+
+// RFC 9147. A.2. Handshake Protocol
+// struct {
+// HandshakeType msg_type; -- handshake type
+// uint24 length; -- bytes in message
+// uint16 message_seq; -- DTLS-required field
+// uint24 fragment_offset; -- DTLS-required field
+// uint24 fragment_length; -- DTLS-required field
+// select (msg_type) {
+// ...
+// case key_update: KeyUpdate;
+// } body;
+// } Handshake;
+//
+// enum {
+// update_not_requested(0), update_requested(1), (255)
+// } KeyUpdateRequest;
+
+// The next tests send malformed KeyUpdate messages.
+// A remainder: TLSKeyUpdateDamager filter takes as an input an agent,
+// a byte index and a value that the existing value of the byte with the byte
+// index will be replaced with. The filter catchs only the KeyUpdate messages,
+// keeping unchanged all the rest.
+
+// The first test, DTLSKeyUpdateDamagerFilterTestingNoModification,
+// checks the correctness of the filter itself. It replaces the value of 12th
+// byte with 0: The 12th byte is used to specify KeyUpdateRequest. Thus, the
+// modification done in the test will still result in the correct KeyUpdate
+// request.
+
+// The test DTLSKU_WrongValueForUpdateRequested is modifying
+// KeyUpdateRequest byte to have an not-allowed value.
+
+// The test DTLSKeyUpdateDamagedLength modifies the 3rd byte (one of the length
+// bytes).
+
+// The test DTLSKeyUpdateDamagedLengthLongMessage changes the length of the
+// message as well.
+
+// The test DTLSKeyUpdateDamagedFragmentLength modifies the 10th byte (one of
+// the fragment_length bytes)
+
+TEST_F(TlsConnectDatagram13, DTLSKU_WrongValueForUpdateRequested) {
+ EnsureTlsSetup();
+ // Filter replacing the update_requested with an unexpected value.
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 12, 2);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+ filter->Enable();
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+
+ ExpectAlert(server_, kTlsAlertDecodeError);
+ client_->ExpectReceiveAlert(kTlsAlertDecodeError);
+
+ server_->ExpectReadWriteError();
+ client_->ExpectReadWriteError();
+
+ server_->ReadBytes();
+ client_->ReadBytes();
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_KEY_UPDATE);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+
+ // No KeyUpdate happened.
+ CheckEpochs(3, 3);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_DamagedLength) {
+ EnsureTlsSetup();
+ // Filter replacing the length value with 0.
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 3, 0);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+ filter->Enable();
+
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // No KeyUpdate happened.
+ CheckEpochs(3, 3);
+ SendReceive(50);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_DamagedLengthTooLong) {
+ EnsureTlsSetup();
+ // Filter replacing the second byte of length with one
+ // The message length is increased by 2 ^ 8
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 2, 2);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+ filter->Enable();
+
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // No KeyUpdate happened.
+ CheckEpochs(3, 3);
+ SendReceive(50);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_DamagedFragmentLength) {
+ EnsureTlsSetup();
+ // Filter replacing the fragment length with 1.
+ auto filter = MakeTlsFilter<TLSKeyUpdateDamager>(client_, 10, 1);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+ filter->Enable();
+
+ SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE);
+ filter->Disable();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ client_->ReadBytes();
+ // No KeyUpdate happened.
+ CheckEpochs(3, 3);
+ SendReceive(50);
+}
+
+// This filter is used in order to modify an ACK message.
+// As it's possible that one record contains several ACKs,
+// we fault all of them.
+
+class TLSACKDamager : public TlsRecordFilter {
+ public:
+ TLSACKDamager(const std::shared_ptr<TlsAgent>& a, size_t byte, uint8_t val)
+ : TlsRecordFilter(a), offset_(byte), value_(val) {}
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ if (!header.is_protected()) {
+ return KEEP;
+ }
+
+ uint16_t protection_epoch;
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ TlsRecordHeader out_header;
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ if (plaintext.data() == NULL || plaintext.len() == 0) {
+ return KEEP;
+ }
+
+ if (decrypting() && inner_content_type != ssl_ct_ack) {
+ return KEEP;
+ }
+
+ // We compute the number of ACKS in the message
+ // As we keep processing the ACK even if one message is incorrent,
+ // we fault all the found ACKs.
+
+ uint8_t ack_message_header_len = 2;
+ uint8_t ack_message_len_one_ACK = 16;
+ uint64_t acks = plaintext.len() - ack_message_header_len;
+ EXPECT_EQ((uint64_t)0, acks % ack_message_len_one_ACK);
+ acks = acks / ack_message_len_one_ACK;
+
+ if (plaintext.len() <= ack_message_header_len + offset_ +
+ (acks - 1) * ack_message_len_one_ACK) {
+ return KEEP;
+ }
+
+ for (size_t i = 0; i < acks; i++) {
+ // Here we replace the offset_-th byte after the header
+ // i.e. headerAck + ACK(0) + ACK(1) <-- the offset_-th byte
+ // of ACK(0), ACK(1), etc
+ plaintext.data()[ack_message_header_len + offset_ +
+ i * ack_message_len_one_ACK] = value_;
+ }
+
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintext, &ciphertext, &out_header);
+ if (!ok) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+
+ protected:
+ size_t offset_;
+ uint8_t value_;
+};
+
+// The next two tests are modifying the ACK message:
+
+// First, we call KeyUpdate on the client side. The server successfully
+// processes it, and it's sending an ACK message. At this moment, the filter
+// modifies the content of the ACK message by changing the seqNum or epoch and
+// sends it back to the client.
+//
+// struct {
+// uint64 epoch;
+// uint64 sequence_number;
+// } RecordNumber;
+
+// struct {
+// RecordNumber record_numbers<0..2^16-1>;
+// } ACK;
+
+TEST_F(TlsConnectDatagram13, DTLSKU_ModifACKEpoch) {
+ EnsureTlsSetup();
+ uint8_t byte = 3;
+ uint8_t v = 1;
+ // The filter will replace value-th byte of each ACK with one
+ // The epoch will be more than v * 2 ^ ((byte - 1) * 8).
+ // HandleACK function allows the epochs such that (epoch > RECORD_EPOCH_MAX)
+ // where RECORD_EPOCH_MAX == ((0x1ULL << 16) - 1)
+ auto filter = MakeTlsFilter<TLSACKDamager>(server_, byte, v);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+ CheckEpochs(3, 3);
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ server_->ReadBytes();
+
+ filter->Enable();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ filter->Disable();
+
+ client_->ReadBytes();
+ server_->CheckEpochs(4, 3);
+ // The client has not received the ACK, so it will not update the key.
+ client_->CheckEpochs(3, 3);
+
+ // The communication still continues.
+ SendReceive(50);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_ModifACKSeqNum) {
+ EnsureTlsSetup();
+ uint8_t byte = 7;
+ uint8_t v = 1;
+ // The filter will replace value byte of each ACK with one
+ // The seqNum will be more than v * 2 ^ ((byte - 1) * 8).
+ // HandleACK function allows the epochs such that (seq > RECORD_SEQ_MAX)
+ // where RECORD_SEQ_MAX == ((0x1ULL << 48) - 1)
+
+ // here byte + 8 means that we modify not epoch, but sequenceNum
+ auto filter = MakeTlsFilter<TLSACKDamager>(server_, byte + 8, v);
+ filter->EnableDecryption();
+ filter->Disable();
+ Connect();
+
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+ server_->ReadBytes();
+
+ filter->Enable();
+ SSLInt_SendImmediateACK(server_->ssl_fd());
+ filter->Disable();
+
+ client_->ReadBytes();
+
+ client_->ReadBytes();
+ server_->CheckEpochs(4, 3);
+ // The client has not received the ACK, so it will not update the key.
+ client_->CheckEpochs(3, 3);
+
+ // The communication still continues.
+ SendReceive(50);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_TooEarly_ClientCannotSendKeyUpdate) {
+ StartConnect();
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(server_);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+
+ EXPECT_EQ(SECFailure, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKeyUpdateTooEarly_ServerCannotSendKeyUpdate) {
+ StartConnect();
+ auto filter = MakeTlsFilter<TLSRecordSaveAndDropNext>(server_);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+
+ EXPECT_EQ(SECFailure, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
+}
+
+class DTlsEncryptedHandshakeHeaderReplacer : public TlsRecordFilter {
+ public:
+ DTlsEncryptedHandshakeHeaderReplacer(const std::shared_ptr<TlsAgent>& a,
+ uint8_t old_ct, uint8_t new_ct)
+ : TlsRecordFilter(a),
+ old_ct_(old_ct),
+ new_ct_(new_ct),
+ replaced_(false) {}
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ if (replaced_) return KEEP;
+
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ auto& protection_spec = spec(protection_epoch);
+ uint32_t msg_type = 256; // Not a real message
+ if (!plaintext.Read(0, 1, &msg_type) || msg_type == old_ct_) {
+ replaced_ = true;
+ plaintext.Write(0, new_ct_, 1);
+ }
+
+ uint64_t seq_num = protection_spec.next_out_seqno();
+ if (out_header.is_dtls()) {
+ seq_num |= out_header.sequence_number() & (0xffffULL << 48);
+ }
+ out_header.sequence_number(seq_num);
+
+ DataBuffer ciphertext;
+ bool rv = Protect(protection_spec, out_header, inner_content_type,
+ plaintext, &ciphertext, &out_header);
+ if (!rv) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+
+ private:
+ uint8_t old_ct_;
+ uint8_t new_ct_;
+ bool replaced_;
+};
+
+// The next tests check the behaviour of KU before the handshake is finished.
+TEST_F(TlsConnectDatagram13, DTLSKU_TooEarly_Client) {
+ StartConnect();
+ // This filter takes the record and if it finds kTlsHandshakeFinished
+ // it replaces it with kTlsHandshakeKeyUpdate
+ // Then, the KeyUpdate will be started when the handshake is not yet finished
+ // This handshake will be cancelled.
+ auto filter = MakeTlsFilter<DTlsEncryptedHandshakeHeaderReplacer>(
+ server_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+ ExpectAlert(client_, kTlsAlertUnexpectedMessage);
+ client_->Handshake();
+ client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
+ server_->Handshake();
+ server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+TEST_F(TlsConnectDatagram13, DTLSKU_TooEarly_Server) {
+ StartConnect();
+ // This filter takes the record and if it finds kTlsHandshakeFinished
+ // it replaces it with kTlsHandshakeKeyUpdate
+ auto filter = MakeTlsFilter<DTlsEncryptedHandshakeHeaderReplacer>(
+ client_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
+ filter->EnableDecryption();
+
+ client_->Handshake();
+ server_->Handshake();
+ client_->Handshake();
+ ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ server_->Handshake();
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
+ client_->Handshake();
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+} // namespace nss_test \ No newline at end of file