diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py index d132b78..ceaa903 100755 --- a/third_party/tlslite/tlslite/constants.py +++ b/third_party/tlslite/tlslite/constants.py @@ -30,6 +30,7 @@ class HandshakeType: certificate_verify = 15 client_key_exchange = 16 finished = 20 + certificate_status = 22 next_protocol = 67 encrypted_extensions = 203 @@ -40,8 +41,12 @@ class ContentType: application_data = 23 all = (20,21,22,23) +class CertificateStatusType: + ocsp = 1 + class ExtensionType: # RFC 6066 / 4366 server_name = 0 # RFC 6066 / 4366 + status_request = 5 # RFC 6066 / 4366 srp = 12 # RFC 5054 cert_type = 9 # RFC 6091 signed_cert_timestamps = 18 # RFC 6962 diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py index 5a2cd6c..532d86b 100755 --- a/third_party/tlslite/tlslite/messages.py +++ b/third_party/tlslite/tlslite/messages.py @@ -114,6 +114,7 @@ class ClientHello(HandshakeMsg): self.server_name = bytearray(0) 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, srpUsername=None, @@ -187,6 +188,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) index2 = p.index @@ -253,6 +267,7 @@ class ServerHello(HandshakeMsg): self.next_protos = None self.channel_id = False self.signed_cert_timestamps = None + self.status_request = False def create(self, version, random, session_id, cipher_suite, certificate_type, tackExt, next_protos_advertised): @@ -345,6 +360,9 @@ class ServerHello(HandshakeMsg): if self.signed_cert_timestamps: w2.add(ExtensionType.signed_cert_timestamps, 2) w2.addVarSeq(bytearray(self.signed_cert_timestamps), 1, 2) + if self.status_request: + w2.add(ExtensionType.status_request, 2) + w2.add(0, 2) if len(w2.bytes): w.add(len(w2.bytes), 2) w.bytes += w2.bytes @@ -402,6 +420,37 @@ class Certificate(HandshakeMsg): raise AssertionError() return self.postWrite(w) +class CertificateStatus(HandshakeMsg): + def __init__(self): + HandshakeMsg.__init__(self, HandshakeType.certificate_status) + + 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 + p.stopLengthCheck() + return self + + def write(self): + w = Writer() + w.add(CertificateStatusType.ocsp, 1) + w.addVarSeq(bytearray(self.ocsp_response), 1, 3) + return self.postWrite(w) + class CertificateRequest(HandshakeMsg): def __init__(self): HandshakeMsg.__init__(self, HandshakeType.certificate_request) diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py index bd92161..b9797d2 100755 --- a/third_party/tlslite/tlslite/tlsconnection.py +++ b/third_party/tlslite/tlslite/tlsconnection.py @@ -967,7 +967,7 @@ class TLSConnection(TLSRecordLayer): tacks=None, activationFlags=0, nextProtos=None, anon=False, tlsIntolerant=None, signedCertTimestamps=None, - fallbackSCSV=False): + fallbackSCSV=False, ocspResponse=None): """Perform a handshake in the role of server. This function performs an SSL or TLS handshake. Depending on @@ -1051,6 +1051,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. @@ -1064,7 +1074,7 @@ class TLSConnection(TLSRecordLayer): tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, - fallbackSCSV=fallbackSCSV): + fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): pass @@ -1076,7 +1086,8 @@ class TLSConnection(TLSRecordLayer): nextProtos=None, anon=False, tlsIntolerant=None, signedCertTimestamps=None, - fallbackSCSV=False + fallbackSCSV=False, + ocspResponse=None ): """Start a server handshake operation on the TLS connection. @@ -1098,7 +1109,8 @@ class TLSConnection(TLSRecordLayer): nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, - fallbackSCSV=fallbackSCSV) + fallbackSCSV=fallbackSCSV, + ocspResponse=ocspResponse) for result in self._handshakeWrapperAsync(handshaker, checker): yield result @@ -1108,7 +1120,8 @@ class TLSConnection(TLSRecordLayer): settings, reqCAs, tacks, activationFlags, nextProtos, anon, - tlsIntolerant, signedCertTimestamps, fallbackSCSV): + tlsIntolerant, signedCertTimestamps, fallbackSCSV, + ocspResponse): self._handshakeStart(client=False) @@ -1178,6 +1191,8 @@ class TLSConnection(TLSRecordLayer): serverHello.channel_id = clientHello.channel_id if clientHello.support_signed_cert_timestamps: serverHello.signed_cert_timestamps = signedCertTimestamps + if clientHello.status_request: + serverHello.status_request = ocspResponse # Perform the SRP key exchange clientCertChain = None @@ -1194,7 +1209,7 @@ class TLSConnection(TLSRecordLayer): for result in self._serverCertKeyExchange(clientHello, serverHello, certChain, privateKey, reqCert, reqCAs, cipherSuite, - settings): + settings, ocspResponse): if result in (0,1): yield result else: break (premasterSecret, clientCertChain) = result @@ -1471,7 +1486,7 @@ class TLSConnection(TLSRecordLayer): def _serverCertKeyExchange(self, clientHello, serverHello, serverCertChain, privateKey, reqCert, reqCAs, cipherSuite, - settings): + settings, ocspResponse): #Send ServerHello, Certificate[, CertificateRequest], #ServerHelloDone msgs = [] @@ -1481,6 +1496,8 @@ class TLSConnection(TLSRecordLayer): msgs.append(serverHello) msgs.append(Certificate(CertificateType.x509).create(serverCertChain)) + if serverHello.status_request: + msgs.append(CertificateStatus().create(ocspResponse)) if reqCert and reqCAs: msgs.append(CertificateRequest().create(\ [ClientCertificateType.rsa_sign], reqCAs))