diff options
Diffstat (limited to 'chromium/net/quic/crypto/crypto_server_test.cc')
-rw-r--r-- | chromium/net/quic/crypto/crypto_server_test.cc | 255 |
1 files changed, 238 insertions, 17 deletions
diff --git a/chromium/net/quic/crypto/crypto_server_test.cc b/chromium/net/quic/crypto/crypto_server_test.cc index b2cdf820c34..4eec2a86a28 100644 --- a/chromium/net/quic/crypto/crypto_server_test.cc +++ b/chromium/net/quic/crypto/crypto_server_test.cc @@ -3,11 +3,15 @@ // found in the LICENSE file. #include "base/strings/string_number_conversions.h" -#include "net/quic/crypto/crypto_server_config.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_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 "testing/gtest/include/gtest/gtest.h" using base::StringPiece; @@ -24,12 +28,13 @@ class CryptoServerTest : public ::testing::Test { addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? ip_ : IPAddressNumber(), 1) { config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); + supported_versions_ = QuicSupportedVersions(); } virtual void SetUp() { scoped_ptr<CryptoHandshakeMessage> msg( config_.AddDefaultConfig(rand_, &clock_, - QuicCryptoServerConfig::ConfigOptions())); + config_options_)); StringPiece orbit; CHECK(msg->GetStringPiece(kORBT, &orbit)); @@ -69,29 +74,85 @@ class CryptoServerTest : public ::testing::Test { 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 { + 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 ShouldSucceed(const CryptoHandshakeMessage& message) { - string error_details; - QuicErrorCode error = config_.ProcessClientHello( - message, 1 /* GUID */, addr_, &clock_, - rand_, ¶ms_, &out_, &error_details); + bool called = false; + ShouldSucceed(message, &called); + EXPECT_TRUE(called); + } - ASSERT_EQ(error, QUIC_NO_ERROR) - << "Message failed with error " << error_details << ": " - << message.DebugString(); + void ShouldSucceed(const CryptoHandshakeMessage& message, + bool* called) { + config_.ValidateClientHello( + message, addr_, &clock_, + new ValidateCallback(this, true, "", called)); } void ShouldFailMentioning(const char* error_substr, const CryptoHandshakeMessage& message) { - string error_details; - QuicErrorCode error = config_.ProcessClientHello( - message, 1 /* GUID */, addr_, &clock_, - rand_, ¶ms_, &out_, &error_details); + bool called = false; + ShouldFailMentioning(error_substr, message, &called); + EXPECT_TRUE(called); + } - ASSERT_NE(error, QUIC_NO_ERROR) - << "Message didn't fail: " << message.DebugString(); + void ShouldFailMentioning(const char* error_substr, + const CryptoHandshakeMessage& message, + bool* called) { + config_.ValidateClientHello( + message, addr_, &clock_, + new ValidateCallback(this, false, error_substr, called)); + } - EXPECT_TRUE(error_details.find(error_substr) != string::npos) - << error_substr << " not in " << error_details; + 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 /* GUID */, addr_, + 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, ...) { @@ -118,7 +179,9 @@ class CryptoServerTest : public ::testing::Test { protected: QuicRandom* const rand_; MockClock clock_; + QuicVersionVector supported_versions_; QuicCryptoServerConfig config_; + QuicCryptoServerConfig::ConfigOptions config_options_; QuicCryptoNegotiatedParameters params_; CryptoHandshakeMessage out_; IPAddressNumber ip_; @@ -211,6 +274,22 @@ TEST_F(CryptoServerTest, BadClientNonce) { } } +TEST_F(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 client_version = QuicUtils::TagToString( + QuicVersionToQuicTag(supported_versions_.back())); + + ShouldFailMentioning("Downgrade", InchoateClientHello( + "CHLO", + "VER\0", client_version.data(), + NULL)); +} + TEST_F(CryptoServerTest, ReplayProtection) { // This tests that disabling replay protection works. CryptoHandshakeMessage msg = CryptoTestUtils::Message( @@ -237,6 +316,83 @@ TEST_F(CryptoServerTest, ReplayProtection) { ShouldSucceed(msg); // The message should accepted twice when replay protection is off. ASSERT_EQ(kSHLO, out_.tag()); + const QuicTag* versions; + size_t num_versions; + out_.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]); + } +} + +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_TRUE(0 == memcmp(digest, scid_str.data(), sizeof(digest))); } class CryptoServerTestNoConfig : public CryptoServerTest { @@ -252,5 +408,70 @@ TEST_F(CryptoServerTestNoConfig, DontCrash) { NULL)); } +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_F(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(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + + // Clear the message tag. + out_.set_tag(0); + + bool called = false; + ShouldSucceed(msg, &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. + ShouldSucceed(msg, &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 |