/* -*- 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(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(); 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(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 kPskDummyVal(16, 0xFF); SECItem psk_item = {siBuffer, toUcharPtr(kPskDummyVal.data()), static_cast(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 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(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(short_size); EXPECT_EQ(SECSuccess, SSL_SetMaxEarlyDataSize(server_->ssl_fd(), static_cast(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(strlen(big_message))); } else { sent = PR_Write(client_->ssl_fd(), big_message, static_cast(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 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(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 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 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(); client_->SetFilter(coalesce); // Send (and hold) early data. static const std::vector early_data = {3, 2, 1}; EXPECT_EQ(static_cast(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 late_data = {7, 8, 9, 10}; EXPECT_EQ(static_cast(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 buf(10); PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.size()); ASSERT_EQ(static_cast(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(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(sizeof(data)), rv); // Consume the ClientHello and generate ServerHello..Finished. server_->Handshake(); // Read some of the data. std::vector small_buffer(1 + sizeof(data) / 2); rv = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size()); EXPECT_EQ(static_cast(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(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(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(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(sizeof(data)), written); // Block and capture the next packet. auto holder = std::make_shared(); client_->SetFilter(holder); written = PR_Write(client_->ssl_fd(), data, sizeof(data)); EXPECT_EQ(static_cast(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 small_buffer(sizeof(data)); PRInt32 read = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size()); EXPECT_EQ(static_cast(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(sizeof(data) - 1), written); // Consume the ClientHello and generate ServerHello..Finished. server_->Handshake(); // Read all of the data. std::vector buffer(sizeof(data)); PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size()); EXPECT_EQ(static_cast(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(sizeof(data) - 1), written); // Consume the ClientHello and generate ServerHello..Finished. server_->Handshake(); // Try to read all of the data. std::vector 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(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(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 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(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(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