mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 18:07:31 +09:00
285 lines
10 KiB
C++
285 lines
10 KiB
C++
|
|
/* -*- 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
|