From 1e60de98fa52cb3a38b7bc0e041aaa7de0ed8232 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 25 Oct 2019 19:23:46 +0200 Subject: [PATCH 1/5] fix missing copy of useExperimentalTackExtension --- tlslite/handshakesettings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tlslite/handshakesettings.py b/tlslite/handshakesettings.py index 2c0c501a..7a5034e4 100644 --- a/tlslite/handshakesettings.py +++ b/tlslite/handshakesettings.py @@ -530,6 +530,7 @@ def _copy_extension_settings(self, other): """Copy values of settings related to extensions.""" other.useExtendedMasterSecret = self.useExtendedMasterSecret other.requireExtendedMasterSecret = self.requireExtendedMasterSecret + other.useExperimentalTackExtension = self.useExperimentalTackExtension other.sendFallbackSCSV = self.sendFallbackSCSV other.useEncryptThenMAC = self.useEncryptThenMAC other.usePaddingExtension = self.usePaddingExtension From 454a111b88131d1321945e11208c790a491b758a Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Thu, 24 Oct 2019 21:00:20 +0200 Subject: [PATCH 2/5] settings for virtual hosts --- tlslite/handshakesettings.py | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tlslite/handshakesettings.py b/tlslite/handshakesettings.py index 7a5034e4..b0d7e96b 100644 --- a/tlslite/handshakesettings.py +++ b/tlslite/handshakesettings.py @@ -49,6 +49,80 @@ PSK_MODES = ["psk_dhe_ke", "psk_ke"] +class Keypair(object): + """ + Key, certificate and related data. + + Stores also certificate associate data like OCSPs and transparency info. + TODO: add the above + + First certificate in certificates needs to match key, remaining should + build a trust path to a root CA. + + :vartype key: RSAKey or ECDSAKey + :ivar key: private key + + :vartype certificates: list of X509 + :ivar certificates: the certificates to send to peer if the key is selected + for use. The first one MUST include the public key of the ``key`` + """ + def __init__(self, key=None, certificates=tuple()): + self.key = key + self.certificates = certificates + + def validate(self): + """Sanity check the keypair.""" + if not self.key or not self.certificates: + raise ValueError("Key or certificate missing in Keypair") + + +class VirtualHost(object): + """ + Configuration of keys and certs for a single virual server. + + This class encapsulates keys and certificates for hosts specified by + server_name (SNI) and ALPN extensions. + + TODO: support SRP as alternative to certificates + TODO: support PSK as alternative to certificates + + :vartype keys: list of :ref:`~Keypair` + :ivar keys: List of certificates and keys to be used in this + virtual host. First keypair able to server ClientHello will be used. + + :vartype hostnames: set of bytes + :ivar hostnames: all the hostnames that server supports + please use :ref:`matches_hostname` to verify if the VirtualHost + can serve a request to a given hostname as that allows wildcard hosts + that always reply True. + + :vartype trust_anchors: list of X509 + :ivar trust_anchors: list of CA certificates supported for client + certificate authentication, sent in CertificateRequest + + :ivar app_protocols: all the application protocols that the server supports + (for ALPN) + """ + + def __init__(self): + """Set up default configuration.""" + self.keys = [] + self.hostnames = set() + self.trust_anchors = [] + self.app_protocols = [] + + def matches_hostname(self, hostname): + """Checks if the virtual host can serve hostname""" + return hostname in self.hostnames + + def validate(self): + """Sanity check the settings""" + if not self.keys: + raise ValueError("Virtual host missing keys") + for i in self.keys: + i.validate() + + class HandshakeSettings(object): """ This class encapsulates various parameters that can be used with @@ -254,6 +328,7 @@ def _init_key_settings(self): self.maxKeySize = 8193 self.rsaSigHashes = list(RSA_SIGNATURE_HASHES) self.rsaSchemes = list(RSA_SCHEMES) + self.virtual_hosts = [] # DH key settings self.eccCurves = list(CURVE_NAMES) self.dhParams = None @@ -312,6 +387,9 @@ def _sanityCheckKeySizes(other): raise ValueError("maxKeySize too large") if other.maxKeySize < other.minKeySize: raise ValueError("maxKeySize smaller than minKeySize") + # check also keys of virtual hosts + for i in other.virtual_hosts: + i.validate() @staticmethod def _not_matching(values, sieve): @@ -575,6 +653,7 @@ def _copy_key_settings(self, other): other.rsaSigHashes = self.rsaSigHashes other.rsaSchemes = self.rsaSchemes other.ecdsaSigHashes = self.ecdsaSigHashes + other.virtual_hosts = self.virtual_hosts # DH key params other.eccCurves = self.eccCurves other.dhParams = self.dhParams From e7b4a758ab63f69fb2b67ce7e3ad476a677ab851 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 25 Oct 2019 18:50:00 +0200 Subject: [PATCH 3/5] nicer names for few variables --- tlslite/tlsconnection.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py index 8ca80d5d..75e10f1a 100644 --- a/tlslite/tlsconnection.py +++ b/tlslite/tlsconnection.py @@ -2039,7 +2039,8 @@ def _handshakeServerAsyncHelper(self, verifierDB, # ****************************** # Handle ClientHello and resumption - for result in self._serverGetClientHello(settings, cert_chain, + for result in self._serverGetClientHello(settings, privateKey, + cert_chain, verifierDB, sessionCache, anon, alpn, sni): if result in (0,1): yield result @@ -2047,7 +2048,7 @@ def _handshakeServerAsyncHelper(self, verifierDB, self._handshakeDone(resumed=True) return # Handshake was resumed, we're done else: break - (clientHello, cipherSuite, version, scheme) = result + (clientHello, version, cipherSuite, sig_scheme) = result # in TLS 1.3 the handshake is completely different # (extensions go into different messages, format of messages is @@ -2056,7 +2057,7 @@ def _handshakeServerAsyncHelper(self, verifierDB, for result in self._serverTLS13Handshake(settings, clientHello, cipherSuite, privateKey, cert_chain, - version, scheme, + version, sig_scheme, alpn, reqCert): if result in (0, 1): yield result @@ -2823,7 +2824,8 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite, yield "finished" - def _serverGetClientHello(self, settings, cert_chain, verifierDB, + def _serverGetClientHello(self, settings, private_key, cert_chain, + verifierDB, sessionCache, anon, alpn, sni): # Tentatively set version to most-desirable version, so if an error # occurs parsing the ClientHello, this will be the version we'll use @@ -3070,6 +3072,7 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB, self._recordLayer.max_early_data = settings.max_early_data self._recordLayer.early_data_ok = True + # negotiate the protocol version for the connection high_ver = None if ver_ext: high_ver = getFirstMatching(settings.versions, @@ -3107,13 +3110,13 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB, # TODO when TLS 1.3 is final, check the client hello random for # downgrade too - scheme = None + sig_scheme = None if version >= (3, 4): try: - scheme = self._pickServerKeyExchangeSig(settings, - clientHello, - cert_chain, - version) + sig_scheme = self._pickServerKeyExchangeSig(settings, + clientHello, + cert_chain, + version) except TLSHandshakeFailure as alert: for result in self._sendError( AlertDescription.handshake_failure, @@ -3628,7 +3631,7 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB, # we have no session cache, or # the client's session_id was not found in cache: #pylint: disable = undefined-loop-variable - yield (clientHello, cipherSuite, version, scheme) + yield (clientHello, version, cipherSuite, sig_scheme) #pylint: enable = undefined-loop-variable def _serverSRPKeyExchange(self, clientHello, serverHello, verifierDB, From 3beff18fef3950373f78806c2a8a430a45b3c10e Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 25 Oct 2019 18:50:40 +0200 Subject: [PATCH 4/5] document use of parameters with virtual hosts (planned) describe the planned behaviour --- tlslite/tlsconnection.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py index 75e10f1a..3bd7daf4 100644 --- a/tlslite/tlsconnection.py +++ b/tlslite/tlsconnection.py @@ -1910,11 +1910,13 @@ def handshakeServer(self, verifierDB=None, :type certChain: ~tlslite.x509certchain.X509CertChain :param certChain: The certificate chain to be used if the - client requests server certificate authentication. + client requests server certificate authentication and no virtual + host defined in HandshakeSettings matches ClientHello. :type privateKey: ~tlslite.utils.rsakey.RSAKey :param privateKey: The private key to be used if the client - requests server certificate authentication. + requests server certificate authentication and no virtual host + defined in HandshakeSettings matches ClientHello. :type reqCert: bool :param reqCert: Whether to request client certificate @@ -1941,21 +1943,23 @@ def handshakeServer(self, verifierDB=None, :type reqCAs: list of bytearray :param reqCAs: A collection of DER-encoded DistinguishedNames that - will be sent along with a certificate request. This does not affect - verification. + will be sent along with a certificate request to help client pick + a certificates. This does not affect verification. :type nextProtos: list of str :param nextProtos: A list of upper layer protocols to expose to the clients through the Next-Protocol Negotiation Extension, - if they support it. + if they support it. Deprecated, use the `virtual_hosts` in + HandshakeSettings. :type alpn: list of bytearray :param alpn: names of application layer protocols supported. Note that it will be used instead of NPN if both were advertised by - client. + client. Deprecated, use the `virtual_hosts` in HandshakeSettings. :type sni: bytearray - :param sni: expected virtual name hostname. + :param sni: expected virtual name hostname. Deprecated, use the + `virtual_hosts` in HandshakeSettings. :raises socket.error: If a socket error occurs. :raises tlslite.errors.TLSAbruptCloseError: If the socket is closed From 2ccf74b22959dc8e1bd98e53e8c7fd4af31eb8b7 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 25 Oct 2019 18:51:22 +0200 Subject: [PATCH 5/5] check for credentials in virtual hosts too --- tlslite/tlsconnection.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py index 3bd7daf4..75bd35fd 100644 --- a/tlslite/tlsconnection.py +++ b/tlslite/tlsconnection.py @@ -2013,8 +2013,12 @@ def _handshakeServerAsyncHelper(self, verifierDB, self._handshakeStart(client=False) + if not settings: + settings = HandshakeSettings() + settings = settings.validate() + if (not verifierDB) and (not cert_chain) and not anon and \ - not settings.pskConfigs: + not settings.pskConfigs and not settings.virtual_hosts: raise ValueError("Caller passed no authentication credentials") if cert_chain and not privateKey: raise ValueError("Caller passed a cert_chain but no privateKey") @@ -2034,9 +2038,6 @@ def _handshakeServerAsyncHelper(self, verifierDB, if alpn is not None and not alpn: raise ValueError("Empty list of ALPN protocols") - if not settings: - settings = HandshakeSettings() - settings = settings.validate() self.sock.padding_cb = settings.padding_cb # OK Start exchanging messages