summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorekasper@google.com <ekasper@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-28 13:43:26 +0000
committerekasper@google.com <ekasper@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-28 13:43:26 +0000
commit4e72ee50e8f859f1b7dc9a8904d41462c107277e (patch)
tree3f36a9f06c3f7b01407de9bea103f8c6442d159c /net
parent560e2f57c9f16b8c54a4fba8dc241a228dd6f049 (diff)
downloadchromium_src-4e72ee50e8f859f1b7dc9a8904d41462c107277e.zip
chromium_src-4e72ee50e8f859f1b7dc9a8904d41462c107277e.tar.gz
chromium_src-4e72ee50e8f859f1b7dc9a8904d41462c107277e.tar.bz2
Add support for fetching Certificate Transparency SCTs over a TLS extension
BUG=309578 Review URL: https://codereview.chromium.org/83333003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237775 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/socket/ssl_client_socket.cc12
-rw-r--r--net/socket/ssl_client_socket.h11
-rw-r--r--net/socket/ssl_client_socket_nss.cc29
-rw-r--r--net/socket/ssl_client_socket_unittest.cc99
-rw-r--r--net/ssl/ssl_config_service.cc1
-rw-r--r--net/ssl/ssl_config_service.h3
-rw-r--r--net/test/spawned_test_server/base_test_server.cc6
-rw-r--r--net/test/spawned_test_server/base_test_server.h9
-rw-r--r--net/third_party/nss/ssl/exports_win.def1
-rwxr-xr-xnet/tools/testserver/testserver.py18
10 files changed, 184 insertions, 5 deletions
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index a849488..588b701 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -16,7 +16,8 @@ SSLClientSocket::SSLClientSocket()
: was_npn_negotiated_(false),
was_spdy_negotiated_(false),
protocol_negotiated_(kProtoUnknown),
- channel_id_sent_(false) {
+ channel_id_sent_(false),
+ signed_cert_timestamps_received_(false) {
}
// static
@@ -144,6 +145,15 @@ void SSLClientSocket::set_channel_id_sent(bool channel_id_sent) {
channel_id_sent_ = channel_id_sent;
}
+bool SSLClientSocket::WereSignedCertTimestampsReceived() const {
+ return signed_cert_timestamps_received_;
+}
+
+void SSLClientSocket::set_signed_cert_timestamps_received(
+ bool signed_cert_timestamps_received) {
+ signed_cert_timestamps_received_ = signed_cert_timestamps_received;
+}
+
// static
void SSLClientSocket::RecordChannelIDSupport(
ServerBoundCertService* server_bound_cert_service,
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 9f8532a..a88c893 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -126,9 +126,18 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
// Public for ssl_client_socket_openssl_unittest.cc.
virtual bool WasChannelIDSent() const;
+ // Returns true if the server sent Certificate Transparency SCTs
+ // via a TLS extension.
+ // Temporary glue for testing while the CT code hasn't landed.
+ // TODO(ekasper): expose received SCTs via SSLInfo instead.
+ virtual bool WereSignedCertTimestampsReceived() const;
+
protected:
virtual void set_channel_id_sent(bool channel_id_sent);
+ virtual void set_signed_cert_timestamps_received(
+ bool signed_cert_timestamps_received);
+
// Records histograms for channel id support during full handshakes - resumed
// handshakes are ignored.
static void RecordChannelIDSupport(
@@ -151,6 +160,8 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
NextProto protocol_negotiated_;
// True if a channel ID was sent.
bool channel_id_sent_;
+ // True if SCTs were received via a TLS extension.
+ bool signed_cert_timestamps_received_;
};
} // namespace net
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 89eab14..9f2f10a 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -414,6 +414,7 @@ struct HandshakeState {
channel_id_sent = false;
server_cert_chain.Reset(NULL);
server_cert = NULL;
+ sct_list_from_tls_extension.clear();
resumed_handshake = false;
ssl_connection_status = 0;
}
@@ -443,6 +444,8 @@ struct HandshakeState {
// always be non-NULL.
PeerCertificateChain server_cert_chain;
scoped_refptr<X509Certificate> server_cert;
+ // SignedCertificateTimestampList received via TLS extension (RFC 6962).
+ std::string sct_list_from_tls_extension;
// True if the current handshake was the result of TLS session resumption.
bool resumed_handshake;
@@ -754,6 +757,10 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> {
// Updates the NSS and platform specific certificates.
void UpdateServerCert();
+ // Update the nss_handshake_state_ with SignedCertificateTimestampLists
+ // received in the handshake, via a TLS extension or (to be implemented)
+ // OCSP stapling.
+ void UpdateSignedCertTimestamps();
// Updates the nss_handshake_state_ with the negotiated security parameters.
void UpdateConnectionStatus();
// Record histograms for channel id support during full handshakes - resumed
@@ -1652,6 +1659,7 @@ void SSLClientSocketNSS::Core::HandshakeSucceeded() {
RecordChannelIDSupportOnNSSTaskRunner();
UpdateServerCert();
+ UpdateSignedCertTimestamps();
UpdateConnectionStatus();
UpdateNextProto();
@@ -2413,6 +2421,18 @@ void SSLClientSocketNSS::Core::UpdateServerCert() {
}
}
+void SSLClientSocketNSS::Core::UpdateSignedCertTimestamps() {
+ const SECItem* signed_cert_timestamps =
+ SSL_PeerSignedCertTimestamps(nss_fd_);
+
+ if (!signed_cert_timestamps || !signed_cert_timestamps->len)
+ return;
+
+ nss_handshake_state_.sct_list_from_tls_extension = std::string(
+ reinterpret_cast<char*>(signed_cert_timestamps->data),
+ signed_cert_timestamps->len);
+}
+
void SSLClientSocketNSS::Core::UpdateConnectionStatus() {
SSLChannelInfo channel_info;
SECStatus ok = SSL_GetChannelInfo(nss_fd_,
@@ -3175,6 +3195,13 @@ int SSLClientSocketNSS::InitializeSSLOptions() {
}
#endif
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
+ ssl_config_.signed_cert_timestamps_enabled);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_OptionSet",
+ "SSL_ENABLE_SIGNED_CERT_TIMESTAMPS");
+ }
+
// Chromium patch to libssl
#ifdef SSL_ENABLE_CACHED_INFO
rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_CACHED_INFO,
@@ -3320,6 +3347,8 @@ int SSLClientSocketNSS::DoHandshakeComplete(int result) {
// Done!
}
set_channel_id_sent(core_->state().channel_id_sent);
+ set_signed_cert_timestamps_received(
+ !core_->state().sct_list_from_tls_extension.empty());
LeaveFunction(result);
return result;
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index f791928..0e667c6 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1793,6 +1793,105 @@ TEST_F(SSLClientSocketCertRequestInfoTest, TwoAuthorities) {
request_info->cert_authorities[1]);
}
+TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabled) {
+ SpawnedTestServer::SSLOptions ssl_options;
+ ssl_options.signed_cert_timestamps = "test";
+
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ ssl_options,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ CapturingNetLog log;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr, &log, NetLog::Source()));
+ int rv = transport->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ SSLConfig ssl_config;
+ ssl_config.signed_cert_timestamps_enabled = true;
+
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server.host_port_pair(), ssl_config));
+
+ EXPECT_FALSE(sock->IsConnected());
+
+ rv = sock->Connect(callback.callback());
+
+ CapturingNetLog::CapturedEntryList entries;
+ log.GetEntries(&entries);
+ EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT));
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock->IsConnected());
+ log.GetEntries(&entries);
+ EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1));
+
+#if !defined(USE_OPENSSL)
+ EXPECT_TRUE(sock->WereSignedCertTimestampsReceived());
+#else
+ // Enabling CT for OpenSSL is currently a noop.
+ EXPECT_FALSE(sock->WereSignedCertTimestampsReceived());
+#endif
+
+ sock->Disconnect();
+ EXPECT_FALSE(sock->IsConnected());
+}
+
+TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) {
+ SpawnedTestServer::SSLOptions ssl_options;
+ ssl_options.signed_cert_timestamps = "test";
+
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ ssl_options,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ CapturingNetLog log;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr, &log, NetLog::Source()));
+ int rv = transport->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ SSLConfig ssl_config;
+ ssl_config.signed_cert_timestamps_enabled = false;
+
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server.host_port_pair(), ssl_config));
+
+ EXPECT_FALSE(sock->IsConnected());
+
+ rv = sock->Connect(callback.callback());
+
+ CapturingNetLog::CapturedEntryList entries;
+ log.GetEntries(&entries);
+ EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT));
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock->IsConnected());
+ log.GetEntries(&entries);
+ EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1));
+
+ EXPECT_FALSE(sock->WereSignedCertTimestampsReceived());
+
+ sock->Disconnect();
+ EXPECT_FALSE(sock->IsConnected());
+}
+
} // namespace
} // namespace net
diff --git a/net/ssl/ssl_config_service.cc b/net/ssl/ssl_config_service.cc
index 893506f..a28c46d 100644
--- a/net/ssl/ssl_config_service.cc
+++ b/net/ssl/ssl_config_service.cc
@@ -43,6 +43,7 @@ SSLConfig::SSLConfig()
cached_info_enabled(false),
channel_id_enabled(true),
false_start_enabled(true),
+ signed_cert_timestamps_enabled(true),
require_forward_secrecy(false),
unrestricted_ssl3_fallback_enabled(false),
send_client_cert(false),
diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h
index efd09ef..0b19e30 100644
--- a/net/ssl/ssl_config_service.h
+++ b/net/ssl/ssl_config_service.h
@@ -99,6 +99,9 @@ struct NET_EXPORT SSLConfig {
bool cached_info_enabled; // True if TLS cached info extension is enabled.
bool channel_id_enabled; // True if TLS channel ID extension is enabled.
bool false_start_enabled; // True if we'll use TLS False Start.
+ // True if the Certificate Transparency signed_certificate_timestamp
+ // TLS extension is enabled.
+ bool signed_cert_timestamps_enabled;
// require_forward_secrecy, if true, causes only (EC)DHE cipher suites to be
// enabled. NOTE: this only applies to server sockets currently, although
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc
index b8697d4..775341b 100644
--- a/net/test/spawned_test_server/base_test_server.cc
+++ b/net/test/spawned_test_server/base_test_server.cc
@@ -398,6 +398,12 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
arguments->Set("tls-intolerant",
new base::FundamentalValue(ssl_options_.tls_intolerant));
}
+ if (!ssl_options_.signed_cert_timestamps.empty()) {
+ std::string b64_scts;
+ if (!base::Base64Encode(ssl_options_.signed_cert_timestamps, &b64_scts))
+ return false;
+ arguments->SetString("signed-cert-timestamps", b64_scts);
+ }
}
return GenerateAdditionalArguments(arguments);
diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h
index ff395c5..9d5cda8 100644
--- a/net/test/spawned_test_server/base_test_server.h
+++ b/net/test/spawned_test_server/base_test_server.h
@@ -147,6 +147,14 @@ class BaseTestServer {
// If not TLS_INTOLERANT_NONE, the server will abort any handshake that
// negotiates an intolerant TLS version in order to test version fallback.
TLSIntolerantLevel tls_intolerant;
+
+ // (Fake) SignedCertificateTimestampList (as a raw binary string) to send in
+ // a TLS extension.
+ // Temporary glue for testing: validation of SCTs is application-controlled
+ // and can be appropriately mocked out, so sending fake data here does not
+ // affect handshaking behaviour.
+ // TODO(ekasper): replace with valid SCT files for test certs.
+ std::string signed_cert_timestamps;
};
// Pass as the 'host' parameter during construction to server on 127.0.0.1
@@ -260,4 +268,3 @@ class BaseTestServer {
} // namespace net
#endif // NET_TEST_SPAWNED_TEST_SERVER_BASE_TEST_SERVER_H_
-
diff --git a/net/third_party/nss/ssl/exports_win.def b/net/third_party/nss/ssl/exports_win.def
index a9dc8eb..677ecfe 100644
--- a/net/third_party/nss/ssl/exports_win.def
+++ b/net/third_party/nss/ssl/exports_win.def
@@ -60,3 +60,4 @@ SSL_GetPlatformClientAuthDataHook
SSL_HandshakeResumedSession
SSL_RestartHandshakeAfterChannelIDReq
SSL_GetChannelBinding
+SSL_PeerSignedCertTimestamps
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index f028497..83c14d6 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -128,7 +128,7 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
def __init__(self, server_address, request_hander_class, pem_cert_and_key,
ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
- record_resume_info, tls_intolerant):
+ record_resume_info, tls_intolerant, signed_cert_timestamps):
self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key)
# Force using only python implementation - otherwise behavior is different
# depending on whether m2crypto Python module is present (error is thrown
@@ -140,6 +140,7 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
self.ssl_client_auth = ssl_client_auth
self.ssl_client_cas = []
self.tls_intolerant = tls_intolerant
+ self.signed_cert_timestamps = signed_cert_timestamps
for ca_file in ssl_client_cas:
s = open(ca_file).read()
@@ -171,7 +172,9 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
reqCert=self.ssl_client_auth,
settings=self.ssl_handshake_settings,
reqCAs=self.ssl_client_cas,
- tlsIntolerant=self.tls_intolerant)
+ tlsIntolerant=self.tls_intolerant,
+ signedCertTimestamps=
+ self.signed_cert_timestamps)
tlsConnection.ignoreAbruptClose = True
return True
except tlslite.api.TLSAbruptCloseError:
@@ -1935,7 +1938,9 @@ class ServerRunner(testserver_base.TestServerRunner):
self.options.ssl_client_ca,
self.options.ssl_bulk_cipher,
self.options.record_resume,
- self.options.tls_intolerant)
+ self.options.tls_intolerant,
+ self.options.signed_cert_timestamps.decode(
+ "base64"))
print 'HTTPS server started on %s:%d...' % (host, server.server_port)
else:
server = HTTPServer((host, port), TestPageHandler)
@@ -2073,6 +2078,13 @@ class ServerRunner(testserver_base.TestServerRunner):
'aborted. 2 means TLS 1.1 or higher will be '
'aborted. 3 means TLS 1.2 or higher will be '
'aborted.')
+ self.option_parser.add_option('--signed-cert-timestamps',
+ dest='signed_cert_timestamps',
+ default='',
+ help='Base64 encoded SCT list. If set, '
+ 'server will respond with a '
+ 'signed_certificate_timestamp TLS extension '
+ 'whenever the client supports it.')
self.option_parser.add_option('--https-record-resume',
dest='record_resume', const=True,
default=False, action='store_const',