summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
new file mode 100644
index 0000000000..51ec9d3ee5
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -0,0 +1,1183 @@
+/* -*- 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 "sslexp.h"
+#include "sslproto.h"
+
+extern "C" {
+// This is not something that should make you happy.
+#include "libssl_internals.h"
+}
+
+#include "cpputil.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_P(TlsConnectTls13, ZeroRtt) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ZeroRttApplicationReject) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ auto reject_0rtt = [](PRBool firstHello, const PRUint8* clientToken,
+ unsigned int clientTokenLen, PRUint8* appToken,
+ unsigned int* appTokenLen, unsigned int appTokenMax,
+ void* arg) {
+ auto* called = reinterpret_cast<bool*>(arg);
+ *called = true;
+
+ EXPECT_TRUE(firstHello);
+ EXPECT_EQ(0U, clientTokenLen);
+ return ssl_hello_retry_reject_0rtt;
+ };
+
+ bool cb_run = false;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+ reject_0rtt, &cb_run));
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ EXPECT_TRUE(cb_run);
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
+ // The test fixtures enable anti-replay in SetUp(). This results in 0-RTT
+ // being rejected until at least one window passes. SetupFor0Rtt() forces a
+ // rollover of the anti-replay filters, which clears that state and allows
+ // 0-RTT to work. Make the first connection manually to avoid that rollover
+ // and cause 0-RTT to be rejected.
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->Set0RttEnabled(true); // So we signal that we allow 0-RTT.
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys();
+
+ Reset();
+ StartConnect();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+class TlsZeroRttReplayTest : public TlsConnectTls13 {
+ private:
+ class SaveFirstPacket : public PacketFilter {
+ public:
+ PacketFilter::Action Filter(const DataBuffer& input,
+ DataBuffer* output) override {
+ if (!packet_.len() && input.len()) {
+ packet_ = input;
+ }
+ return KEEP;
+ }
+
+ const DataBuffer& packet() const { return packet_; }
+
+ private:
+ DataBuffer packet_;
+ };
+
+ protected:
+ void RunTest(bool rollover, const ScopedPK11SymKey& epsk) {
+ // Now run a true 0-RTT handshake, but capture the first packet.
+ auto first_packet = std::make_shared<SaveFirstPacket>();
+ client_->SetFilter(first_packet);
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ EXPECT_LT(0U, first_packet->packet().len());
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+
+ if (rollover) {
+ RolloverAntiReplay();
+ }
+
+ // Now replay that packet against the server.
+ Reset();
+ server_->StartConnect();
+ server_->Set0RttEnabled(true);
+ server_->SetAntiReplayContext(anti_replay_);
+ if (epsk) {
+ AddPsk(epsk, std::string("foo"), ssl_hash_sha256,
+ TLS_CHACHA20_POLY1305_SHA256);
+ }
+
+ // Capture the early_data extension, which should not appear.
+ auto early_data_ext =
+ MakeTlsFilter<TlsExtensionCapture>(server_, ssl_tls13_early_data_xtn);
+
+ // Finally, replay the ClientHello and force the server to consume it. Stop
+ // after the server sends its first flight; the client will not be able to
+ // complete this handshake.
+ server_->adapter()->PacketReceived(first_packet->packet());
+ server_->Handshake();
+ EXPECT_FALSE(early_data_ext->captured());
+ }
+
+ void RunResPskTest(bool rollover) {
+ // Run the initial handshake
+ SetupForZeroRtt();
+ ExpectResumption(RESUME_TICKET);
+ RunTest(rollover, ScopedPK11SymKey(nullptr));
+ }
+
+ void RunExtPskTest(bool rollover) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ ASSERT_NE(nullptr, slot);
+
+ const std::vector<uint8_t> kPskDummyVal(16, 0xFF);
+ SECItem psk_item = {siBuffer, toUcharPtr(kPskDummyVal.data()),
+ static_cast<unsigned int>(kPskDummyVal.size())};
+ PK11SymKey* key =
+ PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
+ CKA_DERIVE, &psk_item, NULL);
+ ASSERT_NE(nullptr, key);
+ ScopedPK11SymKey scoped_psk(key);
+ RolloverAntiReplay();
+ AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256,
+ TLS_CHACHA20_POLY1305_SHA256);
+ StartConnect();
+ RunTest(rollover, scoped_psk);
+ }
+};
+
+TEST_P(TlsZeroRttReplayTest, ResPskZeroRttReplay) { RunResPskTest(false); }
+
+TEST_P(TlsZeroRttReplayTest, ExtPskZeroRttReplay) { RunExtPskTest(false); }
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) {
+ RunResPskTest(true);
+}
+
+// Test that we don't try to send 0-RTT data when the server sent
+// us a ticket without the 0-RTT flags.
+TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+ Reset();
+ StartConnect();
+ // Now turn on 0-RTT but too late for the ticket.
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(false, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+// Make sure that a session ticket sent well after the original handshake
+// can be used for 0-RTT.
+// Stream because DTLS doesn't support SSL_SendSessionTicket.
+TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicket) {
+ // Use a small-ish anti-replay window.
+ ResetAntiReplay(100 * PR_USEC_PER_MSEC);
+ RolloverAntiReplay();
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->Set0RttEnabled(true);
+ Connect();
+ CheckKeys();
+
+ // Now move time forward 30s and send a ticket.
+ AdvanceTime(30 * PR_USEC_PER_SEC);
+ EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
+ SendReceive();
+ Reset();
+ StartConnect();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+}
+
+// Check that post-handshake authentication with a long RTT doesn't
+// make things worse.
+TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketPha) {
+ // Use a small-ish anti-replay window.
+ ResetAntiReplay(100 * PR_USEC_PER_MSEC);
+ RolloverAntiReplay();
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->Set0RttEnabled(true);
+ client_->SetupClientAuth();
+ client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+ Connect();
+ CheckKeys();
+
+ // Add post-handshake authentication, with some added delays.
+ AdvanceTime(10 * PR_USEC_PER_SEC);
+ EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()));
+ AdvanceTime(10 * PR_USEC_PER_SEC);
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ReadBytes(50);
+
+ AdvanceTime(10 * PR_USEC_PER_SEC);
+ EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
+ server_->SendData(100);
+ client_->ReadBytes(100);
+ Reset();
+ StartConnect();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+}
+
+// Same, but with client authentication on the first connection.
+TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketClientAuth) {
+ // Use a small-ish anti-replay window.
+ ResetAntiReplay(100 * PR_USEC_PER_MSEC);
+ RolloverAntiReplay();
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+ server_->Set0RttEnabled(true);
+ Connect();
+ CheckKeys();
+
+ // Now move time forward 30s and send a ticket.
+ AdvanceTime(30 * PR_USEC_PER_SEC);
+ EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
+ SendReceive();
+ Reset();
+ StartConnect();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ClearServerCache();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
+ ExpectResumption(RESUME_NONE);
+ server_->Set0RttEnabled(true);
+ StartConnect();
+
+ // Client sends ordinary ClientHello.
+ client_->Handshake();
+
+ // Verify that the server doesn't get data.
+ uint8_t buf[100];
+ PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_EQ(SECFailure, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ // Now make sure that things complete.
+ Handshake();
+ CheckConnected();
+ SendReceive();
+ CheckKeys();
+}
+
+// Advancing time after sending the ClientHello means that the ticket age that
+// arrives at the server is too low. The server then rejects early data if this
+// delay exceeds half the anti-replay window.
+TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
+ static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
+ ResetAntiReplay(kWindow);
+ SetupForZeroRtt();
+
+ Reset();
+ StartConnect();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false, [this]() {
+ AdvanceTime(1 + kWindow / 2);
+ return true;
+ });
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+ SendReceive();
+}
+
+// In this test, we falsely inflate the estimate of the RTT by delaying the
+// ServerHello on the first handshake. This results in the server estimating a
+// higher value of the ticket age than the client ultimately provides. Add a
+// small tolerance for variation in ticket age and the ticket will appear to
+// arrive prematurely, causing the server to reject early data.
+TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
+ static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
+ ResetAntiReplay(kWindow);
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->Set0RttEnabled(true);
+ StartConnect();
+ client_->Handshake(); // ClientHello
+ server_->Handshake(); // ServerHello
+ AdvanceTime(1 + kWindow / 2);
+ Handshake(); // Remainder of handshake
+ CheckConnected();
+ SendReceive();
+ CheckKeys();
+
+ Reset();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ExpectEarlyDataAccepted(false);
+ StartConnect();
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
+ EnableAlpn();
+ SetupForZeroRtt();
+ EnableAlpn();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ExpectEarlyDataAccepted(true);
+ ZeroRttSendReceive(true, true, [this]() {
+ client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
+ return true;
+ });
+ Handshake();
+ CheckConnected();
+ SendReceive();
+ CheckAlpn("a");
+}
+
+// NOTE: In this test and those below, the client always sends
+// post-ServerHello alerts with the handshake keys, even if the server
+// has accepted 0-RTT. In some cases, as with errors in
+// EncryptedExtensions, the client can't know the server's behavior,
+// and in others it's just simpler. What the server is expecting
+// depends on whether it accepted 0-RTT or not. Eventually, we may
+// make the server trial decrypt.
+//
+// Have the server negotiate a different ALPN value, and therefore
+// reject 0-RTT.
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeServer) {
+ EnableAlpn();
+ SetupForZeroRtt();
+ static const uint8_t client_alpn[] = {0x01, 0x61, 0x01, 0x62}; // "a", "b"
+ static const uint8_t server_alpn[] = {0x01, 0x62}; // "b"
+ client_->EnableAlpn(client_alpn, sizeof(client_alpn));
+ server_->EnableAlpn(server_alpn, sizeof(server_alpn));
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false, [this]() {
+ client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
+ return true;
+ });
+ Handshake();
+ CheckConnected();
+ SendReceive();
+ CheckAlpn("b");
+}
+
+// Check that the client validates the ALPN selection of the server.
+// Stomp the ALPN on the client after sending the ClientHello so
+// that the server selection appears to be incorrect. The client
+// should then fail the connection.
+TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) {
+ EnableAlpn();
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ EnableAlpn();
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true, [this]() {
+ PRUint8 b[] = {'b'};
+ client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
+ EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b)));
+ client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+ return true;
+ });
+ if (variant_ == ssl_variant_stream) {
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ } else {
+ client_->Handshake();
+ }
+ client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+}
+
+// Set up with no ALPN and then set the client so it thinks it has ALPN.
+// The server responds without the extension and the client returns an
+// error.
+TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true, [this]() {
+ PRUint8 b[] = {'b'};
+ EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
+ client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+ return true;
+ });
+ if (variant_ == ssl_variant_stream) {
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ Handshake();
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ } else {
+ client_->Handshake();
+ }
+ client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+}
+
+// Remove the old ALPN value and so the client will not offer early data.
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeBoth) {
+ EnableAlpn();
+ SetupForZeroRtt();
+ static const std::vector<uint8_t> alpn({0x01, 0x62}); // "b"
+ EnableAlpn(alpn);
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false, [this]() {
+ client_->CheckAlpn(SSL_NEXT_PROTO_NO_SUPPORT);
+ return false;
+ });
+ Handshake();
+ CheckConnected();
+ SendReceive();
+ CheckAlpn("b");
+}
+
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (no app data sent)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->Set0RttEnabled(true); // set ticket_allow_early_data
+ Connect();
+
+ SendReceive(); // Need to read so that we absorb the session tickets.
+ CheckKeys();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ StartConnect();
+ // We will send the early data xtn without sending actual early data. Thus
+ // a 1.2 server shouldn't fail until the client sends an alert because the
+ // client sends end_of_early_data only after reading the server's flight.
+ client_->Set0RttEnabled(true);
+
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+ if (variant_ == ssl_variant_stream) {
+ server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+ }
+ client_->Handshake();
+ server_->Handshake();
+ ASSERT_TRUE_WAIT(
+ (client_->error_code() == SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA), 2000);
+
+ // DTLS will timeout as we bump the epoch when installing the early app data
+ // cipher suite. Thus the encrypted alert will be ignored.
+ if (variant_ == ssl_variant_stream) {
+ // The client sends an encrypted alert message.
+ ASSERT_TRUE_WAIT(
+ (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
+ 2000);
+ }
+}
+
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (with app data)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
+ const char* k0RttData = "ABCDEF";
+ const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->Set0RttEnabled(true); // set ticket_allow_early_data
+ Connect();
+
+ SendReceive(); // Need to read so that we absorb the session tickets.
+ CheckKeys();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ StartConnect();
+ // Send the early data xtn in the CH, followed by early app data. The server
+ // will fail right after sending its flight, when receiving the early data.
+ client_->Set0RttEnabled(true);
+ client_->Handshake(); // Send ClientHello.
+ PRInt32 rv =
+ PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write.
+ EXPECT_EQ(k0RttDataLen, rv);
+
+ if (variant_ == ssl_variant_stream) {
+ // When the server receives the early data, it will fail.
+ server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+ server_->Handshake(); // Consume ClientHello
+ EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
+ } else {
+ // If it's datagram, we just discard the early data.
+ server_->Handshake(); // Consume ClientHello
+ EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+ }
+
+ // The client now reads the ServerHello and fails.
+ ASSERT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+ client_->Handshake();
+ client_->CheckErrorCode(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA);
+}
+
+TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
+ EnsureTlsSetup();
+ const char* big_message = "0123456789abcdef";
+ const size_t short_size = strlen(big_message) - 1;
+ const PRInt32 short_length = static_cast<PRInt32>(short_size);
+ EXPECT_EQ(SECSuccess,
+ SSL_SetMaxEarlyDataSize(server_->ssl_fd(),
+ static_cast<PRUint32>(short_size)));
+ SetupForZeroRtt();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ client_->Handshake();
+ CheckEarlyDataLimit(client_, short_size);
+
+ PRInt32 sent;
+ // Writing more than the limit will succeed in TLS, but fail in DTLS.
+ if (variant_ == ssl_variant_stream) {
+ sent = PR_Write(client_->ssl_fd(), big_message,
+ static_cast<PRInt32>(strlen(big_message)));
+ } else {
+ sent = PR_Write(client_->ssl_fd(), big_message,
+ static_cast<PRInt32>(strlen(big_message)));
+ EXPECT_GE(0, sent);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ // Try an exact-sized write now.
+ sent = PR_Write(client_->ssl_fd(), big_message, short_length);
+ }
+ EXPECT_EQ(short_length, sent);
+
+ // Even a single octet write should now fail.
+ sent = PR_Write(client_->ssl_fd(), big_message, 1);
+ EXPECT_GE(0, sent);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ // Process the ClientHello and read 0-RTT.
+ server_->Handshake();
+ CheckEarlyDataLimit(server_, short_size);
+
+ std::vector<uint8_t> buf(short_size + 1);
+ PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
+ EXPECT_EQ(short_length, read);
+ EXPECT_EQ(0, memcmp(big_message, buf.data(), short_size));
+
+ // Second read fails.
+ read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
+ EXPECT_EQ(SECFailure, read);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ Handshake();
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
+ EnsureTlsSetup();
+
+ const size_t limit = 5;
+ EXPECT_EQ(SECSuccess, SSL_SetMaxEarlyDataSize(server_->ssl_fd(), limit));
+ SetupForZeroRtt();
+
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ client_->Handshake(); // Send ClientHello
+ CheckEarlyDataLimit(client_, limit);
+
+ server_->Handshake(); // Process ClientHello, send server flight.
+
+ // Lift the limit on the client.
+ EXPECT_EQ(SECSuccess,
+ SSLInt_SetSocketMaxEarlyDataSize(client_->ssl_fd(), 1000));
+
+ // Send message
+ const char* message = "0123456789abcdef";
+ const PRInt32 message_len = static_cast<PRInt32>(strlen(message));
+ EXPECT_EQ(message_len, PR_Write(client_->ssl_fd(), message, message_len));
+
+ if (variant_ == ssl_variant_stream) {
+ // This error isn't fatal for DTLS.
+ ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+ }
+
+ server_->Handshake(); // This reads the early data and maybe throws an error.
+ if (variant_ == ssl_variant_stream) {
+ server_->CheckErrorCode(SSL_ERROR_TOO_MUCH_EARLY_DATA);
+ } else {
+ EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+ }
+ CheckEarlyDataLimit(server_, limit);
+
+ // Attempt to read early data. This will get an error.
+ std::vector<uint8_t> buf(strlen(message) + 1);
+ EXPECT_GT(0, PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()));
+ if (variant_ == ssl_variant_stream) {
+ EXPECT_EQ(SSL_ERROR_HANDSHAKE_FAILED, PORT_GetError());
+ } else {
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+ }
+
+ client_->Handshake(); // Process the server's first flight.
+ if (variant_ == ssl_variant_stream) {
+ client_->Handshake(); // Process the alert.
+ client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+ } else {
+ server_->Handshake(); // Finish connecting.
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+ }
+}
+
+class PacketCoalesceFilter : public PacketFilter {
+ public:
+ PacketCoalesceFilter() : packet_data_() {}
+
+ void SendCoalesced(std::shared_ptr<TlsAgent> agent) {
+ agent->SendDirect(packet_data_);
+ }
+
+ protected:
+ PacketFilter::Action Filter(const DataBuffer& input,
+ DataBuffer* output) override {
+ packet_data_.Write(packet_data_.len(), input);
+ return DROP;
+ }
+
+ private:
+ DataBuffer packet_data_;
+};
+
+TEST_P(TlsConnectTls13, ZeroRttOrdering) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ // Send out the ClientHello.
+ client_->Handshake();
+
+ // Now, coalesce the next three things from the client: early data, second
+ // flight and 1-RTT data.
+ auto coalesce = std::make_shared<PacketCoalesceFilter>();
+ client_->SetFilter(coalesce);
+
+ // Send (and hold) early data.
+ static const std::vector<uint8_t> early_data = {3, 2, 1};
+ EXPECT_EQ(static_cast<PRInt32>(early_data.size()),
+ PR_Write(client_->ssl_fd(), early_data.data(), early_data.size()));
+
+ // Send (and hold) the second client handshake flight.
+ // The client sends EndOfEarlyData after seeing the server Finished.
+ server_->Handshake();
+ client_->Handshake();
+
+ // Send (and hold) 1-RTT data.
+ static const std::vector<uint8_t> late_data = {7, 8, 9, 10};
+ EXPECT_EQ(static_cast<PRInt32>(late_data.size()),
+ PR_Write(client_->ssl_fd(), late_data.data(), late_data.size()));
+
+ // Now release them all at once.
+ coalesce->SendCoalesced(client_);
+
+ // Now ensure that the three steps are exposed in the right order on the
+ // server: delivery of early data, handshake callback, delivery of 1-RTT.
+ size_t step = 0;
+ server_->SetHandshakeCallback([&step](TlsAgent*) {
+ EXPECT_EQ(1U, step);
+ ++step;
+ });
+
+ std::vector<uint8_t> buf(10);
+ PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.size());
+ ASSERT_EQ(static_cast<PRInt32>(early_data.size()), read);
+ buf.resize(read);
+ EXPECT_EQ(early_data, buf);
+ EXPECT_EQ(0U, step);
+ ++step;
+
+ // The third read should be after the handshake callback and should return the
+ // data that was sent after the handshake completed.
+ buf.resize(10);
+ read = PR_Read(server_->ssl_fd(), buf.data(), buf.size());
+ ASSERT_EQ(static_cast<PRInt32>(late_data.size()), read);
+ buf.resize(read);
+ EXPECT_EQ(late_data, buf);
+ EXPECT_EQ(2U, step);
+}
+
+// Early data remains available after the handshake completes for TLS.
+TEST_F(TlsConnectStreamTls13, ZeroRttLateReadTls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data.
+ const uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ PRInt32 rv = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), rv);
+
+ // Consume the ClientHello and generate ServerHello..Finished.
+ server_->Handshake();
+
+ // Read some of the data.
+ std::vector<uint8_t> small_buffer(1 + sizeof(data) / 2);
+ rv = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
+ EXPECT_EQ(static_cast<PRInt32>(small_buffer.size()), rv);
+ EXPECT_EQ(0, memcmp(data, small_buffer.data(), small_buffer.size()));
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+
+ // After the handshake, it should be possible to read the remainder.
+ uint8_t big_buf[100];
+ rv = PR_Read(server_->ssl_fd(), big_buf, sizeof(big_buf));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - small_buffer.size()), rv);
+ EXPECT_EQ(0, memcmp(&data[small_buffer.size()], big_buf,
+ sizeof(data) - small_buffer.size()));
+
+ // And that's all there is to read.
+ rv = PR_Read(server_->ssl_fd(), big_buf, sizeof(big_buf));
+ EXPECT_GT(0, rv);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
+// Early data that arrives before the handshake can be read after the handshake
+// is complete.
+TEST_F(TlsConnectDatagram13, ZeroRttLateReadDtls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data.
+ const uint8_t data[] = {1, 2, 3};
+ PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+
+ // Reading at the server should return the early data, which was buffered.
+ uint8_t buf[sizeof(data) + 1] = {0};
+ PRInt32 read = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
+ EXPECT_EQ(0, memcmp(data, buf, sizeof(data)));
+}
+
+class PacketHolder : public PacketFilter {
+ public:
+ PacketHolder() = default;
+
+ virtual Action Filter(const DataBuffer& input, DataBuffer* output) {
+ packet_ = input;
+ Disable();
+ return DROP;
+ }
+
+ const DataBuffer& packet() const { return packet_; }
+
+ private:
+ DataBuffer packet_;
+};
+
+// Early data that arrives late is discarded for DTLS.
+TEST_F(TlsConnectDatagram13, ZeroRttLateArrivalDtls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data. Twice, so that we can read bits of it.
+ const uint8_t data[] = {1, 2, 3};
+ PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
+
+ // Block and capture the next packet.
+ auto holder = std::make_shared<PacketHolder>();
+ client_->SetFilter(holder);
+ written = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
+ EXPECT_FALSE(holder->enabled()) << "the filter should disable itself";
+
+ // Consume the ClientHello and generate ServerHello..Finished.
+ server_->Handshake();
+
+ // Read some of the data.
+ std::vector<uint8_t> small_buffer(sizeof(data));
+ PRInt32 read =
+ PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
+
+ EXPECT_EQ(static_cast<PRInt32>(small_buffer.size()), read);
+ EXPECT_EQ(0, memcmp(data, small_buffer.data(), small_buffer.size()));
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+
+ server_->SendDirect(holder->packet());
+
+ // Reading now should return nothing, even though a valid packet was
+ // delivered.
+ read = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
+ EXPECT_GT(0, read);
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
+// Early data reads in TLS should be coalesced.
+TEST_F(TlsConnectStreamTls13, ZeroRttCoalesceReadTls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data. In two writes.
+ const uint8_t data[] = {1, 2, 3, 4, 5, 6};
+ PRInt32 written = PR_Write(client_->ssl_fd(), data, 1);
+ EXPECT_EQ(1, written);
+
+ written = PR_Write(client_->ssl_fd(), data + 1, sizeof(data) - 1);
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), written);
+
+ // Consume the ClientHello and generate ServerHello..Finished.
+ server_->Handshake();
+
+ // Read all of the data.
+ std::vector<uint8_t> buffer(sizeof(data));
+ PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
+ EXPECT_EQ(0, memcmp(data, buffer.data(), sizeof(data)));
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+}
+
+// Early data reads in DTLS should not be coalesced.
+TEST_F(TlsConnectDatagram13, ZeroRttNoCoalesceReadDtls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data. In two writes.
+ const uint8_t data[] = {1, 2, 3, 4, 5, 6};
+ PRInt32 written = PR_Write(client_->ssl_fd(), data, 1);
+ EXPECT_EQ(1, written);
+
+ written = PR_Write(client_->ssl_fd(), data + 1, sizeof(data) - 1);
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), written);
+
+ // Consume the ClientHello and generate ServerHello..Finished.
+ server_->Handshake();
+
+ // Try to read all of the data.
+ std::vector<uint8_t> buffer(sizeof(data));
+ PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
+ EXPECT_EQ(1, read);
+ EXPECT_EQ(0, memcmp(data, buffer.data(), 1));
+
+ // Read the remainder.
+ read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), read);
+ EXPECT_EQ(0, memcmp(data + 1, buffer.data(), sizeof(data) - 1));
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+}
+
+// Early data reads in DTLS should fail if the buffer is too small.
+TEST_F(TlsConnectDatagram13, ZeroRttShortReadDtls) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ client_->Handshake(); // ClientHello
+
+ // Write some early data. In two writes.
+ const uint8_t data[] = {1, 2, 3, 4, 5, 6};
+ PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
+
+ // Consume the ClientHello and generate ServerHello..Finished.
+ server_->Handshake();
+
+ // Try to read all of the data into a small buffer.
+ std::vector<uint8_t> buffer(sizeof(data));
+ PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), 1);
+ EXPECT_GT(0, read);
+ EXPECT_EQ(SSL_ERROR_RX_SHORT_DTLS_READ, PORT_GetError());
+
+ // Read again with more space.
+ read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
+ EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
+ EXPECT_EQ(0, memcmp(data, buffer.data(), sizeof(data)));
+
+ Handshake(); // Complete the handshake.
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+}
+
+// There are few ways in which TLS uses the clock and most of those operate on
+// timescales that would be ridiculous to wait for in a test. This is the one
+// test we have that uses the real clock. It tests that time passes by checking
+// that a small sleep results in rejection of early data. 0-RTT has a
+// configurable timer, which makes it ideal for this.
+TEST_F(TlsConnectStreamTls13, TimePassesByDefault) {
+ // Calling EnsureTlsSetup() replaces the time function on client and server,
+ // and sets up anti-replay, which we don't want, so initialize each directly.
+ client_->EnsureTlsSetup();
+ server_->EnsureTlsSetup();
+ // StartConnect() calls EnsureTlsSetup(), so avoid that too.
+ client_->StartConnect();
+ server_->StartConnect();
+
+ // Set a tiny anti-replay window. This has to be at least 2 milliseconds to
+ // have any chance of being relevant as that is the smallest window that we
+ // can detect. Anything smaller rounds to zero.
+ static const unsigned int kTinyWindowMs = 5;
+ ResetAntiReplay(static_cast<PRTime>(kTinyWindowMs * PR_USEC_PER_MSEC));
+ server_->SetAntiReplayContext(anti_replay_);
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->Set0RttEnabled(true);
+ Handshake();
+ CheckConnected();
+ SendReceive(); // Absorb a session ticket.
+ CheckKeys();
+
+ // Clear the first window.
+ PR_Sleep(PR_MillisecondsToInterval(kTinyWindowMs));
+
+ Reset();
+ client_->EnsureTlsSetup();
+ server_->EnsureTlsSetup();
+ client_->StartConnect();
+ server_->StartConnect();
+
+ // Early data is rejected by the server only if time passes for it as well.
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, false, []() {
+ // Sleep long enough that we minimize the risk of our RTT estimation being
+ // duped by stutters in test execution. This is very long to allow for
+ // flaky and low-end hardware, especially what our CI runs on.
+ PR_Sleep(PR_MillisecondsToInterval(1000));
+ return true;
+ });
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+}
+
+// Test that SSL_CreateAntiReplayContext doesn't pass bad inputs.
+TEST_F(TlsConnectStreamTls13, BadAntiReplayArgs) {
+ SSLAntiReplayContext* p;
+ // Zero or negative window.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, -1, 1, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 0, 1, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ // Zero k.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 0, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ // Zero bits.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 0, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 1, nullptr));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+
+ // Prove that these parameters do work, even if they are useless..
+ EXPECT_EQ(SECSuccess, SSL_CreateAntiReplayContext(0, 1, 1, 1, &p));
+ ASSERT_NE(nullptr, p);
+ ScopedSSLAntiReplayContext ctx(p);
+
+ // The socket isn't a client or server until later, so configuring a client
+ // should work OK.
+ client_->EnsureTlsSetup();
+ EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), ctx.get()));
+ EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), nullptr));
+}
+
+// See also TlsConnectGenericResumption.ResumeServerIncompatibleCipher
+TEST_P(TlsConnectTls13, ZeroRttDifferentCompatibleCipher) {
+ EnsureTlsSetup();
+ server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ // Change the ciphersuite. Resumption is OK because the hash is the same, but
+ // early data will be rejected.
+ server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256);
+ ExpectResumption(RESUME_TICKET);
+
+ StartConnect();
+ ZeroRttSendReceive(true, false);
+
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+ SendReceive();
+}
+
+// See also TlsConnectGenericResumption.ResumeServerIncompatibleCipher
+TEST_P(TlsConnectTls13, ZeroRttDifferentIncompatibleCipher) {
+ EnsureTlsSetup();
+ server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384);
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ // Resumption is rejected because the hash is different.
+ server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256);
+ ExpectResumption(RESUME_NONE);
+
+ StartConnect();
+ ZeroRttSendReceive(true, false);
+
+ Handshake();
+ ExpectEarlyDataAccepted(false);
+ CheckConnected();
+ SendReceive();
+}
+
+// The client failing to provide EndOfEarlyData results in failure.
+// After 0-RTT working perfectly, things fall apart later.
+// The server is unable to detect the change in keys, so it fails decryption.
+// The client thinks everything has worked until it gets the alert.
+TEST_F(TlsConnectStreamTls13, SuppressEndOfEarlyDataClientOnly) {
+ SetupForZeroRtt();
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ ExpectAlert(server_, kTlsAlertBadRecordMac);
+ Handshake();
+ EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+ EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
+ client_->CheckErrorCode(SSL_ERROR_BAD_MAC_ALERT);
+}
+
+TEST_P(TlsConnectGeneric, SuppressEndOfEarlyDataNoZeroRtt) {
+ EnsureTlsSetup();
+ client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
+ server_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
+ Connect();
+ SendReceive();
+}
+
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_SUITE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
+ TlsConnectTestBase::kTlsVariantsAll);
+#endif
+
+} // namespace nss_test