/* -*- 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 #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 kDefaultSuites = {TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256}; std::vector kChaChaSuite = {TLS_CHACHA20_POLY1305_SHA256}; std::vector kBogusSuites = {0}; std::vector 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(time_t windowStart, SSLNamedGroup group, std::vector& 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, windowStart, windowStart + 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(const std::shared_ptr& client, const std::shared_ptr& server, SSLNamedGroup group = ssl_grp_ec_curve25519) { ScopedSECKEYPublicKey pub; ScopedSECKEYPrivateKey priv; DataBuffer record; GenerateEsniKey(time(nullptr), 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(kDummySni), strlen(kDummySni)); ASSERT_EQ(expected, name); } static void ClientInstallEsni(std::shared_ptr& agent, 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()); } } TEST_P(TlsAgentTestClient13, EsniInstall) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); ClientInstallEsni(agent_, record); } // The next set of tests fail at setup time. TEST_P(TlsAgentTestClient13, EsniInvalidHash) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.data()[2]++; ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS); } TEST_P(TlsAgentTestClient13, EsniInvalidVersion) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.Write(0, 0xffff, 2); ClientInstallEsni(agent_, record, SSL_ERROR_UNSUPPORTED_VERSION); } TEST_P(TlsAgentTestClient13, EsniShort) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.Truncate(record.len() - 1); UpdateEsniKeysChecksum(&record); ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS); } TEST_P(TlsAgentTestClient13, EsniLong) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.Write(record.len(), 1, 1); UpdateEsniKeysChecksum(&record); ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS); } TEST_P(TlsAgentTestClient13, EsniExtensionMismatch) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.Write(record.len() - 1, 1, 1); UpdateEsniKeysChecksum(&record); ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS); } // The following tests fail by ignoring the Esni block. TEST_P(TlsAgentTestClient13, EsniUnknownGroup) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); record.Write(8, 0xffff, 2); // Fake group UpdateEsniKeysChecksum(&record); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(agent_, ssl_tls13_encrypted_sni_xtn); agent_->Handshake(); ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state()); ASSERT_TRUE(!filter->captured()); } TEST_P(TlsAgentTestClient13, EsniUnknownCS) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kBogusSuites, &record); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(agent_, ssl_tls13_encrypted_sni_xtn); agent_->Handshake(); ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state()); ASSERT_TRUE(!filter->captured()); } TEST_P(TlsAgentTestClient13, EsniInvalidCS) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kTls12Suites, &record); UpdateEsniKeysChecksum(&record); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(agent_, ssl_tls13_encrypted_sni_xtn); agent_->Handshake(); ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state()); ASSERT_TRUE(!filter->captured()); } TEST_P(TlsAgentTestClient13, EsniNotReady) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0) + 1000, ssl_grp_ec_curve25519, kDefaultSuites, &record); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(agent_, ssl_tls13_encrypted_sni_xtn); agent_->Handshake(); ASSERT_TRUE(!filter->captured()); } TEST_P(TlsAgentTestClient13, EsniExpired) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0) - 1000, ssl_grp_ec_curve25519, kDefaultSuites, &record); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(agent_, ssl_tls13_encrypted_sni_xtn); agent_->Handshake(); ASSERT_TRUE(!filter->captured()); } TEST_P(TlsAgentTestClient13, NoSniSoNoEsni) { EnsureInit(); DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); SSL_SetURL(agent_->ssl_fd(), ""); ClientInstallEsni(agent_, record, 0); auto filter = MakeTlsFilter(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(const_cast("server")), 6}; EXPECT_TRUE(!SECITEM_CompareItem(&expected, &srvNameAddr[0])); return SECSuccess; } TEST_P(TlsConnectTls13, ConnectEsni) { EnsureTlsSetup(); SetupEsni(client_, server_); auto cFilterSni = MakeTlsFilter(client_, ssl_server_name_xtn); auto cFilterEsni = MakeTlsFilter(client_, ssl_tls13_encrypted_sni_xtn); client_->SetFilter(std::make_shared( ChainedPacketFilterInit({cFilterSni, cFilterEsni}))); auto sfilter = MakeTlsFilter(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(suite)); ASSERT_TRUE(!sfilter->captured()); } TEST_P(TlsConnectTls13, ConnectEsniHrr) { EnsureTlsSetup(); const std::vector groups = {ssl_grp_ec_secp384r1}; server_->ConfigNamedGroups(groups); SetupEsni(client_, server_); auto hrr_capture = MakeTlsFilter( server_, kTlsHandshakeHelloRetryRequest); auto filter = MakeTlsFilter(client_, ssl_server_name_xtn); auto filter2 = MakeTlsFilter(client_, ssl_server_name_xtn, true); client_->SetFilter(std::make_shared( ChainedPacketFilterInit({filter, filter2}))); server_->SetSniCallback(SniCallback); Connect(); CheckSniExtension(filter->extension()); CheckSniExtension(filter2->extension()); EXPECT_NE(0UL, hrr_capture->buffer().len()); } TEST_P(TlsConnectTls13, ConnectEsniNoDummy) { EnsureTlsSetup(); ScopedSECKEYPublicKey pub; ScopedSECKEYPrivateKey priv; DataBuffer record; GenerateEsniKey(time(nullptr), 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(client_, ssl_server_name_xtn); auto sfilter = MakeTlsFilter(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(time(nullptr), 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(client_, ssl_server_name_xtn); auto sfilter = MakeTlsFilter(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(time(nullptr), 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, time(0), time(0) + 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(client_, server_, ssl_grp_ec_secp256r1); auto cfilter = MakeTlsFilter(client_, ssl_server_name_xtn); auto sfilter = MakeTlsFilter(server_, ssl_server_name_xtn); server_->SetSniCallback(SniCallback); Connect(); CheckSniExtension(cfilter->extension()); ASSERT_TRUE(!sfilter->captured()); } TEST_P(TlsConnectTls13, ConnectMismatchedEsniKeys) { EnsureTlsSetup(); SetupEsni(client_, server_); // Now install a new set of keys on the client, so we have a mismatch. DataBuffer record; GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record); ClientInstallEsni(client_, record, 0); ConnectExpectAlert(server_, illegal_parameter); server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); } TEST_P(TlsConnectTls13, ConnectDamagedEsniExtensionCH) { EnsureTlsSetup(); SetupEsni(client_, server_); auto filter = MakeTlsFilter( 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(client_, server_); auto filter = MakeTlsFilter(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(client_, server_); DataBuffer shortNonce; auto filter = MakeTlsFilter( 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(client_, server_); const uint8_t bogusNonceBuf[16] = {0}; DataBuffer bogusNonce(bogusNonceBuf, sizeof(bogusNonceBuf)); auto filter = MakeTlsFilter( 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(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)); } }