Commit 4516d102 authored by Kevin Jacobs's avatar Kevin Jacobs

Bug 1654332 - Update ESNI to draft-08 (ECH). r=mt

This patch adds support for Encrypted Client Hello (draft-ietf-tls-esni-08), replacing the existing ESNI (draft -02) support.

There are five new experimental functions to enable this:

  - SSL_EncodeEchConfig: Generates an encoded (not BASE64) ECHConfig given a set of parameters.
  - SSL_SetClientEchConfigs: Configures the provided ECHConfig to the given socket. When configured, an ephemeral HPKE keypair will be generated for the CH encryption.
  - SSL_SetServerEchConfigs: Configures the provided ECHConfig and keypair to the socket. The keypair specified will be used for HPKE operations in order to decrypt encrypted Client Hellos as they are received.
  - SSL_GetEchRetryConfigs: If ECH is rejected by the server and compatible retry_configs are provided, this API allows the application to extract those retry_configs for use in a new connection.
  - SSL_EnableTls13GreaseEch: When enabled, non-ECH Client Hellos will have a "GREASE ECH" (i.e. fake) extension appended. GREASE ECH is disabled by default, as there are known compatibility issues that will be addressed in a subsequent draft.

The following ESNI experimental functions are deprecated by this update:

  - SSL_EncodeESNIKeys
  - SSL_EnableESNI
  - SSL_SetESNIKeyPair


In order to be used, NSS must be compiled with `NSS_ENABLE_DRAFT_HPKE` defined.

Differential Revision: https://phabricator.services.mozilla.com/D86106

--HG--
rename : gtests/ssl_gtest/tls_esni_unittest.cc => gtests/ssl_gtest/tls_ech_unittest.cc
rename : lib/ssl/tls13esni.c => lib/ssl/tls13ech.c
rename : lib/ssl/tls13esni.h => lib/ssl/tls13ech.h
extra : moz-landing-system : lando
parent 85f350f0
2 functions with some indirect sub-type change:
[C] 'function SECStatus SSL_GetChannelInfo(PRFileDesc*, SSLChannelInfo*, PRUintn)' at sslinfo.c:14:1 has some indirect sub-type changes:
parameter 2 of type 'SSLChannelInfo*' has sub-type changes:
in pointed to type 'typedef SSLChannelInfo' at sslt.h:378:1:
underlying type 'struct SSLChannelInfoStr' at sslt.h:299:1 changed:
type size hasn't changed
1 data member insertion:
'PRBool SSLChannelInfoStr::echAccepted', at offset 992 (in bits) at sslt.h:374:1
[C] 'function SECStatus SSL_GetPreliminaryChannelInfo(PRFileDesc*, SSLPreliminaryChannelInfo*, PRUintn)' at sslinfo.c:122:1 has some indirect sub-type changes:
parameter 2 of type 'SSLPreliminaryChannelInfo*' has sub-type changes:
in pointed to type 'typedef SSLPreliminaryChannelInfo' at sslt.h:446:1:
underlying type 'struct SSLPreliminaryChannelInfoStr' at sslt.h:386:1 changed:
type size changed from 288 to 384 (in bits)
2 data member insertions:
'PRBool SSLPreliminaryChannelInfoStr::echAccepted', at offset 288 (in bits) at sslt.h:439:1
'const char* SSLPreliminaryChannelInfoStr::echPublicName', at offset 320 (in bits) at sslt.h:442:1
......@@ -231,7 +231,7 @@ PrintUsageHeader()
" [-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n"
" [-I groups] [-J signatureschemes]\n"
" [-A requestfile] [-L totalconnections] [-P {client,server}]\n"
" [-N encryptedSniKeys] [-Q] [-z externalPsk]\n"
" [-N echConfigs] [-Q] [-z externalPsk]\n"
"\n",
progName);
}
......@@ -316,7 +316,7 @@ PrintParameterUsage()
fprintf(stderr, "%-20s Enable alternative TLS 1.3 handshake\n", "-X alt-server-hello");
fprintf(stderr, "%-20s Use DTLS\n", "-P {client, server}");
fprintf(stderr, "%-20s Exit after handshake\n", "-Q");
fprintf(stderr, "%-20s Encrypted SNI Keys\n", "-N");
fprintf(stderr, "%-20s Use Encrypted Client Hello with the given Base64-encoded ECHConfigs\n", "-N");
fprintf(stderr, "%-20s Enable post-handshake authentication\n"
"%-20s for TLS 1.3; need to specify -n\n",
"-E", "");
......@@ -1010,7 +1010,7 @@ PRBool stopAfterHandshake = PR_FALSE;
PRBool requestToExit = PR_FALSE;
char *versionString = NULL;
PRBool handshakeComplete = PR_FALSE;
char *encryptedSNIKeys = NULL;
char *echConfigs = NULL;
PRBool enablePostHandshakeAuth = PR_FALSE;
PRBool enableDelegatedCredentials = PR_FALSE;
const secuExporter *enabledExporters = NULL;
......@@ -1263,6 +1263,30 @@ importPsk(PRFileDesc *s)
return rv;
}
static SECStatus
printEchRetryConfigs(PRFileDesc *s)
{
if (PORT_GetError() == SSL_ERROR_ECH_RETRY_WITH_ECH) {
SECItem retries = { siBuffer, NULL, 0 };
SECStatus rv = SSL_GetEchRetryConfigs(s, &retries);
if (rv != SECSuccess) {
SECU_PrintError(progName, "SSL_GetEchRetryConfigs failed");
return SECFailure;
}
char *retriesBase64 = NSSBase64_EncodeItem(NULL, NULL, 0, &retries);
if (!retriesBase64) {
SECU_PrintError(progName, "NSSBase64_EncodeItem on retry_configs failed");
SECITEM_FreeItem(&retries, PR_FALSE);
return SECFailure;
}
fprintf(stderr, "Received ECH retry_configs: \n%s\n", retriesBase64);
PORT_Free(retriesBase64);
SECITEM_FreeItem(&retries, PR_FALSE);
}
return SECSuccess;
}
static int
run()
{
......@@ -1511,21 +1535,20 @@ run()
}
}
if (encryptedSNIKeys) {
SECItem esniKeysBin = { siBuffer, NULL, 0 };
if (echConfigs) {
SECItem echConfigsBin = { siBuffer, NULL, 0 };
if (!NSSBase64_DecodeBuffer(NULL, &esniKeysBin, encryptedSNIKeys,
strlen(encryptedSNIKeys))) {
SECU_PrintError(progName, "ESNIKeys record is invalid base64");
if (!NSSBase64_DecodeBuffer(NULL, &echConfigsBin, echConfigs,
strlen(echConfigs))) {
SECU_PrintError(progName, "ECHConfigs record is invalid base64");
error = 1;
goto done;
}
rv = SSL_EnableESNI(s, esniKeysBin.data, esniKeysBin.len,
"dummy.invalid");
SECITEM_FreeItem(&esniKeysBin, PR_FALSE);
rv = SSL_SetClientEchConfigs(s, echConfigsBin.data, echConfigsBin.len);
SECITEM_FreeItem(&echConfigsBin, PR_FALSE);
if (rv < 0) {
SECU_PrintError(progName, "SSL_EnableESNI failed");
SECU_PrintError(progName, "SSL_SetClientEchConfigs failed");
error = 1;
goto done;
}
......@@ -1702,6 +1725,9 @@ run()
} else {
error = writeBytesToServer(s, buf, nb);
if (error) {
if (echConfigs) {
(void)printEchRetryConfigs(s);
}
goto done;
}
pollset[SSOCK_FD].in_flags = PR_POLL_READ;
......@@ -1881,7 +1907,7 @@ main(int argc, char **argv)
break;
case 'N':
encryptedSNIKeys = PORT_Strdup(optstate->value);
echConfigs = PORT_Strdup(optstate->value);
break;
case 'P':
......@@ -2257,7 +2283,7 @@ done:
PORT_Free(pwdata.data);
PORT_Free(host);
PORT_Free(zeroRttData);
PORT_Free(encryptedSNIKeys);
PORT_Free(echConfigs);
SECITEM_ZfreeItem(&psk, PR_FALSE);
SECITEM_ZfreeItem(&pskLabel, PR_FALSE);
......
......@@ -56,6 +56,7 @@ const uint8_t kTlsAlertUnsupportedExtension = 110;
const uint8_t kTlsAlertUnrecognizedName = 112;
const uint8_t kTlsAlertCertificateRequired = 116;
const uint8_t kTlsAlertNoApplicationProtocol = 120;
const uint8_t kTlsAlertEchRequired = 121;
const uint8_t kTlsFakeChangeCipherSpec[] = {
ssl_ct_change_cipher_spec, // Type
......
......@@ -8,8 +8,10 @@
#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"
......@@ -481,3 +483,17 @@ 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);
memcpy(cfg->raw.data, buf, len);
return SECSuccess;
}
......@@ -49,5 +49,7 @@ 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);
#endif // ndef libssl_internals_h_
......@@ -58,7 +58,7 @@ CPPSRCS = \
tls_protect.cc \
tls_psk_unittest.cc \
tls_subcerts_unittest.cc \
tls_esni_unittest.cc \
tls_ech_unittest.cc \
$(SSLKEYLOGFILE_FILES) \
$(NULL)
......
......@@ -660,6 +660,18 @@ TEST_P(TlsConnectGeneric, ClientAuthEcdsa) {
CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa);
}
#ifdef NSS_ENABLE_DRAFT_HPKE
TEST_P(TlsConnectGeneric, ClientAuthWithEch) {
Reset(TlsAgent::kServerEcdsa256);
EnsureTlsSetup();
SetupEch(client_, server_);
client_->SetupClientAuth();
server_->RequestClientAuth(true);
Connect();
CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa);
}
#endif
TEST_P(TlsConnectGeneric, ClientAuthBigRsa) {
Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048);
client_->SetupClientAuth();
......
......@@ -67,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),
......
......@@ -83,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)
......@@ -1155,6 +1098,22 @@ TEST_P(TlsExtensionTest13, HrrThenRemoveSupportedGroups) {
SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION);
}
#ifdef NSS_ENABLE_DRAFT_HPKE
TEST_P(TlsExtensionTest13, HrrThenRemoveEch) {
if (variant_ == ssl_variant_datagram) {
// ECH not supported in DTLS.
return;
}
EnsureTlsSetup();
SetupEch(client_, server_);
ExpectAlert(server_, kTlsAlertIllegalParameter);
HrrThenRemoveExtensionsTest(ssl_tls13_encrypted_client_hello_xtn,
SSL_ERROR_ILLEGAL_PARAMETER_ALERT,
SSL_ERROR_BAD_2ND_CLIENT_HELLO);
}
#endif
TEST_P(TlsExtensionTest13, EmptyVersionList) {
static const uint8_t ext[] = {0x00, 0x00};
ConnectWithBogusVersionList(ext, sizeof(ext));
......@@ -1289,6 +1248,7 @@ TEST_P(TlsBogusExtensionTest13, AddBogusExtensionNewSessionTicket) {
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.
......
......@@ -55,7 +55,7 @@
'tls_connect.cc',
'tls_filter.cc',
'tls_hkdf_unittest.cc',
'tls_esni_unittest.cc',
'tls_ech_unittest.cc',
'tls_protect.cc',
'tls_psk_unittest.cc',
'tls_subcerts_unittest.cc'
......
......@@ -214,6 +214,31 @@ TEST_F(Tls13CompatTest, EnabledHrrZeroRtt) {
CheckForCompatHandshake();
}
#ifdef NSS_ENABLE_DRAFT_HPKE
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);
}
#endif
class TlsSessionIDEchoFilter : public TlsHandshakeFilter {
public:
TlsSessionIDEchoFilter(const std::shared_ptr<TlsAgent>& a)
......
......@@ -74,6 +74,7 @@ TlsAgent::TlsAgent(const std::string& nm, Role rl, SSLProtocolVariant var)
expected_version_(0),
expected_cipher_suite_(0),
expect_client_auth_(false),
expect_ech_(false),
expect_psk_(ssl_psk_none),
can_falsestart_hook_called_(false),
sni_hook_called_(false),
......@@ -687,7 +688,9 @@ void TlsAgent::EnableFalseStart() {
SetOption(SSL_ENABLE_FALSE_START, PR_TRUE);
}
void TlsAgent::ExpectPsk() { expect_psk_ = ssl_psk_external; }
void TlsAgent::ExpectEch(bool expected) { expect_ech_ = expected; }
void TlsAgent::ExpectPsk(SSLPskType psk) { expect_psk_ = psk; }
void TlsAgent::ExpectResumption() { expect_psk_ = ssl_psk_resume; }
......@@ -820,7 +823,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
......@@ -932,6 +934,7 @@ void TlsAgent::Connected() {
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
......
......@@ -158,7 +158,9 @@ class TlsAgent : public PollTarget {
void SetServerKeyBits(uint16_t bits);
void ExpectReadWriteError();
void EnableFalseStart();
void ExpectPsk();
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);
......@@ -186,6 +188,7 @@ class TlsAgent : public PollTarget {
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);
......@@ -426,6 +429,7 @@ class TlsAgent : public PollTarget {
uint16_t expected_version_;
uint16_t expected_cipher_suite_;
bool expect_client_auth_;
bool expect_ech_;
SSLPskType expect_psk_;
bool can_falsestart_hook_called_;
bool sni_hook_called_;
......
......@@ -248,6 +248,91 @@ void TlsConnectTestBase::ResetAntiReplay(PRTime window) {
anti_replay_.reset(p_anti_replay);
}
void TlsConnectTestBase::MakeEcKeyParams(SECItem* params, SSLNamedGroup group) {
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[0] = SEC_ASN1_OBJECT_ID;
params->data[1] = oidData->oid.len;
memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
}
void TlsConnectTestBase::GenerateEchConfig(
HpkeKemId kem_id, const std::vector<uint32_t>& cipher_suites,
const std::string& public_name, uint16_t max_name_len, DataBuffer& record,
ScopedSECKEYPublicKey& pubKey, ScopedSECKEYPrivateKey& privKey) {
bool gen_keys = !pubKey && !privKey;
SECKEYECParams ecParams = {siBuffer, NULL, 0};
MakeEcKeyParams(&ecParams, ssl_grp_ec_curve25519);
SECKEYPublicKey* pub = nullptr;
SECKEYPrivateKey* priv = nullptr;
if (gen_keys) {
priv = SECKEY_CreateECPrivateKey(&ecParams, &pub, nullptr);
} else {
priv = privKey.get();
pub = pubKey.get();
}
ASSERT_NE(nullptr, priv);
SECITEM_FreeItem(&ecParams, PR_FALSE);
PRUint8 encoded[1024];
unsigned int encoded_len = 0;
SECStatus rv = SSL_EncodeEchConfig(
public_name.c_str(), cipher_suites.data(), cipher_suites.size(), kem_id,
pub, max_name_len, 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) {
EXPECT_TRUE(set_server_config || set_client_config);
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
DataBuffer record;
static const std::vector<uint32_t> kDefaultSuites = {
(static_cast<uint16_t>(HpkeKdfHkdfSha256) << 16) |
HpkeAeadChaCha20Poly1305,
(static_cast<uint16_t>(HpkeKdfHkdfSha256) << 16) | HpkeAeadAes128Gcm};
GenerateEchConfig(kem_id, kDefaultSuites, "public.name", 100, 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();
......
......@@ -146,6 +146,19 @@ class TlsConnectTestBase : public ::testing::Test {
void SaveAlgorithmPolicy();
void RestoreAlgorithmPolicy();
static void MakeEcKeyParams(SECItem* params, SSLNamedGroup group);
static void GenerateEchConfig(HpkeKemId kem_id,
const std::vector<uint32_t>& 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);
protected:
SSLProtocolVariant variant_;
std::shared_ptr<TlsAgent> client_;
......
This diff is collapsed.
This diff is collapsed.
......@@ -17,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 {
......@@ -1031,6 +1032,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_) {
......
......@@ -11,6 +11,7 @@
#include <memory>
#include <set>
#include <vector>
#include "pk11pub.h"
#include "sslt.h"
#include "sslproto.h"
#include "test_io.h"
......@@ -525,6 +526,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)
......
......@@ -585,3 +585,18 @@ ER3(SSL_ERROR_DC_EXPIRED, (SSL_ERROR_BASE + 185),
ER3(SSL_ERROR_DC_INAPPROPRIATE_VALIDITY_PERIOD, (SSL_ERROR_BASE + 186),
"SSL received a delegated credential with excessive TTL.")
ER3(SSL_ERROR_FEATURE_DISABLED, (SSL_ERROR_BASE + 187),
"The requested feature is disabled.")
ER3(SSL_ERROR_ECH_RETRY_WITH_ECH, (SSL_ERROR_BASE + 188),
"TLS ECH was rejected, but verification succeeded and compatible retry_configs are available.")
ER3(SSL_ERROR_ECH_RETRY_WITHOUT_ECH, (SSL_ERROR_BASE + 189),
"TLS ECH was rejected, but verification succeeded and no compatible retry_configs were found.")
ER3(SSL_ERROR_ECH_FAILED, (SSL_ERROR_BASE + 190),
"TLS ECH was rejected and verification failed.")
ER3(SSL_ERROR_ECH_REQUIRED_ALERT, (SSL_ERROR_BASE + 191),
"SSL peer reported ECH required.")
......@@ -60,7 +60,7 @@ CSRCS = \
sslcert.c \
sslgrp.c \
sslprimitive.c \
tls13esni.c \
tls13ech.c \
tls13subcerts.c \
$(NULL)
......
......@@ -44,7 +44,7 @@
'ssltrace.c',
'sslver.c',
'tls13con.c',
'tls13esni.c',
'tls13ech.c',
'tls13exthandle.c',
'tls13hashstate.c',
'tls13hkdf.c',
......
This diff is collapsed.
......@@ -53,7 +53,6 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
{ ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
{ ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
{ ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn },
{ ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn },
{ ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
{ 0, NULL }
......@@ -74,6 +73,7 @@ static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
{ ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
{ ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn }, </