Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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,
#include "tls_connect.h"
#include "sslexp.h"
extern "C" {
#include "libssl_internals.h"
}
#include <iostream>
#include "databuffer.h"
#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "sslproto.h"
extern std::string g_working_dir_path;
namespace nss_test {
static const SSLProtocolVariant kTlsVariantsStreamArr[] = {ssl_variant_stream};
::testing::internal::ParamGenerator<SSLProtocolVariant>
TlsConnectTestBase::kTlsVariantsStream =
::testing::ValuesIn(kTlsVariantsStreamArr);
static const SSLProtocolVariant kTlsVariantsDatagramArr[] = {
ssl_variant_datagram};
::testing::internal::ParamGenerator<SSLProtocolVariant>
TlsConnectTestBase::kTlsVariantsDatagram =
::testing::ValuesIn(kTlsVariantsDatagramArr);
static const SSLProtocolVariant kTlsVariantsAllArr[] = {ssl_variant_stream,
ssl_variant_datagram};
::testing::internal::ParamGenerator<SSLProtocolVariant>
TlsConnectTestBase::kTlsVariantsAll =
::testing::ValuesIn(kTlsVariantsAllArr);
static const uint16_t kTlsV10Arr[] = {SSL_LIBRARY_VERSION_TLS_1_0};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV10 =
::testing::ValuesIn(kTlsV10Arr);
static const uint16_t kTlsV11Arr[] = {SSL_LIBRARY_VERSION_TLS_1_1};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV11 =
::testing::ValuesIn(kTlsV11Arr);
static const uint16_t kTlsV12Arr[] = {SSL_LIBRARY_VERSION_TLS_1_2};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV12 =
::testing::ValuesIn(kTlsV12Arr);
static const uint16_t kTlsV10V11Arr[] = {SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_1};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV10V11 =
::testing::ValuesIn(kTlsV10V11Arr);
static const uint16_t kTlsV10ToV12Arr[] = {SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_2};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV10ToV12 =
::testing::ValuesIn(kTlsV10ToV12Arr);
static const uint16_t kTlsV11V12Arr[] = {SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_2};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV11V12 =
::testing::ValuesIn(kTlsV11V12Arr);
static const uint16_t kTlsV11PlusArr[] = {
#ifndef NSS_DISABLE_TLS_1_3
SSL_LIBRARY_VERSION_TLS_1_3,
#endif
SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_1};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV11Plus =
::testing::ValuesIn(kTlsV11PlusArr);
static const uint16_t kTlsV12PlusArr[] = {
#ifndef NSS_DISABLE_TLS_1_3
SSL_LIBRARY_VERSION_TLS_1_3,
#endif
SSL_LIBRARY_VERSION_TLS_1_2};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV12Plus =
::testing::ValuesIn(kTlsV12PlusArr);
static const uint16_t kTlsV13Arr[] = {SSL_LIBRARY_VERSION_TLS_1_3};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsV13 =
::testing::ValuesIn(kTlsV13Arr);
static const uint16_t kTlsVAllArr[] = {
#ifndef NSS_DISABLE_TLS_1_3
SSL_LIBRARY_VERSION_TLS_1_3,
#endif
SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_0};
::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsVAll =
::testing::ValuesIn(kTlsVAllArr);
std::string VersionString(uint16_t version) {
switch (version) {
case 0:
return "(no version)";
case SSL_LIBRARY_VERSION_3_0:
return "1.0";
case SSL_LIBRARY_VERSION_TLS_1_0:
return "1.0";
case SSL_LIBRARY_VERSION_TLS_1_1:
return "1.1";
case SSL_LIBRARY_VERSION_TLS_1_2:
return "1.2";
case SSL_LIBRARY_VERSION_TLS_1_3:
return "1.3";
default:
std::cerr << "Invalid version: " << version << std::endl;
EXPECT_TRUE(false);
return "";
}
}
// The default anti-replay window for tests. Tests that rely on a different
// value call ResetAntiReplay directly.
static PRTime kAntiReplayWindow = 100 * PR_USEC_PER_SEC;
TlsConnectTestBase::TlsConnectTestBase(SSLProtocolVariant variant,
uint16_t version)
: variant_(variant),
client_(new TlsAgent(TlsAgent::kClient, TlsAgent::CLIENT, variant_)),
server_(new TlsAgent(TlsAgent::kServerRsa, TlsAgent::SERVER, variant_)),
client_model_(nullptr),
server_model_(nullptr),
version_(version),
expected_resumption_mode_(RESUME_NONE),
expected_resumptions_(0),
session_ids_(),
expect_extended_master_secret_(false),
expect_early_data_accepted_(false),
skip_version_checks_(false) {
std::string v;
if (variant_ == ssl_variant_datagram &&
version_ == SSL_LIBRARY_VERSION_TLS_1_1) {
v = "1.0";
} else {
v = VersionString(version_);
}
std::cerr << "Version: " << variant_ << " " << v << std::endl;
}
TlsConnectTestBase::~TlsConnectTestBase() {}
// Check the group of each of the supported groups
void TlsConnectTestBase::CheckGroups(
const DataBuffer& groups, std::function<void(SSLNamedGroup)> check_group) {
DuplicateGroupChecker group_set;
uint32_t tmp = 0;
EXPECT_TRUE(groups.Read(0, 2, &tmp));
EXPECT_EQ(groups.len() - 2, static_cast<size_t>(tmp));
for (size_t i = 2; i < groups.len(); i += 2) {
EXPECT_TRUE(groups.Read(i, 2, &tmp));
SSLNamedGroup group = static_cast<SSLNamedGroup>(tmp);
group_set.AddAndCheckGroup(group);
check_group(group);
}
}
// Check the group of each of the shares
void TlsConnectTestBase::CheckShares(
const DataBuffer& shares, std::function<void(SSLNamedGroup)> check_group) {
DuplicateGroupChecker group_set;
uint32_t tmp = 0;
EXPECT_TRUE(shares.Read(0, 2, &tmp));
EXPECT_EQ(shares.len() - 2, static_cast<size_t>(tmp));
size_t i;
for (i = 2; i < shares.len(); i += 4 + tmp) {
ASSERT_TRUE(shares.Read(i, 2, &tmp));
SSLNamedGroup group = static_cast<SSLNamedGroup>(tmp);
group_set.AddAndCheckGroup(group);
check_group(group);
ASSERT_TRUE(shares.Read(i + 2, 2, &tmp));
}
EXPECT_EQ(shares.len(), i);
}
void TlsConnectTestBase::CheckEpochs(uint16_t client_epoch,
uint16_t server_epoch) const {
client_->CheckEpochs(server_epoch, client_epoch);
server_->CheckEpochs(client_epoch, server_epoch);
}
void TlsConnectTestBase::ClearStats() {
// Clear statistics.
SSL3Statistics* stats = SSL_GetStatistics();
memset(stats, 0, sizeof(*stats));
}
void TlsConnectTestBase::ClearServerCache() {
SSL_ShutdownServerSessionIDCache();
SSLInt_ClearSelfEncryptKey();
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
}
void TlsConnectTestBase::SaveAlgorithmPolicy() {
saved_policies_.clear();
for (auto it = algorithms_.begin(); it != algorithms_.end(); ++it) {
uint32_t policy;
SECStatus rv = NSS_GetAlgorithmPolicy(*it, &policy);
ASSERT_EQ(SECSuccess, rv);
saved_policies_.push_back(std::make_tuple(*it, policy));
}
saved_options_.clear();
for (auto it : options_) {
int32_t option;
SECStatus rv = NSS_OptionGet(it, &option);
ASSERT_EQ(SECSuccess, rv);
saved_options_.push_back(std::make_tuple(it, option));
}
}
void TlsConnectTestBase::RestoreAlgorithmPolicy() {
for (auto it = saved_policies_.begin(); it != saved_policies_.end(); ++it) {
auto algorithm = std::get<0>(*it);
auto policy = std::get<1>(*it);
SECStatus rv = NSS_SetAlgorithmPolicy(
algorithm, policy, NSS_USE_POLICY_IN_SSL | NSS_USE_ALG_IN_SSL_KX);
ASSERT_EQ(SECSuccess, rv);
}
for (auto it = saved_options_.begin(); it != saved_options_.end(); ++it) {
auto option_id = std::get<0>(*it);
auto option = std::get<1>(*it);
SECStatus rv = NSS_OptionSet(option_id, option);
ASSERT_EQ(SECSuccess, rv);
}
}
PRTime TlsConnectTestBase::TimeFunc(void* arg) {
return *reinterpret_cast<PRTime*>(arg);
}
void TlsConnectTestBase::SetUp() {
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
SSLInt_ClearSelfEncryptKey();
now_ = PR_Now();
ResetAntiReplay(kAntiReplayWindow);
ClearStats();
SaveAlgorithmPolicy();
Init();
}
void TlsConnectTestBase::TearDown() {
client_ = nullptr;
server_ = nullptr;
SSL_ClearSessionCache();
SSLInt_ClearSelfEncryptKey();
SSL_ShutdownServerSessionIDCache();
RestoreAlgorithmPolicy();
}
void TlsConnectTestBase::Init() {
client_->SetPeer(server_);
server_->SetPeer(client_);
if (version_) {
ConfigureVersion(version_);
}
}
void TlsConnectTestBase::ResetAntiReplay(PRTime window) {
SSLAntiReplayContext* p_anti_replay = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateAntiReplayContext(now_, window, 1, 3, &p_anti_replay));
EXPECT_NE(nullptr, p_anti_replay);
anti_replay_.reset(p_anti_replay);
}
ScopedSECItem TlsConnectTestBase::MakeEcKeyParams(SSLNamedGroup group) {
auto groupDef = ssl_LookupNamedGroup(group);
EXPECT_NE(nullptr, groupDef);
auto oidData = SECOID_FindOIDByTag(groupDef->oidTag);
EXPECT_NE(nullptr, oidData);
ScopedSECItem params(
SECITEM_AllocItem(nullptr, nullptr, (2 + oidData->oid.len)));
EXPECT_TRUE(!!params);
params->data[0] = SEC_ASN1_OBJECT_ID;
params->data[1] = oidData->oid.len;
memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
return params;
}
void TlsConnectTestBase::GenerateEchConfig(
HpkeKemId kem_id, const std::vector<HpkeSymmetricSuite>& cipher_suites,
const std::string& public_name, uint16_t max_name_len, DataBuffer& record,
ScopedSECKEYPublicKey& pubKey, ScopedSECKEYPrivateKey& privKey) {
bool gen_keys = !pubKey && !privKey;
SECKEYPublicKey* pub = nullptr;
SECKEYPrivateKey* priv = nullptr;
if (gen_keys) {
ScopedSECItem ecParams = MakeEcKeyParams(ssl_grp_ec_curve25519);
priv = SECKEY_CreateECPrivateKey(ecParams.get(), &pub, nullptr);
} else {
priv = privKey.get();
pub = pubKey.get();
}
ASSERT_NE(nullptr, priv);
PRUint8 encoded[1024];
unsigned int encoded_len = 0;
SECStatus rv = SSL_EncodeEchConfigId(
77, public_name.c_str(), max_name_len, kem_id, pub, cipher_suites.data(),
cipher_suites.size(), encoded, &encoded_len, sizeof(encoded));
EXPECT_EQ(SECSuccess, rv);
EXPECT_GT(encoded_len, 0U);
if (gen_keys) {
pubKey.reset(pub);
privKey.reset(priv);
}
record.Truncate(0);
record.Write(0, encoded, encoded_len);
}
void TlsConnectTestBase::SetupEch(std::shared_ptr<TlsAgent>& client,
std::shared_ptr<TlsAgent>& server,
HpkeKemId kem_id, bool expect_ech,
bool set_client_config,
bool set_server_config, int max_name_len) {
EXPECT_TRUE(set_server_config || set_client_config);
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
static const std::vector<HpkeSymmetricSuite> kDefaultSuites = {
{HpkeKdfHkdfSha256, HpkeAeadChaCha20Poly1305},
{HpkeKdfHkdfSha256, HpkeAeadAes128Gcm}};
GenerateEchConfig(kem_id, kDefaultSuites, "public.name", max_name_len, record,
pub, priv);
ASSERT_NE(0U, record.len());
SECStatus rv;
if (set_server_config) {
rv = SSL_SetServerEchConfigs(server->ssl_fd(), pub.get(), priv.get(),
record.data(), record.len());
ASSERT_EQ(SECSuccess, rv);
}
if (set_client_config) {
rv = SSL_SetClientEchConfigs(client->ssl_fd(), record.data(), record.len());
ASSERT_EQ(SECSuccess, rv);
}
/* Filter expect_ech, which typically defaults to true. Parameterized tests
* running DTLS or TLS < 1.3 should expect only a non-ECH result. */
bool expect = expect_ech && variant_ != ssl_variant_datagram &&
version_ >= SSL_LIBRARY_VERSION_TLS_1_3 && set_client_config &&
set_server_config;
client->ExpectEch(expect);
server->ExpectEch(expect);
}
void TlsConnectTestBase::Reset() {
// Take a copy of the names because they are about to disappear.
std::string server_name = server_->name();
std::string client_name = client_->name();
Reset(server_name, client_name);
}
void TlsConnectTestBase::Reset(const std::string& server_name,
const std::string& client_name) {
auto token = client_->GetResumptionToken();
client_.reset(new TlsAgent(client_name, TlsAgent::CLIENT, variant_));
client_->SetResumptionToken(token);
server_.reset(new TlsAgent(server_name, TlsAgent::SERVER, variant_));
if (skip_version_checks_) {
client_->SkipVersionChecks();
server_->SkipVersionChecks();
}
std::cerr << "Reset server:" << server_name << ", client:" << client_name
<< std::endl;
Init();
}
void TlsConnectTestBase::MakeNewServer() {
auto replacement = std::make_shared<TlsAgent>(
server_->name(), TlsAgent::SERVER, server_->variant());
server_ = replacement;
if (version_) {
server_->SetVersionRange(version_, version_);
}
client_->SetPeer(server_);
server_->SetPeer(client_);
server_->StartConnect();
}
void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected,
uint8_t num_resumptions) {
expected_resumption_mode_ = expected;
if (expected != RESUME_NONE) {
client_->ExpectResumption();
server_->ExpectResumption();
expected_resumptions_ = num_resumptions;
}
EXPECT_EQ(expected_resumptions_ == 0, expected == RESUME_NONE);
}
void TlsConnectTestBase::EnsureTlsSetup() {
EXPECT_TRUE(server_->EnsureTlsSetup(
server_model_ ? server_model_->ssl_fd().get() : nullptr));
EXPECT_TRUE(client_->EnsureTlsSetup(
client_model_ ? client_model_->ssl_fd().get() : nullptr));
server_->SetAntiReplayContext(anti_replay_);
EXPECT_EQ(SECSuccess, SSL_SetTimeFunc(client_->ssl_fd(),
TlsConnectTestBase::TimeFunc, &now_));
EXPECT_EQ(SECSuccess, SSL_SetTimeFunc(server_->ssl_fd(),
TlsConnectTestBase::TimeFunc, &now_));
}
void TlsConnectTestBase::Handshake() {
client_->SetServerKeyBits(server_->server_key_bits());
client_->Handshake();
server_->Handshake();
ASSERT_TRUE_WAIT((client_->state() != TlsAgent::STATE_CONNECTING) &&
(server_->state() != TlsAgent::STATE_CONNECTING),
5000);
}
void TlsConnectTestBase::EnableExtendedMasterSecret() {
client_->EnableExtendedMasterSecret();
server_->EnableExtendedMasterSecret();
ExpectExtendedMasterSecret(true);
}
void TlsConnectTestBase::Connect() {
StartConnect();
client_->MaybeSetResumptionToken();
Handshake();
CheckConnected();
}
void TlsConnectTestBase::StartConnect() {
EnsureTlsSetup();
server_->StartConnect();
client_->StartConnect();
}
void TlsConnectTestBase::ConnectWithCipherSuite(uint16_t cipher_suite) {
EnsureTlsSetup();
client_->EnableSingleCipher(cipher_suite);
Connect();
SendReceive();
// Check that we used the right cipher suite.
uint16_t actual;
EXPECT_TRUE(client_->cipher_suite(&actual));
EXPECT_EQ(cipher_suite, actual);
EXPECT_TRUE(server_->cipher_suite(&actual));
EXPECT_EQ(cipher_suite, actual);
}
void TlsConnectTestBase::CheckConnected() {
// Have the client read handshake twice to make sure we get the
// NST and the ACK.
if (client_->version() >= SSL_LIBRARY_VERSION_TLS_1_3 &&
variant_ == ssl_variant_datagram) {
client_->Handshake();
client_->Handshake();
auto suites = SSLInt_CountCipherSpecs(client_->ssl_fd());
// Verify that we dropped the client's retransmission cipher suites.
EXPECT_EQ(2, suites) << "Client has the wrong number of suites";
if (suites != 2) {
SSLInt_PrintCipherSpecs("client", client_->ssl_fd());
}
}
EXPECT_EQ(client_->version(), server_->version());
if (!skip_version_checks_) {
// Check the version is as expected
EXPECT_EQ(std::min(client_->max_version(), server_->max_version()),
client_->version());
}
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
uint16_t cipher_suite1, cipher_suite2;
ASSERT_TRUE(client_->cipher_suite(&cipher_suite1));
ASSERT_TRUE(server_->cipher_suite(&cipher_suite2));
EXPECT_EQ(cipher_suite1, cipher_suite2);
std::cerr << "Connected with version " << client_->version()
<< " cipher suite " << client_->cipher_suite_name() << std::endl;
if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
// Check and store session ids.
std::vector<uint8_t> sid_c1 = client_->session_id();
EXPECT_EQ(32U, sid_c1.size());
std::vector<uint8_t> sid_s1 = server_->session_id();
EXPECT_EQ(32U, sid_s1.size());
EXPECT_EQ(sid_c1, sid_s1);
session_ids_.push_back(sid_c1);
}
CheckExtendedMasterSecret();
CheckEarlyDataAccepted();
CheckResumption(expected_resumption_mode_);
client_->CheckSecretsDestroyed();
server_->CheckSecretsDestroyed();
}
void TlsConnectTestBase::CheckEarlyDataLimit(
const std::shared_ptr<TlsAgent>& agent, size_t expected_size) {
SSLPreliminaryChannelInfo preinfo;
SECStatus rv =
SSL_GetPreliminaryChannelInfo(agent->ssl_fd(), &preinfo, sizeof(preinfo));
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(expected_size, static_cast<size_t>(preinfo.maxEarlyDataSize));
}
void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
SSLAuthType auth_type,
SSLSignatureScheme sig_scheme) const {
if (kea_group != ssl_grp_none) {
client_->CheckKEA(kea_type, kea_group);
server_->CheckKEA(kea_type, kea_group);
}
server_->CheckAuthType(auth_type, sig_scheme);
client_->CheckAuthType(auth_type, sig_scheme);
}
void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type,
SSLAuthType auth_type) const {
SSLNamedGroup group;
switch (kea_type) {
case ssl_kea_ecdh:
group = ssl_grp_ec_curve25519;
break;
case ssl_kea_dh:
group = ssl_grp_ffdhe_2048;
break;
case ssl_kea_rsa:
group = ssl_grp_none;
break;
default:
EXPECT_TRUE(false) << "unexpected KEA";
group = ssl_grp_none;
break;
}
SSLSignatureScheme scheme;
switch (auth_type) {
case ssl_auth_rsa_decrypt:
scheme = ssl_sig_none;
break;
case ssl_auth_rsa_sign:
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_2) {
scheme = ssl_sig_rsa_pss_rsae_sha256;
} else {
scheme = ssl_sig_rsa_pkcs1_sha256;
}
break;
case ssl_auth_rsa_pss:
scheme = ssl_sig_rsa_pss_rsae_sha256;
break;
case ssl_auth_ecdsa:
scheme = ssl_sig_ecdsa_secp256r1_sha256;
break;
case ssl_auth_dsa:
scheme = ssl_sig_dsa_sha1;
break;
default:
EXPECT_TRUE(false) << "unexpected auth type";
scheme = static_cast<SSLSignatureScheme>(0x0100);
break;
}
CheckKeys(kea_type, group, auth_type, scheme);
}
void TlsConnectTestBase::CheckKeys() const {
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
}
void TlsConnectTestBase::CheckKeysResumption(SSLKEAType kea_type,
SSLNamedGroup kea_group,
SSLNamedGroup original_kea_group,
SSLAuthType auth_type,
SSLSignatureScheme sig_scheme) {
CheckKeys(kea_type, kea_group, auth_type, sig_scheme);
EXPECT_TRUE(expected_resumption_mode_ != RESUME_NONE);
client_->CheckOriginalKEA(original_kea_group);
server_->CheckOriginalKEA(original_kea_group);
}
void TlsConnectTestBase::ConnectExpectFail() {
StartConnect();
Handshake();
ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
}
void TlsConnectTestBase::ExpectAlert(std::shared_ptr<TlsAgent>& sender,
uint8_t alert) {
EnsureTlsSetup();
auto receiver = (sender == client_) ? server_ : client_;
sender->ExpectSendAlert(alert);
receiver->ExpectReceiveAlert(alert);
}
void TlsConnectTestBase::ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender,
uint8_t alert) {
ExpectAlert(sender, alert);
ConnectExpectFail();
}
void TlsConnectTestBase::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
client_->Handshake();
server_->Handshake();
auto failing_agent = server_;
if (failing_side == TlsAgent::CLIENT) {
failing_agent = client_;
}
ASSERT_TRUE_WAIT(failing_agent->state() == TlsAgent::STATE_ERROR, 5000);
}
void TlsConnectTestBase::ConfigureVersion(uint16_t version) {
version_ = version;
client_->SetVersionRange(version, version);
server_->SetVersionRange(version, version);
}
void TlsConnectTestBase::SetExpectedVersion(uint16_t version) {
client_->SetExpectedVersion(version);
server_->SetExpectedVersion(version);
}
void TlsConnectTestBase::AddPsk(const ScopedPK11SymKey& psk, std::string label,
SSLHashType hash, uint16_t zeroRttSuite) {
client_->AddPsk(psk, label, hash, zeroRttSuite);
server_->AddPsk(psk, label, hash, zeroRttSuite);
client_->ExpectPsk();
server_->ExpectPsk();
}
void TlsConnectTestBase::DisableAllCiphers() {
EnsureTlsSetup();
client_->DisableAllCiphers();
server_->DisableAllCiphers();
}
void TlsConnectTestBase::EnableOnlyStaticRsaCiphers() {
DisableAllCiphers();
client_->EnableCiphersByKeyExchange(ssl_kea_rsa);
server_->EnableCiphersByKeyExchange(ssl_kea_rsa);
}
void TlsConnectTestBase::EnableOnlyDheCiphers() {
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
DisableAllCiphers();
client_->EnableCiphersByKeyExchange(ssl_kea_dh);
server_->EnableCiphersByKeyExchange(ssl_kea_dh);
} else {
client_->ConfigNamedGroups(kFFDHEGroups);
server_->ConfigNamedGroups(kFFDHEGroups);
}
}
void TlsConnectTestBase::EnableSomeEcdhCiphers() {
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
client_->EnableCiphersByAuthType(ssl_auth_ecdh_rsa);
client_->EnableCiphersByAuthType(ssl_auth_ecdh_ecdsa);
server_->EnableCiphersByAuthType(ssl_auth_ecdh_rsa);
server_->EnableCiphersByAuthType(ssl_auth_ecdh_ecdsa);
} else {
client_->ConfigNamedGroups(kECDHEGroups);
server_->ConfigNamedGroups(kECDHEGroups);
}
}
void TlsConnectTestBase::ConfigureSelfEncrypt() {
ScopedCERTCertificate cert;
ScopedSECKEYPrivateKey privKey;
ASSERT_TRUE(
TlsAgent::LoadCertificate(TlsAgent::kServerRsaDecrypt, &cert, &privKey));
ScopedSECKEYPublicKey pubKey(CERT_ExtractPublicKey(cert.get()));
ASSERT_TRUE(pubKey);
EXPECT_EQ(SECSuccess,
SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
}
void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client,
SessionResumptionMode server) {
client_->ConfigureSessionCache(client);
server_->ConfigureSessionCache(server);
if ((server & RESUME_TICKET) != 0) {
ConfigureSelfEncrypt();
}
}
void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
EXPECT_NE(RESUME_BOTH, expected);
int resume_count = expected ? expected_resumptions_ : 0;
int stateless_count = (expected & RESUME_TICKET) ? expected_resumptions_ : 0;
// Note: hch == server counter; hsh == client counter.
SSL3Statistics* stats = SSL_GetStatistics();
EXPECT_EQ(resume_count, stats->hch_sid_cache_hits);
EXPECT_EQ(resume_count, stats->hsh_sid_cache_hits);
EXPECT_EQ(stateless_count, stats->hch_sid_stateless_resumes);
EXPECT_EQ(stateless_count, stats->hsh_sid_stateless_resumes);
if (expected != RESUME_NONE) {
if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3 &&
client_->GetResumptionToken().size() == 0) {
// Check that the last two session ids match.
ASSERT_EQ(1U + expected_resumptions_, session_ids_.size());
EXPECT_EQ(session_ids_[session_ids_.size() - 1],
session_ids_[session_ids_.size() - 2]);
} else {
// We've either chosen TLS 1.3 or are using an external resumption token,
// both of which only use tickets.
EXPECT_TRUE(expected & RESUME_TICKET);
}
}
}
static SECStatus NextProtoCallbackServer(void* arg, PRFileDesc* fd,
const unsigned char* protos,
unsigned int protos_len,
unsigned char* protoOut,
unsigned int* protoOutLen,
unsigned int protoMaxLen) {
EXPECT_EQ(protoMaxLen, 255U);
TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
// Check that agent->alpn_value_to_use_ is in protos.
if (protos_len < 1) {
return SECFailure;
}
for (size_t i = 0; i < protos_len;) {
size_t l = protos[i];
EXPECT_LT(i + l, protos_len);
if (i + l >= protos_len) {
return SECFailure;
}
std::string protos_s(reinterpret_cast<const char*>(protos + i + 1), l);
if (protos_s == agent->alpn_value_to_use_) {
size_t s_len = agent->alpn_value_to_use_.size();
EXPECT_LE(s_len, 255U);
memcpy(protoOut, &agent->alpn_value_to_use_[0], s_len);
*protoOutLen = s_len;
return SECSuccess;
}
i += l + 1;
}
return SECFailure;
}
void TlsConnectTestBase::EnableAlpn() {
client_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
server_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
}
void TlsConnectTestBase::EnableAlpnWithCallback(
const std::vector<uint8_t>& client_vals, std::string server_choice) {
EnsureTlsSetup();
server_->alpn_value_to_use_ = server_choice;
EXPECT_EQ(SECSuccess,
SSL_SetNextProtoNego(client_->ssl_fd(), client_vals.data(),
client_vals.size()));
SECStatus rv = SSL_SetNextProtoCallback(
server_->ssl_fd(), NextProtoCallbackServer, server_.get());
EXPECT_EQ(SECSuccess, rv);
}
void TlsConnectTestBase::EnableAlpn(const std::vector<uint8_t>& vals) {
client_->EnableAlpn(vals.data(), vals.size());
server_->EnableAlpn(vals.data(), vals.size());
}
void TlsConnectTestBase::EnsureModelSockets() {
// Make sure models agents are available.
if (!client_model_) {
ASSERT_EQ(server_model_, nullptr);
client_model_.reset(
new TlsAgent(TlsAgent::kClient, TlsAgent::CLIENT, variant_));
server_model_.reset(
new TlsAgent(TlsAgent::kServerRsa, TlsAgent::SERVER, variant_));
if (skip_version_checks_) {
client_model_->SkipVersionChecks();
server_model_->SkipVersionChecks();
}
}
}
void TlsConnectTestBase::CheckAlpn(const std::string& val) {
client_->CheckAlpn(SSL_NEXT_PROTO_SELECTED, val);
server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, val);
}
void TlsConnectTestBase::EnableSrtp() {
client_->EnableSrtp();
server_->EnableSrtp();
}
void TlsConnectTestBase::CheckSrtp() const {
client_->CheckSrtp();
server_->CheckSrtp();
}
void TlsConnectTestBase::SendReceive(size_t total) {
ASSERT_GT(total, client_->received_bytes());
ASSERT_GT(total, server_->received_bytes());
client_->SendData(total - server_->received_bytes());
server_->SendData(total - client_->received_bytes());
Receive(total); // Receive() is cumulative
}
// Do a first connection so we can do 0-RTT on the second one.
void TlsConnectTestBase::SetupForZeroRtt() {
// Force rollover of the anti-replay window.
// If we don't do this, then all 0-RTT attempts will be rejected.
RolloverAntiReplay();
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();
}
// Do a first connection so we can do resumption
void TlsConnectTestBase::SetupForResume() {
EnsureTlsSetup();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys();
Reset();
}
void TlsConnectTestBase::ZeroRttSendReceive(
bool expect_writable, bool expect_readable,
std::function<bool()> post_clienthello_check) {
const char* k0RttData = "ABCDEF";
const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
client_->Handshake(); // Send ClientHello.
if (post_clienthello_check) {
if (!post_clienthello_check()) return;
}
PRInt32 rv =
PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write.
if (expect_writable) {
EXPECT_EQ(k0RttDataLen, rv);
} else {
EXPECT_EQ(SECFailure, rv);
}
server_->Handshake(); // Consume ClientHello
std::vector<uint8_t> buf(k0RttDataLen);
rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen); // 0-RTT read
if (expect_readable) {
std::cerr << "0-RTT read " << rv << " bytes\n";
EXPECT_EQ(k0RttDataLen, rv);
} else {
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError())
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
}
// Do a second read. This should fail.
rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
void TlsConnectTestBase::Receive(size_t amount) {
WAIT_(client_->received_bytes() == amount &&
server_->received_bytes() == amount,
2000);
ASSERT_EQ(amount, client_->received_bytes());
ASSERT_EQ(amount, server_->received_bytes());
}
void TlsConnectTestBase::ExpectExtendedMasterSecret(bool expected) {
expect_extended_master_secret_ = expected;
}
void TlsConnectTestBase::CheckExtendedMasterSecret() {
client_->CheckExtendedMasterSecret(expect_extended_master_secret_);
server_->CheckExtendedMasterSecret(expect_extended_master_secret_);
}
void TlsConnectTestBase::ExpectEarlyDataAccepted(bool expected) {
expect_early_data_accepted_ = expected;
}
void TlsConnectTestBase::CheckEarlyDataAccepted() {
client_->CheckEarlyDataAccepted(expect_early_data_accepted_);
server_->CheckEarlyDataAccepted(expect_early_data_accepted_);
}
void TlsConnectTestBase::EnableECDHEServerKeyReuse() {
server_->EnableECDHEServerKeyReuse();
}
void TlsConnectTestBase::SkipVersionChecks() {
skip_version_checks_ = true;
client_->SkipVersionChecks();
server_->SkipVersionChecks();
}
// Shift the DTLS timers, to the minimum time necessary to let the next timer
// run on either client or server. This allows tests to skip waiting without
// having timers run out of order.
void TlsConnectTestBase::ShiftDtlsTimers() {
PRIntervalTime time_shift = PR_INTERVAL_NO_TIMEOUT;
PRIntervalTime time;
SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time);
if (rv == SECSuccess) {
time_shift = time;
}
rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time);
if (rv == SECSuccess &&
(time < time_shift || time_shift == PR_INTERVAL_NO_TIMEOUT)) {
time_shift = time;
}
if (time_shift != PR_INTERVAL_NO_TIMEOUT) {
AdvanceTime(PR_IntervalToMicroseconds(time_shift));
EXPECT_EQ(SECSuccess,
SSLInt_ShiftDtlsTimers(client_->ssl_fd(), time_shift));
EXPECT_EQ(SECSuccess,
SSLInt_ShiftDtlsTimers(server_->ssl_fd(), time_shift));
}
}
void TlsConnectTestBase::AdvanceTime(PRTime time_shift) { now_ += time_shift; }
// Advance time by a full anti-replay window.
void TlsConnectTestBase::RolloverAntiReplay() {
AdvanceTime(kAntiReplayWindow);
}
TlsConnectGeneric::TlsConnectGeneric()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
TlsConnectPre12::TlsConnectPre12()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
TlsConnectTls12::TlsConnectTls12()
: TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_2) {}
TlsConnectTls12Plus::TlsConnectTls12Plus()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
TlsConnectTls13::TlsConnectTls13()
: TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
TlsConnectGenericResumption::TlsConnectGenericResumption()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())),
external_cache_(std::get<2>(GetParam())) {}
TlsConnectTls13ResumptionToken::TlsConnectTls13ResumptionToken()
: TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
TlsConnectGenericResumptionToken::TlsConnectGenericResumptionToken()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
void TlsKeyExchangeTest::EnsureKeyShareSetup() {
EnsureTlsSetup();
groups_capture_ =
std::make_shared<TlsExtensionCapture>(client_, ssl_supported_groups_xtn);
shares_capture_ =
std::make_shared<TlsExtensionCapture>(client_, ssl_tls13_key_share_xtn);
shares_capture2_ = std::make_shared<TlsExtensionCapture>(
client_, ssl_tls13_key_share_xtn, true);
std::vector<std::shared_ptr<PacketFilter>> captures = {
groups_capture_, shares_capture_, shares_capture2_};
client_->SetFilter(std::make_shared<ChainedPacketFilter>(captures));
capture_hrr_ = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeHelloRetryRequest);
}
void TlsKeyExchangeTest::ConfigNamedGroups(
const std::vector<SSLNamedGroup>& groups) {
client_->ConfigNamedGroups(groups);
server_->ConfigNamedGroups(groups);
}
std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetGroupDetails(
const std::shared_ptr<TlsExtensionCapture>& capture) {
EXPECT_TRUE(capture->captured());
const DataBuffer& ext = capture->extension();
uint32_t tmp = 0;
EXPECT_TRUE(ext.Read(0, 2, &tmp));
EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
EXPECT_TRUE(ext.len() % 2 == 0);
std::vector<SSLNamedGroup> groups;
for (size_t i = 1; i < ext.len() / 2; i += 1) {
EXPECT_TRUE(ext.Read(2 * i, 2, &tmp));
groups.push_back(static_cast<SSLNamedGroup>(tmp));
}
return groups;
}
std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetShareDetails(
const std::shared_ptr<TlsExtensionCapture>& capture) {
EXPECT_TRUE(capture->captured());
const DataBuffer& ext = capture->extension();
uint32_t tmp = 0;
EXPECT_TRUE(ext.Read(0, 2, &tmp));
EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
std::vector<SSLNamedGroup> shares;
size_t i = 2;
while (i < ext.len()) {
EXPECT_TRUE(ext.Read(i, 2, &tmp));
shares.push_back(static_cast<SSLNamedGroup>(tmp));
EXPECT_TRUE(ext.Read(i + 2, 2, &tmp));
i += 4 + tmp;
}
EXPECT_EQ(ext.len(), i);
return shares;
}
void TlsKeyExchangeTest::CheckKEXDetails(
const std::vector<SSLNamedGroup>& expected_groups,
const std::vector<SSLNamedGroup>& expected_shares, bool expect_hrr) {
std::vector<SSLNamedGroup> groups = GetGroupDetails(groups_capture_);
EXPECT_EQ(expected_groups, groups);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
ASSERT_LT(0U, expected_shares.size());
std::vector<SSLNamedGroup> shares = GetShareDetails(shares_capture_);
EXPECT_EQ(expected_shares, shares);
} else {
EXPECT_FALSE(shares_capture_->captured());
}
EXPECT_EQ(expect_hrr, capture_hrr_->buffer().len() != 0);
}
void TlsKeyExchangeTest::CheckKEXDetails(
const std::vector<SSLNamedGroup>& expected_groups,
const std::vector<SSLNamedGroup>& expected_shares) {
CheckKEXDetails(expected_groups, expected_shares, false);
}
void TlsKeyExchangeTest::CheckKEXDetails(
const std::vector<SSLNamedGroup>& expected_groups,
const std::vector<SSLNamedGroup>& expected_shares,
SSLNamedGroup expected_share2) {
CheckKEXDetails(expected_groups, expected_shares, true);
for (auto it : expected_shares) {
EXPECT_NE(expected_share2, it);
}
std::vector<SSLNamedGroup> expected_shares2 = {expected_share2};
EXPECT_EQ(expected_shares2, GetShareDetails(shares_capture2_));
}
} // namespace nss_test