summaryrefslogtreecommitdiff
path: root/gtests
diff options
context:
space:
mode:
authorDennis Jackson <djackson@mozilla.com>2023-02-15 11:09:04 +0000
committerDennis Jackson <djackson@mozilla.com>2023-02-15 11:09:04 +0000
commitc237e20cbdab95c371f55845164d30ef3285860d (patch)
treee63368740343ecba8bf268e01c04ec72687b1d71 /gtests
parentc43bb7f02350617704950c4deccba23f3e00005b (diff)
downloadnss-hg-c237e20cbdab95c371f55845164d30ef3285860d.tar.gz
Bug 1570615: Add presence/absence tests for TLS GREASE. r=mt
This patch adds tests to check that we correctly add GREASE codepoints in the permitted locations (when enabled and using TLS1.3 or higher) and that we do not add any codepoints if disabled or negotiating an earlier version of TLS. The tests check: For ClientHello: - 1 codepoint is added to ciphersuites, name groups, key share, sig algs, supported versions, psk exchange methods, ALPN. - A 0-byte and a 1-byte GREASE extension is added. For CertificateRequests: - 1 codepoint is added to the sig alg extension. - 1 0-byte GREASE extension is added. For NewSessionTicket: - 1 1-byte GREASE extension is added. Differential Revision: https://phabricator.services.mozilla.com/D169621
Diffstat (limited to 'gtests')
-rw-r--r--gtests/ssl_gtest/tls_filter.cc22
-rw-r--r--gtests/ssl_gtest/tls_filter.h20
-rw-r--r--gtests/ssl_gtest/tls_grease_unittest.cc337
3 files changed, 372 insertions, 7 deletions
diff --git a/gtests/ssl_gtest/tls_filter.cc b/gtests/ssl_gtest/tls_filter.cc
index 2851619b0..ab52a07e8 100644
--- a/gtests/ssl_gtest/tls_filter.cc
+++ b/gtests/ssl_gtest/tls_filter.cc
@@ -1268,4 +1268,26 @@ PacketFilter::Action ClientHelloPreambleCapture::FilterHandshake(
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
diff --git a/gtests/ssl_gtest/tls_filter.h b/gtests/ssl_gtest/tls_filter.h
index fc88d0b18..7c45aab12 100644
--- a/gtests/ssl_gtest/tls_filter.h
+++ b/gtests/ssl_gtest/tls_filter.h
@@ -874,6 +874,26 @@ class ClientHelloPreambleCapture : public TlsHandshakeFilter {
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)
diff --git a/gtests/ssl_gtest/tls_grease_unittest.cc b/gtests/ssl_gtest/tls_grease_unittest.cc
index 8580ee1fc..1bd37d8a0 100644
--- a/gtests/ssl_gtest/tls_grease_unittest.cc
+++ b/gtests/ssl_gtest/tls_grease_unittest.cc
@@ -13,6 +13,336 @@
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();
@@ -483,13 +813,6 @@ TEST_F(TlsConnectStreamTls13, GreaseClientHelloExtensionPermutation) {
Connect();
}
-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};
-
INSTANTIATE_TEST_SUITE_P(GreaseTestTls12, GreaseTestStreamTls12,
::testing::ValuesIn(kTlsGreaseValues));