diff options
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc')
-rw-r--r-- | security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc | 285 |
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 |