diff options
author | ekasper@google.com <ekasper@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 00:25:51 +0000 |
---|---|---|
committer | ekasper@google.com <ekasper@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 00:25:51 +0000 |
commit | c9feb5fdfc7df5f0e30710b58b95a887ac27125d (patch) | |
tree | 807e19c1ccd5a312086a0fc5e67746d0c10947ad /third_party/tlslite | |
parent | a399a758b43c578203f26d592fdca2e47b9c6165 (diff) | |
download | chromium_src-c9feb5fdfc7df5f0e30710b58b95a887ac27125d.zip chromium_src-c9feb5fdfc7df5f0e30710b58b95a887ac27125d.tar.gz chromium_src-c9feb5fdfc7df5f0e30710b58b95a887ac27125d.tar.bz2 |
Extract Certificate Transparency SCTs from stapled OCSP responses
BUG=309578
Review URL: https://codereview.chromium.org/102613006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241083 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/tlslite')
-rw-r--r-- | third_party/tlslite/README.chromium | 1 | ||||
-rw-r--r-- | third_party/tlslite/patches/status_request.patch | 208 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/TLSConnection.py | 29 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/constants.py | 5 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/messages.py | 53 |
5 files changed, 289 insertions, 7 deletions
diff --git a/third_party/tlslite/README.chromium b/third_party/tlslite/README.chromium index ec2fad8..ed0d793 100644 --- a/third_party/tlslite/README.chromium +++ b/third_party/tlslite/README.chromium @@ -38,3 +38,4 @@ Local Modifications: Certificate Timestamps over a TLS extension. - patches/fallback_scsv.patch: add support for TLS_FALLBACK_SCSV. See https://tools.ietf.org/html/draft-bmoeller-tls-downgrade-scsv-01 +- patches/status_request.patch: add support for sending stapled OCSP responses. diff --git a/third_party/tlslite/patches/status_request.patch b/third_party/tlslite/patches/status_request.patch new file mode 100644 index 0000000..15f01d4 --- /dev/null +++ b/third_party/tlslite/patches/status_request.patch @@ -0,0 +1,208 @@ +diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py +index e6ce187..94ee5eb 100644 +--- a/third_party/tlslite/tlslite/TLSConnection.py ++++ b/third_party/tlslite/tlslite/TLSConnection.py +@@ -937,8 +937,8 @@ class TLSConnection(TLSRecordLayer): + certChain=None, privateKey=None, reqCert=False, + sessionCache=None, settings=None, checker=None, + reqCAs=None, tlsIntolerant=0, +- signedCertTimestamps=None, +- fallbackSCSV=False): ++ signedCertTimestamps=None, fallbackSCSV=False, ++ ocspResponse=None): + """Perform a handshake in the role of server. + + This function performs an SSL or TLS handshake. Depending on +@@ -1014,6 +1014,16 @@ class TLSConnection(TLSRecordLayer): + binary 8-bit string) that will be sent as a TLS extension whenever + the client announces support for the extension. + ++ @type ocspResponse: str ++ @param ocspResponse: An OCSP response (as a binary 8-bit string) that ++ will be sent stapled in the handshake whenever the client announces ++ support for the status_request extension. ++ Note that the response is sent independent of the ClientHello ++ status_request extension contents, and is thus only meant for testing ++ environments. Real OCSP stapling is more complicated as it requires ++ choosing a suitable response based on the ClientHello status_request ++ extension contents. ++ + @raise socket.error: If a socket error occurs. + @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed + without a preceding alert. +@@ -1024,7 +1034,7 @@ class TLSConnection(TLSRecordLayer): + for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, + certChain, privateKey, reqCert, sessionCache, settings, + checker, reqCAs, tlsIntolerant, signedCertTimestamps, +- fallbackSCSV): ++ fallbackSCSV, ocspResponse): + pass + + +@@ -1033,7 +1043,7 @@ class TLSConnection(TLSRecordLayer): + sessionCache=None, settings=None, checker=None, + reqCAs=None, tlsIntolerant=0, + signedCertTimestamps=None, +- fallbackSCSV=False): ++ fallbackSCSV=False, ocspResponse=None): + """Start a server handshake operation on the TLS connection. + + This function returns a generator which behaves similarly to +@@ -1053,7 +1063,8 @@ class TLSConnection(TLSRecordLayer): + reqCAs=reqCAs, + tlsIntolerant=tlsIntolerant, + signedCertTimestamps=signedCertTimestamps, +- fallbackSCSV=fallbackSCSV) ++ fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse) ++ + for result in self._handshakeWrapperAsync(handshaker, checker): + yield result + +@@ -1062,7 +1073,7 @@ class TLSConnection(TLSRecordLayer): + certChain, privateKey, reqCert, + sessionCache, settings, reqCAs, + tlsIntolerant, signedCertTimestamps, +- fallbackSCSV): ++ fallbackSCSV, ocspResponse): + + self._handshakeStart(client=False) + +@@ -1439,10 +1450,14 @@ class TLSConnection(TLSRecordLayer): + sessionID, cipherSuite, certificateType) + serverHello.channel_id = clientHello.channel_id + if clientHello.support_signed_cert_timestamps: +- serverHello.signed_cert_timestamps = signedCertTimestamps ++ serverHello.signed_cert_timestamps = signedCertTimestamps ++ serverHello.status_request = (clientHello.status_request and ++ ocspResponse) + doingChannelID = clientHello.channel_id + msgs.append(serverHello) + msgs.append(Certificate(certificateType).create(serverCertChain)) ++ if serverHello.status_request: ++ msgs.append(CertificateStatus().create(ocspResponse)) + if reqCert and reqCAs: + msgs.append(CertificateRequest().create([], reqCAs)) + elif reqCert: +diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py +index 23e3dcb..d027ef5 100644 +--- a/third_party/tlslite/tlslite/constants.py ++++ b/third_party/tlslite/tlslite/constants.py +@@ -22,6 +22,7 @@ class HandshakeType: + certificate_verify = 15 + client_key_exchange = 16 + finished = 20 ++ certificate_status = 22 + encrypted_extensions = 203 + + class ContentType: +@@ -31,7 +32,11 @@ class ContentType: + application_data = 23 + all = (20,21,22,23) + ++class CertificateStatusType: ++ ocsp = 1 ++ + class ExtensionType: ++ status_request = 5 # OCSP stapling + signed_cert_timestamps = 18 # signed_certificate_timestamp in RFC 6962 + channel_id = 30031 + +diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py +index 296f422..497ef60 100644 +--- a/third_party/tlslite/tlslite/messages.py ++++ b/third_party/tlslite/tlslite/messages.py +@@ -132,6 +132,7 @@ class ClientHello(HandshakeMsg): + self.srp_username = None # a string + self.channel_id = False + self.support_signed_cert_timestamps = False ++ self.status_request = False + + def create(self, version, random, session_id, cipher_suites, + certificate_types=None, srp_username=None): +@@ -182,6 +183,19 @@ class ClientHello(HandshakeMsg): + if extLength: + raise SyntaxError() + self.support_signed_cert_timestamps = True ++ elif extType == ExtensionType.status_request: ++ # Extension contents are currently ignored. ++ # According to RFC 6066, this is not strictly forbidden ++ # (although it is suboptimal): ++ # Servers that receive a client hello containing the ++ # "status_request" extension MAY return a suitable ++ # certificate status response to the client along with ++ # their certificate. If OCSP is requested, they ++ # SHOULD use the information contained in the extension ++ # when selecting an OCSP responder and SHOULD include ++ # request_extensions in the OCSP request. ++ p.getFixBytes(extLength) ++ self.status_request = True + else: + p.getFixBytes(extLength) + soFar += 4 + extLength +@@ -230,6 +244,7 @@ class ServerHello(HandshakeMsg): + self.compression_method = 0 + self.channel_id = False + self.signed_cert_timestamps = None ++ self.status_request = False + + def create(self, version, random, session_id, cipher_suite, + certificate_type): +@@ -282,6 +297,9 @@ class ServerHello(HandshakeMsg): + if self.signed_cert_timestamps: + extLength += 4 + len(self.signed_cert_timestamps) + ++ if self.status_request: ++ extLength += 4 ++ + if extLength != 0: + w.add(extLength, 2) + +@@ -299,6 +317,10 @@ class ServerHello(HandshakeMsg): + w.add(ExtensionType.signed_cert_timestamps, 2) + w.addVarSeq(stringToBytes(self.signed_cert_timestamps), 1, 2) + ++ if self.status_request: ++ w.add(ExtensionType.status_request, 2) ++ w.add(0, 2) ++ + return HandshakeMsg.postWrite(self, w, trial) + + class Certificate(HandshakeMsg): +@@ -367,6 +389,37 @@ class Certificate(HandshakeMsg): + raise AssertionError() + return HandshakeMsg.postWrite(self, w, trial) + ++class CertificateStatus(HandshakeMsg): ++ def __init__(self): ++ self.contentType = ContentType.handshake ++ ++ def create(self, ocsp_response): ++ self.ocsp_response = ocsp_response ++ return self ++ ++ # Defined for the sake of completeness, even though we currently only ++ # support sending the status message (server-side), not requesting ++ # or receiving it (client-side). ++ def parse(self, p): ++ p.startLengthCheck(3) ++ status_type = p.get(1) ++ # Only one type is specified, so hardwire it. ++ if status_type != CertificateStatusType.ocsp: ++ raise SyntaxError() ++ ocsp_response = p.getVarBytes(3) ++ if not ocsp_response: ++ # Can't be empty ++ raise SyntaxError() ++ self.ocsp_response = ocsp_response ++ return self ++ ++ def write(self, trial=False): ++ w = HandshakeMsg.preWrite(self, HandshakeType.certificate_status, ++ trial) ++ w.add(CertificateStatusType.ocsp, 1) ++ w.addVarSeq(stringToBytes(self.ocsp_response), 1, 3) ++ return HandshakeMsg.postWrite(self, w, trial) ++ + class CertificateRequest(HandshakeMsg): + def __init__(self): + self.contentType = ContentType.handshake diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py index c5722d6..0c34536 100644 --- a/third_party/tlslite/tlslite/TLSConnection.py +++ b/third_party/tlslite/tlslite/TLSConnection.py @@ -937,8 +937,8 @@ class TLSConnection(TLSRecordLayer): certChain=None, privateKey=None, reqCert=False, sessionCache=None, settings=None, checker=None, reqCAs=None, tlsIntolerant=0, - signedCertTimestamps=None, - fallbackSCSV=False): + signedCertTimestamps=None, fallbackSCSV=False, + ocspResponse=None): """Perform a handshake in the role of server. This function performs an SSL or TLS handshake. Depending on @@ -1027,6 +1027,16 @@ class TLSConnection(TLSRecordLayer): TLS_FALLBACK_SCSV and thus reject connections using less than the server's maximum TLS version that include this cipher suite. + @type ocspResponse: str + @param ocspResponse: An OCSP response (as a binary 8-bit string) that + will be sent stapled in the handshake whenever the client announces + support for the status_request extension. + Note that the response is sent independent of the ClientHello + status_request extension contents, and is thus only meant for testing + environments. Real OCSP stapling is more complicated as it requires + choosing a suitable response based on the ClientHello status_request + extension contents. + @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @@ -1037,7 +1047,7 @@ class TLSConnection(TLSRecordLayer): for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, certChain, privateKey, reqCert, sessionCache, settings, checker, reqCAs, tlsIntolerant, signedCertTimestamps, - fallbackSCSV): + fallbackSCSV, ocspResponse): pass @@ -1046,7 +1056,7 @@ class TLSConnection(TLSRecordLayer): sessionCache=None, settings=None, checker=None, reqCAs=None, tlsIntolerant=0, signedCertTimestamps=None, - fallbackSCSV=False): + fallbackSCSV=False, ocspResponse=None): """Start a server handshake operation on the TLS connection. This function returns a generator which behaves similarly to @@ -1066,7 +1076,8 @@ class TLSConnection(TLSRecordLayer): reqCAs=reqCAs, tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, - fallbackSCSV=fallbackSCSV) + fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse) + for result in self._handshakeWrapperAsync(handshaker, checker): yield result @@ -1075,7 +1086,7 @@ class TLSConnection(TLSRecordLayer): certChain, privateKey, reqCert, sessionCache, settings, reqCAs, tlsIntolerant, signedCertTimestamps, - fallbackSCSV): + fallbackSCSV, ocspResponse): self._handshakeStart(client=False) @@ -1452,10 +1463,14 @@ class TLSConnection(TLSRecordLayer): sessionID, cipherSuite, certificateType) serverHello.channel_id = clientHello.channel_id if clientHello.support_signed_cert_timestamps: - serverHello.signed_cert_timestamps = signedCertTimestamps + serverHello.signed_cert_timestamps = signedCertTimestamps + serverHello.status_request = (clientHello.status_request and + ocspResponse) doingChannelID = clientHello.channel_id msgs.append(serverHello) msgs.append(Certificate(certificateType).create(serverCertChain)) + if serverHello.status_request: + msgs.append(CertificateStatus().create(ocspResponse)) if reqCert and reqCAs: msgs.append(CertificateRequest().create([], reqCAs)) elif reqCert: diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py index 23e3dcb..d027ef5 100644 --- a/third_party/tlslite/tlslite/constants.py +++ b/third_party/tlslite/tlslite/constants.py @@ -22,6 +22,7 @@ class HandshakeType: certificate_verify = 15 client_key_exchange = 16 finished = 20 + certificate_status = 22 encrypted_extensions = 203 class ContentType: @@ -31,7 +32,11 @@ class ContentType: application_data = 23 all = (20,21,22,23) +class CertificateStatusType: + ocsp = 1 + class ExtensionType: + status_request = 5 # OCSP stapling signed_cert_timestamps = 18 # signed_certificate_timestamp in RFC 6962 channel_id = 30031 diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py index 296f422..497ef60 100644 --- a/third_party/tlslite/tlslite/messages.py +++ b/third_party/tlslite/tlslite/messages.py @@ -132,6 +132,7 @@ class ClientHello(HandshakeMsg): self.srp_username = None # a string self.channel_id = False self.support_signed_cert_timestamps = False + self.status_request = False def create(self, version, random, session_id, cipher_suites, certificate_types=None, srp_username=None): @@ -182,6 +183,19 @@ class ClientHello(HandshakeMsg): if extLength: raise SyntaxError() self.support_signed_cert_timestamps = True + elif extType == ExtensionType.status_request: + # Extension contents are currently ignored. + # According to RFC 6066, this is not strictly forbidden + # (although it is suboptimal): + # Servers that receive a client hello containing the + # "status_request" extension MAY return a suitable + # certificate status response to the client along with + # their certificate. If OCSP is requested, they + # SHOULD use the information contained in the extension + # when selecting an OCSP responder and SHOULD include + # request_extensions in the OCSP request. + p.getFixBytes(extLength) + self.status_request = True else: p.getFixBytes(extLength) soFar += 4 + extLength @@ -230,6 +244,7 @@ class ServerHello(HandshakeMsg): self.compression_method = 0 self.channel_id = False self.signed_cert_timestamps = None + self.status_request = False def create(self, version, random, session_id, cipher_suite, certificate_type): @@ -282,6 +297,9 @@ class ServerHello(HandshakeMsg): if self.signed_cert_timestamps: extLength += 4 + len(self.signed_cert_timestamps) + if self.status_request: + extLength += 4 + if extLength != 0: w.add(extLength, 2) @@ -299,6 +317,10 @@ class ServerHello(HandshakeMsg): w.add(ExtensionType.signed_cert_timestamps, 2) w.addVarSeq(stringToBytes(self.signed_cert_timestamps), 1, 2) + if self.status_request: + w.add(ExtensionType.status_request, 2) + w.add(0, 2) + return HandshakeMsg.postWrite(self, w, trial) class Certificate(HandshakeMsg): @@ -367,6 +389,37 @@ class Certificate(HandshakeMsg): raise AssertionError() return HandshakeMsg.postWrite(self, w, trial) +class CertificateStatus(HandshakeMsg): + def __init__(self): + self.contentType = ContentType.handshake + + def create(self, ocsp_response): + self.ocsp_response = ocsp_response + return self + + # Defined for the sake of completeness, even though we currently only + # support sending the status message (server-side), not requesting + # or receiving it (client-side). + def parse(self, p): + p.startLengthCheck(3) + status_type = p.get(1) + # Only one type is specified, so hardwire it. + if status_type != CertificateStatusType.ocsp: + raise SyntaxError() + ocsp_response = p.getVarBytes(3) + if not ocsp_response: + # Can't be empty + raise SyntaxError() + self.ocsp_response = ocsp_response + return self + + def write(self, trial=False): + w = HandshakeMsg.preWrite(self, HandshakeType.certificate_status, + trial) + w.add(CertificateStatusType.ocsp, 1) + w.addVarSeq(stringToBytes(self.ocsp_response), 1, 3) + return HandshakeMsg.postWrite(self, w, trial) + class CertificateRequest(HandshakeMsg): def __init__(self): self.contentType = ContentType.handshake |