| // Copyright (c) 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 <ostream> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "crypto/secure_hash.h" |
| #include "net/quic/crypto/crypto_utils.h" |
| #include "net/quic/crypto/quic_crypto_server_config.h" |
| #include "net/quic/crypto/quic_random.h" |
| #include "net/quic/quic_flags.h" |
| #include "net/quic/quic_socket_address_coder.h" |
| #include "net/quic/quic_utils.h" |
| #include "net/quic/test_tools/crypto_test_utils.h" |
| #include "net/quic/test_tools/delayed_verify_strike_register_client.h" |
| #include "net/quic/test_tools/mock_clock.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 base::StringPiece; |
| using std::ostream; |
| using std::string; |
| using std::vector; |
| |
| namespace net { |
| namespace test { |
| |
| class QuicCryptoServerConfigPeer { |
| public: |
| explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) |
| : server_config_(server_config) {} |
| |
| base::Lock* GetStrikeRegisterClientLock() { |
| return &server_config_->strike_register_client_lock_; |
| } |
| |
| private: |
| QuicCryptoServerConfig* server_config_; |
| }; |
| |
| // Run tests with combinations of |
| // {FLAGS_use_early_return_when_verifying_chlo, |
| // FLAGS_send_quic_crypto_reject_reason}. |
| struct TestParams { |
| TestParams(bool use_early_return_when_verifying_chlo, |
| bool send_quic_crypto_reject_reason) |
| : use_early_return_when_verifying_chlo( |
| use_early_return_when_verifying_chlo), |
| send_quic_crypto_reject_reason(send_quic_crypto_reject_reason) { |
| } |
| |
| friend ostream& operator<<(ostream& os, const TestParams& p) { |
| os << "{ use_early_return_when_verifying_chlo: " |
| << p.use_early_return_when_verifying_chlo |
| << " send_quic_crypto_reject_reason: " |
| << p.send_quic_crypto_reject_reason << " }"; |
| return os; |
| } |
| |
| bool use_early_return_when_verifying_chlo; |
| bool send_quic_crypto_reject_reason; |
| }; |
| |
| // Constructs various test permutations. |
| vector<TestParams> GetTestParams() { |
| vector<TestParams> params; |
| params.push_back(TestParams(false, false)); |
| params.push_back(TestParams(false, true)); |
| params.push_back(TestParams(true, false)); |
| params.push_back(TestParams(true, true)); |
| return params; |
| } |
| |
| class CryptoServerTest : public ::testing::TestWithParam<TestParams> { |
| public: |
| CryptoServerTest() |
| : rand_(QuicRandom::GetInstance()), |
| client_address_(Loopback4(), 1234), |
| config_(QuicCryptoServerConfig::TESTING, rand_) { |
| config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); |
| supported_versions_ = QuicSupportedVersions(); |
| client_version_ = QuicUtils::TagToString( |
| QuicVersionToQuicTag(supported_versions_.front())); |
| |
| FLAGS_use_early_return_when_verifying_chlo = |
| GetParam().use_early_return_when_verifying_chlo; |
| FLAGS_send_quic_crypto_reject_reason = |
| GetParam().send_quic_crypto_reject_reason; |
| } |
| |
| virtual void SetUp() { |
| scoped_ptr<CryptoHandshakeMessage> msg( |
| config_.AddDefaultConfig(rand_, &clock_, |
| config_options_)); |
| |
| StringPiece orbit; |
| CHECK(msg->GetStringPiece(kORBT, &orbit)); |
| CHECK_EQ(sizeof(orbit_), orbit.size()); |
| memcpy(orbit_, orbit.data(), orbit.size()); |
| |
| char public_value[32]; |
| memset(public_value, 42, sizeof(public_value)); |
| |
| const string nonce_str = GenerateNonce(); |
| nonce_hex_ = "#" + base::HexEncode(nonce_str.data(), nonce_str.size()); |
| pub_hex_ = "#" + base::HexEncode(public_value, sizeof(public_value)); |
| |
| CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(client_hello); |
| // The message should be rejected because the source-address token is |
| // missing. |
| ASSERT_EQ(kREJ, out_.tag()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| |
| StringPiece srct; |
| ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct)); |
| srct_hex_ = "#" + base::HexEncode(srct.data(), srct.size()); |
| |
| StringPiece scfg; |
| ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg)); |
| server_config_.reset(CryptoFramer::ParseMessage(scfg)); |
| |
| StringPiece scid; |
| ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid)); |
| scid_hex_ = "#" + base::HexEncode(scid.data(), scid.size()); |
| } |
| |
| // Helper used to accept the result of ValidateClientHello and pass |
| // it on to ProcessClientHello. |
| class ValidateCallback : public ValidateClientHelloResultCallback { |
| public: |
| ValidateCallback(CryptoServerTest* test, |
| bool should_succeed, |
| const char* error_substr, |
| bool* called) |
| : test_(test), |
| should_succeed_(should_succeed), |
| error_substr_(error_substr), |
| called_(called) { |
| *called_ = false; |
| } |
| |
| virtual void RunImpl(const CryptoHandshakeMessage& client_hello, |
| const Result& result) OVERRIDE { |
| { |
| // Ensure that the strike register client lock is not held. |
| QuicCryptoServerConfigPeer peer(&test_->config_); |
| base::Lock* m = peer.GetStrikeRegisterClientLock(); |
| // In Chromium, we will dead lock if the lock is held by the current |
| // thread. Chromium doesn't have AssertNotHeld API call. |
| // m->AssertNotHeld(); |
| base::AutoLock lock(*m); |
| } |
| ASSERT_FALSE(*called_); |
| test_->ProcessValidationResult( |
| client_hello, result, should_succeed_, error_substr_); |
| *called_ = true; |
| } |
| |
| private: |
| CryptoServerTest* test_; |
| bool should_succeed_; |
| const char* error_substr_; |
| bool* called_; |
| }; |
| |
| void CheckServerHello(const CryptoHandshakeMessage& server_hello) { |
| const QuicTag* versions; |
| size_t num_versions; |
| server_hello.GetTaglist(kVER, &versions, &num_versions); |
| ASSERT_EQ(QuicSupportedVersions().size(), num_versions); |
| for (size_t i = 0; i < num_versions; ++i) { |
| EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]); |
| } |
| |
| StringPiece address; |
| ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address)); |
| QuicSocketAddressCoder decoder; |
| ASSERT_TRUE(decoder.Decode(address.data(), address.size())); |
| EXPECT_EQ(client_address_.address(), decoder.ip()); |
| EXPECT_EQ(client_address_.port(), decoder.port()); |
| } |
| |
| void ShouldSucceed(const CryptoHandshakeMessage& message) { |
| bool called = false; |
| RunValidate(message, new ValidateCallback(this, true, "", &called)); |
| EXPECT_TRUE(called); |
| } |
| |
| void RunValidate( |
| const CryptoHandshakeMessage& message, |
| ValidateClientHelloResultCallback* cb) { |
| config_.ValidateClientHello(message, client_address_, &clock_, cb); |
| } |
| |
| void ShouldFailMentioning(const char* error_substr, |
| const CryptoHandshakeMessage& message) { |
| bool called = false; |
| ShouldFailMentioning(error_substr, message, &called); |
| EXPECT_TRUE(called); |
| } |
| |
| void ShouldFailMentioning(const char* error_substr, |
| const CryptoHandshakeMessage& message, |
| bool* called) { |
| config_.ValidateClientHello( |
| message, client_address_, &clock_, |
| new ValidateCallback(this, false, error_substr, called)); |
| } |
| |
| void ProcessValidationResult(const CryptoHandshakeMessage& message, |
| const ValidateCallback::Result& result, |
| bool should_succeed, |
| const char* error_substr) { |
| string error_details; |
| QuicErrorCode error = config_.ProcessClientHello( |
| result, 1 /* ConnectionId */, client_address_, |
| supported_versions_.front(), supported_versions_, &clock_, rand_, |
| ¶ms_, &out_, &error_details); |
| |
| if (should_succeed) { |
| ASSERT_EQ(error, QUIC_NO_ERROR) |
| << "Message failed with error " << error_details << ": " |
| << message.DebugString(); |
| } else { |
| ASSERT_NE(error, QUIC_NO_ERROR) |
| << "Message didn't fail: " << message.DebugString(); |
| |
| EXPECT_TRUE(error_details.find(error_substr) != string::npos) |
| << error_substr << " not in " << error_details; |
| } |
| } |
| |
| CryptoHandshakeMessage InchoateClientHello(const char* message_tag, ...) { |
| va_list ap; |
| va_start(ap, message_tag); |
| |
| CryptoHandshakeMessage message = |
| CryptoTestUtils::BuildMessage(message_tag, ap); |
| va_end(ap); |
| |
| message.SetStringPiece(kPAD, string(kClientHelloMinimumSize, '-')); |
| return message; |
| } |
| |
| string GenerateNonce() { |
| string nonce; |
| CryptoUtils::GenerateNonce( |
| clock_.WallNow(), rand_, |
| StringPiece(reinterpret_cast<const char*>(orbit_), sizeof(orbit_)), |
| &nonce); |
| return nonce; |
| } |
| |
| void CheckRejectReasons( |
| const HandshakeFailureReason* expected_handshake_failures, |
| size_t expected_count) { |
| const QuicTag* reject_reason_tags; |
| size_t num_reject_reasons; |
| QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reason_tags, |
| &num_reject_reasons); |
| if (!FLAGS_send_quic_crypto_reject_reason) { |
| ASSERT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error_code); |
| return; |
| } |
| ASSERT_EQ(QUIC_NO_ERROR, error_code); |
| |
| if (FLAGS_use_early_return_when_verifying_chlo) { |
| EXPECT_EQ(1u, num_reject_reasons); |
| } else { |
| EXPECT_EQ(expected_count, num_reject_reasons); |
| } |
| for (size_t i = 0; i < num_reject_reasons; ++i) { |
| EXPECT_EQ(expected_handshake_failures[i], reject_reason_tags[i]); |
| } |
| } |
| |
| protected: |
| QuicRandom* const rand_; |
| MockClock clock_; |
| const IPEndPoint client_address_; |
| QuicVersionVector supported_versions_; |
| string client_version_; |
| QuicCryptoServerConfig config_; |
| QuicCryptoServerConfig::ConfigOptions config_options_; |
| QuicCryptoNegotiatedParameters params_; |
| CryptoHandshakeMessage out_; |
| uint8 orbit_[kOrbitSize]; |
| |
| // These strings contain hex escaped values from the server suitable for |
| // passing to |InchoateClientHello| when constructing client hello messages. |
| string nonce_hex_, pub_hex_, srct_hex_, scid_hex_; |
| scoped_ptr<CryptoHandshakeMessage> server_config_; |
| }; |
| |
| // Run all CryptoServerTest with all combinations of |
| // FLAGS_use_early_return_when_verifying_chlo and |
| // FLAGS_send_quic_crypto_reject_reason. |
| INSTANTIATE_TEST_CASE_P(CryptoServerTests, |
| CryptoServerTest, |
| ::testing::ValuesIn(GetTestParams())); |
| |
| TEST_P(CryptoServerTest, BadSNI) { |
| static const char* kBadSNIs[] = { |
| "", |
| "foo", |
| "#00", |
| "#ff00", |
| "127.0.0.1", |
| "ffee::1", |
| }; |
| |
| string client_version = QuicUtils::TagToString( |
| QuicVersionToQuicTag(supported_versions_.front())); |
| |
| for (size_t i = 0; i < arraysize(kBadSNIs); i++) { |
| ShouldFailMentioning("SNI", InchoateClientHello( |
| "CHLO", |
| "SNI", kBadSNIs[i], |
| "VER\0", client_version.data(), |
| NULL)); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| } |
| |
| // TODO(rtenneti): Enable the DefaultCert test after implementing ProofSource. |
| TEST_F(CryptoServerTest, DISABLED_DefaultCert) { |
| // Check that the server replies with a default certificate when no SNI is |
| // specified. |
| ShouldSucceed(InchoateClientHello( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", srct_hex_.c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| "PDMD", "X509", |
| "VER\0", client_version_.data(), |
| NULL)); |
| |
| StringPiece cert, proof; |
| EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); |
| EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); |
| EXPECT_NE(0u, cert.size()); |
| EXPECT_NE(0u, proof.size()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| CLIENT_NONCE_UNKNOWN_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, TooSmall) { |
| ShouldFailMentioning("too small", CryptoTestUtils::Message( |
| "CHLO", |
| "VER\0", client_version_.data(), |
| NULL)); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, BadSourceAddressToken) { |
| // Invalid source-address tokens should be ignored. |
| static const char* kBadSourceAddressTokens[] = { |
| "", |
| "foo", |
| "#0000", |
| "#0000000000000000000000000000000000000000", |
| }; |
| |
| for (size_t i = 0; i < arraysize(kBadSourceAddressTokens); i++) { |
| ShouldSucceed(InchoateClientHello( |
| "CHLO", |
| "STK", kBadSourceAddressTokens[i], |
| "VER\0", client_version_.data(), |
| NULL)); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| } |
| |
| TEST_P(CryptoServerTest, BadClientNonce) { |
| // Invalid nonces should be ignored. |
| static const char* kBadNonces[] = { |
| "", |
| "#0000", |
| "#0000000000000000000000000000000000000000", |
| }; |
| |
| for (size_t i = 0; i < arraysize(kBadNonces); i++) { |
| ShouldSucceed(InchoateClientHello( |
| "CHLO", |
| "NONC", kBadNonces[i], |
| "VER\0", client_version_.data(), |
| NULL)); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| } |
| |
| TEST_P(CryptoServerTest, DowngradeAttack) { |
| if (supported_versions_.size() == 1) { |
| // No downgrade attack is possible if the server only supports one version. |
| return; |
| } |
| // Set the client's preferred version to a supported version that |
| // is not the "current" version (supported_versions_.front()). |
| string bad_version = QuicUtils::TagToString( |
| QuicVersionToQuicTag(supported_versions_.back())); |
| |
| ShouldFailMentioning("Downgrade", InchoateClientHello( |
| "CHLO", |
| "VER\0", bad_version.data(), |
| NULL)); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, CorruptServerConfig) { |
| // This tests corrupted server config. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", (string(1, 'X') + scid_hex_).c_str(), |
| "#004b5453", srct_hex_.c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(msg); |
| ASSERT_EQ(kREJ, out_.tag()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, CorruptSourceAddressToken) { |
| // This tests corrupted source address token. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", (string(1, 'X') + srct_hex_).c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(msg); |
| ASSERT_EQ(kREJ, out_.tag()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) { |
| // This test corrupts client nonce and source address token. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", (string(1, 'X') + srct_hex_).c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", (string(1, 'X') + nonce_hex_).c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(msg); |
| ASSERT_EQ(kREJ, out_.tag()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, |
| CLIENT_NONCE_INVALID_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, CorruptMultipleTags) { |
| // This test corrupts client nonce, server nonce and source address token. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", (string(1, 'X') + srct_hex_).c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", (string(1, 'X') + nonce_hex_).c_str(), |
| "SNO\0", (string(1, 'X') + nonce_hex_).c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(msg); |
| ASSERT_EQ(kREJ, out_.tag()); |
| const HandshakeFailureReason kRejectReasons[] = { |
| SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, |
| CLIENT_NONCE_INVALID_FAILURE, |
| SERVER_NONCE_DECRYPTION_FAILURE, |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| TEST_P(CryptoServerTest, ReplayProtection) { |
| // This tests that disabling replay protection works. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", srct_hex_.c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| ShouldSucceed(msg); |
| // The message should be rejected because the strike-register is still |
| // quiescent. |
| ASSERT_EQ(kREJ, out_.tag()); |
| |
| const HandshakeFailureReason kRejectReasons[] = { |
| CLIENT_NONCE_UNKNOWN_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| |
| config_.set_replay_protection(false); |
| |
| ShouldSucceed(msg); |
| // The message should be accepted now. |
| ASSERT_EQ(kSHLO, out_.tag()); |
| CheckServerHello(out_); |
| |
| ShouldSucceed(msg); |
| // The message should accepted twice when replay protection is off. |
| ASSERT_EQ(kSHLO, out_.tag()); |
| CheckServerHello(out_); |
| } |
| |
| TEST(CryptoServerConfigGenerationTest, Determinism) { |
| // Test that using a deterministic PRNG causes the server-config to be |
| // deterministic. |
| |
| MockRandom rand_a, rand_b; |
| const QuicCryptoServerConfig::ConfigOptions options; |
| MockClock clock; |
| |
| QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a); |
| QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b); |
| scoped_ptr<CryptoHandshakeMessage> scfg_a( |
| a.AddDefaultConfig(&rand_a, &clock, options)); |
| scoped_ptr<CryptoHandshakeMessage> scfg_b( |
| b.AddDefaultConfig(&rand_b, &clock, options)); |
| |
| ASSERT_EQ(scfg_a->DebugString(), scfg_b->DebugString()); |
| } |
| |
| TEST(CryptoServerConfigGenerationTest, SCIDVaries) { |
| // This test ensures that the server config ID varies for different server |
| // configs. |
| |
| MockRandom rand_a, rand_b; |
| const QuicCryptoServerConfig::ConfigOptions options; |
| MockClock clock; |
| |
| QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a); |
| rand_b.ChangeValue(); |
| QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b); |
| scoped_ptr<CryptoHandshakeMessage> scfg_a( |
| a.AddDefaultConfig(&rand_a, &clock, options)); |
| scoped_ptr<CryptoHandshakeMessage> scfg_b( |
| b.AddDefaultConfig(&rand_b, &clock, options)); |
| |
| StringPiece scid_a, scid_b; |
| EXPECT_TRUE(scfg_a->GetStringPiece(kSCID, &scid_a)); |
| EXPECT_TRUE(scfg_b->GetStringPiece(kSCID, &scid_b)); |
| |
| EXPECT_NE(scid_a, scid_b); |
| } |
| |
| |
| TEST(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) { |
| MockRandom rand_a; |
| const QuicCryptoServerConfig::ConfigOptions options; |
| MockClock clock; |
| |
| QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a); |
| scoped_ptr<CryptoHandshakeMessage> scfg( |
| a.AddDefaultConfig(&rand_a, &clock, options)); |
| |
| StringPiece scid; |
| EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid)); |
| // Need to take a copy of |scid| has we're about to call |Erase|. |
| const string scid_str(scid.as_string()); |
| |
| scfg->Erase(kSCID); |
| scfg->MarkDirty(); |
| const QuicData& serialized(scfg->GetSerialized()); |
| |
| scoped_ptr<crypto::SecureHash> hash( |
| crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
| hash->Update(serialized.data(), serialized.length()); |
| uint8 digest[16]; |
| hash->Finish(digest, sizeof(digest)); |
| |
| ASSERT_EQ(scid.size(), sizeof(digest)); |
| EXPECT_EQ(0, memcmp(digest, scid_str.data(), sizeof(digest))); |
| } |
| |
| class CryptoServerTestNoConfig : public CryptoServerTest { |
| public: |
| virtual void SetUp() { |
| // Deliberately don't add a config so that we can test this situation. |
| } |
| }; |
| |
| TEST_P(CryptoServerTestNoConfig, DontCrash) { |
| ShouldFailMentioning("No config", InchoateClientHello( |
| "CHLO", |
| "VER\0", client_version_.data(), |
| NULL)); |
| |
| const HandshakeFailureReason kRejectReasons[] = { |
| CLIENT_NONCE_UNKNOWN_FAILURE |
| }; |
| CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); |
| } |
| |
| class AsyncStrikeServerVerificationTest : public CryptoServerTest { |
| protected: |
| AsyncStrikeServerVerificationTest() { |
| } |
| |
| virtual void SetUp() { |
| const string kOrbit = "12345678"; |
| config_options_.orbit = kOrbit; |
| strike_register_client_ = new DelayedVerifyStrikeRegisterClient( |
| 10000, // strike_register_max_entries |
| static_cast<uint32>(clock_.WallNow().ToUNIXSeconds()), |
| 60, // strike_register_window_secs |
| reinterpret_cast<const uint8 *>(kOrbit.data()), |
| StrikeRegister::NO_STARTUP_PERIOD_NEEDED); |
| config_.SetStrikeRegisterClient(strike_register_client_); |
| CryptoServerTest::SetUp(); |
| strike_register_client_->StartDelayingVerification(); |
| } |
| |
| DelayedVerifyStrikeRegisterClient* strike_register_client_; |
| }; |
| |
| TEST_P(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { |
| // This tests async validation with a strike register works. |
| CryptoHandshakeMessage msg = CryptoTestUtils::Message( |
| "CHLO", |
| "AEAD", "AESG", |
| "KEXS", "C255", |
| "SCID", scid_hex_.c_str(), |
| "#004b5453", srct_hex_.c_str(), |
| "PUBS", pub_hex_.c_str(), |
| "NONC", nonce_hex_.c_str(), |
| "VER\0", client_version_.data(), |
| "$padding", static_cast<int>(kClientHelloMinimumSize), |
| NULL); |
| |
| // Clear the message tag. |
| out_.set_tag(0); |
| |
| bool called = false; |
| RunValidate(msg, new ValidateCallback(this, true, "", &called)); |
| // The verification request was queued. |
| ASSERT_FALSE(called); |
| EXPECT_EQ(0u, out_.tag()); |
| EXPECT_EQ(1, strike_register_client_->PendingVerifications()); |
| |
| // Continue processing the verification request. |
| strike_register_client_->RunPendingVerifications(); |
| ASSERT_TRUE(called); |
| EXPECT_EQ(0, strike_register_client_->PendingVerifications()); |
| // The message should be accepted now. |
| EXPECT_EQ(kSHLO, out_.tag()); |
| |
| // Rejected if replayed. |
| RunValidate(msg, new ValidateCallback(this, true, "", &called)); |
| // The verification request was queued. |
| ASSERT_FALSE(called); |
| EXPECT_EQ(1, strike_register_client_->PendingVerifications()); |
| |
| strike_register_client_->RunPendingVerifications(); |
| ASSERT_TRUE(called); |
| EXPECT_EQ(0, strike_register_client_->PendingVerifications()); |
| // The message should be rejected now. |
| EXPECT_EQ(kREJ, out_.tag()); |
| } |
| |
| } // namespace test |
| } // namespace net |