summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-12-15 01:42:53 +0100
committerwolfbeast <mcwerewolf@gmail.com>2018-12-15 01:42:53 +0100
commit74cabf7948b2597f5b6a67d6910c844fd1a88ff6 (patch)
treedb1f30ada487c3831ea8e4e98b2d39edc9e88eea /security/nss/gtests/ssl_gtest/tls_esni_unittest.cc
parent09ef48bd005a7f9e97a3fe797a079fcf2b5e58d3 (diff)
downloadUXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar
UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.gz
UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.lz
UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.xz
UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.zip
Update NSS to 3.41
Diffstat (limited to 'security/nss/gtests/ssl_gtest/tls_esni_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/tls_esni_unittest.cc470
1 files changed, 470 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc b/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc
new file mode 100644
index 000000000..3c860a0b2
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_esni_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 <ctime>
+
+#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<uint16_t> kDefaultSuites = {TLS_AES_256_GCM_SHA384,
+ TLS_AES_128_GCM_SHA256};
+std::vector<uint16_t> kChaChaSuite = {TLS_CHACHA20_POLY1305_SHA256};
+std::vector<uint16_t> kBogusSuites = {0};
+std::vector<uint16_t> 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<uint16_t>& 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;
+
+ 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<TlsAgent>& client,
+ const std::shared_ptr<TlsAgent>& 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<const uint8_t*>(kDummySni),
+ strlen(kDummySni));
+ ASSERT_EQ(expected, name);
+}
+
+static void ClientInstallEsni(std::shared_ptr<TlsAgent>& 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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<unsigned char*>(const_cast<char*>("server")),
+ 6};
+ EXPECT_TRUE(!SECITEM_CompareItem(&expected, &srvNameAddr[0]));
+ return SECSuccess;
+}
+
+TEST_P(TlsConnectTls13, ConnectEsni) {
+ EnsureTlsSetup();
+ SetupEsni(client_, server_);
+ auto cFilterSni =
+ MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ auto cFilterEsni =
+ MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_encrypted_sni_xtn);
+ client_->SetFilter(std::make_shared<ChainedPacketFilter>(
+ ChainedPacketFilterInit({cFilterSni, cFilterEsni})));
+ auto sfilter =
+ MakeTlsFilter<TlsExtensionCapture>(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<PRUint16>(suite));
+ ASSERT_TRUE(!sfilter->captured());
+}
+
+TEST_P(TlsConnectTls13, ConnectEsniHrr) {
+ EnsureTlsSetup();
+ const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+ server_->ConfigNamedGroups(groups);
+ SetupEsni(client_, server_);
+ auto hrr_capture = MakeTlsFilter<TlsHandshakeRecorder>(
+ server_, kTlsHandshakeHelloRetryRequest);
+ auto filter =
+ MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ auto cfilter =
+ MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ server_->SetSniCallback(SniCallback);
+ Connect();
+ CheckSniExtension(cfilter->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<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ auto sfilter =
+ MakeTlsFilter<TlsExtensionCapture>(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<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ auto sfilter =
+ MakeTlsFilter<TlsExtensionCapture>(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;
+
+ 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));
+ 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<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+ auto sfilter =
+ MakeTlsFilter<TlsExtensionCapture>(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<TlsExtensionDamager>(
+ 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<TlsExtensionDropper>(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<TlsExtensionReplacer>(
+ 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<TlsExtensionReplacer>(
+ 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));
+}
+}