blob: d15f6b0cdb65f3d827b7895fdaa27b1ed81fd64c [file] [log] [blame]
// 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/crypto/quic_crypto_client_config.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/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;
}
};
} // 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,
&params, &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,
&params, &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, 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(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,
&params, &msg);
StringPiece scid;
EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid));
EXPECT_EQ("12345678", scid);
}
TEST(QuicCryptoClientConfigTest, InchoateChloSecureNoEcdsa) {
QuicCryptoClientConfig::CachedState state;
QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting());
config.DisableEcdsa();
QuicCryptoNegotiatedParameters params;
CryptoHandshakeMessage msg;
QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED);
MockRandom rand;
config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
&params, &msg);
QuicTag pdmd;
EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
EXPECT_EQ(kX59R, pdmd);
}
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
&params, &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 = QuicSupportedVersions();
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());
QuicServerId server_id("www.google.com", 443, PRIVACY_MODE_DISABLED);
QuicCryptoClientConfig::CachedState* state = config.LookupOrCreate(server_id);
// TODO(rch): Populate other fields of |state|.
vector<string> certs(1);
certs[0] = "Hello Cert";
state->SetProof(certs, "cert_sct", "chlo_hash", "signature");
state->set_source_address_token("TOKEN");
state->SetProofValid();
EXPECT_EQ(1u, state->generation_counter());
// Verify LookupOrCreate returns the same data.
QuicCryptoClientConfig::CachedState* other = config.LookupOrCreate(server_id);
EXPECT_EQ(state, other);
EXPECT_EQ(1u, other->generation_counter());
// Clear the cached states.
config.ClearCachedStates();
// Verify LookupOrCreate doesn't have any data.
QuicCryptoClientConfig::CachedState* cleared_cache =
config.LookupOrCreate(server_id);
EXPECT_EQ(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(2u, 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),
QuicSupportedVersions().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),
QuicSupportedVersions().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),
QuicSupportedVersions().front(), "",
&cached, &out_params, &error));
EXPECT_FALSE(cached.has_server_designated_connection_id());
EXPECT_EQ("Missing kRCID", error);
}
TEST(QuicCryptoClientConfigTest, ServerNonceinSHLO_BeforeQ027) {
// Test that in QUIC_VERSION_26 and lower, the the server does not need to
// include a nonce in the SHLO.
CryptoHandshakeMessage msg;
msg.set_tag(kSHLO);
// Choose the lowest version.
QuicVersionVector supported_versions;
QuicVersion version = QuicSupportedVersions().back();
supported_versions.push_back(version);
EXPECT_LE(version, QUIC_VERSION_26);
QuicTagVector versions;
versions.push_back(QuicVersionToQuicTag(version));
msg.SetVector(kVER, versions);
QuicCryptoClientConfig config(CryptoTestUtils::ProofVerifierForTesting());
QuicCryptoClientConfig::CachedState cached;
QuicCryptoNegotiatedParameters out_params;
string error_details;
config.ProcessServerHello(msg, 0, version, supported_versions, &cached,
&out_params, &error_details);
EXPECT_NE("server hello missing server nonce", error_details);
}
TEST(QuicCryptoClientConfigTest, ServerNonceinSHLO_AfterQ027) {
// Test that in QUIC_VERSION_27 and higher, the the server must include a
// nonce in the SHLO.
CryptoHandshakeMessage msg;
msg.set_tag(kSHLO);
// Choose the latest version.
QuicVersionVector supported_versions;
QuicVersion version = QuicSupportedVersions().front();
supported_versions.push_back(version);
EXPECT_LE(QUIC_VERSION_27, 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