summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc343
1 files changed, 299 insertions, 44 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
index 85b7011a1..08781af71 100644
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -7,6 +7,7 @@
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
+#include "sslexp.h"
#include "sslproto.h"
extern "C" {
@@ -44,6 +45,92 @@ TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
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.
+
+ 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) {
+ // Run the initial handshake
+ SetupForZeroRtt();
+
+ // 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);
+ ExpectResumption(RESUME_TICKET);
+ ZeroRttSendReceive(true, true);
+ Handshake();
+ EXPECT_LT(0U, first_packet->packet().len());
+ ExpectEarlyDataAccepted(true);
+ CheckConnected();
+ SendReceive();
+
+ if (rollover) {
+ SSLInt_RolloverAntiReplay();
+ }
+
+ // Now replay that packet against the server.
+ Reset();
+ server_->StartConnect();
+ server_->Set0RttEnabled(true);
+
+ // 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());
+ }
+};
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); }
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(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) {
@@ -52,8 +139,7 @@ TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
Reset();
- server_->StartConnect();
- client_->StartConnect();
+ StartConnect();
// Now turn on 0-RTT but too late for the ticket.
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
@@ -80,8 +166,7 @@ TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
ExpectResumption(RESUME_NONE);
server_->Set0RttEnabled(true);
- client_->StartConnect();
- server_->StartConnect();
+ StartConnect();
// Client sends ordinary ClientHello.
client_->Handshake();
@@ -99,6 +184,61 @@ 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.
+TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
+ SetupForZeroRtt();
+ 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));
+ 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) {
+ 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));
+ Handshake(); // Remainder of handshake
+ CheckConnected();
+ SendReceive();
+ CheckKeys();
+
+ 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();
+ ZeroRttSendReceive(true, false);
+ Handshake();
+ CheckConnected();
+ SendReceive();
+}
+
TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
EnableAlpn();
SetupForZeroRtt();
@@ -117,6 +257,14 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
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) {
@@ -155,12 +303,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) {
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");
- ExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
return true;
});
- Handshake();
+ 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);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Set up with no ALPN and then set the client so it thinks it has ALPN.
@@ -175,12 +328,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
PRUint8 b[] = {'b'};
EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
- ExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->ExpectSendAlert(kTlsAlertIllegalParameter);
return true;
});
- Handshake();
+ 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);
- server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Remove the old ALPN value and so the client will not offer early data.
@@ -218,9 +376,7 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
- client_->StartConnect();
- server_->StartConnect();
-
+ 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.
@@ -248,6 +404,9 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
// 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();
@@ -261,33 +420,32 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
- client_->StartConnect();
- server_->StartConnect();
-
+ 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);
- ZeroRttSendReceive(true, false, [this]() {
- client_->ExpectSendAlert(kTlsAlertIllegalParameter);
- if (variant_ == ssl_variant_stream) {
- server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
- }
- return true;
- });
-
- client_->Handshake();
- server_->Handshake();
- ASSERT_TRUE_WAIT(
- (client_->error_code() == SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA), 2000);
+ client_->Handshake(); // Send ClientHello.
+ PRInt32 rv =
+ PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write.
+ EXPECT_EQ(k0RttDataLen, rv);
- // 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 server sends an alert when receiving the early app data record.
- ASSERT_TRUE_WAIT(
- (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
- 2000);
+ // 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);
}
static void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent,
@@ -300,17 +458,19 @@ static void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent,
}
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);
- SSLInt_SetMaxEarlyDataSize(static_cast<PRUint32>(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);
- ExpectAlert(client_, kTlsAlertEndOfEarlyData);
client_->Handshake();
CheckEarlyDataLimit(client_, short_size);
@@ -356,18 +516,21 @@ TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
}
TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
+ EnsureTlsSetup();
+
const size_t limit = 5;
- SSLInt_SetMaxEarlyDataSize(limit);
+ EXPECT_EQ(SECSuccess, SSL_SetMaxEarlyDataSize(server_->ssl_fd(), limit));
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
- client_->ExpectSendAlert(kTlsAlertEndOfEarlyData);
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));
@@ -381,22 +544,114 @@ TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
// This error isn't fatal for DTLS.
ExpectAlert(server_, kTlsAlertUnexpectedMessage);
}
- server_->Handshake(); // Process ClientHello, send server flight.
- server_->Handshake(); // Just to make sure that we don't read ahead.
+
+ 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.
+ // 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) {
- server_->CheckErrorCode(SSL_ERROR_TOO_MUCH_EARLY_DATA);
+ EXPECT_EQ(SSL_ERROR_HANDSHAKE_FAILED, PORT_GetError());
+ } else {
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
- client_->Handshake(); // Process the handshake.
- client_->Handshake(); // Process the alert.
+ 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);
+}
+
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
+ TlsConnectTestBase::kTlsVariantsAll);
+#endif
+
} // namespace nss_test