Commit 05bfc260 authored by wtc@chromium.org's avatar wtc@chromium.org

Change the prototype of ChannelIDSource::GetChannelIDKey() to allow an

asynchronous implementation.

Current ChannelIDSource implementations work in synchronous mode. So
QuicCryptoClientStream still calls ChannelIDSource::GetChannelIDKey() as
a synchronous function. In a future CL, QuicCryptoClientStream will properly
call ChannelIDSource::GetChannelIDKey() as an asynchronous function.

Note that ChannelIDSourceForTesting() returns a ChannelIDSource that works
in synchronous mode.

Merge internal CL: 68399207

R=rch@chromium.org,rtenneti@chromium.org
BUG=none

Review URL: https://codereview.chromium.org/320663002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275634 0039d316-1c4b-4281-b951-d872f2087c98
parent eceaabf4
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
#include "net/quic/quic_types.h"
namespace net { namespace net {
...@@ -22,10 +23,23 @@ class NET_EXPORT_PRIVATE ChannelIDKey { ...@@ -22,10 +23,23 @@ class NET_EXPORT_PRIVATE ChannelIDKey {
// Sign signs |signed_data| using the ChannelID private key and puts the // Sign signs |signed_data| using the ChannelID private key and puts the
// signature into |out_signature|. It returns true on success. // signature into |out_signature|. It returns true on success.
virtual bool Sign(base::StringPiece signed_data, virtual bool Sign(base::StringPiece signed_data,
std::string* out_signature) = 0; std::string* out_signature) const = 0;
// SerializeKey returns the serialized ChannelID public key. // SerializeKey returns the serialized ChannelID public key.
virtual std::string SerializeKey() = 0; virtual std::string SerializeKey() const = 0;
};
// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource
// to call back after an asynchronous GetChannelIDKey operation.
class ChannelIDSourceCallback {
public:
virtual ~ChannelIDSourceCallback() {}
// Run is called on the original thread to mark the completion of an
// asynchonous GetChannelIDKey operation. If |*channel_id_key| is not NULL
// then the channel ID lookup is successful. |Run| may take ownership of
// |*channel_id_key| by calling |release| on it.
virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) = 0;
}; };
// ChannelIDSource is an abstract interface by which a QUIC client can obtain // ChannelIDSource is an abstract interface by which a QUIC client can obtain
...@@ -35,9 +49,17 @@ class NET_EXPORT_PRIVATE ChannelIDSource { ...@@ -35,9 +49,17 @@ class NET_EXPORT_PRIVATE ChannelIDSource {
virtual ~ChannelIDSource() {} virtual ~ChannelIDSource() {}
// GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it
// returns true and stores the ChannelIDKey in |*channel_id|. // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|,
virtual bool GetChannelIDKey(const std::string& hostname, // which the caller takes ownership of. On failure, it returns QUIC_FAILURE.
scoped_ptr<ChannelIDKey>* channel_id_key) = 0; //
// This function may also return QUIC_PENDING, in which case the
// ChannelIDSource will call back, on the original thread, via |callback|
// when complete. In this case, the ChannelIDSource will take ownership of
// |callback|.
virtual QuicAsyncStatus GetChannelIDKey(
const std::string& hostname,
scoped_ptr<ChannelIDKey>* channel_id_key,
ChannelIDSourceCallback* callback) = 0;
}; };
// ChannelIDVerifier verifies ChannelID signatures. // ChannelIDVerifier verifies ChannelID signatures.
......
...@@ -226,7 +226,9 @@ TEST(ChannelIDTest, SignAndVerify) { ...@@ -226,7 +226,9 @@ TEST(ChannelIDTest, SignAndVerify) {
const string signed_data = "signed data"; const string signed_data = "signed data";
const string hostname = "foo.example.com"; const string hostname = "foo.example.com";
scoped_ptr<ChannelIDKey> channel_id_key; scoped_ptr<ChannelIDKey> channel_id_key;
ASSERT_TRUE(source->GetChannelIDKey(hostname, &channel_id_key)); QuicAsyncStatus status =
source->GetChannelIDKey(hostname, &channel_id_key, NULL);
ASSERT_EQ(QUIC_SUCCESS, status);
string signature; string signature;
ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature)); ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature));
......
...@@ -37,7 +37,7 @@ class NET_EXPORT_PRIVATE ProofVerifierCallback { ...@@ -37,7 +37,7 @@ class NET_EXPORT_PRIVATE ProofVerifierCallback {
// Run is called on the original thread to mark the completion of an // Run is called on the original thread to mark the completion of an
// asynchonous verification. If |ok| is true then the certificate is valid // asynchonous verification. If |ok| is true then the certificate is valid
// and |*error_details| is unused. Otherwise, |*error_details| contains a // and |error_details| is unused. Otherwise, |error_details| contains a
// description of the error. |details| contains implementation-specific // description of the error. |details| contains implementation-specific
// details of the verification. |Run| may take ownership of |details| by // details of the verification. |Run| may take ownership of |details| by
// calling |release| on it. // calling |release| on it.
...@@ -55,7 +55,7 @@ class NET_EXPORT_PRIVATE ProofVerifier { ...@@ -55,7 +55,7 @@ class NET_EXPORT_PRIVATE ProofVerifier {
// VerifyProof checks that |signature| is a valid signature of // VerifyProof checks that |signature| is a valid signature of
// |server_config| by the public key in the leaf certificate of |certs|, and // |server_config| by the public key in the leaf certificate of |certs|, and
// that |certs| is a valid chain for |hostname|. On success, it returns // that |certs| is a valid chain for |hostname|. On success, it returns
// QUIC_SUCCESS. On failure, it returns QUIC_ERROR and sets |*error_details| // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
// to a description of the problem. In either case it may set |*details|, // to a description of the problem. In either case it may set |*details|,
// which the caller takes ownership of. // which the caller takes ownership of.
// //
......
...@@ -40,8 +40,8 @@ class ProofVerifierChromium::Job { ...@@ -40,8 +40,8 @@ class ProofVerifierChromium::Job {
CertVerifier* cert_verifier, CertVerifier* cert_verifier,
const BoundNetLog& net_log); const BoundNetLog& net_log);
// Starts the proof verification. If |PENDING| is returned, then |callback| // Starts the proof verification. If |QUIC_PENDING| is returned, then
// will be invoked asynchronously when the verification completes. // |callback| will be invoked asynchronously when the verification completes.
QuicAsyncStatus VerifyProof(const std::string& hostname, QuicAsyncStatus VerifyProof(const std::string& hostname,
const std::string& server_config, const std::string& server_config,
const std::vector<std::string>& certs, const std::vector<std::string>& certs,
......
...@@ -350,6 +350,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( ...@@ -350,6 +350,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
const CachedState* cached, const CachedState* cached,
QuicWallTime now, QuicWallTime now,
QuicRandom* rand, QuicRandom* rand,
const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params, QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out, CryptoHandshakeMessage* out,
string* error_details) const { string* error_details) const {
...@@ -447,22 +448,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( ...@@ -447,22 +448,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
} }
out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
bool do_channel_id = false; if (channel_id_key) {
if (channel_id_source_.get()) {
const QuicTag* their_proof_demands;
size_t num_their_proof_demands;
if (scfg->GetTaglist(kPDMD, &their_proof_demands,
&num_their_proof_demands) == QUIC_NO_ERROR) {
for (size_t i = 0; i < num_their_proof_demands; i++) {
if (their_proof_demands[i] == kCHID) {
do_channel_id = true;
break;
}
}
}
}
if (do_channel_id) {
// In order to calculate the encryption key for the CETV block we need to // In order to calculate the encryption key for the CETV block we need to
// serialise the client hello as it currently is (i.e. without the CETV // serialise the client hello as it currently is (i.e. without the CETV
// block). For this, the client hello is serialized without padding. // block). For this, the client hello is serialized without padding.
...@@ -482,12 +468,6 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( ...@@ -482,12 +468,6 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
client_hello_serialized.length()); client_hello_serialized.length());
hkdf_input.append(cached->server_config()); hkdf_input.append(cached->server_config());
scoped_ptr<ChannelIDKey> channel_id_key;
if (!channel_id_source_->GetChannelIDKey(server_id.host(),
&channel_id_key)) {
*error_details = "Channel ID lookup failed";
return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
}
string key = channel_id_key->SerializeKey(); string key = channel_id_key->SerializeKey();
string signature; string signature;
if (!channel_id_key->Sign(hkdf_input, &signature)) { if (!channel_id_key->Sign(hkdf_input, &signature)) {
...@@ -523,6 +503,10 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( ...@@ -523,6 +503,10 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
out->set_minimum_size(orig_min_size); out->set_minimum_size(orig_min_size);
} }
// Derive the symmetric keys and set up the encrypters and decrypters.
// Set the following members of out_params:
// out_params->hkdf_input_suffix
// out_params->initial_crypters
out_params->hkdf_input_suffix.clear(); out_params->hkdf_input_suffix.clear();
out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id), out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id),
sizeof(connection_id)); sizeof(connection_id));
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
namespace net { namespace net {
class ChannelIDKey;
class ChannelIDSource; class ChannelIDSource;
class CryptoHandshakeMessage; class CryptoHandshakeMessage;
class ProofVerifier; class ProofVerifier;
...@@ -168,6 +169,10 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { ...@@ -168,6 +169,10 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// accept. |preferred_version| is the version of the QUIC protocol that this // accept. |preferred_version| is the version of the QUIC protocol that this
// client chose to use initially. This allows the server to detect downgrade // client chose to use initially. This allows the server to detect downgrade
// attacks. // attacks.
//
// If |channel_id_key| is not null, it is used to sign a secret value derived
// from the client and server's keys, and the Channel ID public key and the
// signature are placed in the CETV value of the CHLO.
QuicErrorCode FillClientHello(const QuicServerId& server_id, QuicErrorCode FillClientHello(const QuicServerId& server_id,
QuicConnectionId connection_id, QuicConnectionId connection_id,
const QuicVersion preferred_version, const QuicVersion preferred_version,
...@@ -175,6 +180,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { ...@@ -175,6 +180,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
const CachedState* cached, const CachedState* cached,
QuicWallTime now, QuicWallTime now,
QuicRandom* rand, QuicRandom* rand,
const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params, QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out, CryptoHandshakeMessage* out,
std::string* error_details) const; std::string* error_details) const;
......
...@@ -122,6 +122,7 @@ TEST(QuicCryptoClientConfigTest, FillClientHello) { ...@@ -122,6 +122,7 @@ TEST(QuicCryptoClientConfigTest, FillClientHello) {
&state, &state,
QuicWallTime::Zero(), QuicWallTime::Zero(),
&rand, &rand,
NULL, // channel_id_key
&params, &params,
&chlo, &chlo,
&error_details); &error_details);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/null_encrypter.h"
...@@ -151,6 +152,35 @@ void QuicCryptoClientStream::DoHandshakeLoop( ...@@ -151,6 +152,35 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return; return;
} }
session()->config()->ToHandshakeMessage(&out); session()->config()->ToHandshakeMessage(&out);
scoped_ptr<ChannelIDKey> channel_id_key;
bool do_channel_id = false;
if (crypto_config_->channel_id_source()) {
const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
DCHECK(scfg);
const QuicTag* their_proof_demands;
size_t num_their_proof_demands;
if (scfg->GetTaglist(kPDMD, &their_proof_demands,
&num_their_proof_demands) == QUIC_NO_ERROR) {
for (size_t i = 0; i < num_their_proof_demands; i++) {
if (their_proof_demands[i] == kCHID) {
do_channel_id = true;
break;
}
}
}
}
if (do_channel_id) {
QuicAsyncStatus status =
crypto_config_->channel_id_source()->GetChannelIDKey(
server_id_.host(), &channel_id_key, NULL);
if (status != QUIC_SUCCESS) {
CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
"Channel ID lookup failed");
return;
}
}
error = crypto_config_->FillClientHello( error = crypto_config_->FillClientHello(
server_id_, server_id_,
session()->connection()->connection_id(), session()->connection()->connection_id(),
...@@ -159,6 +189,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( ...@@ -159,6 +189,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
cached, cached,
session()->connection()->clock()->WallNow(), session()->connection()->clock()->WallNow(),
session()->connection()->random_generator(), session()->connection()->random_generator(),
channel_id_key.get(),
&crypto_negotiated_params_, &crypto_negotiated_params_,
&out, &out,
&error_details); &error_details);
......
...@@ -192,8 +192,11 @@ int CryptoTestUtils::HandshakeWithFakeClient( ...@@ -192,8 +192,11 @@ int CryptoTestUtils::HandshakeWithFakeClient(
if (options.channel_id_enabled) { if (options.channel_id_enabled) {
scoped_ptr<ChannelIDKey> channel_id_key; scoped_ptr<ChannelIDKey> channel_id_key;
EXPECT_TRUE(crypto_config.channel_id_source()->GetChannelIDKey( QuicAsyncStatus status =
kServerHostname, &channel_id_key)); crypto_config.channel_id_source()->GetChannelIDKey(kServerHostname,
&channel_id_key,
NULL);
EXPECT_EQ(QUIC_SUCCESS, status);
EXPECT_EQ(channel_id_key->SerializeKey(), EXPECT_EQ(channel_id_key->SerializeKey(),
server->crypto_negotiated_params().channel_id); server->crypto_negotiated_params().channel_id);
} }
......
...@@ -133,7 +133,9 @@ class CryptoTestUtils { ...@@ -133,7 +133,9 @@ class CryptoTestUtils {
va_list ap); va_list ap);
// ChannelIDSourceForTesting returns a ChannelIDSource that generates keys // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
// deterministically based on the hostname given in the GetChannelID call. // deterministically based on the hostname given in the GetChannelIDKey call.
// This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
// method never returns QUIC_PENDING.
static ChannelIDSource* ChannelIDSourceForTesting(); static ChannelIDSource* ChannelIDSourceForTesting();
private: private:
......
...@@ -31,7 +31,7 @@ class TestChannelIDKey : public ChannelIDKey { ...@@ -31,7 +31,7 @@ class TestChannelIDKey : public ChannelIDKey {
// ChannelIDKey implementation. // ChannelIDKey implementation.
virtual bool Sign(StringPiece signed_data, virtual bool Sign(StringPiece signed_data,
string* out_signature) OVERRIDE { string* out_signature) const OVERRIDE {
unsigned char hash_buf[SHA256_LENGTH]; unsigned char hash_buf[SHA256_LENGTH];
SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) }; SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) };
...@@ -71,13 +71,13 @@ class TestChannelIDKey : public ChannelIDKey { ...@@ -71,13 +71,13 @@ class TestChannelIDKey : public ChannelIDKey {
return true; return true;
} }
virtual string SerializeKey() OVERRIDE { virtual string SerializeKey() const OVERRIDE {
static const unsigned int kExpectedKeyLength = 65;
const SECKEYPublicKey* public_key = ecdsa_keypair_->public_key(); const SECKEYPublicKey* public_key = ecdsa_keypair_->public_key();
// public_key->u.ec.publicValue is an ANSI X9.62 public key which, for // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for
// a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
// elements as 32-byte, big-endian numbers. // elements as 32-byte, big-endian numbers.
static const unsigned int kExpectedKeyLength = 65;
const unsigned char* const data = public_key->u.ec.publicValue.data; const unsigned char* const data = public_key->u.ec.publicValue.data;
const unsigned int len = public_key->u.ec.publicValue.len; const unsigned int len = public_key->u.ec.publicValue.len;
...@@ -101,11 +101,12 @@ class TestChannelIDSource : public ChannelIDSource { ...@@ -101,11 +101,12 @@ class TestChannelIDSource : public ChannelIDSource {
// ChannelIDSource implementation. // ChannelIDSource implementation.
virtual bool GetChannelIDKey( virtual QuicAsyncStatus GetChannelIDKey(
const string& hostname, const string& hostname,
scoped_ptr<ChannelIDKey>* channel_id_key) OVERRIDE { scoped_ptr<ChannelIDKey>* channel_id_key,
ChannelIDSourceCallback* /*callback*/) OVERRIDE {
channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname))); channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
return true; return QUIC_SUCCESS;
} }
private: private:
...@@ -125,7 +126,6 @@ class TestChannelIDSource : public ChannelIDSource { ...@@ -125,7 +126,6 @@ class TestChannelIDSource : public ChannelIDSource {
return keypair; return keypair;
} }
HostnameToKeyMap hostname_to_key_; HostnameToKeyMap hostname_to_key_;
}; };
......
...@@ -38,7 +38,7 @@ class TestChannelIDKey : public ChannelIDKey { ...@@ -38,7 +38,7 @@ class TestChannelIDKey : public ChannelIDKey {
// ChannelIDKey implementation. // ChannelIDKey implementation.
virtual bool Sign(StringPiece signed_data, virtual bool Sign(StringPiece signed_data,
string* out_signature) OVERRIDE { string* out_signature) const OVERRIDE {
EVP_MD_CTX md_ctx; EVP_MD_CTX md_ctx;
EVP_MD_CTX_init(&md_ctx); EVP_MD_CTX_init(&md_ctx);
crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp> crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp>
...@@ -85,7 +85,7 @@ class TestChannelIDKey : public ChannelIDKey { ...@@ -85,7 +85,7 @@ class TestChannelIDKey : public ChannelIDKey {
return true; return true;
} }
virtual string SerializeKey() OVERRIDE { virtual string SerializeKey() const OVERRIDE {
// i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256 // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
// key, is 0x04 (meaning uncompressed) followed by the x and y field // key, is 0x04 (meaning uncompressed) followed by the x and y field
// elements as 32-byte, big-endian numbers. // elements as 32-byte, big-endian numbers.
...@@ -113,11 +113,12 @@ class TestChannelIDSource : public ChannelIDSource { ...@@ -113,11 +113,12 @@ class TestChannelIDSource : public ChannelIDSource {
// ChannelIDSource implementation. // ChannelIDSource implementation.
virtual bool GetChannelIDKey( virtual QuicAsyncStatus GetChannelIDKey(
const string& hostname, const string& hostname,
scoped_ptr<ChannelIDKey>* channel_id_key) OVERRIDE { scoped_ptr<ChannelIDKey>* channel_id_key,
ChannelIDSourceCallback* /*callback*/) OVERRIDE {
channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname))); channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
return true; return QUIC_SUCCESS;
} }
private: private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment