| """ |
| 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 |
| |
| |
| class TLSConnection(TLSRecordLayer): |
| """ |
| This class wraps a socket and provides TLS handshaking and data |
| transfer. |
| |
| To use this class, create a new instance, passing a connected |
| socket into the constructor. Then call some handshake function. |
| If the handshake completes without raising an exception, then a TLS |
| connection has been negotiated. You can transfer data over this |
| connection as if it were a socket. |
| |
| This class provides both synchronous and asynchronous versions of |
| its key functions. The synchronous versions should be used when |
| writing single-or multi-threaded code using blocking sockets. The |
| asynchronous versions should be used when performing asynchronous, |
| event-based I/O with non-blocking sockets. |
| |
| Asynchronous I/O is a complicated subject; typically, you should |
| 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}). |
| """ |
| |
| |
| def __init__(self, sock): |
| """Create a new TLSConnection instance. |
| |
| @param sock: The socket data will be transmitted on. The |
| socket should already be connected. It may be in blocking or |
| non-blocking mode. |
| |
| @type sock: L{socket.socket} |
| """ |
| 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. |
| |
| 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. |
| |
| 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 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. |
| |
| @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(srpParams=(username, password), |
| session=session, settings=settings, checker=checker) |
| 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. |
| |
| 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 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 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} |
| @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 |
| 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(certParams=(certChain, |
| privateKey), session=session, settings=settings, |
| checker=checker) |
| 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. |
| |
| 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 |
| 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 certCallback: callable |
| @param certCallback: The callback to be used if the server |
| requests client certificate authentication. |
| |
| @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} |
| @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(unknownParams=(srpCallback, |
| certCallback), session=session, settings=settings, |
| checker=checker) |
| 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=(), |
| session=None, settings=None, checker=None, |
| recursive=False): |
| |
| handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams, |
| certParams=certParams, unknownParams=unknownParams, |
| sharedKeyParams=sharedKeyParams, session=session, |
| settings=settings, recursive=recursive) |
| 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) |
| |
| #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 |
| |
| if srpParams: |
| srpUsername, password = srpParams |
| elif certParams: |
| clientCertChain, privateKey = certParams |
| elif unknownParams: |
| srpCallback, certCallback = unknownParams |
| elif sharedKeyParams: |
| session = Session()._createSharedKey(*sharedKeyParams) |
| |
| if not settings: |
| settings = HandshakeSettings() |
| settings = settings._filter() |
| |
| #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 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: |
| raise ValueError("Unrecognized certificate type") |
| |
| |
| if session: |
| if not session.valid(): |
| session = None #ignore non-resumable sessions... |
| elif session.resumable and \ |
| (session.srpUsername != srpUsername): |
| raise ValueError("Session username 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) |
| |
| #Initialize acceptable ciphersuites |
| cipherSuites = [] |
| if srpParams: |
| cipherSuites += CipherSuite.getSrpRsaSuites(settings.cipherNames) |
| cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames) |
| 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) |
| else: |
| cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
| |
| #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 |
| #ciphersuite must be one of the acceptable ciphersuites |
| if (not sharedKeyParams) and \ |
| session.cipherSuite not in cipherSuites: |
| raise ValueError("Session's cipher suite not consistent "\ |
| "with parameters") |
| else: |
| clientHello = ClientHello() |
| clientHello.create(settings.maxVersion, clientRandom, |
| session.sessionID, cipherSuites, |
| certificateTypes, session.srpUsername) |
| |
| #Or send ClientHello (without) |
| else: |
| clientHello = ClientHello() |
| clientHello.create(settings.maxVersion, clientRandom, |
| createByteArraySequence([]), cipherSuites, |
| certificateTypes, srpUsername) |
| for result in self._sendMsg(clientHello): |
| yield result |
| |
| #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 |
| |
| #Get the server version. Do this before anything else, so any |
| #error alerts will use the server's version |
| self.version = serverHello.server_version |
| |
| #Future responses from server must use this version |
| self._versionCheck = True |
| |
| #Check ServerHello |
| if serverHello.server_version < settings.minVersion: |
| for result in self._sendError(\ |
| AlertDescription.protocol_version, |
| "Too old version: %s" % str(serverHello.server_version)): |
| yield result |
| if serverHello.server_version > settings.maxVersion: |
| for result in self._sendError(\ |
| AlertDescription.protocol_version, |
| "Too new version: %s" % str(serverHello.server_version)): |
| yield result |
| if serverHello.cipher_suite not in cipherSuites: |
| for result in self._sendError(\ |
| AlertDescription.illegal_parameter, |
| "Server responded with incorrect ciphersuite"): |
| yield result |
| if serverHello.certificate_type not in certificateTypes: |
| for result in self._sendError(\ |
| AlertDescription.illegal_parameter, |
| "Server responded with incorrect certificate type"): |
| yield result |
| if serverHello.compression_method != 0: |
| for result in self._sendError(\ |
| AlertDescription.illegal_parameter, |
| "Server responded with incorrect compression method"): |
| yield result |
| |
| #Get the server nonce |
| serverRandom = serverHello.random |
| |
| #If the server agrees to resume |
| if session and session.sessionID and \ |
| 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: |
| for result in self._sendError(\ |
| AlertDescription.illegal_parameter,\ |
| "Server's ciphersuite doesn't match session"): |
| yield result |
| |
| #Set the session for this connection |
| self.session = session |
| |
| #Calculate pending connection states |
| self._calcPendingStates(clientRandom, serverRandom, |
| settings.cipherImplementations) |
| |
| #Exchange ChangeCipherSpec and Finished messages |
| for result in self._getFinished(): |
| yield result |
| for result in self._sendFinished(): |
| yield result |
| |
| #Mark the connection as open |
| self._handshakeDone(resumed=True) |
| |
| #If server DOES NOT agree to resume |
| else: |
| |
| if sharedKeyParams: |
| for result in self._sendError(\ |
| AlertDescription.user_canceled, |
| "Was expecting a shared-key resumption"): |
| yield result |
| |
| #We've already validated these |
| cipherSuite = serverHello.cipher_suite |
| certificateType = serverHello.certificate_type |
| |
| #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 |
| |
| for result in self._getMsg(ContentType.handshake, |
| HandshakeType.server_hello_done): |
| if result in (0,1): |
| yield result |
| else: |
| break |
| serverHelloDone = result |
| |
| #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 |
| |
| for result in self._getMsg(ContentType.handshake, |
| HandshakeType.server_key_exchange, cipherSuite): |
| if result in (0,1): |
| yield result |
| else: |
| break |
| serverKeyExchange = result |
| |
| for result in self._getMsg(ContentType.handshake, |
| HandshakeType.server_hello_done): |
| if result in (0,1): |
| yield result |
| else: |
| break |
| serverHelloDone = result |
| |
| #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() |
| |
| |
| #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 |
| |
| 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: |
| 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): |
| 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 |
| |
| #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 |
| |
| |
| #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 |
| |
| #Calculate pending connection states |
| self._calcPendingStates(clientRandom, serverRandom, |
| settings.cipherImplementations) |
| |
| #Exchange ChangeCipherSpec and Finished messages |
| for result in self._sendFinished(): |
| yield result |
| for result in self._getFinished(): |
| yield result |
| |
| #Mark the connection as open |
| self.session._setResumable(True) |
| self._handshakeDone(resumed=False) |
| |
| |
| |
| def handshakeServer(self, sharedKeyDB=None, verifierDB=None, |
| certChain=None, privateKey=None, reqCert=False, |
| sessionCache=None, settings=None, checker=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 |
| can also perform a combined SRP and server-certificate |
| handshake. |
| |
| 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 does not send a Hello Request message before |
| performing the handshake, so if re-handshaking is required, |
| the server must signal the client to begin the re-handshake |
| through some other means. |
| |
| 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 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} |
| @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} |
| @param certChain: The certificate chain to be used if the |
| client requests server certificate authentication. |
| |
| @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 |
| performs a client certificate authentication, the sessions's |
| clientCertChain attribute will be set. |
| |
| @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} |
| @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} |
| @param checker: A Checker instance. This instance will be |
| invoked to examine the other party's authentication |
| credentials, if the handshake completes succesfully. |
| |
| @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. |
| """ |
| for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, |
| certChain, privateKey, reqCert, sessionCache, settings, |
| checker): |
| pass |
| |
| |
| def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None, |
| certChain=None, privateKey=None, reqCert=False, |
| sessionCache=None, settings=None, checker=None): |
| """Start a server handshake operation on the TLS connection. |
| |
| This function returns a generator which behaves similarly to |
| handshakeServer(). 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 it will raise StopIteration |
| if the handshake operation is complete. |
| |
| @rtype: iterable |
| @return: A generator; see above for details. |
| """ |
| handshaker = self._handshakeServerAsyncHelper(\ |
| sharedKeyDB=sharedKeyDB, |
| verifierDB=verifierDB, certChain=certChain, |
| privateKey=privateKey, reqCert=reqCert, |
| sessionCache=sessionCache, settings=settings) |
| for result in self._handshakeWrapperAsync(handshaker, checker): |
| yield result |
| |
| |
| def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB, |
| certChain, privateKey, reqCert, sessionCache, |
| settings): |
| |
| self._handshakeStart(client=False) |
| |
| if (not sharedKeyDB) and (not verifierDB) and (not certChain): |
| 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 not settings: |
| settings = HandshakeSettings() |
| settings = settings._filter() |
| |
| #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 |
| |
| #Tentatively set version to most-desirable version, so if an error |
| #occurs parsing the ClientHello, this is what we'll use for the |
| #error alert |
| self.version = settings.maxVersion |
| |
| #Get ClientHello |
| for result in self._getMsg(ContentType.handshake, |
| HandshakeType.client_hello): |
| if result in (0,1): |
| yield result |
| else: |
| break |
| clientHello = result |
| |
| #If client's version is too low, reject it |
| 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 |
| |
| #If client's version is too high, propose my highest version |
| elif clientHello.client_version > settings.maxVersion: |
| self.version = settings.maxVersion |
| |
| else: |
| #Set the version to the client's version |
| self.version = clientHello.client_version |
| |
| #Get the client nonce; create server nonce |
| clientRandom = clientHello.random |
| serverRandom = getRandomBytes(32) |
| |
| #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 |
| |
| #If resumption was requested... |
| if clientHello.session_id and (sharedKeyDB or 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 |
| if sessionCache and not session: |
| try: |
| session = sessionCache[bytesToString(\ |
| clientHello.session_id)] |
| if session.sharedKey: |
| raise AssertionError() |
| if not session.resumable: |
| raise AssertionError() |
| #Check for consistency with ClientHello |
| if session.cipherSuite not in cipherSuites: |
| for result in self._sendError(\ |
| AlertDescription.handshake_failure): |
| yield result |
| if session.cipherSuite not in clientHello.cipher_suites: |
| for result in self._sendError(\ |
| AlertDescription.handshake_failure): |
| yield result |
| if clientHello.srp_username: |
| if clientHello.srp_username != session.srpUsername: |
| 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, |
| session.sessionID, session.cipherSuite, |
| certificateType) |
| for result in self._sendMsg(serverHello): |
| yield result |
| |
| #From here on, the client's messages must have the right version |
| self._versionCheck = True |
| |
| #Calculate pending connection states |
| self._calcPendingStates(clientRandom, serverRandom, |
| settings.cipherImplementations) |
| |
| #Exchange ChangeCipherSpec and Finished messages |
| for result in self._sendFinished(): |
| yield result |
| for result in self._getFinished(): |
| yield result |
| |
| #Mark the connection as open |
| self._handshakeDone(resumed=True) |
| return |
| |
| |
| #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: |
| for result in self._sendError(\ |
| AlertDescription.handshake_failure): |
| 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 we've selected an SRP suite, exchange keys and calculate |
| #premaster secret: |
| if cipherSuite in CipherSuite.srpSuites + CipherSuite.srpRsaSuites: |
| |
| #If there's no SRP username... |
| if not clientHello.srp_username: |
| |
| #Ask the client to re-send ClientHello with one |
| for result in self._sendMsg(Alert().create(\ |
| AlertDescription.missing_srp_username, |
| AlertLevel.warning)): |
| 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 |
| |
| #If client's version is too high, propose my highest version |
| elif clientHello.client_version > settings.maxVersion: |
| self.version = settings.maxVersion |
| |
| 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 |
| |
| #Get the client nonce; create server nonce |
| clientRandom = clientHello.random |
| serverRandom = getRandomBytes(32) |
| |
| #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 |
| |
| |
| #Get username |
| self.allegedSrpUsername = clientHello.srp_username |
| |
| #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 |
| |
| #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 = [] |
| msgs.append(ServerHello().create(self.version, serverRandom, |
| sessionID, cipherSuite, certificateType)) |
| msgs.append(Certificate(certificateType).create(serverCertChain)) |
| if 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 |
| |
| #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 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) |
| |
| 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 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") |
| |
| |
| #Create the session object |
| self.session = Session() |
| self.session._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) |
| |
| #Exchange ChangeCipherSpec and Finished messages |
| for result in self._getFinished(): |
| yield result |
| |
| #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): |
| yield result |
| |
| for result in self._sendFinished(): |
| yield result |
| |
| #Add the session object to the session cache |
| if sessionCache and sessionID: |
| sessionCache[bytesToString(sessionID)] = self.session |
| |
| #Mark the connection as open |
| self.session._setResumable(True) |
| self._handshakeDone(resumed=False) |
| |
| |
| def _handshakeWrapperAsync(self, handshaker, checker): |
| if not self.fault: |
| 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: |
| self._shutdown(False) |
| 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: |
| pass |
| 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 |