Commits

trevp  committed 659978b

pre-0.3.0

  • Participants
  • Parent commits 45441e4

Comments (0)

Files changed (47)

File tlslite/Checker.py

+"""Class for post-handshake certificate checking."""
 
 from utils.cryptomath import hashAndBase64
-from X509 import X509CertChain
-
-
-class AuthenticationError(Exception):
-    pass
-
-class NoAuthenticationError(AuthenticationError):
-    pass
-
-class AuthenticationTypeError(AuthenticationError):
-    pass
-
-class FingerprintError(AuthenticationError):
-    pass
-
-class AuthorizationError(AuthenticationError):
-    pass
-
-class ValidationError(AuthenticationError):
-    pass
-
+from X509 import X509
+from X509CertChain import X509CertChain
 
 
 class Checker:
-    def __init__(self, cryptoID=None, protocol=None, x509Fingerprint=None):
-        self.checkResumedSession = False
+    """This class is passed to a handshake function to check the other
+    party's certificate chain.
+
+    If a handshake function completes successfully, but the Checker
+    judges the other party's certificate chain to be missing or
+    inadequate, a subclass of
+    L{tlslite.errors.TLSAuthenticationError} will be raised.
+
+    Currently, the Checker can check either an X.509 or a cryptoID
+    chain (for the latter, cryptoIDlib must be installed).
+    """
+
+    def __init__(self, cryptoID=None, protocol=None, x509Fingerprint=None,
+                 checkResumedSession=False):
+        """Create a new Checker instance.
+
+        @type cryptoID: str
+        @param cryptoID: A cryptoID which the other party's certificate
+        chain must match.  Mutually exclusive with the 'x509Fingerprint'
+        argument.
+
+        @type protocol: str
+        @param protocol: A cryptoID protocol URI which the other
+        party's certificate chain must match.  Requires the 'cryptoID'
+        argument.
+
+        @type x509Fingerprint: str
+        @param x509Fingerprint: A hex-encoded X.509 end-entity
+        fingerprint which the other party's end-entity certificate must
+        match.  Mutually exclusive with the 'cryptoID'
+        argument.
+
+        @type checkResumedSession: bool
+        @param checkResumedSession: If resumed sessions should be
+        checked.  This defaults to False, on the theory that if the
+        session was checked once, we don't need to bother
+        re-checking it.
+        """
+
+        if cryptoID and x509Fingerprint:
+            raise ValueError()
         if cryptoID:
             import cryptoIDlib #So we raise an error here
         self.cryptoID = cryptoID
         self.protocol = protocol
         self.x509Fingerprint = x509Fingerprint
+        self.checkResumedSession = checkResumedSession
 
     def __call__(self, connection):
+        """Check a TLSConnection.
+
+        When a Checker is passed to a handshake function, this will
+        be called at the end of the function.
+
+        @type connection: L{tlslite.TLSConnection.TLSConnection}
+        @param connection: The TLSConnection to examine.
+
+        @raise tlslite.errors.TLSAuthenticationError: If the other
+        party's certificate chain is missing or bad.
+        """
         if not self.checkResumedSession and connection.resumed:
             return
 
         if self.cryptoID or self.x509Fingerprint:
-            if connection.client:
+            if connection._client:
                 chain = connection.session.serverCertChain
             else:
                 chain = connection.session.clientCertChain
 
-            if isinstance(chain, X509CertChain) and self.x509Fingerprint:
-                if chain.getFingerprint() != self.x509Fingerprint:
-                    raise FingerprintError("X.509 fingerprint mismatch: %s, %s" % (chain.getFingerprint(), self.x509Fingerprint))
+            if self.x509Fingerprint:
+                if isinstance(chain, X509CertChain):
+                    if chain.getFingerprint() != self.x509Fingerprint:
+                        raise TLSFingerprintError(\
+                            "X.509 fingerprint mismatch: %s, %s" % \
+                            (chain.getFingerprint(), self.x509Fingerprint))
+                elif chain:
+                    raise TLSAuthenticationTypeError()
+                else:
+                    raise TLSNoAuthenticationError()
             elif self.cryptoID:
                 import cryptoIDlib
                 if isinstance(chain, cryptoIDlib.CertChain):
                     if chain.cryptoID != self.cryptoID:
-                        raise FingerprintError("cryptoID mismatch: %s, %s" % (chain.cryptoID, self.cryptoID))
+                        raise TLSFingerprintError(\
+                            "cryptoID mismatch: %s, %s" % \
+                            (chain.cryptoID, self.cryptoID))
                     if self.protocol:
                         if not chain.checkProtocol(self.protocol):
-                            raise AuthorizationError("cryptoID protocol mismatch")
+                            raise TLSAuthorizationError(\
+                            "cryptoID protocol mismatch")
                     if not chain.validate():
-                        print chain.write()
-                        raise ValidationError("cryptoID validation failure")
-                    return True
-            elif chain:
-                raise AuthenticationTypeError()
-            else:
-                raise NoAuthenticationError()
+                        raise TLSValidationError("cryptoID validation failure")
+                elif chain:
+                    raise TLSAuthenticationTypeError()
+                else:
+                    raise TLSNoAuthenticationError()
+

File tlslite/FileObject.py

-
-#Copied-and-modifed from socket.py
-#This provides a FileObject based on a TLSConnection.
-#Notably, it refcounts based on close()
+"""Class returned by TLSConnection.makefile()."""
 
 class FileObject:
+    """This class provides a file object interface to a
+    L{tlslite.TLSConnection.TLSConnection}.
 
-    default_bufsize = 8192
+    Call makefile() on a TLSConnection to create a FileObject instance.
+
+    This class was copied, with minor modifications, from the
+    _fileobject class in socket.py.  Note that fileno() is not
+    implemented."""
+
+    default_bufsize = 16384 #TREV: changed from 8192
 
     def __init__(self, sock, mode='rb', bufsize=-1):
         self._sock = sock
     def close(self):
         try:
             if self._sock:
-                for result in self._sock.decrefAsync(): #TREV
+                for result in self._sock._decrefAsync(): #TREV
                     pass
         finally:
             self._sock = None
             self._wbuf = []
             self._sock.sendall(buffer)
 
-    def fileno(self):
-        raise NotImplementedError() #TREV
+    #def fileno(self):
+    #    raise NotImplementedError() #TREV
 
     def write(self, data):
         data = str(data) # XXX Should really reject non-string non-buffers

File tlslite/HandshakeSettings.py

+"""Class for setting handshake parameters."""
 
 from constants import CertificateType
 from utils import cryptomath
 from utils import cipherfactory
 
 class HandshakeSettings:
+    """This class encapsulates various parameters that can be used with
+    a TLS handshake.
+    @sort: minKeySize, maxKeySize, cipherNames, certificateTypes,
+    minVersion, maxVersion
+
+    @type minKeySize: int
+    @ivar minKeySize: The minimum bit length for asymmetric keys.
+
+    If the other party tries to use SRP, RSA, or Diffie-Hellman
+    parameters smaller than this length, an alert will be
+    signalled.  The default is 1023.
+
+    @type maxKeySize: int
+    @ivar maxKeySize: The maximum bit length for asymmetric keys.
+
+    If the other party tries to use SRP, RSA, or Diffie-Hellman
+    parameters larger than this length, an alert will be signalled.
+    The default is 8193.
+
+    @type cipherNames: list
+    @ivar cipherNames: The allowed ciphers, in order of preference.
+
+    The allowed values in this list are 'aes256', 'aes128', '3des', and
+    'rc4'.  If these settings are used with a client handshake, they
+    determine the order of the ciphersuites offered in the ClientHello
+    message.
+
+    If these settings are used with a server handshake, the server will
+    choose whichever ciphersuite matches the earliest entry in this
+    list.
+
+    NOTE:  If '3des' is used in this list, but TLS Lite can't find an
+    add-on library that supports 3DES, then '3des' will be silently
+    removed.
+
+    The default value is ['aes256', 'aes128', '3des', 'rc4'].
+
+    @type certificateTypes: list
+    @ivar certificateTypes: The allowed certificate types, in order of
+    preference.
+
+    The allowed values in this list are 'x509' and 'cryptoID'.  This
+    list is only used with a client handshake.  The client will
+    advertise to the server which certificate types are supported, and
+    will check that the server uses one of the appropriate types.
+
+    NOTE:  If 'cryptoID' is used in this list, but cryptoIDlib is not
+    installed, then 'cryptoID' will be silently removed.
+
+    @type minVersion: tuple
+    @ivar minVersion: The minimum allowed SSL/TLS version.
+
+    This variable can be set to (3,0) for SSL 3.0, or (3,1) for
+    TLS 1.0.  If the other party wishes to use a lower version, a
+    protocol_version alert will be signalled.  The default is (3,0).
+
+    @type maxVersion: tuple
+    @ivar maxVersion: The maximum allowed SSL/TLS version.
+
+    This variable can be set to (3,0) for SSL 3.0, or (3,1) for
+    TLS 1.0.  If the other party wishes to use a higher version, a
+    protocol_version alert will be signalled.  The default is (3,1).
+    """
     def __init__(self):
-        self.minKeySize = 1024
+        self.minKeySize = 1023
         self.maxKeySize = 8193
         self.cipherNames = ["aes256", "aes128", "3des", "rc4"]
-        self.cipherImplementations = ["cryptlib", "openssl", "pycrypto", "python"]
+        self.cipherImplementations = ["cryptlib", "openssl", "pycrypto",
+                                      "python"]
         self.certificateTypes = ["x509", "cryptoID"]
         self.minVersion = (3,0)
         self.maxVersion = (3,1)
 
     #Filters out options that are not supported
-    def filter(self):
+    def _filter(self):
         other = HandshakeSettings()
         other.minKeySize = self.minKeySize
         other.maxKeySize = self.maxKeySize
         try:
             import cryptoIDlib
         except ImportError:
-            other.certificateTypes = [e for e in self.certificateTypes if e != "cryptoID"]
+            other.certificateTypes = [e for e in self.certificateTypes \
+                                      if e != "cryptoID"]
         if len(other.certificateTypes)==0:
             raise ValueError("No supported certificate types")
 
         if not cryptomath.cryptlibpyLoaded:
-            other.cipherImplementations = [e for e in self.cipherImplementations if e != "cryptlib"]
+            other.cipherImplementations = [e for e in \
+                self.cipherImplementations if e != "cryptlib"]
         if not cryptomath.m2cryptoLoaded:
-            other.cipherImplementations = [e for e in other.cipherImplementations if e != "openssl"]
+            other.cipherImplementations = [e for e in \
+                other.cipherImplementations if e != "openssl"]
+        if not cryptomath.pycryptoLoaded:
+            other.cipherImplementations = [e for e in \
+                other.cipherImplementations if e != "pycrypto"]
         if len(other.cipherImplementations)==0:
             raise ValueError("No supported cipher implementations")
 
         if other.minVersion > other.maxVersion:
             raise ValueError("Versions set incorrectly")
 
-        if not other.minVersion in ((3,0), (3,1), (3,2)):
+        if not other.minVersion in ((3,0), (3,1)):
             raise ValueError("minVersion set incorrectly")
 
-        if not other.maxVersion in ((3,0), (3,1), (3,2)):
+        if not other.maxVersion in ((3,0), (3,1)):
             raise ValueError("maxVersion set incorrectly")
 
         return other
 
-    def getCertificateTypes(self):
+    def _getCertificateTypes(self):
         l = []
         for ct in self.certificateTypes:
             if ct == "x509":
             elif ct == "cryptoID":
                 l.append(CertificateType.cryptoID)
             else:
-                assert()
+                raise AssertionError()
         return l

File tlslite/Session.py

+"""Class representing a TLS session."""
 
 from utils.jython_compat import *
 from mathtls import *
 from constants import *
 
 class Session:
+    """
+    This class represents a TLS session.
+
+    TLS distinguishes between connections and sessions.  A new
+    handshake creates both a connection and a session.  Data is
+    transmitted over the connection.
+
+    The session contains a more permanent record of the handshake.  The
+    session can be inspected to determine handshake results.  The
+    session can also be used to create a new connection through
+    "session resumption". If the client and server both support this,
+    they can create a new connection based on an old session without
+    the overhead of a full handshake.
+
+    The session for a L{tlslite.TLSConnection.TLSConnection} can be
+    retrieved from the connection's 'session' attribute.
+
+    @type srpUsername: str
+    @ivar srpUsername: The client's SRP username (or None).
+
+    @type sharedKeyUsername: str
+    @ivar sharedKeyUsername: The client's shared-key username (or
+    None).
+
+    @type clientCertChain: L{tlslite.X509CertChain.X509CertChain} or
+    L{cryptoIDlib.CertChain}
+    @ivar clientCertChain: The client's certificate chain (or None).
+
+    @type serverCertChain: L{tlslite.X509CertChain.X509CertChain} or
+    L{cryptoIDlib.CertChain}
+    @ivar serverCertChain: The server's certificate chain (or None).
+    """
+
     def __init__(self):
         self.masterSecret = createByteArraySequence([])
         self.sessionID = createByteArraySequence([])
         self.resumable = False
         self.sharedKey = False
 
-    def clone(self):
+    def _clone(self):
         other = Session()
         other.masterSecret = self.masterSecret
         other.sessionID = self.sessionID
         other.resumable = self.resumable
         other.sharedKey = self.sharedKey
         return other
-       
-    def calcMasterSecret(self, version, premasterSecret, clientRandom, serverRandom):
+
+    def _calcMasterSecret(self, version, premasterSecret, clientRandom,
+                         serverRandom):
         if version == (3,0):
-            self.masterSecret = PRF_SSL(premasterSecret, concatArrays(clientRandom, serverRandom), 48)
+            self.masterSecret = PRF_SSL(premasterSecret,
+                                concatArrays(clientRandom, serverRandom), 48)
         elif version == (3,1) or self.version == (3,2):
-            self.masterSecret = PRF(premasterSecret, "master secret", concatArrays(clientRandom,serverRandom), 48)
+            self.masterSecret = PRF(premasterSecret, "master secret",
+                                concatArrays(clientRandom, serverRandom), 48)
         else:
-            assert()
-        
+            raise AssertionError()
+
     def valid(self):
+        """If this session can be used for session resumption.
+
+        @rtype: bool
+        @return: If this session can be used for session resumption.
+        """
         return self.resumable or self.sharedKey
-        
-    def setResumable(self, boolean):
+
+    def _setResumable(self, boolean):
         #Only let it be set if this isn't a shared key
         if not self.sharedKey:
             #Only let it be set to True if the sessionID is non-null
                 self.resumable = boolean
 
     def getCipherName(self):
-        if self.cipherSuite in aes128Suites:
+        """Get the name of the cipher used with this connection.
+
+        @rtype: str
+        @return: The name of the cipher used with this connection.
+        Either 'aes128', 'aes256', 'rc4', or '3des'.
+        """
+        if self.cipherSuite in CipherSuite.aes128Suites:
             return "aes128"
-        elif self.cipherSuite in aes256Suites:
+        elif self.cipherSuite in CipherSuite.aes256Suites:
             return "aes256"
-        elif self.cipherSuite in rc4Suites:
+        elif self.cipherSuite in CipherSuite.rc4Suites:
             return "rc4"
+        elif self.cipherSuite in CipherSuite.tripleDESSuites:
+            return "3des"
         else:
             return None
-        
-    def createSharedKey(self, sharedKeyUsername, sharedKey):
+
+    def _createSharedKey(self, sharedKeyUsername, sharedKey):
         if len(sharedKeyUsername)>16:
             raise ValueError()
         if len(sharedKey)>47:
             raise ValueError()
-        
+
         self.sharedKeyUsername = sharedKeyUsername
-            
+
         self.sessionID = createByteArrayZeros(16)
         for x in range(len(sharedKeyUsername)):
             self.sessionID[x] = ord(sharedKeyUsername[x])
-            
+
         premasterSecret = createByteArrayZeros(48)
         sharedKey = chr(len(sharedKey)) + sharedKey
         for x in range(48):
             premasterSecret[x] = ord(sharedKey[x % len(sharedKey)])
-            
-        self.masterSecret = PRF(premasterSecret, "shared secret", createByteArraySequence([]), 48)
+
+        self.masterSecret = PRF(premasterSecret, "shared secret",
+                                createByteArraySequence([]), 48)
         self.sharedKey = True
         return self
-        
-        
+
+

File tlslite/SessionCache.py

+"""Class for caching TLS sessions."""
 
 import thread
 import time
 
-#This holds Session instances.  References to these instances
-#are also held by the caller, who may change the 'resumable'
-#flag, so the SessionCache must return the same instances
-#it was passed in.
+class SessionCache:
+    """This class is used by the server to cache TLS sessions.
 
-class SessionCache:
+    Caching sessions allows the client to use TLS session resumption
+    and avoid the expense of a full handshake.  To use this class,
+    simply pass a SessionCache instance into the server handshake
+    function.
+
+    This class is thread-safe.
+    """
+
+    #References to these instances
+    #are also held by the caller, who may change the 'resumable'
+    #flag, so the SessionCache must return the same instances
+    #it was passed in.
+
     def __init__(self, maxEntries=10000, maxAge=14400):
+        """Create a new SessionCache.
+
+        @type maxEntries: int
+        @param maxEntries: The maximum size of the cache.  When this
+        limit is reached, the oldest sessions will be deleted as
+        necessary to make room for new ones.  The default is 10000.
+
+        @type maxAge: int
+        @param maxAge:  The number of seconds before a session expires
+        from the cache.  The default is 14400 (i.e. 4 hours)."""
+
         self.lock = thread.allocate_lock()
-        self.entriesDict = {} # Maps sessionIDs to sessions
-        self.entriesList = [(None,None)] * maxEntries #Circular list of (sessionID, timestamp) pairs
+
+        # Maps sessionIDs to sessions
+        self.entriesDict = {}
+
+        #Circular list of (sessionID, timestamp) pairs
+        self.entriesList = [(None,None)] * maxEntries
+
         self.firstIndex = 0
         self.lastIndex = 0
         self.maxAge = maxAge
-        
+
     def __getitem__(self, sessionID):
         self.lock.acquire()
         try:
-            self.purge() #Delete old items, so we're assured of a new one
+            self._purge() #Delete old items, so we're assured of a new one
             session = self.entriesDict[sessionID]
-            
+
             #When we add sessions they're resumable, but it's possible
             #for the session to be invalidated later on (if a fatal alert
             #is returned), so we have to check for resumability before
             #returning the session.
-            
+
             if session.valid():
                 return session
             else:
                 raise KeyError()
         finally:
             self.lock.release()
-        
-        
+
+
     def __setitem__(self, sessionID, session):
         self.lock.acquire()
         try:
             self.entriesList[self.lastIndex] = (sessionID, time.time())
             self.lastIndex = (self.lastIndex+1) % len(self.entriesList)
 
-            #If the cache is full, we delete the oldest element to make an empty space
+            #If the cache is full, we delete the oldest element to make an
+            #empty space
             if self.lastIndex == self.firstIndex:
                 del(self.entriesDict[self.entriesList[self.firstIndex][0]])
                 self.firstIndex = (self.firstIndex+1) % len(self.entriesList)
-        finally:                
+        finally:
             self.lock.release()
 
     #Delete expired items
-    def purge(self):
+    def _purge(self):
         currentTime = time.time()
-        
-        #Search through the circular list, deleting expired elements until 
-        #we reach a non-expired element.  Since elements in list are 
-        #ordered in time, we can break once we reach the first non-expired element
+
+        #Search through the circular list, deleting expired elements until
+        #we reach a non-expired element.  Since elements in list are
+        #ordered in time, we can break once we reach the first non-expired
+        #element
         index = self.firstIndex
         while index != self.lastIndex:
             if currentTime - self.entriesList[index][1] > self.maxAge:

File tlslite/SharedKeyDB.py

+"""Class for storing shared keys."""
 
-import anydbm
 from utils.cryptomath import *
 from utils.jython_compat import *
 from mathtls import *
 from Session import Session
-import thread
+from BaseDB import BaseDB
 
-class SharedKeyDB:
+class SharedKeyDB(BaseDB):
+    """This class represent an in-memory or on-disk database of shared
+    keys.
+
+    A SharedKeyDB can be passed to a server handshake function to
+    authenticate a client based on one of the shared keys.
+
+    This class is thread-safe.
+    """
+
     def __init__(self, filename=None):
-        self.filename = filename
-        if self.filename:
-            self.db = None
-        else:
-            self.db = {}
-        self.lock = thread.allocate_lock()        
-    
-    def create(self):
-        if self.filename:
-            self.db = anydbm.open(self.filename, "n") #raises anydbm.error
-            self.db["--Reserved--type"] = "sharedkey"
-            self.db.sync()
-        else:
-            self.db = {}
+        """Create a new SharedKeyDB.
 
-    def open(self):      
-        if not self.filename:
-            raise ValueError("Can only open on-disk shared key databases")
-        self.db = anydbm.open(self.filename, "w") #raises anydbm.error
-        try:
-            if self.db["--Reserved--type"] != "sharedkey":
-                raise ValueError("Not a shared key database")
-        except KeyError:
-            raise ValueError("Not a shared key database")
-        
-    def __getitem__(self, username):
-        if self.db == None:
-            raise KeyError("DB not open")
+        @type filename: str
+        @param filename: Filename for an on-disk database, or None for
+        an in-memory database.
+        """
+        BaseDB.__init__(self, filename, "shared key")
+
+    def _getItem(self, username, valueStr):
+        session = Session()
+        session._createSharedKey(username, valueStr)
+        return session
+
+    def __setitem__(self, username, sharedKey):
+        """Add a shared key to the database.
+
+        @type username: str
+        @param username: The username to associate the shared key with.
+        Must be less than or equal to 16 characters in length, and must
+        not already be in the database.
+
+        @type sharedKey: str
+        @param sharedKey: The shared key to add.  Must be less than 48
+        characters in length.
+        """
+        BaseDB.__setitem__(self, username, sharedKey)
+
+    def _setItem(self, username, value):
         if len(username)>16:
-            raise KeyError("username too long for a shared key")
+            raise ValueError("username too long")
+        if len(value)>=48:
+            raise ValueError("shared key too long")
+        return value
 
-        self.lock.acquire()
-        try:
-            try:
-                sharedKey = self.db[username]
-            except KeyError:
-                sharedKey = None
-        finally:
-            self.lock.release()        
-
-        if not sharedKey:
-            raise KeyError("DB not open")
-        
-        session = Session()
-        session.createSharedKey(username, sharedKey)
-        return session
-        
-    def __setitem__(self, username, sharedKey):
-        if self.db == None:
-            raise KeyError("DB not open")
-        if len(username)>16:
-            raise KeyError("username too long for a shared key")
-        
-        self.lock.acquire()
-        try:
-            if self.db.has_key(username):
-                raise KeyError()
-            self.db[username] = sharedKey
-            assert(self.db[username])
-            if self.filename:
-                self.db.sync()
-        finally:
-            self.lock.release()
-        
-    def __delitem__(self, username):
-        if self.db == None:
-            raise KeyError("DB not open")
-        self.lock.acquire()
-        try:
-            if not self.db.has_key(username):
-                raise KeyError()
-            del(self.db[username])
-            if self.filename:
-                self.db.sync()
-        finally:
-            self.lock.release()  
-
-    def __contains__(self, username):
-        self.lock.acquire()
-        try:
-            return self.db.has_key(username)
-        finally:
-            self.lock.release()
-            
-    def check(self, username, sharedKey):
-        try:
-            checkKey = self.db[username]
-        except KeyError:
-            return False
-        return (checkKey == sharedKey)
-        
-    def keys(self):
-        self.lock.acquire()
-        try:
-            usernames = self.db.keys()
-        finally:
-            self.lock.release()  
-        usernames = [u for u in usernames if not u.startswith("--Reserved--")]
-        return usernames        
+    def _checkItem(self, value, username, param):
+        return value == param

File tlslite/TLSConnection.py

-
-from TLSRecordLayer import *
+"""
+Main class for TLS Lite.
+"""
+import socket
+from utils.jython_compat import formatExceptionTrace
+from TLSRecordLayer import TLSRecordLayer
 from Session import Session
-from faults import *
-import time
-from utils import keyfactory
+from constants import *
+from utils.cryptomath import getRandomBytes
+from errors import TLSAlert
+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 getCipherName(self):
-        if not self.currentWriteState.encContext:
-            return None
-        return self.currentWriteState.encContext.name
+    def handshakeClientSRP(self, username, password, session=None,
+                           settings=None, checker=None, async=False):
+        """Perform an SRP handshake in the role of client.
 
-    def getCipherImplementation(self):
-        if not self.currentWriteState.encContext:
-            return None
-        return self.currentWriteState.encContext.implementation
+        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.
 
-    def handshakeClientSRP(self, username, password, session=None, settings=None, checker=None, async=False):
-        handshaker = self.handshakeClientAsync(srpParams=(username, password), session=session, settings=settings, checker=checker)
+        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, privateKey, session=None, settings=None, checker=None, async=False):
-        handshaker = self.handshakeClientAsync(certParams=(certChain, privateKey), session=session, settings=settings, checker=checker)
+    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
+        whatsoever.
+
+        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}
+        @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):
-        handshaker = self.handshakeClientAsync(unknownParams=(srpCallback, certCallback), session=session, settings=settings, checker=checker)
+    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 a 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.
+
+        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 handshakeClientNoAuth(self, session=None, settings=None, checker=None, async=False):
-        handshaker = self.handshakeClientAsync(session=session, settings=settings, checker=checker)
+    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.
+
+        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 handshakeClientSharedKey(self, username, sharedKey, settings=None, async=False):
-        handshaker = self.handshakeClientAsync(sharedKeyParams=(username, sharedKey), settings=settings)
-        if async:
-            return handshaker
-        for result in handshaker:
-            pass
+    def _handshakeClientAsync(self, srpParams=(), certParams=(),
+                             unknownParams=(), sharedKeyParams=(),
+                             session=None, settings=None, checker=None,
+                             recursive=False):
 
-    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
 
-        try:
-            #Zeroize handshake hashes; set client member variable
-            #Ignore if called recursively as part of SRP missing_srp_username idiom
-            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 directly)
-            #settings               # directly
+    def _handshakeClientAsyncHelper(self, srpParams, certParams, unknownParams,
+                                   sharedKeyParams, session, settings, recursive):
+        if not recursive:
+            self._handshakeStart(client=True)
 
-            if srpParams:
-                srpUsername, password = srpParams
-            elif certParams:
-                clientCertChain, privateKey = certParams
-            elif unknownParams:
-                srpCallback, certCallback = unknownParams
-            elif sharedKeyParams:
-                session = Session().createSharedKey(*sharedKeyParams)
+        #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 not settings:
-                settings = HandshakeSettings()
-            settings = settings.filter()
+        if srpParams:
+            srpUsername, password = srpParams
+        elif certParams:
+            clientCertChain, privateKey = certParams
+        elif unknownParams:
+            srpCallback, certCallback = unknownParams
+        elif sharedKeyParams:
+            session = Session()._createSharedKey(*sharedKeyParams)
 
-            #Validate parameters
-            if srpUsername and not password:
-                raise ValueError("Caller passed an srpUsername but no password")
-            if password and not srpUsername:
-                raise ValueError("Caller passed a password but no srpUsername")
+        if not settings:
+            settings = HandshakeSettings()
+        settings = settings._filter()
 
-            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")
+        #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:
-                foundType = False
-                try:
-                    import cryptoIDlib
-                    if isinstance(clientCertChain, cryptoIDlib.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"]
+        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
+                if isinstance(clientCertChain, cryptoIDlib.CertChain):
+                    if "cryptoID" not in settings.certificateTypes:
+                        raise ValueError("Client certificate doesn't "\
+                                         "match Handshake Settings")
+                    settings.certificateTypes = ["cryptoID"]
                     foundType = True
-                if not foundType:
-                    raise ValueError("Unrecognized certificate type")
+            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():
-                    #raise ValueError("Session is not resumable")
-                    session = None #ignore non-resumable sessions...
-                elif session.resumable and (session.srpUsername != srpUsername):
-                    raise ValueError("Session and parameter usernames don't match")
+        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 == ClientSRPFault.badUsername:
-                srpUsername += "GARBAGE"
-            if password and self.fault == ClientSRPFault.badPassword:
-                password += "GARBAGE"
-            if sharedKeyParams:
-                identifier = sharedKeyParams[0]
-                sharedKey = sharedKeyParams[1]
-                if self.fault == ClientSharedKeyFault.badIdentifier:
-                    identifier += "GARBAGE"
-                    session = Session().createSharedKey(identifier, sharedKey)
-                elif self.fault == ClientSharedKeyFault.badSharedKey:
-                    sharedKey += "GARBAGE"
-                    session = Session().createSharedKey(identifier, sharedKey)
+        #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
+        #Initialize locals
+        serverCertChain = None
+        cipherSuite = 0
+        certificateType = CertificateType.x509
+        premasterSecret = None
 
-            #Get client nonce
-            clientRandom = getRandomBytes(32)
+        #Get client nonce
+        clientRandom = getRandomBytes(32)
 
-            #Initialize acceptable ciphersuites
-            cipherSuites = []
-            if srpParams:
-                cipherSuites += getSrpRsaSuites(settings.cipherNames)
-                cipherSuites += getSrpSuites(settings.cipherNames)
-            elif certParams:
-                cipherSuites += getRsaSuites(settings.cipherNames)
-            elif unknownParams:
-                if srpCallback:
-                    cipherSuites += getSrpRsaSuites(settings.cipherNames)
-                    cipherSuites += getSrpSuites(settings.cipherNames)
-                cipherSuites += getRsaSuites(settings.cipherNames)
-            elif sharedKeyParams:
-                cipherSuites += getRsaSuites(settings.cipherNames)
-            else:
-                cipherSuites += getRsaSuites(settings.cipherNames)
+        #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()
+        #Initialize acceptable certificate types
+        certificateTypes = settings._getCertificateTypes()
 
-            #Tentatively set the version to the client's favorite 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
+        #Tentatively set the version to the client's favorite 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 currently-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)
+        #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, createByteArraySequence([]),
-                                   cipherSuites, certificateTypes, srpUsername)
-            for result in self.sendMsg(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 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
+        #Get the server nonce
+        serverRandom = serverHello.random
 
-            if isinstance(msg, ServerHello):
-                serverHello = msg
-            elif isinstance(msg, Alert):
-                alert = msg
+        #If the server agrees to resume
+        if session and session.sessionID and \
+                       serverHello.session_id == session.sessionID:
 
-                #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 ciphersuites
-                #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.handshakeClientAsync(srpParams=srpParams, settings=settings, checker=checker, recursive=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
-            self.versionCheck = True #Future responses from server must use this version
-
-            #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"):
+            #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
 
-            #Get the server nonce
-            serverRandom = serverHello.random
+            #Set the session for this connection
+            self.session = session
 
-            #If the server agrees to resume
-            if session and session.sessionID and serverHello.session_id == session.sessionID:
+            #Calculate pending connection states
+            self._calcPendingStates(clientRandom, serverRandom,
+                                   settings.cipherImplementations)
 
-                #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
+            #Exchange ChangeCipherSpec and Finished messages
+            for result in self._getFinished():
+                yield result
+            for result in self._sendFinished():
+                yield result
 
-                #Set the session for this connection
-                self.session = session
+            #Mark the connection as open
+            self._handshakeDone(resumed=True)
 
-                #Calculate pending connection states
-                self.calcPendingStates(clientRandom, serverRandom, settings.cipherImplementations)
+        #If server DOES NOT agree to resume
+        else:
 
-                #Exchange ChangeCipherSpec and Finished messages
-                for result in self.getFinished():
-                    yield result
-                for result in self.sendFinished():
+            if sharedKeyParams:
+                for result in self._sendError(\
+                        AlertDescription.user_canceled,
+                        "Was expecting a shared-key resumption"):
                     yield result
 
-                #Mark the connection as open
-                self.handshakeDone(resumed=True)
+            #We've already validated these
+            cipherSuite = serverHello.cipher_suite
+            certificateType = serverHello.certificate_type
 
-            #If server DOES NOT agree to resume
-            else:
+            #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
 
-                #We've already validated these
-                cipherSuite = serverHello.cipher_suite
-                certificateType = serverHello.certificate_type
+                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 suite...
-                if cipherSuite in 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 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()
 
-                #If the server chose an SRP+RSA suite...
-                elif cipherSuite in 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 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:
-                    assert()
+            #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
 
-                #Do this check after we've read all the server hello messages, so the error
-                #won't appear on the server while it's in the middle of sending
-                if sharedKeyParams:
-                    for result in self.sendError(AlertDescription.user_canceled, "Was expecting a shared-key resumption, but server declined"):
+                if (g,N) not in goodGroupParameters:
+                    for result in self._sendError(\
+                            AlertDescription.handshake_failure,
+                            "Unknown group parameters"):
+                        yield result
+                if numBits(N) < settings.minKeySize:
+                    for result in self._sendError(\
+                            AlertDescription.insufficient_security,
+                            "N value is too small: %d" % numBits(N)):
+                        yield result
+                if numBits(N) > settings.maxKeySize:
+                    for result in self._sendError(\
+                            AlertDescription.handshake_failure,
+                            "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)
 
-                #Calculate SRP premaster secret, if server chose an SRP or SRP+RSA suite
-                if cipherSuite in srpSuites + srpRsaSuites:
-                    #Get and check the server's group parameters and B value
-                    g = serverKeyExchange.srp_g
-                    N = serverKeyExchange.srp_N
-                    s = serverKeyExchange.srp_s
-                    B = serverKeyExchange.srp_B
-
-                    if (g,N) not in goodGroupParameters:
-                        for result in self.sendError(AlertDescription.handshake_failure, "Unknown group parameters"):
-                            yield result
-                    if numBits(N) < settings.minKeySize:
-                        for result in self.sendError(AlertDescription.insufficient_security, "N value is too small: %d" % numBits(N)):
-                            yield result
-                    if numBits(N) > settings.maxKeySize:
-                        for result in self.sendError(AlertDescription.handshake_failure, "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"):
+                    #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
 
-                    #Check the server's signature, if server chose an SRP+RSA suite
-                    if cipherSuite in srpRsaSuites:
-                        #Hash ServerKeyExchange/ServerSRPParams along with Nonces
-                        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(A, B)
-
-                    #Calculate premaster secret
-                    S = powMod((B - (3*v)) % N, a+(u*x), N)
-
-                    if self.fault == ClientSRPFault.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 rsaSuites:
-
-                    #Handle the presence or absence of a CertificateRequest
-                    if certificateRequest:
-                        if certParams: #server asks for a cert - we've got one
-                            pass
-                        elif unknownParams and certCallback: #server asks for a cert - go get one
-                            certParamsNew = certCallback()
-                            if certParamsNew:
-                                clientCertChain, privateKey = certParamsNew
-                        else: #server asks for a cert - we don't have one!
-                            pass #Should this be an error?  Let's say no.
-                            #self.sendError(AlertDescription.user_canceled, "Not expecting a certificate request, got one")
-                    else:
-                        if certParams: #server fails to asks for a cert - but we have one!
-                            for result in self.sendError(AlertDescription.user_canceled, "Expecting a certificate request, didn't get one"):
-                                yield result
-                        elif unknownParams: #server doesn't ask for a cert - so don't get one
-                            pass
-                        else: #server doesn't ask for a cert, and we don't have one
-                            pass
-
                     #Get server's public key from the Certificate message
-                    for result in self.getKeyFromChain(serverCertificate, settings):
+                    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==ClientNoAuthFault.badPremasterPadding:
-                        premasterSecret[0] = 5
-                    if self.fault==ClientNoAuthFault.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...
-                    if certificateRequest:
-                        if clientCertChain:
-                            #with certificates
-
-                            #Check to make sure we have the same type of certificates
-                            #the server requested
-                            if certificateType == CertificateType.x509:
-                                if not isinstance(clientCertChain, X509CertChain):
-                                    for result in self.sendError(AlertDescription.handshake_failure, "Client certificate chain is of wrong type"):
-                                        yield result
-                            elif certificateType == CertificateType.cryptoID:
-                                if not isinstance(clientCertChain, cryptoIDlib.CertChain):
-                                    for result in self.sendError(AlertDescription.handshake_failure, "Client certificate chain is of wrong type"):
-                                        yield result
-
-                            clientCertificate = Certificate(certificateType)
-                            clientCertificate.create(clientCertChain)
-                        else:
-                            #or empty (if the certCallback returned None)
-                            clientCertificate = Certificate(certificateType)
-
-                        for result in self.sendMsg(clientCertificate):
+                    #Verify signature
+                    if not publicKey.verify(sigBytes, hashBytes):
+                        for result in self._sendError(\
+                                AlertDescription.decrypt_error,
+                                "Signature failed to verify"):
                             yield result
 
-                    #Send ClientKeyExchange
-                    for result in self.sendMsg(ClientKeyExchange(cipherSuite, self.version).createRSA(encryptedPreMasterSecret)):
-                        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
-                            #checking the CertificateVerify
-                            session = Session()
-                            session.calcMasterSecret(self.version, premasterSecret, clientRandom, serverRandom)
-                            verifyBytes = self.calcSSLHandshakeHash(session.masterSecret, "")
-                        elif self.version in ((3,0), (3,1)):
-                            verifyBytes = stringToBytes(self.handshake_md5.digest()+self.handshake_sha.digest())
-                        if self.fault == ClientCertFault.badVerifyMessage:
-                            verifyBytes[0] = ((verifyBytes[0]+1) % 256)
-                        signedBytes = privateKey.sign(verifyBytes)
-                        certificateVerify = CertificateVerify().create(signedBytes)
-                        for result in self.sendMsg(certificateVerify):
-                            yield result
+                #Calculate client's ephemeral DH values (a, A)
+                a = bytesToNumber(getRandomBytes(32))
+                A = powMod(g, a, N)
 
-
-                #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)
-
-        except TLSAlert, alert:
-            if self.fault:
-                #If faults are turned on, we need to check that the right error occurred:
-                if alert.description not in faultAlerts[self.fault]:
-                    raise FaultError(str(alert))
-                else:
-                    pass
-            else:
-                self.shutdown(False)
-                raise
-
-        except socket.error, e:
-            if self.fault:
-                raise FaultError("socket error!")
-
-            #The socket was closed unexpectedly
-            self.shutdown(False)
-            raise
-        except ValueError, e:
-            #Bad parameters
-            self.shutdown(False)
-            raise
-        except Exception, e:
-            #Catch unhandled exceptions; shouldn't happen
-            for result in self.sendError(AlertDescription.internal_error, formatExceptionTrace(e)):
-                yield result
-        else:
-            if checker:
-                checker(self) #Check the other side's credentials
-
-            if self.fault:
-                raise FaultError("No error!")
-
-
-
-    def handshakeServer(self, sharedKeyDB=None, verifierDB=None, certChain=None,
-                        privateKey=None, reqCert=False, sessionCache=None, settings=None,
-                        checker=None):
-        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):
-        try:
-            self.handshakeStart(client=False)
-
-