summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--third_party/tlslite/README.chromium2
-rw-r--r--third_party/tlslite/patches/signed_certificate_timestamps.patch147
-rw-r--r--third_party/tlslite/tlslite/TLSConnection.py26
-rw-r--r--third_party/tlslite/tlslite/constants.py1
-rw-r--r--third_party/tlslite/tlslite/messages.py13
15 files changed, 367 insertions, 11 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',
diff --git a/third_party/tlslite/README.chromium b/third_party/tlslite/README.chromium
index de941c0..c52e5e4 100644
--- a/third_party/tlslite/README.chromium
+++ b/third_party/tlslite/README.chromium
@@ -34,3 +34,5 @@ Local Modifications:
- patches/tls_intolerant.patch: allow TLSLite to simulate a TLS-intolerant server.
- patches/channel_id.patch: add basic ChannelID support. (Signatures are not
checked.)
+- patches/signed_certificate_timestamps.patch: add support for sending Signed
+ Certificate Timestamps over a TLS extension.
diff --git a/third_party/tlslite/patches/signed_certificate_timestamps.patch b/third_party/tlslite/patches/signed_certificate_timestamps.patch
new file mode 100644
index 0000000..55db061
--- /dev/null
+++ b/third_party/tlslite/patches/signed_certificate_timestamps.patch
@@ -0,0 +1,147 @@
+diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py
+index e882e2c..d2270a9 100644
+--- a/third_party/tlslite/tlslite/TLSConnection.py
++++ b/third_party/tlslite/tlslite/TLSConnection.py
+@@ -936,7 +936,8 @@ class TLSConnection(TLSRecordLayer):
+ def handshakeServer(self, sharedKeyDB=None, verifierDB=None,
+ certChain=None, privateKey=None, reqCert=False,
+ sessionCache=None, settings=None, checker=None,
+- reqCAs=None, tlsIntolerant=0):
++ reqCAs=None, tlsIntolerant=0,
++ signedCertTimestamps=None):
+ """Perform a handshake in the role of server.
+
+ This function performs an SSL or TLS handshake. Depending on
+@@ -1007,6 +1008,11 @@ class TLSConnection(TLSRecordLayer):
+ will be sent along with a certificate request. This does not affect
+ verification.
+
++ @type signedCertTimestamps: str
++ @param signedCertTimestamps: A SignedCertificateTimestampList (as a
++ binary 8-bit string) that will be sent as a TLS extension whenever
++ the client announces support for the extension.
++
+ @raise socket.error: If a socket error occurs.
+ @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed
+ without a preceding alert.
+@@ -1016,14 +1022,15 @@ class TLSConnection(TLSRecordLayer):
+ """
+ for result in self.handshakeServerAsync(sharedKeyDB, verifierDB,
+ certChain, privateKey, reqCert, sessionCache, settings,
+- checker, reqCAs, tlsIntolerant):
++ checker, reqCAs, tlsIntolerant, signedCertTimestamps):
+ pass
+
+
+ def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None,
+ certChain=None, privateKey=None, reqCert=False,
+ sessionCache=None, settings=None, checker=None,
+- reqCAs=None, tlsIntolerant=0):
++ reqCAs=None, tlsIntolerant=0,
++ signedCertTimestamps=None):
+ """Start a server handshake operation on the TLS connection.
+
+ This function returns a generator which behaves similarly to
+@@ -1041,14 +1048,16 @@ class TLSConnection(TLSRecordLayer):
+ privateKey=privateKey, reqCert=reqCert,
+ sessionCache=sessionCache, settings=settings,
+ reqCAs=reqCAs,
+- tlsIntolerant=tlsIntolerant)
++ tlsIntolerant=tlsIntolerant,
++ signedCertTimestamps=signedCertTimestamps)
+ for result in self._handshakeWrapperAsync(handshaker, checker):
+ yield result
+
+
+ def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB,
+- certChain, privateKey, reqCert, sessionCache,
+- settings, reqCAs, tlsIntolerant):
++ certChain, privateKey, reqCert,
++ sessionCache, settings, reqCAs,
++ tlsIntolerant, signedCertTimestamps):
+
+ self._handshakeStart(client=False)
+
+@@ -1060,6 +1069,9 @@ class TLSConnection(TLSRecordLayer):
+ raise ValueError("Caller passed a privateKey but no certChain")
+ if reqCAs and not reqCert:
+ raise ValueError("Caller passed reqCAs but not reqCert")
++ if signedCertTimestamps and not certChain:
++ raise ValueError("Caller passed signedCertTimestamps but no "
++ "certChain")
+
+ if not settings:
+ settings = HandshakeSettings()
+@@ -1415,6 +1427,8 @@ class TLSConnection(TLSRecordLayer):
+ self.version, serverRandom,
+ sessionID, cipherSuite, certificateType)
+ serverHello.channel_id = clientHello.channel_id
++ if clientHello.support_signed_cert_timestamps:
++ serverHello.signed_cert_timestamps = signedCertTimestamps
+ doingChannelID = clientHello.channel_id
+ msgs.append(serverHello)
+ msgs.append(Certificate(certificateType).create(serverCertChain))
+diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py
+index e357dd0..b5a345a 100644
+--- a/third_party/tlslite/tlslite/constants.py
++++ b/third_party/tlslite/tlslite/constants.py
+@@ -32,6 +32,7 @@ class ContentType:
+ all = (20,21,22,23)
+
+ class ExtensionType:
++ signed_cert_timestamps = 18 # signed_certificate_timestamp in RFC 6962
+ channel_id = 30031
+
+ class AlertLevel:
+diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py
+index fa4d817..296f422 100644
+--- a/third_party/tlslite/tlslite/messages.py
++++ b/third_party/tlslite/tlslite/messages.py
+@@ -131,6 +131,7 @@ class ClientHello(HandshakeMsg):
+ self.compression_methods = [] # a list of 8-bit values
+ self.srp_username = None # a string
+ self.channel_id = False
++ self.support_signed_cert_timestamps = False
+
+ def create(self, version, random, session_id, cipher_suites,
+ certificate_types=None, srp_username=None):
+@@ -177,6 +178,10 @@ class ClientHello(HandshakeMsg):
+ self.certificate_types = p.getVarList(1, 1)
+ elif extType == ExtensionType.channel_id:
+ self.channel_id = True
++ elif extType == ExtensionType.signed_cert_timestamps:
++ if extLength:
++ raise SyntaxError()
++ self.support_signed_cert_timestamps = True
+ else:
+ p.getFixBytes(extLength)
+ soFar += 4 + extLength
+@@ -224,6 +229,7 @@ class ServerHello(HandshakeMsg):
+ self.certificate_type = CertificateType.x509
+ self.compression_method = 0
+ self.channel_id = False
++ self.signed_cert_timestamps = None
+
+ def create(self, version, random, session_id, cipher_suite,
+ certificate_type):
+@@ -273,6 +279,9 @@ class ServerHello(HandshakeMsg):
+ if self.channel_id:
+ extLength += 4
+
++ if self.signed_cert_timestamps:
++ extLength += 4 + len(self.signed_cert_timestamps)
++
+ if extLength != 0:
+ w.add(extLength, 2)
+
+@@ -286,6 +295,10 @@ class ServerHello(HandshakeMsg):
+ w.add(ExtensionType.channel_id, 2)
+ w.add(0, 2)
+
++ if self.signed_cert_timestamps:
++ w.add(ExtensionType.signed_cert_timestamps, 2)
++ w.addVarSeq(stringToBytes(self.signed_cert_timestamps), 1, 2)
++
+ return HandshakeMsg.postWrite(self, w, trial)
+
+ class Certificate(HandshakeMsg):
diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py
index e882e2c8..d2270a9 100644
--- a/third_party/tlslite/tlslite/TLSConnection.py
+++ b/third_party/tlslite/tlslite/TLSConnection.py
@@ -936,7 +936,8 @@ class TLSConnection(TLSRecordLayer):
def handshakeServer(self, sharedKeyDB=None, verifierDB=None,
certChain=None, privateKey=None, reqCert=False,
sessionCache=None, settings=None, checker=None,
- reqCAs=None, tlsIntolerant=0):
+ reqCAs=None, tlsIntolerant=0,
+ signedCertTimestamps=None):
"""Perform a handshake in the role of server.
This function performs an SSL or TLS handshake. Depending on
@@ -1007,6 +1008,11 @@ class TLSConnection(TLSRecordLayer):
will be sent along with a certificate request. This does not affect
verification.
+ @type signedCertTimestamps: str
+ @param signedCertTimestamps: A SignedCertificateTimestampList (as a
+ binary 8-bit string) that will be sent as a TLS extension whenever
+ the client announces support for the extension.
+
@raise socket.error: If a socket error occurs.
@raise tlslite.errors.TLSAbruptCloseError: If the socket is closed
without a preceding alert.
@@ -1016,14 +1022,15 @@ class TLSConnection(TLSRecordLayer):
"""
for result in self.handshakeServerAsync(sharedKeyDB, verifierDB,
certChain, privateKey, reqCert, sessionCache, settings,
- checker, reqCAs, tlsIntolerant):
+ checker, reqCAs, tlsIntolerant, signedCertTimestamps):
pass
def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None,
certChain=None, privateKey=None, reqCert=False,
sessionCache=None, settings=None, checker=None,
- reqCAs=None, tlsIntolerant=0):
+ reqCAs=None, tlsIntolerant=0,
+ signedCertTimestamps=None):
"""Start a server handshake operation on the TLS connection.
This function returns a generator which behaves similarly to
@@ -1041,14 +1048,16 @@ class TLSConnection(TLSRecordLayer):
privateKey=privateKey, reqCert=reqCert,
sessionCache=sessionCache, settings=settings,
reqCAs=reqCAs,
- tlsIntolerant=tlsIntolerant)
+ tlsIntolerant=tlsIntolerant,
+ signedCertTimestamps=signedCertTimestamps)
for result in self._handshakeWrapperAsync(handshaker, checker):
yield result
def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB,
- certChain, privateKey, reqCert, sessionCache,
- settings, reqCAs, tlsIntolerant):
+ certChain, privateKey, reqCert,
+ sessionCache, settings, reqCAs,
+ tlsIntolerant, signedCertTimestamps):
self._handshakeStart(client=False)
@@ -1060,6 +1069,9 @@ class TLSConnection(TLSRecordLayer):
raise ValueError("Caller passed a privateKey but no certChain")
if reqCAs and not reqCert:
raise ValueError("Caller passed reqCAs but not reqCert")
+ if signedCertTimestamps and not certChain:
+ raise ValueError("Caller passed signedCertTimestamps but no "
+ "certChain")
if not settings:
settings = HandshakeSettings()
@@ -1415,6 +1427,8 @@ class TLSConnection(TLSRecordLayer):
self.version, serverRandom,
sessionID, cipherSuite, certificateType)
serverHello.channel_id = clientHello.channel_id
+ if clientHello.support_signed_cert_timestamps:
+ serverHello.signed_cert_timestamps = signedCertTimestamps
doingChannelID = clientHello.channel_id
msgs.append(serverHello)
msgs.append(Certificate(certificateType).create(serverCertChain))
diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py
index e357dd0..b5a345a 100644
--- a/third_party/tlslite/tlslite/constants.py
+++ b/third_party/tlslite/tlslite/constants.py
@@ -32,6 +32,7 @@ class ContentType:
all = (20,21,22,23)
class ExtensionType:
+ signed_cert_timestamps = 18 # signed_certificate_timestamp in RFC 6962
channel_id = 30031
class AlertLevel:
diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py
index fa4d817..296f422 100644
--- a/third_party/tlslite/tlslite/messages.py
+++ b/third_party/tlslite/tlslite/messages.py
@@ -131,6 +131,7 @@ class ClientHello(HandshakeMsg):
self.compression_methods = [] # a list of 8-bit values
self.srp_username = None # a string
self.channel_id = False
+ self.support_signed_cert_timestamps = False
def create(self, version, random, session_id, cipher_suites,
certificate_types=None, srp_username=None):
@@ -177,6 +178,10 @@ class ClientHello(HandshakeMsg):
self.certificate_types = p.getVarList(1, 1)
elif extType == ExtensionType.channel_id:
self.channel_id = True
+ elif extType == ExtensionType.signed_cert_timestamps:
+ if extLength:
+ raise SyntaxError()
+ self.support_signed_cert_timestamps = True
else:
p.getFixBytes(extLength)
soFar += 4 + extLength
@@ -224,6 +229,7 @@ class ServerHello(HandshakeMsg):
self.certificate_type = CertificateType.x509
self.compression_method = 0
self.channel_id = False
+ self.signed_cert_timestamps = None
def create(self, version, random, session_id, cipher_suite,
certificate_type):
@@ -273,6 +279,9 @@ class ServerHello(HandshakeMsg):
if self.channel_id:
extLength += 4
+ if self.signed_cert_timestamps:
+ extLength += 4 + len(self.signed_cert_timestamps)
+
if extLength != 0:
w.add(extLength, 2)
@@ -286,6 +295,10 @@ class ServerHello(HandshakeMsg):
w.add(ExtensionType.channel_id, 2)
w.add(0, 2)
+ if self.signed_cert_timestamps:
+ w.add(ExtensionType.signed_cert_timestamps, 2)
+ w.addVarSeq(stringToBytes(self.signed_cert_timestamps), 1, 2)
+
return HandshakeMsg.postWrite(self, w, trial)
class Certificate(HandshakeMsg):