diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py index 69e6067..1a1ace9 100644 --- a/third_party/tlslite/tlslite/constants.py +++ b/third_party/tlslite/tlslite/constants.py @@ -31,6 +31,7 @@ class HandshakeType: certificate_verify = 15 client_key_exchange = 16 finished = 20 + certificate_status = 22 next_protocol = 67 encrypted_extensions = 203 @@ -41,8 +42,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 876b033..9a8e5f6 100644 --- a/third_party/tlslite/tlslite/messages.py +++ b/third_party/tlslite/tlslite/messages.py @@ -115,6 +115,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, @@ -188,6 +189,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 @@ -254,6 +268,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): @@ -346,6 +361,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 @@ -403,6 +421,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, version): HandshakeMsg.__init__(self, HandshakeType.certificate_request) diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py index 0563fb5f..5d508ed 100644 --- a/third_party/tlslite/tlslite/tlsconnection.py +++ b/third_party/tlslite/tlslite/tlsconnection.py @@ -970,7 +970,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 @@ -1054,6 +1054,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. @@ -1067,7 +1077,7 @@ class TLSConnection(TLSRecordLayer): tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, signedCertTimestamps=signedCertTimestamps, - fallbackSCSV=fallbackSCSV): + fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): pass @@ -1079,7 +1089,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. @@ -1101,7 +1112,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 @@ -1111,7 +1123,8 @@ class TLSConnection(TLSRecordLayer): settings, reqCAs, tacks, activationFlags, nextProtos, anon, - tlsIntolerant, signedCertTimestamps, fallbackSCSV): + tlsIntolerant, signedCertTimestamps, fallbackSCSV, + ocspResponse): self._handshakeStart(client=False) @@ -1181,6 +1194,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 @@ -1197,7 +1212,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 @@ -1475,7 +1490,7 @@ class TLSConnection(TLSRecordLayer): def _serverCertKeyExchange(self, clientHello, serverHello, serverCertChain, privateKey, reqCert, reqCAs, cipherSuite, - settings): + settings, ocspResponse): #Send ServerHello, Certificate[, CertificateRequest], #ServerHelloDone msgs = [] @@ -1485,6 +1500,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))