Replace NSS with Pale Moon's

This commit is contained in:
wuggy 2026-06-29 21:29:25 +01:00
commit 8c2e376f94
2870 changed files with 1762232 additions and 1374220 deletions

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -101,7 +102,7 @@ static const BloomFilterConfig kBloomFilterConfigurations[] = {
{16, 9}, // This also uses all of the bits from the hashes.
};
INSTANTIATE_TEST_CASE_P(BloomFilterConfigurations, BloomFilterTest,
::testing::ValuesIn(kBloomFilterConfigurations));
INSTANTIATE_TEST_SUITE_P(BloomFilterConfigurations, BloomFilterTest,
::testing::ValuesIn(kBloomFilterConfigurations));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -7,9 +8,59 @@
#include "libssl_internals.h"
#include "nss.h"
#include "pk11hpke.h"
#include "pk11pub.h"
#include "pk11priv.h"
#include "tls13ech.h"
#include "seccomon.h"
#include "selfencrypt.h"
#include "secmodti.h"
#include "sslproto.h"
SECStatus SSLInt_RemoveServerCertificates(PRFileDesc *fd) {
if (!fd) {
return SECFailure;
}
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
PRCList *cursor;
while (!PR_CLIST_IS_EMPTY(&ss->serverCerts)) {
cursor = PR_LIST_TAIL(&ss->serverCerts);
PR_REMOVE_LINK(cursor);
ssl_FreeServerCert((sslServerCert *)cursor);
}
return SECSuccess;
}
SECStatus SSLInt_SetDCAdvertisedSigSchemes(PRFileDesc *fd,
const SSLSignatureScheme *schemes,
uint32_t num_sig_schemes) {
if (!fd) {
return SECFailure;
}
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
// Alloc and copy, libssl will free.
SSLSignatureScheme *dc_schemes =
PORT_ZNewArray(SSLSignatureScheme, num_sig_schemes);
if (!dc_schemes) {
return SECFailure;
}
memcpy(dc_schemes, schemes, sizeof(SSLSignatureScheme) * num_sig_schemes);
if (ss->xtnData.delegCredSigSchemesAdvertised) {
PORT_Free(ss->xtnData.delegCredSigSchemesAdvertised);
}
ss->xtnData.delegCredSigSchemesAdvertised = dc_schemes;
ss->xtnData.numDelegCredSigSchemesAdvertised = num_sig_schemes;
return SECSuccess;
}
SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits,
PRBool changeScheme) {
@ -96,6 +147,8 @@ PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext) {
return (PRBool)(ss && ssl3_ExtensionNegotiated(ss, ext));
}
// Tests should not use this function directly, because the keys may
// still be in cache. Instead, use TlsConnectTestBase::ClearServerCache.
void SSLInt_ClearSelfEncryptKey() { ssl_ResetSelfEncryptKeys(); }
sslSelfEncryptKeys *ssl_GetSelfEncryptKeysInt();
@ -303,6 +356,9 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
sslSocket *ss;
ssl3CipherSpec *spec;
PK11Context *pk11ctxt;
const ssl3BulkCipherDef *cipher_def;
ss = ssl_FindSocket(fd);
if (!ss) {
@ -313,7 +369,43 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
ss->ssl3.cwSpec->nextSeqNum = to;
spec = ss->ssl3.cwSpec;
cipher_def = spec->cipherDef;
spec->nextSeqNum = to;
if (cipher_def->type != type_aead) {
ssl_ReleaseSpecWriteLock(ss);
return SECSuccess;
}
/* If we are using aead, we need to advance the counter in the
* internal IV generator as well.
* This could be in the token or software. */
pk11ctxt = spec->cipherContext;
/* If counter is in the token, we need to switch it to software,
* since we don't have access to the internal state of the token. We do
* that by turning on the simulated message interface, then setting up the
* software IV generator */
if (pk11ctxt->ivCounter == 0) {
_PK11_ContextSetAEADSimulation(pk11ctxt);
pk11ctxt->ivLen = cipher_def->iv_size + cipher_def->explicit_nonce_size;
pk11ctxt->ivMaxCount = PR_UINT64(0xffffffffffffffff);
if ((cipher_def->explicit_nonce_size == 0) ||
(spec->version >= SSL_LIBRARY_VERSION_TLS_1_3)) {
pk11ctxt->ivFixedBits =
(pk11ctxt->ivLen - sizeof(sslSequenceNumber)) * BPB;
pk11ctxt->ivGen = CKG_GENERATE_COUNTER_XOR;
} else {
pk11ctxt->ivFixedBits = cipher_def->iv_size * BPB;
pk11ctxt->ivGen = CKG_GENERATE_COUNTER;
}
/* DTLS included the epoch in the fixed portion of the IV */
if (IS_DTLS(ss)) {
pk11ctxt->ivFixedBits += 2 * BPB;
}
}
/* now we can update the internal counter (either we are already using
* the software IV generator, or we just switched to it above */
pk11ctxt->ivCounter = to;
ssl_ReleaseSpecWriteLock(ss);
return SECSuccess;
}
@ -332,6 +424,24 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra) {
return SSLInt_AdvanceWriteSeqNum(fd, to);
}
SECStatus SSLInt_AdvanceDtls13DecryptFailures(PRFileDesc *fd, PRUint64 to) {
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
ssl3CipherSpec *spec = ss->ssl3.crSpec;
if (spec->cipherDef->type != type_aead) {
ssl_ReleaseSpecWriteLock(ss);
return SECFailure;
}
spec->deprotectionFailures = to;
ssl_ReleaseSpecWriteLock(ss);
return SECSuccess;
}
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group);
if (!groupDef) return ssl_kea_null;
@ -373,3 +483,19 @@ SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending) {
ssl_ReleaseSSL3HandshakeLock(ss);
return SECSuccess;
}
SECStatus SSLInt_SetRawEchConfigForRetry(PRFileDesc *fd, const uint8_t *buf,
size_t len) {
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
SECITEM_FreeItem(&cfg->raw, PR_FALSE);
SECITEM_AllocItem(NULL, &cfg->raw, len);
PORT_Memcpy(cfg->raw.data, buf, len);
return SECSuccess;
}
PRBool SSLInt_IsIp(PRUint8 *s, unsigned int len) { return tls13_IsIp(s, len); }

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -35,6 +36,7 @@ PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd);
SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len);
PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType);
PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type);
SECStatus SSLInt_AdvanceDtls13DecryptFailures(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
@ -43,5 +45,12 @@ SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits,
PRBool changeScheme);
SECStatus SSLInt_SetDCAdvertisedSigSchemes(PRFileDesc *fd,
const SSLSignatureScheme *schemes,
uint32_t num_sig_schemes);
SECStatus SSLInt_RemoveServerCertificates(PRFileDesc *fd);
SECStatus SSLInt_SetRawEchConfigForRetry(PRFileDesc *fd, const uint8_t *buf,
size_t len);
PRBool SSLInt_IsIp(PRUint8 *s, unsigned int len);
#endif // ndef libssl_internals_h_
#endif // ifndef libssl_internals_h_

View file

@ -14,6 +14,7 @@ CSRCS = \
CPPSRCS = \
bloomfilter_unittest.cc \
ssl_0rtt_unittest.cc \
ssl_aead_unittest.cc \
ssl_agent_unittest.cc \
ssl_auth_unittest.cc \
ssl_cert_ext_unittest.cc \
@ -35,8 +36,8 @@ CPPSRCS = \
ssl_hrr_unittest.cc \
ssl_keyupdate_unittest.cc \
ssl_loopback_unittest.cc \
ssl_masking_unittest.cc \
ssl_misc_unittest.cc \
ssl_primitive_unittest.cc \
ssl_record_unittest.cc \
ssl_recordsep_unittest.cc \
ssl_recordsize_unittest.cc \
@ -55,8 +56,9 @@ CPPSRCS = \
tls_hkdf_unittest.cc \
tls_filter.cc \
tls_protect.cc \
tls_psk_unittest.cc \
tls_subcerts_unittest.cc \
tls_esni_unittest.cc \
tls_ech_unittest.cc \
$(SSLKEYLOGFILE_FILES) \
$(NULL)

View file

@ -0,0 +1,107 @@
/* -*- 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/. */
#ifndef nss_policy_h_
#define nss_policy_h_
#include "prtypes.h"
#include "secoid.h"
#include "nss.h"
namespace nss_test {
// container class to hold all a temp policy
class NssPolicy {
public:
NssPolicy() : oid_(SEC_OID_UNKNOWN), set_(0), clear_(0) {}
NssPolicy(SECOidTag _oid, PRUint32 _set, PRUint32 _clear)
: oid_(_oid), set_(_set), clear_(_clear) {}
NssPolicy(const NssPolicy &p)
: oid_(p.oid_), set_(p.set_), clear_(p.clear_) {}
// clone the current policy for this oid
NssPolicy(SECOidTag _oid) : oid_(_oid), set_(0), clear_(0) {
NSS_GetAlgorithmPolicy(_oid, &set_);
clear_ = ~set_;
}
SECOidTag oid(void) const { return oid_; }
PRUint32 set(void) const { return set_; }
PRUint32 clear(void) const { return clear_; }
operator bool() const { return oid_ != SEC_OID_UNKNOWN; }
private:
SECOidTag oid_;
PRUint32 set_;
PRUint32 clear_;
};
// container class to hold a temp option
class NssOption {
public:
NssOption() : id_(-1), value_(0) {}
NssOption(PRInt32 _id, PRInt32 _value) : id_(_id), value_(_value) {}
NssOption(const NssOption &o) : id_(o.id_), value_(o.value_) {}
// clone the current option for this id
NssOption(PRInt32 _id) : id_(_id), value_(0) { NSS_OptionGet(id_, &value_); }
PRInt32 id(void) const { return id_; }
PRInt32 value(void) const { return value_; }
operator bool() const { return id_ != -1; }
private:
PRInt32 id_;
PRInt32 value_;
};
// set the policy indicated in NssPolicy and restor the old policy
// when we go out of scope
class NssManagePolicy {
public:
NssManagePolicy(const NssPolicy &p, const NssOption &o)
: policy_(p), save_policy_(~(PRUint32)0), option_(o), save_option_(0) {
if (p) {
(void)NSS_GetAlgorithmPolicy(p.oid(), &save_policy_);
(void)NSS_SetAlgorithmPolicy(p.oid(), p.set(), p.clear());
}
if (o) {
(void)NSS_OptionGet(o.id(), &save_option_);
(void)NSS_OptionSet(o.id(), o.value());
}
}
~NssManagePolicy() {
if (policy_) {
(void)NSS_SetAlgorithmPolicy(policy_.oid(), save_policy_, ~save_policy_);
}
if (option_) {
(void)NSS_OptionSet(option_.id(), save_option_);
}
}
private:
NssPolicy policy_;
PRUint32 save_policy_;
NssOption option_;
PRInt32 save_option_;
};
// wrapping PRFileDesc this way ensures that tests that attempt to access
// PRFileDesc always correctly apply
// the policy that was bound to that socket with TlsAgent::SetPolicy().
class NssManagedFileDesc {
public:
NssManagedFileDesc(PRFileDesc *fd, const NssPolicy &policy,
const NssOption &option)
: fd_(fd), managed_policy_(policy, option) {}
PRFileDesc *get(void) const { return fd_; }
operator PRFileDesc *() const { return fd_; }
bool operator==(PRFileDesc *fd) const { return fd_ == fd; }
private:
PRFileDesc *fd_;
NssManagePolicy managed_policy_;
};
} // namespace nss_test
#endif

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -274,7 +275,7 @@ TEST_F(SelfEncryptTest128, AESWithMacKeyDecrypt) {
SEC_ERROR_INVALID_KEY);
}
INSTANTIATE_TEST_CASE_P(VariousSizes, SelfEncryptTestVariable,
::testing::Values(0, 15, 16, 31, 255, 256, 257));
INSTANTIATE_TEST_SUITE_P(VariousSizes, SelfEncryptTestVariable,
::testing::Values(0, 15, 16, 31, 255, 256, 257));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -14,6 +15,7 @@ extern "C" {
#include "libssl_internals.h"
}
#include "cpputil.h"
#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
@ -116,16 +118,12 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 {
};
protected:
void RunTest(bool rollover) {
// Run the initial handshake
SetupForZeroRtt();
void RunTest(bool rollover, const ScopedPK11SymKey& epsk) {
// 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());
@ -141,6 +139,11 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 {
Reset();
server_->StartConnect();
server_->Set0RttEnabled(true);
server_->SetAntiReplayContext(anti_replay_);
if (epsk) {
AddPsk(epsk, std::string("foo"), ssl_hash_sha256,
TLS_CHACHA20_POLY1305_SHA256);
}
// Capture the early_data extension, which should not appear.
auto early_data_ext =
@ -153,11 +156,41 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 {
server_->Handshake();
EXPECT_FALSE(early_data_ext->captured());
}
void RunResPskTest(bool rollover) {
// Run the initial handshake
SetupForZeroRtt();
ExpectResumption(RESUME_TICKET);
RunTest(rollover, ScopedPK11SymKey(nullptr));
}
void RunExtPskTest(bool rollover) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
const std::vector<uint8_t> kPskDummyVal(16, 0xFF);
SECItem psk_item = {siBuffer, toUcharPtr(kPskDummyVal.data()),
static_cast<unsigned int>(kPskDummyVal.size())};
PK11SymKey* key =
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
CKA_DERIVE, &psk_item, NULL);
ASSERT_NE(nullptr, key);
ScopedPK11SymKey scoped_psk(key);
RolloverAntiReplay();
AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256,
TLS_CHACHA20_POLY1305_SHA256);
StartConnect();
RunTest(rollover, scoped_psk);
}
};
TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); }
TEST_P(TlsZeroRttReplayTest, ResPskZeroRttReplay) { RunResPskTest(false); }
TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(true); }
TEST_P(TlsZeroRttReplayTest, ExtPskZeroRttReplay) { RunExtPskTest(false); }
TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) {
RunResPskTest(true);
}
// Test that we don't try to send 0-RTT data when the server sent
// us a ticket without the 0-RTT flags.
@ -178,6 +211,106 @@ TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
SendReceive();
}
// Make sure that a session ticket sent well after the original handshake
// can be used for 0-RTT.
// Stream because DTLS doesn't support SSL_SendSessionTicket.
TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicket) {
// Use a small-ish anti-replay window.
ResetAntiReplay(100 * PR_USEC_PER_MSEC);
RolloverAntiReplay();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
server_->Set0RttEnabled(true);
Connect();
CheckKeys();
// Now move time forward 30s and send a ticket.
AdvanceTime(30 * PR_USEC_PER_SEC);
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
SendReceive();
Reset();
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}
// Check that post-handshake authentication with a long RTT doesn't
// make things worse.
TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketPha) {
// Use a small-ish anti-replay window.
ResetAntiReplay(100 * PR_USEC_PER_MSEC);
RolloverAntiReplay();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
server_->Set0RttEnabled(true);
client_->SetupClientAuth();
client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
Connect();
CheckKeys();
// Add post-handshake authentication, with some added delays.
AdvanceTime(10 * PR_USEC_PER_SEC);
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()));
AdvanceTime(10 * PR_USEC_PER_SEC);
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
AdvanceTime(10 * PR_USEC_PER_SEC);
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
server_->SendData(100);
client_->ReadBytes(100);
Reset();
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}
// Same, but with client authentication on the first connection.
TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketClientAuth) {
// Use a small-ish anti-replay window.
ResetAntiReplay(100 * PR_USEC_PER_MSEC);
RolloverAntiReplay();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
client_->SetupClientAuth();
server_->RequestClientAuth(true);
server_->Set0RttEnabled(true);
Connect();
CheckKeys();
// Now move time forward 30s and send a ticket.
AdvanceTime(30 * PR_USEC_PER_SEC);
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
SendReceive();
Reset();
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}
TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
@ -476,15 +609,6 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
client_->CheckErrorCode(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA);
}
static void 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));
}
TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
EnsureTlsSetup();
const char* big_message = "0123456789abcdef";
@ -1022,9 +1146,38 @@ TEST_P(TlsConnectTls13, ZeroRttDifferentIncompatibleCipher) {
SendReceive();
}
// The client failing to provide EndOfEarlyData results in failure.
// After 0-RTT working perfectly, things fall apart later.
// The server is unable to detect the change in keys, so it fails decryption.
// The client thinks everything has worked until it gets the alert.
TEST_F(TlsConnectStreamTls13, SuppressEndOfEarlyDataClientOnly) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
ExpectAlert(server_, kTlsAlertBadRecordMac);
Handshake();
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
client_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
client_->CheckErrorCode(SSL_ERROR_BAD_MAC_ALERT);
}
TEST_P(TlsConnectGeneric, SuppressEndOfEarlyDataNoZeroRtt) {
EnsureTlsSetup();
client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
server_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
Connect();
SendReceive();
}
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
TlsConnectTestBase::kTlsVariantsAll);
#endif
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -53,7 +54,7 @@ class AeadTest : public ::testing::Test {
ASSERT_GE(kMaxSize, ciphertext_len);
ASSERT_LT(0U, ciphertext_len);
uint8_t output[kMaxSize];
uint8_t output[kMaxSize] = {0};
unsigned int output_len = 0;
EXPECT_EQ(SECSuccess, SSL_AeadEncrypt(ctx.get(), 0, kAad, sizeof(kAad),
kPlaintext, sizeof(kPlaintext),
@ -180,7 +181,7 @@ TEST_F(AeadTest, AeadNoPointer) {
}
TEST_F(AeadTest, AeadAes128Gcm) {
SSLAeadContext *ctxInit;
SSLAeadContext *ctxInit = nullptr;
ASSERT_EQ(SECSuccess,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256,
secret_.get(), kLabel, strlen(kLabel), &ctxInit));
@ -202,7 +203,7 @@ TEST_F(AeadTest, AeadAes256Gcm) {
}
TEST_F(AeadTest, AeadChaCha20Poly1305) {
SSLAeadContext *ctxInit;
SSLAeadContext *ctxInit = nullptr;
ASSERT_EQ(
SECSuccess,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_CHACHA20_POLY1305_SHA256,

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -153,7 +154,6 @@ TEST_F(TlsAgentDgramTestClient, AckWithBogusLengthField) {
sizeof(ackBuf), &record, 0);
agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
SSL_LIBRARY_VERSION_TLS_1_3);
ExpectAlert(kTlsAlertDecodeError);
ProcessMessage(record, TlsAgent::STATE_ERROR,
SSL_ERROR_RX_MALFORMED_DTLS_ACK);
}
@ -170,7 +170,8 @@ TEST_F(TlsAgentDgramTestClient, AckWithNonEvenLength) {
// Because we haven't negotiated the version,
// ssl3_DecodeError() sends an older (pre-TLS error).
ExpectAlert(kTlsAlertIllegalParameter);
ProcessMessage(record, TlsAgent::STATE_ERROR, SSL_ERROR_BAD_SERVER);
ProcessMessage(record, TlsAgent::STATE_ERROR,
SSL_ERROR_RX_MALFORMED_DTLS_ACK);
}
TEST_F(TlsAgentStreamTestClient, Set0RttOptionThenWrite) {
@ -223,15 +224,12 @@ TEST_F(TlsAgentStreamTestServer, Set0RttOptionClientHelloThenRead) {
ProcessMessage(buffer, TlsAgent::STATE_ERROR, SSL_ERROR_BAD_MAC_READ);
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
AgentTests, TlsAgentTest,
::testing::Combine(TlsAgentTestBase::kTlsRolesAll,
TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(ClientTests, TlsAgentTestClient,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(ClientTests13, TlsAgentTestClient13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(ClientTests13, TlsAgentTestClient13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
} // namespace nss_test

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -151,17 +152,17 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
SendReceive();
// Check that we used the right cipher suite, auth type and kea type.
uint16_t actual;
uint16_t actual = TLS_NULL_WITH_NULL_NULL;
EXPECT_TRUE(client_->cipher_suite(&actual));
EXPECT_EQ(cipher_suite_, actual);
EXPECT_TRUE(server_->cipher_suite(&actual));
EXPECT_EQ(cipher_suite_, actual);
SSLAuthType auth;
SSLAuthType auth = ssl_auth_size;
EXPECT_TRUE(client_->auth_type(&auth));
EXPECT_EQ(auth_type_, auth);
EXPECT_TRUE(server_->auth_type(&auth));
EXPECT_EQ(auth_type_, auth);
SSLKEAType kea;
SSLKEAType kea = ssl_kea_size;
EXPECT_TRUE(client_->kea_type(&kea));
EXPECT_EQ(kea_type_, kea);
EXPECT_TRUE(server_->kea_type(&kea));
@ -242,7 +243,7 @@ TEST_P(TlsCipherSuiteTest, SingleCipherSuite) {
TEST_P(TlsCipherSuiteTest, ResumeCipherSuite) {
if (SkipIfCipherSuiteIsDSA()) {
return; // Tickets don't work with DSA (bug 1174677).
GTEST_SKIP() << "Tickets not supported with DSA (bug 1174677).";
}
SetupCertificate(); // This is only needed once.
@ -262,6 +263,7 @@ TEST_P(TlsCipherSuiteTest, ResumeCipherSuite) {
TEST_P(TlsCipherSuiteTest, ReadLimit) {
SetupCertificate();
EnableSingleCipher();
TlsSendCipherSpecCapturer capturer(client_);
ConnectAndCheckCipherSuite();
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
uint64_t last = last_safe_write();
@ -294,9 +296,31 @@ TEST_P(TlsCipherSuiteTest, ReadLimit) {
} else {
epoch = 0;
}
TlsAgentTestBase::MakeRecord(variant_, ssl_ct_application_data, version_,
payload, sizeof(payload), &record,
(epoch << 48) | record_limit());
uint64_t seqno = (epoch << 48) | record_limit();
// DTLS 1.3 masks the sequence number
if (variant_ == ssl_variant_datagram &&
version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
auto spec = capturer.spec(1);
ASSERT_NE(nullptr, spec.get());
ASSERT_EQ(3, spec->epoch());
DataBuffer pt, ct;
uint8_t dtls13_ctype = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno |
kCtDtlsCiphertextLengthPresent;
TlsRecordHeader hdr(variant_, version_, dtls13_ctype, seqno);
pt.Assign(payload, sizeof(payload));
TlsRecordHeader out_hdr;
spec->Protect(hdr, pt, &ct, &out_hdr);
auto rv = out_hdr.Write(&record, 0, ct);
EXPECT_EQ(out_hdr.header_length() + ct.len(), rv);
} else {
TlsAgentTestBase::MakeRecord(variant_, ssl_ct_application_data, version_,
payload, sizeof(payload), &record, seqno);
}
client_->SendDirect(record);
server_->ExpectReadWriteError();
server_->ReadBytes();
@ -306,7 +330,7 @@ TEST_P(TlsCipherSuiteTest, ReadLimit) {
TEST_P(TlsCipherSuiteTest, WriteLimit) {
// This asserts in TLS 1.3 because we expect an automatic update.
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
return;
GTEST_SKIP();
}
SetupCertificate();
EnableSingleCipher();
@ -324,7 +348,7 @@ TEST_P(TlsCipherSuiteTest, WriteLimit) {
static const uint16_t k##name##CiphersArr[] = {__VA_ARGS__}; \
static const ::testing::internal::ParamGenerator<uint16_t> \
k##name##Ciphers = ::testing::ValuesIn(k##name##CiphersArr); \
INSTANTIATE_TEST_CASE_P( \
INSTANTIATE_TEST_SUITE_P( \
CipherSuite##name, TlsCipherSuiteTest, \
::testing::Combine(TlsConnectTestBase::kTlsVariants##modes, \
TlsConnectTestBase::kTls##versions, k##name##Ciphers, \
@ -501,7 +525,7 @@ static const SecStatusParams kSecStatusTestValuesArr[] = {
"AES-256-GCM", 256},
{SSL_LIBRARY_VERSION_TLS_1_2, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
"ChaCha20-Poly1305", 256}};
INSTANTIATE_TEST_CASE_P(TestSecurityStatus, SecurityStatusTest,
::testing::ValuesIn(kSecStatusTestValuesArr));
INSTANTIATE_TEST_SUITE_P(TestSecurityStatus, SecurityStatusTest,
::testing::ValuesIn(kSecStatusTestValuesArr));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -22,23 +23,23 @@ static void IncrementCounterArg(void *arg) {
}
}
PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
PRUint8 *data, unsigned int *len,
unsigned int maxLen, void *arg) {
static PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
PRUint8 *data, unsigned int *len,
unsigned int maxLen, void *arg) {
IncrementCounterArg(arg);
return PR_FALSE;
}
PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
PRUint8 *data, unsigned int *len,
unsigned int maxLen, void *arg) {
static PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
PRUint8 *data, unsigned int *len,
unsigned int maxLen, void *arg) {
IncrementCounterArg(arg);
return PR_TRUE;
}
SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
const PRUint8 *data, unsigned int len,
SSLAlertDescription *alert, void *arg) {
static SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
const PRUint8 *data, unsigned int len,
SSLAlertDescription *alert, void *arg) {
return SECSuccess;
}
@ -66,8 +67,8 @@ static const uint16_t kManyExtensions[] = {
ssl_tls13_certificate_authorities_xtn,
ssl_next_proto_nego_xtn,
ssl_renegotiation_info_xtn,
ssl_tls13_short_header_xtn,
ssl_record_size_limit_xtn,
ssl_tls13_encrypted_client_hello_xtn,
1,
0xffff};
// The list here includes all extensions we expect to use (SSL_MAX_EXTENSIONS),

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -27,8 +28,7 @@ extern FILE* ssl_keylog_iob;
TEST_P(TlsConnectGeneric, DebugEnvTraceFileNotSet) {
char* ev = PR_GetEnvSecure("SSLDEBUGFILE");
if (ev && ev[0]) {
// note: should use GTEST_SKIP when GTest gets updated to support it
return;
GTEST_SKIP();
}
Connect();
@ -40,8 +40,7 @@ TEST_P(TlsConnectGeneric, DebugEnvTraceFileNotSet) {
TEST_P(TlsConnectGeneric, DebugEnvKeylogFileNotSet) {
char* ev = PR_GetEnvSecure("SSLKEYLOGFILE");
if (ev && ev[0]) {
// note: should use GTEST_SKIP when GTest gets updated to support it
return;
GTEST_SKIP();
}
Connect();

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -345,11 +346,11 @@ static const bool kTrueFalseArr[] = {true, false};
static ::testing::internal::ParamGenerator<bool> kTrueFalse =
::testing::ValuesIn(kTrueFalseArr);
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
DamageYStream, TlsDamageDHYTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12, kAllY, kTrueFalse));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
DamageYDatagram, TlsDamageDHYTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12, kAllY, kTrueFalse));
@ -642,37 +643,6 @@ TEST_P(TlsConnectGenericPre13, InvalidDERSignatureFfdhe) {
client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
}
// Replace SignatureAndHashAlgorithm of a SKE.
class DHEServerKEXSigAlgReplacer : public TlsHandshakeFilter {
public:
DHEServerKEXSigAlgReplacer(const std::shared_ptr<TlsAgent>& server,
SSLSignatureScheme sig_scheme)
: TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}),
sig_scheme_(sig_scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
uint32_t len;
uint32_t idx = 0;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
output->Write(idx, sig_scheme_, 2);
return CHANGE;
}
private:
SSLSignatureScheme sig_scheme_;
};
TEST_P(TlsConnectTls12, ConnectInconsistentSigAlgDHE) {
EnableOnlyDheCiphers();
@ -753,6 +723,34 @@ TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicyDhe) {
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
}
TEST_P(TlsConnectPre12, ConnectSigAlgDisabledWeakGroupByOption3072DhePre12) {
EnableOnlyDheCiphers();
// explicitly enable the weak groups
EXPECT_EQ(SECSuccess,
SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE));
EXPECT_EQ(SECSuccess,
SSL_EnableWeakDHEPrimeGroup(client_->ssl_fd(), PR_TRUE));
server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072);
Connect();
client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
}
TEST_P(TlsConnectPre12, ConnectSigAlgDisabledWeakGroupByOption2048DhePre12) {
EnableOnlyDheCiphers();
// explicitly enable the weak groups
EXPECT_EQ(SECSuccess,
SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE));
EXPECT_EQ(SECSuccess,
SSL_EnableWeakDHEPrimeGroup(client_->ssl_fd(), PR_TRUE));
server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 2048);
Connect();
client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_2048, 2048);
server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_2048, 2048);
}
TEST_P(TlsConnectPre12, ConnectSigAlgDisabledByPolicyDhePre12) {
EnableOnlyDheCiphers();
@ -777,4 +775,28 @@ TEST_P(TlsConnectPre12, ConnectSigAlgDisabledByPolicyDhePre12) {
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
}
TEST_P(TlsConnectTls12, ConnectSigAlgDisablePreferredGroupByOption3072Dhe) {
EnableOnlyDheCiphers();
static const SSLDHEGroupType dhe_groups[] = {
ssl_ff_dhe_2048_group, // first in the lists is the preferred group
ssl_ff_dhe_3072_group};
server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072);
EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), &dhe_groups[0],
PR_ARRAY_SIZE(dhe_groups)));
Connect();
// our option size should override the preferred group
client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
}
TEST_P(TlsConnectTls12, ConnectSigAlgDisableGroupByOption3072Dhe) {
EnableOnlyDheCiphers();
server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072);
Connect();
client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072);
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -618,55 +619,6 @@ TEST_P(TlsDropDatagram13, ReorderServerEE) {
// The client sends an out of order non-handshake message
// but with the handshake key.
class TlsSendCipherSpecCapturer {
public:
TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent)
: agent_(agent), send_cipher_specs_() {
EXPECT_EQ(SECSuccess,
SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this));
}
std::shared_ptr<TlsCipherSpec> spec(size_t i) {
if (i >= send_cipher_specs_.size()) {
return nullptr;
}
return send_cipher_specs_[i];
}
private:
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSLSecretDirection dir, PK11SymKey* secret,
void* arg) {
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
std::cerr << self->agent_->role_str() << ": capture " << dir
<< " secret for epoch " << epoch << std::endl;
if (dir == ssl_secret_read) {
return;
}
SSLPreliminaryChannelInfo preinfo;
EXPECT_EQ(SECSuccess,
SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo,
sizeof(preinfo)));
EXPECT_EQ(sizeof(preinfo), preinfo.length);
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
SSLCipherSuiteInfo cipherinfo;
EXPECT_EQ(SECSuccess,
SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo,
sizeof(cipherinfo)));
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
auto spec = std::make_shared<TlsCipherSpec>(true, epoch);
EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret));
self->send_cipher_specs_.push_back(spec);
}
std::shared_ptr<TlsAgent> agent_;
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
};
TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) {
StartConnect();
// Capturing secrets means that we can't use decrypting filters on the client.
@ -683,8 +635,10 @@ TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) {
auto spec = capturer.spec(0);
ASSERT_NE(nullptr, spec.get());
ASSERT_EQ(2, spec->epoch());
ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002,
ssl_ct_application_data,
uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno |
kCtDtlsCiphertextLengthPresent;
ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002, dtls13_ct,
DataBuffer(buf, sizeof(buf))));
// Now have the server consume the bogus message.
@ -843,7 +797,7 @@ static void GetCipherAndLimit(uint16_t version, uint16_t* cipher,
// a reasonable amount of time.
*cipher = TLS_CHACHA20_POLY1305_SHA256;
// Assume that we are starting with an expected sequence number of 0.
*limit = (1ULL << 29) - 1;
*limit = (1ULL << 15) - 1;
}
}
@ -865,14 +819,14 @@ TEST_P(TlsConnectDatagram, MissLotsOfPackets) {
SendReceive();
}
// Send a sequence number of 0xfffffffd and it should be interpreted as that
// Send a sequence number of 0xfffd and it should be interpreted as that
// (and not -3 or UINT64_MAX - 2).
TEST_F(TlsConnectDatagram13, UnderflowSequenceNumber) {
Connect();
// This is only valid if short headers are disabled.
client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_FALSE);
EXPECT_EQ(SECSuccess,
SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), (1ULL << 30) - 3));
SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), (1ULL << 16) - 3));
SendReceive();
}
@ -917,9 +871,13 @@ class TlsReplaceFirstRecordWithJunk : public TlsRecordFilter {
return KEEP;
}
replaced_ = true;
TlsRecordHeader out_header(header.variant(), header.version(),
ssl_ct_application_data,
header.sequence_number());
uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno |
kCtDtlsCiphertextLengthPresent;
TlsRecordHeader out_header(
header.variant(), header.version(),
is_dtls13() ? dtls13_ct : ssl_ct_application_data,
header.sequence_number());
static const uint8_t junk[] = {1, 2, 3, 4};
*offset = out_header.Write(output, *offset, DataBuffer(junk, sizeof(junk)));
@ -942,15 +900,15 @@ TEST_P(TlsConnectDatagram, ReplaceFirstClientRecordWithApplicationData) {
Connect();
}
INSTANTIATE_TEST_CASE_P(Datagram12Plus, TlsConnectDatagram12Plus,
TlsConnectTestBase::kTlsV12Plus);
INSTANTIATE_TEST_CASE_P(DatagramPre13, TlsConnectDatagramPre13,
TlsConnectTestBase::kTlsV11V12);
INSTANTIATE_TEST_CASE_P(DatagramDrop13, TlsDropDatagram13,
::testing::Values(true, false));
INSTANTIATE_TEST_CASE_P(DatagramReorder13, TlsReorderDatagram13,
::testing::Values(true, false));
INSTANTIATE_TEST_CASE_P(DatagramFragment13, TlsFragmentationAndRecoveryTest,
::testing::Values(true, false));
INSTANTIATE_TEST_SUITE_P(Datagram12Plus, TlsConnectDatagram12Plus,
TlsConnectTestBase::kTlsV12Plus);
INSTANTIATE_TEST_SUITE_P(DatagramPre13, TlsConnectDatagramPre13,
TlsConnectTestBase::kTlsV11V12);
INSTANTIATE_TEST_SUITE_P(DatagramDrop13, TlsDropDatagram13,
::testing::Values(true, false));
INSTANTIATE_TEST_SUITE_P(DatagramReorder13, TlsReorderDatagram13,
::testing::Values(true, false));
INSTANTIATE_TEST_SUITE_P(DatagramFragment13, TlsFragmentationAndRecoveryTest,
::testing::Values(true, false));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -619,31 +620,6 @@ TEST_P(TlsConnectGenericPre13, ConnectUnsupportedPointFormat) {
client_->CheckErrorCode(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM);
}
// Replace SignatureAndHashAlgorithm of a SKE.
class ECCServerKEXSigAlgReplacer : public TlsHandshakeFilter {
public:
ECCServerKEXSigAlgReplacer(const std::shared_ptr<TlsAgent> &server,
SSLSignatureScheme sig_scheme)
: TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}),
sig_scheme_(sig_scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
const DataBuffer &input,
DataBuffer *output) {
*output = input;
uint32_t point_len;
EXPECT_TRUE(output->Read(3, 1, &point_len));
output->Write(4 + point_len, sig_scheme_, 2);
return CHANGE;
}
private:
SSLSignatureScheme sig_scheme_;
};
TEST_P(TlsConnectTls12, ConnectUnsupportedSigAlg) {
EnsureTlsSetup();
client_->DisableAllCiphers();
@ -739,14 +715,14 @@ TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicy) {
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
}
INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11Plus));
INSTANTIATE_TEST_SUITE_P(KeyExchangeTest, TlsKeyExchangeTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11Plus));
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(KeyExchangeTest, TlsKeyExchangeTest13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
#endif
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -147,4 +148,41 @@ TEST_P(TlsConnectTls13, EarlyExporter) {
SendReceive();
}
TEST_P(TlsConnectTls13, EarlyExporterExternalPsk) {
RolloverAntiReplay();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(!!slot);
ScopedPK11SymKey scoped_psk(
PK11_KeyGen(slot.get(), CKM_HKDF_KEY_GEN, nullptr, 16, nullptr));
AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256,
TLS_CHACHA20_POLY1305_SHA256);
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
client_->Handshake(); // Send ClientHello.
uint8_t client_value[10] = {0};
RegularExporterShouldFail(client_.get(), nullptr, 0);
EXPECT_EQ(SECSuccess,
SSL_ExportEarlyKeyingMaterial(
client_->ssl_fd(), kExporterLabel, strlen(kExporterLabel),
kExporterContext, sizeof(kExporterContext), client_value,
sizeof(client_value)));
server_->SetSniCallback(RegularExporterShouldFail);
server_->Handshake(); // Handle ClientHello.
uint8_t server_value[10] = {0};
EXPECT_EQ(SECSuccess,
SSL_ExportEarlyKeyingMaterial(
server_->ssl_fd(), kExporterLabel, strlen(kExporterLabel),
kExporterContext, sizeof(kExporterContext), server_value,
sizeof(server_value)));
EXPECT_EQ(0, memcmp(client_value, server_value, sizeof(client_value)));
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -19,6 +20,45 @@
namespace nss_test {
class Dtls13LegacyCookieInjector : public TlsHandshakeFilter {
public:
Dtls13LegacyCookieInjector(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeClientHello}) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
const uint8_t cookie_bytes[] = {0x03, 0x0A, 0x0B, 0x0C};
uint32_t offset = 2 /* version */ + 32 /* random */;
if (agent()->variant() != ssl_variant_datagram) {
ADD_FAILURE();
return KEEP;
}
if (header.handshake_type() != ssl_hs_client_hello) {
return KEEP;
}
DataBuffer cookie(cookie_bytes, sizeof(cookie_bytes));
*output = input;
// Add the SID length (if any) to locate the cookie.
uint32_t sid_len = 0;
if (!output->Read(offset, 1, &sid_len)) {
ADD_FAILURE();
return KEEP;
}
offset += 1 + sid_len;
output->Splice(cookie, offset, 1);
return CHANGE;
}
private:
DataBuffer cookie_;
};
class TlsExtensionTruncator : public TlsExtensionFilter {
public:
TlsExtensionTruncator(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
@ -43,63 +83,6 @@ class TlsExtensionTruncator : public TlsExtensionFilter {
size_t length_;
};
class TlsExtensionAppender : public TlsHandshakeFilter {
public:
TlsExtensionAppender(const std::shared_ptr<TlsAgent>& a,
uint8_t handshake_type, uint16_t ext, DataBuffer& data)
: TlsHandshakeFilter(a, {handshake_type}), extension_(ext), data_(data) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
TlsParser parser(input);
if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
return KEEP;
}
*output = input;
// Increase the length of the extensions block.
if (!UpdateLength(output, parser.consumed(), 2)) {
return KEEP;
}
// Extensions in Certificate are nested twice. Increase the size of the
// certificate list.
if (header.handshake_type() == kTlsHandshakeCertificate) {
TlsParser p2(input);
if (!p2.SkipVariable(1)) {
ADD_FAILURE();
return KEEP;
}
if (!UpdateLength(output, p2.consumed(), 3)) {
return KEEP;
}
}
size_t offset = output->len();
offset = output->Write(offset, extension_, 2);
WriteVariable(output, offset, data_, 2);
return CHANGE;
}
private:
bool UpdateLength(DataBuffer* output, size_t offset, size_t size) {
uint32_t len;
if (!output->Read(offset, size, &len)) {
ADD_FAILURE();
return false;
}
len += 4 + data_.len();
output->Write(offset, len, size);
return true;
}
const uint16_t extension_;
const DataBuffer data_;
};
class TlsExtensionTestBase : public TlsConnectTestBase {
protected:
TlsExtensionTestBase(SSLProtocolVariant variant, uint16_t version)
@ -188,8 +171,29 @@ class TlsExtensionTest13
}
void ConnectWithReplacementVersionList(uint16_t version) {
DataBuffer versions_buf;
// Convert the version encoding for DTLS, if needed.
if (variant_ == ssl_variant_datagram) {
switch (version) {
case SSL_LIBRARY_VERSION_TLS_1_3:
#ifdef DTLS_1_3_DRAFT_VERSION
version = 0x7f00 | DTLS_1_3_DRAFT_VERSION;
#else
version = SSL_LIBRARY_VERSION_DTLS_1_3_WIRE;
#endif
break;
case SSL_LIBRARY_VERSION_TLS_1_2:
version = SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
break;
case SSL_LIBRARY_VERSION_TLS_1_1:
/* TLS_1_1 maps to DTLS_1_0, see sslproto.h. */
version = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
break;
default:
PORT_Assert(0);
}
}
DataBuffer versions_buf;
size_t index = versions_buf.Write(0, 2, 1);
versions_buf.Write(index, version, 2);
MakeTlsFilter<TlsExtensionReplacer>(
@ -322,6 +326,28 @@ TEST_P(TlsExtensionTestGeneric, AlpnMismatch) {
server_->EnableAlpn(server_alpn, sizeof(server_alpn));
ClientHelloErrorTest(nullptr, kTlsAlertNoApplicationProtocol);
client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
}
TEST_P(TlsExtensionTestGeneric, AlpnDisabledServer) {
const uint8_t client_alpn[] = {0x01, 0x61};
client_->EnableAlpn(client_alpn, sizeof(client_alpn));
server_->EnableAlpn(nullptr, 0);
ClientHelloErrorTest(nullptr, kTlsAlertUnsupportedExtension);
}
TEST_P(TlsConnectGeneric, AlpnDisabled) {
server_->EnableAlpn(nullptr, 0);
Connect();
SSLNextProtoState state;
uint8_t buf[255] = {0};
unsigned int buf_len = 3;
EXPECT_EQ(SECSuccess, SSL_GetNextProto(client_->ssl_fd(), &state, buf,
&buf_len, sizeof(buf)));
EXPECT_EQ(SSL_NEXT_PROTO_NO_SUPPORT, state);
EXPECT_EQ(0U, buf_len);
}
// Many of these tests fail in TLS 1.3 because the extension is encrypted, which
@ -405,7 +431,10 @@ TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsBadLength) {
}
TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsTrailingData) {
const uint8_t val[] = {0x00, 0x02, 0x04, 0x01, 0x00}; // sha-256, rsa
// make sure the test uses an algorithm that is legal for
// tls 1.3 (or tls 1.3 will throw a handshake failure alert
// instead of a decode error alert)
const uint8_t val[] = {0x00, 0x02, 0x08, 0x09, 0x00}; // sha-256, rsa-pss-pss
DataBuffer extension(val, sizeof(val));
ClientHelloErrorTest(std::make_shared<TlsExtensionReplacer>(
client_, ssl_signature_algorithms_xtn, extension));
@ -569,6 +598,22 @@ TEST_P(TlsExtensionTestPre13, SupportedPointsTrailingData) {
client_, ssl_ec_point_formats_xtn, extension));
}
TEST_P(TlsExtensionTestPre13, SupportedPointsCompressed) {
const uint8_t val[] = {0x01, 0x02};
DataBuffer extension(val, sizeof(val));
ClientHelloErrorTest(std::make_shared<TlsExtensionReplacer>(
client_, ssl_ec_point_formats_xtn, extension),
kTlsAlertIllegalParameter);
}
TEST_P(TlsExtensionTestPre13, SupportedPointsUndefined) {
const uint8_t val[] = {0x01, 0xAA};
DataBuffer extension(val, sizeof(val));
ClientHelloErrorTest(std::make_shared<TlsExtensionReplacer>(
client_, ssl_ec_point_formats_xtn, extension),
kTlsAlertIllegalParameter);
}
TEST_P(TlsExtensionTestPre13, RenegotiationInfoBadLength) {
const uint8_t val[] = {0x99};
DataBuffer extension(val, sizeof(val));
@ -887,6 +932,26 @@ TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderValue) {
server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
}
// Do the same with an External PSK.
TEST_P(TlsConnectTls13, TestTls13PskInvalidBinderValue) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(!!slot);
ScopedPK11SymKey key(
PK11_KeyGen(slot.get(), CKM_HKDF_KEY_GEN, nullptr, 16, nullptr));
ASSERT_TRUE(!!key);
AddPsk(key, std::string("foo"), ssl_hash_sha256);
StartConnect();
ASSERT_TRUE(client_->MaybeSetResumptionToken());
MakeTlsFilter<TlsPreSharedKeyReplacer>(
client_, [](TlsPreSharedKeyReplacer* r) {
r->binders_[0].Write(0, r->binders_[0].data()[0] ^ 0xff, 1);
});
ConnectExpectAlert(server_, kTlsAlertDecryptError);
client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
}
// Extend the binder by one.
TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderLength) {
SetupForResume();
@ -1077,13 +1142,34 @@ TEST_P(TlsExtensionTest13, HrrThenRemoveSupportedGroups) {
}
TEST_P(TlsExtensionTest13, EmptyVersionList) {
static const uint8_t ext[] = {0x00, 0x00};
ConnectWithBogusVersionList(ext, sizeof(ext));
static const uint8_t kExt[] = {0x00, 0x00};
ConnectWithBogusVersionList(kExt, sizeof(kExt));
}
TEST_P(TlsExtensionTest13, OddVersionList) {
static const uint8_t ext[] = {0x00, 0x01, 0x00};
ConnectWithBogusVersionList(ext, sizeof(ext));
static const uint8_t kExt[] = {0x00, 0x01, 0x00};
ConnectWithBogusVersionList(kExt, sizeof(kExt));
}
TEST_P(TlsExtensionTest13, SignatureAlgorithmsInvalidTls13) {
// testing the case where we ask for a invalid parameter for tls13
const uint8_t val[] = {0x00, 0x02, 0x04, 0x01}; // sha-256, rsa-pkcs1
DataBuffer extension(val, sizeof(val));
ClientHelloErrorTest(std::make_shared<TlsExtensionReplacer>(
client_, ssl_signature_algorithms_xtn, extension),
kTlsAlertHandshakeFailure);
}
// Use the stream version number for TLS 1.3 (0x0304) in DTLS.
TEST_F(TlsConnectDatagram13, TlsVersionInDtls) {
static const uint8_t kExt[] = {0x02, 0x03, 0x04};
DataBuffer versions_buf(kExt, sizeof(kExt));
MakeTlsFilter<TlsExtensionReplacer>(client_, ssl_tls13_supported_versions_xtn,
versions_buf);
ConnectExpectAlert(server_, kTlsAlertProtocolVersion);
client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
}
// TODO: this only tests extensions in server messages. The client can extend
@ -1246,6 +1332,7 @@ TEST_P(TlsDisallowedUnadvertisedExtensionTest13,
TEST_P(TlsConnectStream, IncludePadding) {
EnsureTlsSetup();
SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE); // Don't GREASE
// This needs to be long enough to push a TLS 1.0 ClientHello over 255, but
// short enough not to push a TLS 1.3 ClientHello over 511.
@ -1262,48 +1349,157 @@ TEST_P(TlsConnectStream, IncludePadding) {
EXPECT_TRUE(capture->captured());
}
INSTANTIATE_TEST_CASE_P(
TEST_F(TlsConnectDatagram13, Dtls13RejectLegacyCookie) {
EnsureTlsSetup();
MakeTlsFilter<Dtls13LegacyCookieInjector>(client_);
ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
TEST_P(TlsConnectGeneric, ClientHelloExtensionPermutation) {
EnsureTlsSetup();
PR_ASSERT(SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_CH_EXTENSION_PERMUTATION,
PR_TRUE) == SECSuccess);
Connect();
}
TEST_F(TlsConnectStreamTls13, ClientHelloExtensionPermutationWithPSK) {
EnsureTlsSetup();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
const uint8_t kPskDummyVal_[16] = {0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
SECItem psk_item;
psk_item.type = siBuffer;
psk_item.len = sizeof(kPskDummyVal_);
psk_item.data = const_cast<uint8_t*>(kPskDummyVal_);
PK11SymKey* key =
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
CKA_DERIVE, &psk_item, NULL);
ScopedPK11SymKey scoped_psk_(key);
const std::string kPskDummyLabel_ = "NSS PSK GTEST label";
const SSLHashType kPskHash_ = ssl_hash_sha384;
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
PR_ASSERT(SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_CH_EXTENSION_PERMUTATION,
PR_TRUE) == SECSuccess);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
/* This test checks that the ClientHello extension order is actually permuted
* if ss->opt.chXtnPermutation is set. It is asserted that at least one out of
* 10 extension orders differs from the others.
*
* This is a probabilistic test: The default TLS 1.3 ClientHello contains 8
* extensions, leading to a 1/8! probability for any extension order and the
* same probability for two drawn extension orders to coincide.
* Since all sequences are compared against each other this leads to a false
* positive rate of (1/8!)^(n^2-n).
* To achieve a spurious failure rate << 1/2^64, we compare n=10 drawn orders.
*
* This test assures that randomisation is happening but does not check quality
* of the used Fisher-Yates shuffle. */
TEST_F(TlsConnectStreamTls13,
ClientHelloExtensionPermutationProbabilisticTest) {
std::vector<std::vector<uint16_t>> orders;
/* Capture the extension order of 10 ClientHello messages. */
for (size_t i = 0; i < 10; i++) {
client_->StartConnect();
/* Enable ClientHello extension permutation. */
ASSERT_TRUE(SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_CH_EXTENSION_PERMUTATION,
PR_TRUE) == SECSuccess);
/* Capture extension order filter. */
auto filter = MakeTlsFilter<TlsExtensionOrderCapture>(
client_, kTlsHandshakeClientHello);
/* Send ClientHello. */
client_->Handshake();
/* Remember extension order. */
orders.push_back(filter->order);
/* Reset client / server state. */
Reset();
}
/* Check for extension order inequality. */
size_t inequal = 0;
for (auto& outerOrders : orders) {
for (auto& innerOrders : orders) {
if (outerOrders != innerOrders) {
inequal++;
}
}
}
ASSERT_TRUE(inequal >= 1);
}
// The certificate_authorities xtn can be included in a ClientHello [RFC 8446,
// Section 4.2]
TEST_F(TlsConnectStreamTls13, ClientHelloCertAuthXtnToleration) {
EnsureTlsSetup();
uint8_t bodyBuf[3] = {0x00, 0x01, 0xff};
DataBuffer body(bodyBuf, sizeof(bodyBuf));
auto ch = MakeTlsFilter<TlsExtensionAppender>(
client_, kTlsHandshakeClientHello, ssl_tls13_certificate_authorities_xtn,
body);
// The Connection will fail because the added extension isn't in the client's
// transcript not because the extension is unsupported (Bug 1815167).
server_->ExpectSendAlert(bad_record_mac);
client_->ExpectSendAlert(bad_record_mac);
ConnectExpectFail();
server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
}
INSTANTIATE_TEST_SUITE_P(
ExtensionStream, TlsExtensionTestGeneric,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
ExtensionDatagram, TlsExtensionTestGeneric,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
INSTANTIATE_TEST_CASE_P(ExtensionDatagramOnly, TlsExtensionTestDtls,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_SUITE_P(ExtensionDatagramOnly, TlsExtensionTestDtls,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_CASE_P(ExtensionTls12, TlsExtensionTest12,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12));
INSTANTIATE_TEST_SUITE_P(ExtensionTls12, TlsExtensionTest12,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12));
INSTANTIATE_TEST_CASE_P(ExtensionTls12Plus, TlsExtensionTest12Plus,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12Plus));
INSTANTIATE_TEST_SUITE_P(ExtensionTls12Plus, TlsExtensionTest12Plus,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12Plus));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
ExtensionPre13Stream, TlsExtensionTestPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
INSTANTIATE_TEST_CASE_P(ExtensionPre13Datagram, TlsExtensionTestPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_SUITE_P(ExtensionPre13Datagram, TlsExtensionTestPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(ExtensionTls13, TlsExtensionTest13,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(ExtensionTls13, TlsExtensionTest13,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
BogusExtensionStream, TlsBogusExtensionTestPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
BogusExtensionDatagram, TlsBogusExtensionTestPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(BogusExtension13, TlsBogusExtensionTest13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(BogusExtension13, TlsBogusExtensionTest13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(DisallowedExtension13, TlsDisallowedExtensionTest13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -57,7 +57,6 @@ FUZZ_P(TlsFuzzTest, DeterministicExporter) {
Reset();
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
DisableECDHEServerKeyReuse();
// Reset the RNG state.
EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
@ -71,7 +70,6 @@ FUZZ_P(TlsFuzzTest, DeterministicExporter) {
Reset();
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
DisableECDHEServerKeyReuse();
// Reset the RNG state.
EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
@ -97,7 +95,6 @@ FUZZ_P(TlsFuzzTest, DeterministicTranscript) {
for (size_t i = 0; i < 5; i++) {
Reset();
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
DisableECDHEServerKeyReuse();
DataBuffer buffer;
MakeTlsFilter<TlsConversationRecorder>(client_, buffer);
@ -244,11 +241,11 @@ FUZZ_P(TlsFuzzTest, UnencryptedSessionTickets) {
client_->CheckCipherSuite(static_cast<uint16_t>(suite));
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
FuzzStream, TlsFuzzTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
FuzzDatagram, TlsFuzzTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -24,21 +25,6 @@ class GatherV2ClientHelloTest : public TlsConnectTestBase {
}
};
// Gather a 5-byte v3 record, with a zero fragment length. The empty handshake
// message should be ignored, and the connection will succeed afterwards.
TEST_F(TlsConnectTest, GatherEmptyV3Record) {
DataBuffer buffer;
size_t idx = 0;
idx = buffer.Write(idx, 0x16, 1); // handshake
idx = buffer.Write(idx, 0x0301, 2); // record_version
(void)buffer.Write(idx, 0U, 2); // length=0
EnsureTlsSetup();
client_->SendDirect(buffer);
Connect();
}
// Gather a 5-byte v3 record, with a fragment length exceeding the maximum.
TEST_F(TlsConnectTest, GatherExcessiveV3Record) {
DataBuffer buffer;
@ -140,4 +126,31 @@ TEST_F(GatherV2ClientHelloTest, GatherEmptyV2RecordShortHeader) {
ConnectExpectMalformedClientHello(buffer);
}
/* Test correct gather buffer clearing/freeing and (re-)allocation.
*
* Freeing and (re-)allocation of the gather buffers after reception of single
* records is only done in DEBUG builds. Normally they are created and
* destroyed with the SSL socket.
*
* TLS 1.0 record splitting leads to implicit complete read of the data.
*
* The NSS DTLS impelmentation does not allow partial reads
* (see sslsecur.c, line 535-543). */
TEST_P(TlsConnectStream, GatherBufferPartialReadTest) {
EnsureTlsSetup();
Connect();
client_->SendData(1000);
if (version_ > SSL_LIBRARY_VERSION_TLS_1_0) {
for (unsigned i = 1; i <= 20; i++) {
server_->ReadBytes(50);
ASSERT_EQ(server_->received_bytes(), 50U * i);
}
} else {
server_->ReadBytes(50);
ASSERT_EQ(server_->received_bytes(), 1000U);
}
}
} // namespace nss_test

View file

@ -15,6 +15,7 @@
'libssl_internals.c',
'selfencrypt_unittest.cc',
'ssl_0rtt_unittest.cc',
'ssl_aead_unittest.cc',
'ssl_agent_unittest.cc',
'ssl_auth_unittest.cc',
'ssl_cert_ext_unittest.cc',
@ -36,8 +37,8 @@
'ssl_hrr_unittest.cc',
'ssl_keyupdate_unittest.cc',
'ssl_loopback_unittest.cc',
'ssl_masking_unittest.cc',
'ssl_misc_unittest.cc',
'ssl_primitive_unittest.cc',
'ssl_record_unittest.cc',
'ssl_recordsep_unittest.cc',
'ssl_recordsize_unittest.cc',
@ -54,9 +55,11 @@
'tls_connect.cc',
'tls_filter.cc',
'tls_hkdf_unittest.cc',
'tls_esni_unittest.cc',
'tls_ech_unittest.cc',
'tls_protect.cc',
'tls_subcerts_unittest.cc'
'tls_psk_unittest.cc',
'tls_subcerts_unittest.cc',
'tls_grease_unittest.cc'
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
@ -103,6 +106,18 @@
'NSS_ALLOW_SSLKEYLOGFILE',
],
}],
# ssl_gtest fuzz defines should only be determined by the 'fuzz_tls'
# flag (so as to match lib/ssl). If gtest.gypi added the define due
# to '--fuzz' only, remove it.
['fuzz_tls==1', {
'defines': [
'UNSAFE_FUZZER_MODE',
],
}, {
'defines!': [
'UNSAFE_FUZZER_MODE',
],
}],
],
}
],

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -958,6 +959,34 @@ TEST_P(TlsKeyExchange13, ConnectEcdhePreferenceMismatchHrr) {
CheckKEXDetails(client_groups, expectedShares, ssl_grp_ec_curve25519);
}
TEST_P(TlsKeyExchange13, SecondClientHelloPreambleMatches) {
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
SSL_LIBRARY_VERSION_TLS_1_3);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
ConfigureSelfEncrypt();
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};
client_->ConfigNamedGroups(client_groups);
server_->ConfigNamedGroups(server_groups);
auto ch1 = MakeTlsFilter<ClientHelloPreambleCapture>(client_);
StartConnect();
client_->Handshake();
server_->Handshake();
MakeNewServer();
auto ch2 = MakeTlsFilter<ClientHelloPreambleCapture>(client_);
Handshake();
EXPECT_TRUE(ch1->captured());
EXPECT_TRUE(ch2->captured());
EXPECT_EQ(ch1->contents(), ch2->contents());
}
// This should work, but not use HRR because the key share for x25519 was
// pre-generated by the client.
TEST_P(TlsKeyExchange13, ConnectEcdhePreferenceMismatchHrrExtraShares) {
@ -1107,9 +1136,10 @@ class HelloRetryRequestAgentTest : public TlsAgentTestClient {
hrr_data.Allocate(len + 6);
size_t i = 0;
i = hrr_data.Write(i, variant_ == ssl_variant_datagram
? SSL_LIBRARY_VERSION_DTLS_1_2_WIRE
: SSL_LIBRARY_VERSION_TLS_1_2,
i = hrr_data.Write(i,
variant_ == ssl_variant_datagram
? SSL_LIBRARY_VERSION_DTLS_1_2_WIRE
: SSL_LIBRARY_VERSION_TLS_1_2,
2);
i = hrr_data.Write(i, ssl_hello_retry_random,
sizeof(ssl_hello_retry_random));
@ -1121,9 +1151,10 @@ class HelloRetryRequestAgentTest : public TlsAgentTestClient {
// Now the supported version.
i = hrr_data.Write(i, ssl_tls13_supported_versions_xtn, 2);
i = hrr_data.Write(i, 2, 2);
i = hrr_data.Write(i, (variant_ == ssl_variant_datagram)
? (0x7f00 | DTLS_1_3_DRAFT_VERSION)
: SSL_LIBRARY_VERSION_TLS_1_3,
i = hrr_data.Write(i,
(variant_ == ssl_variant_datagram)
? (0x7f00 | DTLS_1_3_DRAFT_VERSION)
: SSL_LIBRARY_VERSION_TLS_1_3,
2);
if (len) {
hrr_data.Write(i, body, len);
@ -1320,13 +1351,14 @@ TEST_F(TlsConnectStreamTls13, HrrThenTls12SupportedVersions) {
client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
}
INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(HelloRetryRequestAgentTests,
HelloRetryRequestAgentTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
INSTANTIATE_TEST_SUITE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
#endif
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -112,16 +113,16 @@ TEST_P(KeyLogFileTest, KeyLogFile) {
ASSERT_EXIT(ConnectAndCheck(), ::testing::ExitedWithCode(0), "");
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileDTLS12, KeyLogFileTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileTLS12, KeyLogFileTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileTLS13, KeyLogFileTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV13));
@ -145,16 +146,16 @@ TEST_P(KeyLogFileUnsetTest, KeyLogFile) {
ASSERT_EXIT(ConnectAndCheck(), ::testing::ExitedWithCode(0), "");
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileDTLS12, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileTLS12, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
KeyLogFileTLS13, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV13));

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -389,7 +390,7 @@ TEST_P(TlsConnectDatagram, ShortRead) {
TEST_P(TlsConnectStream, ShortRead) {
// This test behaves oddly with TLS 1.0 because of 1/n+1 splitting,
// so skip in that case.
if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) return;
if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) GTEST_SKIP();
Connect();
server_->SendData(50, 50);
@ -727,74 +728,74 @@ TEST_P(TlsConnectGeneric, ShutdownOneSideThenCloseTcp) {
EXPECT_EQ(PR_NOT_CONNECTED_ERROR, PR_GetError());
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericStream, TlsConnectGeneric,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericDatagram, TlsConnectGeneric,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
INSTANTIATE_TEST_CASE_P(StreamOnly, TlsConnectStream,
TlsConnectTestBase::kTlsVAll);
INSTANTIATE_TEST_CASE_P(DatagramOnly, TlsConnectDatagram,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_CASE_P(DatagramHolddown, TlsHolddownTest,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_SUITE_P(StreamOnly, TlsConnectStream,
TlsConnectTestBase::kTlsVAll);
INSTANTIATE_TEST_SUITE_P(DatagramOnly, TlsConnectDatagram,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_SUITE_P(DatagramHolddown, TlsHolddownTest,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
Pre12Stream, TlsConnectPre12,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10V11));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
Pre12Datagram, TlsConnectPre12,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11));
INSTANTIATE_TEST_CASE_P(Version12Only, TlsConnectTls12,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(Version12Only, TlsConnectTls12,
TlsConnectTestBase::kTlsVariantsAll);
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(Version13Only, TlsConnectTls13,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(Version13Only, TlsConnectTls13,
TlsConnectTestBase::kTlsVariantsAll);
#endif
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
Pre13Stream, TlsConnectGenericPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
Pre13Datagram, TlsConnectGenericPre13,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(Pre13StreamOnly, TlsConnectStreamPre13,
TlsConnectTestBase::kTlsV10ToV12);
INSTANTIATE_TEST_SUITE_P(Pre13StreamOnly, TlsConnectStreamPre13,
TlsConnectTestBase::kTlsV10ToV12);
INSTANTIATE_TEST_CASE_P(Version12Plus, TlsConnectTls12Plus,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12Plus));
INSTANTIATE_TEST_SUITE_P(Version12Plus, TlsConnectTls12Plus,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12Plus));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericStream, TlsConnectGenericResumption,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll,
::testing::Values(true, false)));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericDatagram, TlsConnectGenericResumption,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus,
::testing::Values(true, false)));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericStream, TlsConnectGenericResumptionToken,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
GenericDatagram, TlsConnectGenericResumptionToken,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
INSTANTIATE_TEST_CASE_P(GenericDatagram, TlsConnectTls13ResumptionToken,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(GenericDatagram, TlsConnectTls13ResumptionToken,
TlsConnectTestBase::kTlsVariantsAll);
} // namespace nss_test

View file

@ -0,0 +1,350 @@
/* -*- 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 <memory>
#include "keyhi.h"
#include "pk11pub.h"
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
#include "sslexp.h"
#include "sslproto.h"
#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "scoped_ptrs_ssl.h"
#include "tls_connect.h"
namespace nss_test {
// From tls_hkdf_unittest.cc:
extern size_t GetHashLength(SSLHashType ht);
const std::string kLabel = "sn";
class MaskingTest : public ::testing::Test {
public:
MaskingTest() : slot_(PK11_GetInternalSlot()) {}
void InitSecret(SSLHashType hash_type) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
PK11SymKey *s = PK11_KeyGen(slot_.get(), CKM_GENERIC_SECRET_KEY_GEN,
nullptr, AES_128_KEY_LENGTH, nullptr);
ASSERT_NE(nullptr, s);
secret_.reset(s);
}
void SetUp() override {
InitSecret(ssl_hash_sha256);
PORT_SetError(0);
}
protected:
ScopedPK11SymKey secret_;
ScopedPK11SlotInfo slot_;
// Should have 4B ctr, 12B nonce for ChaCha, or >=16B ciphertext for AES.
// Use the same default size for mask output.
static const int kSampleSize = 16;
static const int kMaskSize = 16;
void CreateMask(PRUint16 ciphersuite, SSLProtocolVariant variant,
std::string label, const std::vector<uint8_t> &sample,
std::vector<uint8_t> *out_mask) {
ASSERT_NE(nullptr, out_mask);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite, variant,
secret_.get(), label.c_str(), label.size(), &ctx_init));
ASSERT_NE(nullptr, ctx_init);
ScopedSSLMaskingContext ctx(ctx_init);
EXPECT_EQ(SECSuccess,
SSL_CreateMask(ctx.get(), sample.data(), sample.size(),
out_mask->data(), out_mask->size()));
bool all_zeros = std::all_of(out_mask->begin(), out_mask->end(),
[](uint8_t v) { return v == 0; });
// If out_mask is short, |all_zeros| will be (expectedly) true often enough
// to fail tests.
// In this case, just retry to make sure we're not outputting zeros
// continuously.
if (all_zeros && out_mask->size() < 3) {
unsigned int tries = 2;
std::vector<uint8_t> tmp_sample = sample;
std::vector<uint8_t> tmp_mask(out_mask->size());
while (tries--) {
tmp_sample.data()[0]++; // Tweak something to get a new mask.
EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), tmp_sample.data(),
tmp_sample.size(), tmp_mask.data(),
tmp_mask.size()));
bool retry_zero = std::all_of(tmp_mask.begin(), tmp_mask.end(),
[](uint8_t v) { return v == 0; });
if (!retry_zero) {
all_zeros = false;
break;
}
}
}
EXPECT_FALSE(all_zeros);
}
};
class SuiteTest : public MaskingTest,
public ::testing::WithParamInterface<uint16_t> {
public:
SuiteTest() : ciphersuite_(GetParam()) {}
void CreateMask(std::string label, const std::vector<uint8_t> &sample,
std::vector<uint8_t> *out_mask) {
MaskingTest::CreateMask(ciphersuite_, ssl_variant_datagram, label, sample,
out_mask);
}
protected:
const uint16_t ciphersuite_;
};
class VariantTest : public MaskingTest,
public ::testing::WithParamInterface<SSLProtocolVariant> {
public:
VariantTest() : variant_(GetParam()) {}
void CreateMask(uint16_t ciphersuite, std::string label,
const std::vector<uint8_t> &sample,
std::vector<uint8_t> *out_mask) {
MaskingTest::CreateMask(ciphersuite, variant_, label, sample, out_mask);
}
protected:
const SSLProtocolVariant variant_;
};
class VariantSuiteTest : public MaskingTest,
public ::testing::WithParamInterface<
std::tuple<SSLProtocolVariant, uint16_t>> {
public:
VariantSuiteTest()
: variant_(std::get<0>(GetParam())),
ciphersuite_(std::get<1>(GetParam())) {}
void CreateMask(std::string label, const std::vector<uint8_t> &sample,
std::vector<uint8_t> *out_mask) {
MaskingTest::CreateMask(ciphersuite_, variant_, label, sample, out_mask);
}
protected:
const SSLProtocolVariant variant_;
const uint16_t ciphersuite_;
};
TEST_P(VariantSuiteTest, MaskContextNoLabel) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(kMaskSize);
CreateMask(std::string(""), sample, &mask);
}
TEST_P(VariantSuiteTest, MaskNoSample) {
std::vector<uint8_t> mask(kMaskSize);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_,
secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init));
ASSERT_NE(nullptr, ctx_init);
ScopedSSLMaskingContext ctx(ctx_init);
EXPECT_EQ(SECFailure,
SSL_CreateMask(ctx.get(), nullptr, 0, mask.data(), mask.size()));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), nullptr, mask.size(),
mask.data(), mask.size()));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_P(VariantSuiteTest, MaskShortSample) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(kMaskSize);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_,
secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init));
ASSERT_NE(nullptr, ctx_init);
ScopedSSLMaskingContext ctx(ctx_init);
EXPECT_EQ(SECFailure,
SSL_CreateMask(ctx.get(), sample.data(), sample.size() - 1,
mask.data(), mask.size()));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_P(VariantSuiteTest, MaskContextUnsupportedMech) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(kMaskSize);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECFailure,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_AES_128_CBC_SHA256,
variant_, secret_.get(), nullptr, 0, &ctx_init));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, ctx_init);
}
TEST_P(VariantSuiteTest, MaskContextUnsupportedVersion) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(kMaskSize);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECFailure, SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_2, ciphersuite_, variant_,
secret_.get(), nullptr, 0, &ctx_init));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, ctx_init);
}
TEST_P(VariantSuiteTest, MaskMaxLength) {
uint32_t max_mask_len = kMaskSize;
if (ciphersuite_ == TLS_CHACHA20_POLY1305_SHA256) {
// Internal limitation for ChaCha20 masks.
max_mask_len = 128;
}
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(max_mask_len + 1);
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_,
secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init));
ASSERT_NE(nullptr, ctx_init);
ScopedSSLMaskingContext ctx(ctx_init);
EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), sample.data(), sample.size(),
mask.data(), mask.size() - 1));
EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), sample.data(), sample.size(),
mask.data(), mask.size()));
EXPECT_EQ(SEC_ERROR_OUTPUT_LEN, PORT_GetError());
}
TEST_P(VariantSuiteTest, MaskMinLength) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask(1); // Don't pass a null
SSLMaskingContext *ctx_init = nullptr;
EXPECT_EQ(SECSuccess,
SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_,
secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init));
ASSERT_NE(nullptr, ctx_init);
ScopedSSLMaskingContext ctx(ctx_init);
EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), sample.data(), sample.size(),
mask.data(), 0));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), sample.data(), sample.size(),
mask.data(), 1));
}
TEST_P(VariantSuiteTest, MaskRotateLabel) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask1(kMaskSize);
std::vector<uint8_t> mask2(kMaskSize);
EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(),
sample.size()));
CreateMask(kLabel, sample, &mask1);
CreateMask(std::string("sn1"), sample, &mask2);
EXPECT_FALSE(mask1 == mask2);
}
TEST_P(VariantSuiteTest, MaskRotateSample) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask1(kMaskSize);
std::vector<uint8_t> mask2(kMaskSize);
EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(),
sample.size()));
CreateMask(kLabel, sample, &mask1);
EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(),
sample.size()));
CreateMask(kLabel, sample, &mask2);
EXPECT_FALSE(mask1 == mask2);
}
TEST_P(VariantSuiteTest, MaskRederive) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> mask1(kMaskSize);
std::vector<uint8_t> mask2(kMaskSize);
SECStatus rv =
PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), sample.size());
EXPECT_EQ(SECSuccess, rv);
// Check that re-using inputs with a new context produces the same mask.
CreateMask(kLabel, sample, &mask1);
CreateMask(kLabel, sample, &mask2);
EXPECT_TRUE(mask1 == mask2);
}
TEST_P(SuiteTest, MaskTlsVariantKeySeparation) {
std::vector<uint8_t> sample(kSampleSize);
std::vector<uint8_t> tls_mask(kMaskSize);
std::vector<uint8_t> dtls_mask(kMaskSize);
SSLMaskingContext *stream_ctx_init = nullptr;
SSLMaskingContext *datagram_ctx_init = nullptr;
// Init
EXPECT_EQ(SECSuccess, SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_,
ssl_variant_stream, secret_.get(), kLabel.c_str(),
kLabel.size(), &stream_ctx_init));
ASSERT_NE(nullptr, stream_ctx_init);
EXPECT_EQ(SECSuccess, SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_,
ssl_variant_datagram, secret_.get(), kLabel.c_str(),
kLabel.size(), &datagram_ctx_init));
ASSERT_NE(nullptr, datagram_ctx_init);
ScopedSSLMaskingContext tls_ctx(stream_ctx_init);
ScopedSSLMaskingContext dtls_ctx(datagram_ctx_init);
// Derive
EXPECT_EQ(SECSuccess,
SSL_CreateMask(tls_ctx.get(), sample.data(), sample.size(),
tls_mask.data(), tls_mask.size()));
EXPECT_EQ(SECSuccess,
SSL_CreateMask(dtls_ctx.get(), sample.data(), sample.size(),
dtls_mask.data(), dtls_mask.size()));
EXPECT_NE(tls_mask, dtls_mask);
}
TEST_P(VariantTest, MaskChaChaRederiveOddSizes) {
// Non-block-aligned.
std::vector<uint8_t> sample(27);
std::vector<uint8_t> mask1(26);
std::vector<uint8_t> mask2(25);
EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(),
sample.size()));
CreateMask(TLS_CHACHA20_POLY1305_SHA256, kLabel, sample, &mask1);
CreateMask(TLS_CHACHA20_POLY1305_SHA256, kLabel, sample, &mask2);
mask1.pop_back();
EXPECT_TRUE(mask1 == mask2);
}
static const uint16_t kMaskingCiphersuites[] = {TLS_CHACHA20_POLY1305_SHA256,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384};
::testing::internal::ParamGenerator<uint16_t> kMaskingCiphersuiteParams =
::testing::ValuesIn(kMaskingCiphersuites);
INSTANTIATE_TEST_SUITE_P(GenericMasking, SuiteTest, kMaskingCiphersuiteParams);
INSTANTIATE_TEST_SUITE_P(GenericMasking, VariantTest,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(GenericMasking, VariantSuiteTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
kMaskingCiphersuiteParams));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -184,12 +185,42 @@ TEST_F(TlsConnectStreamTls13, TooLargeRecord) {
class ShortHeaderChecker : public PacketFilter {
public:
PacketFilter::Action Filter(const DataBuffer& input, DataBuffer* output) {
// The first octet should be 0b001xxxxx.
EXPECT_EQ(1, input.data()[0] >> 5);
// The first octet should be 0b001000xx.
EXPECT_EQ(kCtDtlsCiphertext, (input.data()[0] & ~0x3));
return KEEP;
}
};
TEST_F(TlsConnectDatagram13, AeadLimit) {
Connect();
EXPECT_EQ(SECSuccess, SSLInt_AdvanceDtls13DecryptFailures(server_->ssl_fd(),
(1ULL << 36) - 2));
SendReceive(50);
// Expect this to increment the counter. We should still be able to talk.
client_->SetFilter(std::make_shared<TlsRecordLastByteDamager>(client_));
client_->SendData(10);
server_->ReadBytes(10);
client_->ClearFilter();
client_->ResetSentBytes(50);
SendReceive(60);
// Expect alert when the limit is hit.
client_->SetFilter(std::make_shared<TlsRecordLastByteDamager>(client_));
client_->SendData(10);
ExpectAlert(server_, kTlsAlertBadRecordMac);
// Check the error on both endpoints.
uint8_t buf[10];
PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);
EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, PORT_GetError());
rv = PR_Read(client_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);
EXPECT_EQ(SSL_ERROR_BAD_MAC_ALERT, PORT_GetError());
}
TEST_F(TlsConnectDatagram13, ShortHeadersClient) {
Connect();
client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_TRUE);
@ -204,6 +235,35 @@ TEST_F(TlsConnectDatagram13, ShortHeadersServer) {
SendReceive();
}
// Send a DTLSCiphertext header with a 2B sequence number, and no length.
TEST_F(TlsConnectDatagram13, DtlsAlternateShortHeader) {
StartConnect();
TlsSendCipherSpecCapturer capturer(client_);
Connect();
SendReceive(50);
uint8_t buf[] = {0x32, 0x33, 0x34};
auto spec = capturer.spec(1);
ASSERT_NE(nullptr, spec.get());
ASSERT_EQ(3, spec->epoch());
uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno;
TlsRecordHeader header(variant_, SSL_LIBRARY_VERSION_TLS_1_3, dtls13_ct,
0x0003000000000001);
TlsRecordHeader out_header(header);
DataBuffer msg(buf, sizeof(buf));
msg.Write(msg.len(), ssl_ct_application_data, 1);
DataBuffer ciphertext;
EXPECT_TRUE(spec->Protect(header, msg, &ciphertext, &out_header));
DataBuffer record;
auto rv = out_header.Write(&record, 0, ciphertext);
EXPECT_EQ(out_header.header_length() + ciphertext.len(), rv);
client_->SendDirect(record);
server_->ReadBytes(3);
}
TEST_F(TlsConnectStreamTls13, UnencryptedFinishedMessage) {
StartConnect();
client_->Handshake(); // Send ClientHello
@ -247,6 +307,520 @@ auto kContentSizes = ::testing::ValuesIn(kContentSizesArr);
const static bool kTrueFalseArr[] = {true, false};
auto kTrueFalse = ::testing::ValuesIn(kTrueFalseArr);
INSTANTIATE_TEST_CASE_P(TlsPadding, TlsPaddingTest,
::testing::Combine(kContentSizes, kTrueFalse));
} // namespace nss_test
INSTANTIATE_TEST_SUITE_P(TlsPadding, TlsPaddingTest,
::testing::Combine(kContentSizes, kTrueFalse));
/* Filter to modify record header and content */
class Tls13RecordModifier : public TlsRecordFilter {
public:
Tls13RecordModifier(const std::shared_ptr<TlsAgent>& a,
uint8_t contentType = ssl_ct_handshake, size_t size = 0,
size_t padding = 0)
: TlsRecordFilter(a),
contentType_(contentType),
size_(size),
padding_(padding) {}
protected:
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& record, size_t* offset,
DataBuffer* output) override {
if (!header.is_protected()) {
return KEEP;
}
uint16_t protection_epoch;
uint8_t inner_content_type;
DataBuffer plaintext;
TlsRecordHeader out_header;
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext, &out_header)) {
return KEEP;
}
if (decrypting() && inner_content_type != ssl_ct_application_data) {
return KEEP;
}
DataBuffer ciphertext;
bool ok = Protect(spec(protection_epoch), out_header, contentType_,
DataBuffer(size_), &ciphertext, &out_header, padding_);
EXPECT_TRUE(ok);
if (!ok) {
return KEEP;
}
*offset = out_header.Write(output, *offset, ciphertext);
return CHANGE;
}
private:
uint8_t contentType_;
size_t size_;
size_t padding_;
};
/* Zero-length InnerPlaintext test class
*
* Parameter = Tuple of:
* - TLS variant (datagram/stream)
* - Content type to be set in zero-length inner plaintext record
* - Padding of record plaintext
*/
class ZeroLengthInnerPlaintextSetupTls13
: public TlsConnectTestBase,
public testing::WithParamInterface<
std::tuple<SSLProtocolVariant, SSLContentType, size_t>> {
public:
ZeroLengthInnerPlaintextSetupTls13()
: TlsConnectTestBase(std::get<0>(GetParam()),
SSL_LIBRARY_VERSION_TLS_1_3),
contentType_(std::get<1>(GetParam())),
padding_(std::get<2>(GetParam())){};
protected:
SSLContentType contentType_;
size_t padding_;
};
/* Test correct rejection of TLS 1.3 encrypted handshake/alert records with
* zero-length inner plaintext content length with and without padding.
*
* Implementations MUST NOT send Handshake and Alert records that have a
* zero-length TLSInnerPlaintext.content; if such a message is received,
* the receiving implementation MUST terminate the connection with an
* "unexpected_message" alert [RFC8446, Section 5.4]. */
TEST_P(ZeroLengthInnerPlaintextSetupTls13, ZeroLengthInnerPlaintextRun) {
EnsureTlsSetup();
// Filter modifies record to be zero-length
auto filter =
MakeTlsFilter<Tls13RecordModifier>(client_, contentType_, 0, padding_);
filter->EnableDecryption();
filter->Disable();
Connect();
filter->Enable();
// Record will be overwritten
client_->SendData(0xf);
// Receive corrupt record
if (variant_ == ssl_variant_stream) {
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
// 22B = 16B MAC + 1B innerContentType + 5B Header
server_->ReadBytes(22);
// Process alert at peer
client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
client_->Handshake();
} else { /* DTLS */
size_t received = server_->received_bytes();
// 22B = 16B MAC + 1B innerContentType + 5B Header
server_->ReadBytes(22);
// Check that no bytes were received => packet was dropped
ASSERT_EQ(received, server_->received_bytes());
// Check that we are still connected / not in error state
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
}
}
// Test for TLS and DTLS
const SSLProtocolVariant kZeroLengthInnerPlaintextVariants[] = {
ssl_variant_stream, ssl_variant_datagram};
// Test for handshake and alert fragments
const SSLContentType kZeroLengthInnerPlaintextContentTypes[] = {
ssl_ct_handshake, ssl_ct_alert};
// Test with 0,1 and 100 octets of padding
const size_t kZeroLengthInnerPlaintextPadding[] = {0, 1, 100};
INSTANTIATE_TEST_SUITE_P(
ZeroLengthInnerPlaintextTest, ZeroLengthInnerPlaintextSetupTls13,
testing::Combine(testing::ValuesIn(kZeroLengthInnerPlaintextVariants),
testing::ValuesIn(kZeroLengthInnerPlaintextContentTypes),
testing::ValuesIn(kZeroLengthInnerPlaintextPadding)),
[](const testing::TestParamInfo<
ZeroLengthInnerPlaintextSetupTls13::ParamType>& inf) {
return std::string(std::get<0>(inf.param) == ssl_variant_stream
? "Tls"
: "Dtls") +
"ZeroLengthInnerPlaintext" +
(std::get<1>(inf.param) == ssl_ct_handshake ? "Handshake"
: "Alert") +
(std::get<2>(inf.param)
? "Padding" + std::to_string(std::get<2>(inf.param)) + "B"
: "") +
"Test";
});
/* Zero-length record test class
*
* Parameter = Tuple of:
* - TLS variant (datagram/stream)
* - TLS version
* - Content type to be set in zero-length record
*/
class ZeroLengthRecordSetup
: public TlsConnectTestBase,
public testing::WithParamInterface<
std::tuple<SSLProtocolVariant, uint16_t, SSLContentType>> {
public:
ZeroLengthRecordSetup()
: TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())),
variant_(std::get<0>(GetParam())),
contentType_(std::get<2>(GetParam())){};
void createZeroLengthRecord(DataBuffer& buffer, unsigned epoch = 0,
unsigned seqn = 0) {
size_t idx = 0;
// Set header content type
idx = buffer.Write(idx, contentType_, 1);
// The record version is not checked during record layer handling
idx = buffer.Write(idx, 0xDEAD, 2);
// DTLS (version always < TLS 1.3)
if (variant_ == ssl_variant_datagram) {
// Set epoch (Should be 0 before handshake)
idx = buffer.Write(idx, 0U, 2);
// Set 6B sequence number (0 if send as first message)
idx = buffer.Write(idx, 0U, 2);
idx = buffer.Write(idx, 0U, 4);
}
// Set fragment to be of zero-length
(void)buffer.Write(idx, 0U, 2);
}
protected:
SSLProtocolVariant variant_;
SSLContentType contentType_;
};
/* Test handling of zero-length (ciphertext/fragment) records before handshake.
*
* This is only tested before the first handshake, since after it all of these
* messages are expected to be encrypted which is impossible for a content
* length of zero, always leading to a bad record mac. For TLS 1.3 only
* records of application data content type is legal after the handshake.
*
* Handshake records of length zero will be ignored in the record layer since
* the RFC does only specify that such records MUST NOT be sent but it does not
* state that an alert should be sent or the connection be terminated
* [RFC8446, Section 5.1].
*
* Even though only handshake messages are handled (ignored) in the record
* layer handling, this test covers zero-length records of all content types
* for complete coverage of cases.
*
* !!! Expected TLS (Stream) behavior !!!
* - Handshake records of zero length are ignored.
* - Alert and ChangeCipherSpec records of zero-length lead to illegal
* parameter alerts due to the malformed record content.
* - ApplicationData before the handshake leads to an unexpected message alert.
*
* !!! Expected DTLS (Datagram) behavior !!!
* - Handshake message of zero length are ignored.
* - Alert messages lead to an illegal parameter alert due to malformed record
* content.
* - ChangeCipherSpec records before the first handshake are not expected and
* ignored (see ssl3con.c, line 3276).
* - ApplicationData before the handshake is ignored since it could be a packet
* received in incorrect order (see ssl3con.c, line 13353).
*/
TEST_P(ZeroLengthRecordSetup, ZeroLengthRecordRun) {
EnsureTlsSetup();
// Send zero-length record
DataBuffer buffer;
createZeroLengthRecord(buffer);
client_->SendDirect(buffer);
// This must be set, otherwise handshake completness assertions might fail
server_->StartConnect();
SSLAlertDescription alert = close_notify;
switch (variant_) {
case ssl_variant_datagram:
switch (contentType_) {
case ssl_ct_alert:
// Should actually be ignored, see bug 1829391.
alert = illegal_parameter;
break;
case ssl_ct_ack:
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
// Skipped due to bug 1829391.
GTEST_SKIP();
}
// DTLS versions < 1.3 correctly ignore the invalid record
// so we fall through.
case ssl_ct_change_cipher_spec:
case ssl_ct_application_data:
case ssl_ct_handshake:
server_->Handshake();
Connect();
return;
}
break;
case ssl_variant_stream:
switch (contentType_) {
case ssl_ct_alert:
case ssl_ct_change_cipher_spec:
alert = illegal_parameter;
break;
case ssl_ct_application_data:
case ssl_ct_ack:
alert = unexpected_message;
break;
case ssl_ct_handshake:
// TLS ignores unprotected zero-length handshake records
server_->Handshake();
Connect();
return;
}
break;
}
// Assert alert is send for TLS and DTLS alert records
server_->ExpectSendAlert(alert);
server_->Handshake();
// Consume alert at peer, expect alert for TLS and DTLS alert records
client_->StartConnect();
client_->ExpectReceiveAlert(alert);
client_->Handshake();
}
// Test for handshake, alert, change_cipher_spec and application data fragments
const SSLContentType kZeroLengthRecordContentTypes[] = {
ssl_ct_handshake, ssl_ct_alert, ssl_ct_change_cipher_spec,
ssl_ct_application_data, ssl_ct_ack};
INSTANTIATE_TEST_SUITE_P(
ZeroLengthRecordTest, ZeroLengthRecordSetup,
testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11Plus,
testing::ValuesIn(kZeroLengthRecordContentTypes)),
[](const testing::TestParamInfo<ZeroLengthRecordSetup::ParamType>& inf) {
std::string variant =
(std::get<0>(inf.param) == ssl_variant_stream) ? "Tls" : "Dtls";
std::string version = VersionString(std::get<1>(inf.param));
std::replace(version.begin(), version.end(), '.', '_');
std::string contentType;
switch (std::get<2>(inf.param)) {
case ssl_ct_handshake:
contentType = "Handshake";
break;
case ssl_ct_alert:
contentType = "Alert";
break;
case ssl_ct_application_data:
contentType = "ApplicationData";
break;
case ssl_ct_change_cipher_spec:
contentType = "ChangeCipherSpec";
break;
case ssl_ct_ack:
contentType = "Ack";
break;
}
return variant + version + "ZeroLength" + contentType + "Test";
});
/* Test correct handling of records with invalid content types.
*
* TLS:
* If a TLS implementation receives an unexpected record type, it MUST
* terminate the connection with an "unexpected_message" alert
* [RFC8446, Section 5].
*
* DTLS:
* In general, invalid records SHOULD be silently discarded...
* [RFC6347, Section 4.1.2.7]. */
class UndefinedContentTypeSetup : public TlsConnectGeneric {
public:
UndefinedContentTypeSetup() : TlsConnectGeneric() { StartConnect(); };
void createUndefinedContentTypeRecord(DataBuffer& buffer, unsigned epoch = 0,
unsigned seqn = 0) {
// dummy data
uint8_t data[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
size_t idx = 0;
// Set undefined content type
idx = buffer.Write(idx, 0xFF, 1);
// The record version is not checked during record layer handling
idx = buffer.Write(idx, 0xDEAD, 2);
// DTLS (version always < TLS 1.3)
if (variant_ == ssl_variant_datagram) {
// Set epoch (Should be 0 before/during handshake)
idx = buffer.Write(idx, epoch, 2);
// Set 6B sequence number (0 if send as first message)
idx = buffer.Write(idx, 0U, 2);
idx = buffer.Write(idx, seqn, 4);
}
// Set fragment length
idx = buffer.Write(idx, 5U, 2);
// Add data to record
(void)buffer.Write(idx, data, 5);
}
void checkUndefinedContentTypeHandling(std::shared_ptr<TlsAgent> sender,
std::shared_ptr<TlsAgent> receiver) {
if (variant_ == ssl_variant_stream) {
// Handle record and expect alert to be sent
receiver->ExpectSendAlert(kTlsAlertUnexpectedMessage);
receiver->ReadBytes();
/* Digest and assert that the correct alert was received at peer
*
* The 1.3 server expects all messages other than the ClientHello to be
* encrypted and responds with an unexpected message alert to alerts. */
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3 && sender == server_) {
sender->ExpectSendAlert(kTlsAlertUnexpectedMessage);
} else {
sender->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
}
sender->ReadBytes();
} else { // DTLS drops invalid records silently
size_t received = receiver->received_bytes();
receiver->ReadBytes();
// Ensure no bytes were received/record was dropped
ASSERT_EQ(received, receiver->received_bytes());
}
}
protected:
DataBuffer buffer_;
};
INSTANTIATE_TEST_SUITE_P(
UndefinedContentTypePreHandshakeStream, UndefinedContentTypeSetup,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_SUITE_P(
UndefinedContentTypePreHandshakeDatagram, UndefinedContentTypeSetup,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
TEST_P(UndefinedContentTypeSetup,
ServerReceiveUndefinedContentTypePreClientHello) {
createUndefinedContentTypeRecord(buffer_);
// Send undefined content type record
client_->SendDirect(buffer_);
checkUndefinedContentTypeHandling(client_, server_);
}
TEST_P(UndefinedContentTypeSetup,
ServerReceiveUndefinedContentTypePostClientHello) {
// Set epoch to 0 (handshake), and sequence number to 1 since hello is sent
createUndefinedContentTypeRecord(buffer_, 0, 1);
// Send ClientHello
client_->Handshake();
// Send undefined content type record
client_->SendDirect(buffer_);
checkUndefinedContentTypeHandling(client_, server_);
}
TEST_P(UndefinedContentTypeSetup,
ClientReceiveUndefinedContentTypePreClientHello) {
createUndefinedContentTypeRecord(buffer_);
// Send undefined content type record
server_->SendDirect(buffer_);
checkUndefinedContentTypeHandling(server_, client_);
}
TEST_P(UndefinedContentTypeSetup,
ClientReceiveUndefinedContentTypePostClientHello) {
// Set epoch to 0 (handshake), and sequence number to 1 since hello is sent
createUndefinedContentTypeRecord(buffer_, 0, 1);
// Send ClientHello
client_->Handshake();
// Send undefined content type record
server_->SendDirect(buffer_);
checkUndefinedContentTypeHandling(server_, client_);
}
class RecordOuterContentTypeSetter : public TlsRecordFilter {
public:
RecordOuterContentTypeSetter(const std::shared_ptr<TlsAgent>& a,
uint8_t contentType = ssl_ct_handshake)
: TlsRecordFilter(a), contentType_(contentType) {}
protected:
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& record, size_t* offset,
DataBuffer* output) override {
TlsRecordHeader hdr(header.variant(), header.version(), contentType_,
header.sequence_number());
*offset = hdr.Write(output, *offset, record);
return CHANGE;
}
private:
uint8_t contentType_;
};
/* Test correct handling of invalid inner and outer record content type.
* This is only possible for TLS 1.3, since only for this version decryption
* and encryption of manipulated records is supported by the test suite. */
TEST_P(TlsConnectTls13, UndefinedOuterContentType13) {
EnsureTlsSetup();
Connect();
// Manipulate record: set invalid content type 0xff
MakeTlsFilter<RecordOuterContentTypeSetter>(client_, 0xff);
client_->SendData(50);
if (variant_ == ssl_variant_stream) {
// Handle invalid record
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
server_->ReadBytes();
// Handle alert at peer
client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
client_->ReadBytes();
} else {
// Make sure DTLS drops invalid record silently
size_t received = server_->received_bytes();
server_->ReadBytes();
ASSERT_EQ(received, server_->received_bytes());
}
}
TEST_P(TlsConnectTls13, UndefinedInnerContentType13) {
EnsureTlsSetup();
// Manipulate record: set invalid content type 0xff and length to 50.
auto filter = MakeTlsFilter<Tls13RecordModifier>(client_, 0xff, 50, 0);
filter->EnableDecryption();
filter->Disable();
Connect();
filter->Enable();
// Send manipulate record with invalid content type
client_->SendData(50);
if (variant_ == ssl_variant_stream) {
// Handle invalid record
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
server_->ReadBytes();
// Handle alert at peer
client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
client_->ReadBytes();
} else {
// Make sure DTLS drops invalid record silently
size_t received = server_->received_bytes();
server_->ReadBytes();
ASSERT_EQ(received, server_->received_bytes());
}
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -276,7 +277,8 @@ class StagedRecords {
// Now there should be staged data.
EXPECT_FALSE(data_.empty());
if (g_ssl_gtest_verbose) {
std::cerr << role_ << ": forward " << data_ << std::endl;
std::cerr << role_ << ": forward epoch " << epoch_ << " " << data_
<< std::endl;
}
EXPECT_EQ(SECSuccess,
SSL_RecordLayerData(peer->ssl_fd(), epoch_, content_type_,
@ -364,14 +366,24 @@ TEST_P(TlsConnectStream, ReplaceRecordLayer) {
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
// This processes the ClientHello and stages the first server flight.
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
// In TLS 1.3, this is 0-RTT; in <TLS 1.3, this is application data.
// Neither is acceptable.
RefuseApplicationData(client_, 1);
RefuseApplicationData(server_, 1);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// Application data in handshake is never acceptable.
RefuseApplicationData(client_, 2);
RefuseApplicationData(server_, 2);
// Don't accept real data until the handshake is done.
RefuseApplicationData(client_, 3);
RefuseApplicationData(server_, 3);
// Process the server flight and the client is done.
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
} else {
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
RefuseApplicationData(client_, 1);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
}
@ -382,6 +394,52 @@ TEST_P(TlsConnectStream, ReplaceRecordLayer) {
SendForwardReceive(server_, server_stage, client_);
}
TEST_F(TlsConnectStreamTls13, ReplaceRecordLayerZeroRtt) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
ExpectResumption(RESUME_TICKET);
// Send ClientHello
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
// The client can never accept 0-RTT.
RefuseApplicationData(client_, 1);
// Send some 0-RTT data, which get staged in `client_stage`.
const char* kMsg = "EarlyData";
const PRInt32 kMsgLen = static_cast<PRInt32>(strlen(kMsg));
PRInt32 rv = PR_Write(client_->ssl_fd(), kMsg, kMsgLen);
EXPECT_EQ(kMsgLen, rv);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
// The server should now have 0-RTT to read.
std::vector<uint8_t> buf(kMsgLen);
rv = PR_Read(server_->ssl_fd(), buf.data(), kMsgLen);
EXPECT_EQ(kMsgLen, rv);
// The handshake should happily finish.
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
ExpectEarlyDataAccepted(true);
CheckConnected();
// Reading and writing application data should work.
SendForwardReceive(client_, client_stage, server_);
SendForwardReceive(server_, server_stage, client_);
}
static SECStatus AuthCompleteBlock(TlsAgent*, PRBool, PRBool) {
return SECWouldBlock;
}
@ -573,4 +631,49 @@ TEST_F(TlsConnectDatagram13, ForwardDataDtls) {
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_F(TlsConnectStreamTls13, SuppressEndOfEarlyData) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
server_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true);
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
ExpectResumption(RESUME_TICKET);
// Send ClientHello
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
// Send some 0-RTT data, which get staged in `client_stage`.
const char* kMsg = "ABCDEF";
const PRInt32 kMsgLen = static_cast<PRInt32>(strlen(kMsg));
PRInt32 rv = PR_Write(client_->ssl_fd(), kMsg, kMsgLen);
EXPECT_EQ(kMsgLen, rv);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
// The server should now have 0-RTT to read.
std::vector<uint8_t> buf(kMsgLen);
rv = PR_Read(server_->ssl_fd(), buf.data(), kMsgLen);
EXPECT_EQ(kMsgLen, rv);
// The handshake should happily finish, without the end of the early data.
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
ExpectEarlyDataAccepted(true);
CheckConnected();
// Reading and writing application data should work.
SendForwardReceive(client_, client_stage, server_);
SendForwardReceive(server_, server_stage, client_);
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -18,7 +19,8 @@ namespace nss_test {
// This class tracks the maximum size of record that was sent, both cleartext
// and plain. It only tracks records that have an outer type of
// application_data. In TLS 1.3, this includes handshake messages.
// application_data or DTLSCiphertext. In TLS 1.3, this includes handshake
// messages.
class TlsRecordMaximum : public TlsRecordFilter {
public:
TlsRecordMaximum(const std::shared_ptr<TlsAgent>& a)
@ -33,7 +35,7 @@ class TlsRecordMaximum : public TlsRecordFilter {
DataBuffer* output) override {
std::cerr << "max: " << record << std::endl;
// Ignore unprotected packets.
if (header.content_type() != ssl_ct_application_data) {
if (!header.is_protected()) {
return KEEP;
}
@ -194,9 +196,23 @@ class TlsRecordExpander : public TlsRecordFilter {
virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& data,
DataBuffer* changed) {
if (header.content_type() != ssl_ct_application_data) {
return KEEP;
if (!header.is_protected()) {
// We're targeting application_data records. If the record is
// |!is_protected()|, we have two possibilities:
if (!decrypting()) {
// 1) We're not decrypting, in which this case this is truly an
// unencrypted record (Keep).
return KEEP;
}
if (header.content_type() != ssl_ct_application_data) {
// 2) We are decrypting, so is_protected() read the internal
// content_type. If the internal ct IS NOT application_data, then
// it's not our target (Keep).
return KEEP;
}
// Otherwise, the the internal ct IS application_data (Change).
}
changed->Allocate(data.len() + expansion_);
changed->Write(0, data.data(), data.len());
return CHANGE;
@ -207,7 +223,7 @@ class TlsRecordExpander : public TlsRecordFilter {
};
// Tweak the plaintext of server records so that they exceed the client's limit.
TEST_P(TlsConnectTls13, RecordSizePlaintextExceed) {
TEST_F(TlsConnectStreamTls13, RecordSizePlaintextExceed) {
EnsureTlsSetup();
auto server_expand = MakeTlsFilter<TlsRecordExpander>(server_, 1);
server_expand->EnableDecryption();
@ -231,7 +247,7 @@ TEST_P(TlsConnectTls13, RecordSizePlaintextExceed) {
// This requires a much larger expansion than for plaintext to trigger the
// guard, which runs before decryption (current allowance is 320 octets,
// see MAX_EXPANSION in ssl3con.c).
TEST_P(TlsConnectTls13, RecordSizeCiphertextExceed) {
TEST_F(TlsConnectStreamTls13, RecordSizeCiphertextExceed) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
@ -250,6 +266,27 @@ TEST_P(TlsConnectTls13, RecordSizeCiphertextExceed) {
server_->CheckErrorCode(SSL_ERROR_RECORD_OVERFLOW_ALERT);
}
TEST_F(TlsConnectStreamTls13, ClientHelloF5Padding) {
EnsureTlsSetup();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(
PK11_KeyGen(slot.get(), CKM_NSS_CHACHA20_POLY1305, nullptr, 32, nullptr));
auto filter =
MakeTlsFilter<TlsHandshakeRecorder>(client_, kTlsHandshakeClientHello);
// Add PSK with label long enough to push CH length into [256, 511].
std::vector<uint8_t> label(100);
EXPECT_EQ(SECSuccess,
SSL_AddExternalPsk(client_->ssl_fd(), key.get(), label.data(),
label.size(), ssl_hash_sha256));
StartConnect();
client_->Handshake();
// Filter removes the 4B handshake header.
EXPECT_EQ(508UL, filter->buffer().len());
}
// This indiscriminately adds padding to application data records.
class TlsRecordPadder : public TlsRecordFilter {
public:
@ -260,30 +297,31 @@ class TlsRecordPadder : public TlsRecordFilter {
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& record, size_t* offset,
DataBuffer* output) override {
if (header.content_type() != ssl_ct_application_data) {
if (!header.is_protected()) {
return KEEP;
}
uint16_t protection_epoch;
uint8_t inner_content_type;
DataBuffer plaintext;
TlsRecordHeader out_header;
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext)) {
&plaintext, &out_header)) {
return KEEP;
}
if (inner_content_type != ssl_ct_application_data) {
if (decrypting() && inner_content_type != ssl_ct_application_data) {
return KEEP;
}
DataBuffer ciphertext;
bool ok = Protect(spec(protection_epoch), header, inner_content_type,
plaintext, &ciphertext, padding_);
bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
plaintext, &ciphertext, &out_header, padding_);
EXPECT_TRUE(ok);
if (!ok) {
return KEEP;
}
*offset = header.Write(output, *offset, ciphertext);
*offset = out_header.Write(output, *offset, ciphertext);
return CHANGE;
}
@ -291,7 +329,7 @@ class TlsRecordPadder : public TlsRecordFilter {
size_t padding_;
};
TEST_P(TlsConnectTls13, RecordSizeExceedPad) {
TEST_F(TlsConnectStreamTls13, RecordSizeExceedPad) {
EnsureTlsSetup();
auto server_max = std::make_shared<TlsRecordMaximum>(server_);
auto server_expand = std::make_shared<TlsRecordPadder>(server_, 1);
@ -456,4 +494,233 @@ TEST_F(RecordSizeDefaultsTest, RecordSizeGetValue) {
EXPECT_EQ(3000, v);
}
} // namespace nss_test
class TlsCtextResizer : public TlsRecordFilter {
public:
TlsCtextResizer(const std::shared_ptr<TlsAgent>& a, size_t size)
: TlsRecordFilter(a), size_(size) {}
protected:
virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& data,
DataBuffer* changed) {
// allocate and initialise buffer
changed->Allocate(size_);
// copy record data (partially)
changed->Write(0, data.data(),
((data.len() >= size_) ? size_ : data.len()));
return CHANGE;
}
private:
size_t size_;
};
/* (D)TLS overlong record test for maximum default record size of
* 2^14 + (256 (TLS 1.3) OR 2048 (TLS <= 1.2)
* [RFC8446, Section 5.2; RFC5246 , Section 6.2.3].
* This should fail the first size check in ssl3gthr.c/ssl3_GatherData().
* DTLS Record errors are dropped silently. [RFC6347, Section 4.1.2.7]. */
TEST_P(TlsConnectGeneric, RecordGatherOverlong) {
EnsureTlsSetup();
size_t max_ctext = MAX_FRAGMENT_LENGTH;
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
max_ctext += TLS_1_3_MAX_EXPANSION;
} else {
max_ctext += TLS_1_2_MAX_EXPANSION;
}
Connect();
MakeTlsFilter<TlsCtextResizer>(server_, max_ctext + 1);
// Dummy record will be overwritten
server_->SendData(0xf0);
/* Drop DTLS Record Errors silently [RFC6347, Section 4.1.2.7]. */
if (variant_ == ssl_variant_datagram) {
size_t received = client_->received_bytes();
client_->ReadBytes(max_ctext + 1);
ASSERT_EQ(received, client_->received_bytes());
} else {
client_->ExpectSendAlert(kTlsAlertRecordOverflow);
client_->ReadBytes(max_ctext + 1);
server_->ExpectReceiveAlert(kTlsAlertRecordOverflow);
server_->Handshake();
}
}
/* (D)TLS overlong record test with recordSizeLimit Extension and plus RFC
* specified maximum Expansion: 2^14 + (256 (TLS 1.3) OR 2048 (TLS <= 1.2)
* [RFC8446, Section 5.2; RFC5246 , Section 6.2.3].
* DTLS Record errors are dropped silently. [RFC6347, Section 4.1.2.7]. */
TEST_P(TlsConnectGeneric, RecordSizeExtensionOverlong) {
EnsureTlsSetup();
// Set some boundary
size_t max_ctext = 1000;
client_->SetOption(SSL_RECORD_SIZE_LIMIT, max_ctext);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// The record size limit includes the inner content type byte
max_ctext += TLS_1_3_MAX_EXPANSION - 1;
} else {
max_ctext += TLS_1_2_MAX_EXPANSION;
}
Connect();
MakeTlsFilter<TlsCtextResizer>(server_, max_ctext + 1);
// Dummy record will be overwritten
server_->SendData(0xf);
/* Drop DTLS Record Errors silently [RFC6347, Section 4.1.2.7].
* For DTLS 1.0 and 1.2 the package is dropped before the size check because
* of the modification. This just tests that no error is thrown as required.
*/
if (variant_ == ssl_variant_datagram) {
size_t received = client_->received_bytes();
client_->ReadBytes(max_ctext + 1);
ASSERT_EQ(received, client_->received_bytes());
} else {
client_->ExpectSendAlert(kTlsAlertRecordOverflow);
client_->ReadBytes(max_ctext + 1);
server_->ExpectReceiveAlert(kTlsAlertRecordOverflow);
server_->Handshake();
}
}
/* For TLS <= 1.2:
* MAX_EXPANSION is the amount by which a record might plausibly be expanded
* when protected. It's the worst case estimate, so the sum of block cipher
* padding (up to 256 octets), HMAC (48 octets for SHA-384), and IV (16
* octets for AES). */
#define MAX_EXPANSION (256 + 48 + 16)
/* (D)TLS overlong record test for specific ciphersuite expansion.
* Testing the smallest illegal record.
* This check is performed in ssl3con.c/ssl3_UnprotectRecord() OR
* tls13con.c/tls13_UnprotectRecord() and enforces stricter size limitations,
* dependent on the implemented cipher suites, than the RFC.
* DTLS Record errors are dropped silently. [RFC6347, Section 4.1.2.7]. */
TEST_P(TlsConnectGeneric, RecordExpansionOverlong) {
EnsureTlsSetup();
// Set some boundary
size_t max_ctext = 1000;
client_->SetOption(SSL_RECORD_SIZE_LIMIT, max_ctext);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// For TLS1.3 all ciphers expand the cipherext by 16B
// The inner content type byte is included in the record size limit
max_ctext += 16;
} else {
// For TLS<=1.2 the max possible expansion in the NSS implementation is 320
max_ctext += MAX_EXPANSION;
}
Connect();
MakeTlsFilter<TlsCtextResizer>(server_, max_ctext + 1);
// Dummy record will be overwritten
server_->SendData(0xf);
/* Drop DTLS Record Errors silently [RFC6347, Section 4.1.2.7].
* For DTLS 1.0 and 1.2 the package is dropped before the size check because
* of the modification. This just tests that no error is thrown as required/
* no bytes are received. */
if (variant_ == ssl_variant_datagram) {
size_t received = client_->received_bytes();
client_->ReadBytes(max_ctext + 1);
ASSERT_EQ(received, client_->received_bytes());
} else {
client_->ExpectSendAlert(kTlsAlertRecordOverflow);
client_->ReadBytes(max_ctext + 1);
server_->ExpectReceiveAlert(kTlsAlertRecordOverflow);
server_->Handshake();
}
}
/* (D)TLS longest allowed record default size test. */
TEST_P(TlsConnectGeneric, RecordSizeDefaultLong) {
EnsureTlsSetup();
Connect();
// Maximum allowed plaintext size
size_t max = MAX_FRAGMENT_LENGTH;
/* For TLS 1.0 the first byte of application data is sent in a single record
* as explained in the documentation of SSL_CBC_RANDOM_IV in ssl.h.
* Because of that we use TlsCTextResizer to send a record of max size.
* A bad record mac alert is expected since we modify the record. */
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0 &&
variant_ == ssl_variant_stream) {
// Set size to maxi plaintext + max allowed expansion
MakeTlsFilter<TlsCtextResizer>(server_, max + MAX_EXPANSION);
// Dummy record will be overwritten
server_->SendData(0xF);
// Expect alert
client_->ExpectSendAlert(kTlsAlertBadRecordMac);
// Receive record
client_->ReadBytes(max);
// Handle alert on server side
server_->ExpectReceiveAlert(kTlsAlertBadRecordMac);
server_->Handshake();
} else { // Everything but TLS 1.0
// Send largest legal plaintext as single record
// by setting SendData() block size to max.
server_->SendData(max, max);
// Receive record
client_->ReadBytes(max);
// Assert that data was received successfully
ASSERT_EQ(client_->received_bytes(), max);
}
}
/* (D)TLS longest allowed record size limit extension test. */
TEST_P(TlsConnectGeneric, RecordSizeLimitLong) {
EnsureTlsSetup();
// Set some boundary
size_t max = 1000;
client_->SetOption(SSL_RECORD_SIZE_LIMIT, max);
Connect();
// For TLS 1.3 the InnerContentType byte is included in the record size limit
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
max--;
}
/* For TLS 1.0 the first byte of application data is sent in a single record
* as explained in the documentation of SSL_CBC_RANDOM_IV in ssl.h.
* Because of that we use TlsCTextResizer to send a record of max size.
* A bad record mac alert is expected since we modify the record. */
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0 &&
variant_ == ssl_variant_stream) {
// Set size to maxi plaintext + max allowed expansion
MakeTlsFilter<TlsCtextResizer>(server_, max + MAX_EXPANSION);
// Dummy record will be overwritten
server_->SendData(0xF);
// Expect alert
client_->ExpectSendAlert(kTlsAlertBadRecordMac);
// Receive record
client_->ReadBytes(max);
// Handle alert on server side
server_->ExpectReceiveAlert(kTlsAlertBadRecordMac);
server_->Handshake();
} else { // Everything but TLS 1.0
// Send largest legal plaintext as single record
// by setting SendData() block size to max.
server_->SendData(max, max);
// Receive record
client_->ReadBytes(max);
// Assert that data was received successfully
ASSERT_EQ(client_->received_bytes(), max);
}
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -69,7 +70,7 @@ TEST_F(TlsConnectTest, RenegotiationConfigTls13) {
TEST_P(TlsConnectStream, ConnectTls10AndServerRenegotiateHigher) {
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
return;
GTEST_SKIP();
}
// Set the client so it will accept any version from 1.0
// to |version_|.
@ -109,7 +110,7 @@ TEST_P(TlsConnectStream, ConnectTls10AndServerRenegotiateHigher) {
TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
return;
GTEST_SKIP();
}
// Set the client so it will accept any version from 1.0
// to |version_|.
@ -147,7 +148,7 @@ TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
TEST_P(TlsConnectStream, ConnectAndServerRenegotiateLower) {
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
return;
GTEST_SKIP();
}
Connect();
@ -180,7 +181,7 @@ TEST_P(TlsConnectStream, ConnectAndServerRenegotiateLower) {
TEST_P(TlsConnectStream, ConnectAndServerWontRenegotiateLower) {
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
return;
GTEST_SKIP();
}
Connect();
@ -199,7 +200,7 @@ TEST_P(TlsConnectStream, ConnectAndServerWontRenegotiateLower) {
TEST_P(TlsConnectStream, ConnectAndClientWontRenegotiateLower) {
if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
return;
GTEST_SKIP();
}
Connect();

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -456,10 +457,10 @@ TEST_P(TlsConnectGeneric, ServerSNICertTypeSwitch) {
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
}
// Prior to TLS 1.3, we were not fully ephemeral; though 1.3 fixes that
TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) {
auto filter = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
EnableECDHEServerKeyReuse();
Connect();
CheckKeys();
TlsServerKeyExchangeEcdhe dhe1;
@ -467,6 +468,7 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) {
// Restart
Reset();
EnableECDHEServerKeyReuse();
auto filter2 = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
@ -484,7 +486,6 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) {
// This test parses the ServerKeyExchange, which isn't in 1.3
TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) {
server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
auto filter = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
Connect();
@ -494,7 +495,6 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) {
// Restart
Reset();
server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
auto filter2 = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
@ -736,7 +736,7 @@ TEST_P(TlsConnectGenericPre13, TestResumptionOverrideVersion) {
if (variant_ == ssl_variant_stream) {
switch (version_) {
case SSL_LIBRARY_VERSION_TLS_1_0:
return; // Skip the test.
GTEST_SKIP();
case SSL_LIBRARY_VERSION_TLS_1_1:
override_version = SSL_LIBRARY_VERSION_TLS_1_0;
break;
@ -751,7 +751,7 @@ TEST_P(TlsConnectGenericPre13, TestResumptionOverrideVersion) {
override_version = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
} else {
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, version_);
return; // Skip the test.
GTEST_SKIP();
}
}
@ -836,7 +836,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) {
Connect();
// Clear the session ticket keys to invalidate the old ticket.
SSLInt_ClearSelfEncryptKey();
ClearServerCache();
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
SendReceive(); // Need to read so that we absorb the session tickets.
@ -884,7 +884,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNSTWithToken) {
Connect();
// Clear the session ticket keys to invalidate the old ticket.
SSLInt_ClearSelfEncryptKey();
ClearServerCache();
nst_capture->Reset();
uint8_t token[] = {0x20, 0x20, 0xff, 0x00};
EXPECT_EQ(SECSuccess,
@ -914,8 +914,7 @@ TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
server_->SetOption(SSL_ENABLE_SESSION_TICKETS, PR_FALSE);
auto nst_capture =
MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket);
@ -943,6 +942,50 @@ TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) {
NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
}
// Successfully send a session ticket after resuming and then use it.
TEST_F(TlsConnectTest, SendTicketAfterResumption) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
Connect();
SendReceive(); // Need to read so that we absorb the session tickets.
CheckKeys();
// Resume the connection.
Reset();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
ExpectResumption(RESUME_TICKET);
// We need to capture just one ticket, so
// disable automatic sending of tickets at the server.
// ConfigureSessionCache enables this option, so revert that.
server_->SetOption(SSL_ENABLE_SESSION_TICKETS, PR_FALSE);
auto nst_capture =
MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket);
nst_capture->EnableDecryption();
Connect();
ClearServerCache();
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
SendReceive();
// Reset stats so that the counters for resumptions match up.
ClearStats();
// Resume again and ensure that we get the same ticket.
Reset();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
ExpectResumption(RESUME_TICKET);
auto psk_capture =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn);
Connect();
SendReceive();
NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
}
// Test calling SSL_SendSessionTicket in inappropriate conditions.
TEST_F(TlsConnectTest, SendSessionTicketInappropriate) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -233,13 +234,13 @@ TEST_P(Tls13SkipTest, SkipClientCertificateVerify) {
SSL_ERROR_RX_UNEXPECTED_FINISHED);
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
SkipTls10, TlsSkipTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10));
INSTANTIATE_TEST_CASE_P(SkipVariants, TlsSkipTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(Skip13Variants, Tls13SkipTest,
TlsConnectTestBase::kTlsVariantsAll);
INSTANTIATE_TEST_SUITE_P(SkipVariants, TlsSkipTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_SUITE_P(Skip13Variants, Tls13SkipTest,
TlsConnectTestBase::kTlsVariantsAll);
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -213,6 +214,29 @@ TEST_F(Tls13CompatTest, EnabledHrrZeroRtt) {
CheckForCompatHandshake();
}
TEST_F(Tls13CompatTest, EnabledAcceptedEch) {
EnsureTlsSetup();
SetupEch(client_, server_);
EnableCompatMode();
InstallFilters();
Connect();
CheckForCompatHandshake();
}
TEST_F(Tls13CompatTest, EnabledRejectedEch) {
EnsureTlsSetup();
// Configure ECH on the client only, and expect CCS.
SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
EnableCompatMode();
InstallFilters();
ExpectAlert(client_, kTlsAlertEchRequired);
ConnectExpectFailOneSide(TlsAgent::CLIENT);
client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
CheckForCompatHandshake();
// Reset expectations for the TlsAgent dtor.
server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning);
}
class TlsSessionIDEchoFilter : public TlsHandshakeFilter {
public:
TlsSessionIDEchoFilter(const std::shared_ptr<TlsAgent>& a)
@ -462,14 +486,16 @@ TEST_F(TlsConnectDatagram13, CompatModeDtlsClient) {
ASSERT_EQ(2U, client_records->count()); // CH, Fin
EXPECT_EQ(ssl_ct_handshake, client_records->record(0).header.content_type());
EXPECT_EQ(ssl_ct_application_data,
client_records->record(1).header.content_type());
EXPECT_EQ(kCtDtlsCiphertext,
(client_records->record(1).header.content_type() &
kCtDtlsCiphertextMask));
ASSERT_EQ(6U, server_records->count()); // SH, EE, CT, CV, Fin, Ack
EXPECT_EQ(ssl_ct_handshake, server_records->record(0).header.content_type());
for (size_t i = 1; i < server_records->count(); ++i) {
EXPECT_EQ(ssl_ct_application_data,
server_records->record(i).header.content_type());
EXPECT_EQ(kCtDtlsCiphertext,
(server_records->record(i).header.content_type() &
kCtDtlsCiphertextMask));
}
}
@ -518,8 +544,9 @@ TEST_F(TlsConnectDatagram13, CompatModeDtlsServer) {
ASSERT_EQ(5U, server_records->count()); // SH, EE, CT, CV, Fin
EXPECT_EQ(ssl_ct_handshake, server_records->record(0).header.content_type());
for (size_t i = 1; i < server_records->count(); ++i) {
EXPECT_EQ(ssl_ct_application_data,
server_records->record(i).header.content_type());
EXPECT_EQ(kCtDtlsCiphertext,
(server_records->record(i).header.content_type() &
kCtDtlsCiphertextMask));
}
uint32_t session_id_len = 0;

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -213,7 +214,7 @@ TEST_P(SSLv2ClientHelloTest, ConnectDisabled) {
// But to be certain, feed in more data to see if an error comes out.
uint8_t zeros[SSL_LIBRARY_VERSION_TLS_1_2] = {0};
client_->SendDirect(DataBuffer(zeros, sizeof(zeros)));
ExpectAlert(server_, kTlsAlertIllegalParameter);
ExpectAlert(server_, kTlsAlertUnexpectedMessage);
server_->Handshake();
client_->Handshake();
}
@ -236,8 +237,8 @@ TEST_P(SSLv2ClientHelloTest, ConnectAfterEmptyV3Record) {
// as the record length.
SetPadding(255);
ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
EXPECT_EQ(SSL_ERROR_BAD_CLIENT, server_->error_code());
ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, server_->error_code());
}
// Test negotiating TLS 1.3.
@ -276,7 +277,7 @@ TEST_P(SSLv2ClientHelloTest, SendSecurityEscape) {
// Set a big padding so that the server fails instead of timing out.
SetPadding(255);
ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
}
// Invalid SSLv2 client hello padding must fail the handshake.
@ -405,9 +406,9 @@ TEST_F(SSLv2ClientHelloTestF, InappropriateFallbackSCSV) {
EXPECT_EQ(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, server_->error_code());
}
INSTANTIATE_TEST_CASE_P(VersionsStream10Pre13, SSLv2ClientHelloTest,
TlsConnectTestBase::kTlsV10);
INSTANTIATE_TEST_CASE_P(VersionsStreamPre13, SSLv2ClientHelloTest,
TlsConnectTestBase::kTlsV11V12);
INSTANTIATE_TEST_SUITE_P(VersionsStream10Pre13, SSLv2ClientHelloTest,
TlsConnectTestBase::kTlsV10);
INSTANTIATE_TEST_SUITE_P(VersionsStreamPre13, SSLv2ClientHelloTest,
TlsConnectTestBase::kTlsV11V12);
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -27,7 +28,7 @@ TEST_P(TlsConnectStream, ServerNegotiateTls10) {
}
TEST_P(TlsConnectGeneric, ServerNegotiateTls11) {
if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) return;
if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) GTEST_SKIP();
uint16_t minver, maxver;
client_->GetVersionRange(&minver, &maxver);
@ -38,7 +39,7 @@ TEST_P(TlsConnectGeneric, ServerNegotiateTls11) {
}
TEST_P(TlsConnectGeneric, ServerNegotiateTls12) {
if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) return;
if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) GTEST_SKIP();
uint16_t minver, maxver;
client_->GetVersionRange(&minver, &maxver);
@ -59,8 +60,8 @@ TEST_F(TlsConnectTest, TestDowngradeDetectionToTls11) {
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_2);
client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
MakeTlsFilter<TlsClientHelloVersionSetter>(client_,
SSL_LIBRARY_VERSION_TLS_1_1);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
SSL_LIBRARY_VERSION_TLS_1_1);
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@ -68,11 +69,10 @@ TEST_F(TlsConnectTest, TestDowngradeDetectionToTls11) {
// Attempt to negotiate the bogus DTLS 1.1 version.
TEST_F(DtlsConnectTest, TestDtlsVersion11) {
MakeTlsFilter<TlsClientHelloVersionSetter>(client_, ((~0x0101) & 0xffff));
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
// It's kind of surprising that SSL_ERROR_NO_CYPHER_OVERLAP is
// what is returned here, but this is deliberate in ssl3_HandleAlert().
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
((~0x0101) & 0xffff));
ConnectExpectAlert(server_, kTlsAlertProtocolVersion);
client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
}
@ -128,7 +128,7 @@ TEST_P(TlsDowngradeTest, TlsDowngradeSentinelTest) {
static const size_t kRandomLen = 32;
if (c_ver > s_ver) {
return;
GTEST_SKIP();
}
client_->SetVersionRange(c_ver, c_ver);
@ -161,8 +161,8 @@ TEST_P(TlsDowngradeTest, TlsDowngradeSentinelTest) {
TEST_F(TlsConnectTest, TestDowngradeDetectionToTls10) {
// Setting the option here has no effect.
client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
MakeTlsFilter<TlsClientHelloVersionSetter>(client_,
SSL_LIBRARY_VERSION_TLS_1_0);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
SSL_LIBRARY_VERSION_TLS_1_0);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_1);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
@ -203,6 +203,7 @@ TEST_F(TlsConnectTest, DisableFalseStartOnFallback) {
SSL_SetCanFalseStartCallback(client_->ssl_fd(), AllowFalseStart,
&false_start_attempted));
client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_FALSE);
client_->SetDowngradeCheckVersion(SSL_LIBRARY_VERSION_TLS_1_3);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
@ -275,8 +276,8 @@ class Tls13NoSupportedVersions : public TlsConnectStreamTls12 {
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, max_server_version);
MakeTlsFilter<TlsClientHelloVersionSetter>(client_,
overwritten_client_version);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
overwritten_client_version);
auto capture =
MakeTlsFilter<TlsHandshakeRecorder>(server_, kTlsHandshakeServerHello);
ConnectExpectAlert(server_, kTlsAlertDecryptError);
@ -310,8 +311,8 @@ TEST_F(Tls13NoSupportedVersions,
// Offer 1.3 but with ClientHello.legacy_version == TLS 1.4. This
// causes a bad MAC error when we read EncryptedExtensions.
TEST_F(TlsConnectStreamTls13, Tls14ClientHelloWithSupportedVersions) {
MakeTlsFilter<TlsClientHelloVersionSetter>(client_,
SSL_LIBRARY_VERSION_TLS_1_3 + 1);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
SSL_LIBRARY_VERSION_TLS_1_3 + 1);
auto capture = MakeTlsFilter<TlsExtensionCapture>(
server_, ssl_tls13_supported_versions_xtn);
client_->ExpectSendAlert(kTlsAlertBadRecordMac);
@ -330,12 +331,14 @@ TEST_F(TlsConnectStreamTls13, Tls14ClientHelloWithSupportedVersions) {
// Offer 1.3 but with Server/ClientHello.legacy_version == SSL 3.0. This
// causes a protocol version alert. See RFC 8446 Appendix D.5.
TEST_F(TlsConnectStreamTls13, Ssl30ClientHelloWithSupportedVersions) {
MakeTlsFilter<TlsClientHelloVersionSetter>(client_, SSL_LIBRARY_VERSION_3_0);
MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
SSL_LIBRARY_VERSION_3_0);
ConnectExpectAlert(server_, kTlsAlertProtocolVersion);
}
TEST_F(TlsConnectStreamTls13, Ssl30ServerHelloWithSupportedVersions) {
MakeTlsFilter<TlsServerHelloVersionSetter>(server_, SSL_LIBRARY_VERSION_3_0);
MakeTlsFilter<TlsMessageVersionSetter>(server_, kTlsHandshakeServerHello,
SSL_LIBRARY_VERSION_3_0);
StartConnect();
client_->ExpectSendAlert(kTlsAlertProtocolVersion);
/* Since the handshake is not finished the client will send an unencrypted
@ -345,7 +348,106 @@ TEST_F(TlsConnectStreamTls13, Ssl30ServerHelloWithSupportedVersions) {
Handshake();
}
INSTANTIATE_TEST_CASE_P(
// Verify the client sends only DTLS versions in supported_versions
TEST_F(DtlsConnectTest, DtlsSupportedVersionsEncoding) {
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
auto capture = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_supported_versions_xtn);
Connect();
ASSERT_EQ(7U, capture->extension().len());
uint32_t version = 0;
ASSERT_TRUE(capture->extension().Read(1, 2, &version));
EXPECT_EQ(0x7f00 | DTLS_1_3_DRAFT_VERSION, static_cast<int>(version));
ASSERT_TRUE(capture->extension().Read(3, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, static_cast<int>(version));
ASSERT_TRUE(capture->extension().Read(5, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_DTLS_1_0_WIRE, static_cast<int>(version));
}
// Verify the DTLS 1.3 supported_versions interop workaround.
TEST_F(DtlsConnectTest, Dtls13VersionWorkaround) {
static const uint16_t kExpectVersionsWorkaround[] = {
0x7f00 | DTLS_1_3_DRAFT_VERSION, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE,
SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_DTLS_1_0_WIRE,
SSL_LIBRARY_VERSION_TLS_1_1};
const int min_ver = SSL_LIBRARY_VERSION_TLS_1_1,
max_ver = SSL_LIBRARY_VERSION_TLS_1_3;
// Toggle the workaround, then verify both encodings are present.
EnsureTlsSetup();
SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE);
SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_FALSE);
SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE);
client_->SetVersionRange(min_ver, max_ver);
server_->SetVersionRange(min_ver, max_ver);
auto capture = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_supported_versions_xtn);
Connect();
uint32_t version = 0;
size_t off = 1;
ASSERT_EQ(1 + sizeof(kExpectVersionsWorkaround), capture->extension().len());
for (unsigned int i = 0; i < PR_ARRAY_SIZE(kExpectVersionsWorkaround); i++) {
ASSERT_TRUE(capture->extension().Read(off, 2, &version));
EXPECT_EQ(kExpectVersionsWorkaround[i], static_cast<uint16_t>(version));
off += 2;
}
}
// Verify the client sends only TLS versions in supported_versions
TEST_F(TlsConnectTest, TlsSupportedVersionsEncoding) {
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
SSL_LIBRARY_VERSION_TLS_1_3);
auto capture = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_supported_versions_xtn);
Connect();
ASSERT_EQ(9U, capture->extension().len());
uint32_t version = 0;
ASSERT_TRUE(capture->extension().Read(1, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_3, static_cast<int>(version));
ASSERT_TRUE(capture->extension().Read(3, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<int>(version));
ASSERT_TRUE(capture->extension().Read(5, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, static_cast<int>(version));
ASSERT_TRUE(capture->extension().Read(7, 2, &version));
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, static_cast<int>(version));
}
/* Test that on reception of unsupported ClientHello.legacy_version the TLS 1.3
* server sends the correct alert.
*
* If the "supported_versions" extension is absent and the server only supports
* versions greater than ClientHello.legacy_version, the server MUST abort the
* handshake with a "protocol_version" alert [RFC8446, Appendix D.2]. */
TEST_P(TlsConnectGenericPre13, ClientHelloUnsupportedTlsVersion) {
StartConnect();
if (variant_ == ssl_variant_stream) {
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
SSL_LIBRARY_VERSION_TLS_1_3);
} else {
server_->SetVersionRange(SSL_LIBRARY_VERSION_DTLS_1_3,
SSL_LIBRARY_VERSION_DTLS_1_3);
}
// Try to handshake
client_->Handshake();
// Expect protocol version alert
server_->ExpectSendAlert(kTlsAlertProtocolVersion);
server_->Handshake();
// Digest alert at peer
client_->ExpectReceiveAlert(kTlsAlertProtocolVersion);
client_->ReadBytes();
}
INSTANTIATE_TEST_SUITE_P(
TlsDowngradeSentinelTest, TlsDowngradeTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll,

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -375,9 +376,10 @@ TEST_P(TestPolicyVersionRange, TestAllTLSVersionsAndPolicyCombinations) {
Connect();
}
INSTANTIATE_TEST_CASE_P(TLSVersionRanges, TestPolicyVersionRange,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
kExpandedVersions, kExpandedVersions,
kExpandedVersions,
kExpandedVersions));
INSTANTIATE_TEST_SUITE_P(TLSVersionRanges, TestPolicyVersionRange,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
kExpandedVersions,
kExpandedVersions,
kExpandedVersions,
kExpandedVersions));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -109,7 +110,6 @@ int32_t DummyPrSocket::Recv(PRFileDesc *f, void *buf, int32_t buflen,
auto &front = input_.front();
if (static_cast<size_t>(buflen) < front.len()) {
PR_ASSERT(false);
PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
return -1;
}

View file

@ -1,4 +1,5 @@
/* -*- 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/. */

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -48,6 +49,7 @@ const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa";
const std::string TlsAgent::kServerDsa = "dsa";
const std::string TlsAgent::kDelegatorEcdsa256 = "delegator_ecdsa256";
const std::string TlsAgent::kDelegatorRsae2048 = "delegator_rsae2048";
const std::string TlsAgent::kDelegatorRsaPss2048 = "delegator_rsa_pss2048";
static const uint8_t kCannedTls13ServerHello[] = {
0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
@ -71,8 +73,9 @@ TlsAgent::TlsAgent(const std::string& nm, Role rl, SSLProtocolVariant var)
falsestart_enabled_(false),
expected_version_(0),
expected_cipher_suite_(0),
expect_resumption_(false),
expect_client_auth_(false),
expect_ech_(false),
expect_psk_(ssl_psk_none),
can_falsestart_hook_called_(false),
sni_hook_called_(false),
auth_certificate_hook_called_(false),
@ -90,7 +93,8 @@ TlsAgent::TlsAgent(const std::string& nm, Role rl, SSLProtocolVariant var)
auth_certificate_callback_(),
sni_callback_(),
skip_version_checks_(false),
resumption_token_() {
resumption_token_(),
policy_() {
memset(&info_, 0, sizeof(info_));
memset(&csinfo_, 0, sizeof(csinfo_));
SECStatus rv = SSL_VersionRangeGetDefault(variant_, &vrange_);
@ -224,6 +228,7 @@ bool TlsAgent::ConfigServerCert(const std::string& id, bool updateKeyBits,
bool TlsAgent::EnsureTlsSetup(PRFileDesc* modelSocket) {
// Don't set up twice
if (ssl_fd_) return true;
NssManagePolicy policyManage(policy_, option_);
ScopedPRFileDesc dummy_fd(adapter_->CreateFD());
EXPECT_NE(nullptr, dummy_fd);
@ -299,7 +304,7 @@ bool TlsAgent::MaybeSetResumptionToken() {
// rv is SECFailure with error set to SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR
// if the resumption token was bad (expired/malformed/etc.).
if (expect_resumption_) {
if (expect_psk_ == ssl_psk_resume) {
// Only in case we expect resumption this has to be successful. We might
// not expect resumption due to some reason but the token is totally fine.
EXPECT_EQ(SECSuccess, rv);
@ -307,8 +312,8 @@ bool TlsAgent::MaybeSetResumptionToken() {
if (rv != SECSuccess) {
EXPECT_EQ(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR, PORT_GetError());
resumption_token_.clear();
EXPECT_FALSE(expect_resumption_);
if (expect_resumption_) return false;
EXPECT_FALSE(expect_psk_ == ssl_psk_resume);
if (expect_psk_ == ssl_psk_resume) return false;
}
}
@ -316,13 +321,22 @@ bool TlsAgent::MaybeSetResumptionToken() {
}
void TlsAgent::SetAntiReplayContext(ScopedSSLAntiReplayContext& ctx) {
EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(ssl_fd_.get(), ctx.get()));
EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(ssl_fd(), ctx.get()));
}
void TlsAgent::SetupClientAuth() {
// Defaults to a Sync callback returning success
void TlsAgent::SetupClientAuth(ClientAuthCallbackType callbackType,
bool callbackSuccess) {
EXPECT_TRUE(EnsureTlsSetup());
ASSERT_EQ(CLIENT, role_);
client_auth_callback_type_ = callbackType;
client_auth_callback_success_ = callbackSuccess;
if (callbackType == ClientAuthCallbackType::kNone && !callbackSuccess) {
// Don't set a callback for this case.
return;
}
EXPECT_EQ(SECSuccess,
SSL_GetClientAuthDataHook(ssl_fd(), GetClientAuthDataHook,
reinterpret_cast<void*>(this)));
@ -339,26 +353,95 @@ void CheckCertReqAgainstDefaultCAs(const CERTDistNames* caNames) {
}
}
// Complete processing of Client Certificate Selection
// A No-op if the agent is using synchronous client cert selection.
// Otherwise, calls SSL_ClientCertCallbackComplete.
// kAsyncDelay triggers a call to SSL_ForceHandshake prior to completion to
// ensure that the socket is correctly blocked.
void TlsAgent::ClientAuthCallbackComplete() {
ASSERT_EQ(CLIENT, role_);
if (client_auth_callback_type_ != ClientAuthCallbackType::kAsyncDelay &&
client_auth_callback_type_ != ClientAuthCallbackType::kAsyncImmediate) {
return;
}
client_auth_callback_fired_++;
EXPECT_TRUE(client_auth_callback_awaiting_);
std::cerr << "client: calling SSL_ClientCertCallbackComplete with status "
<< (client_auth_callback_success_ ? "success" : "failed")
<< std::endl;
client_auth_callback_awaiting_ = false;
if (client_auth_callback_type_ == ClientAuthCallbackType::kAsyncDelay) {
std::cerr
<< "Running Handshake prior to running SSL_ClientCertCallbackComplete"
<< std::endl;
SECStatus rv = SSL_ForceHandshake(ssl_fd());
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(PORT_GetError(), PR_WOULD_BLOCK_ERROR);
}
ScopedCERTCertificate cert;
ScopedSECKEYPrivateKey priv;
if (client_auth_callback_success_) {
ASSERT_TRUE(TlsAgent::LoadCertificate(name(), &cert, &priv));
EXPECT_EQ(SECSuccess,
SSL_ClientCertCallbackComplete(ssl_fd(), SECSuccess,
priv.release(), cert.release()));
} else {
EXPECT_EQ(SECSuccess, SSL_ClientCertCallbackComplete(ssl_fd(), SECFailure,
nullptr, nullptr));
}
}
SECStatus TlsAgent::GetClientAuthDataHook(void* self, PRFileDesc* fd,
CERTDistNames* caNames,
CERTCertificate** clientCert,
SECKEYPrivateKey** clientKey) {
TlsAgent* agent = reinterpret_cast<TlsAgent*>(self);
ScopedCERTCertificate peerCert(SSL_PeerCertificate(agent->ssl_fd()));
EXPECT_TRUE(peerCert) << "Client should be able to see the server cert";
EXPECT_EQ(CLIENT, agent->role_);
agent->client_auth_callback_fired_++;
// See bug 1573945
// CheckCertReqAgainstDefaultCAs(caNames);
switch (agent->client_auth_callback_type_) {
case ClientAuthCallbackType::kAsyncDelay:
case ClientAuthCallbackType::kAsyncImmediate:
std::cerr << "Waiting for complete call" << std::endl;
agent->client_auth_callback_awaiting_ = true;
return SECWouldBlock;
case ClientAuthCallbackType::kSync:
case ClientAuthCallbackType::kNone:
// Handle the sync case. None && Success is treated as Sync and Success.
if (!agent->client_auth_callback_success_) {
return SECFailure;
}
ScopedCERTCertificate peerCert(SSL_PeerCertificate(agent->ssl_fd()));
EXPECT_TRUE(peerCert) << "Client should be able to see the server cert";
ScopedCERTCertificate cert;
ScopedSECKEYPrivateKey priv;
if (!TlsAgent::LoadCertificate(agent->name(), &cert, &priv)) {
return SECFailure;
// See bug 1573945
// CheckCertReqAgainstDefaultCAs(caNames);
ScopedCERTCertificate cert;
ScopedSECKEYPrivateKey priv;
if (!TlsAgent::LoadCertificate(agent->name(), &cert, &priv)) {
return SECFailure;
}
*clientCert = cert.release();
*clientKey = priv.release();
return SECSuccess;
}
/* This is unreachable, but some old compilers can't tell that. */
PORT_Assert(0);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
*clientCert = cert.release();
*clientKey = priv.release();
return SECSuccess;
// Increments by 1 for each callback
bool TlsAgent::CheckClientAuthCallbacksCompleted(uint8_t expected) {
EXPECT_EQ(CLIENT, role_);
return expected == client_auth_callback_fired_;
}
bool TlsAgent::GetPeerChainLength(size_t* count) {
@ -632,7 +715,9 @@ void TlsAgent::CheckAuthType(SSLAuthType auth,
SSLSignatureScheme sig_scheme) const {
EXPECT_EQ(STATE_CONNECTED, state_);
EXPECT_EQ(auth, info_.authType);
EXPECT_EQ(server_key_bits_, info_.authKeyBits);
if (auth != ssl_auth_psk) {
EXPECT_EQ(server_key_bits_, info_.authKeyBits);
}
if (expected_version_ < SSL_LIBRARY_VERSION_TLS_1_2) {
switch (auth) {
case ssl_auth_rsa_sign:
@ -683,13 +768,33 @@ void TlsAgent::EnableFalseStart() {
SetOption(SSL_ENABLE_FALSE_START, PR_TRUE);
}
void TlsAgent::ExpectResumption() { expect_resumption_ = true; }
void TlsAgent::ExpectEch(bool expected) { expect_ech_ = expected; }
void TlsAgent::ExpectPsk(SSLPskType psk) { expect_psk_ = psk; }
void TlsAgent::ExpectResumption() { expect_psk_ = ssl_psk_resume; }
void TlsAgent::EnableAlpn(const uint8_t* val, size_t len) {
EXPECT_TRUE(EnsureTlsSetup());
EXPECT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd(), val, len));
}
void TlsAgent::AddPsk(const ScopedPK11SymKey& psk, std::string label,
SSLHashType hash, uint16_t zeroRttSuite) {
EXPECT_TRUE(EnsureTlsSetup());
EXPECT_EQ(SECSuccess, SSL_AddExternalPsk0Rtt(
ssl_fd(), psk.get(),
reinterpret_cast<const uint8_t*>(label.data()),
label.length(), hash, zeroRttSuite, 1000));
}
void TlsAgent::RemovePsk(std::string label) {
EXPECT_EQ(SECSuccess,
SSL_RemoveExternalPsk(
ssl_fd(), reinterpret_cast<const uint8_t*>(label.data()),
label.length()));
}
void TlsAgent::CheckAlpn(SSLNextProtoState expected_state,
const std::string& expected) const {
SSLNextProtoState alpn_state;
@ -798,7 +903,6 @@ void TlsAgent::CheckPreliminaryInfo() {
SSL_GetPreliminaryChannelInfo(ssl_fd(), &preinfo, sizeof(preinfo)));
EXPECT_EQ(sizeof(preinfo), preinfo.length);
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_version);
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
// A version of 0 is invalid and indicates no expectation. This value is
// initialized to 0 so that tests that don't explicitly set an expected
@ -819,22 +923,22 @@ void TlsAgent::CheckPreliminaryInfo() {
void TlsAgent::CheckCallbacks() const {
// If false start happens, the handshake is reported as being complete at the
// point that false start happens.
if (expect_resumption_ || !falsestart_enabled_) {
if (expect_psk_ == ssl_psk_resume || !falsestart_enabled_) {
EXPECT_TRUE(handshake_callback_called_);
}
// These callbacks shouldn't fire if we are resuming, except on TLS 1.3.
if (role_ == SERVER) {
PRBool have_sni = SSLInt_ExtensionNegotiated(ssl_fd(), ssl_server_name_xtn);
EXPECT_EQ(((!expect_resumption_ && have_sni) ||
EXPECT_EQ(((expect_psk_ != ssl_psk_resume && have_sni) ||
expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3),
sni_hook_called_);
} else {
EXPECT_EQ(!expect_resumption_, auth_certificate_hook_called_);
EXPECT_EQ(expect_psk_ == ssl_psk_none, auth_certificate_hook_called_);
// Note that this isn't unconditionally called, even with false start on.
// But the callback is only skipped if a cipher that is ridiculously weak
// (80 bits) is chosen. Don't test that: plan to remove bad ciphers.
EXPECT_EQ(falsestart_enabled_ && !expect_resumption_,
EXPECT_EQ(falsestart_enabled_ && expect_psk_ != ssl_psk_resume,
can_falsestart_hook_called_);
}
}
@ -845,8 +949,8 @@ void TlsAgent::ResetPreliminaryInfo() {
}
void TlsAgent::UpdatePreliminaryChannelInfo() {
SECStatus rv = SSL_GetPreliminaryChannelInfo(ssl_fd_.get(), &pre_info_,
sizeof(pre_info_));
SECStatus rv =
SSL_GetPreliminaryChannelInfo(ssl_fd(), &pre_info_, sizeof(pre_info_));
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(sizeof(pre_info_), pre_info_.length);
}
@ -870,7 +974,7 @@ void TlsAgent::ValidateCipherSpecs() {
} else {
// For DTLS 1.1 and 1.2, the last endpoint to send maintains a cipher spec
// until the holddown timer runs down.
if (expect_resumption_) {
if (expect_psk_ == ssl_psk_resume) {
if (role_ == CLIENT) {
expected = 3;
}
@ -908,7 +1012,9 @@ void TlsAgent::Connected() {
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(sizeof(info_), info_.length);
EXPECT_EQ(expect_resumption_, info_.resumed == PR_TRUE);
EXPECT_EQ(expect_psk_ == ssl_psk_resume, info_.resumed == PR_TRUE);
EXPECT_EQ(expect_psk_, info_.pskType);
EXPECT_EQ(expect_ech_, info_.echAccepted);
// Preliminary values are exposed through callbacks during the handshake.
// If either expected values were set or the callbacks were called, check
@ -926,6 +1032,24 @@ void TlsAgent::Connected() {
SetState(STATE_CONNECTED);
}
void TlsAgent::CheckClientAuthCompleted(uint8_t handshakes) {
EXPECT_FALSE(client_auth_callback_awaiting_);
switch (client_auth_callback_type_) {
case ClientAuthCallbackType::kNone:
if (!client_auth_callback_success_) {
EXPECT_TRUE(CheckClientAuthCallbacksCompleted(0));
break;
}
case ClientAuthCallbackType::kSync:
EXPECT_TRUE(CheckClientAuthCallbacksCompleted(handshakes));
break;
case ClientAuthCallbackType::kAsyncDelay:
case ClientAuthCallbackType::kAsyncImmediate:
EXPECT_TRUE(CheckClientAuthCallbacksCompleted(2 * handshakes));
break;
}
}
void TlsAgent::EnableExtendedMasterSecret() {
SetOption(SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
}
@ -960,6 +1084,10 @@ void TlsAgent::SetDowngradeCheckVersion(uint16_t ver) {
void TlsAgent::Handshake() {
LOGV("Handshake");
SECStatus rv = SSL_ForceHandshake(ssl_fd());
if (client_auth_callback_awaiting_) {
ClientAuthCallbackComplete();
rv = SSL_ForceHandshake(ssl_fd());
}
if (rv == SECSuccess) {
Connected();
Poller::Instance()->Wait(READABLE_EVENT, adapter_, this,
@ -1063,21 +1191,28 @@ void TlsAgent::SendBuffer(const DataBuffer& buf) {
bool TlsAgent::SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
uint64_t seq, uint8_t ct,
const DataBuffer& buf) {
LOGV("Encrypting " << buf.len() << " bytes");
// Ensure that we are doing TLS 1.3.
EXPECT_GE(expected_version_, SSL_LIBRARY_VERSION_TLS_1_3);
TlsRecordHeader header(variant_, expected_version_, ssl_ct_application_data,
seq);
if (variant_ != ssl_variant_datagram) {
ADD_FAILURE();
return false;
}
LOGV("Encrypting " << buf.len() << " bytes");
uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno |
kCtDtlsCiphertextLengthPresent;
TlsRecordHeader header(variant_, expected_version_, dtls13_ct, seq);
TlsRecordHeader out_header(header);
DataBuffer padded = buf;
padded.Write(padded.len(), ct, 1);
DataBuffer ciphertext;
if (!spec->Protect(header, padded, &ciphertext)) {
if (!spec->Protect(header, padded, &ciphertext, &out_header)) {
return false;
}
DataBuffer record;
auto rv = header.Write(&record, 0, ciphertext);
EXPECT_EQ(header.header_length() + ciphertext.len(), rv);
auto rv = out_header.Write(&record, 0, ciphertext);
EXPECT_EQ(out_header.header_length() + ciphertext.len(), rv);
SendDirect(record);
return true;
}
@ -1124,7 +1259,7 @@ void TlsAgent::ReadBytes(size_t amount) {
}
}
void TlsAgent::ResetSentBytes() { send_ctr_ = 0; }
void TlsAgent::ResetSentBytes(size_t bytes) { send_ctr_ = bytes; }
void TlsAgent::SetOption(int32_t option, int value) {
ASSERT_TRUE(EnsureTlsSetup());
@ -1137,9 +1272,9 @@ void TlsAgent::ConfigureSessionCache(SessionResumptionMode mode) {
mode & RESUME_TICKET ? PR_TRUE : PR_FALSE);
}
void TlsAgent::DisableECDHEServerKeyReuse() {
void TlsAgent::EnableECDHEServerKeyReuse() {
ASSERT_EQ(TlsAgent::SERVER, role_);
SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_TRUE);
}
static const std::string kTlsRolesAllArr[] = {"CLIENT", "SERVER"};
@ -1201,16 +1336,26 @@ void TlsAgentTestBase::MakeRecord(SSLProtocolVariant variant, uint8_t type,
uint16_t version, const uint8_t* buf,
size_t len, DataBuffer* out,
uint64_t sequence_number) {
// Fixup the content type for DTLSCiphertext
if (variant == ssl_variant_datagram &&
version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
type == ssl_ct_application_data) {
type = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno |
kCtDtlsCiphertextLengthPresent;
}
size_t index = 0;
index = out->Write(index, type, 1);
if (variant == ssl_variant_stream) {
index = out->Write(index, type, 1);
index = out->Write(index, version, 2);
} else if (version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
type == ssl_ct_application_data) {
(type & kCtDtlsCiphertextMask) == kCtDtlsCiphertext) {
uint32_t epoch = (sequence_number >> 48) & 0x3;
uint32_t seqno = sequence_number & ((1ULL << 30) - 1);
index = out->Write(index, (epoch << 30) | seqno, 4);
index = out->Write(index, type | epoch, 1);
uint32_t seqno = sequence_number & ((1ULL << 16) - 1);
index = out->Write(index, seqno, 2);
} else {
index = out->Write(index, type, 1);
index = out->Write(index, TlsVersionToDtlsVersion(version), 2);
index = out->Write(index, sequence_number >> 32, 4);
index = out->Write(index, sequence_number & PR_UINT32_MAX, 4);

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -8,10 +9,12 @@
#include "prio.h"
#include "ssl.h"
#include "sslproto.h"
#include <functional>
#include <iostream>
#include "nss_policy.h"
#include "test_io.h"
#define GTEST_HAS_RTTI 0
@ -36,6 +39,13 @@ enum SessionResumptionMode {
RESUME_BOTH = RESUME_SESSIONID | RESUME_TICKET
};
enum class ClientAuthCallbackType {
kAsyncImmediate,
kAsyncDelay,
kSync,
kNone,
};
class PacketFilter;
class TlsAgent;
class TlsCipherSpec;
@ -75,8 +85,9 @@ class TlsAgent : public PollTarget {
static const std::string kServerEcdhEcdsa;
static const std::string kServerEcdhRsa;
static const std::string kServerDsa;
static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts
static const std::string kDelegatorRsae2048; // draft-ietf-tls-subcerts
static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts
static const std::string kDelegatorRsae2048; // draft-ietf-tls-subcerts
static const std::string kDelegatorRsaPss2048; // draft-ietf-tls-subcerts
TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant);
virtual ~TlsAgent();
@ -140,9 +151,13 @@ class TlsAgent : public PollTarget {
bool ConfigServerCertWithChain(const std::string& name);
bool EnsureTlsSetup(PRFileDesc* modelSocket = nullptr);
void SetupClientAuth();
void SetupClientAuth(
ClientAuthCallbackType callbackType = ClientAuthCallbackType::kSync,
bool callbackSuccess = true);
void RequestClientAuth(bool requireAuth);
void ClientAuthCallbackComplete();
bool CheckClientAuthCallbacksCompleted(uint8_t expected);
void CheckClientAuthCompleted(uint8_t handshakes = 1);
void SetOption(int32_t option, int value);
void ConfigureSessionCache(SessionResumptionMode mode);
void Set0RttEnabled(bool en);
@ -155,6 +170,9 @@ class TlsAgent : public PollTarget {
void SetServerKeyBits(uint16_t bits);
void ExpectReadWriteError();
void EnableFalseStart();
void ExpectEch(bool expected = true);
bool GetEchExpected() const { return expect_ech_; }
void ExpectPsk(SSLPskType psk = ssl_psk_external);
void ExpectResumption();
void SkipVersionChecks();
void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count);
@ -174,15 +192,19 @@ class TlsAgent : public PollTarget {
// Send data directly to the underlying socket, skipping the TLS layer.
void SendDirect(const DataBuffer& buf);
void SendRecordDirect(const TlsRecord& record);
void AddPsk(const ScopedPK11SymKey& psk, std::string label, SSLHashType hash,
uint16_t zeroRttSuite = TLS_NULL_WITH_NULL_NULL);
void RemovePsk(std::string label);
void ReadBytes(size_t max = 16384U);
void ResetSentBytes(); // Hack to test drops.
void ResetSentBytes(size_t bytes = 0); // Hack to test drops.
void EnableExtendedMasterSecret();
void CheckExtendedMasterSecret(bool expected);
void CheckEarlyDataAccepted(bool expected);
void CheckEchAccepted(bool expected);
void SetDowngradeCheckVersion(uint16_t version);
void CheckSecretsDestroyed();
void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
void DisableECDHEServerKeyReuse();
void EnableECDHEServerKeyReuse();
bool GetPeerChainLength(size_t* count);
void CheckCipherSuite(uint16_t cipher_suite);
void SetResumptionTokenCallback();
@ -221,7 +243,9 @@ class TlsAgent : public PollTarget {
static const char* state_str(State state) { return states[state]; }
PRFileDesc* ssl_fd() const { return ssl_fd_.get(); }
NssManagedFileDesc ssl_fd() const {
return NssManagedFileDesc(ssl_fd_.get(), policy_, option_);
}
std::shared_ptr<DummyPrSocket>& adapter() { return adapter_; }
const SSLChannelInfo& info() const {
@ -246,6 +270,8 @@ class TlsAgent : public PollTarget {
return true;
}
void expected_cipher_suite(uint16_t suite) { expected_cipher_suite_ = suite; }
std::string cipher_suite_name() const {
if (state_ != STATE_CONNECTED) return "UNKNOWN";
@ -295,6 +321,13 @@ class TlsAgent : public PollTarget {
void ExpectSendAlert(uint8_t alert, uint8_t level = 0);
std::string alpn_value_to_use_ = "";
// set the given policy before this agent runs
void SetPolicy(SECOidTag oid, PRUint32 set, PRUint32 clear) {
policy_ = NssPolicy(oid, set, clear);
}
void SetNssOption(PRInt32 id, PRInt32 value) {
option_ = NssOption(id, value);
}
private:
const static char* states[];
@ -416,8 +449,9 @@ class TlsAgent : public PollTarget {
bool falsestart_enabled_;
uint16_t expected_version_;
uint16_t expected_cipher_suite_;
bool expect_resumption_;
bool expect_client_auth_;
bool expect_ech_;
SSLPskType expect_psk_;
bool can_falsestart_hook_called_;
bool sni_hook_called_;
bool auth_certificate_hook_called_;
@ -440,6 +474,13 @@ class TlsAgent : public PollTarget {
SniCallbackFunction sni_callback_;
bool skip_version_checks_;
std::vector<uint8_t> resumption_token_;
NssPolicy policy_;
NssOption option_;
ClientAuthCallbackType client_auth_callback_type_ =
ClientAuthCallbackType::kNone;
bool client_auth_callback_success_ = false;
uint8_t client_auth_callback_fired_ = 0;
bool client_auth_callback_awaiting_ = false;
};
inline std::ostream& operator<<(std::ostream& stream,

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -106,7 +107,7 @@ std::string VersionString(uint16_t version) {
}
// The default anti-replay window for tests. Tests that rely on a different
// value call SSL_InitAntiReplay directly.
// value call ResetAntiReplay directly.
static PRTime kAntiReplayWindow = 100 * PR_USEC_PER_SEC;
TlsConnectTestBase::TlsConnectTestBase(SSLProtocolVariant variant,
@ -194,6 +195,13 @@ void TlsConnectTestBase::SaveAlgorithmPolicy() {
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() {
@ -204,6 +212,12 @@ void TlsConnectTestBase::RestoreAlgorithmPolicy() {
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) {
@ -247,6 +261,90 @@ void TlsConnectTestBase::ResetAntiReplay(PRTime window) {
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();
@ -294,10 +392,10 @@ void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected,
}
void TlsConnectTestBase::EnsureTlsSetup() {
EXPECT_TRUE(server_->EnsureTlsSetup(server_model_ ? server_model_->ssl_fd()
: nullptr));
EXPECT_TRUE(client_->EnsureTlsSetup(client_model_ ? client_model_->ssl_fd()
: nullptr));
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_));
@ -374,10 +472,8 @@ void TlsConnectTestBase::CheckConnected() {
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
uint16_t cipher_suite1, cipher_suite2;
bool ret = client_->cipher_suite(&cipher_suite1);
EXPECT_TRUE(ret);
ret = server_->cipher_suite(&cipher_suite2);
EXPECT_TRUE(ret);
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()
@ -400,6 +496,15 @@ void TlsConnectTestBase::CheckConnected() {
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 {
@ -519,6 +624,14 @@ void TlsConnectTestBase::SetExpectedVersion(uint16_t 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();
@ -755,7 +868,7 @@ void TlsConnectTestBase::ZeroRttSendReceive(
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
}
// Do a second read. this should fail.
// 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());
@ -787,8 +900,8 @@ void TlsConnectTestBase::CheckEarlyDataAccepted() {
server_->CheckEarlyDataAccepted(expect_early_data_accepted_);
}
void TlsConnectTestBase::DisableECDHEServerKeyReuse() {
server_->DisableECDHEServerKeyReuse();
void TlsConnectTestBase::EnableECDHEServerKeyReuse() {
server_->EnableECDHEServerKeyReuse();
}
void TlsConnectTestBase::SkipVersionChecks() {

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -10,6 +11,7 @@
#include "sslproto.h"
#include "sslt.h"
#include "nss.h"
#include "tls_agent.h"
#include "tls_filter.h"
@ -79,6 +81,8 @@ class TlsConnectTestBase : public ::testing::Test {
void ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender, uint8_t alert);
void ConnectExpectFailOneSide(TlsAgent::Role failingSide);
void ConnectWithCipherSuite(uint16_t cipher_suite);
void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent,
size_t expected_size);
// Check that the keys used in the handshake match expectations.
void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const;
@ -119,6 +123,9 @@ class TlsConnectTestBase : public ::testing::Test {
void EnableSrtp();
void CheckSrtp() const;
void SendReceive(size_t total = 50);
void AddPsk(const ScopedPK11SymKey& psk, std::string label, SSLHashType hash,
uint16_t zeroRttSuite = TLS_NULL_WITH_NULL_NULL);
void RemovePsk(std::string label);
void SetupForZeroRtt();
void SetupForResume();
void ZeroRttSendReceive(
@ -127,7 +134,7 @@ class TlsConnectTestBase : public ::testing::Test {
void Receive(size_t amount);
void ExpectExtendedMasterSecret(bool expected);
void ExpectEarlyDataAccepted(bool expected);
void DisableECDHEServerKeyReuse();
void EnableECDHEServerKeyReuse();
void SkipVersionChecks();
// Move the DTLS timers for both endpoints to pop the next timer.
@ -140,6 +147,17 @@ class TlsConnectTestBase : public ::testing::Test {
void SaveAlgorithmPolicy();
void RestoreAlgorithmPolicy();
static ScopedSECItem MakeEcKeyParams(SSLNamedGroup group);
static void 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);
void SetupEch(std::shared_ptr<TlsAgent>& client,
std::shared_ptr<TlsAgent>& server,
HpkeKemId kem_id = HpkeDhKemX25519Sha256,
bool expect_ech = true, bool set_client_config = true,
bool set_server_config = true, int maxConfigSize = 100);
protected:
SSLProtocolVariant variant_;
std::shared_ptr<TlsAgent> client_;
@ -165,6 +183,10 @@ class TlsConnectTestBase : public ::testing::Test {
SEC_OID_ANSIX9_DSA_SIGNATURE,
SEC_OID_CURVE25519, SEC_OID_SHA1};
std::vector<std::tuple<SECOidTag, uint32_t>> saved_policies_;
const std::vector<PRInt32> options_ = {
NSS_RSA_MIN_KEY_SIZE, NSS_DH_MIN_KEY_SIZE, NSS_DSA_MIN_KEY_SIZE,
NSS_TLS_VERSION_MIN_POLICY, NSS_TLS_VERSION_MAX_POLICY};
std::vector<std::tuple<PRInt32, uint32_t>> saved_options_;
private:
void CheckResumption(SessionResumptionMode expected);

File diff suppressed because it is too large Load diff

View file

@ -1,493 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "gtest_utils.h"
#include "tls_agent.h"
#include "tls_connect.h"
namespace nss_test {
static const char* kDummySni("dummy.invalid");
std::vector<uint16_t> kDefaultSuites = {TLS_AES_256_GCM_SHA384,
TLS_AES_128_GCM_SHA256};
std::vector<uint16_t> kChaChaSuite = {TLS_CHACHA20_POLY1305_SHA256};
std::vector<uint16_t> kBogusSuites = {0};
std::vector<uint16_t> kTls12Suites = {
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256};
static void NamedGroup2ECParams(SSLNamedGroup group, SECItem* params) {
auto groupDef = ssl_LookupNamedGroup(group);
ASSERT_NE(nullptr, groupDef);
auto oidData = SECOID_FindOIDByTag(groupDef->oidTag);
ASSERT_NE(nullptr, oidData);
ASSERT_NE(nullptr,
SECITEM_AllocItem(nullptr, params, (2 + oidData->oid.len)));
/*
* params->data needs to contain the ASN encoding of an object ID (OID)
* representing the named curve. The actual OID is in
* oidData->oid.data so we simply prepend 0x06 and OID length
*/
params->data[0] = SEC_ASN1_OBJECT_ID;
params->data[1] = oidData->oid.len;
memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
}
/* Checksum is a 4-byte array. */
static void UpdateEsniKeysChecksum(DataBuffer* buf) {
SECStatus rv;
PRUint8 sha256[32];
/* Stomp the checksum. */
PORT_Memset(buf->data() + 2, 0, 4);
rv = PK11_HashBuf(ssl3_HashTypeToOID(ssl_hash_sha256), sha256, buf->data(),
buf->len());
ASSERT_EQ(SECSuccess, rv);
buf->Write(2, sha256, 4);
}
static void GenerateEsniKey(PRTime now, SSLNamedGroup group,
std::vector<uint16_t>& cipher_suites,
DataBuffer* record,
ScopedSECKEYPublicKey* pubKey = nullptr,
ScopedSECKEYPrivateKey* privKey = nullptr) {
SECKEYECParams ecParams = {siBuffer, NULL, 0};
NamedGroup2ECParams(group, &ecParams);
SECKEYPublicKey* pub = nullptr;
SECKEYPrivateKey* priv = SECKEY_CreateECPrivateKey(&ecParams, &pub, nullptr);
ASSERT_NE(nullptr, priv);
SECITEM_FreeItem(&ecParams, PR_FALSE);
PRUint8 encoded[1024];
unsigned int encoded_len = 0;
SECStatus rv = SSL_EncodeESNIKeys(
&cipher_suites[0], cipher_suites.size(), group, pub, 100,
(now / PR_USEC_PER_SEC) - 1, (now / PR_USEC_PER_SEC) + 10, encoded,
&encoded_len, sizeof(encoded));
ASSERT_EQ(SECSuccess, rv);
ASSERT_GT(encoded_len, 0U);
if (pubKey) {
pubKey->reset(pub);
} else {
SECKEY_DestroyPublicKey(pub);
}
if (privKey) {
privKey->reset(priv);
} else {
SECKEY_DestroyPrivateKey(priv);
}
record->Truncate(0);
record->Write(0, encoded, encoded_len);
}
static void SetupEsni(PRTime now, const std::shared_ptr<TlsAgent>& client,
const std::shared_ptr<TlsAgent>& server,
SSLNamedGroup group = ssl_grp_ec_curve25519) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
GenerateEsniKey(now, ssl_grp_ec_curve25519, kDefaultSuites, &record, &pub,
&priv);
SECStatus rv = SSL_SetESNIKeyPair(server->ssl_fd(), priv.get(), record.data(),
record.len());
ASSERT_EQ(SECSuccess, rv);
rv = SSL_EnableESNI(client->ssl_fd(), record.data(), record.len(), kDummySni);
ASSERT_EQ(SECSuccess, rv);
}
static void CheckSniExtension(const DataBuffer& data) {
TlsParser parser(data.data(), data.len());
uint32_t tmp;
ASSERT_TRUE(parser.Read(&tmp, 2));
ASSERT_EQ(parser.remaining(), tmp);
ASSERT_TRUE(parser.Read(&tmp, 1));
ASSERT_EQ(0U, tmp); /* sni_nametype_hostname */
DataBuffer name;
ASSERT_TRUE(parser.ReadVariable(&name, 2));
ASSERT_EQ(0U, parser.remaining());
DataBuffer expected(reinterpret_cast<const uint8_t*>(kDummySni),
strlen(kDummySni));
ASSERT_EQ(expected, name);
}
class TlsAgentEsniTest : public TlsAgentTestClient13 {
public:
void SetUp() override { now_ = PR_Now(); }
protected:
PRTime now() const { return now_; }
void InstallEsni(const DataBuffer& record, PRErrorCode err = 0) {
SECStatus rv = SSL_EnableESNI(agent_->ssl_fd(), record.data(), record.len(),
kDummySni);
if (err == 0) {
ASSERT_EQ(SECSuccess, rv);
} else {
ASSERT_EQ(SECFailure, rv);
ASSERT_EQ(err, PORT_GetError());
}
}
private:
PRTime now_ = 0;
};
TEST_P(TlsAgentEsniTest, EsniInstall) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
InstallEsni(record);
}
// The next set of tests fail at setup time.
TEST_P(TlsAgentEsniTest, EsniInvalidHash) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.data()[2]++;
InstallEsni(record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
}
TEST_P(TlsAgentEsniTest, EsniInvalidVersion) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.Write(0, 0xffff, 2);
InstallEsni(record, SSL_ERROR_UNSUPPORTED_VERSION);
}
TEST_P(TlsAgentEsniTest, EsniShort) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.Truncate(record.len() - 1);
UpdateEsniKeysChecksum(&record);
InstallEsni(record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
}
TEST_P(TlsAgentEsniTest, EsniLong) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.Write(record.len(), 1, 1);
UpdateEsniKeysChecksum(&record);
InstallEsni(record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
}
TEST_P(TlsAgentEsniTest, EsniExtensionMismatch) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.Write(record.len() - 1, 1, 1);
UpdateEsniKeysChecksum(&record);
InstallEsni(record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
}
// The following tests fail by ignoring the Esni block.
TEST_P(TlsAgentEsniTest, EsniUnknownGroup) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
record.Write(8, 0xffff, 2); // Fake group
UpdateEsniKeysChecksum(&record);
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
ASSERT_TRUE(!filter->captured());
}
TEST_P(TlsAgentEsniTest, EsniUnknownCS) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kBogusSuites, &record);
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
ASSERT_TRUE(!filter->captured());
}
TEST_P(TlsAgentEsniTest, EsniInvalidCS) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kTls12Suites, &record);
UpdateEsniKeysChecksum(&record);
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
ASSERT_TRUE(!filter->captured());
}
TEST_P(TlsAgentEsniTest, EsniNotReady) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now() + 1000, ssl_grp_ec_curve25519, kDefaultSuites, &record);
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_TRUE(!filter->captured());
}
TEST_P(TlsAgentEsniTest, EsniExpired) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now() - 1000, ssl_grp_ec_curve25519, kDefaultSuites, &record);
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_TRUE(!filter->captured());
}
TEST_P(TlsAgentEsniTest, NoSniSoNoEsni) {
EnsureInit();
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
SSL_SetURL(agent_->ssl_fd(), "");
InstallEsni(record, 0);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
agent_->Handshake();
ASSERT_TRUE(!filter->captured());
}
static int32_t SniCallback(TlsAgent* agent, const SECItem* srvNameAddr,
PRUint32 srvNameArrSize) {
EXPECT_EQ(1U, srvNameArrSize);
SECItem expected = {
siBuffer, reinterpret_cast<unsigned char*>(const_cast<char*>("server")),
6};
EXPECT_TRUE(!SECITEM_CompareItem(&expected, &srvNameAddr[0]));
return SECSuccess;
}
TEST_P(TlsConnectTls13, ConnectEsni) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
auto cFilterSni =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto cFilterEsni =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_encrypted_sni_xtn);
client_->SetFilter(std::make_shared<ChainedPacketFilter>(
ChainedPacketFilterInit({cFilterSni, cFilterEsni})));
auto sfilter =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
sfilter->EnableDecryption();
server_->SetSniCallback(SniCallback);
Connect();
CheckSniExtension(cFilterSni->extension());
ASSERT_TRUE(cFilterEsni->captured());
// Check that our most preferred suite got chosen.
uint32_t suite;
ASSERT_TRUE(cFilterEsni->extension().Read(0, 2, &suite));
ASSERT_EQ(TLS_AES_128_GCM_SHA256, static_cast<PRUint16>(suite));
ASSERT_TRUE(!sfilter->captured());
}
TEST_P(TlsConnectTls13, ConnectEsniHrr) {
EnsureTlsSetup();
const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
server_->ConfigNamedGroups(groups);
SetupEsni(now(), client_, server_);
auto hrr_capture = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeHelloRetryRequest);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto filter2 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn, true);
auto efilter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_encrypted_sni_xtn);
auto efilter2 = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_encrypted_sni_xtn, true);
client_->SetFilter(std::make_shared<ChainedPacketFilter>(
ChainedPacketFilterInit({filter, filter2, efilter, efilter2})));
server_->SetSniCallback(SniCallback);
Connect();
CheckSniExtension(filter->extension());
CheckSniExtension(filter2->extension());
ASSERT_TRUE(efilter->captured());
ASSERT_TRUE(efilter2->captured());
ASSERT_NE(efilter->extension(), efilter2->extension());
EXPECT_NE(0UL, hrr_capture->buffer().len());
}
TEST_P(TlsConnectTls13, ConnectEsniNoDummy) {
EnsureTlsSetup();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record, &pub,
&priv);
SECStatus rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(),
record.data(), record.len());
ASSERT_EQ(SECSuccess, rv);
rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), "");
ASSERT_EQ(SECSuccess, rv);
auto cfilter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto sfilter =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
server_->SetSniCallback(SniCallback);
Connect();
ASSERT_TRUE(!cfilter->captured());
ASSERT_TRUE(!sfilter->captured());
}
TEST_P(TlsConnectTls13, ConnectEsniNullDummy) {
EnsureTlsSetup();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record, &pub,
&priv);
SECStatus rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(),
record.data(), record.len());
ASSERT_EQ(SECSuccess, rv);
rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), nullptr);
ASSERT_EQ(SECSuccess, rv);
auto cfilter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto sfilter =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
server_->SetSniCallback(SniCallback);
Connect();
ASSERT_TRUE(!cfilter->captured());
ASSERT_TRUE(!sfilter->captured());
}
/* Tell the client that it supports AES but the server that it supports ChaCha
*/
TEST_P(TlsConnectTls13, ConnectEsniCSMismatch) {
EnsureTlsSetup();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record, &pub,
&priv);
PRUint8 encoded[1024];
unsigned int encoded_len = 0;
SECStatus rv = SSL_EncodeESNIKeys(
&kChaChaSuite[0], kChaChaSuite.size(), ssl_grp_ec_curve25519, pub.get(),
100, (now() / PR_USEC_PER_SEC) - 1, (now() / PR_USEC_PER_SEC) + 10,
encoded, &encoded_len, sizeof(encoded));
ASSERT_EQ(SECSuccess, rv);
ASSERT_LT(0U, encoded_len);
rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(), encoded, encoded_len);
ASSERT_EQ(SECSuccess, rv);
rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), "");
ASSERT_EQ(SECSuccess, rv);
ConnectExpectAlert(server_, illegal_parameter);
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
}
TEST_P(TlsConnectTls13, ConnectEsniP256) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_, ssl_grp_ec_secp256r1);
auto cfilter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto sfilter =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
server_->SetSniCallback(SniCallback);
Connect();
CheckSniExtension(cfilter->extension());
ASSERT_TRUE(!sfilter->captured());
}
TEST_P(TlsConnectTls13, ConnectMismatchedEsniKeys) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
// Now install a new set of keys on the client, so we have a mismatch.
DataBuffer record;
GenerateEsniKey(now(), ssl_grp_ec_curve25519, kDefaultSuites, &record);
SECStatus rv =
SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), kDummySni);
ASSERT_EQ(SECSuccess, rv);
ConnectExpectAlert(server_, illegal_parameter);
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
}
TEST_P(TlsConnectTls13, ConnectDamagedEsniExtensionCH) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
auto filter = MakeTlsFilter<TlsExtensionDamager>(
client_, ssl_tls13_encrypted_sni_xtn, 50); // in the ciphertext
ConnectExpectAlert(server_, illegal_parameter);
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
}
TEST_P(TlsConnectTls13, ConnectRemoveEsniExtensionEE) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
auto filter =
MakeTlsFilter<TlsExtensionDropper>(server_, ssl_tls13_encrypted_sni_xtn);
filter->EnableDecryption();
ConnectExpectAlert(client_, missing_extension);
client_->CheckErrorCode(SSL_ERROR_MISSING_ESNI_EXTENSION);
}
TEST_P(TlsConnectTls13, ConnectShortEsniExtensionEE) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
DataBuffer shortNonce;
auto filter = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_tls13_encrypted_sni_xtn, shortNonce);
filter->EnableDecryption();
ConnectExpectAlert(client_, illegal_parameter);
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION);
}
TEST_P(TlsConnectTls13, ConnectBogusEsniExtensionEE) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
const uint8_t bogusNonceBuf[16] = {0};
DataBuffer bogusNonce(bogusNonceBuf, sizeof(bogusNonceBuf));
auto filter = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_tls13_encrypted_sni_xtn, bogusNonce);
filter->EnableDecryption();
ConnectExpectAlert(client_, illegal_parameter);
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION);
}
// ESNI is a commitment to doing TLS 1.3 or above.
// The TLS 1.2 server ignores ESNI and processes the dummy SNI.
// The client then aborts when it sees the server did TLS 1.2.
TEST_P(TlsConnectTls13, EsniButTLS12Server) {
EnsureTlsSetup();
SetupEsni(now(), client_, server_);
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_2);
ConnectExpectAlert(client_, kTlsAlertProtocolVersion);
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
server_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
ASSERT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
ssl_tls13_encrypted_sni_xtn));
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -16,6 +17,7 @@ extern "C" {
#include "gtest_utils.h"
#include "tls_agent.h"
#include "tls_filter.h"
#include "tls_parser.h"
#include "tls_protect.h"
namespace nss_test {
@ -100,13 +102,16 @@ void TlsRecordFilter::SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSL_GetCipherSuiteInfo(suite, &cipherinfo, sizeof(cipherinfo)));
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
bool is_dtls = self->agent()->variant() == ssl_variant_datagram;
self->cipher_specs_.emplace_back(is_dtls, epoch);
self->cipher_specs_.emplace_back(self->is_dtls_agent(), epoch);
EXPECT_TRUE(self->cipher_specs_.back().SetKeys(&cipherinfo, secret));
}
bool TlsRecordFilter::is_dtls_agent() const {
return agent()->variant() == ssl_variant_datagram;
}
bool TlsRecordFilter::is_dtls13() const {
if (agent()->variant() != ssl_variant_datagram) {
if (!is_dtls_agent()) {
return false;
}
if (agent()->state() == TlsAgent::STATE_CONNECTED) {
@ -119,6 +124,10 @@ bool TlsRecordFilter::is_dtls13() const {
info.canSendEarlyData;
}
bool TlsRecordFilter::is_dtls13_ciphertext(uint8_t ct) const {
return is_dtls13() && (ct & kCtDtlsCiphertextMask) == kCtDtlsCiphertext;
}
// Gets the cipher spec that matches the specified epoch.
TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) {
for (auto& sp : cipher_specs_) {
@ -131,8 +140,7 @@ TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) {
// count sequence numbers.
EXPECT_FALSE(decrypting_) << "No spec available for epoch " << write_epoch;
;
bool is_dtls = agent()->variant() == ssl_variant_datagram;
cipher_specs_.emplace_back(is_dtls, write_epoch);
cipher_specs_.emplace_back(is_dtls_agent(), write_epoch);
return cipher_specs_.back();
}
@ -195,23 +203,24 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
uint8_t inner_content_type;
DataBuffer plaintext;
uint16_t protection_epoch = 0;
TlsRecordHeader out_header(header);
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext)) {
&plaintext, &out_header)) {
std::cerr << agent()->role_str() << ": unprotect failed: " << header << ":"
<< record << std::endl;
return KEEP;
}
auto& protection_spec = spec(protection_epoch);
TlsRecordHeader real_header(header.variant(), header.version(),
inner_content_type, header.sequence_number());
TlsRecordHeader real_header(out_header.variant(), out_header.version(),
inner_content_type, out_header.sequence_number());
PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered);
// In stream mode, even if something doesn't change we need to re-encrypt if
// previous packets were dropped.
if (action == KEEP) {
if (header.is_dtls() || !protection_spec.record_dropped()) {
if (out_header.is_dtls() || !protection_spec.record_dropped()) {
// Count every outgoing packet.
protection_spec.RecordProtected();
return KEEP;
@ -220,7 +229,7 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
}
if (action == DROP) {
std::cerr << "record drop: " << header << ":" << record << std::endl;
std::cerr << "record drop: " << out_header << ":" << record << std::endl;
protection_spec.RecordDropped();
return DROP;
}
@ -232,17 +241,15 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
}
uint64_t seq_num = protection_spec.next_out_seqno();
if (!decrypting_ && header.is_dtls()) {
if (!decrypting_ && out_header.is_dtls()) {
// Copy over the epoch, which isn't tracked when not decrypting.
seq_num |= header.sequence_number() & (0xffffULL << 48);
seq_num |= out_header.sequence_number() & (0xffffULL << 48);
}
TlsRecordHeader out_header(header.variant(), header.version(),
header.content_type(), seq_num);
out_header.sequence_number(seq_num);
DataBuffer ciphertext;
bool rv = Protect(protection_spec, out_header, inner_content_type, filtered,
&ciphertext);
&ciphertext, &out_header);
if (!rv) {
return KEEP;
}
@ -261,19 +268,72 @@ size_t TlsRecordHeader::header_length() const {
return WriteHeader(&buf, 0, 0);
}
uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
bool TlsRecordHeader::MaskSequenceNumber() {
return MaskSequenceNumber(sn_mask());
}
bool TlsRecordHeader::MaskSequenceNumber(const DataBuffer& mask_buf) {
if (mask_buf.empty()) {
return false;
}
DataBuffer mask;
if (is_dtls13_ciphertext()) {
uint64_t seqno = sequence_number();
uint8_t len = content_type() & kCtDtlsCiphertext16bSeqno ? 2 : 1;
uint16_t seqno_bitmask = (1 << len * 8) - 1;
DataBuffer val;
if (val.Write(0, seqno & seqno_bitmask, len) != len) {
return false;
}
#ifdef UNSAFE_FUZZER_MODE
// Use a null mask.
mask.Allocate(mask_buf.len());
#endif
mask.Append(mask_buf);
val.data()[0] ^= mask.data()[0];
if (len == 2 && mask.len() > 1) {
val.data()[1] ^= mask.data()[1];
}
uint32_t tmp;
if (!val.Read(0, len, &tmp)) {
return false;
}
seqno = (seqno & ~seqno_bitmask) | tmp;
seqno_is_masked_ = !seqno_is_masked_;
if (!seqno_is_masked_) {
seqno = ParseSequenceNumber(guess_seqno_, seqno, len * 8, 2);
}
sequence_number_ = seqno;
// Now update the header bytes
if (header_.len() > 1) {
header_.data()[1] ^= mask.data()[0];
if ((content_type() & kCtDtlsCiphertext16bSeqno) && header().len() > 2) {
header_.data()[2] ^= mask.data()[1];
}
}
}
sn_mask_ = mask;
return true;
}
uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t guess_seqno,
uint32_t partial,
size_t partial_bits) {
EXPECT_GE(32U, partial_bits);
uint64_t mask = (1ULL << partial_bits) - 1;
// First we determine the highest possible value. This is half the
// expressible range above the expected value, less 1.
// expressible range above the expected value (|guess_seqno|), less 1.
//
// We subtract the extra 1 from the cap so that when given a choice between
// the equidistant expected+N and expected-N we want to chose the lower. With
// 0-RTT, we sometimes have to recover an epoch of 1 when we expect an epoch
// of 3 and with 2 partial bits, the alternative result of 5 is wrong.
uint64_t cap = expected + (1ULL << (partial_bits - 1)) - 1;
uint64_t cap = guess_seqno + (1ULL << (partial_bits - 1)) - 1;
// Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234.
uint64_t seq_no = (cap & ~mask) | partial;
// If the partial value is higher than the same partial piece from the cap,
@ -285,15 +345,18 @@ uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
}
// Determine the full epoch and sequence number from an expected and raw value.
// The expected and output values are packed as they are in DTLS 1.2 and
// earlier: with 16 bits of epoch and 48 bits of sequence number.
uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint32_t raw,
// The expected, raw, and output values are packed as they are in DTLS 1.2 and
// earlier: with 16 bits of epoch and 48 bits of sequence number. The raw value
// is packed this way (even before recovery) so that we don't need to track a
// moving value between two calls (one to recover the epoch, and one after
// unmasking to recover the sequence number).
uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint64_t raw,
size_t seq_no_bits,
size_t epoch_bits) {
uint64_t epoch_mask = (1ULL << epoch_bits) - 1;
uint64_t epoch = RecoverSequenceNumber(
expected >> 48, (raw >> seq_no_bits) & epoch_mask, epoch_bits);
if (epoch > (expected >> 48)) {
uint64_t ep = RecoverSequenceNumber(expected >> 48, (raw >> 48) & epoch_mask,
epoch_bits);
if (ep > (expected >> 48)) {
// If the epoch has changed, reset the expected sequence number.
expected = 0;
} else {
@ -301,9 +364,12 @@ uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint32_t raw,
expected &= (1ULL << 48) - 1;
}
uint64_t seq_no_mask = (1ULL << seq_no_bits) - 1;
uint64_t seq_no =
RecoverSequenceNumber(expected, raw & seq_no_mask, seq_no_bits);
return (epoch << 48) | seq_no;
uint64_t seq_no = (raw & seq_no_mask);
if (!seqno_is_masked_) {
seq_no = RecoverSequenceNumber(expected, seq_no, seq_no_bits);
}
return (ep << 48) | seq_no;
}
bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser,
@ -319,38 +385,47 @@ bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser,
version_ = SSL_LIBRARY_VERSION_TLS_1_3;
#ifndef UNSAFE_FUZZER_MODE
// Deal with the 7 octet header.
if (content_type_ == ssl_ct_application_data) {
// Deal with the DTLSCipherText header.
if (is_dtls13_ciphertext()) {
uint8_t seq_no_bytes =
(content_type_ & kCtDtlsCiphertext16bSeqno) ? 2 : 1;
uint32_t tmp;
if (!parser->Read(&tmp, 4)) {
return false;
}
sequence_number_ = ParseSequenceNumber(seqno, tmp, 30, 2);
if (!parser->ReadFromMark(&header_, parser->consumed() + 2 - mark,
mark)) {
return false;
}
return parser->ReadVariable(body, 2);
}
// The short, 2 octet header.
if ((content_type_ & 0xe0) == 0x20) {
uint32_t tmp;
if (!parser->Read(&tmp, 1)) {
if (!parser->Read(&tmp, seq_no_bytes)) {
return false;
}
// Need to use the low 5 bits of the first octet too.
tmp |= (content_type_ & 0x1f) << 8;
content_type_ = ssl_ct_application_data;
sequence_number_ = ParseSequenceNumber(seqno, tmp, 12, 1);
// Store the guess if masked. If and when seqno_bytesenceNumber is called,
// the value will be unmasked and recovered. This assumes we only call
// Parse() on headers containing masked values.
seqno_is_masked_ = true;
guess_seqno_ = seqno;
uint64_t ep = content_type_ & 0x03;
sequence_number_ = (ep << 48) | tmp;
// Recover the full epoch. Note the sequence number portion holds the
// masked value until a call to Mask() reveals it (as indicated by
// |seqno_is_masked_|).
sequence_number_ =
ParseSequenceNumber(seqno, sequence_number_, seq_no_bytes * 8, 2);
uint32_t len_bytes =
(content_type_ & kCtDtlsCiphertextLengthPresent) ? 2 : 0;
if (len_bytes) {
if (!parser->Read(&tmp, 2)) {
return false;
}
}
if (!parser->ReadFromMark(&header_, parser->consumed() - mark, mark)) {
return false;
}
return parser->Read(body, parser->remaining());
return len_bytes ? parser->Read(body, tmp)
: parser->Read(body, parser->remaining());
}
// The full 13 octet header can only be used for a few types.
// The full DTLSPlainText header can only be used for a few types.
EXPECT_TRUE(content_type_ == ssl_ct_alert ||
content_type_ == ssl_ct_handshake ||
content_type_ == ssl_ct_ack);
@ -388,15 +463,20 @@ bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser,
size_t TlsRecordHeader::WriteHeader(DataBuffer* buffer, size_t offset,
size_t body_len) const {
offset = buffer->Write(offset, content_type_, 1);
if (is_dtls() && version_ >= SSL_LIBRARY_VERSION_TLS_1_3 &&
content_type() == ssl_ct_application_data) {
if (is_dtls13_ciphertext()) {
uint8_t seq_no_bytes = (content_type_ & kCtDtlsCiphertext16bSeqno) ? 2 : 1;
// application_data records in TLS 1.3 have a different header format.
// Always use the long header here for simplicity.
uint32_t e = (sequence_number_ >> 48) & 0x3;
uint32_t seqno = sequence_number_ & ((1ULL << 30) - 1);
offset = buffer->Write(offset, (e << 30) | seqno, 4);
uint32_t seqno = sequence_number_ & ((1ULL << seq_no_bytes * 8) - 1);
uint8_t new_content_type_ = content_type_ | e;
offset = buffer->Write(offset, new_content_type_, 1);
offset = buffer->Write(offset, seqno, seq_no_bytes);
if (content_type_ & kCtDtlsCiphertextLengthPresent) {
offset = buffer->Write(offset, body_len, 2);
}
} else {
offset = buffer->Write(offset, content_type_, 1);
uint16_t v = is_dtls() ? TlsVersionToDtlsVersion(version_) : version_;
offset = buffer->Write(offset, v, 2);
if (is_dtls()) {
@ -404,8 +484,9 @@ size_t TlsRecordHeader::WriteHeader(DataBuffer* buffer, size_t offset,
offset = buffer->Write(offset, sequence_number_ >> 32, 4);
offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4);
}
offset = buffer->Write(offset, body_len, 2);
}
offset = buffer->Write(offset, body_len, 2);
return offset;
}
@ -420,11 +501,12 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
uint16_t* protection_epoch,
uint8_t* inner_content_type,
DataBuffer* plaintext) {
if (!decrypting_ || header.content_type() != ssl_ct_application_data) {
DataBuffer* plaintext,
TlsRecordHeader* out_header) {
if (!decrypting_ || !header.is_protected()) {
// Maintain the epoch and sequence number for plaintext records.
uint16_t ep = 0;
if (agent()->variant() == ssl_variant_datagram) {
if (is_dtls_agent()) {
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
}
spec(ep).RecordUnprotected(header.sequence_number());
@ -435,9 +517,9 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
}
uint16_t ep = 0;
if (agent()->variant() == ssl_variant_datagram) {
if (is_dtls_agent()) {
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
if (!spec(ep).Unprotect(header, ciphertext, plaintext)) {
if (!spec(ep).Unprotect(header, ciphertext, plaintext, out_header)) {
return false;
}
} else {
@ -445,7 +527,8 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
// can't just use the newest keys because the same flight of messages can
// contain multiple epochs. So... trial decrypt!
for (size_t i = cipher_specs_.size() - 1; i > 0; --i) {
if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext)) {
if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext,
out_header)) {
ep = cipher_specs_[i].epoch();
break;
}
@ -480,7 +563,8 @@ bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec,
const TlsRecordHeader& header,
uint8_t inner_content_type,
const DataBuffer& plaintext,
DataBuffer* ciphertext, size_t padding) {
DataBuffer* ciphertext,
TlsRecordHeader* out_header, size_t padding) {
if (!protection_spec.is_protected()) {
// Not protected, just keep the sequence numbers updated.
protection_spec.RecordProtected();
@ -493,7 +577,7 @@ bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec,
size_t offset = padded.Write(0, plaintext.data(), plaintext.len());
padded.Write(offset, inner_content_type, 1);
bool ok = protection_spec.Protect(header, padded, ciphertext);
bool ok = protection_spec.Protect(header, padded, ciphertext, out_header);
if (!ok) {
ADD_FAILURE() << "protect fail";
} else if (g_ssl_gtest_verbose) {
@ -931,6 +1015,12 @@ PacketFilter::Action TlsExtensionFilter::FilterExtensions(
return KEEP;
}
PacketFilter::Action TlsExtensionOrderCapture::FilterExtension(
uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
order.push_back(extension_type);
return KEEP;
}
PacketFilter::Action TlsExtensionCapture::FilterExtension(
uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
if (extension_type == extension_ && (last_ || !captured_)) {
@ -950,6 +1040,69 @@ PacketFilter::Action TlsExtensionReplacer::FilterExtension(
return CHANGE;
}
PacketFilter::Action TlsExtensionResizer::FilterExtension(
uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
if (extension_type != extension_) {
return KEEP;
}
if (input.len() <= length_) {
DataBuffer buf(length_ - input.len());
output->Append(buf);
return CHANGE;
}
output->Assign(input.data(), length_);
return CHANGE;
}
PacketFilter::Action TlsExtensionAppender::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
TlsParser parser(input);
if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
return KEEP;
}
*output = input;
// Increase the length of the extensions block.
if (!UpdateLength(output, parser.consumed(), 2)) {
return KEEP;
}
// Extensions in Certificate are nested twice. Increase the size of the
// certificate list.
if (header.handshake_type() == kTlsHandshakeCertificate) {
TlsParser p2(input);
if (!p2.SkipVariable(1)) {
ADD_FAILURE();
return KEEP;
}
if (!UpdateLength(output, p2.consumed(), 3)) {
return KEEP;
}
}
size_t offset = output->len();
offset = output->Write(offset, extension_, 2);
WriteVariable(output, offset, data_, 2);
return CHANGE;
}
bool TlsExtensionAppender::UpdateLength(DataBuffer* output, size_t offset,
size_t size) {
uint32_t len;
if (!output->Read(offset, size, &len)) {
ADD_FAILURE();
return false;
}
len += 4 + data_.len();
output->Write(offset, len, size);
return true;
}
PacketFilter::Action TlsExtensionDropper::FilterExtension(
uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
if (extension_type == extension_) {
@ -1052,15 +1205,7 @@ PacketFilter::Action SelectiveRecordDropFilter::FilterRecord(
return pattern;
}
PacketFilter::Action TlsClientHelloVersionSetter::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
*output = input;
output->Write(0, version_, 2);
return CHANGE;
}
PacketFilter::Action TlsServerHelloVersionSetter::FilterHandshake(
PacketFilter::Action TlsMessageVersionSetter::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
*output = input;
@ -1074,15 +1219,75 @@ PacketFilter::Action SelectedCipherSuiteReplacer::FilterHandshake(
*output = input;
uint32_t temp = 0;
EXPECT_TRUE(input.Read(0, 2, &temp));
// Cipher suite is after version(2) and random(32).
EXPECT_EQ(header.version(), NormalizeTlsVersion(temp));
// Cipher suite is after version(2), random(32)
// and [legacy_]session_id(<0..32>).
size_t pos = 34;
if (temp < SSL_LIBRARY_VERSION_TLS_1_3) {
// In old versions, we have to skip a session_id too.
EXPECT_TRUE(input.Read(pos, 1, &temp));
pos += 1 + temp;
}
EXPECT_TRUE(input.Read(pos, 1, &temp));
pos += 1 + temp;
output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
return CHANGE;
}
PacketFilter::Action ServerHelloRandomChanger::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
*output = input;
uint32_t temp = 0;
size_t pos = 30;
EXPECT_TRUE(input.Read(pos, 2, &temp));
output->Write(pos, (temp ^ 0xffff), 2);
return CHANGE;
}
PacketFilter::Action ClientHelloPreambleCapture::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
EXPECT_TRUE(header.handshake_type() == kTlsHandshakeClientHello);
if (captured_) {
return KEEP;
}
captured_ = true;
DataBuffer temp;
TlsParser parser(input);
EXPECT_TRUE(parser.Read(&temp, 2 + 32)); // Version + Random
EXPECT_TRUE(parser.ReadVariable(&temp, 1)); // Session ID
if (is_dtls_agent()) {
EXPECT_TRUE(parser.ReadVariable(&temp, 1)); // Cookie
}
EXPECT_TRUE(parser.ReadVariable(&temp, 2)); // Ciphersuites
EXPECT_TRUE(parser.ReadVariable(&temp, 1)); // Compression
// Copy the preamble into a new buffer
data_ = input;
data_.Truncate(parser.consumed());
return KEEP;
}
PacketFilter::Action ClientHelloCiphersuiteCapture::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
EXPECT_TRUE(header.handshake_type() == kTlsHandshakeClientHello);
if (captured_) {
return KEEP;
}
captured_ = true;
TlsParser parser(input);
EXPECT_TRUE(parser.Skip(2 + 32)); // Version + Random
EXPECT_TRUE(parser.SkipVariable(1)); // Session ID
if (is_dtls_agent()) {
EXPECT_TRUE(parser.SkipVariable(1)); // Cookie
}
EXPECT_TRUE(parser.ReadVariable(&data_, 2)); // Ciphersuites
return KEEP;
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -10,7 +11,9 @@
#include <memory>
#include <set>
#include <vector>
#include "pk11pub.h"
#include "sslt.h"
#include "sslproto.h"
#include "test_io.h"
#include "tls_agent.h"
#include "tls_parser.h"
@ -24,6 +27,59 @@ namespace nss_test {
class TlsCipherSpec;
class TlsSendCipherSpecCapturer {
public:
TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent)
: agent_(agent), send_cipher_specs_() {
EXPECT_EQ(SECSuccess,
SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this));
}
std::shared_ptr<TlsCipherSpec> spec(size_t i) {
if (i >= send_cipher_specs_.size()) {
return nullptr;
}
return send_cipher_specs_[i];
}
private:
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSLSecretDirection dir, PK11SymKey* secret,
void* arg) {
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
std::cerr << self->agent_->role_str() << ": capture " << dir
<< " secret for epoch " << epoch << std::endl;
if (dir == ssl_secret_read) {
return;
}
SSLPreliminaryChannelInfo preinfo;
EXPECT_EQ(SECSuccess,
SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo,
sizeof(preinfo)));
EXPECT_EQ(sizeof(preinfo), preinfo.length);
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
// Check the version:
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_version);
ASSERT_GE(SSL_LIBRARY_VERSION_TLS_1_3, preinfo.protocolVersion);
SSLCipherSuiteInfo cipherinfo;
EXPECT_EQ(SECSuccess,
SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo,
sizeof(cipherinfo)));
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
auto spec = std::make_shared<TlsCipherSpec>(true, epoch);
EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret));
self->send_cipher_specs_.push_back(spec);
}
std::shared_ptr<TlsAgent> agent_;
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
};
class TlsVersioned {
public:
TlsVersioned() : variant_(ssl_variant_stream), version_(0) {}
@ -44,22 +100,57 @@ class TlsVersioned {
class TlsRecordHeader : public TlsVersioned {
public:
TlsRecordHeader()
: TlsVersioned(), content_type_(0), sequence_number_(0), header_() {}
: TlsVersioned(),
content_type_(0),
guess_seqno_(0),
seqno_is_masked_(false),
sequence_number_(0),
header_() {}
TlsRecordHeader(SSLProtocolVariant var, uint16_t ver, uint8_t ct,
uint64_t seqno)
: TlsVersioned(var, ver),
content_type_(ct),
guess_seqno_(0),
seqno_is_masked_(false),
sequence_number_(seqno),
header_() {}
header_(),
sn_mask_() {}
bool is_protected() const {
// *TLS < 1.3
if (version() < SSL_LIBRARY_VERSION_TLS_1_3 &&
content_type() == ssl_ct_application_data) {
return true;
}
// TLS 1.3
if (!is_dtls() && version() >= SSL_LIBRARY_VERSION_TLS_1_3 &&
content_type() == ssl_ct_application_data) {
return true;
}
// DTLS 1.3
return is_dtls13_ciphertext();
}
uint8_t content_type() const { return content_type_; }
uint64_t sequence_number() const { return sequence_number_; }
uint16_t epoch() const {
return static_cast<uint16_t>(sequence_number_ >> 48);
}
uint64_t sequence_number() const { return sequence_number_; }
void sequence_number(uint64_t seqno) { sequence_number_ = seqno; }
const DataBuffer& sn_mask() const { return sn_mask_; }
bool is_dtls13_ciphertext() const {
return is_dtls() && (version() >= SSL_LIBRARY_VERSION_TLS_1_3) &&
(content_type() & kCtDtlsCiphertextMask) == kCtDtlsCiphertext;
}
size_t header_length() const;
const DataBuffer& header() const { return header_; }
bool MaskSequenceNumber();
bool MaskSequenceNumber(const DataBuffer& mask_buf);
// Parse the header; return true if successful; body in an outparam if OK.
bool Parse(bool is_dtls13, uint64_t sequence_number, TlsParser* parser,
DataBuffer* body);
@ -69,14 +160,17 @@ class TlsRecordHeader : public TlsVersioned {
size_t WriteHeader(DataBuffer* buffer, size_t offset, size_t body_len) const;
private:
static uint64_t RecoverSequenceNumber(uint64_t expected, uint32_t partial,
static uint64_t RecoverSequenceNumber(uint64_t guess_seqno, uint32_t partial,
size_t partial_bits);
static uint64_t ParseSequenceNumber(uint64_t expected, uint32_t raw,
size_t seq_no_bits, size_t epoch_bits);
uint64_t ParseSequenceNumber(uint64_t expected, uint64_t raw,
size_t seq_no_bits, size_t epoch_bits);
uint8_t content_type_;
uint64_t guess_seqno_;
bool seqno_is_masked_;
uint64_t sequence_number_;
DataBuffer header_;
DataBuffer sn_mask_;
};
struct TlsRecord {
@ -110,12 +204,14 @@ class TlsRecordFilter : public PacketFilter {
// Enabling it for lower version tests will cause undefined
// behavior.
void EnableDecryption();
bool decrypting() const { return decrypting_; };
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
uint16_t* protection_epoch, uint8_t* inner_content_type,
DataBuffer* plaintext);
DataBuffer* plaintext, TlsRecordHeader* out_header);
bool Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header,
uint8_t inner_content_type, const DataBuffer& plaintext,
DataBuffer* ciphertext, size_t padding = 0);
DataBuffer* ciphertext, TlsRecordHeader* out_header,
size_t padding = 0);
protected:
// There are two filter functions which can be overriden. Both are
@ -139,7 +235,9 @@ class TlsRecordFilter : public PacketFilter {
return KEEP;
}
bool is_dtls_agent() const;
bool is_dtls13() const;
bool is_dtls13_ciphertext(uint8_t ct) const;
TlsCipherSpec& spec(uint16_t epoch);
private:
@ -390,6 +488,19 @@ class TlsExtensionFilter : public TlsHandshakeFilter {
DataBuffer* output);
};
class TlsExtensionOrderCapture : public TlsExtensionFilter {
public:
TlsExtensionOrderCapture(const std::shared_ptr<TlsAgent>& a, uint8_t message)
: TlsExtensionFilter(a, {message}){};
std::vector<uint16_t> order;
protected:
PacketFilter::Action FilterExtension(uint16_t extension_type,
const DataBuffer& input,
DataBuffer* output) override;
};
class TlsExtensionCapture : public TlsExtensionFilter {
public:
TlsExtensionCapture(const std::shared_ptr<TlsAgent>& a, uint16_t ext,
@ -429,6 +540,37 @@ class TlsExtensionReplacer : public TlsExtensionFilter {
const DataBuffer data_;
};
class TlsExtensionResizer : public TlsExtensionFilter {
public:
TlsExtensionResizer(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
size_t length)
: TlsExtensionFilter(a), extension_(extension), length_(length) {}
PacketFilter::Action FilterExtension(uint16_t extension_type,
const DataBuffer& input,
DataBuffer* output) override;
private:
uint16_t extension_;
size_t length_;
};
class TlsExtensionAppender : public TlsHandshakeFilter {
public:
TlsExtensionAppender(const std::shared_ptr<TlsAgent>& a,
uint8_t handshake_type, uint16_t ext, DataBuffer& data)
: TlsHandshakeFilter(a, {handshake_type}), extension_(ext), data_(data) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output);
private:
bool UpdateLength(DataBuffer* output, size_t offset, size_t size);
const uint16_t extension_;
const DataBuffer data_;
};
class TlsExtensionDropper : public TlsExtensionFilter {
public:
TlsExtensionDropper(const std::shared_ptr<TlsAgent>& a, uint16_t extension)
@ -470,8 +612,9 @@ class TlsEncryptedHandshakeMessageReplacer : public TlsRecordFilter {
uint16_t protection_epoch = 0;
uint8_t inner_content_type;
DataBuffer plaintext;
TlsRecordHeader out_header;
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext) ||
&plaintext, &out_header) ||
!plaintext.len()) {
return KEEP;
}
@ -500,12 +643,12 @@ class TlsEncryptedHandshakeMessageReplacer : public TlsRecordFilter {
}
DataBuffer ciphertext;
bool ok = Protect(spec(protection_epoch), header, inner_content_type,
plaintext, &ciphertext, 0);
bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
plaintext, &ciphertext, &out_header);
if (!ok) {
return KEEP;
}
*offset = header.Write(output, *offset, ciphertext);
*offset = out_header.Write(output, *offset, ciphertext);
return CHANGE;
}
@ -654,27 +797,16 @@ class SelectiveRecordDropFilter : public TlsRecordFilter {
uint8_t counter_;
};
// Set the version number in the ClientHello.
class TlsClientHelloVersionSetter : public TlsHandshakeFilter {
// Set the version value in the ClientHello, ServerHello or HelloRetryRequest
class TlsMessageVersionSetter : public TlsHandshakeFilter {
public:
TlsClientHelloVersionSetter(const std::shared_ptr<TlsAgent>& a,
uint16_t version)
: TlsHandshakeFilter(a, {kTlsHandshakeClientHello}), version_(version) {}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output);
private:
uint16_t version_;
};
// Set the version number in the ServerHello.
class TlsServerHelloVersionSetter : public TlsHandshakeFilter {
public:
TlsServerHelloVersionSetter(const std::shared_ptr<TlsAgent>& a,
uint16_t version)
: TlsHandshakeFilter(a, {kTlsHandshakeServerHello}), version_(version) {}
TlsMessageVersionSetter(const std::shared_ptr<TlsAgent>& a, uint8_t message,
uint16_t version)
: TlsHandshakeFilter(a, {message}), version_(version) {
PR_ASSERT(message == kTlsHandshakeClientHello ||
message == kTlsHandshakeServerHello ||
message == kTlsHandshakeHelloRetryRequest);
}
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
@ -722,6 +854,160 @@ class SelectedCipherSuiteReplacer : public TlsHandshakeFilter {
uint16_t cipher_suite_;
};
class ClientHelloPreambleCapture : public TlsHandshakeFilter {
public:
ClientHelloPreambleCapture(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeClientHello}),
captured_(false),
data_() {}
const DataBuffer& contents() const { return data_; }
bool captured() const { return captured_; }
protected:
PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) override;
private:
bool captured_;
DataBuffer data_;
};
class ClientHelloCiphersuiteCapture : public TlsHandshakeFilter {
public:
ClientHelloCiphersuiteCapture(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeClientHello}),
captured_(false),
data_() {}
const DataBuffer& contents() const { return data_; }
bool captured() const { return captured_; }
protected:
PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) override;
private:
bool captured_;
DataBuffer data_;
};
class ServerHelloRandomChanger : public TlsHandshakeFilter {
public:
ServerHelloRandomChanger(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeServerHello}) {}
protected:
PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) override;
};
// Replace SignatureAndHashAlgorithm of a SKE.
class DHEServerKEXSigAlgReplacer : public TlsHandshakeFilter {
public:
DHEServerKEXSigAlgReplacer(const std::shared_ptr<TlsAgent>& server,
uint16_t sig_scheme)
: TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}),
sig_scheme_(sig_scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
uint32_t len;
uint32_t idx = 0;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
EXPECT_TRUE(output->Read(idx, 2, &len));
idx += 2 + len;
output->Write(idx, sig_scheme_, 2);
return CHANGE;
}
private:
uint16_t sig_scheme_;
};
// Replace SignatureAndHashAlgorithm of a SKE.
class ECCServerKEXSigAlgReplacer : public TlsHandshakeFilter {
public:
ECCServerKEXSigAlgReplacer(const std::shared_ptr<TlsAgent>& server,
uint16_t sig_scheme)
: TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}),
sig_scheme_(sig_scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
uint32_t point_len;
EXPECT_TRUE(output->Read(3, 1, &point_len));
output->Write(4 + point_len, sig_scheme_, 2);
return CHANGE;
}
private:
uint16_t sig_scheme_;
};
// Replace NamedCurve of a ECDHE SKE.
class ECCServerKEXNamedCurveReplacer : public TlsHandshakeFilter {
public:
ECCServerKEXNamedCurveReplacer(const std::shared_ptr<TlsAgent>& server,
uint16_t curve_name)
: TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}),
curve_name_(curve_name) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
uint32_t curve_type;
EXPECT_TRUE(output->Read(0, 1, &curve_type));
EXPECT_EQ(curve_type, ec_type_named);
output->Write(1, curve_name_, 2);
return CHANGE;
}
private:
uint16_t curve_name_;
};
// Replaces the signature scheme in a CertificateVerify message.
class TlsReplaceSignatureSchemeFilter : public TlsHandshakeFilter {
public:
TlsReplaceSignatureSchemeFilter(const std::shared_ptr<TlsAgent>& a,
uint16_t scheme)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}),
scheme_(scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
output->Write(0, scheme_, 2);
return CHANGE;
}
private:
uint16_t scheme_;
};
} // namespace nss_test
#endif

View file

@ -0,0 +1,878 @@
/* -*- 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 "gtest_utils.h"
#include "tls_connect.h"
#include "util.h"
namespace nss_test {
const uint8_t kTlsGreaseExtensionMessages[] = {kTlsHandshakeEncryptedExtensions,
kTlsHandshakeCertificate};
const uint16_t kTlsGreaseValues[] = {
0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a,
0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa};
const uint8_t kTlsGreasePskValues[] = {0x0B, 0x2A, 0x49, 0x68,
0x87, 0xA6, 0xC5, 0xE4};
size_t countGreaseInBuffer(const DataBuffer& list) {
if (!list.len()) {
return 0;
}
size_t occurrence = 0;
for (uint16_t greaseVal : kTlsGreaseValues) {
for (size_t i = 0; i < (list.len() - 1); i += 2) {
uint16_t sample = list.data()[i + 1] + (list.data()[i] << 8);
if (greaseVal == sample) {
occurrence++;
}
}
}
return occurrence;
}
class GreasePresenceAbsenceTestBase : public TlsConnectTestBase {
public:
GreasePresenceAbsenceTestBase(SSLProtocolVariant variant, uint16_t version,
bool shouldGrease)
: TlsConnectTestBase(variant, version), set_grease_(shouldGrease){};
void SetupGrease() {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, set_grease_),
SECSuccess);
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, set_grease_),
SECSuccess);
}
bool expectGrease() {
return set_grease_ && version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
}
void checkGreasePresence(const int ifEnabled, const int ifDisabled,
const DataBuffer& buffer) {
size_t expected = expectGrease() ? size_t(ifEnabled) : size_t(ifDisabled);
EXPECT_EQ(expected, countGreaseInBuffer(buffer));
}
private:
bool set_grease_;
};
class GreasePresenceAbsenceTestAllVersions
: public GreasePresenceAbsenceTestBase,
public ::testing::WithParamInterface<
std::tuple<SSLProtocolVariant, uint16_t, bool>> {
public:
GreasePresenceAbsenceTestAllVersions()
: GreasePresenceAbsenceTestBase(std::get<0>(GetParam()),
std::get<1>(GetParam()),
std::get<2>(GetParam())){};
};
// Varies stream/datagram, TLS Version and whether GREASE is enabled
INSTANTIATE_TEST_SUITE_P(GreaseTests, GreasePresenceAbsenceTestAllVersions,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11Plus,
::testing::Values(true, false)));
// Varies whether GREASE is enabled for TLS13 only
class GreasePresenceAbsenceTestTlsStream13
: public GreasePresenceAbsenceTestBase,
public ::testing::WithParamInterface<bool> {
public:
GreasePresenceAbsenceTestTlsStream13()
: GreasePresenceAbsenceTestBase(
ssl_variant_stream, SSL_LIBRARY_VERSION_TLS_1_3, GetParam()){};
};
INSTANTIATE_TEST_SUITE_P(GreaseTests, GreasePresenceAbsenceTestTlsStream13,
::testing::Values(true, false));
// These tests check for the presence / absence of GREASE values in the various
// positions that we are permitted to add them. For positions which existed in
// prior versions of TLS, we check that enabling GREASE is only effective when
// negotiating TLS1.3 or higher and that disabling GREASE results in the absence
// of any GREASE values.
// For positions that specific to TLS1.3, we only check that enabling/disabling
// GREASE results in the correct presence/absence of the GREASE value.
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseCiphersuites) {
SetupGrease();
auto ch1 = MakeTlsFilter<ClientHelloCiphersuiteCapture>(client_);
Connect();
EXPECT_TRUE(ch1->captured());
checkGreasePresence(1, 0, ch1->contents());
}
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseNamedGroups) {
SetupGrease();
auto ch1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_supported_groups_xtn);
Connect();
EXPECT_TRUE(ch1->captured());
checkGreasePresence(1, 0, ch1->extension());
}
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseKeyShare) {
SetupGrease();
auto ch1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_key_share_xtn);
Connect();
EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_3) == ch1->captured());
checkGreasePresence(1, 0, ch1->extension());
}
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseSigAlg) {
SetupGrease();
auto ch1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
Connect();
EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_2) == ch1->captured());
checkGreasePresence(1, 0, ch1->extension());
}
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseSupportedVersions) {
SetupGrease();
auto ch1 = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_supported_versions_xtn);
Connect();
EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_3) == ch1->captured());
// Supported Versions have a 1 byte length field.
TlsParser extParser(ch1->extension());
DataBuffer versions;
extParser.ReadVariable(&versions, 1);
checkGreasePresence(1, 0, versions);
}
TEST_P(GreasePresenceAbsenceTestTlsStream13, ClientGreasePskExchange) {
SetupGrease();
auto ch1 = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_psk_key_exchange_modes_xtn);
Connect();
EXPECT_TRUE(ch1->captured());
// PSK Exchange Modes have a 1 byte length field
TlsParser extParser(ch1->extension());
DataBuffer modes;
extParser.ReadVariable(&modes, 1);
// Scan for single byte GREASE PSK Values
size_t numGrease = 0;
for (uint8_t greaseVal : kTlsGreasePskValues) {
for (unsigned long i = 0; i < modes.len(); i++) {
if (greaseVal == modes.data()[i]) {
numGrease++;
}
}
}
EXPECT_EQ(expectGrease() ? size_t(1) : size_t(0), numGrease);
}
TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseAlpn) {
SetupGrease();
EnableAlpn();
auto ch1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_app_layer_protocol_xtn);
Connect();
EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_1) == ch1->captured());
// ALPN Xtns have a redundant two-byte length
TlsParser alpnParser(ch1->extension());
alpnParser.Skip(2); // Skip the length
DataBuffer alpnEntry;
// Each ALPN entry has a single byte length prefixed.
size_t greaseAlpnEntrys = 0;
while (alpnParser.remaining()) {
alpnParser.ReadVariable(&alpnEntry, 1);
if (alpnEntry.len() == 2) {
greaseAlpnEntrys += countGreaseInBuffer(alpnEntry);
}
}
EXPECT_EQ(expectGrease() ? size_t(1) : size_t(0), greaseAlpnEntrys);
}
TEST_P(GreasePresenceAbsenceTestAllVersions, GreaseClientHelloExtension) {
SetupGrease();
auto ch1 =
MakeTlsFilter<TlsHandshakeRecorder>(client_, kTlsHandshakeClientHello);
Connect();
EXPECT_TRUE(ch1->buffer().len() > 0);
TlsParser extParser(ch1->buffer());
EXPECT_TRUE(extParser.Skip(2 + 32)); // Version + Random
EXPECT_TRUE(extParser.SkipVariable(1)); // Session ID
if (variant_ == ssl_variant_datagram) {
EXPECT_TRUE(extParser.SkipVariable(1)); // Cookie
}
EXPECT_TRUE(extParser.SkipVariable(2)); // Ciphersuites
EXPECT_TRUE(extParser.SkipVariable(1)); // Compression Methods
EXPECT_TRUE(extParser.Skip(2)); // Extension Lengths
// Scan for a 1-byte and a 0-byte extension.
uint32_t extType;
DataBuffer extBuf;
bool foundSmall = false;
bool foundLarge = false;
size_t numFound = 0;
while (extParser.remaining()) {
extParser.Read(&extType, 2);
extParser.ReadVariable(&extBuf, 2);
for (uint16_t greaseVal : kTlsGreaseValues) {
if (greaseVal == extType) {
numFound++;
foundSmall |= extBuf.len() == 0;
foundLarge |= extBuf.len() > 0;
}
}
}
EXPECT_EQ(foundSmall, expectGrease());
EXPECT_EQ(foundLarge, expectGrease());
EXPECT_EQ(numFound, expectGrease() ? size_t(2) : size_t(0));
}
TEST_P(GreasePresenceAbsenceTestTlsStream13, GreaseCertificateRequestSigAlg) {
SetupGrease();
client_->SetupClientAuth();
server_->RequestClientAuth(true);
auto cr =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_signature_algorithms_xtn);
cr->SetHandshakeTypes({kTlsHandshakeCertificateRequest});
cr->EnableDecryption();
Connect();
EXPECT_TRUE(cr->captured());
checkGreasePresence(1, 0, cr->extension());
}
TEST_P(GreasePresenceAbsenceTestTlsStream13,
GreaseCertificateRequestExtension) {
SetupGrease();
client_->SetupClientAuth();
server_->RequestClientAuth(true);
auto cr = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeCertificateRequest);
cr->EnableDecryption();
Connect();
EXPECT_TRUE(cr->buffer().len() > 0);
TlsParser extParser(cr->buffer());
EXPECT_TRUE(extParser.SkipVariable(1)); // Context
EXPECT_TRUE(extParser.Skip(2)); // Extension Lengths
uint32_t extType;
DataBuffer extBuf;
bool found = false;
// Scan for a single, empty extension
while (extParser.remaining()) {
extParser.Read(&extType, 2);
extParser.ReadVariable(&extBuf, 2);
for (uint16_t greaseVal : kTlsGreaseValues) {
if (greaseVal == extType) {
EXPECT_TRUE(!found);
EXPECT_EQ(extBuf.len(), size_t(0));
found = true;
}
}
}
EXPECT_EQ(expectGrease(), found);
}
TEST_P(GreasePresenceAbsenceTestTlsStream13, GreaseNewSessionTicketExtension) {
SetupGrease();
auto nst = MakeTlsFilter<TlsHandshakeRecorder>(server_,
kTlsHandshakeNewSessionTicket);
nst->EnableDecryption();
Connect();
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0));
EXPECT_TRUE(nst->buffer().len() > 0);
TlsParser extParser(nst->buffer());
EXPECT_TRUE(extParser.Skip(4)); // lifetime
EXPECT_TRUE(extParser.Skip(4)); // age
EXPECT_TRUE(extParser.SkipVariable(1)); // Nonce
EXPECT_TRUE(extParser.SkipVariable(2)); // Ticket
EXPECT_TRUE(extParser.Skip(2)); // Extension Length
uint32_t extType;
DataBuffer extBuf;
bool found = false;
// Scan for a single, empty extension
while (extParser.remaining()) {
extParser.Read(&extType, 2);
extParser.ReadVariable(&extBuf, 2);
for (uint16_t greaseVal : kTlsGreaseValues) {
if (greaseVal == extType) {
EXPECT_TRUE(!found);
EXPECT_EQ(extBuf.len(), size_t(0));
found = true;
}
}
}
EXPECT_EQ(expectGrease(), found);
}
// Generic Client GREASE test
TEST_P(TlsConnectGeneric, ClientGrease) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
Connect();
}
// Generic Server GREASE test
TEST_P(TlsConnectGeneric, ServerGrease) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
Connect();
}
// Generic GREASE test
TEST_P(TlsConnectGeneric, Grease) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
Connect();
}
// Check that GREASE values can be correctly reconstructed after HRR.
TEST_P(TlsConnectGeneric, GreaseHRR) {
EnsureTlsSetup();
const std::vector<SSLNamedGroup> client_groups = {
ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1};
const std::vector<SSLNamedGroup> server_groups = {
ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
client_->ConfigNamedGroups(client_groups);
server_->ConfigNamedGroups(server_groups);
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
Connect();
}
// Check that GREASE additions interact correctly with psk-only handshake.
TEST_F(TlsConnectStreamTls13, GreasePsk) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
const uint8_t kPskDummyVal_[16] = {0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
SECItem psk_item;
psk_item.type = siBuffer;
psk_item.len = sizeof(kPskDummyVal_);
psk_item.data = const_cast<uint8_t*>(kPskDummyVal_);
PK11SymKey* key =
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
CKA_DERIVE, &psk_item, NULL);
ScopedPK11SymKey scoped_psk_(key);
const std::string kPskDummyLabel_ = "NSS PSK GTEST label";
const SSLHashType kPskHash_ = ssl_hash_sha384;
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
// Test that ECH and GREASE work together successfully
TEST_F(TlsConnectStreamTls13, GreaseAndECH) {
EnsureTlsSetup();
SetupEch(client_, server_);
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
Connect();
}
// Test that TLS12 Server handles Client GREASE correctly
TEST_F(TlsConnectTest, GreaseTLS12Server) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_3);
Connect();
}
// Test that TLS12 Client handles Server GREASE correctly
TEST_F(TlsConnectTest, GreaseTLS12Client) {
EnsureTlsSetup();
ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
SECSuccess);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_3);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
SSL_LIBRARY_VERSION_TLS_1_2);
Connect();
}
class GreaseOnlyTestStreamTls13 : public TlsConnectStreamTls13 {
public:
GreaseOnlyTestStreamTls13() : TlsConnectStreamTls13() {}
void ConnectWithCustomChExpectFail(const std::string& ch,
uint8_t server_alert, uint32_t server_code,
uint32_t client_code) {
std::vector<uint8_t> ch_vec = hex_string_to_bytes(ch);
DataBuffer ch_buf;
EnsureTlsSetup();
TlsAgentTestBase::MakeRecord(variant_, ssl_ct_handshake,
SSL_LIBRARY_VERSION_TLS_1_3, ch_vec.data(),
ch_vec.size(), &ch_buf, 0);
StartConnect();
client_->SendDirect(ch_buf);
ExpectAlert(server_, server_alert);
server_->Handshake();
server_->CheckErrorCode(server_code);
client_->ExpectReceiveAlert(server_alert, kTlsAlertFatal);
client_->Handshake();
client_->CheckErrorCode(client_code);
}
};
// Client: Offer only GREASE CipherSuite value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientCipherSuite) {
// 0xdada
std::string ch =
"010000b003038afacda2963358e98f464f3ff0680ed3a9d382a8c3eac5e5604f5721add9"
"855c000002dada010000850000000b0009000006736572766572ff01000100000a001400"
"12001d00170018001901000101010201030104003300260024001d0020683668992de470"
"38660ee37bafc7392b05b8a94402ea1f3463ad3cfd7a694a46002b0003020304000d0018"
"001604030503060302030804080508060401050106010201002d00020101001c0002400"
"1";
ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
SSL_ERROR_NO_CYPHER_OVERLAP,
SSL_ERROR_NO_CYPHER_OVERLAP);
}
// Client: Offer only GREASE SupportedGroups value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSupportedGroup) {
// 0x3a3a
std::string ch =
"010000a40303484a4e14f547404da6115d7f73bbb0f1c9d65e66ac073dee6c4a62f72de9"
"a36f000006130113031302010000750000000b0009000006736572766572ff0100010000"
"0a000400023a3a003300260024001d0020e75cb8e217c95176954e8b5fb95843882462ce"
"2cd3fcfe67cf31463a05ea3d57002b0003020304000d0018001604030503060302030804"
"080508060401050106010201002d00020101001c00024001";
ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
SSL_ERROR_NO_CYPHER_OVERLAP,
SSL_ERROR_NO_CYPHER_OVERLAP);
}
// Client: Offer only GREASE SigAlgs value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSignatureAlgorithm) {
// 0x8a8a
std::string ch =
"010000a00303dfd8e2438a8d1b9f48d921dfc08959108807bd1105238bb3da2a2a8e3db0"
"6990000006130113031302010000710000000b0009000006736572766572ff0100010000"
"0a00140012001d00170018001901000101010201030104003300260024001d002074bb2c"
"94996d3ffc7ae5792f0c3c58676358a85ea304cd029fa3d6551013b333002b0003020304"
"000d000400028a8a002d00020101001c00024001";
ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM,
SSL_ERROR_NO_CYPHER_OVERLAP);
}
// Client: Offer only GREASE SupportedVersions value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSupportedVersion) {
// 0xeaea
std::string ch =
"010000b203037e3618abae0dd0b3f06a504c47354551d1d5be36e9c3e1eac9c139c246b1"
"66da000006130113031302010000830000000b0009000006736572766572ff0100010000"
"0a00140012001d00170018001901000101010201030104003300260024001d00206b1816"
"577ff2e69d4d2661419150eaefa0328ffd396425cf1733ec06536b4e55002b000100000d"
"0018001604030503060302030804080508060401050106010201002d00020101001c0002"
"4001";
ConnectWithCustomChExpectFail(ch, kTlsAlertIllegalParameter,
SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
class GreaseTestStreamTls12
: public TlsConnectStreamTls12,
public ::testing::WithParamInterface<uint16_t /* GREASE */> {
public:
GreaseTestStreamTls12() : TlsConnectStreamTls12(), grease_(GetParam()){};
void ConnectExpectSigAlgFail() {
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
protected:
uint16_t grease_;
};
class TlsCertificateRequestSigAlgSetterFilter : public TlsHandshakeFilter {
public:
TlsCertificateRequestSigAlgSetterFilter(const std::shared_ptr<TlsAgent>& a,
uint16_t sigAlg)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}),
sigAlg_(sigAlg) {}
virtual PacketFilter::Action FilterHandshake(
const TlsHandshakeFilter::HandshakeHeader& header,
const DataBuffer& input, DataBuffer* output) {
TlsParser parser(input);
DataBuffer cert_types;
if (!parser.ReadVariable(&cert_types, 1)) {
ADD_FAILURE();
return KEEP;
}
if (!parser.SkipVariable(2)) {
ADD_FAILURE();
return KEEP;
}
DataBuffer cas;
if (!parser.ReadVariable(&cas, 2)) {
ADD_FAILURE();
return KEEP;
}
size_t idx = 0;
// Write certificate types.
idx = output->Write(idx, cert_types.len(), 1);
idx = output->Write(idx, cert_types);
// Write signature algorithm.
idx = output->Write(idx, sizeof(sigAlg_), 2);
idx = output->Write(idx, sigAlg_, 2);
// Write certificate authorities.
idx = output->Write(idx, cas.len(), 2);
idx = output->Write(idx, cas);
return CHANGE;
}
private:
uint16_t sigAlg_;
};
// Server: Offer only GREASE CertificateRequest SigAlg value
TEST_P(GreaseTestStreamTls12, GreaseOnlyServerTLS12CertificateRequestSigAlg) {
EnsureTlsSetup();
client_->SetupClientAuth();
server_->RequestClientAuth(true);
MakeTlsFilter<TlsCertificateRequestSigAlgSetterFilter>(server_, grease_);
client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
server_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
ConnectExpectFail();
server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
}
// Illegally GREASE ServerKeyExchange ECC SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexEccSigAlg) {
MakeTlsFilter<ECCServerKEXSigAlgReplacer>(server_, grease_);
EnableSomeEcdhCiphers();
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Illegally GREASE ServerKeyExchange DHE SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexDheSigAlg) {
MakeTlsFilter<DHEServerKEXSigAlgReplacer>(server_, grease_);
EnableOnlyDheCiphers();
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Illegally GREASE ServerKeyExchange ECDHE NamedCurve
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexEcdheNamedCurve) {
MakeTlsFilter<ECCServerKEXNamedCurveReplacer>(server_, grease_);
EnableSomeEcdhCiphers();
client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
server_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
ConnectExpectFail();
server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
client_->CheckErrorCode(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
}
// Illegally GREASE TLS12 Client CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ClientCertificateVerifySigAlg) {
client_->SetupClientAuth();
server_->RequestClientAuth(true);
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, grease_);
server_->ExpectSendAlert(kTlsAlertIllegalParameter);
client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
}
class GreaseTestStreamTls13
: public TlsConnectStreamTls13,
public ::testing::WithParamInterface<uint16_t /* GREASE */> {
public:
GreaseTestStreamTls13() : grease_(GetParam()){};
protected:
uint16_t grease_;
};
// Illegally GREASE TLS13 Client CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls13, GreasedTLS13ClientCertificateVerifySigAlg) {
client_->SetupClientAuth();
server_->RequestClientAuth(true);
auto filter =
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, grease_);
filter->EnableDecryption();
server_->ExpectSendAlert(kTlsAlertIllegalParameter);
client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
// Manually trigger handshake to avoid race conditions
StartConnect();
client_->Handshake();
server_->Handshake();
client_->Handshake();
server_->Handshake();
client_->Handshake();
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Illegally GREASE TLS13 Server CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls13, GreasedTLS13ServerCertificateVerifySigAlg) {
EnsureTlsSetup();
auto filter =
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, grease_);
filter->EnableDecryption();
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
}
// Illegally GREASE HelloRetryRequest version value
TEST_P(GreaseTestStreamTls13, GreasedHelloRetryRequestVersion) {
EnsureTlsSetup();
// Trigger HelloRetryRequest
MakeTlsFilter<TlsExtensionDropper>(client_, ssl_tls13_key_share_xtn);
auto filter = MakeTlsFilter<TlsMessageVersionSetter>(
server_, kTlsHandshakeHelloRetryRequest, grease_);
filter->EnableDecryption();
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
class GreaseTestStreamTls123
: public TlsConnectTestBase,
public ::testing::WithParamInterface<
std::tuple<uint16_t /* version */, uint16_t /* GREASE */>> {
public:
GreaseTestStreamTls123()
: TlsConnectTestBase(ssl_variant_stream, std::get<0>(GetParam())),
grease_(std::get<1>(GetParam())){};
void ConnectExpectIllegalGreaseFail() {
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// Server expects handshake but receives encrypted alert.
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
} else {
server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
}
ConnectExpectFail();
}
protected:
uint16_t grease_;
};
// Illegally GREASE TLS12 and TLS13 ServerHello version value
TEST_P(GreaseTestStreamTls123, GreasedServerHelloVersion) {
EnsureTlsSetup();
auto filter = MakeTlsFilter<TlsMessageVersionSetter>(
server_, kTlsHandshakeServerHello, grease_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
filter->EnableDecryption();
}
ConnectExpectIllegalGreaseFail();
client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
}
// Illegally GREASE TLS12 and TLS13 selected CipherSuite value
TEST_P(GreaseTestStreamTls123, GreasedServerHelloCipherSuite) {
EnsureTlsSetup();
auto filter = MakeTlsFilter<SelectedCipherSuiteReplacer>(server_, grease_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
filter->EnableDecryption();
}
ConnectExpectIllegalGreaseFail();
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
class GreaseExtensionTestStreamTls13
: public TlsConnectStreamTls13,
public ::testing::WithParamInterface<
std::tuple<uint8_t /* message */, uint16_t /* GREASE */>> {
public:
GreaseExtensionTestStreamTls13()
: TlsConnectStreamTls13(),
message_(std::get<0>(GetParam())),
grease_(std::get<1>(GetParam())){};
protected:
uint8_t message_;
uint16_t grease_;
};
// Illegally GREASE TLS13 Server EncryptedExtensions and Certificate Extensions
// NSS currently allows offering unkown extensions in HelloRetryRequests!
TEST_P(GreaseExtensionTestStreamTls13, GreasedServerExtensions) {
EnsureTlsSetup();
DataBuffer empty = DataBuffer(1);
auto filter =
MakeTlsFilter<TlsExtensionAppender>(server_, message_, grease_, empty);
filter->EnableDecryption();
server_->ExpectReceiveAlert(kTlsAlertUnsupportedExtension);
client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_EXTENSION_ALERT);
}
// Illegally GREASE TLS12 and TLS13 ServerHello Extensions
TEST_P(GreaseTestStreamTls123, GreasedServerHelloExtensions) {
EnsureTlsSetup();
DataBuffer empty = DataBuffer(1);
auto filter = MakeTlsFilter<TlsExtensionAppender>(
server_, kTlsHandshakeServerHello, grease_, empty);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
filter->EnableDecryption();
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
} else {
server_->ExpectReceiveAlert(kTlsAlertUnsupportedExtension);
}
client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
}
// Illegally GREASE TLS13 Client Certificate Extensions
// Server ignores injected client extensions and fails on CertificateVerify
TEST_P(GreaseTestStreamTls13, GreasedClientCertificateExtensions) {
client_->SetupClientAuth();
server_->RequestClientAuth(true);
DataBuffer empty = DataBuffer(1);
auto filter = MakeTlsFilter<TlsExtensionAppender>(
client_, kTlsHandshakeCertificate, grease_, empty);
filter->EnableDecryption();
server_->ExpectSendAlert(kTlsAlertDecryptError);
client_->ExpectReceiveAlert(kTlsAlertDecryptError);
// Manually trigger handshake to avoid race conditions
StartConnect();
client_->Handshake();
server_->Handshake();
client_->Handshake();
server_->Handshake();
client_->Handshake();
server_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
}
TEST_F(TlsConnectStreamTls13, GreaseClientHelloExtensionPermutation) {
EnsureTlsSetup();
PR_ASSERT(SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_CH_EXTENSION_PERMUTATION,
PR_TRUE) == SECSuccess);
PR_ASSERT(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE) ==
SECSuccess);
Connect();
}
INSTANTIATE_TEST_SUITE_P(GreaseTestTls12, GreaseTestStreamTls12,
::testing::ValuesIn(kTlsGreaseValues));
INSTANTIATE_TEST_SUITE_P(GreaseTestTls13, GreaseTestStreamTls13,
::testing::ValuesIn(kTlsGreaseValues));
INSTANTIATE_TEST_SUITE_P(
GreaseTestTls123, GreaseTestStreamTls123,
::testing::Combine(TlsConnectTestBase::kTlsV12Plus,
::testing::ValuesIn(kTlsGreaseValues)));
INSTANTIATE_TEST_SUITE_P(
GreaseExtensionTest, GreaseExtensionTestStreamTls13,
testing::Combine(testing::ValuesIn(kTlsGreaseExtensionMessages),
testing::ValuesIn(kTlsGreaseValues)));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -67,18 +68,6 @@ size_t GetHashLength(SSLHashType hash) {
return 0;
}
CK_MECHANISM_TYPE GetHkdfMech(SSLHashType hash) {
switch (hash) {
case ssl_hash_sha256:
return CKM_NSS_HKDF_SHA256;
case ssl_hash_sha384:
return CKM_NSS_HKDF_SHA384;
default:
ADD_FAILURE() << "Unknown hash: " << hash;
}
return CKM_INVALID_MECHANISM;
}
PRUint16 GetSomeCipherSuiteForHash(SSLHashType hash) {
switch (hash) {
case ssl_hash_sha256:
@ -172,7 +161,7 @@ class TlsHkdfTest : public ::testing::Test,
ScopedPK11SymKey prkk(prk);
DumpKey("Output", prkk);
VerifyKey(prkk, GetHkdfMech(base_hash), expected);
VerifyKey(prkk, CKM_HKDF_DERIVE, expected);
// Now test the public wrapper.
PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
@ -180,20 +169,21 @@ class TlsHkdfTest : public ::testing::Test,
ikmk2.get(), &prk);
ASSERT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(prk), GetHkdfMech(base_hash), expected);
VerifyKey(ScopedPK11SymKey(prk), CKM_HKDF_DERIVE, expected);
}
void HkdfExpandLabel(ScopedPK11SymKey* prk, SSLHashType base_hash,
const uint8_t* session_hash, size_t session_hash_len,
const char* label, size_t label_len,
const DataBuffer& expected) {
ASSERT_NE(nullptr, prk);
std::cerr << "Hash = " << kHashName[base_hash] << std::endl;
std::vector<uint8_t> output(expected.len());
SECStatus rv = tls13_HkdfExpandLabelRaw(prk->get(), base_hash, session_hash,
session_hash_len, label, label_len,
&output[0], output.size());
SECStatus rv = tls13_HkdfExpandLabelRaw(
prk->get(), base_hash, session_hash, session_hash_len, label, label_len,
ssl_variant_stream, &output[0], output.size());
ASSERT_EQ(SECSuccess, rv);
DumpData("Output", &output[0], output.size());
EXPECT_EQ(0, memcmp(expected.data(), &output[0], expected.len()));
@ -205,15 +195,15 @@ class TlsHkdfTest : public ::testing::Test,
session_hash, session_hash_len, label, label_len,
&secret);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(secret), GetHkdfMech(base_hash), expected);
ASSERT_NE(nullptr, secret);
VerifyKey(ScopedPK11SymKey(secret), CKM_HKDF_DERIVE, expected);
// Verify that a key can be created with a different key type and size.
rv = SSL_HkdfExpandLabelWithMech(
SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(), session_hash,
session_hash_len, label, label_len, CKM_DES3_CBC_PAD, 24, &secret);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
ASSERT_NE(nullptr, secret);
ScopedPK11SymKey with_mech(secret);
EXPECT_EQ(static_cast<CK_MECHANISM_TYPE>(CKM_DES3_CBC_PAD),
PK11_GetMechanism(with_mech.get()));
@ -437,7 +427,7 @@ TEST_P(TlsHkdfTest, BadExpandLabelWrapperInput) {
}
static const SSLHashType kHashTypes[] = {ssl_hash_sha256, ssl_hash_sha384};
INSTANTIATE_TEST_CASE_P(AllHashFuncs, TlsHkdfTest,
::testing::ValuesIn(kHashTypes));
INSTANTIATE_TEST_SUITE_P(AllHashFuncs, TlsHkdfTest,
::testing::ValuesIn(kHashTypes));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -24,39 +25,68 @@ TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc)
bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo,
PK11SymKey* secret) {
SSLAeadContext* ctx;
SECStatus rv = SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3,
cipherinfo->cipherSuite, secret, "",
0, // Use the default labels.
&ctx);
SSLAeadContext* aead_ctx;
SSLProtocolVariant variant =
dtls_ ? ssl_variant_datagram : ssl_variant_stream;
SECStatus rv =
SSL_MakeVariantAead(SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite,
variant, secret, "", 0, // Use the default labels.
&aead_ctx);
if (rv != SECSuccess) {
return false;
}
aead_.reset(ctx);
aead_.reset(aead_ctx);
SSLMaskingContext* mask_ctx;
const char kHkdfPurposeSn[] = "sn";
rv = SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite, variant, secret,
kHkdfPurposeSn, strlen(kHkdfPurposeSn), &mask_ctx);
if (rv != SECSuccess) {
return false;
}
mask_.reset(mask_ctx);
return true;
}
bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
DataBuffer* plaintext) {
if (aead_ == nullptr) {
DataBuffer* plaintext,
TlsRecordHeader* out_header) {
if (!aead_ || !out_header) {
return false;
}
*out_header = header;
// Make space.
plaintext->Allocate(ciphertext.len());
auto header_bytes = header.header();
unsigned int len;
uint64_t seqno;
if (dtls_) {
seqno = header.sequence_number();
} else {
seqno = in_seqno_;
uint64_t seqno = dtls_ ? header.sequence_number() : in_seqno_;
SECStatus rv;
if (header.is_dtls13_ciphertext()) {
if (!mask_ || !out_header) {
return false;
}
PORT_Assert(ciphertext.len() >= 16);
DataBuffer mask(2);
rv = SSL_CreateMask(mask_.get(), ciphertext.data(), ciphertext.len(),
mask.data(), mask.len());
if (rv != SECSuccess) {
return false;
}
if (!out_header->MaskSequenceNumber(mask)) {
return false;
}
seqno = out_header->sequence_number();
}
SECStatus rv =
SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), ciphertext.data(), ciphertext.len(),
plaintext->data(), &len, plaintext->len());
auto header_bytes = out_header->header();
rv = SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), ciphertext.data(), ciphertext.len(),
plaintext->data(), &len, plaintext->len());
if (rv != SECSuccess) {
return false;
}
@ -68,11 +98,14 @@ bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
}
bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
const DataBuffer& plaintext,
DataBuffer* ciphertext) {
if (aead_ == nullptr) {
const DataBuffer& plaintext, DataBuffer* ciphertext,
TlsRecordHeader* out_header) {
if (!aead_ || !out_header) {
return false;
}
*out_header = header;
// Make a padded buffer.
ciphertext->Allocate(plaintext.len() +
32); // Room for any plausible auth tag
@ -80,12 +113,7 @@ bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
DataBuffer header_bytes;
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
uint64_t seqno;
if (dtls_) {
seqno = header.sequence_number();
} else {
seqno = out_seqno_;
}
uint64_t seqno = dtls_ ? header.sequence_number() : out_seqno_;
SECStatus rv =
SSL_AeadEncrypt(aead_.get(), seqno, header_bytes.data(),
@ -95,6 +123,22 @@ bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
return false;
}
if (header.is_dtls13_ciphertext()) {
if (!mask_ || !out_header) {
return false;
}
PORT_Assert(ciphertext->len() >= 16);
DataBuffer mask(2);
rv = SSL_CreateMask(mask_.get(), ciphertext->data(), ciphertext->len(),
mask.data(), mask.len());
if (rv != SECSuccess) {
return false;
}
if (!out_header->MaskSequenceNumber(mask)) {
return false;
}
}
RecordProtected();
ciphertext->Truncate(len);

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -26,9 +27,9 @@ class TlsCipherSpec {
bool SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret);
bool Protect(const TlsRecordHeader& header, const DataBuffer& plaintext,
DataBuffer* ciphertext);
DataBuffer* ciphertext, TlsRecordHeader* out_header);
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext,
DataBuffer* plaintext);
DataBuffer* plaintext, TlsRecordHeader* out_header);
uint16_t epoch() const { return epoch_; }
uint64_t next_in_seqno() const { return in_seqno_; }
@ -51,6 +52,7 @@ class TlsCipherSpec {
uint64_t out_seqno_;
bool record_dropped_ = false;
ScopedSSLAeadContext aead_;
ScopedSSLMaskingContext mask_;
};
} // namespace nss_test

View file

@ -0,0 +1,515 @@
/* -*- 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 <functional>
#include <memory>
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
#include "sslproto.h"
#include "gtest_utils.h"
#include "tls_connect.h"
namespace nss_test {
class Tls13PskTest : public TlsConnectTestBase,
public ::testing::WithParamInterface<
std::tuple<SSLProtocolVariant, uint16_t>> {
public:
Tls13PskTest()
: TlsConnectTestBase(std::get<0>(GetParam()),
SSL_LIBRARY_VERSION_TLS_1_3),
suite_(std::get<1>(GetParam())) {}
void SetUp() override {
TlsConnectTestBase::SetUp();
scoped_psk_.reset(GetPsk());
ASSERT_TRUE(!!scoped_psk_);
}
private:
PK11SymKey* GetPsk() {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE();
return nullptr;
}
SECItem psk_item;
psk_item.type = siBuffer;
psk_item.len = sizeof(kPskDummyVal_);
psk_item.data = const_cast<uint8_t*>(kPskDummyVal_);
PK11SymKey* key =
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
CKA_DERIVE, &psk_item, NULL);
if (!key) {
ADD_FAILURE();
}
return key;
}
protected:
ScopedPK11SymKey scoped_psk_;
const uint16_t suite_;
const uint8_t kPskDummyVal_[16] = {0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
const std::string kPskDummyLabel_ = "NSS PSK GTEST label";
const SSLHashType kPskHash_ = ssl_hash_sha384;
};
// TLS 1.3 PSK connection test.
TEST_P(Tls13PskTest, NormalExternal) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
client_->RemovePsk(kPskDummyLabel_);
server_->RemovePsk(kPskDummyLabel_);
// Removing it again should fail.
EXPECT_EQ(SECFailure, SSL_RemoveExternalPsk(client_->ssl_fd(),
reinterpret_cast<const uint8_t*>(
kPskDummyLabel_.data()),
kPskDummyLabel_.length()));
EXPECT_EQ(SECFailure, SSL_RemoveExternalPsk(server_->ssl_fd(),
reinterpret_cast<const uint8_t*>(
kPskDummyLabel_.data()),
kPskDummyLabel_.length()));
}
TEST_P(Tls13PskTest, KeyTooLarge) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(!!slot);
ScopedPK11SymKey scoped_psk(PK11_KeyGen(
slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 128, nullptr));
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
// Attempt to use a PSK with the wrong PRF hash.
// "Clients MUST verify that...the server selected a cipher suite
// indicating a Hash associated with the PSK"
TEST_P(Tls13PskTest, ClientVerifyHashType) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
MakeTlsFilter<SelectedCipherSuiteReplacer>(server_,
TLS_CHACHA20_POLY1305_SHA256);
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
if (variant_ == ssl_variant_stream) {
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
ConnectExpectFail();
EXPECT_EQ(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE, server_->error_code());
} else {
ConnectExpectFailOneSide(TlsAgent::CLIENT);
}
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
}
// Different EPSKs (by label) on each endpoint. Expect cert auth.
TEST_P(Tls13PskTest, LabelMismatch) {
client_->AddPsk(scoped_psk_, std::string("foo"), kPskHash_);
server_->AddPsk(scoped_psk_, std::string("bar"), kPskHash_);
Connect();
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
}
SSLHelloRetryRequestAction RetryFirstHello(
PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen,
PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax,
void* arg) {
auto* called = reinterpret_cast<size_t*>(arg);
++*called;
EXPECT_EQ(0U, clientTokenLen);
EXPECT_EQ(*called, firstHello ? 1U : 2U);
return firstHello ? ssl_hello_retry_request : ssl_hello_retry_accept;
}
// Test resumption PSK with HRR.
TEST_P(Tls13PskTest, ResPskRetryStateless) {
ConfigureSelfEncrypt();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys();
Reset();
StartConnect();
size_t cb_called = 0;
EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
server_->ssl_fd(), RetryFirstHello, &cb_called));
ExpectResumption(RESUME_TICKET);
Handshake();
CheckConnected();
EXPECT_EQ(2U, cb_called);
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
SendReceive();
}
// Test external PSK with HRR.
TEST_P(Tls13PskTest, ExtPskRetryStateless) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
size_t cb_called = 0;
EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
server_->ssl_fd(), RetryFirstHello, &cb_called));
StartConnect();
client_->Handshake();
server_->Handshake();
EXPECT_EQ(1U, cb_called);
auto replacement = std::make_shared<TlsAgent>(
server_->name(), TlsAgent::SERVER, server_->variant());
server_ = replacement;
server_->SetVersionRange(version_, version_);
client_->SetPeer(server_);
server_->SetPeer(client_);
server_->AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
server_->ExpectPsk();
server_->StartConnect();
Handshake();
CheckConnected();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
// Server not configured with PSK and sends a certificate instead of
// a selected_identity. Client should attempt certificate authentication.
TEST_P(Tls13PskTest, ClientOnly) {
client_->AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
Connect();
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
}
// Set a PSK, remove psk_key_exchange_modes.
TEST_P(Tls13PskTest, DropKexModes) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
StartConnect();
MakeTlsFilter<TlsExtensionDropper>(client_,
ssl_tls13_psk_key_exchange_modes_xtn);
ConnectExpectAlert(server_, kTlsAlertMissingExtension);
client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
server_->CheckErrorCode(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES);
}
// "Clients MUST verify that...a server "key_share" extension is present
// if required by the ClientHello "psk_key_exchange_modes" extension."
// As we don't support PSK without DH, it is always required.
TEST_P(Tls13PskTest, DropRequiredKeyShare) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
StartConnect();
MakeTlsFilter<TlsExtensionDropper>(server_, ssl_tls13_key_share_xtn);
client_->ExpectSendAlert(kTlsAlertMissingExtension);
if (variant_ == ssl_variant_stream) {
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
ConnectExpectFail();
} else {
ConnectExpectFailOneSide(TlsAgent::CLIENT);
}
client_->CheckErrorCode(SSL_ERROR_MISSING_KEY_SHARE);
}
// "Clients MUST verify that...the server's selected_identity is
// within the range supplied by the client". We send one OfferedPsk.
TEST_P(Tls13PskTest, InvalidSelectedIdentity) {
static const uint8_t selected_identity[] = {0x00, 0x01};
DataBuffer buf(selected_identity, sizeof(selected_identity));
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
StartConnect();
MakeTlsFilter<TlsExtensionReplacer>(server_, ssl_tls13_pre_shared_key_xtn,
buf);
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
if (variant_ == ssl_variant_stream) {
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
ConnectExpectFail();
} else {
ConnectExpectFailOneSide(TlsAgent::CLIENT);
}
client_->CheckErrorCode(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
}
// Resume-eligible reconnect with an EPSK configured.
// Expect the EPSK to be used.
TEST_P(Tls13PskTest, PreferEpsk) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys();
Reset();
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
ExpectResumption(RESUME_NONE);
StartConnect();
Handshake();
CheckConnected();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
// Enable resumption, but connect (initially) with an EPSK.
// Expect no session ticket.
TEST_P(Tls13PskTest, SuppressNewSessionTicket) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
auto nst_capture =
MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket);
nst_capture->EnableDecryption();
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0));
EXPECT_EQ(0U, nst_capture->buffer().len());
if (variant_ == ssl_variant_stream) {
EXPECT_EQ(SSL_ERROR_FEATURE_DISABLED, PORT_GetError());
} else {
EXPECT_EQ(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION, PORT_GetError());
}
Reset();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
ExpectResumption(RESUME_NONE);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
TEST_P(Tls13PskTest, BadConfigValues) {
EXPECT_TRUE(client_->EnsureTlsSetup());
std::vector<uint8_t> label{'L', 'A', 'B', 'E', 'L'};
EXPECT_EQ(SECFailure,
SSL_AddExternalPsk(client_->ssl_fd(), nullptr, label.data(),
label.size(), kPskHash_));
EXPECT_EQ(SECFailure, SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(),
nullptr, label.size(), kPskHash_));
EXPECT_EQ(SECFailure, SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(),
label.data(), 0, kPskHash_));
EXPECT_EQ(SECSuccess,
SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(),
label.data(), label.size(), ssl_hash_sha256));
EXPECT_EQ(SECFailure,
SSL_RemoveExternalPsk(client_->ssl_fd(), nullptr, label.size()));
EXPECT_EQ(SECFailure,
SSL_RemoveExternalPsk(client_->ssl_fd(), label.data(), 0));
EXPECT_EQ(SECSuccess, SSL_RemoveExternalPsk(client_->ssl_fd(), label.data(),
label.size()));
}
// If the server has an EPSK configured with a ciphersuite not supported
// by the client, it should use certificate authentication.
TEST_P(Tls13PskTest, FallbackUnsupportedCiphersuite) {
client_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256,
TLS_AES_128_GCM_SHA256);
server_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256,
TLS_CHACHA20_POLY1305_SHA256);
client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
}
// That fallback should not occur if there is no cipher overlap.
TEST_P(Tls13PskTest, ExplicitSuiteNoOverlap) {
client_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256,
TLS_AES_128_GCM_SHA256);
server_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256,
TLS_CHACHA20_POLY1305_SHA256);
client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256);
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
TEST_P(Tls13PskTest, SuppressHandshakeCertReq) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
server_->SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE);
server_->SetOption(SSL_REQUIRE_CERTIFICATE, PR_TRUE);
const std::set<uint8_t> hs_types = {ssl_hs_certificate,
ssl_hs_certificate_request};
auto cr_cert_capture = MakeTlsFilter<TlsHandshakeRecorder>(server_, hs_types);
cr_cert_capture->EnableDecryption();
Connect();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
EXPECT_EQ(0U, cr_cert_capture->buffer().len());
}
TEST_P(Tls13PskTest, DisallowClientConfigWithoutServerCert) {
AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);
server_->SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE);
server_->SetOption(SSL_REQUIRE_CERTIFICATE, PR_TRUE);
const std::set<uint8_t> hs_types = {ssl_hs_certificate,
ssl_hs_certificate_request};
auto cr_cert_capture = MakeTlsFilter<TlsHandshakeRecorder>(server_, hs_types);
cr_cert_capture->EnableDecryption();
EXPECT_EQ(SECSuccess, SSLInt_RemoveServerCertificates(server_->ssl_fd()));
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
EXPECT_EQ(0U, cr_cert_capture->buffer().len());
}
TEST_F(TlsConnectStreamTls13, ClientRejectHandshakeCertReq) {
// Stream only, as the filter doesn't support DTLS 1.3 yet.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(!!slot);
ScopedPK11SymKey scoped_psk(PK11_KeyGen(
slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 32, nullptr));
AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256);
// Inject a CR after EE. This would be legal if not for ssl_auth_psk.
auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
server_, kTlsHandshakeFinished, kTlsHandshakeCertificateRequest);
filter->EnableDecryption();
ExpectAlert(client_, kTlsAlertUnexpectedMessage);
ConnectExpectFail();
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST);
server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
TEST_F(TlsConnectStreamTls13, RejectPha) {
// Stream only, as the filter doesn't support DTLS 1.3 yet.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(!!slot);
ScopedPK11SymKey scoped_psk(PK11_KeyGen(
slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 32, nullptr));
AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256);
server_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
auto kuToCr = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
server_, kTlsHandshakeKeyUpdate, kTlsHandshakeCertificateRequest);
kuToCr->EnableDecryption();
Connect();
// Make sure the direct path is blocked.
EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
EXPECT_EQ(SSL_ERROR_FEATURE_DISABLED, PORT_GetError());
// Inject a PHA CR. Since this is not allowed, send KeyUpdate
// and change the message type.
EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
ExpectAlert(client_, kTlsAlertUnexpectedMessage);
client_->Handshake(); // Eat the CR.
server_->Handshake();
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST);
server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
class Tls13PskTestWithCiphers : public Tls13PskTest {};
TEST_P(Tls13PskTestWithCiphers, 0RttCiphers) {
RolloverAntiReplay();
AddPsk(scoped_psk_, kPskDummyLabel_, tls13_GetHashForCipherSuite(suite_),
suite_);
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
}
TEST_P(Tls13PskTestWithCiphers, 0RttMaxEarlyData) {
EnsureTlsSetup();
RolloverAntiReplay();
const char* big_message = "0123456789abcdef";
const size_t short_size = strlen(big_message) - 1;
const PRInt32 short_length = static_cast<PRInt32>(short_size);
// Set up the PSK
EXPECT_EQ(SECSuccess,
SSL_AddExternalPsk0Rtt(
client_->ssl_fd(), scoped_psk_.get(),
reinterpret_cast<const uint8_t*>(kPskDummyLabel_.data()),
kPskDummyLabel_.length(), tls13_GetHashForCipherSuite(suite_),
suite_, short_length));
EXPECT_EQ(SECSuccess,
SSL_AddExternalPsk0Rtt(
server_->ssl_fd(), scoped_psk_.get(),
reinterpret_cast<const uint8_t*>(kPskDummyLabel_.data()),
kPskDummyLabel_.length(), tls13_GetHashForCipherSuite(suite_),
suite_, short_length));
client_->ExpectPsk();
server_->ExpectPsk();
client_->expected_cipher_suite(suite_);
server_->expected_cipher_suite(suite_);
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
client_->Handshake();
CheckEarlyDataLimit(client_, short_size);
PRInt32 sent;
// Writing more than the limit will succeed in TLS, but fail in DTLS.
if (variant_ == ssl_variant_stream) {
sent = PR_Write(client_->ssl_fd(), big_message,
static_cast<PRInt32>(strlen(big_message)));
} else {
sent = PR_Write(client_->ssl_fd(), big_message,
static_cast<PRInt32>(strlen(big_message)));
EXPECT_GE(0, sent);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
// Try an exact-sized write now.
sent = PR_Write(client_->ssl_fd(), big_message, short_length);
}
EXPECT_EQ(short_length, sent);
// Even a single octet write should now fail.
sent = PR_Write(client_->ssl_fd(), big_message, 1);
EXPECT_GE(0, sent);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
// Process the ClientHello and read 0-RTT.
server_->Handshake();
CheckEarlyDataLimit(server_, short_size);
std::vector<uint8_t> buf(short_size + 1);
PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
EXPECT_EQ(short_length, read);
EXPECT_EQ(0, memcmp(big_message, buf.data(), short_size));
// Second read fails.
read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
EXPECT_EQ(SECFailure, read);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}
static const uint16_t k0RttCipherDefs[] = {TLS_CHACHA20_POLY1305_SHA256,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384};
static const uint16_t kDefaultSuite[] = {TLS_CHACHA20_POLY1305_SHA256};
INSTANTIATE_TEST_SUITE_P(
Tls13PskTest, Tls13PskTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
::testing::ValuesIn(kDefaultSuite)));
INSTANTIATE_TEST_SUITE_P(
Tls13PskTestWithCiphers, Tls13PskTestWithCiphers,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
::testing::ValuesIn(k0RttCipherDefs)));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -8,6 +9,8 @@
#include "prtime.h"
#include "secerr.h"
#include "ssl.h"
#include "nss.h"
#include "blapit.h"
#include "gtest_utils.h"
#include "tls_agent.h"
@ -17,9 +20,10 @@ namespace nss_test {
const std::string kEcdsaDelegatorId = TlsAgent::kDelegatorEcdsa256;
const std::string kRsaeDelegatorId = TlsAgent::kDelegatorRsae2048;
const std::string kPssDelegatorId = TlsAgent::kDelegatorRsaPss2048;
const std::string kDCId = TlsAgent::kServerEcdsa256;
const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256;
const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */;
const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds) */;
static void CheckPreliminaryPeerDelegCred(
const std::shared_ptr<TlsAgent>& client, bool expected,
@ -121,6 +125,23 @@ TEST_P(TlsConnectTls13, DCConnectEcdsaP256) {
EXPECT_EQ(ssl_sig_ecdsa_secp256r1_sha256, client_->info().signatureScheme);
}
// Connected with ECDSA-P384.
TEST_P(TlsConnectTls13, DCConnectEcdsaP483) {
Reset(kEcdsaDelegatorId);
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(TlsAgent::kServerEcdsa384,
ssl_sig_ecdsa_secp384r1_sha384, kDCValidFor,
now());
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_TRUE(cfilter->captured());
CheckPeerDelegCred(client_, true, 384);
EXPECT_EQ(ssl_sig_ecdsa_secp384r1_sha384, client_->info().signatureScheme);
}
// Connected with ECDSA-P521.
TEST_P(TlsConnectTls13, DCConnectEcdsaP521) {
Reset(kEcdsaDelegatorId);
@ -139,46 +160,8 @@ TEST_P(TlsConnectTls13, DCConnectEcdsaP521) {
EXPECT_EQ(ssl_sig_ecdsa_secp521r1_sha512, client_->info().signatureScheme);
}
// Connected with RSA-PSS, using an RSAE DC SPKI.
TEST_P(TlsConnectTls13, DCConnectRsaPssRsae) {
Reset(kEcdsaDelegatorId);
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(
TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now());
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_TRUE(cfilter->captured());
CheckPeerDelegCred(client_, true, 1024);
EXPECT_EQ(ssl_sig_rsa_pss_rsae_sha256, client_->info().signatureScheme);
}
// Connected with RSA-PSS, using a RSAE Delegator SPKI.
TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) {
Reset(kRsaeDelegatorId);
static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(
TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now());
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_TRUE(cfilter->captured());
CheckPeerDelegCred(client_, true, 1024);
EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme);
}
// Connected with RSA-PSS, using a PSS SPKI.
TEST_P(TlsConnectTls13, DCConnectRsaPssPss) {
// Connected with RSA-PSS, using a PSS SPKI and ECDSA delegation cert.
TEST_P(TlsConnectTls13, DCConnectRsaPssEcdsa) {
Reset(kEcdsaDelegatorId);
// Need to enable PSS-PSS, which is not on by default.
@ -200,6 +183,166 @@ TEST_P(TlsConnectTls13, DCConnectRsaPssPss) {
EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme);
}
// Connected with RSA-PSS, using a PSS SPKI and PSS delegation cert.
TEST_P(TlsConnectTls13, DCConnectRsaPssRsaPss) {
Reset(kPssDelegatorId);
// Need to enable PSS-PSS, which is not on by default.
static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(
TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now());
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_TRUE(cfilter->captured());
CheckPeerDelegCred(client_, true, 1024);
EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme);
}
// Connected with ECDSA-P256 using a PSS delegation cert.
TEST_P(TlsConnectTls13, DCConnectEcdsaP256RsaPss) {
Reset(kPssDelegatorId);
// Need to enable PSS-PSS, which is not on by default.
static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256,
ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor,
now());
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_TRUE(cfilter->captured());
CheckPeerDelegCred(client_, true, 256);
EXPECT_EQ(ssl_sig_ecdsa_secp256r1_sha256, client_->info().signatureScheme);
}
// Simulate the client receiving a DC containing algorithms not advertised.
// Do this by tweaking the client's supported sigSchemes after the CH.
TEST_P(TlsConnectTls13, DCReceiveUnadvertisedScheme) {
Reset(kEcdsaDelegatorId);
static const SSLSignatureScheme kClientSchemes[] = {
ssl_sig_ecdsa_secp256r1_sha256, ssl_sig_ecdsa_secp384r1_sha384};
static const SSLSignatureScheme kServerSchemes[] = {
ssl_sig_ecdsa_secp384r1_sha384, ssl_sig_ecdsa_secp256r1_sha256};
static const SSLSignatureScheme kEcdsaP256Only[] = {
ssl_sig_ecdsa_secp256r1_sha256};
client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes));
server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes));
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(TlsAgent::kServerEcdsa384,
ssl_sig_ecdsa_secp384r1_sha384, kDCValidFor,
now());
StartConnect();
client_->Handshake(); // CH with P256/P384.
server_->Handshake(); // Respond with P384 DC.
// Tell the client it only advertised P256.
SECStatus rv = SSLInt_SetDCAdvertisedSigSchemes(
client_->ssl_fd(), kEcdsaP256Only, PR_ARRAY_SIZE(kEcdsaP256Only));
EXPECT_EQ(SECSuccess, rv);
ExpectAlert(client_, kTlsAlertIllegalParameter);
Handshake();
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Server schemes includes only RSAE schemes. Connection should succeed
// without delegation.
TEST_P(TlsConnectTls13, DCConnectServerRsaeOnly) {
Reset(kRsaeDelegatorId);
static const SSLSignatureScheme kClientSchemes[] = {
ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256};
static const SSLSignatureScheme kServerSchemes[] = {
ssl_sig_rsa_pss_rsae_sha256};
client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes));
server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes));
client_->EnableDelegatedCredentials();
Connect();
CheckPeerDelegCred(client_, false);
}
// Connect with an RSA-PSS DC SPKI, and an RSAE Delegator SPKI.
TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) {
Reset(kRsaeDelegatorId);
static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
client_->EnableDelegatedCredentials();
server_->AddDelegatedCredential(
TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now());
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
}
// Client schemes includes only RSAE schemes. Connection should succeed
// without delegation, and no DC extension should be present in the CH.
TEST_P(TlsConnectTls13, DCConnectClientRsaeOnly) {
Reset(kRsaeDelegatorId);
static const SSLSignatureScheme kClientSchemes[] = {
ssl_sig_rsa_pss_rsae_sha256};
static const SSLSignatureScheme kServerSchemes[] = {
ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes));
server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes));
client_->EnableDelegatedCredentials();
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_FALSE(cfilter->captured());
CheckPeerDelegCred(client_, false);
}
// Test fallback. DC extension will not advertise RSAE schemes.
// The server will attempt to set one, but decline to after seeing
// the client-advertised schemes does not include it. Expect non-
// delegated success.
TEST_P(TlsConnectTls13, DCConnectRsaeDcSpki) {
Reset(kRsaeDelegatorId);
static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
client_->EnableDelegatedCredentials();
EnsureTlsSetup();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
EXPECT_TRUE(
TlsAgent::LoadKeyPairFromCert(TlsAgent::kDelegatorRsae2048, &pub, &priv));
StackSECItem dc;
server_->DelegateCredential(server_->name(), pub, ssl_sig_rsa_pss_rsae_sha256,
kDCValidFor, now(), &dc);
SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
nullptr, &dc, priv.get()};
EXPECT_TRUE(server_->ConfigServerCert(server_->name(), true, &extra_data));
auto sfilter = MakeTlsFilter<TlsExtensionCapture>(
server_, ssl_delegated_credentials_xtn);
Connect();
EXPECT_FALSE(sfilter->captured());
CheckPeerDelegCred(client_, false);
}
// Generate a weak key. We can't do this in the fixture because certutil
// won't sign with such a tiny key. That's OK, because this is fast(ish).
static void GenerateWeakRsaKey(ScopedSECKEYPrivateKey& priv,
@ -207,44 +350,44 @@ static void GenerateWeakRsaKey(ScopedSECKEYPrivateKey& priv,
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
PK11RSAGenParams rsaparams;
// The absolute minimum size of RSA key that we can use with SHA-256 is
// 256bit (hash) + 256bit (salt) + 8 (start byte) + 8 (end byte) = 528.
// The absolute minimum size of RSA key that we can use with SHA-256 is
// 256bit (hash) + 256bit (salt) + 8 (start byte) + 8 (end byte) = 528.
#define RSA_WEAK_KEY 528
#if RSA_MIN_MODULUS_BITS < RSA_WEAK_KEY
rsaparams.keySizeInBits = 528;
#else
rsaparams.keySizeInBits = RSA_MIN_MODULUS_BITS + 1;
#endif
rsaparams.pe = 65537;
// Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient
// entropy to generate a random key. We can fake some.
for (int retry = 0; retry < 10; ++retry) {
SECKEYPublicKey* p_pub = nullptr;
priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&rsaparams, &p_pub, false, false, nullptr));
pub.reset(p_pub);
if (priv) {
return;
}
ASSERT_FALSE(pub);
if (PORT_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) {
break;
}
// https://xkcd.com/221/
static const uint8_t FRESH_ENTROPY[16] = {4};
ASSERT_EQ(
SECSuccess,
PK11_RandomUpdate(
const_cast<void*>(reinterpret_cast<const void*>(FRESH_ENTROPY)),
sizeof(FRESH_ENTROPY)));
break;
}
ADD_FAILURE() << "Unable to generate an RSA key: "
<< PORT_ErrorToName(PORT_GetError());
SECKEYPublicKey* p_pub = nullptr;
priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&rsaparams, &p_pub, false, false, nullptr));
pub.reset(p_pub);
PR_ASSERT(priv);
return;
}
// Fail to connect with a weak RSA key.
TEST_P(TlsConnectTls13, DCWeakKey) {
Reset(kEcdsaDelegatorId);
Reset(kPssDelegatorId);
EnsureTlsSetup();
static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_pss_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
#if RSA_MIN_MODULUS_BITS > RSA_WEAK_KEY
// save the MIN POLICY length.
PRInt32 minRsa;
ASSERT_EQ(SECSuccess, NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minRsa));
#if RSA_MIN_MODULUS_BITS >= 2048
ASSERT_EQ(SECSuccess,
NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, RSA_MIN_MODULUS_BITS + 1024));
#else
ASSERT_EQ(SECSuccess, NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 2048));
#endif
#endif
ScopedSECKEYPrivateKey dc_priv;
ScopedSECKEYPublicKey dc_pub;
@ -253,20 +396,23 @@ TEST_P(TlsConnectTls13, DCWeakKey) {
// Construct a DC.
StackSECItem dc;
TlsAgent::DelegateCredential(kEcdsaDelegatorId, dc_pub,
ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now(),
TlsAgent::DelegateCredential(kPssDelegatorId, dc_pub,
ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now(),
&dc);
// Configure the DC on the server.
SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
nullptr, &dc, dc_priv.get()};
EXPECT_TRUE(server_->ConfigServerCert(kEcdsaDelegatorId, true, &extra_data));
EXPECT_TRUE(server_->ConfigServerCert(kPssDelegatorId, true, &extra_data));
client_->EnableDelegatedCredentials();
auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_delegated_credentials_xtn);
ConnectExpectAlert(client_, kTlsAlertInsufficientSecurity);
#if RSA_MIN_MODULUS_BITS > RSA_WEAK_KEY
ASSERT_EQ(SECSuccess, NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, minRsa));
#endif
}
class ReplaceDCSigScheme : public TlsHandshakeFilter {
@ -313,8 +459,8 @@ TEST_P(TlsConnectTls13, DCAbortBadSignature) {
now(), &dc);
ASSERT_TRUE(dc.data != nullptr);
// Flip the first bit of the DC so that the signature is invalid.
dc.data[0] ^= 0x01;
// Flip the last bit of the DC so that the signature is invalid.
dc.data[dc.len - 1] ^= 0x01;
SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
nullptr, &dc, priv.get()};
@ -338,6 +484,17 @@ TEST_P(TlsConnectTls13, DCAbortExpired) {
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Aborted due to remaining TTL > max validity period.
TEST_P(TlsConnectTls13, DCAbortExcessiveTTL) {
Reset(kEcdsaDelegatorId);
server_->AddDelegatedCredential(kDCId, kDCScheme,
kDCValidFor + 1 /* seconds */, now());
client_->EnableDelegatedCredentials();
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
client_->CheckErrorCode(SSL_ERROR_DC_INAPPROPRIATE_VALIDITY_PERIOD);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}
// Aborted because of invalid key usage.
TEST_P(TlsConnectTls13, DCAbortBadKeyUsage) {
// The sever does not have the delegationUsage extension.
@ -528,19 +685,18 @@ TEST_F(DCDelegation, DCDelegations) {
EXPECT_EQ(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, PORT_GetError());
// Using different PSS hashes should be OK.
EXPECT_EQ(SECSuccess,
SSL_DelegateCredential(cert.get(), priv.get(), pub_rsa.get(),
ssl_sig_rsa_pss_rsae_sha256, kDCValidFor,
now, &dc));
EXPECT_EQ(SECSuccess, SSL_DelegateCredential(
cert.get(), priv.get(), pub_rsa.get(),
ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now, &dc));
// Make sure to reset |dc| after each success.
dc.Reset();
EXPECT_EQ(SECSuccess, SSL_DelegateCredential(
cert.get(), priv.get(), pub_rsa.get(),
ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now, &dc));
ssl_sig_rsa_pss_pss_sha384, kDCValidFor, now, &dc));
dc.Reset();
EXPECT_EQ(SECSuccess, SSL_DelegateCredential(
cert.get(), priv.get(), pub_rsa.get(),
ssl_sig_rsa_pss_pss_sha384, kDCValidFor, now, &dc));
ssl_sig_rsa_pss_pss_sha512, kDCValidFor, now, &dc));
dc.Reset();
ScopedSECKEYPublicKey pub_ecdsa;