diff options
Diffstat (limited to 'src/lib/tlslite/TLSConnection.py')
-rwxr-xr-x | src/lib/tlslite/TLSConnection.py | 1600 |
1 files changed, 0 insertions, 1600 deletions
diff --git a/src/lib/tlslite/TLSConnection.py b/src/lib/tlslite/TLSConnection.py deleted file mode 100755 index d125f8f0a..000000000 --- a/src/lib/tlslite/TLSConnection.py +++ /dev/null @@ -1,1600 +0,0 @@ -""" -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 |