summaryrefslogtreecommitdiff
path: root/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'nss/gtests/ssl_gtest/ssl_resumption_unittest.cc')
-rw-r--r--nss/gtests/ssl_gtest/ssl_resumption_unittest.cc582
1 files changed, 582 insertions, 0 deletions
diff --git a/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
new file mode 100644
index 0000000..cfe42cb
--- /dev/null
+++ b/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -0,0 +1,582 @@
+/* -*- 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 <functional>
+#include <memory>
+#include "secerr.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+extern "C" {
+// This is not something that should make you happy.
+#include "libssl_internals.h"
+}
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+class TlsServerKeyExchangeEcdhe {
+ public:
+ bool Parse(const DataBuffer& buffer) {
+ TlsParser parser(buffer);
+
+ uint8_t curve_type;
+ if (!parser.Read(&curve_type)) {
+ return false;
+ }
+
+ if (curve_type != 3) { // named_curve
+ return false;
+ }
+
+ uint32_t named_curve;
+ if (!parser.Read(&named_curve, 2)) {
+ return false;
+ }
+
+ return parser.ReadVariable(&public_key_, 1);
+ }
+
+ DataBuffer public_key_;
+};
+
+TEST_P(TlsConnectGenericPre13, ConnectResumed) {
+ ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
+ Connect();
+
+ Reset();
+ ExpectResumption(RESUME_SESSIONID);
+ Connect();
+}
+
+TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) {
+ ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) {
+ ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectSessionCacheDisabled) {
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeSupportBoth) {
+ // This prefers tickets.
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientTicketServerBoth) {
+ // This causes no resumption because the client needs the
+ // session cache to resume even with tickets.
+ ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicket) {
+ // This causes a ticket resumption.
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientServerTicketOnly) {
+ // This causes no resumption because the client needs the
+ // session cache to resume even with tickets.
+ ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothServerNone) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientNoneServerBoth) {
+ ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+TEST_P(TlsConnectGenericPre13, ConnectResumeWithHigherVersion) {
+ ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_1);
+ SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_1);
+ Connect();
+
+ Reset();
+ EnsureTlsSetup();
+ SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_2);
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+}
+
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicketForget) {
+ // This causes a ticket resumption.
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ClearServerCache();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
+// This callback switches out the "server" cert used on the server with
+// the "client" certificate, which should be the same type.
+static int32_t SwitchCertificates(TlsAgent* agent, const SECItem* srvNameArr,
+ uint32_t srvNameArrSize) {
+ bool ok = agent->ConfigServerCert("client");
+ if (!ok) return SSL_SNI_SEND_ALERT;
+
+ return 0; // first config
+};
+
+TEST_P(TlsConnectGeneric, ServerSNICertSwitch) {
+ Connect();
+ ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd()));
+
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+
+ server_->SetSniCallback(SwitchCertificates);
+
+ Connect();
+ ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd()));
+ CheckKeys();
+ EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+}
+
+TEST_P(TlsConnectGeneric, ServerSNICertTypeSwitch) {
+ Reset(TlsAgent::kServerEcdsa256);
+ Connect();
+ ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd()));
+
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+
+ // Because we configure an RSA certificate here, it only adds a second, unused
+ // certificate, which has no effect on what the server uses.
+ server_->SetSniCallback(SwitchCertificates);
+
+ Connect();
+ ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd()));
+ CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa);
+ EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+}
+
+// Prior to TLS 1.3, we were not fully ephemeral; though 1.3 fixes that
+TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) {
+ TlsInspectorRecordHandshakeMessage* i1 =
+ new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ server_->SetPacketFilter(i1);
+ Connect();
+ CheckKeys();
+ TlsServerKeyExchangeEcdhe dhe1;
+ EXPECT_TRUE(dhe1.Parse(i1->buffer()));
+
+ // Restart
+ Reset();
+ TlsInspectorRecordHandshakeMessage* i2 =
+ new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ server_->SetPacketFilter(i2);
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+ Connect();
+ CheckKeys();
+
+ TlsServerKeyExchangeEcdhe dhe2;
+ EXPECT_TRUE(dhe2.Parse(i2->buffer()));
+
+ // Make sure they are the same.
+ EXPECT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len());
+ EXPECT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
+ dhe1.public_key_.len()));
+}
+
+// This test parses the ServerKeyExchange, which isn't in 1.3
+TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) {
+ server_->EnsureTlsSetup();
+ SECStatus rv =
+ SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
+ EXPECT_EQ(SECSuccess, rv);
+ TlsInspectorRecordHandshakeMessage* i1 =
+ new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ server_->SetPacketFilter(i1);
+ Connect();
+ CheckKeys();
+ TlsServerKeyExchangeEcdhe dhe1;
+ EXPECT_TRUE(dhe1.Parse(i1->buffer()));
+
+ // Restart
+ Reset();
+ server_->EnsureTlsSetup();
+ rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
+ EXPECT_EQ(SECSuccess, rv);
+ TlsInspectorRecordHandshakeMessage* i2 =
+ new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ server_->SetPacketFilter(i2);
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+ Connect();
+ CheckKeys();
+
+ TlsServerKeyExchangeEcdhe dhe2;
+ EXPECT_TRUE(dhe2.Parse(i2->buffer()));
+
+ // Make sure they are different.
+ EXPECT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) &&
+ (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
+ dhe1.public_key_.len())));
+}
+
+// Verify that TLS 1.3 reports an accurate group on resumption.
+TEST_P(TlsConnectTls13, TestTls13ResumeDifferentGroup) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_TICKET);
+ client_->ConfigNamedGroups(kFFDHEGroups);
+ server_->ConfigNamedGroups(kFFDHEGroups);
+ Connect();
+ CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
+}
+
+// We need to enable different cipher suites at different times in the following
+// tests. Those cipher suites need to be suited to the version.
+static uint16_t ChooseOneCipher(uint16_t version) {
+ if (version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return TLS_AES_128_GCM_SHA256;
+ }
+ return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
+}
+
+static uint16_t ChooseAnotherCipher(uint16_t version) {
+ if (version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return TLS_AES_256_GCM_SHA384;
+ }
+ return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
+}
+
+// Test that we don't resume when we can't negotiate the same cipher.
+TEST_P(TlsConnectGeneric, TestResumeClientDifferentCipher) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ client_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ client_->EnableSingleCipher(ChooseAnotherCipher(version_));
+ uint16_t ticket_extension;
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ ticket_extension = ssl_tls13_pre_shared_key_xtn;
+ } else {
+ ticket_extension = ssl_session_ticket_xtn;
+ }
+ auto ticket_capture = new TlsExtensionCapture(ticket_extension);
+ client_->SetPacketFilter(ticket_capture);
+ Connect();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+ EXPECT_EQ(0U, ticket_capture->extension().len());
+}
+
+// Test that we don't resume when we can't negotiate the same cipher.
+TEST_P(TlsConnectGeneric, TestResumeServerDifferentCipher) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ server_->EnableSingleCipher(ChooseAnotherCipher(version_));
+ Connect();
+ CheckKeys();
+}
+
+class SelectedCipherSuiteReplacer : public TlsHandshakeFilter {
+ public:
+ SelectedCipherSuiteReplacer(uint16_t suite) : cipher_suite_(suite) {}
+
+ protected:
+ PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) override {
+ if (header.handshake_type() != kTlsHandshakeServerHello) {
+ return KEEP;
+ }
+
+ *output = input;
+ uint32_t temp = 0;
+ EXPECT_TRUE(input.Read(0, 2, &temp));
+ // Cipher suite is after version(2) and random(32).
+ size_t pos = 34;
+ if (temp < SSL_LIBRARY_VERSION_TLS_1_3) {
+ // In old versions, we have to skip a session_id too.
+ EXPECT_TRUE(input.Read(pos, 1, &temp));
+ pos += 1 + temp;
+ }
+ output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
+ return CHANGE;
+ }
+
+ private:
+ uint16_t cipher_suite_;
+};
+
+// Test that the client doesn't tolerate the server picking a different cipher
+// suite for resumption.
+TEST_P(TlsConnectStream, TestResumptionOverrideCipher) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->EnableSingleCipher(ChooseOneCipher(version_));
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ server_->SetPacketFilter(
+ new SelectedCipherSuiteReplacer(ChooseAnotherCipher(version_)));
+
+ ConnectExpectFail();
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ // The reason this test is stream only: the server is unable to decrypt
+ // the alert that the client sends, see bug 1304603.
+ server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+ } else {
+ server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
+ }
+}
+
+class SelectedVersionReplacer : public TlsHandshakeFilter {
+ public:
+ SelectedVersionReplacer(uint16_t version) : version_(version) {}
+
+ protected:
+ PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) override {
+ if (header.handshake_type() != kTlsHandshakeServerHello) {
+ return KEEP;
+ }
+
+ *output = input;
+ output->Write(0, static_cast<uint32_t>(version_), 2);
+ return CHANGE;
+ }
+
+ private:
+ uint16_t version_;
+};
+
+// Test how the client handles the case where the server picks a
+// lower version number on resumption.
+TEST_P(TlsConnectGenericPre13, TestResumptionOverrideVersion) {
+ uint16_t override_version = 0;
+ if (mode_ == STREAM) {
+ switch (version_) {
+ case SSL_LIBRARY_VERSION_TLS_1_0:
+ return; // Skip the test.
+ case SSL_LIBRARY_VERSION_TLS_1_1:
+ override_version = SSL_LIBRARY_VERSION_TLS_1_0;
+ break;
+ case SSL_LIBRARY_VERSION_TLS_1_2:
+ override_version = SSL_LIBRARY_VERSION_TLS_1_1;
+ break;
+ default:
+ ASSERT_TRUE(false) << "unknown version";
+ }
+ } else {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_2) {
+ override_version = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
+ } else {
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, version_);
+ return; // Skip the test.
+ }
+ }
+
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ // Need to use a cipher that is plausible for the lower version.
+ server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
+ Connect();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ // Enable the lower version on the client.
+ client_->SetVersionRange(version_ - 1, version_);
+ server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
+ server_->SetPacketFilter(new SelectedVersionReplacer(override_version));
+
+ ConnectExpectFail();
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+ server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
+}
+
+// Test that two TLS resumptions work and produce the same ticket.
+// This will change after bug 1257047 is fixed.
+TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys();
+ uint16_t original_suite;
+ EXPECT_TRUE(client_->cipher_suite(&original_suite));
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ExpectResumption(RESUME_TICKET);
+ TlsExtensionCapture* c1 =
+ new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
+ client_->SetPacketFilter(c1);
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+ ssl_sig_none);
+ // The filter will go away when we reset, so save the captured extension.
+ DataBuffer initialTicket(c1->extension());
+ ASSERT_LT(0U, initialTicket.len());
+
+ ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd()));
+ ASSERT_TRUE(!!cert1.get());
+
+ Reset();
+ ClearStats();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ TlsExtensionCapture* c2 =
+ new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
+ client_->SetPacketFilter(c2);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+ ssl_sig_none);
+ ASSERT_LT(0U, c2->extension().len());
+
+ ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd()));
+ ASSERT_TRUE(!!cert2.get());
+
+ // Check that the cipher suite is reported the same on both sides, though in
+ // TLS 1.3 resumption actually negotiates a different cipher suite.
+ uint16_t resumed_suite;
+ EXPECT_TRUE(server_->cipher_suite(&resumed_suite));
+ EXPECT_EQ(original_suite, resumed_suite);
+ EXPECT_TRUE(client_->cipher_suite(&resumed_suite));
+ EXPECT_EQ(original_suite, resumed_suite);
+
+ ASSERT_NE(initialTicket, c2->extension());
+}
+
+// Check that resumption works after receiving two NST messages.
+TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ Connect();
+
+ // Clear the session ticket keys to invalidate the old ticket.
+ SSLInt_ClearSessionTicketKey();
+ SSLInt_SendNewSessionTicket(server_->ssl_fd());
+
+ SendReceive(); // Need to read so that we absorb the session tickets.
+ CheckKeys();
+
+ // Resume the connection.
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+}
+
+} // namespace nss_test