diff options
Diffstat (limited to 'chromium/net/quic/core/crypto/quic_crypto_client_config_test.cc')
-rw-r--r-- | chromium/net/quic/core/crypto/quic_crypto_client_config_test.cc | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/chromium/net/quic/core/crypto/quic_crypto_client_config_test.cc b/chromium/net/quic/core/crypto/quic_crypto_client_config_test.cc new file mode 100644 index 00000000000..072f8657abe --- /dev/null +++ b/chromium/net/quic/core/crypto/quic_crypto_client_config_test.cc @@ -0,0 +1,539 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/core/crypto/quic_crypto_client_config.h" + +#include "net/quic/core/crypto/proof_verifier.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/mock_random.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; +using std::vector; + +namespace net { +namespace test { +namespace { + +class TestProofVerifyDetails : public ProofVerifyDetails { + ~TestProofVerifyDetails() override {} + + // ProofVerifyDetails implementation + ProofVerifyDetails* Clone() const override { + return new TestProofVerifyDetails; + } +}; + +class OneServerIdFilter : public QuicCryptoClientConfig::ServerIdFilter { + public: + OneServerIdFilter(const QuicServerId* server_id) : server_id_(*server_id) {} + + bool Matches(const QuicServerId& server_id) const override { + return server_id == server_id_; + } + + private: + const QuicServerId server_id_; +}; + +class AllServerIdsFilter : public QuicCryptoClientConfig::ServerIdFilter { + public: + bool Matches(const QuicServerId& server_id) const override { return true; } +}; + +} // namespace + +TEST(QuicCryptoClientConfigTest, CachedState_IsEmpty) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_IsComplete) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0))); +} + +TEST(QuicCryptoClientConfigTest, CachedState_GenerationCounter) { + QuicCryptoClientConfig::CachedState state; + EXPECT_EQ(0u, state.generation_counter()); + state.SetProofInvalid(); + EXPECT_EQ(1u, state.generation_counter()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.proof_verify_details() == nullptr); + ProofVerifyDetails* details = new TestProofVerifyDetails; + state.SetProofVerifyDetails(details); + EXPECT_EQ(details, state.proof_verify_details()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_ServerDesignatedConnectionId) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_designated_connection_id()); + + QuicConnectionId connection_id = 1234; + state.add_server_designated_connection_id(connection_id); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + + // Allow the ID to be set multiple times. It's unusual that this would + // happen, but not impossible. + ++connection_id; + state.add_server_designated_connection_id(connection_id); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + ++connection_id; + state.add_server_designated_connection_id(connection_id); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + + // Test FIFO behavior. + const QuicConnectionId first_cid = 0xdeadbeef; + const QuicConnectionId second_cid = 0xfeedbead; + state.add_server_designated_connection_id(first_cid); + state.add_server_designated_connection_id(second_cid); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(first_cid, state.GetNextServerDesignatedConnectionId()); + EXPECT_EQ(second_cid, state.GetNextServerDesignatedConnectionId()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_ServerIdConsumedBeforeSet) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_designated_connection_id()); +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEBUG_DEATH(state.GetNextServerDesignatedConnectionId(), + "Attempting to consume a connection id " + "that was never designated."); +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +} + +TEST(QuicCryptoClientConfigTest, CachedState_ServerNonce) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); + + string server_nonce = "nonce_1"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Allow the ID to be set multiple times. It's unusual that this would + // happen, but not impossible. + server_nonce = "nonce_2"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + server_nonce = "nonce_3"; + state.add_server_nonce(server_nonce); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Test FIFO behavior. + const string first_nonce = "first_nonce"; + const string second_nonce = "second_nonce"; + state.add_server_nonce(first_nonce); + state.add_server_nonce(second_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(first_nonce, state.GetNextServerNonce()); + EXPECT_EQ(second_nonce, state.GetNextServerNonce()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_ServerNonceConsumedBeforeSet) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEBUG_DEATH(state.GetNextServerNonce(), + "Attempting to consume a server nonce " + "that was never designated."); +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +} + +TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig::CachedState other; + state.set_source_address_token("TOKEN"); + // TODO(rch): Populate other fields of |state|. + other.InitializeFrom(state); + EXPECT_EQ(state.server_config(), other.server_config()); + EXPECT_EQ(state.source_address_token(), other.source_address_token()); + EXPECT_EQ(state.certs(), other.certs()); + EXPECT_EQ(1u, other.generation_counter()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + EXPECT_FALSE(state.has_server_nonce()); +} + +TEST(QuicCryptoClientConfigTest, InchoateChlo) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, ¶ms, &msg); + + QuicTag cver; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kVER, &cver)); + EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver); + StringPiece proof_nonce; + EXPECT_TRUE(msg.GetStringPiece(kNONP, &proof_nonce)); + EXPECT_EQ(string(32, 'r'), proof_nonce); +} + +TEST(QuicCryptoClientConfigTest, PreferAesGcm) { + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + if (config.aead.size() > 1) + EXPECT_NE(kAESG, config.aead[0]); + config.PreferAesGcm(); + EXPECT_EQ(kAESG, config.aead[0]); +} + +TEST(QuicCryptoClientConfigTest, InchoateChloSecure) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, ¶ms, &msg); + + QuicTag pdmd; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd)); + EXPECT_EQ(kX509, pdmd); + StringPiece scid; + EXPECT_FALSE(msg.GetStringPiece(kSCID, &scid)); +} + +TEST(QuicCryptoClientConfigTest, InchoateChloSecureWithSCIDNoEXPY) { + // Test that a config with no EXPY is still valid when a non-zero + // expiry time is passed in. + QuicCryptoClientConfig::CachedState state; + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + scfg.SetStringPiece(kSCID, "12345678"); + string details; + QuicWallTime now = QuicWallTime::FromUNIXSeconds(1); + QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2); + state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry, + &details); + + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, ¶ms, &msg); + + StringPiece scid; + EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid)); + EXPECT_EQ("12345678", scid); +} + +TEST(QuicCryptoClientConfigTest, InchoateChloSecureWithSCID) { + QuicCryptoClientConfig::CachedState state; + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + uint64_t future = 1; + scfg.SetValue(kEXPY, future); + scfg.SetStringPiece(kSCID, "12345678"); + string details; + state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), + QuicWallTime::FromUNIXSeconds(1), + QuicWallTime::FromUNIXSeconds(0), &details); + + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, ¶ms, &msg); + + StringPiece scid; + EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid)); + EXPECT_EQ("12345678", scid); +} + +TEST(QuicCryptoClientConfigTest, FillClientHello) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoNegotiatedParameters params; + QuicConnectionId kConnectionId = 1234; + string error_details; + MockRandom rand; + CryptoHandshakeMessage chlo; + QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED); + config.FillClientHello(server_id, kConnectionId, QuicVersionMax(), + QuicVersionMax(), &state, QuicWallTime::Zero(), &rand, + nullptr, // channel_id_key + ¶ms, &chlo, &error_details); + + // Verify that certain QuicTags have been set correctly in the CHLO. + QuicTag cver; + EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver)); + EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver); +} + +TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) { + QuicVersionVector supported_versions = AllSupportedVersions(); + if (supported_versions.size() == 1) { + // No downgrade attack is possible if the client only supports one version. + return; + } + QuicTagVector supported_version_tags; + for (size_t i = supported_versions.size(); i > 0; --i) { + supported_version_tags.push_back( + QuicVersionToQuicTag(supported_versions[i - 1])); + } + CryptoHandshakeMessage msg; + msg.set_tag(kSHLO); + msg.SetVector(kVER, supported_version_tags); + + QuicCryptoClientConfig::CachedState cached; + QuicCryptoNegotiatedParameters out_params; + string error; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + EXPECT_EQ(QUIC_VERSION_NEGOTIATION_MISMATCH, + config.ProcessServerHello(msg, 0, supported_versions.front(), + supported_versions, &cached, &out_params, + &error)); + EXPECT_EQ("Downgrade attack detected", error); +} + +TEST(QuicCryptoClientConfigTest, InitializeFrom) { + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicServerId canonical_server_id("www.google.com", 443, + PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_server_id); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicServerId other_server_id("mail.google.com", 443, PRIVACY_MODE_DISABLED); + config.InitializeFrom(other_server_id, canonical_server_id, &config); + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(other_server_id); + + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); +} + +TEST(QuicCryptoClientConfigTest, Canonical) { + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 443, PRIVACY_MODE_DISABLED); + QuicServerId canonical_id2("mail.google.com", 443, PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(canonical_id2); + + EXPECT_TRUE(state->IsEmpty()); + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); + + QuicServerId different_id("mail.google.org", 443, PRIVACY_MODE_DISABLED); + EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) { + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 443, PRIVACY_MODE_DISABLED); + QuicServerId canonical_id2("mail.google.com", 443, PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + + // Do not set the proof as valid, and check that it is not used + // as a canonical entry. + EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, ClearCachedStates) { + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + + // Create two states on different origins. + struct TestCase { + TestCase(const std::string& host, QuicCryptoClientConfig* config) + : server_id(host, 443, PRIVACY_MODE_DISABLED), + state(config->LookupOrCreate(server_id)) { + // TODO(rch): Populate other fields of |state|. + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + uint64_t future = 1; + scfg.SetValue(kEXPY, future); + scfg.SetStringPiece(kSCID, "12345678"); + string details; + state->SetServerConfig(scfg.GetSerialized().AsStringPiece(), + QuicWallTime::FromUNIXSeconds(0), + QuicWallTime::FromUNIXSeconds(future), &details); + + vector<string> certs(1); + certs[0] = "Hello Cert for " + host; + state->SetProof(certs, "cert_sct", "chlo_hash", "signature"); + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + // The generation counter starts at 2, because proof has been once + // invalidated in SetServerConfig(). + EXPECT_EQ(2u, state->generation_counter()); + } + + QuicServerId server_id; + QuicCryptoClientConfig::CachedState* state; + } test_cases[] = {TestCase("www.google.com", &config), + TestCase("www.example.com", &config)}; + + // Verify LookupOrCreate returns the same data. + for (const TestCase& test_case : test_cases) { + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(test_case.server_id); + EXPECT_EQ(test_case.state, other); + EXPECT_EQ(2u, other->generation_counter()); + } + + // Clear the cached state for www.google.com. + OneServerIdFilter google_com_filter(&test_cases[0].server_id); + config.ClearCachedStates(google_com_filter); + + // Verify LookupOrCreate doesn't have any data for google.com. + QuicCryptoClientConfig::CachedState* cleared_cache = + config.LookupOrCreate(test_cases[0].server_id); + + EXPECT_EQ(test_cases[0].state, cleared_cache); + EXPECT_FALSE(cleared_cache->proof_valid()); + EXPECT_TRUE(cleared_cache->server_config().empty()); + EXPECT_TRUE(cleared_cache->certs().empty()); + EXPECT_TRUE(cleared_cache->cert_sct().empty()); + EXPECT_TRUE(cleared_cache->signature().empty()); + EXPECT_EQ(3u, cleared_cache->generation_counter()); + + // But it still does for www.example.com. + QuicCryptoClientConfig::CachedState* existing_cache = + config.LookupOrCreate(test_cases[1].server_id); + + EXPECT_EQ(test_cases[1].state, existing_cache); + EXPECT_TRUE(existing_cache->proof_valid()); + EXPECT_FALSE(existing_cache->server_config().empty()); + EXPECT_FALSE(existing_cache->certs().empty()); + EXPECT_FALSE(existing_cache->cert_sct().empty()); + EXPECT_FALSE(existing_cache->signature().empty()); + EXPECT_EQ(2u, existing_cache->generation_counter()); + + // Clear all cached states. + AllServerIdsFilter all_server_ids; + config.ClearCachedStates(all_server_ids); + + // The data for www.example.com should now be cleared as well. + cleared_cache = config.LookupOrCreate(test_cases[1].server_id); + + EXPECT_EQ(test_cases[1].state, cleared_cache); + EXPECT_FALSE(cleared_cache->proof_valid()); + EXPECT_TRUE(cleared_cache->server_config().empty()); + EXPECT_TRUE(cleared_cache->certs().empty()); + EXPECT_TRUE(cleared_cache->cert_sct().empty()); + EXPECT_TRUE(cleared_cache->signature().empty()); + EXPECT_EQ(3u, cleared_cache->generation_counter()); +} + +TEST(QuicCryptoClientConfigTest, ProcessReject) { + CryptoHandshakeMessage rej; + CryptoTestUtils::FillInDummyReject(&rej, /* stateless */ false); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicCryptoNegotiatedParameters out_params; + string error; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + EXPECT_EQ(QUIC_NO_ERROR, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedVersions().front(), "", &cached, + &out_params, &error)); + EXPECT_FALSE(cached.has_server_designated_connection_id()); + EXPECT_FALSE(cached.has_server_nonce()); +} + +TEST(QuicCryptoClientConfigTest, ProcessStatelessReject) { + // Create a dummy reject message and mark it as stateless. + CryptoHandshakeMessage rej; + CryptoTestUtils::FillInDummyReject(&rej, /* stateless */ true); + const QuicConnectionId kConnectionId = 0xdeadbeef; + const string server_nonce = "SERVER_NONCE"; + rej.SetValue(kRCID, kConnectionId); + rej.SetStringPiece(kServerNonceTag, server_nonce); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicCryptoNegotiatedParameters out_params; + string error; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + EXPECT_EQ(QUIC_NO_ERROR, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedVersions().front(), "", &cached, + &out_params, &error)); + EXPECT_TRUE(cached.has_server_designated_connection_id()); + EXPECT_EQ(kConnectionId, cached.GetNextServerDesignatedConnectionId()); + EXPECT_EQ(server_nonce, cached.GetNextServerNonce()); +} + +TEST(QuicCryptoClientConfigTest, BadlyFormattedStatelessReject) { + // Create a dummy reject message and mark it as stateless. Do not + // add an server-designated connection-id. + CryptoHandshakeMessage rej; + CryptoTestUtils::FillInDummyReject(&rej, /* stateless */ true); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicCryptoNegotiatedParameters out_params; + string error; + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedVersions().front(), "", &cached, + &out_params, &error)); + EXPECT_FALSE(cached.has_server_designated_connection_id()); + EXPECT_EQ("Missing kRCID", error); +} + +TEST(QuicCryptoClientConfigTest, ServerNonceinSHLO) { + // Test that the server must include a nonce in the SHLO. + CryptoHandshakeMessage msg; + msg.set_tag(kSHLO); + // Choose the latest version. + QuicVersionVector supported_versions; + QuicVersion version = AllSupportedVersions().front(); + supported_versions.push_back(version); + QuicTagVector versions; + versions.push_back(QuicVersionToQuicTag(version)); + msg.SetVector(kVER, versions); + + QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting()); + QuicCryptoClientConfig::CachedState cached; + QuicCryptoNegotiatedParameters out_params; + string error_details; + EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + config.ProcessServerHello(msg, 0, version, supported_versions, + &cached, &out_params, &error_details)); + EXPECT_EQ("server hello missing server nonce", error_details); +} + +} // namespace test +} // namespace net |