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