Commit 4f403546 authored by rch@chromium.org's avatar rch@chromium.org

Implement TLS Channel ID support for SPDY CREDENTIAL frames

BUG=139700


Review URL: https://chromiumcodereview.appspot.com/10807088

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150112 0039d316-1c4b-4281-b951-d872f2087c98
parent 69095878
......@@ -676,6 +676,8 @@
'spdy/buffered_spdy_framer.cc',
'spdy/buffered_spdy_framer.h',
'spdy/spdy_bitmasks.h',
'spdy/spdy_credential_builder.cc',
'spdy/spdy_credential_builder.h',
'spdy/spdy_credential_state.cc',
'spdy/spdy_credential_state.h',
'spdy/spdy_frame_builder.cc',
......@@ -1300,6 +1302,7 @@
'socket_stream/socket_stream_unittest.cc',
'spdy/buffered_spdy_framer_spdy3_unittest.cc',
'spdy/buffered_spdy_framer_spdy2_unittest.cc',
'spdy/spdy_credential_builder_unittest.cc',
'spdy/spdy_credential_state_unittest.cc',
'spdy/spdy_frame_reader_test.cc',
'spdy/spdy_framer_test.cc',
......
......@@ -686,6 +686,8 @@ SSLClientSocket* MockClientSocketFactory::CreateSSLClientSocket(
void MockClientSocketFactory::ClearSSLSessionCache() {
}
const char MockClientSocket::kTlsUnique[] = "MOCK_TLSUNIQ";
MockClientSocket::MockClientSocket(net::NetLog* net_log)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
connected_(false),
......@@ -746,7 +748,7 @@ int MockClientSocket::ExportKeyingMaterial(const base::StringPiece& label,
}
int MockClientSocket::GetTLSUniqueChannelBinding(std::string* out) {
out->assign("MOCK_TLSUNIQ");
out->assign(MockClientSocket::kTlsUnique);
return OK;
}
......
......@@ -589,6 +589,9 @@ class MockClientSocketFactory : public ClientSocketFactory {
class MockClientSocket : public SSLClientSocket {
public:
// Value returned by GetTLSUniqueChannelBinding().
static const char kTlsUnique[];
// TODO(ajwong): Why do we need net::NetLog?
explicit MockClientSocket(net::NetLog* net_log);
......
// Copyright (c) 2012 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/spdy/spdy_credential_builder.h"
#include "base/logging.h"
#include "base/string_piece.h"
#include "crypto/ec_private_key.h"
#include "crypto/ec_signature_creator.h"
#include "crypto/signature_creator.h"
#include "net/base/asn1_util.h"
#include "net/base/server_bound_cert_service.h"
#include "net/base/net_errors.h"
#include "net/socket/ssl_client_socket.h"
#include "net/spdy/spdy_framer.h"
namespace net {
namespace {
std::vector<uint8> ToVector(base::StringPiece piece) {
return std::vector<uint8>(piece.data(), piece.data() + piece.length());
}
} // namespace
// static
int SpdyCredentialBuilder::Build(std::string tls_unique,
SSLClientCertType type,
const std::string& key,
const std::string& cert,
size_t slot,
SpdyCredential* credential) {
if (type != CLIENT_CERT_ECDSA_SIGN)
return ERR_BAD_SSL_CLIENT_AUTH_CERT;
std::string secret = SpdyCredentialBuilder::GetCredentialSecret(tls_unique);
// Extract the SubjectPublicKeyInfo from the certificate.
base::StringPiece public_key_info;
if(!asn1::ExtractSPKIFromDERCert(cert, &public_key_info))
return ERR_BAD_SSL_CLIENT_AUTH_CERT;
// Next, extract the SubjectPublicKey data, which will actually
// be stored in the cert field of the credential frame.
base::StringPiece public_key;
if (!asn1::ExtractSubjectPublicKeyFromSPKI(public_key_info, &public_key))
return ERR_BAD_SSL_CLIENT_AUTH_CERT;
// Drop one byte of padding bits count from the BIT STRING
// (this will always be zero). Drop one byte of X9.62 format specification
// (this will always be 4 to indicated an uncompressed point).
DCHECK_GT(public_key.length(), 2u);
DCHECK_EQ(0, static_cast<int>(public_key[0]));
DCHECK_EQ(4, static_cast<int>(public_key[1]));
public_key = public_key.substr(2, public_key.length());
// Convert the strings into a vector<unit8>
std::vector<uint8> proof_vector;
scoped_ptr<crypto::ECPrivateKey> private_key(
crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
ServerBoundCertService::kEPKIPassword,
ToVector(key), ToVector(public_key_info)));
scoped_ptr<crypto::ECSignatureCreator> creator(
crypto::ECSignatureCreator::Create(private_key.get()));
creator->Sign(reinterpret_cast<const unsigned char *>(secret.data()),
secret.length(), &proof_vector);
credential->slot = slot;
credential->certs.push_back(public_key.as_string());
credential->proof.assign(proof_vector.begin(), proof_vector.end());
return OK;
}
// static
std::string SpdyCredentialBuilder::GetCredentialSecret(std::string tls_unique) {
const char prefix[] = "SPDY CREDENTIAL ChannelID\0client -> server";
std::string secret(prefix, arraysize(prefix));
secret.append(tls_unique);
return secret;
}
} // namespace net
// Copyright (c) 2012 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.
#ifndef NET_SPDY_SPDY_CREDENTIAL_BUILDER_H_
#define NET_SPDY_SPDY_CREDENTIAL_BUILDER_H_
#include <string>
#include "net/base/net_export.h"
#include "net/base/ssl_client_cert_type.h"
namespace net {
class SSLClientSocket;
struct SpdyCredential;
// This class provides facilities for building the various fields of
// SPDY CREDENTIAL frames.
class NET_EXPORT_PRIVATE SpdyCredentialBuilder {
public:
static int Build(std::string tls_unique,
SSLClientCertType type,
const std::string& key,
const std::string& cert,
size_t slot,
SpdyCredential* credential);
private:
friend class SpdyCredentialBuilderTest;
// Returns the secret data to be signed as part of a credential frame.
static std::string GetCredentialSecret(std::string tls_unique);
};
} // namespace net
#endif // NET_SPDY_SPDY_CREDENTIAL_BUILDER_H_
// Copyright (c) 2012 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/spdy/spdy_credential_builder.h"
#include "base/threading/sequenced_worker_pool.h"
#include "crypto/ec_signature_creator.h"
#include "crypto/ec_private_key.h"
#include "net/base/asn1_util.h"
#include "net/base/default_server_bound_cert_store.h"
#include "net/base/server_bound_cert_service.h"
#include "net/spdy/spdy_test_util_spdy3.h"
#include "testing/platform_test.h"
using namespace net::test_spdy3;
namespace net {
namespace {
const static size_t kSlot = 2;
const static char kSecretPrefix[] =
"SPDY CREDENTIAL ChannelID\0client -> server";
void CreateCertAndKey(std::string* cert, std::string* key) {
// TODO(rch): Share this code with ServerBoundCertServiceTest.
scoped_refptr<base::SequencedWorkerPool> sequenced_worker_pool =
new base::SequencedWorkerPool(1, "CreateCertAndKey");
scoped_ptr<ServerBoundCertService> server_bound_cert_service(
new ServerBoundCertService(new DefaultServerBoundCertStore(NULL),
sequenced_worker_pool));
TestCompletionCallback callback;
std::vector<uint8> requested_cert_types;
requested_cert_types.push_back(CLIENT_CERT_ECDSA_SIGN);
SSLClientCertType cert_type;
ServerBoundCertService::RequestHandle request_handle;
int rv = server_bound_cert_service->GetDomainBoundCert(
"https://www.google.com", requested_cert_types, &cert_type, key, cert,
callback.callback(), &request_handle);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, cert_type);
sequenced_worker_pool->Shutdown();
}
} // namespace
class SpdyCredentialBuilderTest : public testing::Test {
public:
SpdyCredentialBuilderTest() {
CreateCertAndKey(&cert_, &key_);
}
protected:
int BuildWithType(SSLClientCertType type) {
return SpdyCredentialBuilder::Build(
MockClientSocket::kTlsUnique, type, key_, cert_, kSlot, &credential_);
}
int Build() {
return BuildWithType(CLIENT_CERT_ECDSA_SIGN);
}
std::string GetCredentialSecret() {
return SpdyCredentialBuilder::GetCredentialSecret(
MockClientSocket::kTlsUnique);
}
SpdyTestStateHelper helper_; // Provides deterministic EC signatures.
std::string cert_;
std::string key_;
SpdyCredential credential_;
};
TEST_F(SpdyCredentialBuilderTest, GetCredentialSecret) {
std::string secret_str(kSecretPrefix, arraysize(kSecretPrefix));
secret_str.append(MockClientSocket::kTlsUnique);
EXPECT_EQ(secret_str, GetCredentialSecret());
}
TEST_F(SpdyCredentialBuilderTest, SucceedsWithECDSACert) {
EXPECT_EQ(OK, BuildWithType(CLIENT_CERT_ECDSA_SIGN));
}
TEST_F(SpdyCredentialBuilderTest, FailsWithRSACert) {
EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
BuildWithType(CLIENT_CERT_RSA_SIGN));
}
TEST_F(SpdyCredentialBuilderTest, SetsSlotCorrectly) {
ASSERT_EQ(OK, Build());
EXPECT_EQ(kSlot, credential_.slot);
}
TEST_F(SpdyCredentialBuilderTest, SetsCertCorrectly) {
ASSERT_EQ(OK, Build());
base::StringPiece spki;
ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(cert_, &spki));
base::StringPiece spk;
ASSERT_TRUE(asn1::ExtractSubjectPublicKeyFromSPKI(spki, &spk));
EXPECT_EQ(1u, credential_.certs.size());
EXPECT_EQ(0, (int)spk[0]);
EXPECT_EQ(4, (int)spk[1]);
EXPECT_EQ(spk.substr(2, spk.length()).as_string(), credential_.certs[0]);
}
TEST_F(SpdyCredentialBuilderTest, SetsProofCorrectly) {
ASSERT_EQ(OK, Build());
base::StringPiece spki;
ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(cert_, &spki));
std::vector<uint8> spki_data(spki.data(),
spki.data() + spki.size());
std::vector<uint8> key_data(key_.data(),
key_.data() + key_.length());
std::vector<uint8> proof_data;
scoped_ptr<crypto::ECPrivateKey> private_key(
crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
ServerBoundCertService::kEPKIPassword, key_data, spki_data));
scoped_ptr<crypto::ECSignatureCreator> creator(
crypto::ECSignatureCreator::Create(private_key.get()));
std::string secret = GetCredentialSecret();
creator->Sign(reinterpret_cast<const unsigned char *>(secret.data()),
secret.length(), &proof_data);
std::string proof(proof_data.begin(), proof_data.end());
EXPECT_EQ(proof, credential_.proof);
}
} // namespace net
......@@ -13,6 +13,7 @@
#include "net/base/default_server_bound_cert_store.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/spdy/spdy_credential_builder.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_test_util_spdy3.h"
......@@ -532,25 +533,12 @@ void GetECServerBoundCertAndProof(
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, cert_type);
unsigned char secret[32];
memset(secret, 'A', arraysize(secret));
// Convert the key string into a vector<unit8>
std::vector<uint8> key_data(key.begin(), key.end());
base::StringPiece spki_piece;
ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(*cert, &spki_piece));
std::vector<uint8> spki(spki_piece.data(),
spki_piece.data() + spki_piece.size());
std::vector<uint8> proof_data;
scoped_ptr<crypto::ECPrivateKey> private_key(
crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
ServerBoundCertService::kEPKIPassword, key_data, spki));
scoped_ptr<crypto::ECSignatureCreator> creator(
crypto::ECSignatureCreator::Create(private_key.get()));
creator->Sign(secret, arraysize(secret), &proof_data);
proof->assign(proof_data.begin(), proof_data.end());
SpdyCredential credential;
SpdyCredentialBuilder::Build(MockClientSocket::kTlsUnique, cert_type, key,
*cert, 2, &credential);
cert->assign(credential.certs[0]);
proof->assign(credential.proof);
}
} // namespace
......
......@@ -31,6 +31,7 @@
#include "net/base/server_bound_cert_service.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties.h"
#include "net/spdy/spdy_credential_builder.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
......@@ -626,42 +627,19 @@ SpdyCredentialControlFrame* SpdySession::CreateCredentialFrame(
const std::string& cert,
RequestPriority priority) {
DCHECK(is_secure_);
unsigned char secret[32]; // 32 bytes from the spec
GetSSLClientSocket()->ExportKeyingMaterial("SPDY certificate proof",
true, origin,
secret, arraysize(secret));
// Convert the key string into a vector<unit8>
std::vector<uint8> key_data;
for (size_t i = 0; i < key.length(); i++) {
key_data.push_back(key[i]);
}
std::vector<uint8> proof;
switch (type) {
case CLIENT_CERT_ECDSA_SIGN: {
base::StringPiece spki_piece;
asn1::ExtractSPKIFromDERCert(cert, &spki_piece);
std::vector<uint8> spki(spki_piece.data(),
spki_piece.data() + spki_piece.size());
scoped_ptr<crypto::ECPrivateKey> private_key(
crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
ServerBoundCertService::kEPKIPassword, key_data, spki));
scoped_ptr<crypto::ECSignatureCreator> creator(
crypto::ECSignatureCreator::Create(private_key.get()));
creator->Sign(secret, arraysize(secret), &proof);
break;
}
default:
NOTREACHED();
}
SSLClientSocket* ssl_socket = GetSSLClientSocket();
DCHECK(ssl_socket);
DCHECK(ssl_socket->WasChannelIDSent());
SpdyCredential credential;
GURL origin_url(origin);
credential.slot =
credential_state_.SetHasCredential(origin_url);
credential.certs.push_back(cert);
credential.proof.assign(proof.begin(), proof.end());
std::string tls_unique;
ssl_socket->GetTLSUniqueChannelBinding(&tls_unique);
size_t slot = credential_state_.SetHasCredential(GURL(origin));
int rv = SpdyCredentialBuilder::Build(tls_unique, type, key, cert, slot,
&credential);
DCHECK_EQ(OK, rv);
if (rv != OK)
return NULL;
DCHECK(buffered_spdy_framer_.get());
scoped_ptr<SpdyCredentialControlFrame> credential_frame(
......
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