diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /security/nss/gtests/ssl_gtest/tls_grease_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/ssl_gtest/tls_grease_unittest.cc')
-rw-r--r-- | security/nss/gtests/ssl_gtest/tls_grease_unittest.cc | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/tls_grease_unittest.cc b/security/nss/gtests/ssl_gtest/tls_grease_unittest.cc new file mode 100644 index 0000000000..5c03c86309 --- /dev/null +++ b/security/nss/gtests/ssl_gtest/tls_grease_unittest.cc @@ -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(); + ASSERT_TRUE(SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_CH_EXTENSION_PERMUTATION, + PR_TRUE) == SECSuccess); + ASSERT_TRUE(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 |