From f4a12fc67689a830e9da1c87fd11afe5bc09deb3 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 2 Jan 2020 21:06:40 +0100 Subject: Issue #1338 - Part 2: Update NSS to 3.48-RTM --- security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc | 410 ++++++++++++++++++++- 1 file changed, 392 insertions(+), 18 deletions(-) (limited to 'security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc') diff --git a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc index 07eadfbd1..928515067 100644 --- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc @@ -45,11 +45,40 @@ TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) { 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 call SSL_SetupAntiReplay() 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 this state. - // Here, we do the setup manually here without that forced rollover. + // 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); @@ -106,7 +135,7 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 { SendReceive(); if (rollover) { - SSLInt_RolloverAntiReplay(); + RolloverAntiReplay(); } // Now replay that packet against the server. @@ -184,20 +213,21 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) { CheckKeys(); } -// A small sleep after sending the ClientHello means that the ticket age that -// arrives at the server is too low. With a small tolerance for variation in -// ticket age (which is determined by the |window| parameter that is passed to -// SSL_SetupAntiReplay()), the server then rejects early data. +// 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); - EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3)); - SSLInt_RolloverAntiReplay(); // Make sure to flush replay state. - SSLInt_RolloverAntiReplay(); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(true, false, []() { - PR_Sleep(PR_MillisecondsToInterval(10)); + ZeroRttSendReceive(true, false, [this]() { + AdvanceTime(1 + kWindow / 2); return true; }); Handshake(); @@ -212,13 +242,15 @@ TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) { // 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 - PR_Sleep(PR_MillisecondsToInterval(10)); + AdvanceTime(1 + kWindow / 2); Handshake(); // Remainder of handshake CheckConnected(); SendReceive(); @@ -227,9 +259,6 @@ TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) { Reset(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); - EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3)); - SSLInt_RolloverAntiReplay(); // Make sure to flush replay state. - SSLInt_RolloverAntiReplay(); ExpectResumption(RESUME_TICKET); ExpectEarlyDataAccepted(false); StartConnect(); @@ -649,6 +678,351 @@ TEST_P(TlsConnectTls13, ZeroRttOrdering) { 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(); +} + #ifndef NSS_DISABLE_TLS_1_3 INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest, TlsConnectTestBase::kTlsVariantsAll); -- cgit v1.2.3