Skip to content

Commit

Permalink
Merge bce01db into 75dbe54
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Oct 25, 2019
2 parents 75dbe54 + bce01db commit 3cc75ce
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 28 deletions.
80 changes: 80 additions & 0 deletions tlslite/handshakesettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -530,6 +608,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
Expand Down Expand Up @@ -574,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
Expand Down
72 changes: 44 additions & 28 deletions tlslite/tlsconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -2009,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")
Expand All @@ -2025,29 +2033,27 @@ def _handshakeServerAsyncHelper(self, verifierDB,
if tacks:
if not tackpyLoaded:
raise ValueError("tackpy is not loaded")
if not settings or not settings.useExperimentalTackExtension:
if not settings.useExperimentalTackExtension:
raise ValueError("useExperimentalTackExtension not enabled")
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
# ******************************

# 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
elif result == None:
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
Expand All @@ -2056,7 +2062,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
Expand Down Expand Up @@ -2823,7 +2829,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
Expand All @@ -2839,6 +2846,8 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB,
else: break
clientHello = result

# check if the ClientHello and its extensions are well-formed

#If client's version is too low, reject it
real_version = clientHello.client_version
if real_version >= (3, 3):
Expand Down Expand Up @@ -2925,12 +2934,6 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB,
AlertDescription.illegal_parameter,
"Host name in SNI is not valid DNS name"):
yield result
# warn the client if the name didn't match the expected value
if sni and sni != name:
alert = Alert().create(AlertDescription.unrecognized_name,
AlertLevel.warning)
for result in self._sendMsg(alert):
yield result

# sanity check the EMS extension
emsExt = clientHello.getExtension(ExtensionType.extended_master_secret)
Expand Down Expand Up @@ -3070,6 +3073,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,
Expand Down Expand Up @@ -3107,13 +3111,25 @@ def _serverGetClientHello(self, settings, cert_chain, verifierDB,
# TODO when TLS 1.3 is final, check the client hello random for
# downgrade too

scheme = None
# start negotiating the parameters of the connection

sni_ext = clientHello.getExtension(ExtensionType.server_name)
if sni_ext:
name = sni_ext.hostNames[0].decode('ascii', 'strict')
# warn the client if the name didn't match the expected value
if sni and sni != name:
alert = Alert().create(AlertDescription.unrecognized_name,
AlertLevel.warning)
for result in self._sendMsg(alert):
yield result

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,
Expand Down Expand Up @@ -3628,7 +3644,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,
Expand Down

0 comments on commit 3cc75ce

Please sign in to comment.