| Index: third_party/tlslite/tlslite/tlsconnection.py
|
| diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py
|
| index 90449afe13faf78390ad67acfbb0848f68147f8b..20cd85bc90f31b456ec45c5d23a746123b962346 100644
|
| --- a/third_party/tlslite/tlslite/tlsconnection.py
|
| +++ b/third_party/tlslite/tlslite/tlsconnection.py
|
| @@ -1,18 +1,27 @@
|
| +# Authors:
|
| +# Trevor Perrin
|
| +# Google - added reqCAs parameter
|
| +# Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support
|
| +# Dimitris Moraitis - Anon ciphersuites
|
| +# Martin von Loewis - python 3 port
|
| +#
|
| +# See the LICENSE file for legal information regarding use of this file.
|
| +
|
| """
|
| MAIN CLASS FOR TLS LITE (START HERE!).
|
| """
|
| -from __future__ import generators
|
|
|
| import socket
|
| -from utils.compat import formatExceptionTrace
|
| -from tlsrecordlayer import TLSRecordLayer
|
| -from session import Session
|
| -from constants import *
|
| -from utils.cryptomath import getRandomBytes
|
| -from errors import *
|
| -from messages import *
|
| -from mathtls import *
|
| -from handshakesettings import HandshakeSettings
|
| +from .utils.compat import formatExceptionTrace
|
| +from .tlsrecordlayer import TLSRecordLayer
|
| +from .session import Session
|
| +from .constants import *
|
| +from .utils.cryptomath import getRandomBytes
|
| +from .errors import *
|
| +from .messages import *
|
| +from .mathtls import *
|
| +from .handshakesettings import HandshakeSettings
|
| +from .utils.tackwrapper import *
|
|
|
|
|
| class TLSConnection(TLSRecordLayer):
|
| @@ -36,11 +45,9 @@ class TLSConnection(TLSRecordLayer):
|
| not use the asynchronous functions directly, but should use some
|
| framework like asyncore or Twisted which TLS Lite integrates with
|
| (see
|
| - L{tlslite.integration.TLSAsyncDispatcherMixIn.TLSAsyncDispatcherMixIn} or
|
| - L{tlslite.integration.TLSTwistedProtocolWrapper.TLSTwistedProtocolWrapper}).
|
| + L{tlslite.integration.tlsasyncdispatchermixin.TLSAsyncDispatcherMixIn}).
|
| """
|
|
|
| -
|
| def __init__(self, sock):
|
| """Create a new TLSConnection instance.
|
|
|
| @@ -52,23 +59,18 @@ class TLSConnection(TLSRecordLayer):
|
| """
|
| TLSRecordLayer.__init__(self, sock)
|
|
|
| - def handshakeClientSRP(self, username, password, session=None,
|
| - settings=None, checker=None, async=False):
|
| - """Perform an SRP handshake in the role of client.
|
| -
|
| - This function performs a TLS/SRP handshake. SRP mutually
|
| - authenticates both parties to each other using only a
|
| - username and password. This function may also perform a
|
| - combined SRP and server-certificate handshake, if the server
|
| - chooses to authenticate itself with a certificate chain in
|
| - addition to doing SRP.
|
| + #*********************************************************
|
| + # Client Handshake Functions
|
| + #*********************************************************
|
|
|
| - TLS/SRP is non-standard. Most TLS implementations don't
|
| - support it. See
|
| - U{http://www.ietf.org/html.charters/tls-charter.html} or
|
| - U{http://trevp.net/tlssrp/} for the latest information on
|
| - TLS/SRP.
|
| + def handshakeClientAnonymous(self, session=None, settings=None,
|
| + checker=None, serverName="",
|
| + async=False):
|
| + """Perform an anonymous handshake in the role of client.
|
|
|
| + This function performs an SSL or TLS handshake using an
|
| + anonymous Diffie Hellman ciphersuite.
|
| +
|
| Like any handshake function, this can be called on a closed
|
| TLS connection, or on a TLS connection that is already open.
|
| If called on an open connection it performs a re-handshake.
|
| @@ -79,17 +81,10 @@ class TLSConnection(TLSRecordLayer):
|
| If an exception is raised, the connection will have been
|
| automatically closed (if it was ever open).
|
|
|
| - @type username: str
|
| - @param username: The SRP username.
|
| -
|
| - @type password: str
|
| - @param password: The SRP password.
|
| -
|
| @type session: L{tlslite.Session.Session}
|
| - @param session: A TLS session to attempt to resume. This
|
| - session must be an SRP session performed with the same username
|
| - and password as were passed in. If the resumption does not
|
| - succeed, a full SRP handshake will be performed.
|
| + @param session: A TLS session to attempt to resume. If the
|
| + resumption does not succeed, a full handshake will be
|
| + performed.
|
|
|
| @type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
| @param settings: Various settings which can be used to control
|
| @@ -100,6 +95,9 @@ class TLSConnection(TLSRecordLayer):
|
| @param checker: A Checker instance. This instance will be
|
| invoked to examine the other party's authentication
|
| credentials, if the handshake completes succesfully.
|
| +
|
| + @type serverName: string
|
| + @param serverName: The ServerNameIndication TLS Extension.
|
|
|
| @type async: bool
|
| @param async: If False, this function will block until the
|
| @@ -120,35 +118,28 @@ class TLSConnection(TLSRecordLayer):
|
| @raise tlslite.errors.TLSAuthenticationError: If the checker
|
| doesn't like the other party's authentication credentials.
|
| """
|
| - handshaker = self._handshakeClientAsync(srpParams=(username, password),
|
| - session=session, settings=settings, checker=checker)
|
| + handshaker = self._handshakeClientAsync(anonParams=(True),
|
| + session=session,
|
| + settings=settings,
|
| + checker=checker,
|
| + serverName=serverName)
|
| if async:
|
| return handshaker
|
| for result in handshaker:
|
| pass
|
|
|
| - def handshakeClientCert(self, certChain=None, privateKey=None,
|
| - session=None, settings=None, checker=None,
|
| - async=False):
|
| - """Perform a certificate-based handshake in the role of client.
|
| -
|
| - This function performs an SSL or TLS handshake. The server
|
| - will authenticate itself using an X.509 or cryptoID certificate
|
| - chain. If the handshake succeeds, the server's certificate
|
| - chain will be stored in the session's serverCertChain attribute.
|
| - Unless a checker object is passed in, this function does no
|
| - validation or checking of the server's certificate chain.
|
| -
|
| - If the server requests client authentication, the
|
| - client will send the passed-in certificate chain, and use the
|
| - passed-in private key to authenticate itself. If no
|
| - certificate chain and private key were passed in, the client
|
| - will attempt to proceed without client authentication. The
|
| - server may or may not allow this.
|
| + def handshakeClientSRP(self, username, password, session=None,
|
| + settings=None, checker=None,
|
| + reqTack=True, serverName="",
|
| + async=False):
|
| + """Perform an SRP handshake in the role of client.
|
|
|
| - Like any handshake function, this can be called on a closed
|
| - TLS connection, or on a TLS connection that is already open.
|
| - If called on an open connection it performs a re-handshake.
|
| + This function performs a TLS/SRP handshake. SRP mutually
|
| + authenticates both parties to each other using only a
|
| + username and password. This function may also perform a
|
| + combined SRP and server-certificate handshake, if the server
|
| + chooses to authenticate itself with a certificate chain in
|
| + addition to doing SRP.
|
|
|
| If the function completes without raising an exception, the
|
| TLS connection will be open and available for data transfer.
|
| @@ -156,30 +147,35 @@ class TLSConnection(TLSRecordLayer):
|
| If an exception is raised, the connection will have been
|
| automatically closed (if it was ever open).
|
|
|
| - @type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
| - L{cryptoIDlib.CertChain.CertChain}
|
| - @param certChain: The certificate chain to be used if the
|
| - server requests client authentication.
|
| + @type username: str
|
| + @param username: The SRP username.
|
|
|
| - @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
| - @param privateKey: The private key to be used if the server
|
| - requests client authentication.
|
| + @type password: str
|
| + @param password: The SRP password.
|
|
|
| - @type session: L{tlslite.Session.Session}
|
| - @param session: A TLS session to attempt to resume. If the
|
| - resumption does not succeed, a full handshake will be
|
| - performed.
|
| + @type session: L{tlslite.session.Session}
|
| + @param session: A TLS session to attempt to resume. This
|
| + session must be an SRP session performed with the same username
|
| + and password as were passed in. If the resumption does not
|
| + succeed, a full SRP handshake will be performed.
|
|
|
| - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
| + @type settings: L{tlslite.handshakesettings.HandshakeSettings}
|
| @param settings: Various settings which can be used to control
|
| the ciphersuites, certificate types, and SSL/TLS versions
|
| offered by the client.
|
|
|
| - @type checker: L{tlslite.Checker.Checker}
|
| + @type checker: L{tlslite.checker.Checker}
|
| @param checker: A Checker instance. This instance will be
|
| invoked to examine the other party's authentication
|
| credentials, if the handshake completes succesfully.
|
|
|
| + @type reqTack: bool
|
| + @param reqTack: Whether or not to send a "tack" TLS Extension,
|
| + requesting the server return a TackExtension if it has one.
|
| +
|
| + @type serverName: string
|
| + @param serverName: The ServerNameIndication TLS Extension.
|
| +
|
| @type async: bool
|
| @param async: If False, this function will block until the
|
| handshake is completed. If True, this function will return a
|
| @@ -199,68 +195,80 @@ class TLSConnection(TLSRecordLayer):
|
| @raise tlslite.errors.TLSAuthenticationError: If the checker
|
| doesn't like the other party's authentication credentials.
|
| """
|
| - handshaker = self._handshakeClientAsync(certParams=(certChain,
|
| - privateKey), session=session, settings=settings,
|
| - checker=checker)
|
| + handshaker = self._handshakeClientAsync(srpParams=(username, password),
|
| + session=session, settings=settings, checker=checker,
|
| + reqTack=reqTack, serverName=serverName)
|
| + # The handshaker is a Python Generator which executes the handshake.
|
| + # It allows the handshake to be run in a "piecewise", asynchronous
|
| + # fashion, returning 1 when it is waiting to able to write, 0 when
|
| + # it is waiting to read.
|
| + #
|
| + # If 'async' is True, the generator is returned to the caller,
|
| + # otherwise it is executed to completion here.
|
| if async:
|
| return handshaker
|
| for result in handshaker:
|
| pass
|
|
|
| - def handshakeClientUnknown(self, srpCallback=None, certCallback=None,
|
| - session=None, settings=None, checker=None,
|
| - async=False):
|
| - """Perform a to-be-determined type of handshake in the role of client.
|
| + def handshakeClientCert(self, certChain=None, privateKey=None,
|
| + session=None, settings=None, checker=None,
|
| + nextProtos=None, reqTack=True, serverName="",
|
| + async=False):
|
| + """Perform a certificate-based handshake in the role of client.
|
| +
|
| + This function performs an SSL or TLS handshake. The server
|
| + will authenticate itself using an X.509 certificate
|
| + chain. If the handshake succeeds, the server's certificate
|
| + chain will be stored in the session's serverCertChain attribute.
|
| + Unless a checker object is passed in, this function does no
|
| + validation or checking of the server's certificate chain.
|
|
|
| - This function performs an SSL or TLS handshake. If the server
|
| - requests client certificate authentication, the
|
| - certCallback will be invoked and should return a (certChain,
|
| - privateKey) pair. If the callback returns None, the library
|
| + If the server requests client authentication, the
|
| + client will send the passed-in certificate chain, and use the
|
| + passed-in private key to authenticate itself. If no
|
| + certificate chain and private key were passed in, the client
|
| will attempt to proceed without client authentication. The
|
| server may or may not allow this.
|
|
|
| - If the server requests SRP authentication, the srpCallback
|
| - will be invoked and should return a (username, password) pair.
|
| - If the callback returns None, the local implementation will
|
| - signal a user_canceled error alert.
|
| -
|
| - After the handshake completes, the client can inspect the
|
| - connection's session attribute to determine what type of
|
| - authentication was performed.
|
| -
|
| - Like any handshake function, this can be called on a closed
|
| - TLS connection, or on a TLS connection that is already open.
|
| - If called on an open connection it performs a re-handshake.
|
| -
|
| If the function completes without raising an exception, the
|
| TLS connection will be open and available for data transfer.
|
|
|
| If an exception is raised, the connection will have been
|
| automatically closed (if it was ever open).
|
|
|
| - @type srpCallback: callable
|
| - @param srpCallback: The callback to be used if the server
|
| - requests SRP authentication. If None, the client will not
|
| - offer support for SRP ciphersuites.
|
| + @type certChain: L{tlslite.x509certchain.X509CertChain}
|
| + @param certChain: The certificate chain to be used if the
|
| + server requests client authentication.
|
|
|
| - @type certCallback: callable
|
| - @param certCallback: The callback to be used if the server
|
| - requests client certificate authentication.
|
| + @type privateKey: L{tlslite.utils.rsakey.RSAKey}
|
| + @param privateKey: The private key to be used if the server
|
| + requests client authentication.
|
|
|
| - @type session: L{tlslite.Session.Session}
|
| + @type session: L{tlslite.session.Session}
|
| @param session: A TLS session to attempt to resume. If the
|
| resumption does not succeed, a full handshake will be
|
| performed.
|
|
|
| - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
| + @type settings: L{tlslite.handshakesettings.HandshakeSettings}
|
| @param settings: Various settings which can be used to control
|
| the ciphersuites, certificate types, and SSL/TLS versions
|
| offered by the client.
|
|
|
| - @type checker: L{tlslite.Checker.Checker}
|
| + @type checker: L{tlslite.checker.Checker}
|
| @param checker: A Checker instance. This instance will be
|
| invoked to examine the other party's authentication
|
| credentials, if the handshake completes succesfully.
|
| +
|
| + @type nextProtos: list of strings.
|
| + @param nextProtos: A list of upper layer protocols ordered by
|
| + preference, to use in the Next-Protocol Negotiation Extension.
|
| +
|
| + @type reqTack: bool
|
| + @param reqTack: Whether or not to send a "tack" TLS Extension,
|
| + requesting the server return a TackExtension if it has one.
|
| +
|
| + @type serverName: string
|
| + @param serverName: The ServerNameIndication TLS Extension.
|
|
|
| @type async: bool
|
| @param async: If False, this function will block until the
|
| @@ -281,276 +289,261 @@ class TLSConnection(TLSRecordLayer):
|
| @raise tlslite.errors.TLSAuthenticationError: If the checker
|
| doesn't like the other party's authentication credentials.
|
| """
|
| - handshaker = self._handshakeClientAsync(unknownParams=(srpCallback,
|
| - certCallback), session=session, settings=settings,
|
| - checker=checker)
|
| + handshaker = self._handshakeClientAsync(certParams=(certChain,
|
| + privateKey), session=session, settings=settings,
|
| + checker=checker, serverName=serverName,
|
| + nextProtos=nextProtos, reqTack=reqTack)
|
| + # The handshaker is a Python Generator which executes the handshake.
|
| + # It allows the handshake to be run in a "piecewise", asynchronous
|
| + # fashion, returning 1 when it is waiting to able to write, 0 when
|
| + # it is waiting to read.
|
| + #
|
| + # If 'async' is True, the generator is returned to the caller,
|
| + # otherwise it is executed to completion here.
|
| if async:
|
| return handshaker
|
| for result in handshaker:
|
| pass
|
|
|
| - def handshakeClientSharedKey(self, username, sharedKey, settings=None,
|
| - checker=None, async=False):
|
| - """Perform a shared-key handshake in the role of client.
|
| -
|
| - This function performs a shared-key handshake. Using shared
|
| - symmetric keys of high entropy (128 bits or greater) mutually
|
| - authenticates both parties to each other.
|
| -
|
| - TLS with shared-keys is non-standard. Most TLS
|
| - implementations don't support it. See
|
| - U{http://www.ietf.org/html.charters/tls-charter.html} for the
|
| - latest information on TLS with shared-keys. If the shared-keys
|
| - Internet-Draft changes or is superceded, TLS Lite will track
|
| - those changes, so the shared-key support in later versions of
|
| - TLS Lite may become incompatible with this version.
|
| -
|
| - Like any handshake function, this can be called on a closed
|
| - TLS connection, or on a TLS connection that is already open.
|
| - If called on an open connection it performs a re-handshake.
|
| -
|
| - If the function completes without raising an exception, the
|
| - TLS connection will be open and available for data transfer.
|
| -
|
| - If an exception is raised, the connection will have been
|
| - automatically closed (if it was ever open).
|
| -
|
| - @type username: str
|
| - @param username: The shared-key username.
|
| -
|
| - @type sharedKey: str
|
| - @param sharedKey: The shared key.
|
| -
|
| - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
| - @param settings: Various settings which can be used to control
|
| - the ciphersuites, certificate types, and SSL/TLS versions
|
| - offered by the client.
|
| -
|
| - @type checker: L{tlslite.Checker.Checker}
|
| - @param checker: A Checker instance. This instance will be
|
| - invoked to examine the other party's authentication
|
| - credentials, if the handshake completes succesfully.
|
| -
|
| - @type async: bool
|
| - @param async: If False, this function will block until the
|
| - handshake is completed. If True, this function will return a
|
| - generator. Successive invocations of the generator will
|
| - return 0 if it is waiting to read from the socket, 1 if it is
|
| - waiting to write to the socket, or will raise StopIteration if
|
| - the handshake operation is completed.
|
| -
|
| - @rtype: None or an iterable
|
| - @return: If 'async' is True, a generator object will be
|
| - returned.
|
|
|
| - @raise socket.error: If a socket error occurs.
|
| - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed
|
| - without a preceding alert.
|
| - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled.
|
| - @raise tlslite.errors.TLSAuthenticationError: If the checker
|
| - doesn't like the other party's authentication credentials.
|
| - """
|
| - handshaker = self._handshakeClientAsync(sharedKeyParams=(username,
|
| - sharedKey), settings=settings, checker=checker)
|
| - if async:
|
| - return handshaker
|
| - for result in handshaker:
|
| - pass
|
| -
|
| - def _handshakeClientAsync(self, srpParams=(), certParams=(),
|
| - unknownParams=(), sharedKeyParams=(),
|
| + def _handshakeClientAsync(self, srpParams=(), certParams=(), anonParams=(),
|
| session=None, settings=None, checker=None,
|
| - recursive=False):
|
| + nextProtos=None, serverName="", reqTack=True):
|
|
|
| handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams,
|
| - certParams=certParams, unknownParams=unknownParams,
|
| - sharedKeyParams=sharedKeyParams, session=session,
|
| - settings=settings, recursive=recursive)
|
| + certParams=certParams,
|
| + anonParams=anonParams,
|
| + session=session,
|
| + settings=settings,
|
| + serverName=serverName,
|
| + nextProtos=nextProtos,
|
| + reqTack=reqTack)
|
| for result in self._handshakeWrapperAsync(handshaker, checker):
|
| yield result
|
|
|
|
|
| - def _handshakeClientAsyncHelper(self, srpParams, certParams, unknownParams,
|
| - sharedKeyParams, session, settings, recursive):
|
| - if not recursive:
|
| - self._handshakeStart(client=True)
|
| + def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams,
|
| + session, settings, serverName, nextProtos, reqTack):
|
| +
|
| + self._handshakeStart(client=True)
|
|
|
| #Unpack parameters
|
| - srpUsername = None # srpParams
|
| - password = None # srpParams
|
| - clientCertChain = None # certParams
|
| - privateKey = None # certParams
|
| - srpCallback = None # unknownParams
|
| - certCallback = None # unknownParams
|
| - #session # sharedKeyParams (or session)
|
| - #settings # settings
|
| + srpUsername = None # srpParams[0]
|
| + password = None # srpParams[1]
|
| + clientCertChain = None # certParams[0]
|
| + privateKey = None # certParams[1]
|
|
|
| + # Allow only one of (srpParams, certParams, anonParams)
|
| if srpParams:
|
| + assert(not certParams)
|
| + assert(not anonParams)
|
| srpUsername, password = srpParams
|
| - elif certParams:
|
| + if certParams:
|
| + assert(not srpParams)
|
| + assert(not anonParams)
|
| clientCertChain, privateKey = certParams
|
| - elif unknownParams:
|
| - srpCallback, certCallback = unknownParams
|
| - elif sharedKeyParams:
|
| - session = Session()._createSharedKey(*sharedKeyParams)
|
| -
|
| - if not settings:
|
| - settings = HandshakeSettings()
|
| - settings = settings._filter()
|
| + if anonParams:
|
| + assert(not srpParams)
|
| + assert(not certParams)
|
|
|
| #Validate parameters
|
| if srpUsername and not password:
|
| raise ValueError("Caller passed a username but no password")
|
| if password and not srpUsername:
|
| raise ValueError("Caller passed a password but no username")
|
| -
|
| if clientCertChain and not privateKey:
|
| raise ValueError("Caller passed a certChain but no privateKey")
|
| if privateKey and not clientCertChain:
|
| raise ValueError("Caller passed a privateKey but no certChain")
|
| + if reqTack:
|
| + if not tackpyLoaded:
|
| + reqTack = False
|
| + if not settings or not settings.useExperimentalTackExtension:
|
| + reqTack = False
|
| + if nextProtos is not None:
|
| + if len(nextProtos) == 0:
|
| + raise ValueError("Caller passed no nextProtos")
|
| +
|
| + # Validates the settings and filters out any unsupported ciphers
|
| + # or crypto libraries that were requested
|
| + if not settings:
|
| + settings = HandshakeSettings()
|
| + settings = settings._filter()
|
|
|
| if clientCertChain:
|
| - foundType = False
|
| - try:
|
| - import cryptoIDlib.CertChain
|
| - if isinstance(clientCertChain, cryptoIDlib.CertChain.CertChain):
|
| - if "cryptoID" not in settings.certificateTypes:
|
| - raise ValueError("Client certificate doesn't "\
|
| - "match Handshake Settings")
|
| - settings.certificateTypes = ["cryptoID"]
|
| - foundType = True
|
| - except ImportError:
|
| - pass
|
| - if not foundType and isinstance(clientCertChain,
|
| - X509CertChain):
|
| - if "x509" not in settings.certificateTypes:
|
| - raise ValueError("Client certificate doesn't match "\
|
| - "Handshake Settings")
|
| - settings.certificateTypes = ["x509"]
|
| - foundType = True
|
| - if not foundType:
|
| + if not isinstance(clientCertChain, X509CertChain):
|
| raise ValueError("Unrecognized certificate type")
|
| -
|
| -
|
| + if "x509" not in settings.certificateTypes:
|
| + raise ValueError("Client certificate doesn't match "\
|
| + "Handshake Settings")
|
| +
|
| if session:
|
| + # session.valid() ensures session is resumable and has
|
| + # non-empty sessionID
|
| if not session.valid():
|
| session = None #ignore non-resumable sessions...
|
| - elif session.resumable and \
|
| - (session.srpUsername != srpUsername):
|
| - raise ValueError("Session username doesn't match")
|
| + elif session.resumable:
|
| + if session.srpUsername != srpUsername:
|
| + raise ValueError("Session username doesn't match")
|
| + if session.serverName != serverName:
|
| + raise ValueError("Session servername doesn't match")
|
|
|
| #Add Faults to parameters
|
| if srpUsername and self.fault == Fault.badUsername:
|
| srpUsername += "GARBAGE"
|
| if password and self.fault == Fault.badPassword:
|
| password += "GARBAGE"
|
| - if sharedKeyParams:
|
| - identifier = sharedKeyParams[0]
|
| - sharedKey = sharedKeyParams[1]
|
| - if self.fault == Fault.badIdentifier:
|
| - identifier += "GARBAGE"
|
| - session = Session()._createSharedKey(identifier, sharedKey)
|
| - elif self.fault == Fault.badSharedKey:
|
| - sharedKey += "GARBAGE"
|
| - session = Session()._createSharedKey(identifier, sharedKey)
|
| -
|
| -
|
| - #Initialize locals
|
| - serverCertChain = None
|
| - cipherSuite = 0
|
| - certificateType = CertificateType.x509
|
| - premasterSecret = None
|
|
|
| - #Get client nonce
|
| - clientRandom = getRandomBytes(32)
|
| + #Tentatively set the version to the client's minimum version.
|
| + #We'll use this for the ClientHello, and if an error occurs
|
| + #parsing the Server Hello, we'll use this version for the response
|
| + self.version = settings.maxVersion
|
| +
|
| + # OK Start sending messages!
|
| + # *****************************
|
| +
|
| + # Send the ClientHello.
|
| + for result in self._clientSendClientHello(settings, session,
|
| + srpUsername, srpParams, certParams,
|
| + anonParams, serverName, nextProtos,
|
| + reqTack):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + clientHello = result
|
| +
|
| + #Get the ServerHello.
|
| + for result in self._clientGetServerHello(settings, clientHello):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverHello = result
|
| + cipherSuite = serverHello.cipher_suite
|
| +
|
| + # Choose a matching Next Protocol from server list against ours
|
| + # (string or None)
|
| + nextProto = self._clientSelectNextProto(nextProtos, serverHello)
|
| +
|
| + #If the server elected to resume the session, it is handled here.
|
| + for result in self._clientResume(session, serverHello,
|
| + clientHello.random,
|
| + settings.cipherImplementations,
|
| + nextProto):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + if result == "resumed_and_finished":
|
| + self._handshakeDone(resumed=True)
|
| + return
|
| +
|
| + #If the server selected an SRP ciphersuite, the client finishes
|
| + #reading the post-ServerHello messages, then derives a
|
| + #premasterSecret and sends a corresponding ClientKeyExchange.
|
| + if cipherSuite in CipherSuite.srpAllSuites:
|
| + for result in self._clientSRPKeyExchange(\
|
| + settings, cipherSuite, serverHello.certificate_type,
|
| + srpUsername, password,
|
| + clientHello.random, serverHello.random,
|
| + serverHello.tackExt):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + (premasterSecret, serverCertChain, tackExt) = result
|
| +
|
| + #If the server selected an anonymous ciphersuite, the client
|
| + #finishes reading the post-ServerHello messages.
|
| + elif cipherSuite in CipherSuite.anonSuites:
|
| + for result in self._clientAnonKeyExchange(settings, cipherSuite,
|
| + clientHello.random, serverHello.random):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + (premasterSecret, serverCertChain, tackExt) = result
|
| +
|
| + #If the server selected a certificate-based RSA ciphersuite,
|
| + #the client finishes reading the post-ServerHello messages. If
|
| + #a CertificateRequest message was sent, the client responds with
|
| + #a Certificate message containing its certificate chain (if any),
|
| + #and also produces a CertificateVerify message that signs the
|
| + #ClientKeyExchange.
|
| + else:
|
| + for result in self._clientRSAKeyExchange(settings, cipherSuite,
|
| + clientCertChain, privateKey,
|
| + serverHello.certificate_type,
|
| + clientHello.random, serverHello.random,
|
| + serverHello.tackExt):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + (premasterSecret, serverCertChain, clientCertChain,
|
| + tackExt) = result
|
| +
|
| + #After having previously sent a ClientKeyExchange, the client now
|
| + #initiates an exchange of Finished messages.
|
| + for result in self._clientFinished(premasterSecret,
|
| + clientHello.random,
|
| + serverHello.random,
|
| + cipherSuite, settings.cipherImplementations,
|
| + nextProto):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + masterSecret = result
|
| +
|
| + # Create the session object which is used for resumptions
|
| + self.session = Session()
|
| + self.session.create(masterSecret, serverHello.session_id, cipherSuite,
|
| + srpUsername, clientCertChain, serverCertChain,
|
| + tackExt, serverHello.tackExt!=None, serverName)
|
| + self._handshakeDone(resumed=False)
|
| +
|
|
|
| + def _clientSendClientHello(self, settings, session, srpUsername,
|
| + srpParams, certParams, anonParams,
|
| + serverName, nextProtos, reqTack):
|
| #Initialize acceptable ciphersuites
|
| - cipherSuites = []
|
| + cipherSuites = [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
|
| if srpParams:
|
| - cipherSuites += CipherSuite.getSrpRsaSuites(settings.cipherNames)
|
| - cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames)
|
| + cipherSuites += CipherSuite.getSrpAllSuites(settings)
|
| elif certParams:
|
| - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames)
|
| - elif unknownParams:
|
| - if srpCallback:
|
| - cipherSuites += \
|
| - CipherSuite.getSrpRsaSuites(settings.cipherNames)
|
| - cipherSuites += \
|
| - CipherSuite.getSrpSuites(settings.cipherNames)
|
| - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames)
|
| - elif sharedKeyParams:
|
| - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames)
|
| + cipherSuites += CipherSuite.getCertSuites(settings)
|
| + elif anonParams:
|
| + cipherSuites += CipherSuite.getAnonSuites(settings)
|
| else:
|
| - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames)
|
| + assert(False)
|
|
|
| #Initialize acceptable certificate types
|
| certificateTypes = settings._getCertificateTypes()
|
| -
|
| - #Tentatively set the version to the client's minimum version.
|
| - #We'll use this for the ClientHello, and if an error occurs
|
| - #parsing the Server Hello, we'll use this version for the response
|
| - self.version = settings.maxVersion
|
| -
|
| +
|
| #Either send ClientHello (with a resumable session)...
|
| - if session:
|
| - #If it's a resumable (i.e. not a shared-key session), then its
|
| + if session and session.sessionID:
|
| + #If it's resumable, then its
|
| #ciphersuite must be one of the acceptable ciphersuites
|
| - if (not sharedKeyParams) and \
|
| - session.cipherSuite not in cipherSuites:
|
| + if session.cipherSuite not in cipherSuites:
|
| raise ValueError("Session's cipher suite not consistent "\
|
| "with parameters")
|
| else:
|
| clientHello = ClientHello()
|
| - clientHello.create(settings.maxVersion, clientRandom,
|
| + clientHello.create(settings.maxVersion, getRandomBytes(32),
|
| session.sessionID, cipherSuites,
|
| - certificateTypes, session.srpUsername)
|
| + certificateTypes,
|
| + session.srpUsername,
|
| + reqTack, nextProtos is not None,
|
| + session.serverName)
|
|
|
| #Or send ClientHello (without)
|
| else:
|
| clientHello = ClientHello()
|
| - clientHello.create(settings.maxVersion, clientRandom,
|
| - createByteArraySequence([]), cipherSuites,
|
| - certificateTypes, srpUsername)
|
| + clientHello.create(settings.maxVersion, getRandomBytes(32),
|
| + bytearray(0), cipherSuites,
|
| + certificateTypes,
|
| + srpUsername,
|
| + reqTack, nextProtos is not None,
|
| + serverName)
|
| for result in self._sendMsg(clientHello):
|
| yield result
|
| + yield clientHello
|
|
|
| - #Get ServerHello (or missing_srp_username)
|
| - for result in self._getMsg((ContentType.handshake,
|
| - ContentType.alert),
|
| - HandshakeType.server_hello):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - msg = result
|
| -
|
| - if isinstance(msg, ServerHello):
|
| - serverHello = msg
|
| - elif isinstance(msg, Alert):
|
| - alert = msg
|
| -
|
| - #If it's not a missing_srp_username, re-raise
|
| - if alert.description != AlertDescription.missing_srp_username:
|
| - self._shutdown(False)
|
| - raise TLSRemoteAlert(alert)
|
| -
|
| - #If we're not in SRP callback mode, we won't have offered SRP
|
| - #without a username, so we shouldn't get this alert
|
| - if not srpCallback:
|
| - for result in self._sendError(\
|
| - AlertDescription.unexpected_message):
|
| - yield result
|
| - srpParams = srpCallback()
|
| - #If the callback returns None, cancel the handshake
|
| - if srpParams == None:
|
| - for result in self._sendError(AlertDescription.user_canceled):
|
| - yield result
|
|
|
| - #Recursively perform handshake
|
| - for result in self._handshakeClientAsyncHelper(srpParams,
|
| - None, None, None, None, settings, True):
|
| - yield result
|
| - return
|
| + def _clientGetServerHello(self, settings, clientHello):
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_hello):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverHello = result
|
|
|
| #Get the server version. Do this before anything else, so any
|
| #error alerts will use the server's version
|
| @@ -570,12 +563,12 @@ class TLSConnection(TLSRecordLayer):
|
| AlertDescription.protocol_version,
|
| "Too new version: %s" % str(serverHello.server_version)):
|
| yield result
|
| - if serverHello.cipher_suite not in cipherSuites:
|
| + if serverHello.cipher_suite not in clientHello.cipher_suites:
|
| for result in self._sendError(\
|
| AlertDescription.illegal_parameter,
|
| "Server responded with incorrect ciphersuite"):
|
| yield result
|
| - if serverHello.certificate_type not in certificateTypes:
|
| + if serverHello.certificate_type not in clientHello.certificate_types:
|
| for result in self._sendError(\
|
| AlertDescription.illegal_parameter,
|
| "Server responded with incorrect certificate type"):
|
| @@ -585,365 +578,401 @@ class TLSConnection(TLSRecordLayer):
|
| AlertDescription.illegal_parameter,
|
| "Server responded with incorrect compression method"):
|
| yield result
|
| + if serverHello.tackExt:
|
| + if not clientHello.tack:
|
| + for result in self._sendError(\
|
| + AlertDescription.illegal_parameter,
|
| + "Server responded with unrequested Tack Extension"):
|
| + yield result
|
| + if serverHello.next_protos and not clientHello.supports_npn:
|
| + for result in self._sendError(\
|
| + AlertDescription.illegal_parameter,
|
| + "Server responded with unrequested NPN Extension"):
|
| + yield result
|
| + if not serverHello.tackExt.verifySignatures():
|
| + for result in self._sendError(\
|
| + AlertDescription.decrypt_error,
|
| + "TackExtension contains an invalid signature"):
|
| + yield result
|
| + yield serverHello
|
|
|
| - #Get the server nonce
|
| - serverRandom = serverHello.random
|
| -
|
| + def _clientSelectNextProto(self, nextProtos, serverHello):
|
| + # nextProtos is None or non-empty list of strings
|
| + # serverHello.next_protos is None or possibly-empty list of strings
|
| + #
|
| + # !!! We assume the client may have specified nextProtos as a list of
|
| + # strings so we convert them to bytearrays (it's awkward to require
|
| + # the user to specify a list of bytearrays or "bytes", and in
|
| + # Python 2.6 bytes() is just an alias for str() anyways...
|
| + if nextProtos is not None and serverHello.next_protos is not None:
|
| + for p in nextProtos:
|
| + if bytearray(p) in serverHello.next_protos:
|
| + return bytearray(p)
|
| + else:
|
| + # If the client doesn't support any of server's protocols,
|
| + # or the server doesn't advertise any (next_protos == [])
|
| + # the client SHOULD select the first protocol it supports.
|
| + return bytearray(nextProtos[0])
|
| + return None
|
| +
|
| + def _clientResume(self, session, serverHello, clientRandom,
|
| + cipherImplementations, nextProto):
|
| #If the server agrees to resume
|
| if session and session.sessionID and \
|
| - serverHello.session_id == session.sessionID:
|
| + serverHello.session_id == session.sessionID:
|
|
|
| - #If a shared-key, we're flexible about suites; otherwise the
|
| - #server-chosen suite has to match the session's suite
|
| - if sharedKeyParams:
|
| - session.cipherSuite = serverHello.cipher_suite
|
| - elif serverHello.cipher_suite != session.cipherSuite:
|
| + if serverHello.cipher_suite != session.cipherSuite:
|
| for result in self._sendError(\
|
| AlertDescription.illegal_parameter,\
|
| "Server's ciphersuite doesn't match session"):
|
| yield result
|
|
|
| + #Calculate pending connection states
|
| + self._calcPendingStates(session.cipherSuite,
|
| + session.masterSecret,
|
| + clientRandom, serverHello.random,
|
| + cipherImplementations)
|
| +
|
| + #Exchange ChangeCipherSpec and Finished messages
|
| + for result in self._getFinished(session.masterSecret):
|
| + yield result
|
| + for result in self._sendFinished(session.masterSecret, nextProto):
|
| + yield result
|
| +
|
| #Set the session for this connection
|
| self.session = session
|
| + yield "resumed_and_finished"
|
| +
|
| + def _clientSRPKeyExchange(self, settings, cipherSuite, certificateType,
|
| + srpUsername, password,
|
| + clientRandom, serverRandom, tackExt):
|
| +
|
| + #If the server chose an SRP+RSA suite...
|
| + if cipherSuite in CipherSuite.srpCertSuites:
|
| + #Get Certificate, ServerKeyExchange, ServerHelloDone
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.certificate, certificateType):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverCertificate = result
|
| + else:
|
| + serverCertificate = None
|
|
|
| - #Calculate pending connection states
|
| - self._calcPendingStates(clientRandom, serverRandom,
|
| - settings.cipherImplementations)
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_key_exchange, cipherSuite):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverKeyExchange = result
|
|
|
| - #Exchange ChangeCipherSpec and Finished messages
|
| - for result in self._getChangeCipherSpec():
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_hello_done):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverHelloDone = result
|
| +
|
| + #Calculate SRP premaster secret
|
| + #Get and check the server's group parameters and B value
|
| + N = serverKeyExchange.srp_N
|
| + g = serverKeyExchange.srp_g
|
| + s = serverKeyExchange.srp_s
|
| + B = serverKeyExchange.srp_B
|
| +
|
| + if (g,N) not in goodGroupParameters:
|
| + for result in self._sendError(\
|
| + AlertDescription.insufficient_security,
|
| + "Unknown group parameters"):
|
| + yield result
|
| + if numBits(N) < settings.minKeySize:
|
| + for result in self._sendError(\
|
| + AlertDescription.insufficient_security,
|
| + "N value is too small: %d" % numBits(N)):
|
| yield result
|
| - for result in self._getFinished():
|
| + if numBits(N) > settings.maxKeySize:
|
| + for result in self._sendError(\
|
| + AlertDescription.insufficient_security,
|
| + "N value is too large: %d" % numBits(N)):
|
| yield result
|
| - for result in self._sendFinished():
|
| + if B % N == 0:
|
| + for result in self._sendError(\
|
| + AlertDescription.illegal_parameter,
|
| + "Suspicious B value"):
|
| yield result
|
|
|
| - #Mark the connection as open
|
| - self._handshakeDone(resumed=True)
|
| + #Check the server's signature, if server chose an
|
| + #SRP+RSA suite
|
| + serverCertChain = None
|
| + if cipherSuite in CipherSuite.srpCertSuites:
|
| + #Hash ServerKeyExchange/ServerSRPParams
|
| + hashBytes = serverKeyExchange.hash(clientRandom, serverRandom)
|
|
|
| - #If server DOES NOT agree to resume
|
| - else:
|
| + #Extract signature bytes from ServerKeyExchange
|
| + sigBytes = serverKeyExchange.signature
|
| + if len(sigBytes) == 0:
|
| + for result in self._sendError(\
|
| + AlertDescription.illegal_parameter,
|
| + "Server sent an SRP ServerKeyExchange "\
|
| + "message without a signature"):
|
| + yield result
|
|
|
| - if sharedKeyParams:
|
| + # Get server's public key from the Certificate message
|
| + # Also validate the chain against the ServerHello's TACKext (if any)
|
| + # If none, and a TACK cert is present, return its TACKext
|
| + for result in self._clientGetKeyFromChain(serverCertificate,
|
| + settings, tackExt):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + publicKey, serverCertChain, tackExt = result
|
| +
|
| + #Verify signature
|
| + if not publicKey.verify(sigBytes, hashBytes):
|
| for result in self._sendError(\
|
| - AlertDescription.user_canceled,
|
| - "Was expecting a shared-key resumption"):
|
| + AlertDescription.decrypt_error,
|
| + "Signature failed to verify"):
|
| yield result
|
|
|
| - #We've already validated these
|
| - cipherSuite = serverHello.cipher_suite
|
| - certificateType = serverHello.certificate_type
|
| + #Calculate client's ephemeral DH values (a, A)
|
| + a = bytesToNumber(getRandomBytes(32))
|
| + A = powMod(g, a, N)
|
|
|
| - #If the server chose an SRP suite...
|
| - if cipherSuite in CipherSuite.srpSuites:
|
| - #Get ServerKeyExchange, ServerHelloDone
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.server_key_exchange, cipherSuite):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverKeyExchange = result
|
| + #Calculate client's static DH values (x, v)
|
| + x = makeX(s, bytearray(srpUsername, "utf-8"),
|
| + bytearray(password, "utf-8"))
|
| + v = powMod(g, x, N)
|
|
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.server_hello_done):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverHelloDone = result
|
| + #Calculate u
|
| + u = makeU(N, A, B)
|
|
|
| - #If the server chose an SRP+RSA suite...
|
| - elif cipherSuite in CipherSuite.srpRsaSuites:
|
| - #Get Certificate, ServerKeyExchange, ServerHelloDone
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.certificate, certificateType):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverCertificate = result
|
| + #Calculate premaster secret
|
| + k = makeK(N, g)
|
| + S = powMod((B - (k*v)) % N, a+(u*x), N)
|
|
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.server_key_exchange, cipherSuite):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverKeyExchange = result
|
| + if self.fault == Fault.badA:
|
| + A = N
|
| + S = 0
|
| +
|
| + premasterSecret = numberToByteArray(S)
|
|
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.server_hello_done):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverHelloDone = result
|
| + #Send ClientKeyExchange
|
| + for result in self._sendMsg(\
|
| + ClientKeyExchange(cipherSuite).createSRP(A)):
|
| + yield result
|
| + yield (premasterSecret, serverCertChain, tackExt)
|
| +
|
|
|
| - #If the server chose an RSA suite...
|
| - elif cipherSuite in CipherSuite.rsaSuites:
|
| - #Get Certificate[, CertificateRequest], ServerHelloDone
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.certificate, certificateType):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverCertificate = result
|
| -
|
| - for result in self._getMsg(ContentType.handshake,
|
| - (HandshakeType.server_hello_done,
|
| - HandshakeType.certificate_request)):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - msg = result
|
| -
|
| - certificateRequest = None
|
| - if isinstance(msg, CertificateRequest):
|
| - certificateRequest = msg
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.server_hello_done):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - serverHelloDone = result
|
| - elif isinstance(msg, ServerHelloDone):
|
| - serverHelloDone = msg
|
| - else:
|
| - raise AssertionError()
|
| + def _clientRSAKeyExchange(self, settings, cipherSuite,
|
| + clientCertChain, privateKey,
|
| + certificateType,
|
| + clientRandom, serverRandom,
|
| + tackExt):
|
|
|
| + #Get Certificate[, CertificateRequest], ServerHelloDone
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.certificate, certificateType):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverCertificate = result
|
|
|
| - #Calculate SRP premaster secret, if server chose an SRP or
|
| - #SRP+RSA suite
|
| - if cipherSuite in CipherSuite.srpSuites + \
|
| - CipherSuite.srpRsaSuites:
|
| - #Get and check the server's group parameters and B value
|
| - N = serverKeyExchange.srp_N
|
| - g = serverKeyExchange.srp_g
|
| - s = serverKeyExchange.srp_s
|
| - B = serverKeyExchange.srp_B
|
| + # Get CertificateRequest or ServerHelloDone
|
| + for result in self._getMsg(ContentType.handshake,
|
| + (HandshakeType.server_hello_done,
|
| + HandshakeType.certificate_request)):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + msg = result
|
| + certificateRequest = None
|
| + if isinstance(msg, CertificateRequest):
|
| + certificateRequest = msg
|
| + # We got CertificateRequest, so this must be ServerHelloDone
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_hello_done):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverHelloDone = result
|
| + elif isinstance(msg, ServerHelloDone):
|
| + serverHelloDone = msg
|
| +
|
| + # Get server's public key from the Certificate message
|
| + # Also validate the chain against the ServerHello's TACKext (if any)
|
| + # If none, and a TACK cert is present, return its TACKext
|
| + for result in self._clientGetKeyFromChain(serverCertificate,
|
| + settings, tackExt):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + publicKey, serverCertChain, tackExt = result
|
| +
|
| + #Calculate premaster secret
|
| + premasterSecret = getRandomBytes(48)
|
| + premasterSecret[0] = settings.maxVersion[0]
|
| + premasterSecret[1] = settings.maxVersion[1]
|
| +
|
| + if self.fault == Fault.badPremasterPadding:
|
| + premasterSecret[0] = 5
|
| + if self.fault == Fault.shortPremasterSecret:
|
| + premasterSecret = premasterSecret[:-1]
|
| +
|
| + #Encrypt premaster secret to server's public key
|
| + encryptedPreMasterSecret = publicKey.encrypt(premasterSecret)
|
| +
|
| + #If client authentication was requested, send Certificate
|
| + #message, either with certificates or empty
|
| + if certificateRequest:
|
| + clientCertificate = Certificate(certificateType)
|
|
|
| - if (g,N) not in goodGroupParameters:
|
| - for result in self._sendError(\
|
| - AlertDescription.untrusted_srp_parameters,
|
| - "Unknown group parameters"):
|
| - yield result
|
| - if numBits(N) < settings.minKeySize:
|
| - for result in self._sendError(\
|
| - AlertDescription.untrusted_srp_parameters,
|
| - "N value is too small: %d" % numBits(N)):
|
| - yield result
|
| - if numBits(N) > settings.maxKeySize:
|
| - for result in self._sendError(\
|
| - AlertDescription.untrusted_srp_parameters,
|
| - "N value is too large: %d" % numBits(N)):
|
| - yield result
|
| - if B % N == 0:
|
| + if clientCertChain:
|
| + #Check to make sure we have the same type of
|
| + #certificates the server requested
|
| + wrongType = False
|
| + if certificateType == CertificateType.x509:
|
| + if not isinstance(clientCertChain, X509CertChain):
|
| + wrongType = True
|
| + if wrongType:
|
| for result in self._sendError(\
|
| - AlertDescription.illegal_parameter,
|
| - "Suspicious B value"):
|
| - yield result
|
| -
|
| - #Check the server's signature, if server chose an
|
| - #SRP+RSA suite
|
| - if cipherSuite in CipherSuite.srpRsaSuites:
|
| - #Hash ServerKeyExchange/ServerSRPParams
|
| - hashBytes = serverKeyExchange.hash(clientRandom,
|
| - serverRandom)
|
| -
|
| - #Extract signature bytes from ServerKeyExchange
|
| - sigBytes = serverKeyExchange.signature
|
| - if len(sigBytes) == 0:
|
| - for result in self._sendError(\
|
| - AlertDescription.illegal_parameter,
|
| - "Server sent an SRP ServerKeyExchange "\
|
| - "message without a signature"):
|
| - yield result
|
| -
|
| - #Get server's public key from the Certificate message
|
| - for result in self._getKeyFromChain(serverCertificate,
|
| - settings):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - publicKey, serverCertChain = result
|
| -
|
| - #Verify signature
|
| - if not publicKey.verify(sigBytes, hashBytes):
|
| - for result in self._sendError(\
|
| - AlertDescription.decrypt_error,
|
| - "Signature failed to verify"):
|
| - yield result
|
| -
|
| -
|
| - #Calculate client's ephemeral DH values (a, A)
|
| - a = bytesToNumber(getRandomBytes(32))
|
| - A = powMod(g, a, N)
|
| -
|
| - #Calculate client's static DH values (x, v)
|
| - x = makeX(bytesToString(s), srpUsername, password)
|
| - v = powMod(g, x, N)
|
| -
|
| - #Calculate u
|
| - u = makeU(N, A, B)
|
| -
|
| - #Calculate premaster secret
|
| - k = makeK(N, g)
|
| - S = powMod((B - (k*v)) % N, a+(u*x), N)
|
| -
|
| - if self.fault == Fault.badA:
|
| - A = N
|
| - S = 0
|
| - premasterSecret = numberToBytes(S)
|
| -
|
| - #Send ClientKeyExchange
|
| - for result in self._sendMsg(\
|
| - ClientKeyExchange(cipherSuite).createSRP(A)):
|
| - yield result
|
| -
|
| -
|
| - #Calculate RSA premaster secret, if server chose an RSA suite
|
| - elif cipherSuite in CipherSuite.rsaSuites:
|
| -
|
| - #Handle the presence of a CertificateRequest
|
| - if certificateRequest:
|
| - if unknownParams and certCallback:
|
| - certParamsNew = certCallback()
|
| - if certParamsNew:
|
| - clientCertChain, privateKey = certParamsNew
|
| -
|
| - #Get server's public key from the Certificate message
|
| - for result in self._getKeyFromChain(serverCertificate,
|
| - settings):
|
| - if result in (0,1):
|
| + AlertDescription.handshake_failure,
|
| + "Client certificate is of wrong type"):
|
| yield result
|
| - else:
|
| - break
|
| - publicKey, serverCertChain = result
|
| -
|
| -
|
| - #Calculate premaster secret
|
| - premasterSecret = getRandomBytes(48)
|
| - premasterSecret[0] = settings.maxVersion[0]
|
| - premasterSecret[1] = settings.maxVersion[1]
|
| -
|
| - if self.fault == Fault.badPremasterPadding:
|
| - premasterSecret[0] = 5
|
| - if self.fault == Fault.shortPremasterSecret:
|
| - premasterSecret = premasterSecret[:-1]
|
| -
|
| - #Encrypt premaster secret to server's public key
|
| - encryptedPreMasterSecret = publicKey.encrypt(premasterSecret)
|
| -
|
| - #If client authentication was requested, send Certificate
|
| - #message, either with certificates or empty
|
| - if certificateRequest:
|
| - clientCertificate = Certificate(certificateType)
|
| -
|
| - if clientCertChain:
|
| - #Check to make sure we have the same type of
|
| - #certificates the server requested
|
| - wrongType = False
|
| - if certificateType == CertificateType.x509:
|
| - if not isinstance(clientCertChain, X509CertChain):
|
| - wrongType = True
|
| - elif certificateType == CertificateType.cryptoID:
|
| - if not isinstance(clientCertChain,
|
| - cryptoIDlib.CertChain.CertChain):
|
| - wrongType = True
|
| - if wrongType:
|
| - for result in self._sendError(\
|
| - AlertDescription.handshake_failure,
|
| - "Client certificate is of wrong type"):
|
| - yield result
|
| -
|
| - clientCertificate.create(clientCertChain)
|
|
|
| - for result in self._sendMsg(clientCertificate):
|
| - yield result
|
| - else:
|
| - #The server didn't request client auth, so we
|
| - #zeroize these so the clientCertChain won't be
|
| - #stored in the session.
|
| - privateKey = None
|
| - clientCertChain = None
|
| -
|
| - #Send ClientKeyExchange
|
| - clientKeyExchange = ClientKeyExchange(cipherSuite,
|
| - self.version)
|
| - clientKeyExchange.createRSA(encryptedPreMasterSecret)
|
| - for result in self._sendMsg(clientKeyExchange):
|
| - yield result
|
| + clientCertificate.create(clientCertChain)
|
| + for result in self._sendMsg(clientCertificate):
|
| + yield result
|
| + else:
|
| + #The server didn't request client auth, so we
|
| + #zeroize these so the clientCertChain won't be
|
| + #stored in the session.
|
| + privateKey = None
|
| + clientCertChain = None
|
| +
|
| + #Send ClientKeyExchange
|
| + clientKeyExchange = ClientKeyExchange(cipherSuite,
|
| + self.version)
|
| + clientKeyExchange.createRSA(encryptedPreMasterSecret)
|
| + for result in self._sendMsg(clientKeyExchange):
|
| + yield result
|
|
|
| - #If client authentication was requested and we have a
|
| - #private key, send CertificateVerify
|
| - if certificateRequest and privateKey:
|
| - if self.version == (3,0):
|
| - #Create a temporary session object, just for the
|
| - #purpose of creating the CertificateVerify
|
| - session = Session()
|
| - session._calcMasterSecret(self.version,
|
| - premasterSecret,
|
| - clientRandom,
|
| - serverRandom)
|
| - verifyBytes = self._calcSSLHandshakeHash(\
|
| - session.masterSecret, "")
|
| - elif self.version in ((3,1), (3,2)):
|
| - verifyBytes = stringToBytes(\
|
| - self._handshake_md5.digest() + \
|
| - self._handshake_sha.digest())
|
| - if self.fault == Fault.badVerifyMessage:
|
| - verifyBytes[0] = ((verifyBytes[0]+1) % 256)
|
| - signedBytes = privateKey.sign(verifyBytes)
|
| - certificateVerify = CertificateVerify()
|
| - certificateVerify.create(signedBytes)
|
| - for result in self._sendMsg(certificateVerify):
|
| - yield result
|
| + #If client authentication was requested and we have a
|
| + #private key, send CertificateVerify
|
| + if certificateRequest and privateKey:
|
| + if self.version == (3,0):
|
| + masterSecret = calcMasterSecret(self.version,
|
| + premasterSecret,
|
| + clientRandom,
|
| + serverRandom)
|
| + verifyBytes = self._calcSSLHandshakeHash(masterSecret, b"")
|
| + elif self.version in ((3,1), (3,2)):
|
| + verifyBytes = self._handshake_md5.digest() + \
|
| + self._handshake_sha.digest()
|
| + if self.fault == Fault.badVerifyMessage:
|
| + verifyBytes[0] = ((verifyBytes[0]+1) % 256)
|
| + signedBytes = privateKey.sign(verifyBytes)
|
| + certificateVerify = CertificateVerify()
|
| + certificateVerify.create(signedBytes)
|
| + for result in self._sendMsg(certificateVerify):
|
| + yield result
|
| + yield (premasterSecret, serverCertChain, clientCertChain, tackExt)
|
|
|
| + def _clientAnonKeyExchange(self, settings, cipherSuite, clientRandom,
|
| + serverRandom):
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_key_exchange, cipherSuite):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverKeyExchange = result
|
|
|
| - #Create the session object
|
| - self.session = Session()
|
| - self.session._calcMasterSecret(self.version, premasterSecret,
|
| - clientRandom, serverRandom)
|
| - self.session.sessionID = serverHello.session_id
|
| - self.session.cipherSuite = cipherSuite
|
| - self.session.srpUsername = srpUsername
|
| - self.session.clientCertChain = clientCertChain
|
| - self.session.serverCertChain = serverCertChain
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.server_hello_done):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + serverHelloDone = result
|
| +
|
| + #calculate Yc
|
| + dh_p = serverKeyExchange.dh_p
|
| + dh_g = serverKeyExchange.dh_g
|
| + dh_Xc = bytesToNumber(getRandomBytes(32))
|
| + dh_Ys = serverKeyExchange.dh_Ys
|
| + dh_Yc = powMod(dh_g, dh_Xc, dh_p)
|
| +
|
| + #Send ClientKeyExchange
|
| + for result in self._sendMsg(\
|
| + ClientKeyExchange(cipherSuite, self.version).createDH(dh_Yc)):
|
| + yield result
|
| +
|
| + #Calculate premaster secret
|
| + S = powMod(dh_Ys, dh_Xc, dh_p)
|
| + premasterSecret = numberToByteArray(S)
|
| +
|
| + yield (premasterSecret, None, None)
|
| +
|
| + def _clientFinished(self, premasterSecret, clientRandom, serverRandom,
|
| + cipherSuite, cipherImplementations, nextProto):
|
| +
|
| + masterSecret = calcMasterSecret(self.version, premasterSecret,
|
| + clientRandom, serverRandom)
|
| + self._calcPendingStates(cipherSuite, masterSecret,
|
| + clientRandom, serverRandom,
|
| + cipherImplementations)
|
|
|
| - #Calculate pending connection states
|
| - self._calcPendingStates(clientRandom, serverRandom,
|
| - settings.cipherImplementations)
|
| + #Exchange ChangeCipherSpec and Finished messages
|
| + for result in self._sendFinished(masterSecret, nextProto):
|
| + yield result
|
| + for result in self._getFinished(masterSecret, nextProto=nextProto):
|
| + yield result
|
| + yield masterSecret
|
|
|
| - #Exchange ChangeCipherSpec and Finished messages
|
| - for result in self._sendFinished():
|
| + def _clientGetKeyFromChain(self, certificate, settings, tackExt=None):
|
| + #Get and check cert chain from the Certificate message
|
| + certChain = certificate.certChain
|
| + if not certChain or certChain.getNumCerts() == 0:
|
| + for result in self._sendError(AlertDescription.illegal_parameter,
|
| + "Other party sent a Certificate message without "\
|
| + "certificates"):
|
| yield result
|
| - for result in self._getChangeCipherSpec():
|
| +
|
| + #Get and check public key from the cert chain
|
| + publicKey = certChain.getEndEntityPublicKey()
|
| + if len(publicKey) < settings.minKeySize:
|
| + for result in self._sendError(AlertDescription.handshake_failure,
|
| + "Other party's public key too small: %d" % len(publicKey)):
|
| yield result
|
| - for result in self._getFinished():
|
| + if len(publicKey) > settings.maxKeySize:
|
| + for result in self._sendError(AlertDescription.handshake_failure,
|
| + "Other party's public key too large: %d" % len(publicKey)):
|
| yield result
|
| +
|
| + # If there's no TLS Extension, look for a TACK cert
|
| + if tackpyLoaded:
|
| + if not tackExt:
|
| + tackExt = certChain.getTackExt()
|
| +
|
| + # If there's a TACK (whether via TLS or TACK Cert), check that it
|
| + # matches the cert chain
|
| + if tackExt and tackExt.tacks:
|
| + for tack in tackExt.tacks:
|
| + if not certChain.checkTack(tack):
|
| + for result in self._sendError(
|
| + AlertDescription.illegal_parameter,
|
| + "Other party's TACK doesn't match their public key"):
|
| + yield result
|
| +
|
| + yield publicKey, certChain, tackExt
|
|
|
| - #Mark the connection as open
|
| - self.session._setResumable(True)
|
| - self._handshakeDone(resumed=False)
|
|
|
| + #*********************************************************
|
| + # Server Handshake Functions
|
| + #*********************************************************
|
|
|
|
|
| - def handshakeServer(self, sharedKeyDB=None, verifierDB=None,
|
| + def handshakeServer(self, verifierDB=None,
|
| certChain=None, privateKey=None, reqCert=False,
|
| sessionCache=None, settings=None, checker=None,
|
| - reqCAs=None, tlsIntolerant=0,
|
| - signedCertTimestamps=None, fallbackSCSV=False,
|
| - ocspResponse=None):
|
| + reqCAs = None,
|
| + tacks=None, activationFlags=0,
|
| + nextProtos=None, anon=False,
|
| + tlsIntolerant=None, signedCertTimestamps=None,
|
| + fallbackSCSV=False, ocspResponse=None):
|
| """Perform a handshake in the role of server.
|
|
|
| This function performs an SSL or TLS handshake. Depending on
|
| the arguments and the behavior of the client, this function can
|
| - perform a shared-key, SRP, or certificate-based handshake. It
|
| + perform an SRP, or certificate-based handshake. It
|
| can also perform a combined SRP and server-certificate
|
| handshake.
|
|
|
| @@ -961,67 +990,62 @@ class TLSConnection(TLSRecordLayer):
|
| If an exception is raised, the connection will have been
|
| automatically closed (if it was ever open).
|
|
|
| - @type sharedKeyDB: L{tlslite.SharedKeyDB.SharedKeyDB}
|
| - @param sharedKeyDB: A database of shared symmetric keys
|
| - associated with usernames. If the client performs a
|
| - shared-key handshake, the session's sharedKeyUsername
|
| - attribute will be set.
|
| -
|
| - @type verifierDB: L{tlslite.VerifierDB.VerifierDB}
|
| + @type verifierDB: L{tlslite.verifierdb.VerifierDB}
|
| @param verifierDB: A database of SRP password verifiers
|
| associated with usernames. If the client performs an SRP
|
| handshake, the session's srpUsername attribute will be set.
|
|
|
| - @type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
| - L{cryptoIDlib.CertChain.CertChain}
|
| + @type certChain: L{tlslite.x509certchain.X509CertChain}
|
| @param certChain: The certificate chain to be used if the
|
| client requests server certificate authentication.
|
|
|
| - @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
| + @type privateKey: L{tlslite.utils.rsakey.RSAKey}
|
| @param privateKey: The private key to be used if the client
|
| requests server certificate authentication.
|
|
|
| @type reqCert: bool
|
| @param reqCert: Whether to request client certificate
|
| authentication. This only applies if the client chooses server
|
| - certificate authentication; if the client chooses SRP or
|
| - shared-key authentication, this will be ignored. If the client
|
| + certificate authentication; if the client chooses SRP
|
| + authentication, this will be ignored. If the client
|
| performs a client certificate authentication, the sessions's
|
| clientCertChain attribute will be set.
|
|
|
| - @type sessionCache: L{tlslite.SessionCache.SessionCache}
|
| + @type sessionCache: L{tlslite.sessioncache.SessionCache}
|
| @param sessionCache: An in-memory cache of resumable sessions.
|
| The client can resume sessions from this cache. Alternatively,
|
| if the client performs a full handshake, a new session will be
|
| added to the cache.
|
|
|
| - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
| + @type settings: L{tlslite.handshakesettings.HandshakeSettings}
|
| @param settings: Various settings which can be used to control
|
| the ciphersuites and SSL/TLS version chosen by the server.
|
|
|
| - @type checker: L{tlslite.Checker.Checker}
|
| + @type checker: L{tlslite.checker.Checker}
|
| @param checker: A Checker instance. This instance will be
|
| invoked to examine the other party's authentication
|
| credentials, if the handshake completes succesfully.
|
| -
|
| - @type reqCAs: list of L{array.array} of unsigned bytes
|
| +
|
| + @type reqCAs: list of L{bytearray} of unsigned bytes
|
| @param reqCAs: A collection of DER-encoded DistinguishedNames that
|
| will be sent along with a certificate request. This does not affect
|
| - verification.
|
| + verification.
|
| +
|
| + @type nextProtos: list of strings.
|
| + @param nextProtos: A list of upper layer protocols to expose to the
|
| + clients through the Next-Protocol Negotiation Extension,
|
| + if they support it.
|
| +
|
| + @type tlsIntolerant: (int, int) or None
|
| + @param tlsIntolerant: If tlsIntolerant is not None, the server will
|
| + simulate TLS version intolerance by returning a fatal handshake_failure
|
| + alert to all TLS versions tlsIntolerant or higher.
|
|
|
| @type signedCertTimestamps: str
|
| @param signedCertTimestamps: A SignedCertificateTimestampList (as a
|
| binary 8-bit string) that will be sent as a TLS extension whenever
|
| the client announces support for the extension.
|
|
|
| - @type tlsIntolerant: int
|
| - @param tlsIntolerant: if non-zero, the server will simulate TLS
|
| - version intolerance by returning a fatal, handshake_failure alert.
|
| - The versions to which it's intolerant vary depending on the value:
|
| - 1: reject all TLS versions.
|
| - 2: reject TLS 1.1 or higher.
|
| - 3: reject TLS 1.2 or higher.
|
| -
|
| @type fallbackSCSV: bool
|
| @param fallbackSCSV: if true, the server will implement
|
| TLS_FALLBACK_SCSV and thus reject connections using less than the
|
| @@ -1044,19 +1068,27 @@ class TLSConnection(TLSRecordLayer):
|
| @raise tlslite.errors.TLSAuthenticationError: If the checker
|
| doesn't like the other party's authentication credentials.
|
| """
|
| - for result in self.handshakeServerAsync(sharedKeyDB, verifierDB,
|
| + for result in self.handshakeServerAsync(verifierDB,
|
| certChain, privateKey, reqCert, sessionCache, settings,
|
| - checker, reqCAs, tlsIntolerant, signedCertTimestamps,
|
| - fallbackSCSV, ocspResponse):
|
| + checker, reqCAs,
|
| + tacks=tacks, activationFlags=activationFlags,
|
| + nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant,
|
| + signedCertTimestamps=signedCertTimestamps,
|
| + fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse):
|
| pass
|
|
|
|
|
| - def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None,
|
| + def handshakeServerAsync(self, verifierDB=None,
|
| certChain=None, privateKey=None, reqCert=False,
|
| sessionCache=None, settings=None, checker=None,
|
| - reqCAs=None, tlsIntolerant=0,
|
| + reqCAs=None,
|
| + tacks=None, activationFlags=0,
|
| + nextProtos=None, anon=False,
|
| + tlsIntolerant=None,
|
| signedCertTimestamps=None,
|
| - fallbackSCSV=False, ocspResponse=None):
|
| + fallbackSCSV=False,
|
| + ocspResponse=None
|
| + ):
|
| """Start a server handshake operation on the TLS connection.
|
|
|
| This function returns a generator which behaves similarly to
|
| @@ -1069,35 +1101,47 @@ class TLSConnection(TLSRecordLayer):
|
| @return: A generator; see above for details.
|
| """
|
| handshaker = self._handshakeServerAsyncHelper(\
|
| - sharedKeyDB=sharedKeyDB,
|
| verifierDB=verifierDB, certChain=certChain,
|
| privateKey=privateKey, reqCert=reqCert,
|
| - sessionCache=sessionCache, settings=settings,
|
| - reqCAs=reqCAs,
|
| + sessionCache=sessionCache, settings=settings,
|
| + reqCAs=reqCAs,
|
| + tacks=tacks, activationFlags=activationFlags,
|
| + nextProtos=nextProtos, anon=anon,
|
| tlsIntolerant=tlsIntolerant,
|
| signedCertTimestamps=signedCertTimestamps,
|
| - fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse)
|
| -
|
| + fallbackSCSV=fallbackSCSV,
|
| + ocspResponse=ocspResponse)
|
| for result in self._handshakeWrapperAsync(handshaker, checker):
|
| yield result
|
|
|
|
|
| - def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB,
|
| - certChain, privateKey, reqCert,
|
| - sessionCache, settings, reqCAs,
|
| - tlsIntolerant, signedCertTimestamps,
|
| - fallbackSCSV, ocspResponse):
|
| + def _handshakeServerAsyncHelper(self, verifierDB,
|
| + certChain, privateKey, reqCert, sessionCache,
|
| + settings, reqCAs,
|
| + tacks, activationFlags,
|
| + nextProtos, anon,
|
| + tlsIntolerant, signedCertTimestamps, fallbackSCSV,
|
| + ocspResponse):
|
|
|
| self._handshakeStart(client=False)
|
|
|
| - if (not sharedKeyDB) and (not verifierDB) and (not certChain):
|
| + if (not verifierDB) and (not certChain) and not anon:
|
| raise ValueError("Caller passed no authentication credentials")
|
| if certChain and not privateKey:
|
| raise ValueError("Caller passed a certChain but no privateKey")
|
| if privateKey and not certChain:
|
| raise ValueError("Caller passed a privateKey but no certChain")
|
| if reqCAs and not reqCert:
|
| - raise ValueError("Caller passed reqCAs but not reqCert")
|
| + raise ValueError("Caller passed reqCAs but not reqCert")
|
| + if certChain and not isinstance(certChain, X509CertChain):
|
| + raise ValueError("Unrecognized certificate type")
|
| + if activationFlags and not tacks:
|
| + raise ValueError("Nonzero activationFlags requires tacks")
|
| + if tacks:
|
| + if not tackpyLoaded:
|
| + raise ValueError("tackpy is not loaded")
|
| + if not settings or not settings.useExperimentalTackExtension:
|
| + raise ValueError("useExperimentalTackExtension not enabled")
|
| if signedCertTimestamps and not certChain:
|
| raise ValueError("Caller passed signedCertTimestamps but no "
|
| "certChain")
|
| @@ -1105,36 +1149,129 @@ class TLSConnection(TLSRecordLayer):
|
| if not settings:
|
| settings = HandshakeSettings()
|
| settings = settings._filter()
|
| +
|
| + # OK Start exchanging messages
|
| + # ******************************
|
| +
|
| + # Handle ClientHello and resumption
|
| + for result in self._serverGetClientHello(settings, certChain,\
|
| + verifierDB, sessionCache,
|
| + anon, tlsIntolerant, fallbackSCSV):
|
| + 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) = result
|
| +
|
| + #If not a resumption...
|
| +
|
| + # Create the ServerHello message
|
| + if sessionCache:
|
| + sessionID = getRandomBytes(32)
|
| + else:
|
| + sessionID = bytearray(0)
|
| +
|
| + if not clientHello.supports_npn:
|
| + nextProtos = None
|
| +
|
| + # If not doing a certificate-based suite, discard the TACK
|
| + if not cipherSuite in CipherSuite.certAllSuites:
|
| + tacks = None
|
| +
|
| + # Prepare a TACK Extension if requested
|
| + if clientHello.tack:
|
| + tackExt = TackExtension.create(tacks, activationFlags)
|
| + else:
|
| + tackExt = None
|
| + serverHello = ServerHello()
|
| + serverHello.create(self.version, getRandomBytes(32), sessionID, \
|
| + cipherSuite, CertificateType.x509, tackExt,
|
| + nextProtos)
|
| + 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
|
| + if cipherSuite in CipherSuite.srpAllSuites:
|
| + for result in self._serverSRPKeyExchange(clientHello, serverHello,
|
| + verifierDB, cipherSuite,
|
| + privateKey, certChain):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + premasterSecret = result
|
| +
|
| + # Perform the RSA key exchange
|
| + elif cipherSuite in CipherSuite.certSuites:
|
| + for result in self._serverCertKeyExchange(clientHello, serverHello,
|
| + certChain, privateKey,
|
| + reqCert, reqCAs, cipherSuite,
|
| + settings, ocspResponse):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + (premasterSecret, clientCertChain) = result
|
| +
|
| + # Perform anonymous Diffie Hellman key exchange
|
| + elif cipherSuite in CipherSuite.anonSuites:
|
| + for result in self._serverAnonKeyExchange(clientHello, serverHello,
|
| + cipherSuite, settings):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + premasterSecret = result
|
| +
|
| + else:
|
| + assert(False)
|
| +
|
| + # Exchange Finished messages
|
| + for result in self._serverFinished(premasterSecret,
|
| + clientHello.random, serverHello.random,
|
| + cipherSuite, settings.cipherImplementations,
|
| + nextProtos, clientHello.channel_id):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + masterSecret = result
|
|
|
| + #Create the session object
|
| + self.session = Session()
|
| + if cipherSuite in CipherSuite.certAllSuites:
|
| + serverCertChain = certChain
|
| + else:
|
| + serverCertChain = None
|
| + srpUsername = None
|
| + serverName = None
|
| + if clientHello.srp_username:
|
| + srpUsername = clientHello.srp_username.decode("utf-8")
|
| + if clientHello.server_name:
|
| + serverName = clientHello.server_name.decode("utf-8")
|
| + self.session.create(masterSecret, serverHello.session_id, cipherSuite,
|
| + srpUsername, clientCertChain, serverCertChain,
|
| + tackExt, serverHello.tackExt!=None, serverName)
|
| +
|
| + #Add the session object to the session cache
|
| + if sessionCache and sessionID:
|
| + sessionCache[sessionID] = self.session
|
| +
|
| + self._handshakeDone(resumed=False)
|
| +
|
| +
|
| + def _serverGetClientHello(self, settings, certChain, verifierDB,
|
| + sessionCache, anon, tlsIntolerant, fallbackSCSV):
|
| #Initialize acceptable cipher suites
|
| cipherSuites = []
|
| if verifierDB:
|
| if certChain:
|
| cipherSuites += \
|
| - CipherSuite.getSrpRsaSuites(settings.cipherNames)
|
| - cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames)
|
| - if sharedKeyDB or certChain:
|
| - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames)
|
| -
|
| - #Initialize acceptable certificate type
|
| - certificateType = None
|
| - if certChain:
|
| - try:
|
| - import cryptoIDlib.CertChain
|
| - if isinstance(certChain, cryptoIDlib.CertChain.CertChain):
|
| - certificateType = CertificateType.cryptoID
|
| - except ImportError:
|
| - pass
|
| - if isinstance(certChain, X509CertChain):
|
| - certificateType = CertificateType.x509
|
| - if certificateType == None:
|
| - raise ValueError("Unrecognized certificate type")
|
| -
|
| - #Initialize locals
|
| - clientCertChain = None
|
| - serverCertChain = None #We may set certChain to this later
|
| - postFinishedError = None
|
| - doingChannelID = False
|
| + CipherSuite.getSrpCertSuites(settings)
|
| + cipherSuites += CipherSuite.getSrpSuites(settings)
|
| + elif certChain:
|
| + cipherSuites += CipherSuite.getCertSuites(settings)
|
| + elif anon:
|
| + cipherSuites += CipherSuite.getAnonSuites(settings)
|
| + else:
|
| + assert(False)
|
|
|
| #Tentatively set version to most-desirable version, so if an error
|
| #occurs parsing the ClientHello, this is what we'll use for the
|
| @@ -1144,10 +1281,8 @@ class TLSConnection(TLSRecordLayer):
|
| #Get ClientHello
|
| for result in self._getMsg(ContentType.handshake,
|
| HandshakeType.client_hello):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| + if result in (0,1): yield result
|
| + else: break
|
| clientHello = result
|
|
|
| #If client's version is too low, reject it
|
| @@ -1158,79 +1293,36 @@ class TLSConnection(TLSRecordLayer):
|
| "Too old version: %s" % str(clientHello.client_version)):
|
| yield result
|
|
|
| - #If tlsIntolerant is nonzero, reject certain TLS versions.
|
| - #1: reject all TLS versions.
|
| - #2: reject TLS 1.1 or higher.
|
| - #3: reject TLS 1.2 or higher.
|
| - if (tlsIntolerant == 1 and clientHello.client_version > (3, 0) or
|
| - tlsIntolerant == 2 and clientHello.client_version > (3, 1) or
|
| - tlsIntolerant == 3 and clientHello.client_version > (3, 2)):
|
| + #If simulating TLS intolerance, reject certain TLS versions.
|
| + elif (tlsIntolerant is not None and
|
| + clientHello.client_version >= tlsIntolerant):
|
| for result in self._sendError(\
|
| AlertDescription.handshake_failure):
|
| yield result
|
|
|
| #If client's version is too high, propose my highest version
|
| - if clientHello.client_version > settings.maxVersion:
|
| + elif clientHello.client_version > settings.maxVersion:
|
| self.version = settings.maxVersion
|
| - else:
|
| - #Set the version to the client's version
|
| - self.version = clientHello.client_version
|
| - if (fallbackSCSV and
|
| - clientHello.client_version < settings.maxVersion):
|
| - for cipherSuite in clientHello.cipher_suites:
|
| - if cipherSuite == 0x5600:
|
| - for result in self._sendError(\
|
| - AlertDescription.inappropriate_fallback):
|
| - yield result
|
|
|
| - #Get the client nonce; create server nonce
|
| - clientRandom = clientHello.random
|
| - serverRandom = getRandomBytes(32)
|
| + #Detect if the client performed an inappropriate fallback.
|
| + elif fallbackSCSV and clientHello.client_version < settings.maxVersion:
|
| + if CipherSuite.TLS_FALLBACK_SCSV in clientHello.cipher_suites:
|
| + for result in self._sendError(\
|
| + AlertDescription.inappropriate_fallback):
|
| + yield result
|
|
|
| - #Calculate the first cipher suite intersection.
|
| - #This is the 'privileged' ciphersuite. We'll use it if we're
|
| - #doing a shared-key resumption or a new negotiation. In fact,
|
| - #the only time we won't use it is if we're resuming a non-sharedkey
|
| - #session, in which case we use the ciphersuite from the session.
|
| - #
|
| - #Given the current ciphersuite ordering, this means we prefer SRP
|
| - #over non-SRP.
|
| - for cipherSuite in cipherSuites:
|
| - if cipherSuite in clientHello.cipher_suites:
|
| - break
|
| else:
|
| - for result in self._sendError(\
|
| - AlertDescription.handshake_failure):
|
| - yield result
|
| + #Set the version to the client's version
|
| + self.version = clientHello.client_version
|
|
|
| - #If resumption was requested...
|
| - if clientHello.session_id and (sharedKeyDB or sessionCache):
|
| + #If resumption was requested and we have a session cache...
|
| + if clientHello.session_id and sessionCache:
|
| session = None
|
|
|
| - #Check in the sharedKeys container
|
| - if sharedKeyDB and len(clientHello.session_id)==16:
|
| - try:
|
| - #Trim off zero padding, if any
|
| - for x in range(16):
|
| - if clientHello.session_id[x]==0:
|
| - break
|
| - self.allegedSharedKeyUsername = bytesToString(\
|
| - clientHello.session_id[:x])
|
| - session = sharedKeyDB[self.allegedSharedKeyUsername]
|
| - if not session.sharedKey:
|
| - raise AssertionError()
|
| - #use privileged ciphersuite
|
| - session.cipherSuite = cipherSuite
|
| - except KeyError:
|
| - pass
|
| -
|
| - #Then check in the session cache
|
| + #Check in the session cache
|
| if sessionCache and not session:
|
| try:
|
| - session = sessionCache[bytesToString(\
|
| - clientHello.session_id)]
|
| - if session.sharedKey:
|
| - raise AssertionError()
|
| + session = sessionCache[clientHello.session_id]
|
| if not session.resumable:
|
| raise AssertionError()
|
| #Check for consistency with ClientHello
|
| @@ -1243,390 +1335,460 @@ class TLSConnection(TLSRecordLayer):
|
| AlertDescription.handshake_failure):
|
| yield result
|
| if clientHello.srp_username:
|
| - if clientHello.srp_username != session.srpUsername:
|
| + if not session.srpUsername or \
|
| + clientHello.srp_username != bytearray(session.srpUsername, "utf-8"):
|
| for result in self._sendError(\
|
| AlertDescription.handshake_failure):
|
| yield result
|
| + if clientHello.server_name:
|
| + if not session.serverName or \
|
| + clientHello.server_name != bytearray(session.serverName, "utf-8"):
|
| + for result in self._sendError(\
|
| + AlertDescription.handshake_failure):
|
| + yield result
|
| except KeyError:
|
| pass
|
|
|
| #If a session is found..
|
| if session:
|
| - #Set the session
|
| - self.session = session
|
| -
|
| #Send ServerHello
|
| serverHello = ServerHello()
|
| - serverHello.create(self.version, serverRandom,
|
| + serverHello.create(self.version, getRandomBytes(32),
|
| session.sessionID, session.cipherSuite,
|
| - certificateType)
|
| - serverHello.channel_id = clientHello.channel_id
|
| - doingChannelID = clientHello.channel_id
|
| + CertificateType.x509, None, None)
|
| for result in self._sendMsg(serverHello):
|
| yield result
|
|
|
| - #From here on, the client's messages must have the right version
|
| + #From here on, the client's messages must have right version
|
| self._versionCheck = True
|
|
|
| #Calculate pending connection states
|
| - self._calcPendingStates(clientRandom, serverRandom,
|
| - settings.cipherImplementations)
|
| + self._calcPendingStates(session.cipherSuite,
|
| + session.masterSecret,
|
| + clientHello.random,
|
| + serverHello.random,
|
| + settings.cipherImplementations)
|
|
|
| #Exchange ChangeCipherSpec and Finished messages
|
| - for result in self._sendFinished():
|
| + for result in self._sendFinished(session.masterSecret):
|
| yield result
|
| - for result in self._getChangeCipherSpec():
|
| + for result in self._getFinished(session.masterSecret):
|
| yield result
|
| - if doingChannelID:
|
| - for result in self._getEncryptedExtensions():
|
| - yield result
|
| - for result in self._getFinished():
|
| - yield result
|
| -
|
| - #Mark the connection as open
|
| - self._handshakeDone(resumed=True)
|
| - return
|
|
|
| + #Set the session
|
| + self.session = session
|
| +
|
| + yield None # Handshake done!
|
|
|
| - #If not a resumption...
|
| -
|
| - #TRICKY: we might have chosen an RSA suite that was only deemed
|
| - #acceptable because of the shared-key resumption. If the shared-
|
| - #key resumption failed, because the identifier wasn't recognized,
|
| - #we might fall through to here, where we have an RSA suite
|
| - #chosen, but no certificate.
|
| - if cipherSuite in CipherSuite.rsaSuites and not certChain:
|
| + #Calculate the first cipher suite intersection.
|
| + #This is the 'privileged' ciphersuite. We'll use it if we're
|
| + #doing a new negotiation. In fact,
|
| + #the only time we won't use it is if we're resuming a
|
| + #session, in which case we use the ciphersuite from the session.
|
| + #
|
| + #Use the client's preferences for now.
|
| + for cipherSuite in clientHello.cipher_suites:
|
| + if cipherSuite in cipherSuites:
|
| + break
|
| + else:
|
| for result in self._sendError(\
|
| - AlertDescription.handshake_failure):
|
| + AlertDescription.handshake_failure,
|
| + "No mutual ciphersuite"):
|
| yield result
|
| -
|
| + if cipherSuite in CipherSuite.srpAllSuites and \
|
| + not clientHello.srp_username:
|
| + for result in self._sendError(\
|
| + AlertDescription.unknown_psk_identity,
|
| + "Client sent a hello, but without the SRP username"):
|
| + yield result
|
| +
|
| #If an RSA suite is chosen, check for certificate type intersection
|
| - #(We do this check down here because if the mismatch occurs but the
|
| - # client is using a shared-key session, it's okay)
|
| - if cipherSuite in CipherSuite.rsaSuites + \
|
| - CipherSuite.srpRsaSuites:
|
| - if certificateType not in clientHello.certificate_types:
|
| - for result in self._sendError(\
|
| - AlertDescription.handshake_failure,
|
| - "the client doesn't support my certificate type"):
|
| - yield result
|
| -
|
| - #Move certChain -> serverCertChain, now that we're using it
|
| - serverCertChain = certChain
|
| -
|
| -
|
| - #Create sessionID
|
| - if sessionCache:
|
| - sessionID = getRandomBytes(32)
|
| - else:
|
| - sessionID = createByteArraySequence([])
|
| + if cipherSuite in CipherSuite.certAllSuites and CertificateType.x509 \
|
| + not in clientHello.certificate_types:
|
| + for result in self._sendError(\
|
| + AlertDescription.handshake_failure,
|
| + "the client doesn't support my certificate type"):
|
| + yield result
|
|
|
| - #If we've selected an SRP suite, exchange keys and calculate
|
| - #premaster secret:
|
| - if cipherSuite in CipherSuite.srpSuites + CipherSuite.srpRsaSuites:
|
| + # If resumption was not requested, or
|
| + # we have no session cache, or
|
| + # the client's session_id was not found in cache:
|
| + yield (clientHello, cipherSuite)
|
|
|
| - #If there's no SRP username...
|
| - if not clientHello.srp_username:
|
| + def _serverSRPKeyExchange(self, clientHello, serverHello, verifierDB,
|
| + cipherSuite, privateKey, serverCertChain):
|
|
|
| - #Ask the client to re-send ClientHello with one
|
| - for result in self._sendMsg(Alert().create(\
|
| - AlertDescription.missing_srp_username,
|
| - AlertLevel.warning)):
|
| - yield result
|
| + srpUsername = clientHello.srp_username.decode("utf-8")
|
| + self.allegedSrpUsername = srpUsername
|
| + #Get parameters from username
|
| + try:
|
| + entry = verifierDB[srpUsername]
|
| + except KeyError:
|
| + for result in self._sendError(\
|
| + AlertDescription.unknown_psk_identity):
|
| + yield result
|
| + (N, g, s, v) = entry
|
| +
|
| + #Calculate server's ephemeral DH values (b, B)
|
| + b = bytesToNumber(getRandomBytes(32))
|
| + k = makeK(N, g)
|
| + B = (powMod(g, b, N) + (k*v)) % N
|
| +
|
| + #Create ServerKeyExchange, signing it if necessary
|
| + serverKeyExchange = ServerKeyExchange(cipherSuite)
|
| + serverKeyExchange.createSRP(N, g, s, B)
|
| + if cipherSuite in CipherSuite.srpCertSuites:
|
| + hashBytes = serverKeyExchange.hash(clientHello.random,
|
| + serverHello.random)
|
| + serverKeyExchange.signature = privateKey.sign(hashBytes)
|
| +
|
| + #Send ServerHello[, Certificate], ServerKeyExchange,
|
| + #ServerHelloDone
|
| + msgs = []
|
| + msgs.append(serverHello)
|
| + if cipherSuite in CipherSuite.srpCertSuites:
|
| + certificateMsg = Certificate(CertificateType.x509)
|
| + certificateMsg.create(serverCertChain)
|
| + msgs.append(certificateMsg)
|
| + msgs.append(serverKeyExchange)
|
| + msgs.append(ServerHelloDone())
|
| + for result in self._sendMsgs(msgs):
|
| + yield result
|
|
|
| - #Get ClientHello
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.client_hello):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - clientHello = result
|
| -
|
| - #Check ClientHello
|
| - #If client's version is too low, reject it (COPIED CODE; BAD!)
|
| - if clientHello.client_version < settings.minVersion:
|
| - self.version = settings.minVersion
|
| - for result in self._sendError(\
|
| - AlertDescription.protocol_version,
|
| - "Too old version: %s" % str(clientHello.client_version)):
|
| - yield result
|
| + #From here on, the client's messages must have the right version
|
| + self._versionCheck = True
|
|
|
| - #If client's version is too high, propose my highest version
|
| - elif clientHello.client_version > settings.maxVersion:
|
| - self.version = settings.maxVersion
|
| + #Get and check ClientKeyExchange
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.client_key_exchange,
|
| + cipherSuite):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + clientKeyExchange = result
|
| + A = clientKeyExchange.srp_A
|
| + if A % N == 0:
|
| + for result in self._sendError(AlertDescription.illegal_parameter,
|
| + "Suspicious A value"):
|
| + yield result
|
| + assert(False) # Just to ensure we don't fall through somehow
|
|
|
| - else:
|
| - #Set the version to the client's version
|
| - self.version = clientHello.client_version
|
| -
|
| - #Recalculate the privileged cipher suite, making sure to
|
| - #pick an SRP suite
|
| - cipherSuites = [c for c in cipherSuites if c in \
|
| - CipherSuite.srpSuites + \
|
| - CipherSuite.srpRsaSuites]
|
| - for cipherSuite in cipherSuites:
|
| - if cipherSuite in clientHello.cipher_suites:
|
| - break
|
| - else:
|
| - for result in self._sendError(\
|
| - AlertDescription.handshake_failure):
|
| - yield result
|
| + #Calculate u
|
| + u = makeU(N, A, B)
|
|
|
| - #Get the client nonce; create server nonce
|
| - clientRandom = clientHello.random
|
| - serverRandom = getRandomBytes(32)
|
| + #Calculate premaster secret
|
| + S = powMod((A * powMod(v,u,N)) % N, b, N)
|
| + premasterSecret = numberToByteArray(S)
|
| +
|
| + yield premasterSecret
|
|
|
| - #The username better be there, this time
|
| - if not clientHello.srp_username:
|
| - for result in self._sendError(\
|
| - AlertDescription.illegal_parameter,
|
| - "Client resent a hello, but without the SRP"\
|
| - " username"):
|
| - yield result
|
|
|
| + def _serverCertKeyExchange(self, clientHello, serverHello,
|
| + serverCertChain, privateKey,
|
| + reqCert, reqCAs, cipherSuite,
|
| + settings, ocspResponse):
|
| + #Send ServerHello, Certificate[, CertificateRequest],
|
| + #ServerHelloDone
|
| + msgs = []
|
|
|
| - #Get username
|
| - self.allegedSrpUsername = clientHello.srp_username
|
| + # If we verify a client cert chain, return it
|
| + clientCertChain = None
|
|
|
| - #Get parameters from username
|
| - try:
|
| - entry = verifierDB[self.allegedSrpUsername]
|
| - except KeyError:
|
| - for result in self._sendError(\
|
| - AlertDescription.unknown_srp_username):
|
| - yield result
|
| - (N, g, s, v) = entry
|
| -
|
| - #Calculate server's ephemeral DH values (b, B)
|
| - b = bytesToNumber(getRandomBytes(32))
|
| - k = makeK(N, g)
|
| - B = (powMod(g, b, N) + (k*v)) % N
|
| -
|
| - #Create ServerKeyExchange, signing it if necessary
|
| - serverKeyExchange = ServerKeyExchange(cipherSuite)
|
| - serverKeyExchange.createSRP(N, g, stringToBytes(s), B)
|
| - if cipherSuite in CipherSuite.srpRsaSuites:
|
| - hashBytes = serverKeyExchange.hash(clientRandom,
|
| - serverRandom)
|
| - serverKeyExchange.signature = privateKey.sign(hashBytes)
|
| -
|
| - #Send ServerHello[, Certificate], ServerKeyExchange,
|
| - #ServerHelloDone
|
| - msgs = []
|
| - serverHello = ServerHello()
|
| - serverHello.create(self.version, serverRandom, sessionID,
|
| - cipherSuite, certificateType)
|
| - msgs.append(serverHello)
|
| - if cipherSuite in CipherSuite.srpRsaSuites:
|
| - certificateMsg = Certificate(certificateType)
|
| - certificateMsg.create(serverCertChain)
|
| - msgs.append(certificateMsg)
|
| - msgs.append(serverKeyExchange)
|
| - msgs.append(ServerHelloDone())
|
| - for result in self._sendMsgs(msgs):
|
| - yield result
|
| + 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))
|
| + elif reqCert:
|
| + msgs.append(CertificateRequest())
|
| + msgs.append(ServerHelloDone())
|
| + for result in self._sendMsgs(msgs):
|
| + yield result
|
|
|
| - #From here on, the client's messages must have the right version
|
| - self._versionCheck = True
|
| + #From here on, the client's messages must have the right version
|
| + self._versionCheck = True
|
|
|
| - #Get and check ClientKeyExchange
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.client_key_exchange,
|
| - cipherSuite):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - clientKeyExchange = result
|
| - A = clientKeyExchange.srp_A
|
| - if A % N == 0:
|
| - postFinishedError = (AlertDescription.illegal_parameter,
|
| - "Suspicious A value")
|
| - #Calculate u
|
| - u = makeU(N, A, B)
|
| -
|
| - #Calculate premaster secret
|
| - S = powMod((A * powMod(v,u,N)) % N, b, N)
|
| - premasterSecret = numberToBytes(S)
|
| -
|
| -
|
| - #If we've selected an RSA suite, exchange keys and calculate
|
| - #premaster secret:
|
| - elif cipherSuite in CipherSuite.rsaSuites:
|
| -
|
| - #Send ServerHello, Certificate[, CertificateRequest],
|
| - #ServerHelloDone
|
| - msgs = []
|
| - serverHello = ServerHello().create(
|
| - self.version, serverRandom,
|
| - sessionID, cipherSuite, certificateType)
|
| - serverHello.channel_id = clientHello.channel_id
|
| - if clientHello.support_signed_cert_timestamps:
|
| - 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:
|
| - msgs.append(CertificateRequest())
|
| - msgs.append(ServerHelloDone())
|
| - for result in self._sendMsgs(msgs):
|
| - yield result
|
| + #Get [Certificate,] (if was requested)
|
| + if reqCert:
|
| + if self.version == (3,0):
|
| + for result in self._getMsg((ContentType.handshake,
|
| + ContentType.alert),
|
| + HandshakeType.certificate,
|
| + CertificateType.x509):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + msg = result
|
|
|
| - #From here on, the client's messages must have the right version
|
| - self._versionCheck = True
|
| -
|
| - #Get [Certificate,] (if was requested)
|
| - if reqCert:
|
| - if self.version == (3,0):
|
| - for result in self._getMsg((ContentType.handshake,
|
| - ContentType.alert),
|
| - HandshakeType.certificate,
|
| - certificateType):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - msg = result
|
| -
|
| - if isinstance(msg, Alert):
|
| - #If it's not a no_certificate alert, re-raise
|
| - alert = msg
|
| - if alert.description != \
|
| - AlertDescription.no_certificate:
|
| - self._shutdown(False)
|
| - raise TLSRemoteAlert(alert)
|
| - elif isinstance(msg, Certificate):
|
| - clientCertificate = msg
|
| - if clientCertificate.certChain and \
|
| - clientCertificate.certChain.getNumCerts()!=0:
|
| - clientCertChain = clientCertificate.certChain
|
| - else:
|
| - raise AssertionError()
|
| - elif self.version in ((3,1), (3,2)):
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.certificate,
|
| - certificateType):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - clientCertificate = result
|
| + if isinstance(msg, Alert):
|
| + #If it's not a no_certificate alert, re-raise
|
| + alert = msg
|
| + if alert.description != \
|
| + AlertDescription.no_certificate:
|
| + self._shutdown(False)
|
| + raise TLSRemoteAlert(alert)
|
| + elif isinstance(msg, Certificate):
|
| + clientCertificate = msg
|
| if clientCertificate.certChain and \
|
| clientCertificate.certChain.getNumCerts()!=0:
|
| clientCertChain = clientCertificate.certChain
|
| else:
|
| raise AssertionError()
|
| + elif self.version in ((3,1), (3,2)):
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.certificate,
|
| + CertificateType.x509):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + clientCertificate = result
|
| + if clientCertificate.certChain and \
|
| + clientCertificate.certChain.getNumCerts()!=0:
|
| + clientCertChain = clientCertificate.certChain
|
| + else:
|
| + raise AssertionError()
|
| +
|
| + #Get ClientKeyExchange
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.client_key_exchange,
|
| + cipherSuite):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + clientKeyExchange = result
|
| +
|
| + #Decrypt ClientKeyExchange
|
| + premasterSecret = privateKey.decrypt(\
|
| + clientKeyExchange.encryptedPreMasterSecret)
|
| +
|
| + # On decryption failure randomize premaster secret to avoid
|
| + # Bleichenbacher's "million message" attack
|
| + randomPreMasterSecret = getRandomBytes(48)
|
| + versionCheck = (premasterSecret[0], premasterSecret[1])
|
| + if not premasterSecret:
|
| + premasterSecret = randomPreMasterSecret
|
| + elif len(premasterSecret)!=48:
|
| + premasterSecret = randomPreMasterSecret
|
| + elif versionCheck != clientHello.client_version:
|
| + if versionCheck != self.version: #Tolerate buggy IE clients
|
| + premasterSecret = randomPreMasterSecret
|
|
|
| - #Get ClientKeyExchange
|
| + #Get and check CertificateVerify, if relevant
|
| + if clientCertChain:
|
| + if self.version == (3,0):
|
| + masterSecret = calcMasterSecret(self.version, premasterSecret,
|
| + clientHello.random, serverHello.random)
|
| + verifyBytes = self._calcSSLHandshakeHash(masterSecret, b"")
|
| + elif self.version in ((3,1), (3,2)):
|
| + verifyBytes = self._handshake_md5.digest() + \
|
| + self._handshake_sha.digest()
|
| for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.client_key_exchange,
|
| - cipherSuite):
|
| - if result in (0,1):
|
| + HandshakeType.certificate_verify):
|
| + if result in (0,1): yield result
|
| + else: break
|
| + certificateVerify = result
|
| + publicKey = clientCertChain.getEndEntityPublicKey()
|
| + if len(publicKey) < settings.minKeySize:
|
| + for result in self._sendError(\
|
| + AlertDescription.handshake_failure,
|
| + "Client's public key too small: %d" % len(publicKey)):
|
| yield result
|
| - else:
|
| - break
|
| - clientKeyExchange = result
|
|
|
| - #Decrypt ClientKeyExchange
|
| - premasterSecret = privateKey.decrypt(\
|
| - clientKeyExchange.encryptedPreMasterSecret)
|
| + if len(publicKey) > settings.maxKeySize:
|
| + for result in self._sendError(\
|
| + AlertDescription.handshake_failure,
|
| + "Client's public key too large: %d" % len(publicKey)):
|
| + yield result
|
|
|
| - randomPreMasterSecret = getRandomBytes(48)
|
| - versionCheck = (premasterSecret[0], premasterSecret[1])
|
| - if not premasterSecret:
|
| - premasterSecret = randomPreMasterSecret
|
| - elif len(premasterSecret)!=48:
|
| - premasterSecret = randomPreMasterSecret
|
| - elif versionCheck != clientHello.client_version:
|
| - if versionCheck != self.version: #Tolerate buggy IE clients
|
| - premasterSecret = randomPreMasterSecret
|
| + if not publicKey.verify(certificateVerify.signature, verifyBytes):
|
| + for result in self._sendError(\
|
| + AlertDescription.decrypt_error,
|
| + "Signature failed to verify"):
|
| + yield result
|
| + yield (premasterSecret, clientCertChain)
|
| +
|
| +
|
| + def _serverAnonKeyExchange(self, clientHello, serverHello, cipherSuite,
|
| + settings):
|
| + # Calculate DH p, g, Xs, Ys
|
| + dh_p = getRandomSafePrime(32, False)
|
| + dh_g = getRandomNumber(2, dh_p)
|
| + dh_Xs = bytesToNumber(getRandomBytes(32))
|
| + dh_Ys = powMod(dh_g, dh_Xs, dh_p)
|
| +
|
| + #Create ServerKeyExchange
|
| + serverKeyExchange = ServerKeyExchange(cipherSuite)
|
| + serverKeyExchange.createDH(dh_p, dh_g, dh_Ys)
|
| +
|
| + #Send ServerHello[, Certificate], ServerKeyExchange,
|
| + #ServerHelloDone
|
| + msgs = []
|
| + msgs.append(serverHello)
|
| + msgs.append(serverKeyExchange)
|
| + msgs.append(ServerHelloDone())
|
| + for result in self._sendMsgs(msgs):
|
| + yield result
|
| +
|
| + #From here on, the client's messages must have the right version
|
| + self._versionCheck = True
|
| +
|
| + #Get and check ClientKeyExchange
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.client_key_exchange,
|
| + cipherSuite):
|
| + if result in (0,1):
|
| + yield result
|
| + else:
|
| + break
|
| + clientKeyExchange = result
|
| + dh_Yc = clientKeyExchange.dh_Yc
|
| +
|
| + if dh_Yc % dh_p == 0:
|
| + for result in self._sendError(AlertDescription.illegal_parameter,
|
| + "Suspicious dh_Yc value"):
|
| + yield result
|
| + assert(False) # Just to ensure we don't fall through somehow
|
|
|
| - #Get and check CertificateVerify, if relevant
|
| - if clientCertChain:
|
| - if self.version == (3,0):
|
| - #Create a temporary session object, just for the purpose
|
| - #of checking the CertificateVerify
|
| - session = Session()
|
| - session._calcMasterSecret(self.version, premasterSecret,
|
| - clientRandom, serverRandom)
|
| - verifyBytes = self._calcSSLHandshakeHash(\
|
| - session.masterSecret, "")
|
| - elif self.version in ((3,1), (3,2)):
|
| - verifyBytes = stringToBytes(self._handshake_md5.digest() +\
|
| - self._handshake_sha.digest())
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.certificate_verify):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - certificateVerify = result
|
| - publicKey = clientCertChain.getEndEntityPublicKey()
|
| - if len(publicKey) < settings.minKeySize:
|
| - postFinishedError = (AlertDescription.handshake_failure,
|
| - "Client's public key too small: %d" % len(publicKey))
|
| - if len(publicKey) > settings.maxKeySize:
|
| - postFinishedError = (AlertDescription.handshake_failure,
|
| - "Client's public key too large: %d" % len(publicKey))
|
| -
|
| - if not publicKey.verify(certificateVerify.signature,
|
| - verifyBytes):
|
| - postFinishedError = (AlertDescription.decrypt_error,
|
| - "Signature failed to verify")
|
| + #Calculate premaster secre
|
| + S = powMod(dh_Yc,dh_Xs,dh_p)
|
| + premasterSecret = numberToByteArray(S)
|
| +
|
| + yield premasterSecret
|
|
|
|
|
| - #Create the session object
|
| - self.session = Session()
|
| - self.session._calcMasterSecret(self.version, premasterSecret,
|
| + def _serverFinished(self, premasterSecret, clientRandom, serverRandom,
|
| + cipherSuite, cipherImplementations, nextProtos,
|
| + doingChannelID):
|
| + masterSecret = calcMasterSecret(self.version, premasterSecret,
|
| clientRandom, serverRandom)
|
| - self.session.sessionID = sessionID
|
| - self.session.cipherSuite = cipherSuite
|
| - self.session.srpUsername = self.allegedSrpUsername
|
| - self.session.clientCertChain = clientCertChain
|
| - self.session.serverCertChain = serverCertChain
|
| -
|
| +
|
| #Calculate pending connection states
|
| - self._calcPendingStates(clientRandom, serverRandom,
|
| - settings.cipherImplementations)
|
| + self._calcPendingStates(cipherSuite, masterSecret,
|
| + clientRandom, serverRandom,
|
| + cipherImplementations)
|
|
|
| #Exchange ChangeCipherSpec and Finished messages
|
| - for result in self._getChangeCipherSpec():
|
| + for result in self._getFinished(masterSecret,
|
| + expect_next_protocol=nextProtos is not None,
|
| + expect_channel_id=doingChannelID):
|
| yield result
|
| - if doingChannelID:
|
| - for result in self._getEncryptedExtensions():
|
| - yield result
|
| - for result in self._getFinished():
|
| +
|
| + for result in self._sendFinished(masterSecret):
|
| yield result
|
| +
|
| + yield masterSecret
|
|
|
| - #If we were holding a post-finished error until receiving the client
|
| - #finished message, send it now. We delay the call until this point
|
| - #because calling sendError() throws an exception, and our caller might
|
| - #shut down the socket upon receiving the exception. If he did, and the
|
| - #client was still sending its ChangeCipherSpec or Finished messages, it
|
| - #would cause a socket error on the client side. This is a lot of
|
| - #consideration to show to misbehaving clients, but this would also
|
| - #cause problems with fault-testing.
|
| - if postFinishedError:
|
| - for result in self._sendError(*postFinishedError):
|
| +
|
| + #*********************************************************
|
| + # Shared Handshake Functions
|
| + #*********************************************************
|
| +
|
| +
|
| + def _sendFinished(self, masterSecret, nextProto=None):
|
| + #Send ChangeCipherSpec
|
| + for result in self._sendMsg(ChangeCipherSpec()):
|
| + yield result
|
| +
|
| + #Switch to pending write state
|
| + self._changeWriteState()
|
| +
|
| + if nextProto is not None:
|
| + nextProtoMsg = NextProtocol().create(nextProto)
|
| + for result in self._sendMsg(nextProtoMsg):
|
| yield result
|
|
|
| - for result in self._sendFinished():
|
| + #Calculate verification data
|
| + verifyData = self._calcFinished(masterSecret, True)
|
| + if self.fault == Fault.badFinished:
|
| + verifyData[0] = (verifyData[0]+1)%256
|
| +
|
| + #Send Finished message under new state
|
| + finished = Finished(self.version).create(verifyData)
|
| + for result in self._sendMsg(finished):
|
| yield result
|
|
|
| - #Add the session object to the session cache
|
| - if sessionCache and sessionID:
|
| - sessionCache[bytesToString(sessionID)] = self.session
|
| + def _getFinished(self, masterSecret, expect_next_protocol=False, nextProto=None,
|
| + expect_channel_id=False):
|
| + #Get and check ChangeCipherSpec
|
| + for result in self._getMsg(ContentType.change_cipher_spec):
|
| + if result in (0,1):
|
| + yield result
|
| + changeCipherSpec = result
|
|
|
| - #Mark the connection as open
|
| - self.session._setResumable(True)
|
| - self._handshakeDone(resumed=False)
|
| + if changeCipherSpec.type != 1:
|
| + for result in self._sendError(AlertDescription.illegal_parameter,
|
| + "ChangeCipherSpec type incorrect"):
|
| + yield result
|
| +
|
| + #Switch to pending read state
|
| + self._changeReadState()
|
| +
|
| + #Server Finish - Are we waiting for a next protocol echo?
|
| + if expect_next_protocol:
|
| + for result in self._getMsg(ContentType.handshake, HandshakeType.next_protocol):
|
| + if result in (0,1):
|
| + yield result
|
| + if result is None:
|
| + for result in self._sendError(AlertDescription.unexpected_message,
|
| + "Didn't get NextProtocol message"):
|
| + yield result
|
| +
|
| + self.next_proto = result.next_proto
|
| + else:
|
| + self.next_proto = None
|
| +
|
| + #Client Finish - Only set the next_protocol selected in the connection
|
| + if nextProto:
|
| + self.next_proto = nextProto
|
| +
|
| + #Server Finish - Are we waiting for a EncryptedExtensions?
|
| + if expect_channel_id:
|
| + for result in self._getMsg(ContentType.handshake, HandshakeType.encrypted_extensions):
|
| + if result in (0,1):
|
| + yield result
|
| + if result is None:
|
| + for result in self._sendError(AlertDescription.unexpected_message,
|
| + "Didn't get EncryptedExtensions message"):
|
| + yield result
|
| + encrypted_extensions = result
|
| + self.channel_id = result.channel_id_key
|
| + else:
|
| + self.channel_id = None
|
| +
|
| + #Calculate verification data
|
| + verifyData = self._calcFinished(masterSecret, False)
|
| +
|
| + #Get and check Finished message under new state
|
| + for result in self._getMsg(ContentType.handshake,
|
| + HandshakeType.finished):
|
| + if result in (0,1):
|
| + yield result
|
| + finished = result
|
| + if finished.verify_data != verifyData:
|
| + for result in self._sendError(AlertDescription.decrypt_error,
|
| + "Finished message is incorrect"):
|
| + yield result
|
| +
|
| + def _calcFinished(self, masterSecret, send=True):
|
| + if self.version == (3,0):
|
| + if (self._client and send) or (not self._client and not send):
|
| + senderStr = b"\x43\x4C\x4E\x54"
|
| + else:
|
| + senderStr = b"\x53\x52\x56\x52"
|
| +
|
| + verifyData = self._calcSSLHandshakeHash(masterSecret, senderStr)
|
| + return verifyData
|
| +
|
| + elif self.version in ((3,1), (3,2)):
|
| + if (self._client and send) or (not self._client and not send):
|
| + label = b"client finished"
|
| + else:
|
| + label = b"server finished"
|
| +
|
| + handshakeHashes = self._handshake_md5.digest() + \
|
| + self._handshake_sha.digest()
|
| + verifyData = PRF(masterSecret, label, handshakeHashes, 12)
|
| + return verifyData
|
| + else:
|
| + raise AssertionError()
|
|
|
|
|
| def _handshakeWrapperAsync(self, handshaker, checker):
|
| @@ -1643,27 +1805,11 @@ class TLSConnection(TLSRecordLayer):
|
| for result in self._sendMsg(alert):
|
| yield result
|
| raise
|
| - except:
|
| - self._shutdown(False)
|
| + except GeneratorExit:
|
| + raise
|
| + except TLSAlert as alert:
|
| + if not self.fault:
|
| raise
|
| - else:
|
| - try:
|
| - for result in handshaker:
|
| - yield result
|
| - if checker:
|
| - try:
|
| - checker(self)
|
| - except TLSAuthenticationError:
|
| - alert = Alert().create(AlertDescription.close_notify,
|
| - AlertLevel.fatal)
|
| - for result in self._sendMsg(alert):
|
| - yield result
|
| - raise
|
| - except socket.error, e:
|
| - raise TLSFaultError("socket error!")
|
| - except TLSAbruptCloseError, e:
|
| - raise TLSFaultError("abrupt close error!")
|
| - except TLSAlert, alert:
|
| if alert.description not in Fault.faultAlerts[self.fault]:
|
| raise TLSFaultError(str(alert))
|
| else:
|
| @@ -1671,28 +1817,3 @@ class TLSConnection(TLSRecordLayer):
|
| except:
|
| self._shutdown(False)
|
| raise
|
| - else:
|
| - raise TLSFaultError("No error!")
|
| -
|
| -
|
| - def _getKeyFromChain(self, certificate, settings):
|
| - #Get and check cert chain from the Certificate message
|
| - certChain = certificate.certChain
|
| - if not certChain or certChain.getNumCerts() == 0:
|
| - for result in self._sendError(AlertDescription.illegal_parameter,
|
| - "Other party sent a Certificate message without "\
|
| - "certificates"):
|
| - yield result
|
| -
|
| - #Get and check public key from the cert chain
|
| - publicKey = certChain.getEndEntityPublicKey()
|
| - if len(publicKey) < settings.minKeySize:
|
| - for result in self._sendError(AlertDescription.handshake_failure,
|
| - "Other party's public key too small: %d" % len(publicKey)):
|
| - yield result
|
| - if len(publicKey) > settings.maxKeySize:
|
| - for result in self._sendError(AlertDescription.handshake_failure,
|
| - "Other party's public key too large: %d" % len(publicKey)):
|
| - yield result
|
| -
|
| - yield publicKey, certChain
|
|
|