summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc285
1 files changed, 285 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
new file mode 100644
index 000000000..5d670fa82
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "secerr.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+// This is internal, just to get TLS_1_3_DRAFT_VERSION.
+#include "ssl3prot.h"
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+TEST_P(TlsConnectTls13, HelloRetryRequestAbortsZeroRtt) {
+ const char* k0RttData = "Such is life";
+ const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+
+ SetupForZeroRtt(); // initial handshake as normal
+
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
+ ssl_grp_ec_secp521r1};
+ server_->ConfigNamedGroups(groups);
+ client_->Set0RttEnabled(true);
+ server_->Set0RttEnabled(true);
+ ExpectResumption(RESUME_TICKET);
+
+ // Send first ClientHello and send 0-RTT data
+ auto capture_early_data = new TlsExtensionCapture(ssl_tls13_early_data_xtn);
+ client_->SetPacketFilter(capture_early_data);
+ client_->Handshake();
+ EXPECT_EQ(k0RttDataLen, PR_Write(client_->ssl_fd(), k0RttData,
+ k0RttDataLen)); // 0-RTT write.
+ EXPECT_TRUE(capture_early_data->captured());
+
+ // Send the HelloRetryRequest
+ auto hrr_capture =
+ new TlsInspectorRecordHandshakeMessage(kTlsHandshakeHelloRetryRequest);
+ server_->SetPacketFilter(hrr_capture);
+ server_->Handshake();
+ EXPECT_LT(0U, hrr_capture->buffer().len());
+
+ // The server can't read
+ std::vector<uint8_t> buf(k0RttDataLen);
+ EXPECT_EQ(SECFailure, PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen));
+ EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+ // Make a new capture for the early data.
+ capture_early_data = new TlsExtensionCapture(ssl_tls13_early_data_xtn);
+ client_->SetPacketFilter(capture_early_data);
+
+ // Complete the handshake successfully
+ Handshake();
+ ExpectEarlyDataAccepted(false); // The server should reject 0-RTT
+ CheckConnected();
+ SendReceive();
+ EXPECT_FALSE(capture_early_data->captured());
+}
+
+class KeyShareReplayer : public TlsExtensionFilter {
+ public:
+ KeyShareReplayer() {}
+
+ virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
+ const DataBuffer& input,
+ DataBuffer* output) {
+ if (extension_type != ssl_tls13_key_share_xtn) {
+ return KEEP;
+ }
+
+ if (!data_.len()) {
+ data_ = input;
+ return KEEP;
+ }
+
+ *output = data_;
+ return CHANGE;
+ }
+
+ private:
+ DataBuffer data_;
+};
+
+// This forces a HelloRetryRequest by disabling P-256 on the server. However,
+// the second ClientHello is modified so that it omits the requested share. The
+// server should reject this.
+TEST_P(TlsConnectTls13, RetryWithSameKeyShare) {
+ EnsureTlsSetup();
+ client_->SetPacketFilter(new KeyShareReplayer());
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
+ ssl_grp_ec_secp521r1};
+ server_->ConfigNamedGroups(groups);
+ ConnectExpectFail();
+ EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
+ EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
+}
+
+// This tests that the second attempt at sending a ClientHello (after receiving
+// a HelloRetryRequest) is correctly retransmitted.
+TEST_F(TlsConnectDatagram13, DropClientSecondFlightWithHelloRetry) {
+ static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
+ ssl_grp_ec_secp521r1};
+ server_->ConfigNamedGroups(groups);
+ server_->SetPacketFilter(new SelectiveDropFilter(0x2));
+ Connect();
+}
+
+class TlsKeyExchange13 : public TlsKeyExchangeTest {};
+
+// This should work, with an HRR, because the server prefers x25519 and the
+// client generates a share for P-384 on the initial ClientHello.
+TEST_P(TlsKeyExchange13, ConnectEcdhePreferenceMismatchHrr) {
+ EnsureKeyShareSetup();
+ static const std::vector<SSLNamedGroup> client_groups = {
+ ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
+ static const std::vector<SSLNamedGroup> server_groups = {
+ ssl_grp_ec_curve25519, ssl_grp_ec_secp384r1};
+ client_->ConfigNamedGroups(client_groups);
+ server_->ConfigNamedGroups(server_groups);
+ Connect();
+ CheckKeys();
+ static const std::vector<SSLNamedGroup> expectedShares = {
+ ssl_grp_ec_secp384r1};
+ CheckKEXDetails(client_groups, expectedShares, ssl_grp_ec_curve25519);
+}
+
+// This should work, but not use HRR because the key share for x25519 was
+// pre-generated by the client.
+TEST_P(TlsKeyExchange13, ConnectEcdhePreferenceMismatchHrrExtraShares) {
+ EnsureKeyShareSetup();
+ static const std::vector<SSLNamedGroup> client_groups = {
+ ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
+ static const std::vector<SSLNamedGroup> server_groups = {
+ ssl_grp_ec_curve25519, ssl_grp_ec_secp384r1};
+ client_->ConfigNamedGroups(client_groups);
+ server_->ConfigNamedGroups(server_groups);
+ EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+
+ Connect();
+ CheckKeys();
+ CheckKEXDetails(client_groups, client_groups);
+}
+
+TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) {
+ EnsureTlsSetup();
+ 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_3);
+ static const std::vector<SSLNamedGroup> client_groups = {
+ ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
+ client_->ConfigNamedGroups(client_groups);
+ static const std::vector<SSLNamedGroup> server_groups = {
+ ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
+ server_->ConfigNamedGroups(server_groups);
+ client_->StartConnect();
+ server_->StartConnect();
+
+ client_->Handshake();
+ server_->Handshake();
+
+ // Here we replace the TLS server with one that does TLS 1.2 only.
+ // This will happily send the client a TLS 1.2 ServerHello.
+ TlsAgent* replacement_server =
+ new TlsAgent(server_->name(), TlsAgent::SERVER, mode_);
+ delete server_;
+ server_ = replacement_server;
+ server_->Init();
+ client_->SetPeer(server_);
+ server_->SetPeer(client_);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ server_->StartConnect();
+ Handshake();
+ EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, server_->error_code());
+ EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
+}
+
+class HelloRetryRequestAgentTest : public TlsAgentTestClient {
+ protected:
+ void SetUp() override {
+ TlsAgentTestClient::SetUp();
+ EnsureInit();
+ agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ agent_->StartConnect();
+ }
+
+ void MakeCannedHrr(const uint8_t* body, size_t len, DataBuffer* hrr_record,
+ uint32_t seq_num = 0) const {
+ DataBuffer hrr_data;
+ hrr_data.Allocate(len + 4);
+ size_t i = 0;
+ i = hrr_data.Write(i, 0x7f00 | TLS_1_3_DRAFT_VERSION, 2);
+ i = hrr_data.Write(i, static_cast<uint32_t>(len), 2);
+ if (len) {
+ hrr_data.Write(i, body, len);
+ }
+ DataBuffer hrr;
+ MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, hrr_data.data(),
+ hrr_data.len(), &hrr, seq_num);
+ MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(),
+ hrr.len(), hrr_record, seq_num);
+ }
+
+ void MakeGroupHrr(SSLNamedGroup group, DataBuffer* hrr_record,
+ uint32_t seq_num = 0) const {
+ const uint8_t group_hrr[] = {
+ static_cast<uint8_t>(ssl_tls13_key_share_xtn >> 8),
+ static_cast<uint8_t>(ssl_tls13_key_share_xtn),
+ 0,
+ 2, // length of key share extension
+ static_cast<uint8_t>(group >> 8),
+ static_cast<uint8_t>(group)};
+ MakeCannedHrr(group_hrr, sizeof(group_hrr), hrr_record, seq_num);
+ }
+};
+
+// Send two HelloRetryRequest messages in response to the ClientHello. The are
+// constructed to appear legitimate by asking for a new share in each, so that
+// the client has to count to work out that the server is being unreasonable.
+TEST_P(HelloRetryRequestAgentTest, SendSecondHelloRetryRequest) {
+ DataBuffer hrr;
+ MakeGroupHrr(ssl_grp_ec_secp384r1, &hrr, 0);
+ ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
+ MakeGroupHrr(ssl_grp_ec_secp521r1, &hrr, 1);
+ ProcessMessage(hrr, TlsAgent::STATE_ERROR,
+ SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST);
+}
+
+// Here the client receives a HelloRetryRequest with a group that they already
+// provided a share for.
+TEST_P(HelloRetryRequestAgentTest, HandleBogusHelloRetryRequest) {
+ DataBuffer hrr;
+ MakeGroupHrr(ssl_grp_ec_curve25519, &hrr);
+ ProcessMessage(hrr, TlsAgent::STATE_ERROR,
+ SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+}
+
+TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) {
+ DataBuffer hrr;
+ MakeCannedHrr(nullptr, 0U, &hrr);
+ ProcessMessage(hrr, TlsAgent::STATE_ERROR,
+ SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+}
+
+TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) {
+ const uint8_t canned_cookie_hrr[] = {
+ static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8),
+ static_cast<uint8_t>(ssl_tls13_cookie_xtn),
+ 0,
+ 5, // length of cookie extension
+ 0,
+ 3, // cookie value length
+ 0xc0,
+ 0x0c,
+ 0x13};
+ DataBuffer hrr;
+ MakeCannedHrr(canned_cookie_hrr, sizeof(canned_cookie_hrr), &hrr);
+ TlsExtensionCapture* capture = new TlsExtensionCapture(ssl_tls13_cookie_xtn);
+ agent_->SetPacketFilter(capture);
+ ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
+ const size_t cookie_pos = 2 + 2; // cookie_xtn, extension len
+ DataBuffer cookie(canned_cookie_hrr + cookie_pos,
+ sizeof(canned_cookie_hrr) - cookie_pos);
+ EXPECT_EQ(cookie, capture->extension());
+}
+
+INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
+ TlsConnectTestBase::kTlsModesAll);
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13,
+ ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
+ TlsConnectTestBase::kTlsV13));
+#endif
+
+} // namespace nss_test