summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
parentInitial commit. (diff)
downloadfirefox-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/ssl_version_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_version_unittest.cc470
1 files changed, 470 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
new file mode 100644
index 0000000000..8ab8c14649
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -0,0 +1,470 @@
+/* -*- 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 "ssl3prot.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "nss_scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+TEST_P(TlsConnectStream, ServerNegotiateTls10) {
+ uint16_t minver, maxver;
+ client_->GetVersionRange(&minver, &maxver);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, maxver);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0);
+ Connect();
+}
+
+TEST_P(TlsConnectGeneric, ServerNegotiateTls11) {
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) GTEST_SKIP();
+
+ uint16_t minver, maxver;
+ client_->GetVersionRange(&minver, &maxver);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, maxver);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_1);
+ Connect();
+}
+
+TEST_P(TlsConnectGeneric, ServerNegotiateTls12) {
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) GTEST_SKIP();
+
+ uint16_t minver, maxver;
+ client_->GetVersionRange(&minver, &maxver);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, maxver);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ Connect();
+}
+
+TEST_P(TlsConnectGeneric,
+ ClientOfferTls11_Tls13ServerNegotiateEachVersionOneByOne) {
+ // DTLS does not support 1.0
+ if (variant_ == ssl_variant_datagram) {
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ } else {
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ }
+ server_->SetVersionRange(version_, version_);
+ Connect();
+}
+
+// Test the ServerRandom version hack from
+// [draft-ietf-tls-tls13-11 Section 6.3.1.1].
+// The first three tests test for active tampering. The next
+// two validate that we can also detect fallback using the
+// SSL_SetDowngradeCheckVersion() API.
+TEST_F(TlsConnectTest, TestDowngradeDetectionToTls11) {
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
+ 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);
+}
+
+// Attempt to negotiate the bogus DTLS 1.1 version.
+TEST_F(DtlsConnectTest, TestDtlsVersion11) {
+ MakeTlsFilter<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
+ ((~0x0101) & 0xffff));
+ ConnectExpectAlert(server_, kTlsAlertProtocolVersion);
+ client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+}
+
+TEST_F(TlsConnectTest, TestDowngradeDetectionToTls12) {
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
+ MakeTlsFilter<TlsExtensionDropper>(client_, ssl_tls13_supported_versions_xtn);
+ 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_3);
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+// Disabling downgrade checks will be caught when the Finished MAC check fails.
+TEST_F(TlsConnectTest, TestDisableDowngradeDetection) {
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_FALSE);
+ MakeTlsFilter<TlsExtensionDropper>(client_, ssl_tls13_supported_versions_xtn);
+ 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_3);
+ ConnectExpectAlert(server_, kTlsAlertDecryptError);
+ client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+}
+
+typedef std::tuple<SSLProtocolVariant,
+ uint16_t, // client version
+ uint16_t> // server version
+ TlsDowngradeProfile;
+
+class TlsDowngradeTest
+ : public TlsConnectTestBase,
+ public ::testing::WithParamInterface<TlsDowngradeProfile> {
+ public:
+ TlsDowngradeTest()
+ : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())),
+ c_ver(std::get<1>(GetParam())),
+ s_ver(std::get<2>(GetParam())) {}
+
+ protected:
+ const uint16_t c_ver;
+ const uint16_t s_ver;
+};
+
+TEST_P(TlsDowngradeTest, TlsDowngradeSentinelTest) {
+ static const uint8_t tls12_downgrade_random[] = {0x44, 0x4F, 0x57, 0x4E,
+ 0x47, 0x52, 0x44, 0x01};
+ static const uint8_t tls1_downgrade_random[] = {0x44, 0x4F, 0x57, 0x4E,
+ 0x47, 0x52, 0x44, 0x00};
+ static const size_t kRandomLen = 32;
+
+ if (c_ver > s_ver) {
+ GTEST_SKIP();
+ }
+
+ client_->SetVersionRange(c_ver, c_ver);
+ server_->SetVersionRange(c_ver, s_ver);
+
+ auto sh = MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_server_hello);
+ Connect();
+ ASSERT_TRUE(sh->buffer().len() > (kRandomLen + 2));
+
+ const uint8_t* downgrade_sentinel =
+ sh->buffer().data() + 2 + kRandomLen - sizeof(tls1_downgrade_random);
+ if (c_ver < s_ver) {
+ if (c_ver == SSL_LIBRARY_VERSION_TLS_1_2) {
+ EXPECT_EQ(0, memcmp(downgrade_sentinel, tls12_downgrade_random,
+ sizeof(tls12_downgrade_random)));
+ } else {
+ EXPECT_EQ(0, memcmp(downgrade_sentinel, tls1_downgrade_random,
+ sizeof(tls1_downgrade_random)));
+ }
+ } else {
+ EXPECT_NE(0, memcmp(downgrade_sentinel, tls12_downgrade_random,
+ sizeof(tls12_downgrade_random)));
+ EXPECT_NE(0, memcmp(downgrade_sentinel, tls1_downgrade_random,
+ sizeof(tls1_downgrade_random)));
+ }
+}
+
+// TLS 1.1 clients do not check the random values, so we should
+// instead get a handshake failure alert from the server.
+TEST_F(TlsConnectTest, TestDowngradeDetectionToTls10) {
+ // Setting the option here has no effect.
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
+ 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,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ ConnectExpectAlert(server_, kTlsAlertDecryptError);
+ server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+ client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+}
+
+TEST_F(TlsConnectTest, TestFallbackFromTls12) {
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_1);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ client_->SetDowngradeCheckVersion(SSL_LIBRARY_VERSION_TLS_1_2);
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+static SECStatus AllowFalseStart(PRFileDesc* fd, void* arg,
+ PRBool* can_false_start) {
+ bool* false_start_attempted = reinterpret_cast<bool*>(arg);
+ *false_start_attempted = true;
+ *can_false_start = PR_TRUE;
+ return SECSuccess;
+}
+
+// If we disable the downgrade check, the sentinel is still generated, and we
+// disable false start instead.
+TEST_F(TlsConnectTest, DisableFalseStartOnFallback) {
+ // Don't call client_->EnableFalseStart(), because that sets the client up for
+ // success, and we want false start to fail.
+ client_->SetOption(SSL_ENABLE_FALSE_START, PR_TRUE);
+ bool false_start_attempted = false;
+ EXPECT_EQ(SECSuccess,
+ 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);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+ EXPECT_FALSE(false_start_attempted);
+}
+
+TEST_F(TlsConnectTest, TestFallbackFromTls13) {
+ client_->SetOption(SSL_ENABLE_HELLO_DOWNGRADE_CHECK, PR_TRUE);
+ client_->SetDowngradeCheckVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectGeneric, TestFallbackSCSVVersionMatch) {
+ client_->SetOption(SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
+ Connect();
+}
+
+TEST_P(TlsConnectGenericPre13, TestFallbackSCSVVersionMismatch) {
+ client_->SetOption(SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
+ server_->SetVersionRange(version_, version_ + 1);
+ ConnectExpectAlert(server_, kTlsAlertInappropriateFallback);
+ client_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
+}
+
+// The TLS v1.3 spec section C.4 states that 'Implementations MUST NOT send or
+// accept any records with a version less than { 3, 0 }'. Thus we will not
+// allow version ranges including both SSL v3 and TLS v1.3.
+TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
+ SECStatus rv;
+ SSLVersionRange vrange = {SSL_LIBRARY_VERSION_3_0,
+ SSL_LIBRARY_VERSION_TLS_1_3};
+
+ EnsureTlsSetup();
+ rv = SSL_VersionRangeSet(client_->ssl_fd(), &vrange);
+ EXPECT_EQ(SECFailure, rv);
+
+ rv = SSL_VersionRangeSet(server_->ssl_fd(), &vrange);
+ EXPECT_EQ(SECFailure, rv);
+}
+
+TEST_P(TlsConnectGeneric, AlertBeforeServerHello) {
+ EnsureTlsSetup();
+ client_->ExpectReceiveAlert(kTlsAlertUnrecognizedName, kTlsAlertWarning);
+ StartConnect();
+ client_->Handshake(); // Send ClientHello.
+ static const uint8_t kWarningAlert[] = {kTlsAlertWarning,
+ kTlsAlertUnrecognizedName};
+ DataBuffer alert;
+ TlsAgentTestBase::MakeRecord(variant_, ssl_ct_alert,
+ SSL_LIBRARY_VERSION_TLS_1_0, kWarningAlert,
+ PR_ARRAY_SIZE(kWarningAlert), &alert);
+ client_->adapter()->PacketReceived(alert);
+ Handshake();
+ CheckConnected();
+}
+
+class Tls13NoSupportedVersions : public TlsConnectStreamTls12 {
+ protected:
+ void Run(uint16_t overwritten_client_version, uint16_t max_server_version) {
+ 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<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
+ overwritten_client_version);
+ auto capture =
+ MakeTlsFilter<TlsHandshakeRecorder>(server_, kTlsHandshakeServerHello);
+ ConnectExpectAlert(server_, kTlsAlertDecryptError);
+ client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+ const DataBuffer& server_hello = capture->buffer();
+ ASSERT_GT(server_hello.len(), 2U);
+ uint32_t ver;
+ ASSERT_TRUE(server_hello.Read(0, 2, &ver));
+ ASSERT_EQ(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), ver);
+ }
+};
+
+// If we offer a 1.3 ClientHello w/o supported_versions, the server should
+// negotiate 1.2.
+TEST_F(Tls13NoSupportedVersions,
+ Tls13ClientHelloWithoutSupportedVersionsServer12) {
+ Run(SSL_LIBRARY_VERSION_TLS_1_3, SSL_LIBRARY_VERSION_TLS_1_2);
+}
+
+TEST_F(Tls13NoSupportedVersions,
+ Tls13ClientHelloWithoutSupportedVersionsServer13) {
+ Run(SSL_LIBRARY_VERSION_TLS_1_3, SSL_LIBRARY_VERSION_TLS_1_3);
+}
+
+TEST_F(Tls13NoSupportedVersions,
+ Tls14ClientHelloWithoutSupportedVersionsServer13) {
+ Run(SSL_LIBRARY_VERSION_TLS_1_3 + 1, SSL_LIBRARY_VERSION_TLS_1_3);
+}
+
+// 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<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
+ SSL_LIBRARY_VERSION_TLS_1_3 + 1);
+ auto capture = MakeTlsFilter<TlsExtensionCapture>(
+ server_, ssl_tls13_supported_versions_xtn);
+ client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+ ConnectExpectFail();
+ client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+
+ ASSERT_EQ(2U, capture->extension().len());
+ uint32_t version = 0;
+ ASSERT_TRUE(capture->extension().Read(0, 2, &version));
+ // This way we don't need to change with new draft version.
+ ASSERT_LT(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), version);
+}
+
+// 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<TlsMessageVersionSetter>(client_, kTlsHandshakeClientHello,
+ SSL_LIBRARY_VERSION_3_0);
+ ConnectExpectAlert(server_, kTlsAlertProtocolVersion);
+}
+
+TEST_F(TlsConnectStreamTls13, Ssl30ServerHelloWithSupportedVersions) {
+ 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
+ * alert. The server is expected to close the connection with a unexpected
+ * message alert. */
+ server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+ Handshake();
+}
+
+// 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(SSL_LIBRARY_VERSION_DTLS_1_3_WIRE, 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[] = {
+ SSL_LIBRARY_VERSION_DTLS_1_3_WIRE, 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,
+ TlsConnectTestBase::kTlsV12Plus));
+
+} // namespace nss_test