summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-06 18:11:12 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-06 18:11:12 +0000
commit4f40354683153162d6cb310aba339a7696f84e0f (patch)
treec89095d726ba576f0a29d1d72b2d08a341de151f
parent6909587866caaf1fc1ddb1ded8fcc69687ecefd1 (diff)
downloadchromium_src-4f40354683153162d6cb310aba339a7696f84e0f.zip
chromium_src-4f40354683153162d6cb310aba339a7696f84e0f.tar.gz
chromium_src-4f40354683153162d6cb310aba339a7696f84e0f.tar.bz2
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
-rw-r--r--net/net.gyp3
-rw-r--r--net/socket/socket_test_util.cc4
-rw-r--r--net/socket/socket_test_util.h3
-rw-r--r--net/spdy/spdy_credential_builder.cc84
-rw-r--r--net/spdy/spdy_credential_builder.h38
-rw-r--r--net/spdy/spdy_credential_builder_unittest.cc133
-rw-r--r--net/spdy/spdy_http_stream_spdy3_unittest.cc26
-rw-r--r--net/spdy/spdy_session.cc46
8 files changed, 283 insertions, 54 deletions
diff --git a/net/net.gyp b/net/net.gyp
index fc277b8..b0e45886 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -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',
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 5b6d05f..41ccd08 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.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;
}
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 6cbd4d5..e0ce38e 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -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);
diff --git a/net/spdy/spdy_credential_builder.cc b/net/spdy/spdy_credential_builder.cc
new file mode 100644
index 0000000..af04eff
--- /dev/null
+++ b/net/spdy/spdy_credential_builder.cc
@@ -0,0 +1,84 @@
+// 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
diff --git a/net/spdy/spdy_credential_builder.h b/net/spdy/spdy_credential_builder.h
new file mode 100644
index 0000000..c8893c1
--- /dev/null
+++ b/net/spdy/spdy_credential_builder.h
@@ -0,0 +1,38 @@
+// 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_
diff --git a/net/spdy/spdy_credential_builder_unittest.cc b/net/spdy/spdy_credential_builder_unittest.cc
new file mode 100644
index 0000000..575ce88
--- /dev/null
+++ b/net/spdy/spdy_credential_builder_unittest.cc
@@ -0,0 +1,133 @@
+// 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
diff --git a/net/spdy/spdy_http_stream_spdy3_unittest.cc b/net/spdy/spdy_http_stream_spdy3_unittest.cc
index 04de978..91acad0 100644
--- a/net/spdy/spdy_http_stream_spdy3_unittest.cc
+++ b/net/spdy/spdy_http_stream_spdy3_unittest.cc
@@ -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
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index fc0a5cc..75497a5 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -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(