summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/Client/Proxy.py81
-rw-r--r--src/lib/Server/Component.py69
-rwxr-xr-xsrc/lib/tlslite/BaseDB.py120
-rwxr-xr-xsrc/lib/tlslite/Checker.py146
-rwxr-xr-xsrc/lib/tlslite/FileObject.py220
-rwxr-xr-xsrc/lib/tlslite/HandshakeSettings.py159
-rwxr-xr-xsrc/lib/tlslite/Session.py131
-rwxr-xr-xsrc/lib/tlslite/SessionCache.py103
-rwxr-xr-xsrc/lib/tlslite/SharedKeyDB.py58
-rwxr-xr-xsrc/lib/tlslite/TLSConnection.py1600
-rwxr-xr-xsrc/lib/tlslite/TLSRecordLayer.py1131
-rwxr-xr-xsrc/lib/tlslite/VerifierDB.py90
-rwxr-xr-xsrc/lib/tlslite/X509.py133
-rwxr-xr-xsrc/lib/tlslite/X509CertChain.py181
-rwxr-xr-xsrc/lib/tlslite/__init__.py39
-rwxr-xr-xsrc/lib/tlslite/api.py75
-rwxr-xr-xsrc/lib/tlslite/constants.py225
-rwxr-xr-xsrc/lib/tlslite/errors.py149
-rwxr-xr-xsrc/lib/tlslite/integration/AsyncStateMachine.py235
-rwxr-xr-xsrc/lib/tlslite/integration/ClientHelper.py163
-rwxr-xr-xsrc/lib/tlslite/integration/HTTPTLSConnection.py169
-rwxr-xr-xsrc/lib/tlslite/integration/IMAP4_TLS.py132
-rwxr-xr-xsrc/lib/tlslite/integration/IntegrationHelper.py52
-rwxr-xr-xsrc/lib/tlslite/integration/POP3_TLS.py142
-rwxr-xr-xsrc/lib/tlslite/integration/SMTP_TLS.py114
-rwxr-xr-xsrc/lib/tlslite/integration/TLSAsyncDispatcherMixIn.py139
-rwxr-xr-xsrc/lib/tlslite/integration/TLSSocketServerMixIn.py59
-rwxr-xr-xsrc/lib/tlslite/integration/TLSTwistedProtocolWrapper.py196
-rwxr-xr-xsrc/lib/tlslite/integration/XMLRPCTransport.py137
-rwxr-xr-xsrc/lib/tlslite/integration/__init__.py17
-rwxr-xr-xsrc/lib/tlslite/mathtls.py170
-rwxr-xr-xsrc/lib/tlslite/messages.py561
-rwxr-xr-xsrc/lib/tlslite/utils/AES.py31
-rwxr-xr-xsrc/lib/tlslite/utils/ASN1Parser.py34
-rwxr-xr-xsrc/lib/tlslite/utils/Cryptlib_AES.py34
-rwxr-xr-xsrc/lib/tlslite/utils/Cryptlib_RC4.py28
-rwxr-xr-xsrc/lib/tlslite/utils/Cryptlib_TripleDES.py35
-rwxr-xr-xsrc/lib/tlslite/utils/OpenSSL_AES.py49
-rwxr-xr-xsrc/lib/tlslite/utils/OpenSSL_RC4.py25
-rwxr-xr-xsrc/lib/tlslite/utils/OpenSSL_RSAKey.py148
-rwxr-xr-xsrc/lib/tlslite/utils/OpenSSL_TripleDES.py44
-rwxr-xr-xsrc/lib/tlslite/utils/PyCrypto_AES.py22
-rwxr-xr-xsrc/lib/tlslite/utils/PyCrypto_RC4.py22
-rwxr-xr-xsrc/lib/tlslite/utils/PyCrypto_RSAKey.py61
-rwxr-xr-xsrc/lib/tlslite/utils/PyCrypto_TripleDES.py22
-rwxr-xr-xsrc/lib/tlslite/utils/Python_AES.py68
-rwxr-xr-xsrc/lib/tlslite/utils/Python_RC4.py39
-rwxr-xr-xsrc/lib/tlslite/utils/Python_RSAKey.py209
-rwxr-xr-xsrc/lib/tlslite/utils/RC4.py17
-rwxr-xr-xsrc/lib/tlslite/utils/RSAKey.py264
-rwxr-xr-xsrc/lib/tlslite/utils/TripleDES.py26
-rwxr-xr-xsrc/lib/tlslite/utils/__init__.py31
-rwxr-xr-xsrc/lib/tlslite/utils/cipherfactory.py111
-rwxr-xr-xsrc/lib/tlslite/utils/codec.py94
-rwxr-xr-xsrc/lib/tlslite/utils/compat.py140
-rwxr-xr-xsrc/lib/tlslite/utils/cryptomath.py400
-rwxr-xr-xsrc/lib/tlslite/utils/dateFuncs.py75
-rwxr-xr-xsrc/lib/tlslite/utils/entropy.c173
-rwxr-xr-xsrc/lib/tlslite/utils/hmac.py104
-rwxr-xr-xsrc/lib/tlslite/utils/jython_compat.py195
-rwxr-xr-xsrc/lib/tlslite/utils/keyfactory.py243
-rwxr-xr-xsrc/lib/tlslite/utils/rijndael.py392
-rwxr-xr-xsrc/lib/tlslite/utils/win32prng.c63
-rwxr-xr-xsrc/lib/tlslite/utils/xmltools.py201
-rwxr-xr-xsrc/sbin/bcfg22
-rwxr-xr-xsrc/sbin/bcfg2-admin15
66 files changed, 10312 insertions, 101 deletions
diff --git a/src/lib/Client/Proxy.py b/src/lib/Client/Proxy.py
index ee1287260..6140d1f90 100644
--- a/src/lib/Client/Proxy.py
+++ b/src/lib/Client/Proxy.py
@@ -1,63 +1,15 @@
'''Cobalt proxy provides client access to cobalt components'''
__revision__ = '$Revision$'
-import logging, socket, sys, time, xmlrpclib, ConfigParser, httplib
+import logging, socket, time, xmlrpclib, ConfigParser
+from Bcfg2.tlslite.integration.XMLRPCTransport import XMLRPCTransport
+import Bcfg2.tlslite.errors
+
+#FIXME need to reimplement _binadaddress support for XMLRPCTransport
class CobaltComponentError(Exception):
pass
-class Bcfg2HTTPSConnection(httplib.HTTPSConnection):
-
- def connect(self):
- "Connect to a host on a given (SSL) port binding the socket to a specific local address"
- print "Proxy connect"
- _cfile = ConfigParser.ConfigParser()
- if '-C' in sys.argv:
- _cfpath = sys.argv[sys.argv.index('-C') + 1]
- else:
- _cfpath = '/etc/bcfg2.conf'
- _cfile.read([_cfpath])
- _bindaddress = ""
- try:
- _bindaddress = _cfile.get('communication', 'bindaddress')
- except:
- self.log.error("%s doesn't contain a valid bindadress value" % (_cfpath))
- raise SystemExit, 1
-
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
- # the following line is the sole modification in comparison to the
- # connect() in httplib.HTTPSConnection
- sock.bind((_bindaddress, 0))
-
- sock.connect((self.host, self.port))
- ssl = socket.ssl(sock, self.key_file, self.cert_file)
- self.sock = httplib.FakeSocket(sock, ssl)
-
-class Bcfg2HTTPS(httplib.HTTPS):
- """Own HTTPS class for overriding the _connection_class value"""
-
- _connection_class = Bcfg2HTTPSConnection # instead of HTTPSConnection from httplib
-
-
-class Bcfg2SafeTransport(xmlrpclib.Transport):
- """Own SafeTransport class for overriding the HTTPS object"""
-
- def make_connection(self, host):
-
- # create a HTTPS connection object from a host descriptor
- # host may be a string, or a (host, x509-dict) tuple
- host, extra_headers, x509 = self.get_host_info(host)
- try:
- HTTPS = Bcfg2HTTPS # instead of HTTPS from httplib
- except AttributeError:
- raise NotImplementedError(
- "your version of httplib doesn't support HTTPS"
- )
- else:
- return HTTPS(host, None, **(x509 or {}))
-
-
class SafeProxy:
'''Wrapper for proxy'''
@@ -103,6 +55,11 @@ class SafeProxy:
self._authinfo = (user, password)
+ if args.has_key('fingerprint'):
+ self.fingerprint = args['fingerprint']
+ else:
+ self.fingerprint = False
+
_bindaddress = ""
try:
_bindaddress = self._cfile.get('communication', 'bindaddress')
@@ -115,11 +72,16 @@ class SafeProxy:
address = self.__get_location(component)
try:
- if _bindaddress != "":
- self.log.info("Binding client to address %s" % _bindaddress)
- self.proxy = xmlrpclib.ServerProxy(address, transport=Bcfg2SafeTransport())
+ # if _bindaddress != "":
+ # self.log.info("Binding client to address %s" % _bindaddress)
+ # self.proxy = xmlrpclib.ServerProxy(address, transport=Bcfg2SafeTransport())
+ # else:
+ if self.fingerprint:
+ transport = XMLRPCTransport(x509Fingerprint=self.fingerprint)
else:
- self.proxy = xmlrpclib.ServerProxy(address, transport=xmlrpclib.SafeTransport())
+ transport = XMLRPCTransport()
+ self.proxy = xmlrpclib.ServerProxy(address, transport=transport)
+
except IOError, io_error:
self.log.error("Invalid server URL %s: %s" % (address, io_error))
raise CobaltComponentError
@@ -149,6 +111,11 @@ class SafeProxy:
except socket.error, serr:
self.log.debug("Attempting %s (%d of %d) failed because %s" % (methodName, (irs+1),
self._retries, serr))
+ except Bcfg2.tlslite.errors.TLSFingerprintError, err:
+ self.log.error("Server fingerprint did not match")
+ errmsg = err.message.split()
+ self.log.error("Got %s expected %s" % (errmsg[3], errmsg[4]))
+ raise SystemExit, 1
except:
self.log.error("Unknown failure", exc_info=1)
break
diff --git a/src/lib/Server/Component.py b/src/lib/Server/Component.py
index 00d17f86c..20423fa51 100644
--- a/src/lib/Server/Component.py
+++ b/src/lib/Server/Component.py
@@ -2,7 +2,9 @@
__revision__ = '$Revision$'
import atexit, logging, select, signal, socket, sys, time, urlparse, xmlrpclib, cPickle, ConfigParser
-import BaseHTTPServer, OpenSSL.SSL, SimpleXMLRPCServer, SocketServer
+import BaseHTTPServer, SimpleXMLRPCServer, SocketServer
+import Bcfg2.tlslite.errors
+import Bcfg2.tlslite.api
import Bcfg2.Client.Proxy as Proxy
@@ -24,8 +26,6 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
# get arguments
data = self.rfile.read(int(self.headers["content-length"]))
response = self.server._cobalt_marshalled_dispatch(data, self.client_address)
- except OpenSSL.SSL.SysCallError:
- log.error("Client %s unexpectedly closed connection" % (self.client_address[0]))
except: # This should only happen if the module is buggy
# internal error, report as HTTP server error
log.error("Unexcepted handler failure in do_POST", exc_info=1)
@@ -41,7 +41,7 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
# shut down the connection
self.wfile.flush()
- self.connection.shutdown()
+ #self.connection.shutdown()
def setup(self):
'''Setup a working connection'''
@@ -49,47 +49,30 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-class SSLServer(BaseHTTPServer.HTTPServer):
- '''This class encapsulates all of the ssl server stuff'''
+class TLSServer(Bcfg2.tlslite.api.TLSSocketServerMixIn,
+ BaseHTTPServer.HTTPServer):
+ '''This class is an tlslite-using SSLServer'''
def __init__(self, address, keyfile, handler):
- SocketServer.BaseServer.__init__(self, address, handler)
- ctxt = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
- ctxt.use_privatekey_file (keyfile)
- ctxt.use_certificate_file(keyfile)
- #ctxt.load_verify_locations('/tmp/keys/CA.cert')
- ctxt.set_verify(OpenSSL.SSL.VERIFY_PEER, self.verify_cb)
- self.socket = OpenSSL.SSL.Connection(ctxt,
- socket.socket(self.address_family, self.socket_type))
- self.server_bind()
- self.server_activate()
-
- def verify_cb(self, conn, cert, errnum, depth, ok):
- '''handle cerificate verification'''
- print "here"
- print 'Got cert: %s' % (cert.get_subject())
- print cert.get_pubkey()
- return ok
-
- def handle_request(self):
- """Handle one request, possibly blocking."""
+ self.sc = Bcfg2.tlslite.api.SessionCache()
+ x509 = Bcfg2.tlslite.api.X509()
+ s = open(keyfile).read()
+ x509.parse(s)
+ self.key = Bcfg2.tlslite.api.parsePEMKey(s, private=True)
+ self.chain = Bcfg2.tlslite.api.X509CertChain([x509])
+ BaseHTTPServer.HTTPServer.__init__(self, address, handler)
+
+ def handshake(self, tlsConnection):
try:
- request, client_address = self.get_request()
- except socket.error:
- return
- if self.verify_request(request, client_address):
- try:
- self.process_request(request, client_address)
- except Exception, err:
- if isinstance(err, OpenSSL.SSL.Error):
- if isinstance(err, OpenSSL.SSL.SysCallError):
- log.error("Client %s unexpectedly closed connection" % (client_address[0]))
- else:
- log.error("%s from %s" % (err[0][0][2], client_address[0]))
- else:
- log.error("Unknown socket I/O failure from %s" % (client_address[0]), exc_info=1)
- self.close_request(request)
+ tlsConnection.handshakeServer(certChain=self.chain,
+ privateKey=self.key,
+ sessionCache=self.sc)
+ tlsConnection.ignoreAbruptClose = True
+ return True
+ except Bcfg2.tlslite.errors.TLSError, error:
+ print "Handshake failure:", str(error)
+ return False
-class Component(SSLServer,
+class Component(TLSServer,
SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
"""Cobalt component providing XML-RPC access"""
__name__ = 'Component'
@@ -133,7 +116,7 @@ class Component(SSLServer,
self.password = self.cfile.get('communication', 'password')
try:
- SSLServer.__init__(self, location, keyfile, CobaltXMLRPCRequestHandler)
+ TLSServer.__init__(self, location, keyfile, CobaltXMLRPCRequestHandler)
except socket.error:
self.logger.error("Failed to bind to socket")
raise ComponentInitError
diff --git a/src/lib/tlslite/BaseDB.py b/src/lib/tlslite/BaseDB.py
new file mode 100755
index 000000000..ca8dff6b4
--- /dev/null
+++ b/src/lib/tlslite/BaseDB.py
@@ -0,0 +1,120 @@
+"""Base class for SharedKeyDB and VerifierDB."""
+
+import anydbm
+import thread
+
+class BaseDB:
+ def __init__(self, filename, type):
+ self.type = type
+ self.filename = filename
+ if self.filename:
+ self.db = None
+ else:
+ self.db = {}
+ self.lock = thread.allocate_lock()
+
+ def create(self):
+ """Create a new on-disk database.
+
+ @raise anydbm.error: If there's a problem creating the database.
+ """
+ if self.filename:
+ self.db = anydbm.open(self.filename, "n") #raises anydbm.error
+ self.db["--Reserved--type"] = self.type
+ self.db.sync()
+ else:
+ self.db = {}
+
+ def open(self):
+ """Open a pre-existing on-disk database.
+
+ @raise anydbm.error: If there's a problem opening the database.
+ @raise ValueError: If the database is not of the right type.
+ """
+ if not self.filename:
+ raise ValueError("Can only open on-disk databases")
+ self.db = anydbm.open(self.filename, "w") #raises anydbm.error
+ try:
+ if self.db["--Reserved--type"] != self.type:
+ raise ValueError("Not a %s database" % self.type)
+ except KeyError:
+ raise ValueError("Not a recognized database")
+
+ def __getitem__(self, username):
+ if self.db == None:
+ raise AssertionError("DB not open")
+
+ self.lock.acquire()
+ try:
+ valueStr = self.db[username]
+ finally:
+ self.lock.release()
+
+ return self._getItem(username, valueStr)
+
+ def __setitem__(self, username, value):
+ if self.db == None:
+ raise AssertionError("DB not open")
+
+ valueStr = self._setItem(username, value)
+
+ self.lock.acquire()
+ try:
+ self.db[username] = valueStr
+ if self.filename:
+ self.db.sync()
+ finally:
+ self.lock.release()
+
+ def __delitem__(self, username):
+ if self.db == None:
+ raise AssertionError("DB not open")
+
+ self.lock.acquire()
+ try:
+ del(self.db[username])
+ if self.filename:
+ self.db.sync()
+ finally:
+ self.lock.release()
+
+ def __contains__(self, username):
+ """Check if the database contains the specified username.
+
+ @type username: str
+ @param username: The username to check for.
+
+ @rtype: bool
+ @return: True if the database contains the username, False
+ otherwise.
+
+ """
+ if self.db == None:
+ raise AssertionError("DB not open")
+
+ self.lock.acquire()
+ try:
+ return self.db.has_key(username)
+ finally:
+ self.lock.release()
+
+ def check(self, username, param):
+ value = self.__getitem__(username)
+ return self._checkItem(value, username, param)
+
+ def keys(self):
+ """Return a list of usernames in the database.
+
+ @rtype: list
+ @return: The usernames in the database.
+ """
+ if self.db == None:
+ raise AssertionError("DB not open")
+
+ 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 \ No newline at end of file
diff --git a/src/lib/tlslite/Checker.py b/src/lib/tlslite/Checker.py
new file mode 100755
index 000000000..f97869762
--- /dev/null
+++ b/src/lib/tlslite/Checker.py
@@ -0,0 +1,146 @@
+"""Class for post-handshake certificate checking."""
+
+from utils.cryptomath import hashAndBase64
+from X509 import X509
+from X509CertChain import X509CertChain
+from errors import *
+
+
+class Checker:
+ """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,
+ x509TrustList=None, x509CommonName=None,
+ checkResumedSession=False):
+ """Create a new Checker instance.
+
+ You must pass in one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ @type cryptoID: str
+ @param cryptoID: A cryptoID which the other party's certificate
+ chain must match. The cryptoIDlib module must be installed.
+ Mutually exclusive with all of the 'x509...' arguments.
+
+ @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' and
+ 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed. Mutually exclusive with the 'cryptoID' and
+ 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' 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 or x509TrustList):
+ raise ValueError()
+ if x509Fingerprint and x509TrustList:
+ raise ValueError()
+ if x509CommonName and not x509TrustList:
+ raise ValueError()
+ if protocol and not cryptoID:
+ raise ValueError()
+ if cryptoID:
+ import cryptoIDlib #So we raise an error here
+ if x509TrustList:
+ import cryptlib_py #So we raise an error here
+ self.cryptoID = cryptoID
+ self.protocol = protocol
+ self.x509Fingerprint = x509Fingerprint
+ self.x509TrustList = x509TrustList
+ self.x509CommonName = x509CommonName
+ 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 or self.x509TrustList:
+ if connection._client:
+ chain = connection.session.serverCertChain
+ else:
+ chain = connection.session.clientCertChain
+
+ if self.x509Fingerprint or self.x509TrustList:
+ if isinstance(chain, X509CertChain):
+ if self.x509Fingerprint:
+ if chain.getFingerprint() != self.x509Fingerprint:
+ raise TLSFingerprintError(\
+ "X.509 fingerprint mismatch: %s, %s" % \
+ (chain.getFingerprint(), self.x509Fingerprint))
+ else: #self.x509TrustList
+ if not chain.validate(self.x509TrustList):
+ raise TLSValidationError("X.509 validation failure")
+ if self.x509CommonName and \
+ (chain.getCommonName() != self.x509CommonName):
+ raise TLSAuthorizationError(\
+ "X.509 Common Name mismatch: %s, %s" % \
+ (chain.getCommonName(), self.x509CommonName))
+ elif chain:
+ raise TLSAuthenticationTypeError()
+ else:
+ raise TLSNoAuthenticationError()
+ elif self.cryptoID:
+ import cryptoIDlib.CertChain
+ if isinstance(chain, cryptoIDlib.CertChain.CertChain):
+ if chain.cryptoID != self.cryptoID:
+ raise TLSFingerprintError(\
+ "cryptoID mismatch: %s, %s" % \
+ (chain.cryptoID, self.cryptoID))
+ if self.protocol:
+ if not chain.checkProtocol(self.protocol):
+ raise TLSAuthorizationError(\
+ "cryptoID protocol mismatch")
+ if not chain.validate():
+ raise TLSValidationError("cryptoID validation failure")
+ elif chain:
+ raise TLSAuthenticationTypeError()
+ else:
+ raise TLSNoAuthenticationError()
+
diff --git a/src/lib/tlslite/FileObject.py b/src/lib/tlslite/FileObject.py
new file mode 100755
index 000000000..6ee02b243
--- /dev/null
+++ b/src/lib/tlslite/FileObject.py
@@ -0,0 +1,220 @@
+"""Class returned by TLSConnection.makefile()."""
+
+class FileObject:
+ """This class provides a file object interface to a
+ L{tlslite.TLSConnection.TLSConnection}.
+
+ 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
+ self.mode = mode # Not actually used in this version
+ if bufsize < 0:
+ bufsize = self.default_bufsize
+ self.bufsize = bufsize
+ self.softspace = False
+ if bufsize == 0:
+ self._rbufsize = 1
+ elif bufsize == 1:
+ self._rbufsize = self.default_bufsize
+ else:
+ self._rbufsize = bufsize
+ self._wbufsize = bufsize
+ self._rbuf = "" # A string
+ self._wbuf = [] # A list of strings
+
+ def _getclosed(self):
+ return self._sock is not None
+ closed = property(_getclosed, doc="True if the file is closed")
+
+ def close(self):
+ try:
+ if self._sock:
+ for result in self._sock._decrefAsync(): #TREV
+ pass
+ finally:
+ self._sock = None
+
+ def __del__(self):
+ try:
+ self.close()
+ except:
+ # close() may fail if __init__ didn't complete
+ pass
+
+ def flush(self):
+ if self._wbuf:
+ buffer = "".join(self._wbuf)
+ self._wbuf = []
+ self._sock.sendall(buffer)
+
+ #def fileno(self):
+ # raise NotImplementedError() #TREV
+
+ def write(self, data):
+ data = str(data) # XXX Should really reject non-string non-buffers
+ if not data:
+ return
+ self._wbuf.append(data)
+ if (self._wbufsize == 0 or
+ self._wbufsize == 1 and '\n' in data or
+ self._get_wbuf_len() >= self._wbufsize):
+ self.flush()
+
+ def writelines(self, list):
+ # XXX We could do better here for very long lists
+ # XXX Should really reject non-string non-buffers
+ self._wbuf.extend(filter(None, map(str, list)))
+ if (self._wbufsize <= 1 or
+ self._get_wbuf_len() >= self._wbufsize):
+ self.flush()
+
+ def _get_wbuf_len(self):
+ buf_len = 0
+ for x in self._wbuf:
+ buf_len += len(x)
+ return buf_len
+
+ def read(self, size=-1):
+ data = self._rbuf
+ if size < 0:
+ # Read until EOF
+ buffers = []
+ if data:
+ buffers.append(data)
+ self._rbuf = ""
+ if self._rbufsize <= 1:
+ recv_size = self.default_bufsize
+ else:
+ recv_size = self._rbufsize
+ while True:
+ data = self._sock.recv(recv_size)
+ if not data:
+ break
+ buffers.append(data)
+ return "".join(buffers)
+ else:
+ # Read until size bytes or EOF seen, whichever comes first
+ buf_len = len(data)
+ if buf_len >= size:
+ self._rbuf = data[size:]
+ return data[:size]
+ buffers = []
+ if data:
+ buffers.append(data)
+ self._rbuf = ""
+ while True:
+ left = size - buf_len
+ recv_size = max(self._rbufsize, left)
+ data = self._sock.recv(recv_size)
+ if not data:
+ break
+ buffers.append(data)
+ n = len(data)
+ if n >= left:
+ self._rbuf = data[left:]
+ buffers[-1] = data[:left]
+ break
+ buf_len += n
+ return "".join(buffers)
+
+ def readline(self, size=-1):
+ data = self._rbuf
+ if size < 0:
+ # Read until \n or EOF, whichever comes first
+ if self._rbufsize <= 1:
+ # Speed up unbuffered case
+ assert data == ""
+ buffers = []
+ recv = self._sock.recv
+ while data != "\n":
+ data = recv(1)
+ if not data:
+ break
+ buffers.append(data)
+ return "".join(buffers)
+ nl = data.find('\n')
+ if nl >= 0:
+ nl += 1
+ self._rbuf = data[nl:]
+ return data[:nl]
+ buffers = []
+ if data:
+ buffers.append(data)
+ self._rbuf = ""
+ while True:
+ data = self._sock.recv(self._rbufsize)
+ if not data:
+ break
+ buffers.append(data)
+ nl = data.find('\n')
+ if nl >= 0:
+ nl += 1
+ self._rbuf = data[nl:]
+ buffers[-1] = data[:nl]
+ break
+ return "".join(buffers)
+ else:
+ # Read until size bytes or \n or EOF seen, whichever comes first
+ nl = data.find('\n', 0, size)
+ if nl >= 0:
+ nl += 1
+ self._rbuf = data[nl:]
+ return data[:nl]
+ buf_len = len(data)
+ if buf_len >= size:
+ self._rbuf = data[size:]
+ return data[:size]
+ buffers = []
+ if data:
+ buffers.append(data)
+ self._rbuf = ""
+ while True:
+ data = self._sock.recv(self._rbufsize)
+ if not data:
+ break
+ buffers.append(data)
+ left = size - buf_len
+ nl = data.find('\n', 0, left)
+ if nl >= 0:
+ nl += 1
+ self._rbuf = data[nl:]
+ buffers[-1] = data[:nl]
+ break
+ n = len(data)
+ if n >= left:
+ self._rbuf = data[left:]
+ buffers[-1] = data[:left]
+ break
+ buf_len += n
+ return "".join(buffers)
+
+ def readlines(self, sizehint=0):
+ total = 0
+ list = []
+ while True:
+ line = self.readline()
+ if not line:
+ break
+ list.append(line)
+ total += len(line)
+ if sizehint and total >= sizehint:
+ break
+ return list
+
+ # Iterator protocols
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ line = self.readline()
+ if not line:
+ raise StopIteration
+ return line
diff --git a/src/lib/tlslite/HandshakeSettings.py b/src/lib/tlslite/HandshakeSettings.py
new file mode 100755
index 000000000..c7c3223e5
--- /dev/null
+++ b/src/lib/tlslite/HandshakeSettings.py
@@ -0,0 +1,159 @@
+"""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, (3,1) for
+ TLS 1.0, or (3,2) for TLS 1.1. 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, (3,1) for
+ TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to
+ use a higher version, a protocol_version alert will be signalled.
+ The default is (3,2). (WARNING: Some servers may (improperly)
+ reject clients which offer support for TLS 1.1. In this case,
+ try lowering maxVersion to (3,1)).
+ """
+ def __init__(self):
+ self.minKeySize = 1023
+ self.maxKeySize = 8193
+ self.cipherNames = ["aes256", "aes128", "3des", "rc4"]
+ self.cipherImplementations = ["cryptlib", "openssl", "pycrypto",
+ "python"]
+ self.certificateTypes = ["x509", "cryptoID"]
+ self.minVersion = (3,0)
+ self.maxVersion = (3,2)
+
+ #Filters out options that are not supported
+ def _filter(self):
+ other = HandshakeSettings()
+ other.minKeySize = self.minKeySize
+ other.maxKeySize = self.maxKeySize
+ other.cipherNames = self.cipherNames
+ other.cipherImplementations = self.cipherImplementations
+ other.certificateTypes = self.certificateTypes
+ other.minVersion = self.minVersion
+ other.maxVersion = self.maxVersion
+
+ if not cipherfactory.tripleDESPresent:
+ other.cipherNames = [e for e in self.cipherNames if e != "3des"]
+ if len(other.cipherNames)==0:
+ raise ValueError("No supported ciphers")
+
+ try:
+ import cryptoIDlib
+ except ImportError:
+ 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"]
+ if not cryptomath.m2cryptoLoaded:
+ 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.minKeySize<512:
+ raise ValueError("minKeySize too small")
+ if other.minKeySize>16384:
+ raise ValueError("minKeySize too large")
+ if other.maxKeySize<512:
+ raise ValueError("maxKeySize too small")
+ if other.maxKeySize>16384:
+ raise ValueError("maxKeySize too large")
+ for s in other.cipherNames:
+ if s not in ("aes256", "aes128", "rc4", "3des"):
+ raise ValueError("Unknown cipher name: '%s'" % s)
+ for s in other.cipherImplementations:
+ if s not in ("cryptlib", "openssl", "python", "pycrypto"):
+ raise ValueError("Unknown cipher implementation: '%s'" % s)
+ for s in other.certificateTypes:
+ if s not in ("x509", "cryptoID"):
+ raise ValueError("Unknown certificate type: '%s'" % s)
+
+ if other.minVersion > other.maxVersion:
+ raise ValueError("Versions set incorrectly")
+
+ if not other.minVersion in ((3,0), (3,1), (3,2)):
+ raise ValueError("minVersion set incorrectly")
+
+ if not other.maxVersion in ((3,0), (3,1), (3,2)):
+ raise ValueError("maxVersion set incorrectly")
+
+ return other
+
+ def _getCertificateTypes(self):
+ l = []
+ for ct in self.certificateTypes:
+ if ct == "x509":
+ l.append(CertificateType.x509)
+ elif ct == "cryptoID":
+ l.append(CertificateType.cryptoID)
+ else:
+ raise AssertionError()
+ return l
diff --git a/src/lib/tlslite/Session.py b/src/lib/tlslite/Session.py
new file mode 100755
index 000000000..a951f4589
--- /dev/null
+++ b/src/lib/tlslite/Session.py
@@ -0,0 +1,131 @@
+"""Class representing a TLS session."""
+
+from utils.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.CertChain}
+ @ivar clientCertChain: The client's certificate chain (or None).
+
+ @type serverCertChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @ivar serverCertChain: The server's certificate chain (or None).
+ """
+
+ def __init__(self):
+ self.masterSecret = createByteArraySequence([])
+ self.sessionID = createByteArraySequence([])
+ self.cipherSuite = 0
+ self.srpUsername = None
+ self.sharedKeyUsername = None
+ self.clientCertChain = None
+ self.serverCertChain = None
+ self.resumable = False
+ self.sharedKey = False
+
+ def _clone(self):
+ other = Session()
+ other.masterSecret = self.masterSecret
+ other.sessionID = self.sessionID
+ other.cipherSuite = self.cipherSuite
+ other.srpUsername = self.srpUsername
+ other.sharedKeyUsername = self.sharedKeyUsername
+ other.clientCertChain = self.clientCertChain
+ other.serverCertChain = self.serverCertChain
+ other.resumable = self.resumable
+ other.sharedKey = self.sharedKey
+ return other
+
+ def _calcMasterSecret(self, version, premasterSecret, clientRandom,
+ serverRandom):
+ if version == (3,0):
+ self.masterSecret = PRF_SSL(premasterSecret,
+ concatArrays(clientRandom, serverRandom), 48)
+ elif version in ((3,1), (3,2)):
+ self.masterSecret = PRF(premasterSecret, "master secret",
+ concatArrays(clientRandom, serverRandom), 48)
+ else:
+ 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):
+ #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
+ if (not boolean) or (boolean and self.sessionID):
+ self.resumable = boolean
+
+ def getCipherName(self):
+ """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 CipherSuite.aes256Suites:
+ return "aes256"
+ elif self.cipherSuite in CipherSuite.rc4Suites:
+ return "rc4"
+ elif self.cipherSuite in CipherSuite.tripleDESSuites:
+ return "3des"
+ else:
+ return None
+
+ 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.sharedKey = True
+ return self
+
+
diff --git a/src/lib/tlslite/SessionCache.py b/src/lib/tlslite/SessionCache.py
new file mode 100755
index 000000000..34cf0b0ec
--- /dev/null
+++ b/src/lib/tlslite/SessionCache.py
@@ -0,0 +1,103 @@
+"""Class for caching TLS sessions."""
+
+import thread
+import time
+
+class SessionCache:
+ """This class is used by the server to cache TLS sessions.
+
+ 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()
+
+ # 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
+ 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:
+ #Add the new element
+ self.entriesDict[sessionID] = session
+ 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 self.lastIndex == self.firstIndex:
+ del(self.entriesDict[self.entriesList[self.firstIndex][0]])
+ self.firstIndex = (self.firstIndex+1) % len(self.entriesList)
+ finally:
+ self.lock.release()
+
+ #Delete expired items
+ 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
+ index = self.firstIndex
+ while index != self.lastIndex:
+ if currentTime - self.entriesList[index][1] > self.maxAge:
+ del(self.entriesDict[self.entriesList[index][0]])
+ index = (index+1) % len(self.entriesList)
+ else:
+ break
+ self.firstIndex = index
+
+def _test():
+ import doctest, SessionCache
+ return doctest.testmod(SessionCache)
+
+if __name__ == "__main__":
+ _test()
diff --git a/src/lib/tlslite/SharedKeyDB.py b/src/lib/tlslite/SharedKeyDB.py
new file mode 100755
index 000000000..3246ec7f1
--- /dev/null
+++ b/src/lib/tlslite/SharedKeyDB.py
@@ -0,0 +1,58 @@
+"""Class for storing shared keys."""
+
+from utils.cryptomath import *
+from utils.compat import *
+from mathtls import *
+from Session import Session
+from BaseDB import BaseDB
+
+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):
+ """Create a new SharedKeyDB.
+
+ @type filename: str
+ @param filename: Filename for an on-disk database, or None for
+ an in-memory database. If the filename already exists, follow
+ this with a call to open(). To create a new on-disk database,
+ follow this with a call to create().
+ """
+ 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 ValueError("username too long")
+ if len(value)>=48:
+ raise ValueError("shared key too long")
+ return value
+
+ def _checkItem(self, value, username, param):
+ newSession = self._getItem(username, param)
+ return value.masterSecret == newSession.masterSecret \ No newline at end of file
diff --git a/src/lib/tlslite/TLSConnection.py b/src/lib/tlslite/TLSConnection.py
new file mode 100755
index 000000000..d125f8f0a
--- /dev/null
+++ b/src/lib/tlslite/TLSConnection.py
@@ -0,0 +1,1600 @@
+"""
+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
diff --git a/src/lib/tlslite/TLSRecordLayer.py b/src/lib/tlslite/TLSRecordLayer.py
new file mode 100755
index 000000000..002a56862
--- /dev/null
+++ b/src/lib/tlslite/TLSRecordLayer.py
@@ -0,0 +1,1131 @@
+"""Helper class for TLSConnection."""
+from __future__ import generators
+
+from utils.compat import *
+from utils.cryptomath import *
+from utils.cipherfactory import createAES, createRC4, createTripleDES
+from utils.codec import *
+from errors import *
+from messages import *
+from mathtls import *
+from constants import *
+from utils.cryptomath import getRandomBytes
+from utils import hmac
+from FileObject import FileObject
+import sha
+import md5
+import socket
+import errno
+import traceback
+
+try:
+ GeneratorExit
+except NameError:
+ class GeneratorExit(Exception):
+ pass
+
+class _ConnectionState:
+ def __init__(self):
+ self.macContext = None
+ self.encContext = None
+ self.seqnum = 0
+
+ def getSeqNumStr(self):
+ w = Writer(8)
+ w.add(self.seqnum, 8)
+ seqnumStr = bytesToString(w.bytes)
+ self.seqnum += 1
+ return seqnumStr
+
+
+class TLSRecordLayer:
+ """
+ This class handles data transmission for a TLS connection.
+
+ Its only subclass is L{tlslite.TLSConnection.TLSConnection}. We've
+ separated the code in this class from TLSConnection to make things
+ more readable.
+
+
+ @type sock: socket.socket
+ @ivar sock: The underlying socket object.
+
+ @type session: L{tlslite.Session.Session}
+ @ivar session: The session corresponding to this connection.
+
+ Due to TLS session resumption, multiple connections can correspond
+ to the same underlying session.
+
+ @type version: tuple
+ @ivar version: The TLS version being used for this connection.
+
+ (3,0) means SSL 3.0, and (3,1) means TLS 1.0.
+
+ @type closed: bool
+ @ivar closed: If this connection is closed.
+
+ @type resumed: bool
+ @ivar resumed: If this connection is based on a resumed session.
+
+ @type allegedSharedKeyUsername: str or None
+ @ivar allegedSharedKeyUsername: This is set to the shared-key
+ username asserted by the client, whether the handshake succeeded or
+ not. If the handshake fails, this can be inspected to
+ determine if a guessing attack is in progress against a particular
+ user account.
+
+ @type allegedSrpUsername: str or None
+ @ivar allegedSrpUsername: This is set to the SRP username
+ asserted by the client, whether the handshake succeeded or not.
+ If the handshake fails, this can be inspected to determine
+ if a guessing attack is in progress against a particular user
+ account.
+
+ @type closeSocket: bool
+ @ivar closeSocket: If the socket should be closed when the
+ connection is closed (writable).
+
+ If you set this to True, TLS Lite will assume the responsibility of
+ closing the socket when the TLS Connection is shutdown (either
+ through an error or through the user calling close()). The default
+ is False.
+
+ @type ignoreAbruptClose: bool
+ @ivar ignoreAbruptClose: If an abrupt close of the socket should
+ raise an error (writable).
+
+ If you set this to True, TLS Lite will not raise a
+ L{tlslite.errors.TLSAbruptCloseError} exception if the underlying
+ socket is unexpectedly closed. Such an unexpected closure could be
+ caused by an attacker. However, it also occurs with some incorrect
+ TLS implementations.
+
+ You should set this to True only if you're not worried about an
+ attacker truncating the connection, and only if necessary to avoid
+ spurious errors. The default is False.
+
+ @sort: __init__, read, readAsync, write, writeAsync, close, closeAsync,
+ getCipherImplementation, getCipherName
+ """
+
+ def __init__(self, sock):
+ self.sock = sock
+
+ #My session object (Session instance; read-only)
+ self.session = None
+
+ #Am I a client or server?
+ self._client = None
+
+ #Buffers for processing messages
+ self._handshakeBuffer = []
+ self._readBuffer = ""
+
+ #Handshake digests
+ self._handshake_md5 = md5.md5()
+ self._handshake_sha = sha.sha()
+
+ #TLS Protocol Version
+ self.version = (0,0) #read-only
+ self._versionCheck = False #Once we choose a version, this is True
+
+ #Current and Pending connection states
+ self._writeState = _ConnectionState()
+ self._readState = _ConnectionState()
+ self._pendingWriteState = _ConnectionState()
+ self._pendingReadState = _ConnectionState()
+
+ #Is the connection open?
+ self.closed = True #read-only
+ self._refCount = 0 #Used to trigger closure
+
+ #Is this a resumed (or shared-key) session?
+ self.resumed = False #read-only
+
+ #What username did the client claim in his handshake?
+ self.allegedSharedKeyUsername = None
+ self.allegedSrpUsername = None
+
+ #On a call to close(), do we close the socket? (writeable)
+ self.closeSocket = False
+
+ #If the socket is abruptly closed, do we ignore it
+ #and pretend the connection was shut down properly? (writeable)
+ self.ignoreAbruptClose = False
+
+ #Fault we will induce, for testing purposes
+ self.fault = None
+
+ #*********************************************************
+ # Public Functions START
+ #*********************************************************
+
+ def read(self, max=None, min=1):
+ """Read some data from the TLS connection.
+
+ This function will block until at least 'min' bytes are
+ available (or the connection is closed).
+
+ If an exception is raised, the connection will have been
+ automatically closed.
+
+ @type max: int
+ @param max: The maximum number of bytes to return.
+
+ @type min: int
+ @param min: The minimum number of bytes to return
+
+ @rtype: str
+ @return: A string of no more than 'max' bytes, and no fewer
+ than 'min' (unless the connection has been closed, in which
+ case fewer than 'min' bytes may 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.
+ """
+ for result in self.readAsync(max, min):
+ pass
+ return result
+
+ def readAsync(self, max=None, min=1):
+ """Start a read operation on the TLS connection.
+
+ This function returns a generator which behaves similarly to
+ read(). 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 a string if the read operation has
+ completed.
+
+ @rtype: iterable
+ @return: A generator; see above for details.
+ """
+ try:
+ while len(self._readBuffer)<min and not self.closed:
+ try:
+ for result in self._getMsg(ContentType.application_data):
+ if result in (0,1):
+ yield result
+ applicationData = result
+ self._readBuffer += bytesToString(applicationData.write())
+ except TLSRemoteAlert, alert:
+ if alert.description != AlertDescription.close_notify:
+ raise
+ except TLSAbruptCloseError:
+ if not self.ignoreAbruptClose:
+ raise
+ else:
+ self._shutdown(True)
+
+ if max == None:
+ max = len(self._readBuffer)
+
+ returnStr = self._readBuffer[:max]
+ self._readBuffer = self._readBuffer[max:]
+ yield returnStr
+ except GeneratorExit:
+ pass
+ except:
+ self._shutdown(False)
+ raise
+
+ def write(self, s):
+ """Write some data to the TLS connection.
+
+ This function will block until all the data has been sent.
+
+ If an exception is raised, the connection will have been
+ automatically closed.
+
+ @type s: str
+ @param s: The data to transmit to the other party.
+
+ @raise socket.error: If a socket error occurs.
+ """
+ for result in self.writeAsync(s):
+ pass
+
+ def writeAsync(self, s):
+ """Start a write operation on the TLS connection.
+
+ This function returns a generator which behaves similarly to
+ write(). Successive invocations of the generator will return
+ 1 if it is waiting to write to the socket, or will raise
+ StopIteration if the write operation has completed.
+
+ @rtype: iterable
+ @return: A generator; see above for details.
+ """
+ try:
+ if self.closed:
+ raise ValueError()
+
+ index = 0
+ blockSize = 16384
+ skipEmptyFrag = False
+ while 1:
+ startIndex = index * blockSize
+ endIndex = startIndex + blockSize
+ if startIndex >= len(s):
+ break
+ if endIndex > len(s):
+ endIndex = len(s)
+ block = stringToBytes(s[startIndex : endIndex])
+ applicationData = ApplicationData().create(block)
+ for result in self._sendMsg(applicationData, skipEmptyFrag):
+ yield result
+ skipEmptyFrag = True #only send an empy fragment on 1st message
+ index += 1
+ except:
+ self._shutdown(False)
+ raise
+
+ def close(self):
+ """Close the TLS connection.
+
+ This function will block until it has exchanged close_notify
+ alerts with the other party. After doing so, it will shut down the
+ TLS connection. Further attempts to read through this connection
+ will return "". Further attempts to write through this connection
+ will raise ValueError.
+
+ If makefile() has been called on this connection, the connection
+ will be not be closed until the connection object and all file
+ objects have been closed.
+
+ Even if an exception is raised, the connection will have been
+ closed.
+
+ @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.
+ """
+ if not self.closed:
+ for result in self._decrefAsync():
+ pass
+
+ def closeAsync(self):
+ """Start a close operation on the TLS connection.
+
+ This function returns a generator which behaves similarly to
+ close(). 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
+ close operation has completed.
+
+ @rtype: iterable
+ @return: A generator; see above for details.
+ """
+ if not self.closed:
+ for result in self._decrefAsync():
+ yield result
+
+ def _decrefAsync(self):
+ self._refCount -= 1
+ if self._refCount == 0 and not self.closed:
+ try:
+ for result in self._sendMsg(Alert().create(\
+ AlertDescription.close_notify, AlertLevel.warning)):
+ yield result
+ alert = None
+ while not alert:
+ for result in self._getMsg((ContentType.alert, \
+ ContentType.application_data)):
+ if result in (0,1):
+ yield result
+ if result.contentType == ContentType.alert:
+ alert = result
+ if alert.description == AlertDescription.close_notify:
+ self._shutdown(True)
+ else:
+ raise TLSRemoteAlert(alert)
+ except (socket.error, TLSAbruptCloseError):
+ #If the other side closes the socket, that's okay
+ self._shutdown(True)
+ except:
+ self._shutdown(False)
+ raise
+
+ def getCipherName(self):
+ """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 not self._writeState.encContext:
+ return None
+ return self._writeState.encContext.name
+
+ def getCipherImplementation(self):
+ """Get the name of the cipher implementation used with
+ this connection.
+
+ @rtype: str
+ @return: The name of the cipher implementation used with
+ this connection. Either 'python', 'cryptlib', 'openssl',
+ or 'pycrypto'.
+ """
+ if not self._writeState.encContext:
+ return None
+ return self._writeState.encContext.implementation
+
+
+
+ #Emulate a socket, somewhat -
+ def send(self, s):
+ """Send data to the TLS connection (socket emulation).
+
+ @raise socket.error: If a socket error occurs.
+ """
+ self.write(s)
+ return len(s)
+
+ def sendall(self, s):
+ """Send data to the TLS connection (socket emulation).
+
+ @raise socket.error: If a socket error occurs.
+ """
+ self.write(s)
+
+ def recv(self, bufsize):
+ """Get some data from the TLS connection (socket emulation).
+
+ @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.
+ """
+ return self.read(bufsize)
+
+ def makefile(self, mode='r', bufsize=-1):
+ """Create a file object for the TLS connection (socket emulation).
+
+ @rtype: L{tlslite.FileObject.FileObject}
+ """
+ self._refCount += 1
+ return FileObject(self, mode, bufsize)
+
+ def getsockname(self):
+ """Return the socket's own address (socket emulation)."""
+ return self.sock.getsockname()
+
+ def getpeername(self):
+ """Return the remote address to which the socket is connected
+ (socket emulation)."""
+ return self.sock.getpeername()
+
+ def settimeout(self, value):
+ """Set a timeout on blocking socket operations (socket emulation)."""
+ return self.sock.settimeout(value)
+
+ def gettimeout(self):
+ """Return the timeout associated with socket operations (socket
+ emulation)."""
+ return self.sock.gettimeout()
+
+ def setsockopt(self, level, optname, value):
+ """Set the value of the given socket option (socket emulation)."""
+ return self.sock.setsockopt(level, optname, value)
+
+
+ #*********************************************************
+ # Public Functions END
+ #*********************************************************
+
+ def _shutdown(self, resumable):
+ self._writeState = _ConnectionState()
+ self._readState = _ConnectionState()
+ #Don't do this: self._readBuffer = ""
+ self.version = (0,0)
+ self._versionCheck = False
+ self.closed = True
+ if self.closeSocket:
+ self.sock.close()
+
+ #Even if resumable is False, we'll never toggle this on
+ if not resumable and self.session:
+ self.session.resumable = False
+
+
+ def _sendError(self, alertDescription, errorStr=None):
+ alert = Alert().create(alertDescription, AlertLevel.fatal)
+ for result in self._sendMsg(alert):
+ yield result
+ self._shutdown(False)
+ raise TLSLocalAlert(alert, errorStr)
+
+ def _sendMsgs(self, msgs):
+ skipEmptyFrag = False
+ for msg in msgs:
+ for result in self._sendMsg(msg, skipEmptyFrag):
+ yield result
+ skipEmptyFrag = True
+
+ def _sendMsg(self, msg, skipEmptyFrag=False):
+ bytes = msg.write()
+ contentType = msg.contentType
+
+ #Whenever we're connected and asked to send a message,
+ #we first send an empty Application Data message. This prevents
+ #an attacker from launching a chosen-plaintext attack based on
+ #knowing the next IV.
+ if not self.closed and not skipEmptyFrag and self.version == (3,1):
+ if self._writeState.encContext:
+ if self._writeState.encContext.isBlockCipher:
+ for result in self._sendMsg(ApplicationData(),
+ skipEmptyFrag=True):
+ yield result
+
+ #Update handshake hashes
+ if contentType == ContentType.handshake:
+ bytesStr = bytesToString(bytes)
+ self._handshake_md5.update(bytesStr)
+ self._handshake_sha.update(bytesStr)
+
+ #Calculate MAC
+ if self._writeState.macContext:
+ seqnumStr = self._writeState.getSeqNumStr()
+ bytesStr = bytesToString(bytes)
+ mac = self._writeState.macContext.copy()
+ mac.update(seqnumStr)
+ mac.update(chr(contentType))
+ if self.version == (3,0):
+ mac.update( chr( int(len(bytes)/256) ) )
+ mac.update( chr( int(len(bytes)%256) ) )
+ elif self.version in ((3,1), (3,2)):
+ mac.update(chr(self.version[0]))
+ mac.update(chr(self.version[1]))
+ mac.update( chr( int(len(bytes)/256) ) )
+ mac.update( chr( int(len(bytes)%256) ) )
+ else:
+ raise AssertionError()
+ mac.update(bytesStr)
+ macString = mac.digest()
+ macBytes = stringToBytes(macString)
+ if self.fault == Fault.badMAC:
+ macBytes[0] = (macBytes[0]+1) % 256
+
+ #Encrypt for Block or Stream Cipher
+ if self._writeState.encContext:
+ #Add padding and encrypt (for Block Cipher):
+ if self._writeState.encContext.isBlockCipher:
+
+ #Add TLS 1.1 fixed block
+ if self.version == (3,2):
+ bytes = self.fixedIVBlock + bytes
+
+ #Add padding: bytes = bytes + (macBytes + paddingBytes)
+ currentLength = len(bytes) + len(macBytes) + 1
+ blockLength = self._writeState.encContext.block_size
+ paddingLength = blockLength-(currentLength % blockLength)
+
+ paddingBytes = createByteArraySequence([paddingLength] * \
+ (paddingLength+1))
+ if self.fault == Fault.badPadding:
+ paddingBytes[0] = (paddingBytes[0]+1) % 256
+ endBytes = concatArrays(macBytes, paddingBytes)
+ bytes = concatArrays(bytes, endBytes)
+ #Encrypt
+ plaintext = stringToBytes(bytes)
+ ciphertext = self._writeState.encContext.encrypt(plaintext)
+ bytes = stringToBytes(ciphertext)
+
+ #Encrypt (for Stream Cipher)
+ else:
+ bytes = concatArrays(bytes, macBytes)
+ plaintext = bytesToString(bytes)
+ ciphertext = self._writeState.encContext.encrypt(plaintext)
+ bytes = stringToBytes(ciphertext)
+
+ #Add record header and send
+ r = RecordHeader3().create(self.version, contentType, len(bytes))
+ s = bytesToString(concatArrays(r.write(), bytes))
+ while 1:
+ try:
+ bytesSent = self.sock.send(s) #Might raise socket.error
+ except socket.error, why:
+ if why[0] == errno.EWOULDBLOCK:
+ yield 1
+ continue
+ else:
+ raise
+ if bytesSent == len(s):
+ return
+ s = s[bytesSent:]
+ yield 1
+
+
+ def _getMsg(self, expectedType, secondaryType=None, constructorType=None):
+ try:
+ if not isinstance(expectedType, tuple):
+ expectedType = (expectedType,)
+
+ #Spin in a loop, until we've got a non-empty record of a type we
+ #expect. The loop will be repeated if:
+ # - we receive a renegotiation attempt; we send no_renegotiation,
+ # then try again
+ # - we receive an empty application-data fragment; we try again
+ while 1:
+ for result in self._getNextRecord():
+ if result in (0,1):
+ yield result
+ recordHeader, p = result
+
+ #If this is an empty application-data fragment, try again
+ if recordHeader.type == ContentType.application_data:
+ if p.index == len(p.bytes):
+ continue
+
+ #If we received an unexpected record type...
+ if recordHeader.type not in expectedType:
+
+ #If we received an alert...
+ if recordHeader.type == ContentType.alert:
+ alert = Alert().parse(p)
+
+ #We either received a fatal error, a warning, or a
+ #close_notify. In any case, we're going to close the
+ #connection. In the latter two cases we respond with
+ #a close_notify, but ignore any socket errors, since
+ #the other side might have already closed the socket.
+ if alert.level == AlertLevel.warning or \
+ alert.description == AlertDescription.close_notify:
+
+ #If the sendMsg() call fails because the socket has
+ #already been closed, we will be forgiving and not
+ #report the error nor invalidate the "resumability"
+ #of the session.
+ try:
+ alertMsg = Alert()
+ alertMsg.create(AlertDescription.close_notify,
+ AlertLevel.warning)
+ for result in self._sendMsg(alertMsg):
+ yield result
+ except socket.error:
+ pass
+
+ if alert.description == \
+ AlertDescription.close_notify:
+ self._shutdown(True)
+ elif alert.level == AlertLevel.warning:
+ self._shutdown(False)
+
+ else: #Fatal alert:
+ self._shutdown(False)
+
+ #Raise the alert as an exception
+ raise TLSRemoteAlert(alert)
+
+ #If we received a renegotiation attempt...
+ if recordHeader.type == ContentType.handshake:
+ subType = p.get(1)
+ reneg = False
+ if self._client:
+ if subType == HandshakeType.hello_request:
+ reneg = True
+ else:
+ if subType == HandshakeType.client_hello:
+ reneg = True
+ #Send no_renegotiation, then try again
+ if reneg:
+ alertMsg = Alert()
+ alertMsg.create(AlertDescription.no_renegotiation,
+ AlertLevel.warning)
+ for result in self._sendMsg(alertMsg):
+ yield result
+ continue
+
+ #Otherwise: this is an unexpected record, but neither an
+ #alert nor renegotiation
+ for result in self._sendError(\
+ AlertDescription.unexpected_message,
+ "received type=%d" % recordHeader.type):
+ yield result
+
+ break
+
+ #Parse based on content_type
+ if recordHeader.type == ContentType.change_cipher_spec:
+ yield ChangeCipherSpec().parse(p)
+ elif recordHeader.type == ContentType.alert:
+ yield Alert().parse(p)
+ elif recordHeader.type == ContentType.application_data:
+ yield ApplicationData().parse(p)
+ elif recordHeader.type == ContentType.handshake:
+ #Convert secondaryType to tuple, if it isn't already
+ if not isinstance(secondaryType, tuple):
+ secondaryType = (secondaryType,)
+
+ #If it's a handshake message, check handshake header
+ if recordHeader.ssl2:
+ subType = p.get(1)
+ if subType != HandshakeType.client_hello:
+ for result in self._sendError(\
+ AlertDescription.unexpected_message,
+ "Can only handle SSLv2 ClientHello messages"):
+ yield result
+ if HandshakeType.client_hello not in secondaryType:
+ for result in self._sendError(\
+ AlertDescription.unexpected_message):
+ yield result
+ subType = HandshakeType.client_hello
+ else:
+ subType = p.get(1)
+ if subType not in secondaryType:
+ for result in self._sendError(\
+ AlertDescription.unexpected_message,
+ "Expecting %s, got %s" % (str(secondaryType), subType)):
+ yield result
+
+ #Update handshake hashes
+ sToHash = bytesToString(p.bytes)
+ self._handshake_md5.update(sToHash)
+ self._handshake_sha.update(sToHash)
+
+ #Parse based on handshake type
+ if subType == HandshakeType.client_hello:
+ yield ClientHello(recordHeader.ssl2).parse(p)
+ elif subType == HandshakeType.server_hello:
+ yield ServerHello().parse(p)
+ elif subType == HandshakeType.certificate:
+ yield Certificate(constructorType).parse(p)
+ elif subType == HandshakeType.certificate_request:
+ yield CertificateRequest().parse(p)
+ elif subType == HandshakeType.certificate_verify:
+ yield CertificateVerify().parse(p)
+ elif subType == HandshakeType.server_key_exchange:
+ yield ServerKeyExchange(constructorType).parse(p)
+ elif subType == HandshakeType.server_hello_done:
+ yield ServerHelloDone().parse(p)
+ elif subType == HandshakeType.client_key_exchange:
+ yield ClientKeyExchange(constructorType, \
+ self.version).parse(p)
+ elif subType == HandshakeType.finished:
+ yield Finished(self.version).parse(p)
+ else:
+ raise AssertionError()
+
+ #If an exception was raised by a Parser or Message instance:
+ except SyntaxError, e:
+ for result in self._sendError(AlertDescription.decode_error,
+ formatExceptionTrace(e)):
+ yield result
+
+
+ #Returns next record or next handshake message
+ def _getNextRecord(self):
+
+ #If there's a handshake message waiting, return it
+ if self._handshakeBuffer:
+ recordHeader, bytes = self._handshakeBuffer[0]
+ self._handshakeBuffer = self._handshakeBuffer[1:]
+ yield (recordHeader, Parser(bytes))
+ return
+
+ #Otherwise...
+ #Read the next record header
+ bytes = createByteArraySequence([])
+ recordHeaderLength = 1
+ ssl2 = False
+ while 1:
+ try:
+ s = self.sock.recv(recordHeaderLength-len(bytes))
+ except socket.error, why:
+ if why[0] == errno.EWOULDBLOCK:
+ yield 0
+ continue
+ else:
+ raise
+
+ #If the connection was abruptly closed, raise an error
+ if len(s)==0:
+ raise TLSAbruptCloseError()
+
+ bytes += stringToBytes(s)
+ if len(bytes)==1:
+ if bytes[0] in ContentType.all:
+ ssl2 = False
+ recordHeaderLength = 5
+ elif bytes[0] == 128:
+ ssl2 = True
+ recordHeaderLength = 2
+ else:
+ raise SyntaxError()
+ if len(bytes) == recordHeaderLength:
+ break
+
+ #Parse the record header
+ if ssl2:
+ r = RecordHeader2().parse(Parser(bytes))
+ else:
+ r = RecordHeader3().parse(Parser(bytes))
+
+ #Check the record header fields
+ if r.length > 18432:
+ for result in self._sendError(AlertDescription.record_overflow):
+ yield result
+
+ #Read the record contents
+ bytes = createByteArraySequence([])
+ while 1:
+ try:
+ s = self.sock.recv(r.length - len(bytes))
+ except socket.error, why:
+ if why[0] == errno.EWOULDBLOCK:
+ yield 0
+ continue
+ else:
+ raise
+
+ #If the connection is closed, raise a socket error
+ if len(s)==0:
+ raise TLSAbruptCloseError()
+
+ bytes += stringToBytes(s)
+ if len(bytes) == r.length:
+ break
+
+ #Check the record header fields (2)
+ #We do this after reading the contents from the socket, so that
+ #if there's an error, we at least don't leave extra bytes in the
+ #socket..
+ #
+ # THIS CHECK HAS NO SECURITY RELEVANCE (?), BUT COULD HURT INTEROP.
+ # SO WE LEAVE IT OUT FOR NOW.
+ #
+ #if self._versionCheck and r.version != self.version:
+ # for result in self._sendError(AlertDescription.protocol_version,
+ # "Version in header field: %s, should be %s" % (str(r.version),
+ # str(self.version))):
+ # yield result
+
+ #Decrypt the record
+ for result in self._decryptRecord(r.type, bytes):
+ if result in (0,1):
+ yield result
+ else:
+ break
+ bytes = result
+ p = Parser(bytes)
+
+ #If it doesn't contain handshake messages, we can just return it
+ if r.type != ContentType.handshake:
+ yield (r, p)
+ #If it's an SSLv2 ClientHello, we can return it as well
+ elif r.ssl2:
+ yield (r, p)
+ else:
+ #Otherwise, we loop through and add the handshake messages to the
+ #handshake buffer
+ while 1:
+ if p.index == len(bytes): #If we're at the end
+ if not self._handshakeBuffer:
+ for result in self._sendError(\
+ AlertDescription.decode_error, \
+ "Received empty handshake record"):
+ yield result
+ break
+ #There needs to be at least 4 bytes to get a header
+ if p.index+4 > len(bytes):
+ for result in self._sendError(\
+ AlertDescription.decode_error,
+ "A record has a partial handshake message (1)"):
+ yield result
+ p.get(1) # skip handshake type
+ msgLength = p.get(3)
+ if p.index+msgLength > len(bytes):
+ for result in self._sendError(\
+ AlertDescription.decode_error,
+ "A record has a partial handshake message (2)"):
+ yield result
+
+ handshakePair = (r, bytes[p.index-4 : p.index+msgLength])
+ self._handshakeBuffer.append(handshakePair)
+ p.index += msgLength
+
+ #We've moved at least one handshake message into the
+ #handshakeBuffer, return the first one
+ recordHeader, bytes = self._handshakeBuffer[0]
+ self._handshakeBuffer = self._handshakeBuffer[1:]
+ yield (recordHeader, Parser(bytes))
+
+
+ def _decryptRecord(self, recordType, bytes):
+ if self._readState.encContext:
+
+ #Decrypt if it's a block cipher
+ if self._readState.encContext.isBlockCipher:
+ blockLength = self._readState.encContext.block_size
+ if len(bytes) % blockLength != 0:
+ for result in self._sendError(\
+ AlertDescription.decryption_failed,
+ "Encrypted data not a multiple of blocksize"):
+ yield result
+ ciphertext = bytesToString(bytes)
+ plaintext = self._readState.encContext.decrypt(ciphertext)
+ if self.version == (3,2): #For TLS 1.1, remove explicit IV
+ plaintext = plaintext[self._readState.encContext.block_size : ]
+ bytes = stringToBytes(plaintext)
+
+ #Check padding
+ paddingGood = True
+ paddingLength = bytes[-1]
+ if (paddingLength+1) > len(bytes):
+ paddingGood=False
+ totalPaddingLength = 0
+ else:
+ if self.version == (3,0):
+ totalPaddingLength = paddingLength+1
+ elif self.version in ((3,1), (3,2)):
+ totalPaddingLength = paddingLength+1
+ paddingBytes = bytes[-totalPaddingLength:-1]
+ for byte in paddingBytes:
+ if byte != paddingLength:
+ paddingGood = False
+ totalPaddingLength = 0
+ else:
+ raise AssertionError()
+
+ #Decrypt if it's a stream cipher
+ else:
+ paddingGood = True
+ ciphertext = bytesToString(bytes)
+ plaintext = self._readState.encContext.decrypt(ciphertext)
+ bytes = stringToBytes(plaintext)
+ totalPaddingLength = 0
+
+ #Check MAC
+ macGood = True
+ macLength = self._readState.macContext.digest_size
+ endLength = macLength + totalPaddingLength
+ if endLength > len(bytes):
+ macGood = False
+ else:
+ #Read MAC
+ startIndex = len(bytes) - endLength
+ endIndex = startIndex + macLength
+ checkBytes = bytes[startIndex : endIndex]
+
+ #Calculate MAC
+ seqnumStr = self._readState.getSeqNumStr()
+ bytes = bytes[:-endLength]
+ bytesStr = bytesToString(bytes)
+ mac = self._readState.macContext.copy()
+ mac.update(seqnumStr)
+ mac.update(chr(recordType))
+ if self.version == (3,0):
+ mac.update( chr( int(len(bytes)/256) ) )
+ mac.update( chr( int(len(bytes)%256) ) )
+ elif self.version in ((3,1), (3,2)):
+ mac.update(chr(self.version[0]))
+ mac.update(chr(self.version[1]))
+ mac.update( chr( int(len(bytes)/256) ) )
+ mac.update( chr( int(len(bytes)%256) ) )
+ else:
+ raise AssertionError()
+ mac.update(bytesStr)
+ macString = mac.digest()
+ macBytes = stringToBytes(macString)
+
+ #Compare MACs
+ if macBytes != checkBytes:
+ macGood = False
+
+ if not (paddingGood and macGood):
+ for result in self._sendError(AlertDescription.bad_record_mac,
+ "MAC failure (or padding failure)"):
+ yield result
+
+ yield bytes
+
+ def _handshakeStart(self, client):
+ self._client = client
+ self._handshake_md5 = md5.md5()
+ self._handshake_sha = sha.sha()
+ self._handshakeBuffer = []
+ self.allegedSharedKeyUsername = None
+ self.allegedSrpUsername = None
+ self._refCount = 1
+
+ def _handshakeDone(self, resumed):
+ self.resumed = resumed
+ self.closed = False
+
+ def _calcPendingStates(self, clientRandom, serverRandom, implementations):
+ if self.session.cipherSuite in CipherSuite.aes128Suites:
+ macLength = 20
+ keyLength = 16
+ ivLength = 16
+ createCipherFunc = createAES
+ elif self.session.cipherSuite in CipherSuite.aes256Suites:
+ macLength = 20
+ keyLength = 32
+ ivLength = 16
+ createCipherFunc = createAES
+ elif self.session.cipherSuite in CipherSuite.rc4Suites:
+ macLength = 20
+ keyLength = 16
+ ivLength = 0
+ createCipherFunc = createRC4
+ elif self.session.cipherSuite in CipherSuite.tripleDESSuites:
+ macLength = 20
+ keyLength = 24
+ ivLength = 8
+ createCipherFunc = createTripleDES
+ else:
+ raise AssertionError()
+
+ if self.version == (3,0):
+ createMACFunc = MAC_SSL
+ elif self.version in ((3,1), (3,2)):
+ createMACFunc = hmac.HMAC
+
+ outputLength = (macLength*2) + (keyLength*2) + (ivLength*2)
+
+ #Calculate Keying Material from Master Secret
+ if self.version == (3,0):
+ keyBlock = PRF_SSL(self.session.masterSecret,
+ concatArrays(serverRandom, clientRandom),
+ outputLength)
+ elif self.version in ((3,1), (3,2)):
+ keyBlock = PRF(self.session.masterSecret,
+ "key expansion",
+ concatArrays(serverRandom,clientRandom),
+ outputLength)
+ else:
+ raise AssertionError()
+
+ #Slice up Keying Material
+ clientPendingState = _ConnectionState()
+ serverPendingState = _ConnectionState()
+ p = Parser(keyBlock)
+ clientMACBlock = bytesToString(p.getFixBytes(macLength))
+ serverMACBlock = bytesToString(p.getFixBytes(macLength))
+ clientKeyBlock = bytesToString(p.getFixBytes(keyLength))
+ serverKeyBlock = bytesToString(p.getFixBytes(keyLength))
+ clientIVBlock = bytesToString(p.getFixBytes(ivLength))
+ serverIVBlock = bytesToString(p.getFixBytes(ivLength))
+ clientPendingState.macContext = createMACFunc(clientMACBlock,
+ digestmod=sha)
+ serverPendingState.macContext = createMACFunc(serverMACBlock,
+ digestmod=sha)
+ clientPendingState.encContext = createCipherFunc(clientKeyBlock,
+ clientIVBlock,
+ implementations)
+ serverPendingState.encContext = createCipherFunc(serverKeyBlock,
+ serverIVBlock,
+ implementations)
+
+ #Assign new connection states to pending states
+ if self._client:
+ self._pendingWriteState = clientPendingState
+ self._pendingReadState = serverPendingState
+ else:
+ self._pendingWriteState = serverPendingState
+ self._pendingReadState = clientPendingState
+
+ if self.version == (3,2) and ivLength:
+ #Choose fixedIVBlock for TLS 1.1 (this is encrypted with the CBC
+ #residue to create the IV for each sent block)
+ self.fixedIVBlock = getRandomBytes(ivLength)
+
+ def _changeWriteState(self):
+ self._writeState = self._pendingWriteState
+ self._pendingWriteState = _ConnectionState()
+
+ def _changeReadState(self):
+ self._readState = self._pendingReadState
+ self._pendingReadState = _ConnectionState()
+
+ def _sendFinished(self):
+ #Send ChangeCipherSpec
+ for result in self._sendMsg(ChangeCipherSpec()):
+ yield result
+
+ #Switch to pending write state
+ self._changeWriteState()
+
+ #Calculate verification data
+ verifyData = self._calcFinished(True)
+ if self.fault == Fault.badFinished:
+ verifyData[0] = (verifyData[0]+1)%256
+
+ #Send Finished message under new state
+ finished = Finished(self.version).create(verifyData)
+ for result in self._sendMsg(finished):
+ yield result
+
+ def _getFinished(self):
+ #Get and check ChangeCipherSpec
+ for result in self._getMsg(ContentType.change_cipher_spec):
+ if result in (0,1):
+ yield result
+ changeCipherSpec = result
+
+ if changeCipherSpec.type != 1:
+ for result in self._sendError(AlertDescription.illegal_parameter,
+ "ChangeCipherSpec type incorrect"):
+ yield result
+
+ #Switch to pending read state
+ self._changeReadState()
+
+ #Calculate verification data
+ verifyData = self._calcFinished(False)
+
+ #Get and check Finished message under new state
+ for result in self._getMsg(ContentType.handshake,
+ HandshakeType.finished):
+ if result in (0,1):
+ yield result
+ finished = result
+ if finished.verify_data != verifyData:
+ for result in self._sendError(AlertDescription.decrypt_error,
+ "Finished message is incorrect"):
+ yield result
+
+ def _calcFinished(self, send=True):
+ if self.version == (3,0):
+ if (self._client and send) or (not self._client and not send):
+ senderStr = "\x43\x4C\x4E\x54"
+ else:
+ senderStr = "\x53\x52\x56\x52"
+
+ verifyData = self._calcSSLHandshakeHash(self.session.masterSecret,
+ senderStr)
+ return verifyData
+
+ elif self.version in ((3,1), (3,2)):
+ if (self._client and send) or (not self._client and not send):
+ label = "client finished"
+ else:
+ label = "server finished"
+
+ handshakeHashes = stringToBytes(self._handshake_md5.digest() + \
+ self._handshake_sha.digest())
+ verifyData = PRF(self.session.masterSecret, label, handshakeHashes,
+ 12)
+ return verifyData
+ else:
+ raise AssertionError()
+
+ #Used for Finished messages and CertificateVerify messages in SSL v3
+ def _calcSSLHandshakeHash(self, masterSecret, label):
+ masterSecretStr = bytesToString(masterSecret)
+
+ imac_md5 = self._handshake_md5.copy()
+ imac_sha = self._handshake_sha.copy()
+
+ imac_md5.update(label + masterSecretStr + '\x36'*48)
+ imac_sha.update(label + masterSecretStr + '\x36'*40)
+
+ md5Str = md5.md5(masterSecretStr + ('\x5c'*48) + \
+ imac_md5.digest()).digest()
+ shaStr = sha.sha(masterSecretStr + ('\x5c'*40) + \
+ imac_sha.digest()).digest()
+
+ return stringToBytes(md5Str + shaStr)
+
diff --git a/src/lib/tlslite/VerifierDB.py b/src/lib/tlslite/VerifierDB.py
new file mode 100755
index 000000000..f706b1796
--- /dev/null
+++ b/src/lib/tlslite/VerifierDB.py
@@ -0,0 +1,90 @@
+"""Class for storing SRP password verifiers."""
+
+from utils.cryptomath import *
+from utils.compat import *
+import mathtls
+from BaseDB import BaseDB
+
+class VerifierDB(BaseDB):
+ """This class represent an in-memory or on-disk database of SRP
+ password verifiers.
+
+ A VerifierDB can be passed to a server handshake to authenticate
+ a client based on one of the verifiers.
+
+ This class is thread-safe.
+ """
+ def __init__(self, filename=None):
+ """Create a new VerifierDB instance.
+
+ @type filename: str
+ @param filename: Filename for an on-disk database, or None for
+ an in-memory database. If the filename already exists, follow
+ this with a call to open(). To create a new on-disk database,
+ follow this with a call to create().
+ """
+ BaseDB.__init__(self, filename, "verifier")
+
+ def _getItem(self, username, valueStr):
+ (N, g, salt, verifier) = valueStr.split(" ")
+ N = base64ToNumber(N)
+ g = base64ToNumber(g)
+ salt = base64ToString(salt)
+ verifier = base64ToNumber(verifier)
+ return (N, g, salt, verifier)
+
+ def __setitem__(self, username, verifierEntry):
+ """Add a verifier entry to the database.
+
+ @type username: str
+ @param username: The username to associate the verifier with.
+ Must be less than 256 characters in length. Must not already
+ be in the database.
+
+ @type verifierEntry: tuple
+ @param verifierEntry: The verifier entry to add. Use
+ L{tlslite.VerifierDB.VerifierDB.makeVerifier} to create a
+ verifier entry.
+ """
+ BaseDB.__setitem__(self, username, verifierEntry)
+
+
+ def _setItem(self, username, value):
+ if len(username)>=256:
+ raise ValueError("username too long")
+ N, g, salt, verifier = value
+ N = numberToBase64(N)
+ g = numberToBase64(g)
+ salt = stringToBase64(salt)
+ verifier = numberToBase64(verifier)
+ valueStr = " ".join( (N, g, salt, verifier) )
+ return valueStr
+
+ def _checkItem(self, value, username, param):
+ (N, g, salt, verifier) = value
+ x = mathtls.makeX(salt, username, param)
+ v = powMod(g, x, N)
+ return (verifier == v)
+
+
+ def makeVerifier(username, password, bits):
+ """Create a verifier entry which can be stored in a VerifierDB.
+
+ @type username: str
+ @param username: The username for this verifier. Must be less
+ than 256 characters in length.
+
+ @type password: str
+ @param password: The password for this verifier.
+
+ @type bits: int
+ @param bits: This values specifies which SRP group parameters
+ to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144,
+ 8192). Larger values are more secure but slower. 2048 is a
+ good compromise between safety and speed.
+
+ @rtype: tuple
+ @return: A tuple which may be stored in a VerifierDB.
+ """
+ return mathtls.makeVerifier(username, password, bits)
+ makeVerifier = staticmethod(makeVerifier) \ No newline at end of file
diff --git a/src/lib/tlslite/X509.py b/src/lib/tlslite/X509.py
new file mode 100755
index 000000000..a47ddcfa2
--- /dev/null
+++ b/src/lib/tlslite/X509.py
@@ -0,0 +1,133 @@
+"""Class representing an X.509 certificate."""
+
+from utils.ASN1Parser import ASN1Parser
+from utils.cryptomath import *
+from utils.keyfactory import _createPublicRSAKey
+
+
+class X509:
+ """This class represents an X.509 certificate.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @ivar bytes: The DER-encoded ASN.1 certificate
+
+ @type publicKey: L{tlslite.utils.RSAKey.RSAKey}
+ @ivar publicKey: The subject public key from the certificate.
+ """
+
+ def __init__(self):
+ self.bytes = createByteArraySequence([])
+ self.publicKey = None
+
+ def parse(self, s):
+ """Parse a PEM-encoded X.509 certificate.
+
+ @type s: str
+ @param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded
+ certificate wrapped with "-----BEGIN CERTIFICATE-----" and
+ "-----END CERTIFICATE-----" tags).
+ """
+
+ start = s.find("-----BEGIN CERTIFICATE-----")
+ end = s.find("-----END CERTIFICATE-----")
+ if start == -1:
+ raise SyntaxError("Missing PEM prefix")
+ if end == -1:
+ raise SyntaxError("Missing PEM postfix")
+ s = s[start+len("-----BEGIN CERTIFICATE-----") : end]
+
+ bytes = base64ToBytes(s)
+ self.parseBinary(bytes)
+ return self
+
+ def parseBinary(self, bytes):
+ """Parse a DER-encoded X.509 certificate.
+
+ @type bytes: str or L{array.array} of unsigned bytes
+ @param bytes: A DER-encoded X.509 certificate.
+ """
+
+ if isinstance(bytes, type("")):
+ bytes = stringToBytes(bytes)
+
+ self.bytes = bytes
+ p = ASN1Parser(bytes)
+
+ #Get the tbsCertificate
+ tbsCertificateP = p.getChild(0)
+
+ #Is the optional version field present?
+ #This determines which index the key is at.
+ if tbsCertificateP.value[0]==0xA0:
+ subjectPublicKeyInfoIndex = 6
+ else:
+ subjectPublicKeyInfoIndex = 5
+
+ #Get the subjectPublicKeyInfo
+ subjectPublicKeyInfoP = tbsCertificateP.getChild(\
+ subjectPublicKeyInfoIndex)
+
+ #Get the algorithm
+ algorithmP = subjectPublicKeyInfoP.getChild(0)
+ rsaOID = algorithmP.value
+ if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]:
+ raise SyntaxError("Unrecognized AlgorithmIdentifier")
+
+ #Get the subjectPublicKey
+ subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1)
+
+ #Adjust for BIT STRING encapsulation
+ if (subjectPublicKeyP.value[0] !=0):
+ raise SyntaxError()
+ subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:])
+
+ #Get the modulus and exponent
+ modulusP = subjectPublicKeyP.getChild(0)
+ publicExponentP = subjectPublicKeyP.getChild(1)
+
+ #Decode them into numbers
+ n = bytesToNumber(modulusP.value)
+ e = bytesToNumber(publicExponentP.value)
+
+ #Create a public key instance
+ self.publicKey = _createPublicRSAKey(n, e)
+
+ def getFingerprint(self):
+ """Get the hex-encoded fingerprint of this certificate.
+
+ @rtype: str
+ @return: A hex-encoded fingerprint.
+ """
+ return sha.sha(self.bytes).hexdigest()
+
+ def getCommonName(self):
+ """Get the Subject's Common Name from the certificate.
+
+ The cryptlib_py module must be installed in order to use this
+ function.
+
+ @rtype: str or None
+ @return: The CN component of the certificate's subject DN, if
+ present.
+ """
+ import cryptlib_py
+ import array
+ c = cryptlib_py.cryptImportCert(self.bytes, cryptlib_py.CRYPT_UNUSED)
+ name = cryptlib_py.CRYPT_CERTINFO_COMMONNAME
+ try:
+ try:
+ length = cryptlib_py.cryptGetAttributeString(c, name, None)
+ returnVal = array.array('B', [0] * length)
+ cryptlib_py.cryptGetAttributeString(c, name, returnVal)
+ returnVal = returnVal.tostring()
+ except cryptlib_py.CryptException, e:
+ if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
+ returnVal = None
+ return returnVal
+ finally:
+ cryptlib_py.cryptDestroyCert(c)
+
+ def writeBytes(self):
+ return self.bytes
+
+
diff --git a/src/lib/tlslite/X509CertChain.py b/src/lib/tlslite/X509CertChain.py
new file mode 100755
index 000000000..d5f0b4d42
--- /dev/null
+++ b/src/lib/tlslite/X509CertChain.py
@@ -0,0 +1,181 @@
+"""Class representing an X.509 certificate chain."""
+
+from utils import cryptomath
+
+class X509CertChain:
+ """This class represents a chain of X.509 certificates.
+
+ @type x509List: list
+ @ivar x509List: A list of L{tlslite.X509.X509} instances,
+ starting with the end-entity certificate and with every
+ subsequent certificate certifying the previous.
+ """
+
+ def __init__(self, x509List=None):
+ """Create a new X509CertChain.
+
+ @type x509List: list
+ @param x509List: A list of L{tlslite.X509.X509} instances,
+ starting with the end-entity certificate and with every
+ subsequent certificate certifying the previous.
+ """
+ if x509List:
+ self.x509List = x509List
+ else:
+ self.x509List = []
+
+ def getNumCerts(self):
+ """Get the number of certificates in this chain.
+
+ @rtype: int
+ """
+ return len(self.x509List)
+
+ def getEndEntityPublicKey(self):
+ """Get the public key from the end-entity certificate.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ """
+ if self.getNumCerts() == 0:
+ raise AssertionError()
+ return self.x509List[0].publicKey
+
+ def getFingerprint(self):
+ """Get the hex-encoded fingerprint of the end-entity certificate.
+
+ @rtype: str
+ @return: A hex-encoded fingerprint.
+ """
+ if self.getNumCerts() == 0:
+ raise AssertionError()
+ return self.x509List[0].getFingerprint()
+
+ def getCommonName(self):
+ """Get the Subject's Common Name from the end-entity certificate.
+
+ The cryptlib_py module must be installed in order to use this
+ function.
+
+ @rtype: str or None
+ @return: The CN component of the certificate's subject DN, if
+ present.
+ """
+ if self.getNumCerts() == 0:
+ raise AssertionError()
+ return self.x509List[0].getCommonName()
+
+ def validate(self, x509TrustList):
+ """Check the validity of the certificate chain.
+
+ This checks that every certificate in the chain validates with
+ the subsequent one, until some certificate validates with (or
+ is identical to) one of the passed-in root certificates.
+
+ The cryptlib_py module must be installed in order to use this
+ function.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ certificate chain must extend to one of these certificates to
+ be considered valid.
+ """
+
+ import cryptlib_py
+ c1 = None
+ c2 = None
+ lastC = None
+ rootC = None
+
+ try:
+ rootFingerprints = [c.getFingerprint() for c in x509TrustList]
+
+ #Check that every certificate in the chain validates with the
+ #next one
+ for cert1, cert2 in zip(self.x509List, self.x509List[1:]):
+
+ #If we come upon a root certificate, we're done.
+ if cert1.getFingerprint() in rootFingerprints:
+ return True
+
+ c1 = cryptlib_py.cryptImportCert(cert1.writeBytes(),
+ cryptlib_py.CRYPT_UNUSED)
+ c2 = cryptlib_py.cryptImportCert(cert2.writeBytes(),
+ cryptlib_py.CRYPT_UNUSED)
+ try:
+ cryptlib_py.cryptCheckCert(c1, c2)
+ except:
+ return False
+ cryptlib_py.cryptDestroyCert(c1)
+ c1 = None
+ cryptlib_py.cryptDestroyCert(c2)
+ c2 = None
+
+ #If the last certificate is one of the root certificates, we're
+ #done.
+ if self.x509List[-1].getFingerprint() in rootFingerprints:
+ return True
+
+ #Otherwise, find a root certificate that the last certificate
+ #chains to, and validate them.
+ lastC = cryptlib_py.cryptImportCert(self.x509List[-1].writeBytes(),
+ cryptlib_py.CRYPT_UNUSED)
+ for rootCert in x509TrustList:
+ rootC = cryptlib_py.cryptImportCert(rootCert.writeBytes(),
+ cryptlib_py.CRYPT_UNUSED)
+ if self._checkChaining(lastC, rootC):
+ try:
+ cryptlib_py.cryptCheckCert(lastC, rootC)
+ return True
+ except:
+ return False
+ return False
+ finally:
+ if not (c1 is None):
+ cryptlib_py.cryptDestroyCert(c1)
+ if not (c2 is None):
+ cryptlib_py.cryptDestroyCert(c2)
+ if not (lastC is None):
+ cryptlib_py.cryptDestroyCert(lastC)
+ if not (rootC is None):
+ cryptlib_py.cryptDestroyCert(rootC)
+
+
+
+ def _checkChaining(self, lastC, rootC):
+ import cryptlib_py
+ import array
+ def compareNames(name):
+ try:
+ length = cryptlib_py.cryptGetAttributeString(lastC, name, None)
+ lastName = array.array('B', [0] * length)
+ cryptlib_py.cryptGetAttributeString(lastC, name, lastName)
+ lastName = lastName.tostring()
+ except cryptlib_py.CryptException, e:
+ if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
+ lastName = None
+ try:
+ length = cryptlib_py.cryptGetAttributeString(rootC, name, None)
+ rootName = array.array('B', [0] * length)
+ cryptlib_py.cryptGetAttributeString(rootC, name, rootName)
+ rootName = rootName.tostring()
+ except cryptlib_py.CryptException, e:
+ if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
+ rootName = None
+
+ return lastName == rootName
+
+ cryptlib_py.cryptSetAttribute(lastC,
+ cryptlib_py.CRYPT_CERTINFO_ISSUERNAME,
+ cryptlib_py.CRYPT_UNUSED)
+
+ if not compareNames(cryptlib_py.CRYPT_CERTINFO_COUNTRYNAME):
+ return False
+ if not compareNames(cryptlib_py.CRYPT_CERTINFO_LOCALITYNAME):
+ return False
+ if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONNAME):
+ return False
+ if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONALUNITNAME):
+ return False
+ if not compareNames(cryptlib_py.CRYPT_CERTINFO_COMMONNAME):
+ return False
+ return True \ No newline at end of file
diff --git a/src/lib/tlslite/__init__.py b/src/lib/tlslite/__init__.py
new file mode 100755
index 000000000..47cfd1c6f
--- /dev/null
+++ b/src/lib/tlslite/__init__.py
@@ -0,0 +1,39 @@
+"""
+TLS Lite is a free python library that implements SSL v3, TLS v1, and
+TLS v1.1. TLS Lite supports non-traditional authentication methods
+such as SRP, shared keys, and cryptoIDs, in addition to X.509
+certificates. TLS Lite is pure python, however it can access OpenSSL,
+cryptlib, pycrypto, and GMPY for faster crypto operations. TLS Lite
+integrates with httplib, xmlrpclib, poplib, imaplib, smtplib,
+SocketServer, asyncore, and Twisted.
+
+To use, do::
+
+ from tlslite.api import *
+
+Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket,
+or use one of the integration classes in L{tlslite.integration}.
+
+@version: 0.3.8
+"""
+__version__ = "0.3.8"
+
+__all__ = ["api",
+ "BaseDB",
+ "Checker",
+ "constants",
+ "errors",
+ "FileObject",
+ "HandshakeSettings",
+ "mathtls",
+ "messages",
+ "Session",
+ "SessionCache",
+ "SharedKeyDB",
+ "TLSConnection",
+ "TLSRecordLayer",
+ "VerifierDB",
+ "X509",
+ "X509CertChain",
+ "integration",
+ "utils"]
diff --git a/src/lib/tlslite/api.py b/src/lib/tlslite/api.py
new file mode 100755
index 000000000..eebfbc609
--- /dev/null
+++ b/src/lib/tlslite/api.py
@@ -0,0 +1,75 @@
+"""Import this module for easy access to TLS Lite objects.
+
+The TLS Lite API consists of classes, functions, and variables spread
+throughout this package. Instead of importing them individually with::
+
+ from tlslite.TLSConnection import TLSConnection
+ from tlslite.HandshakeSettings import HandshakeSettings
+ from tlslite.errors import *
+ .
+ .
+
+It's easier to do::
+
+ from tlslite.api import *
+
+This imports all the important objects (TLSConnection, Checker,
+HandshakeSettings, etc.) into the global namespace. In particular, it
+imports::
+
+ from constants import AlertLevel, AlertDescription, Fault
+ from errors import *
+ from Checker import Checker
+ from HandshakeSettings import HandshakeSettings
+ from Session import Session
+ from SessionCache import SessionCache
+ from SharedKeyDB import SharedKeyDB
+ from TLSConnection import TLSConnection
+ from VerifierDB import VerifierDB
+ from X509 import X509
+ from X509CertChain import X509CertChain
+
+ from integration.HTTPTLSConnection import HTTPTLSConnection
+ from integration.POP3_TLS import POP3_TLS
+ from integration.IMAP4_TLS import IMAP4_TLS
+ from integration.SMTP_TLS import SMTP_TLS
+ from integration.XMLRPCTransport import XMLRPCTransport
+ from integration.TLSSocketServerMixIn import TLSSocketServerMixIn
+ from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn
+ from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper
+ from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded,
+ gmpyLoaded, pycryptoLoaded, prngName
+ from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey,
+ parseAsPublicKey, parsePrivateKey
+"""
+
+from constants import AlertLevel, AlertDescription, Fault
+from errors import *
+from Checker import Checker
+from HandshakeSettings import HandshakeSettings
+from Session import Session
+from SessionCache import SessionCache
+from SharedKeyDB import SharedKeyDB
+from TLSConnection import TLSConnection
+from VerifierDB import VerifierDB
+from X509 import X509
+from X509CertChain import X509CertChain
+
+from integration.HTTPTLSConnection import HTTPTLSConnection
+from integration.TLSSocketServerMixIn import TLSSocketServerMixIn
+from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn
+from integration.POP3_TLS import POP3_TLS
+from integration.IMAP4_TLS import IMAP4_TLS
+from integration.SMTP_TLS import SMTP_TLS
+from integration.XMLRPCTransport import XMLRPCTransport
+try:
+ import twisted
+ del(twisted)
+ from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper
+except ImportError:
+ pass
+
+from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, gmpyLoaded, \
+ pycryptoLoaded, prngName
+from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, \
+ parseAsPublicKey, parsePrivateKey
diff --git a/src/lib/tlslite/constants.py b/src/lib/tlslite/constants.py
new file mode 100755
index 000000000..8f2d5590e
--- /dev/null
+++ b/src/lib/tlslite/constants.py
@@ -0,0 +1,225 @@
+"""Constants used in various places."""
+
+class CertificateType:
+ x509 = 0
+ openpgp = 1
+ cryptoID = 2
+
+class HandshakeType:
+ hello_request = 0
+ client_hello = 1
+ server_hello = 2
+ certificate = 11
+ server_key_exchange = 12
+ certificate_request = 13
+ server_hello_done = 14
+ certificate_verify = 15
+ client_key_exchange = 16
+ finished = 20
+
+class ContentType:
+ change_cipher_spec = 20
+ alert = 21
+ handshake = 22
+ application_data = 23
+ all = (20,21,22,23)
+
+class AlertLevel:
+ warning = 1
+ fatal = 2
+
+class AlertDescription:
+ """
+ @cvar bad_record_mac: A TLS record failed to decrypt properly.
+
+ If this occurs during a shared-key or SRP handshake it most likely
+ indicates a bad password. It may also indicate an implementation
+ error, or some tampering with the data in transit.
+
+ This alert will be signalled by the server if the SRP password is bad. It
+ may also be signalled by the server if the SRP username is unknown to the
+ server, but it doesn't wish to reveal that fact.
+
+ This alert will be signalled by the client if the shared-key username is
+ bad.
+
+ @cvar handshake_failure: A problem occurred while handshaking.
+
+ This typically indicates a lack of common ciphersuites between client and
+ server, or some other disagreement (about SRP parameters or key sizes,
+ for example).
+
+ @cvar protocol_version: The other party's SSL/TLS version was unacceptable.
+
+ This indicates that the client and server couldn't agree on which version
+ of SSL or TLS to use.
+
+ @cvar user_canceled: The handshake is being cancelled for some reason.
+
+ """
+
+ close_notify = 0
+ unexpected_message = 10
+ bad_record_mac = 20
+ decryption_failed = 21
+ record_overflow = 22
+ decompression_failure = 30
+ handshake_failure = 40
+ no_certificate = 41 #SSLv3
+ bad_certificate = 42
+ unsupported_certificate = 43
+ certificate_revoked = 44
+ certificate_expired = 45
+ certificate_unknown = 46
+ illegal_parameter = 47
+ unknown_ca = 48
+ access_denied = 49
+ decode_error = 50
+ decrypt_error = 51
+ export_restriction = 60
+ protocol_version = 70
+ insufficient_security = 71
+ internal_error = 80
+ user_canceled = 90
+ no_renegotiation = 100
+ unknown_srp_username = 120
+ missing_srp_username = 121
+ untrusted_srp_parameters = 122
+
+class CipherSuite:
+ TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0x0050
+ TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0x0053
+ TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0x0056
+
+ TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0x0051
+ TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0x0054
+ TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0x0057
+
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035
+ TLS_RSA_WITH_RC4_128_SHA = 0x0005
+
+ srpSuites = []
+ srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
+ srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
+ srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
+ def getSrpSuites(ciphers):
+ suites = []
+ for cipher in ciphers:
+ if cipher == "aes128":
+ suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
+ elif cipher == "aes256":
+ suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
+ elif cipher == "3des":
+ suites.append(CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
+ return suites
+ getSrpSuites = staticmethod(getSrpSuites)
+
+ srpRsaSuites = []
+ srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
+ srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
+ srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
+ def getSrpRsaSuites(ciphers):
+ suites = []
+ for cipher in ciphers:
+ if cipher == "aes128":
+ suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
+ elif cipher == "aes256":
+ suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
+ elif cipher == "3des":
+ suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
+ return suites
+ getSrpRsaSuites = staticmethod(getSrpRsaSuites)
+
+ rsaSuites = []
+ rsaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA)
+ rsaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA)
+ rsaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA)
+ rsaSuites.append(TLS_RSA_WITH_RC4_128_SHA)
+ def getRsaSuites(ciphers):
+ suites = []
+ for cipher in ciphers:
+ if cipher == "aes128":
+ suites.append(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA)
+ elif cipher == "aes256":
+ suites.append(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA)
+ elif cipher == "rc4":
+ suites.append(CipherSuite.TLS_RSA_WITH_RC4_128_SHA)
+ elif cipher == "3des":
+ suites.append(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
+ return suites
+ getRsaSuites = staticmethod(getRsaSuites)
+
+ tripleDESSuites = []
+ tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
+ tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
+ tripleDESSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA)
+
+ aes128Suites = []
+ aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
+ aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
+ aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA)
+
+ aes256Suites = []
+ aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
+ aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
+ aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA)
+
+ rc4Suites = []
+ rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA)
+
+
+class Fault:
+ badUsername = 101
+ badPassword = 102
+ badA = 103
+ clientSrpFaults = range(101,104)
+
+ badVerifyMessage = 601
+ clientCertFaults = range(601,602)
+
+ badPremasterPadding = 501
+ shortPremasterSecret = 502
+ clientNoAuthFaults = range(501,503)
+
+ badIdentifier = 401
+ badSharedKey = 402
+ clientSharedKeyFaults = range(401,403)
+
+ badB = 201
+ serverFaults = range(201,202)
+
+ badFinished = 300
+ badMAC = 301
+ badPadding = 302
+ genericFaults = range(300,303)
+
+ faultAlerts = {\
+ badUsername: (AlertDescription.unknown_srp_username, \
+ AlertDescription.bad_record_mac),\
+ badPassword: (AlertDescription.bad_record_mac,),\
+ badA: (AlertDescription.illegal_parameter,),\
+ badIdentifier: (AlertDescription.handshake_failure,),\
+ badSharedKey: (AlertDescription.bad_record_mac,),\
+ badPremasterPadding: (AlertDescription.bad_record_mac,),\
+ shortPremasterSecret: (AlertDescription.bad_record_mac,),\
+ badVerifyMessage: (AlertDescription.decrypt_error,),\
+ badFinished: (AlertDescription.decrypt_error,),\
+ badMAC: (AlertDescription.bad_record_mac,),\
+ badPadding: (AlertDescription.bad_record_mac,)
+ }
+
+ faultNames = {\
+ badUsername: "bad username",\
+ badPassword: "bad password",\
+ badA: "bad A",\
+ badIdentifier: "bad identifier",\
+ badSharedKey: "bad sharedkey",\
+ badPremasterPadding: "bad premaster padding",\
+ shortPremasterSecret: "short premaster secret",\
+ badVerifyMessage: "bad verify message",\
+ badFinished: "bad finished message",\
+ badMAC: "bad MAC",\
+ badPadding: "bad padding"
+ }
diff --git a/src/lib/tlslite/errors.py b/src/lib/tlslite/errors.py
new file mode 100755
index 000000000..c7f7ba81d
--- /dev/null
+++ b/src/lib/tlslite/errors.py
@@ -0,0 +1,149 @@
+"""Exception classes.
+@sort: TLSError, TLSAbruptCloseError, TLSAlert, TLSLocalAlert, TLSRemoteAlert,
+TLSAuthenticationError, TLSNoAuthenticationError, TLSAuthenticationTypeError,
+TLSFingerprintError, TLSAuthorizationError, TLSValidationError, TLSFaultError
+"""
+
+from constants import AlertDescription, AlertLevel
+
+class TLSError(Exception):
+ """Base class for all TLS Lite exceptions."""
+ pass
+
+class TLSAbruptCloseError(TLSError):
+ """The socket was closed without a proper TLS shutdown.
+
+ The TLS specification mandates that an alert of some sort
+ must be sent before the underlying socket is closed. If the socket
+ is closed without this, it could signify that an attacker is trying
+ to truncate the connection. It could also signify a misbehaving
+ TLS implementation, or a random network failure.
+ """
+ pass
+
+class TLSAlert(TLSError):
+ """A TLS alert has been signalled."""
+ pass
+
+ _descriptionStr = {\
+ AlertDescription.close_notify: "close_notify",\
+ AlertDescription.unexpected_message: "unexpected_message",\
+ AlertDescription.bad_record_mac: "bad_record_mac",\
+ AlertDescription.decryption_failed: "decryption_failed",\
+ AlertDescription.record_overflow: "record_overflow",\
+ AlertDescription.decompression_failure: "decompression_failure",\
+ AlertDescription.handshake_failure: "handshake_failure",\
+ AlertDescription.no_certificate: "no certificate",\
+ AlertDescription.bad_certificate: "bad_certificate",\
+ AlertDescription.unsupported_certificate: "unsupported_certificate",\
+ AlertDescription.certificate_revoked: "certificate_revoked",\
+ AlertDescription.certificate_expired: "certificate_expired",\
+ AlertDescription.certificate_unknown: "certificate_unknown",\
+ AlertDescription.illegal_parameter: "illegal_parameter",\
+ AlertDescription.unknown_ca: "unknown_ca",\
+ AlertDescription.access_denied: "access_denied",\
+ AlertDescription.decode_error: "decode_error",\
+ AlertDescription.decrypt_error: "decrypt_error",\
+ AlertDescription.export_restriction: "export_restriction",\
+ AlertDescription.protocol_version: "protocol_version",\
+ AlertDescription.insufficient_security: "insufficient_security",\
+ AlertDescription.internal_error: "internal_error",\
+ AlertDescription.user_canceled: "user_canceled",\
+ AlertDescription.no_renegotiation: "no_renegotiation",\
+ AlertDescription.unknown_srp_username: "unknown_srp_username",\
+ AlertDescription.missing_srp_username: "missing_srp_username"}
+
+class TLSLocalAlert(TLSAlert):
+ """A TLS alert has been signalled by the local implementation.
+
+ @type description: int
+ @ivar description: Set to one of the constants in
+ L{tlslite.constants.AlertDescription}
+
+ @type level: int
+ @ivar level: Set to one of the constants in
+ L{tlslite.constants.AlertLevel}
+
+ @type message: str
+ @ivar message: Description of what went wrong.
+ """
+ def __init__(self, alert, message=None):
+ self.description = alert.description
+ self.level = alert.level
+ self.message = message
+
+ def __str__(self):
+ alertStr = TLSAlert._descriptionStr.get(self.description)
+ if alertStr == None:
+ alertStr = str(self.description)
+ if self.message:
+ return alertStr + ": " + self.message
+ else:
+ return alertStr
+
+class TLSRemoteAlert(TLSAlert):
+ """A TLS alert has been signalled by the remote implementation.
+
+ @type description: int
+ @ivar description: Set to one of the constants in
+ L{tlslite.constants.AlertDescription}
+
+ @type level: int
+ @ivar level: Set to one of the constants in
+ L{tlslite.constants.AlertLevel}
+ """
+ def __init__(self, alert):
+ self.description = alert.description
+ self.level = alert.level
+
+ def __str__(self):
+ alertStr = TLSAlert._descriptionStr.get(self.description)
+ if alertStr == None:
+ alertStr = str(self.description)
+ return alertStr
+
+class TLSAuthenticationError(TLSError):
+ """The handshake succeeded, but the other party's authentication
+ was inadequate.
+
+ This exception will only be raised when a
+ L{tlslite.Checker.Checker} has been passed to a handshake function.
+ The Checker will be invoked once the handshake completes, and if
+ the Checker objects to how the other party authenticated, a
+ subclass of this exception will be raised.
+ """
+ pass
+
+class TLSNoAuthenticationError(TLSAuthenticationError):
+ """The Checker was expecting the other party to authenticate with a
+ certificate chain, but this did not occur."""
+ pass
+
+class TLSAuthenticationTypeError(TLSAuthenticationError):
+ """The Checker was expecting the other party to authenticate with a
+ different type of certificate chain."""
+ pass
+
+class TLSFingerprintError(TLSAuthenticationError):
+ """The Checker was expecting the other party to authenticate with a
+ certificate chain that matches a different fingerprint."""
+ pass
+
+class TLSAuthorizationError(TLSAuthenticationError):
+ """The Checker was expecting the other party to authenticate with a
+ certificate chain that has a different authorization."""
+ pass
+
+class TLSValidationError(TLSAuthenticationError):
+ """The Checker has determined that the other party's certificate
+ chain is invalid."""
+ pass
+
+class TLSFaultError(TLSError):
+ """The other party responded incorrectly to an induced fault.
+
+ This exception will only occur during fault testing, when a
+ TLSConnection's fault variable is set to induce some sort of
+ faulty behavior, and the other party doesn't respond appropriately.
+ """
+ pass
diff --git a/src/lib/tlslite/integration/AsyncStateMachine.py b/src/lib/tlslite/integration/AsyncStateMachine.py
new file mode 100755
index 000000000..abed60432
--- /dev/null
+++ b/src/lib/tlslite/integration/AsyncStateMachine.py
@@ -0,0 +1,235 @@
+"""
+A state machine for using TLS Lite with asynchronous I/O.
+"""
+
+class AsyncStateMachine:
+ """
+ This is an abstract class that's used to integrate TLS Lite with
+ asyncore and Twisted.
+
+ This class signals wantsReadsEvent() and wantsWriteEvent(). When
+ the underlying socket has become readable or writeable, the event
+ should be passed to this class by calling inReadEvent() or
+ inWriteEvent(). This class will then try to read or write through
+ the socket, and will update its state appropriately.
+
+ This class will forward higher-level events to its subclass. For
+ example, when a complete TLS record has been received,
+ outReadEvent() will be called with the decrypted data.
+ """
+
+ def __init__(self):
+ self._clear()
+
+ def _clear(self):
+ #These store the various asynchronous operations (i.e.
+ #generators). Only one of them, at most, is ever active at a
+ #time.
+ self.handshaker = None
+ self.closer = None
+ self.reader = None
+ self.writer = None
+
+ #This stores the result from the last call to the
+ #currently active operation. If 0 it indicates that the
+ #operation wants to read, if 1 it indicates that the
+ #operation wants to write. If None, there is no active
+ #operation.
+ self.result = None
+
+ def _checkAssert(self, maxActive=1):
+ #This checks that only one operation, at most, is
+ #active, and that self.result is set appropriately.
+ activeOps = 0
+ if self.handshaker:
+ activeOps += 1
+ if self.closer:
+ activeOps += 1
+ if self.reader:
+ activeOps += 1
+ if self.writer:
+ activeOps += 1
+
+ if self.result == None:
+ if activeOps != 0:
+ raise AssertionError()
+ elif self.result in (0,1):
+ if activeOps != 1:
+ raise AssertionError()
+ else:
+ raise AssertionError()
+ if activeOps > maxActive:
+ raise AssertionError()
+
+ def wantsReadEvent(self):
+ """If the state machine wants to read.
+
+ If an operation is active, this returns whether or not the
+ operation wants to read from the socket. If an operation is
+ not active, this returns None.
+
+ @rtype: bool or None
+ @return: If the state machine wants to read.
+ """
+ if self.result != None:
+ return self.result == 0
+ return None
+
+ def wantsWriteEvent(self):
+ """If the state machine wants to write.
+
+ If an operation is active, this returns whether or not the
+ operation wants to write to the socket. If an operation is
+ not active, this returns None.
+
+ @rtype: bool or None
+ @return: If the state machine wants to write.
+ """
+ if self.result != None:
+ return self.result == 1
+ return None
+
+ def outConnectEvent(self):
+ """Called when a handshake operation completes.
+
+ May be overridden in subclass.
+ """
+ pass
+
+ def outCloseEvent(self):
+ """Called when a close operation completes.
+
+ May be overridden in subclass.
+ """
+ pass
+
+ def outReadEvent(self, readBuffer):
+ """Called when a read operation completes.
+
+ May be overridden in subclass."""
+ pass
+
+ def outWriteEvent(self):
+ """Called when a write operation completes.
+
+ May be overridden in subclass."""
+ pass
+
+ def inReadEvent(self):
+ """Tell the state machine it can read from the socket."""
+ try:
+ self._checkAssert()
+ if self.handshaker:
+ self._doHandshakeOp()
+ elif self.closer:
+ self._doCloseOp()
+ elif self.reader:
+ self._doReadOp()
+ elif self.writer:
+ self._doWriteOp()
+ else:
+ self.reader = self.tlsConnection.readAsync(16384)
+ self._doReadOp()
+ except:
+ self._clear()
+ raise
+
+ def inWriteEvent(self):
+ """Tell the state machine it can write to the socket."""
+ try:
+ self._checkAssert()
+ if self.handshaker:
+ self._doHandshakeOp()
+ elif self.closer:
+ self._doCloseOp()
+ elif self.reader:
+ self._doReadOp()
+ elif self.writer:
+ self._doWriteOp()
+ else:
+ self.outWriteEvent()
+ except:
+ self._clear()
+ raise
+
+ def _doHandshakeOp(self):
+ try:
+ self.result = self.handshaker.next()
+ except StopIteration:
+ self.handshaker = None
+ self.result = None
+ self.outConnectEvent()
+
+ def _doCloseOp(self):
+ try:
+ self.result = self.closer.next()
+ except StopIteration:
+ self.closer = None
+ self.result = None
+ self.outCloseEvent()
+
+ def _doReadOp(self):
+ self.result = self.reader.next()
+ if not self.result in (0,1):
+ readBuffer = self.result
+ self.reader = None
+ self.result = None
+ self.outReadEvent(readBuffer)
+
+ def _doWriteOp(self):
+ try:
+ self.result = self.writer.next()
+ except StopIteration:
+ self.writer = None
+ self.result = None
+
+ def setHandshakeOp(self, handshaker):
+ """Start a handshake operation.
+
+ @type handshaker: generator
+ @param handshaker: A generator created by using one of the
+ asynchronous handshake functions (i.e. handshakeServerAsync, or
+ handshakeClientxxx(..., async=True).
+ """
+ try:
+ self._checkAssert(0)
+ self.handshaker = handshaker
+ self._doHandshakeOp()
+ except:
+ self._clear()
+ raise
+
+ def setServerHandshakeOp(self, **args):
+ """Start a handshake operation.
+
+ The arguments passed to this function will be forwarded to
+ L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}.
+ """
+ handshaker = self.tlsConnection.handshakeServerAsync(**args)
+ self.setHandshakeOp(handshaker)
+
+ def setCloseOp(self):
+ """Start a close operation.
+ """
+ try:
+ self._checkAssert(0)
+ self.closer = self.tlsConnection.closeAsync()
+ self._doCloseOp()
+ except:
+ self._clear()
+ raise
+
+ def setWriteOp(self, writeBuffer):
+ """Start a write operation.
+
+ @type writeBuffer: str
+ @param writeBuffer: The string to transmit.
+ """
+ try:
+ self._checkAssert(0)
+ self.writer = self.tlsConnection.writeAsync(writeBuffer)
+ self._doWriteOp()
+ except:
+ self._clear()
+ raise
+
diff --git a/src/lib/tlslite/integration/ClientHelper.py b/src/lib/tlslite/integration/ClientHelper.py
new file mode 100755
index 000000000..6de4ab7a0
--- /dev/null
+++ b/src/lib/tlslite/integration/ClientHelper.py
@@ -0,0 +1,163 @@
+"""
+A helper class for using TLS Lite with stdlib clients
+(httplib, xmlrpclib, imaplib, poplib).
+"""
+
+from Bcfg2.tlslite.Checker import Checker
+
+class ClientHelper:
+ """This is a helper class used to integrate TLS Lite with various
+ TLS clients (e.g. poplib, smtplib, httplib, etc.)"""
+
+ def __init__(self,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings = None):
+ """
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The constructor does not perform the TLS handshake itself, but
+ simply stores these arguments for later. The handshake is
+ performed only when this class needs to connect with the
+ server. Then you should be prepared to handle TLS-specific
+ exceptions. See the client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+
+ self.username = None
+ self.password = None
+ self.sharedKey = None
+ self.certChain = None
+ self.privateKey = None
+ self.checker = None
+
+ #SRP Authentication
+ if username and password and not \
+ (sharedKey or certChain or privateKey):
+ self.username = username
+ self.password = password
+
+ #Shared Key Authentication
+ elif username and sharedKey and not \
+ (password or certChain or privateKey):
+ self.username = username
+ self.sharedKey = sharedKey
+
+ #Certificate Chain Authentication
+ elif certChain and privateKey and not \
+ (username or password or sharedKey):
+ self.certChain = certChain
+ self.privateKey = privateKey
+
+ #No Authentication
+ elif not password and not username and not \
+ sharedKey and not certChain and not privateKey:
+ pass
+
+ else:
+ raise ValueError("Bad parameters")
+
+ #Authenticate the server based on its cryptoID or fingerprint
+ if sharedKey and (cryptoID or protocol or x509Fingerprint):
+ raise ValueError("Can't use shared keys with other forms of"\
+ "authentication")
+
+ self.checker = Checker(cryptoID, protocol, x509Fingerprint,
+ x509TrustList, x509CommonName)
+ self.settings = settings
+
+ self.tlsSession = None
+
+ def _handshake(self, tlsConnection):
+ if self.username and self.password:
+ tlsConnection.handshakeClientSRP(username=self.username,
+ password=self.password,
+ checker=self.checker,
+ settings=self.settings,
+ session=self.tlsSession)
+ elif self.username and self.sharedKey:
+ tlsConnection.handshakeClientSharedKey(username=self.username,
+ sharedKey=self.sharedKey,
+ settings=self.settings)
+ else:
+ tlsConnection.handshakeClientCert(certChain=self.certChain,
+ privateKey=self.privateKey,
+ checker=self.checker,
+ settings=self.settings,
+ session=self.tlsSession)
+ self.tlsSession = tlsConnection.session
diff --git a/src/lib/tlslite/integration/HTTPTLSConnection.py b/src/lib/tlslite/integration/HTTPTLSConnection.py
new file mode 100755
index 000000000..a20400893
--- /dev/null
+++ b/src/lib/tlslite/integration/HTTPTLSConnection.py
@@ -0,0 +1,169 @@
+"""TLS Lite + httplib."""
+
+import socket
+import httplib
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from Bcfg2.tlslite.integration.ClientHelper import ClientHelper
+
+
+class HTTPBaseTLSConnection(httplib.HTTPConnection):
+ """This abstract class provides a framework for adding TLS support
+ to httplib."""
+
+ default_port = 443
+
+ def __init__(self, host, port=None, strict=None):
+ if strict == None:
+ #Python 2.2 doesn't support strict
+ httplib.HTTPConnection.__init__(self, host, port)
+ else:
+ httplib.HTTPConnection.__init__(self, host, port, strict)
+
+ def connect(self):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ if hasattr(sock, 'settimeout'):
+ sock.settimeout(10)
+ sock.connect((self.host, self.port))
+
+ #Use a TLSConnection to emulate a socket
+ self.sock = TLSConnection(sock)
+
+ #When httplib closes this, close the socket
+ self.sock.closeSocket = True
+ self._handshake(self.sock)
+
+ def _handshake(self, tlsConnection):
+ """Called to perform some sort of handshake.
+
+ This method must be overridden in a subclass to do some type of
+ handshake. This method will be called after the socket has
+ been connected but before any data has been sent. If this
+ method does not raise an exception, the TLS connection will be
+ considered valid.
+
+ This method may (or may not) be called every time an HTTP
+ request is performed, depending on whether the underlying HTTP
+ connection is persistent.
+
+ @type tlsConnection: L{tlslite.TLSConnection.TLSConnection}
+ @param tlsConnection: The connection to perform the handshake
+ on.
+ """
+ raise NotImplementedError()
+
+
+class HTTPTLSConnection(HTTPBaseTLSConnection, ClientHelper):
+ """This class extends L{HTTPBaseTLSConnection} to support the
+ common types of handshaking."""
+
+ def __init__(self, host, port=None,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings = None):
+ """Create a new HTTPTLSConnection.
+
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The constructor does not perform the TLS handshake itself, but
+ simply stores these arguments for later. The handshake is
+ performed only when this class needs to connect with the
+ server. Thus you should be prepared to handle TLS-specific
+ exceptions when calling methods inherited from
+ L{httplib.HTTPConnection} such as request(), connect(), and
+ send(). See the client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type host: str
+ @param host: Server to connect to.
+
+ @type port: int
+ @param port: Port to connect to.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+
+ HTTPBaseTLSConnection.__init__(self, host, port)
+
+ ClientHelper.__init__(self,
+ username, password, sharedKey,
+ certChain, privateKey,
+ cryptoID, protocol,
+ x509Fingerprint,
+ x509TrustList, x509CommonName,
+ settings)
+
+ def _handshake(self, tlsConnection):
+ ClientHelper._handshake(self, tlsConnection)
diff --git a/src/lib/tlslite/integration/IMAP4_TLS.py b/src/lib/tlslite/integration/IMAP4_TLS.py
new file mode 100755
index 000000000..2b005dc7e
--- /dev/null
+++ b/src/lib/tlslite/integration/IMAP4_TLS.py
@@ -0,0 +1,132 @@
+"""TLS Lite + imaplib."""
+
+import socket
+from imaplib import IMAP4
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from Bcfg2.tlslite.integration.ClientHelper import ClientHelper
+
+# IMAP TLS PORT
+IMAP4_TLS_PORT = 993
+
+class IMAP4_TLS(IMAP4, ClientHelper):
+ """This class extends L{imaplib.IMAP4} with TLS support."""
+
+ def __init__(self, host = '', port = IMAP4_TLS_PORT,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings=None):
+ """Create a new IMAP4_TLS.
+
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The caller should be prepared to handle TLS-specific
+ exceptions. See the client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type host: str
+ @param host: Server to connect to.
+
+ @type port: int
+ @param port: Port to connect to.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+
+ ClientHelper.__init__(self,
+ username, password, sharedKey,
+ certChain, privateKey,
+ cryptoID, protocol,
+ x509Fingerprint,
+ x509TrustList, x509CommonName,
+ settings)
+
+ IMAP4.__init__(self, host, port)
+
+
+ def open(self, host = '', port = IMAP4_TLS_PORT):
+ """Setup connection to remote server on "host:port".
+
+ This connection will be used by the routines:
+ read, readline, send, shutdown.
+ """
+ self.host = host
+ self.port = port
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((host, port))
+ self.sock = TLSConnection(self.sock)
+ self.sock.closeSocket = True
+ ClientHelper._handshake(self, self.sock)
+ self.file = self.sock.makefile('rb')
diff --git a/src/lib/tlslite/integration/IntegrationHelper.py b/src/lib/tlslite/integration/IntegrationHelper.py
new file mode 100755
index 000000000..af5193b48
--- /dev/null
+++ b/src/lib/tlslite/integration/IntegrationHelper.py
@@ -0,0 +1,52 @@
+
+class IntegrationHelper:
+
+ def __init__(self,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings = None):
+
+ self.username = None
+ self.password = None
+ self.sharedKey = None
+ self.certChain = None
+ self.privateKey = None
+ self.checker = None
+
+ #SRP Authentication
+ if username and password and not \
+ (sharedKey or certChain or privateKey):
+ self.username = username
+ self.password = password
+
+ #Shared Key Authentication
+ elif username and sharedKey and not \
+ (password or certChain or privateKey):
+ self.username = username
+ self.sharedKey = sharedKey
+
+ #Certificate Chain Authentication
+ elif certChain and privateKey and not \
+ (username or password or sharedKey):
+ self.certChain = certChain
+ self.privateKey = privateKey
+
+ #No Authentication
+ elif not password and not username and not \
+ sharedKey and not certChain and not privateKey:
+ pass
+
+ else:
+ raise ValueError("Bad parameters")
+
+ #Authenticate the server based on its cryptoID or fingerprint
+ if sharedKey and (cryptoID or protocol or x509Fingerprint):
+ raise ValueError("Can't use shared keys with other forms of"\
+ "authentication")
+
+ self.checker = Checker(cryptoID, protocol, x509Fingerprint,
+ x509TrustList, x509CommonName)
+ self.settings = settings \ No newline at end of file
diff --git a/src/lib/tlslite/integration/POP3_TLS.py b/src/lib/tlslite/integration/POP3_TLS.py
new file mode 100755
index 000000000..8245a13ca
--- /dev/null
+++ b/src/lib/tlslite/integration/POP3_TLS.py
@@ -0,0 +1,142 @@
+"""TLS Lite + poplib."""
+
+import socket
+from poplib import POP3
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from Bcfg2.tlslite.integration.ClientHelper import ClientHelper
+
+# POP TLS PORT
+POP3_TLS_PORT = 995
+
+class POP3_TLS(POP3, ClientHelper):
+ """This class extends L{poplib.POP3} with TLS support."""
+
+ def __init__(self, host, port = POP3_TLS_PORT,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings=None):
+ """Create a new POP3_TLS.
+
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The caller should be prepared to handle TLS-specific
+ exceptions. See the client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type host: str
+ @param host: Server to connect to.
+
+ @type port: int
+ @param port: Port to connect to.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+
+ self.host = host
+ self.port = port
+ msg = "getaddrinfo returns an empty list"
+ self.sock = None
+ for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ try:
+ self.sock = socket.socket(af, socktype, proto)
+ self.sock.connect(sa)
+ except socket.error, msg:
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+ continue
+ break
+ if not self.sock:
+ raise socket.error, msg
+
+ ### New code below (all else copied from poplib)
+ ClientHelper.__init__(self,
+ username, password, sharedKey,
+ certChain, privateKey,
+ cryptoID, protocol,
+ x509Fingerprint,
+ x509TrustList, x509CommonName,
+ settings)
+
+ self.sock = TLSConnection(self.sock)
+ self.sock.closeSocket = True
+ ClientHelper._handshake(self, self.sock)
+ ###
+
+ self.file = self.sock.makefile('rb')
+ self._debugging = 0
+ self.welcome = self._getresp()
diff --git a/src/lib/tlslite/integration/SMTP_TLS.py b/src/lib/tlslite/integration/SMTP_TLS.py
new file mode 100755
index 000000000..203cc0b7f
--- /dev/null
+++ b/src/lib/tlslite/integration/SMTP_TLS.py
@@ -0,0 +1,114 @@
+"""TLS Lite + smtplib."""
+
+from smtplib import SMTP
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from Bcfg2.tlslite.integration.ClientHelper import ClientHelper
+
+class SMTP_TLS(SMTP):
+ """This class extends L{smtplib.SMTP} with TLS support."""
+
+ def starttls(self,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings=None):
+ """Puts the connection to the SMTP server into TLS mode.
+
+ If the server supports TLS, this will encrypt the rest of the SMTP
+ session.
+
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The caller should be prepared to handle TLS-specific
+ exceptions. See the client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+ (resp, reply) = self.docmd("STARTTLS")
+ if resp == 220:
+ helper = ClientHelper(
+ username, password, sharedKey,
+ certChain, privateKey,
+ cryptoID, protocol,
+ x509Fingerprint,
+ x509TrustList, x509CommonName,
+ settings)
+ conn = TLSConnection(self.sock)
+ conn.closeSocket = True
+ helper._handshake(conn)
+ self.sock = conn
+ self.file = conn.makefile('rb')
+ return (resp, reply)
diff --git a/src/lib/tlslite/integration/TLSAsyncDispatcherMixIn.py b/src/lib/tlslite/integration/TLSAsyncDispatcherMixIn.py
new file mode 100755
index 000000000..9201e5cba
--- /dev/null
+++ b/src/lib/tlslite/integration/TLSAsyncDispatcherMixIn.py
@@ -0,0 +1,139 @@
+"""TLS Lite + asyncore."""
+
+
+import asyncore
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from AsyncStateMachine import AsyncStateMachine
+
+
+class TLSAsyncDispatcherMixIn(AsyncStateMachine):
+ """This class can be "mixed in" with an
+ L{asyncore.dispatcher} to add TLS support.
+
+ This class essentially sits between the dispatcher and the select
+ loop, intercepting events and only calling the dispatcher when
+ applicable.
+
+ In the case of handle_read(), a read operation will be activated,
+ and when it completes, the bytes will be placed in a buffer where
+ the dispatcher can retrieve them by calling recv(), and the
+ dispatcher's handle_read() will be called.
+
+ In the case of handle_write(), the dispatcher's handle_write() will
+ be called, and when it calls send(), a write operation will be
+ activated.
+
+ To use this class, you must combine it with an asyncore.dispatcher,
+ and pass in a handshake operation with setServerHandshakeOp().
+
+ Below is an example of using this class with medusa. This class is
+ mixed in with http_channel to create http_tls_channel. Note:
+ 1. the mix-in is listed first in the inheritance list
+
+ 2. the input buffer size must be at least 16K, otherwise the
+ dispatcher might not read all the bytes from the TLS layer,
+ leaving some bytes in limbo.
+
+ 3. IE seems to have a problem receiving a whole HTTP response in a
+ single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't
+ be displayed on IE.
+
+ Add the following text into 'start_medusa.py', in the 'HTTP Server'
+ section::
+
+ from tlslite.api import *
+ s = open("./serverX509Cert.pem").read()
+ x509 = X509()
+ x509.parse(s)
+ certChain = X509CertChain([x509])
+
+ s = open("./serverX509Key.pem").read()
+ privateKey = parsePEMKey(s, private=True)
+
+ class http_tls_channel(TLSAsyncDispatcherMixIn,
+ http_server.http_channel):
+ ac_in_buffer_size = 16384
+
+ def __init__ (self, server, conn, addr):
+ http_server.http_channel.__init__(self, server, conn, addr)
+ TLSAsyncDispatcherMixIn.__init__(self, conn)
+ self.tlsConnection.ignoreAbruptClose = True
+ self.setServerHandshakeOp(certChain=certChain,
+ privateKey=privateKey)
+
+ hs.channel_class = http_tls_channel
+
+ If the TLS layer raises an exception, the exception will be caught
+ in asyncore.dispatcher, which will call close() on this class. The
+ TLS layer always closes the TLS connection before raising an
+ exception, so the close operation will complete right away, causing
+ asyncore.dispatcher.close() to be called, which closes the socket
+ and removes this instance from the asyncore loop.
+
+ """
+
+
+ def __init__(self, sock=None):
+ AsyncStateMachine.__init__(self)
+
+ if sock:
+ self.tlsConnection = TLSConnection(sock)
+
+ #Calculate the sibling I'm being mixed in with.
+ #This is necessary since we override functions
+ #like readable(), handle_read(), etc., but we
+ #also want to call the sibling's versions.
+ for cl in self.__class__.__bases__:
+ if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine:
+ self.siblingClass = cl
+ break
+ else:
+ raise AssertionError()
+
+ def readable(self):
+ result = self.wantsReadEvent()
+ if result != None:
+ return result
+ return self.siblingClass.readable(self)
+
+ def writable(self):
+ result = self.wantsWriteEvent()
+ if result != None:
+ return result
+ return self.siblingClass.writable(self)
+
+ def handle_read(self):
+ self.inReadEvent()
+
+ def handle_write(self):
+ self.inWriteEvent()
+
+ def outConnectEvent(self):
+ self.siblingClass.handle_connect(self)
+
+ def outCloseEvent(self):
+ asyncore.dispatcher.close(self)
+
+ def outReadEvent(self, readBuffer):
+ self.readBuffer = readBuffer
+ self.siblingClass.handle_read(self)
+
+ def outWriteEvent(self):
+ self.siblingClass.handle_write(self)
+
+ def recv(self, bufferSize=16384):
+ if bufferSize < 16384 or self.readBuffer == None:
+ raise AssertionError()
+ returnValue = self.readBuffer
+ self.readBuffer = None
+ return returnValue
+
+ def send(self, writeBuffer):
+ self.setWriteOp(writeBuffer)
+ return len(writeBuffer)
+
+ def close(self):
+ if hasattr(self, "tlsConnection"):
+ self.setCloseOp()
+ else:
+ asyncore.dispatcher.close(self)
diff --git a/src/lib/tlslite/integration/TLSSocketServerMixIn.py b/src/lib/tlslite/integration/TLSSocketServerMixIn.py
new file mode 100755
index 000000000..fe9f303b4
--- /dev/null
+++ b/src/lib/tlslite/integration/TLSSocketServerMixIn.py
@@ -0,0 +1,59 @@
+"""TLS Lite + SocketServer."""
+
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+
+class TLSSocketServerMixIn:
+ """
+ This class can be mixed in with any L{SocketServer.TCPServer} to
+ add TLS support.
+
+ To use this class, define a new class that inherits from it and
+ some L{SocketServer.TCPServer} (with the mix-in first). Then
+ implement the handshake() method, doing some sort of server
+ handshake on the connection argument. If the handshake method
+ returns True, the RequestHandler will be triggered. Below is a
+ complete example of a threaded HTTPS server::
+
+ from SocketServer import *
+ from BaseHTTPServer import *
+ from SimpleHTTPServer import *
+ from tlslite.api import *
+
+ s = open("./serverX509Cert.pem").read()
+ x509 = X509()
+ x509.parse(s)
+ certChain = X509CertChain([x509])
+
+ s = open("./serverX509Key.pem").read()
+ privateKey = parsePEMKey(s, private=True)
+
+ sessionCache = SessionCache()
+
+ class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn,
+ HTTPServer):
+ def handshake(self, tlsConnection):
+ try:
+ tlsConnection.handshakeServer(certChain=certChain,
+ privateKey=privateKey,
+ sessionCache=sessionCache)
+ tlsConnection.ignoreAbruptClose = True
+ return True
+ except TLSError, error:
+ print "Handshake failure:", str(error)
+ return False
+
+ httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler)
+ httpd.serve_forever()
+ """
+
+
+ def finish_request(self, sock, client_address):
+ tlsConnection = TLSConnection(sock)
+ if self.handshake(tlsConnection) == True:
+ self.RequestHandlerClass(tlsConnection, client_address, self)
+ tlsConnection.close()
+
+ #Implement this method to do some form of handshaking. Return True
+ #if the handshake finishes properly and the request is authorized.
+ def handshake(self, tlsConnection):
+ raise NotImplementedError()
diff --git a/src/lib/tlslite/integration/TLSTwistedProtocolWrapper.py b/src/lib/tlslite/integration/TLSTwistedProtocolWrapper.py
new file mode 100755
index 000000000..b9d2f8529
--- /dev/null
+++ b/src/lib/tlslite/integration/TLSTwistedProtocolWrapper.py
@@ -0,0 +1,196 @@
+"""TLS Lite + Twisted."""
+
+from twisted.protocols.policies import ProtocolWrapper, WrappingFactory
+from twisted.python.failure import Failure
+
+from AsyncStateMachine import AsyncStateMachine
+from Bcfg2.tlslite.TLSConnection import TLSConnection
+from Bcfg2.tlslite.errors import *
+
+import socket
+import errno
+
+
+#The TLSConnection is created around a "fake socket" that
+#plugs it into the underlying Twisted transport
+class _FakeSocket:
+ def __init__(self, wrapper):
+ self.wrapper = wrapper
+ self.data = ""
+
+ def send(self, data):
+ ProtocolWrapper.write(self.wrapper, data)
+ return len(data)
+
+ def recv(self, numBytes):
+ if self.data == "":
+ raise socket.error, (errno.EWOULDBLOCK, "")
+ returnData = self.data[:numBytes]
+ self.data = self.data[numBytes:]
+ return returnData
+
+class TLSTwistedProtocolWrapper(ProtocolWrapper, AsyncStateMachine):
+ """This class can wrap Twisted protocols to add TLS support.
+
+ Below is a complete example of using TLS Lite with a Twisted echo
+ server.
+
+ There are two server implementations below. Echo is the original
+ protocol, which is oblivious to TLS. Echo1 subclasses Echo and
+ negotiates TLS when the client connects. Echo2 subclasses Echo and
+ negotiates TLS when the client sends "STARTTLS"::
+
+ from twisted.internet.protocol import Protocol, Factory
+ from twisted.internet import reactor
+ from twisted.protocols.policies import WrappingFactory
+ from twisted.protocols.basic import LineReceiver
+ from twisted.python import log
+ from twisted.python.failure import Failure
+ import sys
+ from tlslite.api import *
+
+ s = open("./serverX509Cert.pem").read()
+ x509 = X509()
+ x509.parse(s)
+ certChain = X509CertChain([x509])
+
+ s = open("./serverX509Key.pem").read()
+ privateKey = parsePEMKey(s, private=True)
+
+ verifierDB = VerifierDB("verifierDB")
+ verifierDB.open()
+
+ class Echo(LineReceiver):
+ def connectionMade(self):
+ self.transport.write("Welcome to the echo server!\\r\\n")
+
+ def lineReceived(self, line):
+ self.transport.write(line + "\\r\\n")
+
+ class Echo1(Echo):
+ def connectionMade(self):
+ if not self.transport.tlsStarted:
+ self.transport.setServerHandshakeOp(certChain=certChain,
+ privateKey=privateKey,
+ verifierDB=verifierDB)
+ else:
+ Echo.connectionMade(self)
+
+ def connectionLost(self, reason):
+ pass #Handle any TLS exceptions here
+
+ class Echo2(Echo):
+ def lineReceived(self, data):
+ if data == "STARTTLS":
+ self.transport.setServerHandshakeOp(certChain=certChain,
+ privateKey=privateKey,
+ verifierDB=verifierDB)
+ else:
+ Echo.lineReceived(self, data)
+
+ def connectionLost(self, reason):
+ pass #Handle any TLS exceptions here
+
+ factory = Factory()
+ factory.protocol = Echo1
+ #factory.protocol = Echo2
+
+ wrappingFactory = WrappingFactory(factory)
+ wrappingFactory.protocol = TLSTwistedProtocolWrapper
+
+ log.startLogging(sys.stdout)
+ reactor.listenTCP(1079, wrappingFactory)
+ reactor.run()
+
+ This class works as follows:
+
+ Data comes in and is given to the AsyncStateMachine for handling.
+ AsyncStateMachine will forward events to this class, and we'll
+ pass them on to the ProtocolHandler, which will proxy them to the
+ wrapped protocol. The wrapped protocol may then call back into
+ this class, and these calls will be proxied into the
+ AsyncStateMachine.
+
+ The call graph looks like this:
+ - self.dataReceived
+ - AsyncStateMachine.inReadEvent
+ - self.out(Connect|Close|Read)Event
+ - ProtocolWrapper.(connectionMade|loseConnection|dataReceived)
+ - self.(loseConnection|write|writeSequence)
+ - AsyncStateMachine.(setCloseOp|setWriteOp)
+ """
+
+ #WARNING: IF YOU COPY-AND-PASTE THE ABOVE CODE, BE SURE TO REMOVE
+ #THE EXTRA ESCAPING AROUND "\\r\\n"
+
+ def __init__(self, factory, wrappedProtocol):
+ ProtocolWrapper.__init__(self, factory, wrappedProtocol)
+ AsyncStateMachine.__init__(self)
+ self.fakeSocket = _FakeSocket(self)
+ self.tlsConnection = TLSConnection(self.fakeSocket)
+ self.tlsStarted = False
+ self.connectionLostCalled = False
+
+ def connectionMade(self):
+ try:
+ ProtocolWrapper.connectionMade(self)
+ except TLSError, e:
+ self.connectionLost(Failure(e))
+ ProtocolWrapper.loseConnection(self)
+
+ def dataReceived(self, data):
+ try:
+ if not self.tlsStarted:
+ ProtocolWrapper.dataReceived(self, data)
+ else:
+ self.fakeSocket.data += data
+ while self.fakeSocket.data:
+ AsyncStateMachine.inReadEvent(self)
+ except TLSError, e:
+ self.connectionLost(Failure(e))
+ ProtocolWrapper.loseConnection(self)
+
+ def connectionLost(self, reason):
+ if not self.connectionLostCalled:
+ ProtocolWrapper.connectionLost(self, reason)
+ self.connectionLostCalled = True
+
+
+ def outConnectEvent(self):
+ ProtocolWrapper.connectionMade(self)
+
+ def outCloseEvent(self):
+ ProtocolWrapper.loseConnection(self)
+
+ def outReadEvent(self, data):
+ if data == "":
+ ProtocolWrapper.loseConnection(self)
+ else:
+ ProtocolWrapper.dataReceived(self, data)
+
+
+ def setServerHandshakeOp(self, **args):
+ self.tlsStarted = True
+ AsyncStateMachine.setServerHandshakeOp(self, **args)
+
+ def loseConnection(self):
+ if not self.tlsStarted:
+ ProtocolWrapper.loseConnection(self)
+ else:
+ AsyncStateMachine.setCloseOp(self)
+
+ def write(self, data):
+ if not self.tlsStarted:
+ ProtocolWrapper.write(self, data)
+ else:
+ #Because of the FakeSocket, write operations are guaranteed to
+ #terminate immediately.
+ AsyncStateMachine.setWriteOp(self, data)
+
+ def writeSequence(self, seq):
+ if not self.tlsStarted:
+ ProtocolWrapper.writeSequence(self, seq)
+ else:
+ #Because of the FakeSocket, write operations are guaranteed to
+ #terminate immediately.
+ AsyncStateMachine.setWriteOp(self, "".join(seq))
diff --git a/src/lib/tlslite/integration/XMLRPCTransport.py b/src/lib/tlslite/integration/XMLRPCTransport.py
new file mode 100755
index 000000000..61d03428b
--- /dev/null
+++ b/src/lib/tlslite/integration/XMLRPCTransport.py
@@ -0,0 +1,137 @@
+"""TLS Lite + xmlrpclib."""
+
+import xmlrpclib
+import httplib
+from Bcfg2.tlslite.integration.HTTPTLSConnection import HTTPTLSConnection
+from Bcfg2.tlslite.integration.ClientHelper import ClientHelper
+
+
+class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
+ """Handles an HTTPS transaction to an XML-RPC server."""
+
+ def __init__(self,
+ username=None, password=None, sharedKey=None,
+ certChain=None, privateKey=None,
+ cryptoID=None, protocol=None,
+ x509Fingerprint=None,
+ x509TrustList=None, x509CommonName=None,
+ settings=None):
+ """Create a new XMLRPCTransport.
+
+ An instance of this class can be passed to L{xmlrpclib.ServerProxy}
+ to use TLS with XML-RPC calls::
+
+ from tlslite.api import XMLRPCTransport
+ from xmlrpclib import ServerProxy
+
+ transport = XMLRPCTransport(user="alice", password="abra123")
+ server = ServerProxy("https://localhost", transport)
+
+ For client authentication, use one of these argument
+ combinations:
+ - username, password (SRP)
+ - username, sharedKey (shared-key)
+ - certChain, privateKey (certificate)
+
+ For server authentication, you can either rely on the
+ implicit mutual authentication performed by SRP or
+ shared-keys, or you can do certificate-based server
+ authentication with one of these argument combinations:
+ - cryptoID[, protocol] (requires cryptoIDlib)
+ - x509Fingerprint
+ - x509TrustList[, x509CommonName] (requires cryptlib_py)
+
+ Certificate-based server authentication is compatible with
+ SRP or certificate-based client authentication. It is
+ not compatible with shared-keys.
+
+ The constructor does not perform the TLS handshake itself, but
+ simply stores these arguments for later. The handshake is
+ performed only when this class needs to connect with the
+ server. Thus you should be prepared to handle TLS-specific
+ exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the
+ client handshake functions in
+ L{tlslite.TLSConnection.TLSConnection} for details on which
+ exceptions might be raised.
+
+ @type username: str
+ @param username: SRP or shared-key username. Requires the
+ 'password' or 'sharedKey' argument.
+
+ @type password: str
+ @param password: SRP password for mutual authentication.
+ Requires the 'username' argument.
+
+ @type sharedKey: str
+ @param sharedKey: Shared key for mutual authentication.
+ Requires the 'username' argument.
+
+ @type certChain: L{tlslite.X509CertChain.X509CertChain} or
+ L{cryptoIDlib.CertChain.CertChain}
+ @param certChain: Certificate chain for client authentication.
+ Requires the 'privateKey' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type privateKey: L{tlslite.utils.RSAKey.RSAKey}
+ @param privateKey: Private key for client authentication.
+ Requires the 'certChain' argument. Excludes the SRP or
+ shared-key related arguments.
+
+ @type cryptoID: str
+ @param cryptoID: cryptoID for server authentication. Mutually
+ exclusive with the 'x509...' arguments.
+
+ @type protocol: str
+ @param protocol: cryptoID protocol URI for server
+ authentication. Requires the 'cryptoID' argument.
+
+ @type x509Fingerprint: str
+ @param x509Fingerprint: Hex-encoded X.509 fingerprint for
+ server authentication. Mutually exclusive with the 'cryptoID'
+ and 'x509TrustList' arguments.
+
+ @type x509TrustList: list of L{tlslite.X509.X509}
+ @param x509TrustList: A list of trusted root certificates. The
+ other party must present a certificate chain which extends to
+ one of these root certificates. The cryptlib_py module must be
+ installed to use this parameter. Mutually exclusive with the
+ 'cryptoID' and 'x509Fingerprint' arguments.
+
+ @type x509CommonName: str
+ @param x509CommonName: The end-entity certificate's 'CN' field
+ must match this value. For a web server, this is typically a
+ server name such as 'www.amazon.com'. Mutually exclusive with
+ the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
+ 'x509TrustList' argument.
+
+ @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.
+ """
+
+ ClientHelper.__init__(self,
+ username, password, sharedKey,
+ certChain, privateKey,
+ cryptoID, protocol,
+ x509Fingerprint,
+ x509TrustList, x509CommonName,
+ settings)
+ self._use_datetime = 0
+
+ def make_connection(self, host):
+ # create a HTTPS connection object from a host descriptor
+ host, extra_headers, x509 = self.get_host_info(host)
+ http = HTTPTLSConnection(host, None,
+ self.username, self.password,
+ self.sharedKey,
+ self.certChain, self.privateKey,
+ self.checker.cryptoID,
+ self.checker.protocol,
+ self.checker.x509Fingerprint,
+ self.checker.x509TrustList,
+ self.checker.x509CommonName,
+ self.settings)
+ http2 = httplib.HTTP()
+ http2._setup(http)
+ return http2
diff --git a/src/lib/tlslite/integration/__init__.py b/src/lib/tlslite/integration/__init__.py
new file mode 100755
index 000000000..960f4065f
--- /dev/null
+++ b/src/lib/tlslite/integration/__init__.py
@@ -0,0 +1,17 @@
+"""Classes for integrating TLS Lite with other packages."""
+
+__all__ = ["AsyncStateMachine",
+ "HTTPTLSConnection",
+ "POP3_TLS",
+ "IMAP4_TLS",
+ "SMTP_TLS",
+ "XMLRPCTransport",
+ "TLSSocketServerMixIn",
+ "TLSAsyncDispatcherMixIn",
+ "TLSTwistedProtocolWrapper"]
+
+try:
+ import twisted
+ del twisted
+except ImportError:
+ del __all__[__all__.index("TLSTwistedProtocolWrapper")]
diff --git a/src/lib/tlslite/mathtls.py b/src/lib/tlslite/mathtls.py
new file mode 100755
index 000000000..3b8ede601
--- /dev/null
+++ b/src/lib/tlslite/mathtls.py
@@ -0,0 +1,170 @@
+"""Miscellaneous helper functions."""
+
+from utils.compat import *
+from utils.cryptomath import *
+
+import hmac
+import md5
+import sha
+
+#1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups]
+goodGroupParameters = [(2,0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3),\
+ (2,0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB),\
+ (2,0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73),\
+ (2,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF),\
+ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF),\
+ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF),\
+ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF)]
+
+def P_hash(hashModule, secret, seed, length):
+ bytes = createByteArrayZeros(length)
+ secret = bytesToString(secret)
+ seed = bytesToString(seed)
+ A = seed
+ index = 0
+ while 1:
+ A = hmac.HMAC(secret, A, hashModule).digest()
+ output = hmac.HMAC(secret, A+seed, hashModule).digest()
+ for c in output:
+ if index >= length:
+ return bytes
+ bytes[index] = ord(c)
+ index += 1
+ return bytes
+
+def PRF(secret, label, seed, length):
+ #Split the secret into left and right halves
+ S1 = secret[ : int(math.ceil(len(secret)/2.0))]
+ S2 = secret[ int(math.floor(len(secret)/2.0)) : ]
+
+ #Run the left half through P_MD5 and the right half through P_SHA1
+ p_md5 = P_hash(md5, S1, concatArrays(stringToBytes(label), seed), length)
+ p_sha1 = P_hash(sha, S2, concatArrays(stringToBytes(label), seed), length)
+
+ #XOR the output values and return the result
+ for x in range(length):
+ p_md5[x] ^= p_sha1[x]
+ return p_md5
+
+
+def PRF_SSL(secret, seed, length):
+ secretStr = bytesToString(secret)
+ seedStr = bytesToString(seed)
+ bytes = createByteArrayZeros(length)
+ index = 0
+ for x in range(26):
+ A = chr(ord('A')+x) * (x+1) # 'A', 'BB', 'CCC', etc..
+ input = secretStr + sha.sha(A + secretStr + seedStr).digest()
+ output = md5.md5(input).digest()
+ for c in output:
+ if index >= length:
+ return bytes
+ bytes[index] = ord(c)
+ index += 1
+ return bytes
+
+def makeX(salt, username, password):
+ if len(username)>=256:
+ raise ValueError("username too long")
+ if len(salt)>=256:
+ raise ValueError("salt too long")
+ return stringToNumber(sha.sha(salt + sha.sha(username + ":" + password)\
+ .digest()).digest())
+
+#This function is used by VerifierDB.makeVerifier
+def makeVerifier(username, password, bits):
+ bitsIndex = {1024:0, 1536:1, 2048:2, 3072:3, 4096:4, 6144:5, 8192:6}[bits]
+ g,N = goodGroupParameters[bitsIndex]
+ salt = bytesToString(getRandomBytes(16))
+ x = makeX(salt, username, password)
+ verifier = powMod(g, x, N)
+ return N, g, salt, verifier
+
+def PAD(n, x):
+ nLength = len(numberToString(n))
+ s = numberToString(x)
+ if len(s) < nLength:
+ s = ("\0" * (nLength-len(s))) + s
+ return s
+
+def makeU(N, A, B):
+ return stringToNumber(sha.sha(PAD(N, A) + PAD(N, B)).digest())
+
+def makeK(N, g):
+ return stringToNumber(sha.sha(numberToString(N) + PAD(N, g)).digest())
+
+
+"""
+MAC_SSL
+Modified from Python HMAC by Trevor
+"""
+
+class MAC_SSL:
+ """MAC_SSL class.
+
+ This supports the API for Cryptographic Hash Functions (PEP 247).
+ """
+
+ def __init__(self, key, msg = None, digestmod = None):
+ """Create a new MAC_SSL object.
+
+ key: key for the keyed hash object.
+ msg: Initial input for the hash, if provided.
+ digestmod: A module supporting PEP 247. Defaults to the md5 module.
+ """
+ if digestmod is None:
+ import md5
+ digestmod = md5
+
+ if key == None: #TREVNEW - for faster copying
+ return #TREVNEW
+
+ self.digestmod = digestmod
+ self.outer = digestmod.new()
+ self.inner = digestmod.new()
+ self.digest_size = digestmod.digest_size
+
+ ipad = "\x36" * 40
+ opad = "\x5C" * 40
+
+ self.inner.update(key)
+ self.inner.update(ipad)
+ self.outer.update(key)
+ self.outer.update(opad)
+ if msg is not None:
+ self.update(msg)
+
+
+ def update(self, msg):
+ """Update this hashing object with the string msg.
+ """
+ self.inner.update(msg)
+
+ def copy(self):
+ """Return a separate copy of this hashing object.
+
+ An update to this copy won't affect the original object.
+ """
+ other = MAC_SSL(None) #TREVNEW - for faster copying
+ other.digest_size = self.digest_size #TREVNEW
+ other.digestmod = self.digestmod
+ other.inner = self.inner.copy()
+ other.outer = self.outer.copy()
+ return other
+
+ def digest(self):
+ """Return the hash value of this hashing object.
+
+ This returns a string containing 8-bit data. The object is
+ not altered in any way by this function; you can continue
+ updating the object after calling this function.
+ """
+ h = self.outer.copy()
+ h.update(self.inner.digest())
+ return h.digest()
+
+ def hexdigest(self):
+ """Like digest(), but returns a string of hexadecimal digits instead.
+ """
+ return "".join([hex(ord(x))[2:].zfill(2)
+ for x in tuple(self.digest())])
diff --git a/src/lib/tlslite/messages.py b/src/lib/tlslite/messages.py
new file mode 100755
index 000000000..afccc793a
--- /dev/null
+++ b/src/lib/tlslite/messages.py
@@ -0,0 +1,561 @@
+"""Classes representing TLS messages."""
+
+from utils.compat import *
+from utils.cryptomath import *
+from errors import *
+from utils.codec import *
+from constants import *
+from X509 import X509
+from X509CertChain import X509CertChain
+
+import sha
+import md5
+
+class RecordHeader3:
+ def __init__(self):
+ self.type = 0
+ self.version = (0,0)
+ self.length = 0
+ self.ssl2 = False
+
+ def create(self, version, type, length):
+ self.type = type
+ self.version = version
+ self.length = length
+ return self
+
+ def write(self):
+ w = Writer(5)
+ w.add(self.type, 1)
+ w.add(self.version[0], 1)
+ w.add(self.version[1], 1)
+ w.add(self.length, 2)
+ return w.bytes
+
+ def parse(self, p):
+ self.type = p.get(1)
+ self.version = (p.get(1), p.get(1))
+ self.length = p.get(2)
+ self.ssl2 = False
+ return self
+
+class RecordHeader2:
+ def __init__(self):
+ self.type = 0
+ self.version = (0,0)
+ self.length = 0
+ self.ssl2 = True
+
+ def parse(self, p):
+ if p.get(1)!=128:
+ raise SyntaxError()
+ self.type = ContentType.handshake
+ self.version = (2,0)
+ #We don't support 2-byte-length-headers; could be a problem
+ self.length = p.get(1)
+ return self
+
+
+class Msg:
+ def preWrite(self, trial):
+ if trial:
+ w = Writer()
+ else:
+ length = self.write(True)
+ w = Writer(length)
+ return w
+
+ def postWrite(self, w, trial):
+ if trial:
+ return w.index
+ else:
+ return w.bytes
+
+class Alert(Msg):
+ def __init__(self):
+ self.contentType = ContentType.alert
+ self.level = 0
+ self.description = 0
+
+ def create(self, description, level=AlertLevel.fatal):
+ self.level = level
+ self.description = description
+ return self
+
+ def parse(self, p):
+ p.setLengthCheck(2)
+ self.level = p.get(1)
+ self.description = p.get(1)
+ p.stopLengthCheck()
+ return self
+
+ def write(self):
+ w = Writer(2)
+ w.add(self.level, 1)
+ w.add(self.description, 1)
+ return w.bytes
+
+
+class HandshakeMsg(Msg):
+ def preWrite(self, handshakeType, trial):
+ if trial:
+ w = Writer()
+ w.add(handshakeType, 1)
+ w.add(0, 3)
+ else:
+ length = self.write(True)
+ w = Writer(length)
+ w.add(handshakeType, 1)
+ w.add(length-4, 3)
+ return w
+
+
+class ClientHello(HandshakeMsg):
+ def __init__(self, ssl2=False):
+ self.contentType = ContentType.handshake
+ self.ssl2 = ssl2
+ self.client_version = (0,0)
+ self.random = createByteArrayZeros(32)
+ self.session_id = createByteArraySequence([])
+ self.cipher_suites = [] # a list of 16-bit values
+ self.certificate_types = [CertificateType.x509]
+ self.compression_methods = [] # a list of 8-bit values
+ self.srp_username = None # a string
+
+ def create(self, version, random, session_id, cipher_suites,
+ certificate_types=None, srp_username=None):
+ self.client_version = version
+ self.random = random
+ self.session_id = session_id
+ self.cipher_suites = cipher_suites
+ self.certificate_types = certificate_types
+ self.compression_methods = [0]
+ self.srp_username = srp_username
+ return self
+
+ def parse(self, p):
+ if self.ssl2:
+ self.client_version = (p.get(1), p.get(1))
+ cipherSpecsLength = p.get(2)
+ sessionIDLength = p.get(2)
+ randomLength = p.get(2)
+ self.cipher_suites = p.getFixList(3, int(cipherSpecsLength/3))
+ self.session_id = p.getFixBytes(sessionIDLength)
+ self.random = p.getFixBytes(randomLength)
+ if len(self.random) < 32:
+ zeroBytes = 32-len(self.random)
+ self.random = createByteArrayZeros(zeroBytes) + self.random
+ self.compression_methods = [0]#Fake this value
+
+ #We're not doing a stopLengthCheck() for SSLv2, oh well..
+ else:
+ p.startLengthCheck(3)
+ self.client_version = (p.get(1), p.get(1))
+ self.random = p.getFixBytes(32)
+ self.session_id = p.getVarBytes(1)
+ self.cipher_suites = p.getVarList(2, 2)
+ self.compression_methods = p.getVarList(1, 1)
+ if not p.atLengthCheck():
+ totalExtLength = p.get(2)
+ soFar = 0
+ while soFar != totalExtLength:
+ extType = p.get(2)
+ extLength = p.get(2)
+ if extType == 6:
+ self.srp_username = bytesToString(p.getVarBytes(1))
+ elif extType == 7:
+ self.certificate_types = p.getVarList(1, 1)
+ else:
+ p.getFixBytes(extLength)
+ soFar += 4 + extLength
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.client_hello, trial)
+ w.add(self.client_version[0], 1)
+ w.add(self.client_version[1], 1)
+ w.addFixSeq(self.random, 1)
+ w.addVarSeq(self.session_id, 1, 1)
+ w.addVarSeq(self.cipher_suites, 2, 2)
+ w.addVarSeq(self.compression_methods, 1, 1)
+
+ extLength = 0
+ if self.certificate_types and self.certificate_types != \
+ [CertificateType.x509]:
+ extLength += 5 + len(self.certificate_types)
+ if self.srp_username:
+ extLength += 5 + len(self.srp_username)
+ if extLength > 0:
+ w.add(extLength, 2)
+
+ if self.certificate_types and self.certificate_types != \
+ [CertificateType.x509]:
+ w.add(7, 2)
+ w.add(len(self.certificate_types)+1, 2)
+ w.addVarSeq(self.certificate_types, 1, 1)
+ if self.srp_username:
+ w.add(6, 2)
+ w.add(len(self.srp_username)+1, 2)
+ w.addVarSeq(stringToBytes(self.srp_username), 1, 1)
+
+ return HandshakeMsg.postWrite(self, w, trial)
+
+
+class ServerHello(HandshakeMsg):
+ def __init__(self):
+ self.contentType = ContentType.handshake
+ self.server_version = (0,0)
+ self.random = createByteArrayZeros(32)
+ self.session_id = createByteArraySequence([])
+ self.cipher_suite = 0
+ self.certificate_type = CertificateType.x509
+ self.compression_method = 0
+
+ def create(self, version, random, session_id, cipher_suite,
+ certificate_type):
+ self.server_version = version
+ self.random = random
+ self.session_id = session_id
+ self.cipher_suite = cipher_suite
+ self.certificate_type = certificate_type
+ self.compression_method = 0
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ self.server_version = (p.get(1), p.get(1))
+ self.random = p.getFixBytes(32)
+ self.session_id = p.getVarBytes(1)
+ self.cipher_suite = p.get(2)
+ self.compression_method = p.get(1)
+ if not p.atLengthCheck():
+ totalExtLength = p.get(2)
+ soFar = 0
+ while soFar != totalExtLength:
+ extType = p.get(2)
+ extLength = p.get(2)
+ if extType == 7:
+ self.certificate_type = p.get(1)
+ else:
+ p.getFixBytes(extLength)
+ soFar += 4 + extLength
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.server_hello, trial)
+ w.add(self.server_version[0], 1)
+ w.add(self.server_version[1], 1)
+ w.addFixSeq(self.random, 1)
+ w.addVarSeq(self.session_id, 1, 1)
+ w.add(self.cipher_suite, 2)
+ w.add(self.compression_method, 1)
+
+ extLength = 0
+ if self.certificate_type and self.certificate_type != \
+ CertificateType.x509:
+ extLength += 5
+
+ if extLength != 0:
+ w.add(extLength, 2)
+
+ if self.certificate_type and self.certificate_type != \
+ CertificateType.x509:
+ w.add(7, 2)
+ w.add(1, 2)
+ w.add(self.certificate_type, 1)
+
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class Certificate(HandshakeMsg):
+ def __init__(self, certificateType):
+ self.certificateType = certificateType
+ self.contentType = ContentType.handshake
+ self.certChain = None
+
+ def create(self, certChain):
+ self.certChain = certChain
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ if self.certificateType == CertificateType.x509:
+ chainLength = p.get(3)
+ index = 0
+ certificate_list = []
+ while index != chainLength:
+ certBytes = p.getVarBytes(3)
+ x509 = X509()
+ x509.parseBinary(certBytes)
+ certificate_list.append(x509)
+ index += len(certBytes)+3
+ if certificate_list:
+ self.certChain = X509CertChain(certificate_list)
+ elif self.certificateType == CertificateType.cryptoID:
+ s = bytesToString(p.getVarBytes(2))
+ if s:
+ try:
+ import cryptoIDlib.CertChain
+ except ImportError:
+ raise SyntaxError(\
+ "cryptoID cert chain received, cryptoIDlib not present")
+ self.certChain = cryptoIDlib.CertChain.CertChain().parse(s)
+ else:
+ raise AssertionError()
+
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.certificate, trial)
+ if self.certificateType == CertificateType.x509:
+ chainLength = 0
+ if self.certChain:
+ certificate_list = self.certChain.x509List
+ else:
+ certificate_list = []
+ #determine length
+ for cert in certificate_list:
+ bytes = cert.writeBytes()
+ chainLength += len(bytes)+3
+ #add bytes
+ w.add(chainLength, 3)
+ for cert in certificate_list:
+ bytes = cert.writeBytes()
+ w.addVarSeq(bytes, 1, 3)
+ elif self.certificateType == CertificateType.cryptoID:
+ if self.certChain:
+ bytes = stringToBytes(self.certChain.write())
+ else:
+ bytes = createByteArraySequence([])
+ w.addVarSeq(bytes, 1, 2)
+ else:
+ raise AssertionError()
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class CertificateRequest(HandshakeMsg):
+ def __init__(self):
+ self.contentType = ContentType.handshake
+ self.certificate_types = []
+ #treat as opaque bytes for now
+ self.certificate_authorities = createByteArraySequence([])
+
+ def create(self, certificate_types, certificate_authorities):
+ self.certificate_types = certificate_types
+ self.certificate_authorities = certificate_authorities
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ self.certificate_types = p.getVarList(1, 1)
+ self.certificate_authorities = p.getVarBytes(2)
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.certificate_request,
+ trial)
+ w.addVarSeq(self.certificate_types, 1, 1)
+ w.addVarSeq(self.certificate_authorities, 1, 2)
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class ServerKeyExchange(HandshakeMsg):
+ def __init__(self, cipherSuite):
+ self.cipherSuite = cipherSuite
+ self.contentType = ContentType.handshake
+ self.srp_N = 0L
+ self.srp_g = 0L
+ self.srp_s = createByteArraySequence([])
+ self.srp_B = 0L
+ self.signature = createByteArraySequence([])
+
+ def createSRP(self, srp_N, srp_g, srp_s, srp_B):
+ self.srp_N = srp_N
+ self.srp_g = srp_g
+ self.srp_s = srp_s
+ self.srp_B = srp_B
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ self.srp_N = bytesToNumber(p.getVarBytes(2))
+ self.srp_g = bytesToNumber(p.getVarBytes(2))
+ self.srp_s = p.getVarBytes(1)
+ self.srp_B = bytesToNumber(p.getVarBytes(2))
+ if self.cipherSuite in CipherSuite.srpRsaSuites:
+ self.signature = p.getVarBytes(2)
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.server_key_exchange,
+ trial)
+ w.addVarSeq(numberToBytes(self.srp_N), 1, 2)
+ w.addVarSeq(numberToBytes(self.srp_g), 1, 2)
+ w.addVarSeq(self.srp_s, 1, 1)
+ w.addVarSeq(numberToBytes(self.srp_B), 1, 2)
+ if self.cipherSuite in CipherSuite.srpRsaSuites:
+ w.addVarSeq(self.signature, 1, 2)
+ return HandshakeMsg.postWrite(self, w, trial)
+
+ def hash(self, clientRandom, serverRandom):
+ oldCipherSuite = self.cipherSuite
+ self.cipherSuite = None
+ try:
+ bytes = clientRandom + serverRandom + self.write()[4:]
+ s = bytesToString(bytes)
+ return stringToBytes(md5.md5(s).digest() + sha.sha(s).digest())
+ finally:
+ self.cipherSuite = oldCipherSuite
+
+class ServerHelloDone(HandshakeMsg):
+ def __init__(self):
+ self.contentType = ContentType.handshake
+
+ def create(self):
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.server_hello_done, trial)
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class ClientKeyExchange(HandshakeMsg):
+ def __init__(self, cipherSuite, version=None):
+ self.cipherSuite = cipherSuite
+ self.version = version
+ self.contentType = ContentType.handshake
+ self.srp_A = 0
+ self.encryptedPreMasterSecret = createByteArraySequence([])
+
+ def createSRP(self, srp_A):
+ self.srp_A = srp_A
+ return self
+
+ def createRSA(self, encryptedPreMasterSecret):
+ self.encryptedPreMasterSecret = encryptedPreMasterSecret
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ if self.cipherSuite in CipherSuite.srpSuites + \
+ CipherSuite.srpRsaSuites:
+ self.srp_A = bytesToNumber(p.getVarBytes(2))
+ elif self.cipherSuite in CipherSuite.rsaSuites:
+ if self.version in ((3,1), (3,2)):
+ self.encryptedPreMasterSecret = p.getVarBytes(2)
+ elif self.version == (3,0):
+ self.encryptedPreMasterSecret = \
+ p.getFixBytes(len(p.bytes)-p.index)
+ else:
+ raise AssertionError()
+ else:
+ raise AssertionError()
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.client_key_exchange,
+ trial)
+ if self.cipherSuite in CipherSuite.srpSuites + \
+ CipherSuite.srpRsaSuites:
+ w.addVarSeq(numberToBytes(self.srp_A), 1, 2)
+ elif self.cipherSuite in CipherSuite.rsaSuites:
+ if self.version in ((3,1), (3,2)):
+ w.addVarSeq(self.encryptedPreMasterSecret, 1, 2)
+ elif self.version == (3,0):
+ w.addFixSeq(self.encryptedPreMasterSecret, 1)
+ else:
+ raise AssertionError()
+ else:
+ raise AssertionError()
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class CertificateVerify(HandshakeMsg):
+ def __init__(self):
+ self.contentType = ContentType.handshake
+ self.signature = createByteArraySequence([])
+
+ def create(self, signature):
+ self.signature = signature
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ self.signature = p.getVarBytes(2)
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.certificate_verify,
+ trial)
+ w.addVarSeq(self.signature, 1, 2)
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class ChangeCipherSpec(Msg):
+ def __init__(self):
+ self.contentType = ContentType.change_cipher_spec
+ self.type = 1
+
+ def create(self):
+ self.type = 1
+ return self
+
+ def parse(self, p):
+ p.setLengthCheck(1)
+ self.type = p.get(1)
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = Msg.preWrite(self, trial)
+ w.add(self.type,1)
+ return Msg.postWrite(self, w, trial)
+
+
+class Finished(HandshakeMsg):
+ def __init__(self, version):
+ self.contentType = ContentType.handshake
+ self.version = version
+ self.verify_data = createByteArraySequence([])
+
+ def create(self, verify_data):
+ self.verify_data = verify_data
+ return self
+
+ def parse(self, p):
+ p.startLengthCheck(3)
+ if self.version == (3,0):
+ self.verify_data = p.getFixBytes(36)
+ elif self.version in ((3,1), (3,2)):
+ self.verify_data = p.getFixBytes(12)
+ else:
+ raise AssertionError()
+ p.stopLengthCheck()
+ return self
+
+ def write(self, trial=False):
+ w = HandshakeMsg.preWrite(self, HandshakeType.finished, trial)
+ w.addFixSeq(self.verify_data, 1)
+ return HandshakeMsg.postWrite(self, w, trial)
+
+class ApplicationData(Msg):
+ def __init__(self):
+ self.contentType = ContentType.application_data
+ self.bytes = createByteArraySequence([])
+
+ def create(self, bytes):
+ self.bytes = bytes
+ return self
+
+ def parse(self, p):
+ self.bytes = p.bytes
+ return self
+
+ def write(self):
+ return self.bytes \ No newline at end of file
diff --git a/src/lib/tlslite/utils/AES.py b/src/lib/tlslite/utils/AES.py
new file mode 100755
index 000000000..8413f4c10
--- /dev/null
+++ b/src/lib/tlslite/utils/AES.py
@@ -0,0 +1,31 @@
+"""Abstract class for AES."""
+
+class AES:
+ def __init__(self, key, mode, IV, implementation):
+ if len(key) not in (16, 24, 32):
+ raise AssertionError()
+ if mode != 2:
+ raise AssertionError()
+ if len(IV) != 16:
+ raise AssertionError()
+ self.isBlockCipher = True
+ self.block_size = 16
+ self.implementation = implementation
+ if len(key)==16:
+ self.name = "aes128"
+ elif len(key)==24:
+ self.name = "aes192"
+ elif len(key)==32:
+ self.name = "aes256"
+ else:
+ raise AssertionError()
+
+ #CBC-Mode encryption, returns ciphertext
+ #WARNING: *MAY* modify the input as well
+ def encrypt(self, plaintext):
+ assert(len(plaintext) % 16 == 0)
+
+ #CBC-Mode decryption, returns plaintext
+ #WARNING: *MAY* modify the input as well
+ def decrypt(self, ciphertext):
+ assert(len(ciphertext) % 16 == 0) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/ASN1Parser.py b/src/lib/tlslite/utils/ASN1Parser.py
new file mode 100755
index 000000000..16b50f29c
--- /dev/null
+++ b/src/lib/tlslite/utils/ASN1Parser.py
@@ -0,0 +1,34 @@
+"""Class for parsing ASN.1"""
+from compat import *
+from codec import *
+
+#Takes a byte array which has a DER TLV field at its head
+class ASN1Parser:
+ def __init__(self, bytes):
+ p = Parser(bytes)
+ p.get(1) #skip Type
+
+ #Get Length
+ self.length = self._getASN1Length(p)
+
+ #Get Value
+ self.value = p.getFixBytes(self.length)
+
+ #Assuming this is a sequence...
+ def getChild(self, which):
+ p = Parser(self.value)
+ for x in range(which+1):
+ markIndex = p.index
+ p.get(1) #skip Type
+ length = self._getASN1Length(p)
+ p.getFixBytes(length)
+ return ASN1Parser(p.bytes[markIndex : p.index])
+
+ #Decode the ASN.1 DER length field
+ def _getASN1Length(self, p):
+ firstLength = p.get(1)
+ if firstLength<=127:
+ return firstLength
+ else:
+ lengthLength = firstLength & 0x7F
+ return p.get(lengthLength)
diff --git a/src/lib/tlslite/utils/Cryptlib_AES.py b/src/lib/tlslite/utils/Cryptlib_AES.py
new file mode 100755
index 000000000..9e101fc62
--- /dev/null
+++ b/src/lib/tlslite/utils/Cryptlib_AES.py
@@ -0,0 +1,34 @@
+"""Cryptlib AES implementation."""
+
+from cryptomath import *
+from AES import *
+
+if cryptlibpyLoaded:
+
+ def new(key, mode, IV):
+ return Cryptlib_AES(key, mode, IV)
+
+ class Cryptlib_AES(AES):
+
+ def __init__(self, key, mode, IV):
+ AES.__init__(self, key, mode, IV, "cryptlib")
+ self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_AES)
+ cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC)
+ cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
+ cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
+ cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV)
+
+ def __del__(self):
+ cryptlib_py.cryptDestroyContext(self.context)
+
+ def encrypt(self, plaintext):
+ AES.encrypt(self, plaintext)
+ bytes = stringToBytes(plaintext)
+ cryptlib_py.cryptEncrypt(self.context, bytes)
+ return bytesToString(bytes)
+
+ def decrypt(self, ciphertext):
+ AES.decrypt(self, ciphertext)
+ bytes = stringToBytes(ciphertext)
+ cryptlib_py.cryptDecrypt(self.context, bytes)
+ return bytesToString(bytes)
diff --git a/src/lib/tlslite/utils/Cryptlib_RC4.py b/src/lib/tlslite/utils/Cryptlib_RC4.py
new file mode 100755
index 000000000..7c6d087b8
--- /dev/null
+++ b/src/lib/tlslite/utils/Cryptlib_RC4.py
@@ -0,0 +1,28 @@
+"""Cryptlib RC4 implementation."""
+
+from cryptomath import *
+from RC4 import RC4
+
+if cryptlibpyLoaded:
+
+ def new(key):
+ return Cryptlib_RC4(key)
+
+ class Cryptlib_RC4(RC4):
+
+ def __init__(self, key):
+ RC4.__init__(self, key, "cryptlib")
+ self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_RC4)
+ cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
+ cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
+
+ def __del__(self):
+ cryptlib_py.cryptDestroyContext(self.context)
+
+ def encrypt(self, plaintext):
+ bytes = stringToBytes(plaintext)
+ cryptlib_py.cryptEncrypt(self.context, bytes)
+ return bytesToString(bytes)
+
+ def decrypt(self, ciphertext):
+ return self.encrypt(ciphertext) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/Cryptlib_TripleDES.py b/src/lib/tlslite/utils/Cryptlib_TripleDES.py
new file mode 100755
index 000000000..a4f8155a0
--- /dev/null
+++ b/src/lib/tlslite/utils/Cryptlib_TripleDES.py
@@ -0,0 +1,35 @@
+"""Cryptlib 3DES implementation."""
+
+from cryptomath import *
+
+from TripleDES import *
+
+if cryptlibpyLoaded:
+
+ def new(key, mode, IV):
+ return Cryptlib_TripleDES(key, mode, IV)
+
+ class Cryptlib_TripleDES(TripleDES):
+
+ def __init__(self, key, mode, IV):
+ TripleDES.__init__(self, key, mode, IV, "cryptlib")
+ self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_3DES)
+ cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC)
+ cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
+ cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
+ cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV)
+
+ def __del__(self):
+ cryptlib_py.cryptDestroyContext(self.context)
+
+ def encrypt(self, plaintext):
+ TripleDES.encrypt(self, plaintext)
+ bytes = stringToBytes(plaintext)
+ cryptlib_py.cryptEncrypt(self.context, bytes)
+ return bytesToString(bytes)
+
+ def decrypt(self, ciphertext):
+ TripleDES.decrypt(self, ciphertext)
+ bytes = stringToBytes(ciphertext)
+ cryptlib_py.cryptDecrypt(self.context, bytes)
+ return bytesToString(bytes) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/OpenSSL_AES.py b/src/lib/tlslite/utils/OpenSSL_AES.py
new file mode 100755
index 000000000..e60679bf5
--- /dev/null
+++ b/src/lib/tlslite/utils/OpenSSL_AES.py
@@ -0,0 +1,49 @@
+"""OpenSSL/M2Crypto AES implementation."""
+
+from cryptomath import *
+from AES import *
+
+if m2cryptoLoaded:
+
+ def new(key, mode, IV):
+ return OpenSSL_AES(key, mode, IV)
+
+ class OpenSSL_AES(AES):
+
+ def __init__(self, key, mode, IV):
+ AES.__init__(self, key, mode, IV, "openssl")
+ self.key = key
+ self.IV = IV
+
+ def _createContext(self, encrypt):
+ context = m2.cipher_ctx_new()
+ if len(self.key)==16:
+ cipherType = m2.aes_128_cbc()
+ if len(self.key)==24:
+ cipherType = m2.aes_192_cbc()
+ if len(self.key)==32:
+ cipherType = m2.aes_256_cbc()
+ m2.cipher_init(context, cipherType, self.key, self.IV, encrypt)
+ return context
+
+ def encrypt(self, plaintext):
+ AES.encrypt(self, plaintext)
+ context = self._createContext(1)
+ ciphertext = m2.cipher_update(context, plaintext)
+ m2.cipher_ctx_free(context)
+ self.IV = ciphertext[-self.block_size:]
+ return ciphertext
+
+ def decrypt(self, ciphertext):
+ AES.decrypt(self, ciphertext)
+ context = self._createContext(0)
+ #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in.
+ #To work around this, we append sixteen zeros to the string, below:
+ plaintext = m2.cipher_update(context, ciphertext+('\0'*16))
+
+ #If this bug is ever fixed, then plaintext will end up having a garbage
+ #plaintext block on the end. That's okay - the below code will discard it.
+ plaintext = plaintext[:len(ciphertext)]
+ m2.cipher_ctx_free(context)
+ self.IV = ciphertext[-self.block_size:]
+ return plaintext
diff --git a/src/lib/tlslite/utils/OpenSSL_RC4.py b/src/lib/tlslite/utils/OpenSSL_RC4.py
new file mode 100755
index 000000000..ac433aad7
--- /dev/null
+++ b/src/lib/tlslite/utils/OpenSSL_RC4.py
@@ -0,0 +1,25 @@
+"""OpenSSL/M2Crypto RC4 implementation."""
+
+from cryptomath import *
+from RC4 import RC4
+
+if m2cryptoLoaded:
+
+ def new(key):
+ return OpenSSL_RC4(key)
+
+ class OpenSSL_RC4(RC4):
+
+ def __init__(self, key):
+ RC4.__init__(self, key, "openssl")
+ self.rc4 = m2.rc4_new()
+ m2.rc4_set_key(self.rc4, key)
+
+ def __del__(self):
+ m2.rc4_free(self.rc4)
+
+ def encrypt(self, plaintext):
+ return m2.rc4_update(self.rc4, plaintext)
+
+ def decrypt(self, ciphertext):
+ return self.encrypt(ciphertext)
diff --git a/src/lib/tlslite/utils/OpenSSL_RSAKey.py b/src/lib/tlslite/utils/OpenSSL_RSAKey.py
new file mode 100755
index 000000000..fe1a3cd74
--- /dev/null
+++ b/src/lib/tlslite/utils/OpenSSL_RSAKey.py
@@ -0,0 +1,148 @@
+"""OpenSSL/M2Crypto RSA implementation."""
+
+from cryptomath import *
+
+from RSAKey import *
+from Python_RSAKey import Python_RSAKey
+
+#copied from M2Crypto.util.py, so when we load the local copy of m2
+#we can still use it
+def password_callback(v, prompt1='Enter private key passphrase:',
+ prompt2='Verify passphrase:'):
+ from getpass import getpass
+ while 1:
+ try:
+ p1=getpass(prompt1)
+ if v:
+ p2=getpass(prompt2)
+ if p1==p2:
+ break
+ else:
+ break
+ except KeyboardInterrupt:
+ return None
+ return p1
+
+
+if m2cryptoLoaded:
+ class OpenSSL_RSAKey(RSAKey):
+ def __init__(self, n=0, e=0):
+ self.rsa = None
+ self._hasPrivateKey = False
+ if (n and not e) or (e and not n):
+ raise AssertionError()
+ if n and e:
+ self.rsa = m2.rsa_new()
+ m2.rsa_set_n(self.rsa, numberToMPI(n))
+ m2.rsa_set_e(self.rsa, numberToMPI(e))
+
+ def __del__(self):
+ if self.rsa:
+ m2.rsa_free(self.rsa)
+
+ def __getattr__(self, name):
+ if name == 'e':
+ if not self.rsa:
+ return 0
+ return mpiToNumber(m2.rsa_get_e(self.rsa))
+ elif name == 'n':
+ if not self.rsa:
+ return 0
+ return mpiToNumber(m2.rsa_get_n(self.rsa))
+ else:
+ raise AttributeError
+
+ def hasPrivateKey(self):
+ return self._hasPrivateKey
+
+ def hash(self):
+ return Python_RSAKey(self.n, self.e).hash()
+
+ def _rawPrivateKeyOp(self, m):
+ s = numberToString(m)
+ byteLength = numBytes(self.n)
+ if len(s)== byteLength:
+ pass
+ elif len(s) == byteLength-1:
+ s = '\0' + s
+ else:
+ raise AssertionError()
+ c = stringToNumber(m2.rsa_private_encrypt(self.rsa, s,
+ m2.no_padding))
+ return c
+
+ def _rawPublicKeyOp(self, c):
+ s = numberToString(c)
+ byteLength = numBytes(self.n)
+ if len(s)== byteLength:
+ pass
+ elif len(s) == byteLength-1:
+ s = '\0' + s
+ else:
+ raise AssertionError()
+ m = stringToNumber(m2.rsa_public_decrypt(self.rsa, s,
+ m2.no_padding))
+ return m
+
+ def acceptsPassword(self): return True
+
+ def write(self, password=None):
+ bio = m2.bio_new(m2.bio_s_mem())
+ if self._hasPrivateKey:
+ if password:
+ def f(v): return password
+ m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f)
+ else:
+ def f(): pass
+ m2.rsa_write_key_no_cipher(self.rsa, bio, f)
+ else:
+ if password:
+ raise AssertionError()
+ m2.rsa_write_pub_key(self.rsa, bio)
+ s = m2.bio_read(bio, m2.bio_ctrl_pending(bio))
+ m2.bio_free(bio)
+ return s
+
+ def writeXMLPublicKey(self, indent=''):
+ return Python_RSAKey(self.n, self.e).write(indent)
+
+ def generate(bits):
+ key = OpenSSL_RSAKey()
+ def f():pass
+ key.rsa = m2.rsa_generate_key(bits, 3, f)
+ key._hasPrivateKey = True
+ return key
+ generate = staticmethod(generate)
+
+ def parse(s, passwordCallback=None):
+ if s.startswith("-----BEGIN "):
+ if passwordCallback==None:
+ callback = password_callback
+ else:
+ def f(v, prompt1=None, prompt2=None):
+ return passwordCallback()
+ callback = f
+ bio = m2.bio_new(m2.bio_s_mem())
+ try:
+ m2.bio_write(bio, s)
+ key = OpenSSL_RSAKey()
+ if s.startswith("-----BEGIN RSA PRIVATE KEY-----"):
+ def f():pass
+ key.rsa = m2.rsa_read_key(bio, callback)
+ if key.rsa == None:
+ raise SyntaxError()
+ key._hasPrivateKey = True
+ elif s.startswith("-----BEGIN PUBLIC KEY-----"):
+ key.rsa = m2.rsa_read_pub_key(bio)
+ if key.rsa == None:
+ raise SyntaxError()
+ key._hasPrivateKey = False
+ else:
+ raise SyntaxError()
+ return key
+ finally:
+ m2.bio_free(bio)
+ else:
+ raise SyntaxError()
+
+ parse = staticmethod(parse)
diff --git a/src/lib/tlslite/utils/OpenSSL_TripleDES.py b/src/lib/tlslite/utils/OpenSSL_TripleDES.py
new file mode 100755
index 000000000..f5ba16565
--- /dev/null
+++ b/src/lib/tlslite/utils/OpenSSL_TripleDES.py
@@ -0,0 +1,44 @@
+"""OpenSSL/M2Crypto 3DES implementation."""
+
+from cryptomath import *
+from TripleDES import *
+
+if m2cryptoLoaded:
+
+ def new(key, mode, IV):
+ return OpenSSL_TripleDES(key, mode, IV)
+
+ class OpenSSL_TripleDES(TripleDES):
+
+ def __init__(self, key, mode, IV):
+ TripleDES.__init__(self, key, mode, IV, "openssl")
+ self.key = key
+ self.IV = IV
+
+ def _createContext(self, encrypt):
+ context = m2.cipher_ctx_new()
+ cipherType = m2.des_ede3_cbc()
+ m2.cipher_init(context, cipherType, self.key, self.IV, encrypt)
+ return context
+
+ def encrypt(self, plaintext):
+ TripleDES.encrypt(self, plaintext)
+ context = self._createContext(1)
+ ciphertext = m2.cipher_update(context, plaintext)
+ m2.cipher_ctx_free(context)
+ self.IV = ciphertext[-self.block_size:]
+ return ciphertext
+
+ def decrypt(self, ciphertext):
+ TripleDES.decrypt(self, ciphertext)
+ context = self._createContext(0)
+ #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in.
+ #To work around this, we append sixteen zeros to the string, below:
+ plaintext = m2.cipher_update(context, ciphertext+('\0'*16))
+
+ #If this bug is ever fixed, then plaintext will end up having a garbage
+ #plaintext block on the end. That's okay - the below code will ignore it.
+ plaintext = plaintext[:len(ciphertext)]
+ m2.cipher_ctx_free(context)
+ self.IV = ciphertext[-self.block_size:]
+ return plaintext \ No newline at end of file
diff --git a/src/lib/tlslite/utils/PyCrypto_AES.py b/src/lib/tlslite/utils/PyCrypto_AES.py
new file mode 100755
index 000000000..e38b19d6f
--- /dev/null
+++ b/src/lib/tlslite/utils/PyCrypto_AES.py
@@ -0,0 +1,22 @@
+"""PyCrypto AES implementation."""
+
+from cryptomath import *
+from AES import *
+
+if pycryptoLoaded:
+ import Crypto.Cipher.AES
+
+ def new(key, mode, IV):
+ return PyCrypto_AES(key, mode, IV)
+
+ class PyCrypto_AES(AES):
+
+ def __init__(self, key, mode, IV):
+ AES.__init__(self, key, mode, IV, "pycrypto")
+ self.context = Crypto.Cipher.AES.new(key, mode, IV)
+
+ def encrypt(self, plaintext):
+ return self.context.encrypt(plaintext)
+
+ def decrypt(self, ciphertext):
+ return self.context.decrypt(ciphertext) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/PyCrypto_RC4.py b/src/lib/tlslite/utils/PyCrypto_RC4.py
new file mode 100755
index 000000000..6c6d86afd
--- /dev/null
+++ b/src/lib/tlslite/utils/PyCrypto_RC4.py
@@ -0,0 +1,22 @@
+"""PyCrypto RC4 implementation."""
+
+from cryptomath import *
+from RC4 import *
+
+if pycryptoLoaded:
+ import Crypto.Cipher.ARC4
+
+ def new(key):
+ return PyCrypto_RC4(key)
+
+ class PyCrypto_RC4(RC4):
+
+ def __init__(self, key):
+ RC4.__init__(self, key, "pycrypto")
+ self.context = Crypto.Cipher.ARC4.new(key)
+
+ def encrypt(self, plaintext):
+ return self.context.encrypt(plaintext)
+
+ def decrypt(self, ciphertext):
+ return self.context.decrypt(ciphertext) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/PyCrypto_RSAKey.py b/src/lib/tlslite/utils/PyCrypto_RSAKey.py
new file mode 100755
index 000000000..48b5cef03
--- /dev/null
+++ b/src/lib/tlslite/utils/PyCrypto_RSAKey.py
@@ -0,0 +1,61 @@
+"""PyCrypto RSA implementation."""
+
+from cryptomath import *
+
+from RSAKey import *
+from Python_RSAKey import Python_RSAKey
+
+if pycryptoLoaded:
+
+ from Crypto.PublicKey import RSA
+
+ class PyCrypto_RSAKey(RSAKey):
+ def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
+ if not d:
+ self.rsa = RSA.construct( (n, e) )
+ else:
+ self.rsa = RSA.construct( (n, e, d, p, q) )
+
+ def __getattr__(self, name):
+ return getattr(self.rsa, name)
+
+ def hasPrivateKey(self):
+ return self.rsa.has_private()
+
+ def hash(self):
+ return Python_RSAKey(self.n, self.e).hash()
+
+ def _rawPrivateKeyOp(self, m):
+ s = numberToString(m)
+ byteLength = numBytes(self.n)
+ if len(s)== byteLength:
+ pass
+ elif len(s) == byteLength-1:
+ s = '\0' + s
+ else:
+ raise AssertionError()
+ c = stringToNumber(self.rsa.decrypt((s,)))
+ return c
+
+ def _rawPublicKeyOp(self, c):
+ s = numberToString(c)
+ byteLength = numBytes(self.n)
+ if len(s)== byteLength:
+ pass
+ elif len(s) == byteLength-1:
+ s = '\0' + s
+ else:
+ raise AssertionError()
+ m = stringToNumber(self.rsa.encrypt(s, None)[0])
+ return m
+
+ def writeXMLPublicKey(self, indent=''):
+ return Python_RSAKey(self.n, self.e).write(indent)
+
+ def generate(bits):
+ key = PyCrypto_RSAKey()
+ def f(numBytes):
+ return bytesToString(getRandomBytes(numBytes))
+ key.rsa = RSA.generate(bits, f)
+ return key
+ generate = staticmethod(generate)
diff --git a/src/lib/tlslite/utils/PyCrypto_TripleDES.py b/src/lib/tlslite/utils/PyCrypto_TripleDES.py
new file mode 100755
index 000000000..8c22bb80a
--- /dev/null
+++ b/src/lib/tlslite/utils/PyCrypto_TripleDES.py
@@ -0,0 +1,22 @@
+"""PyCrypto 3DES implementation."""
+
+from cryptomath import *
+from TripleDES import *
+
+if pycryptoLoaded:
+ import Crypto.Cipher.DES3
+
+ def new(key, mode, IV):
+ return PyCrypto_TripleDES(key, mode, IV)
+
+ class PyCrypto_TripleDES(TripleDES):
+
+ def __init__(self, key, mode, IV):
+ TripleDES.__init__(self, key, mode, IV, "pycrypto")
+ self.context = Crypto.Cipher.DES3.new(key, mode, IV)
+
+ def encrypt(self, plaintext):
+ return self.context.encrypt(plaintext)
+
+ def decrypt(self, ciphertext):
+ return self.context.decrypt(ciphertext) \ No newline at end of file
diff --git a/src/lib/tlslite/utils/Python_AES.py b/src/lib/tlslite/utils/Python_AES.py
new file mode 100755
index 000000000..657152f89
--- /dev/null
+++ b/src/lib/tlslite/utils/Python_AES.py
@@ -0,0 +1,68 @@
+"""Pure-Python AES implementation."""
+
+from cryptomath import *
+
+from AES import *
+from rijndael import rijndael
+
+def new(key, mode, IV):
+ return Python_AES(key, mode, IV)
+
+class Python_AES(AES):
+ def __init__(self, key, mode, IV):
+ AES.__init__(self, key, mode, IV, "python")
+ self.rijndael = rijndael(key, 16)
+ self.IV = IV
+
+ def encrypt(self, plaintext):
+ AES.encrypt(self, plaintext)
+
+ plaintextBytes = stringToBytes(plaintext)
+ chainBytes = stringToBytes(self.IV)
+
+ #CBC Mode: For each block...
+ for x in range(len(plaintextBytes)/16):
+
+ #XOR with the chaining block
+ blockBytes = plaintextBytes[x*16 : (x*16)+16]
+ for y in range(16):
+ blockBytes[y] ^= chainBytes[y]
+ blockString = bytesToString(blockBytes)
+
+ #Encrypt it
+ encryptedBytes = stringToBytes(self.rijndael.encrypt(blockString))
+
+ #Overwrite the input with the output
+ for y in range(16):
+ plaintextBytes[(x*16)+y] = encryptedBytes[y]
+
+ #Set the next chaining block
+ chainBytes = encryptedBytes
+
+ self.IV = bytesToString(chainBytes)
+ return bytesToString(plaintextBytes)
+
+ def decrypt(self, ciphertext):
+ AES.decrypt(self, ciphertext)
+
+ ciphertextBytes = stringToBytes(ciphertext)
+ chainBytes = stringToBytes(self.IV)
+
+ #CBC Mode: For each block...
+ for x in range(len(ciphertextBytes)/16):
+
+ #Decrypt it
+ blockBytes = ciphertextBytes[x*16 : (x*16)+16]
+ blockString = bytesToString(blockBytes)
+ decryptedBytes = stringToBytes(self.rijndael.decrypt(blockString))
+
+ #XOR with the chaining block and overwrite the input with output
+ for y in range(16):
+ decryptedBytes[y] ^= chainBytes[y]
+ ciphertextBytes[(x*16)+y] = decryptedBytes[y]
+
+ #Set the next chaining block
+ chainBytes = blockBytes
+
+ self.IV = bytesToString(chainBytes)
+ return bytesToString(ciphertextBytes)
diff --git a/src/lib/tlslite/utils/Python_RC4.py b/src/lib/tlslite/utils/Python_RC4.py
new file mode 100755
index 000000000..56ce5fb2f
--- /dev/null
+++ b/src/lib/tlslite/utils/Python_RC4.py
@@ -0,0 +1,39 @@
+"""Pure-Python RC4 implementation."""
+
+from RC4 import RC4
+from cryptomath import *
+
+def new(key):
+ return Python_RC4(key)
+
+class Python_RC4(RC4):
+ def __init__(self, key):
+ RC4.__init__(self, key, "python")
+ keyBytes = stringToBytes(key)
+ S = [i for i in range(256)]
+ j = 0
+ for i in range(256):
+ j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256
+ S[i], S[j] = S[j], S[i]
+
+ self.S = S
+ self.i = 0
+ self.j = 0
+
+ def encrypt(self, plaintext):
+ plaintextBytes = stringToBytes(plaintext)
+ S = self.S
+ i = self.i
+ j = self.j
+ for x in range(len(plaintextBytes)):
+ i = (i + 1) % 256
+ j = (j + S[i]) % 256
+ S[i], S[j] = S[j], S[i]
+ t = (S[i] + S[j]) % 256
+ plaintextBytes[x] ^= S[t]
+ self.i = i
+ self.j = j
+ return bytesToString(plaintextBytes)
+
+ def decrypt(self, ciphertext):
+ return self.encrypt(ciphertext)
diff --git a/src/lib/tlslite/utils/Python_RSAKey.py b/src/lib/tlslite/utils/Python_RSAKey.py
new file mode 100755
index 000000000..2c469b572
--- /dev/null
+++ b/src/lib/tlslite/utils/Python_RSAKey.py
@@ -0,0 +1,209 @@
+"""Pure-Python RSA implementation."""
+
+from cryptomath import *
+import xmltools
+from ASN1Parser import ASN1Parser
+from RSAKey import *
+
+class Python_RSAKey(RSAKey):
+ def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
+ if (n and not e) or (e and not n):
+ raise AssertionError()
+ self.n = n
+ self.e = e
+ self.d = d
+ self.p = p
+ self.q = q
+ self.dP = dP
+ self.dQ = dQ
+ self.qInv = qInv
+ self.blinder = 0
+ self.unblinder = 0
+
+ def hasPrivateKey(self):
+ return self.d != 0
+
+ def hash(self):
+ s = self.writeXMLPublicKey('\t\t')
+ return hashAndBase64(s.strip())
+
+ def _rawPrivateKeyOp(self, m):
+ #Create blinding values, on the first pass:
+ if not self.blinder:
+ self.unblinder = getRandomNumber(2, self.n)
+ self.blinder = powMod(invMod(self.unblinder, self.n), self.e,
+ self.n)
+
+ #Blind the input
+ m = (m * self.blinder) % self.n
+
+ #Perform the RSA operation
+ c = self._rawPrivateKeyOpHelper(m)
+
+ #Unblind the output
+ c = (c * self.unblinder) % self.n
+
+ #Update blinding values
+ self.blinder = (self.blinder * self.blinder) % self.n
+ self.unblinder = (self.unblinder * self.unblinder) % self.n
+
+ #Return the output
+ return c
+
+
+ def _rawPrivateKeyOpHelper(self, m):
+ #Non-CRT version
+ #c = powMod(m, self.d, self.n)
+
+ #CRT version (~3x faster)
+ s1 = powMod(m, self.dP, self.p)
+ s2 = powMod(m, self.dQ, self.q)
+ h = ((s1 - s2) * self.qInv) % self.p
+ c = s2 + self.q * h
+ return c
+
+ def _rawPublicKeyOp(self, c):
+ m = powMod(c, self.e, self.n)
+ return m
+
+ def acceptsPassword(self): return False
+
+ def write(self, indent=''):
+ if self.d:
+ s = indent+'<privateKey xmlns="http://trevp.net/rsa">\n'
+ else:
+ s = indent+'<publicKey xmlns="http://trevp.net/rsa">\n'
+ s += indent+'\t<n>%s</n>\n' % numberToBase64(self.n)
+ s += indent+'\t<e>%s</e>\n' % numberToBase64(self.e)
+ if self.d:
+ s += indent+'\t<d>%s</d>\n' % numberToBase64(self.d)
+ s += indent+'\t<p>%s</p>\n' % numberToBase64(self.p)
+ s += indent+'\t<q>%s</q>\n' % numberToBase64(self.q)
+ s += indent+'\t<dP>%s</dP>\n' % numberToBase64(self.dP)
+ s += indent+'\t<dQ>%s</dQ>\n' % numberToBase64(self.dQ)
+ s += indent+'\t<qInv>%s</qInv>\n' % numberToBase64(self.qInv)
+ s += indent+'</privateKey>'
+ else:
+ s += indent+'</publicKey>'
+ #Only add \n if part of a larger structure
+ if indent != '':
+ s += '\n'
+ return s
+
+ def writeXMLPublicKey(self, indent=''):
+ return Python_RSAKey(self.n, self.e).write(indent)
+
+ def generate(bits):
+ key = Python_RSAKey()
+ p = getRandomPrime(bits/2, False)
+ q = getRandomPrime(bits/2, False)
+ t = lcm(p-1, q-1)
+ key.n = p * q
+ key.e = 3L #Needed to be long, for Java
+ key.d = invMod(key.e, t)
+ key.p = p
+ key.q = q
+ key.dP = key.d % (p-1)
+ key.dQ = key.d % (q-1)
+ key.qInv = invMod(q, p)
+ return key
+ generate = staticmethod(generate)
+
+ def parsePEM(s, passwordCallback=None):
+ """Parse a string containing a <privateKey> or <publicKey>, or
+ PEM-encoded key."""
+
+ start = s.find("-----BEGIN PRIVATE KEY-----")
+ if start != -1:
+ end = s.find("-----END PRIVATE KEY-----")
+ if end == -1:
+ raise SyntaxError("Missing PEM Postfix")
+ s = s[start+len("-----BEGIN PRIVATE KEY -----") : end]
+ bytes = base64ToBytes(s)
+ return Python_RSAKey._parsePKCS8(bytes)
+ else:
+ start = s.find("-----BEGIN RSA PRIVATE KEY-----")
+ if start != -1:
+ end = s.find("-----END RSA PRIVATE KEY-----")
+ if end == -1:
+ raise SyntaxError("Missing PEM Postfix")
+ s = s[start+len("-----BEGIN RSA PRIVATE KEY -----") : end]
+ bytes = base64ToBytes(s)
+ return Python_RSAKey._parseSSLeay(bytes)
+ raise SyntaxError("Missing PEM Prefix")
+ parsePEM = staticmethod(parsePEM)
+
+ def parseXML(s):
+ element = xmltools.parseAndStripWhitespace(s)
+ return Python_RSAKey._parseXML(element)
+ parseXML = staticmethod(parseXML)
+
+ def _parsePKCS8(bytes):
+ p = ASN1Parser(bytes)
+
+ version = p.getChild(0).value[0]
+ if version != 0:
+ raise SyntaxError("Unrecognized PKCS8 version")
+
+ rsaOID = p.getChild(1).value
+ if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]:
+ raise SyntaxError("Unrecognized AlgorithmIdentifier")
+
+ #Get the privateKey
+ privateKeyP = p.getChild(2)
+
+ #Adjust for OCTET STRING encapsulation
+ privateKeyP = ASN1Parser(privateKeyP.value)
+
+ return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
+ _parsePKCS8 = staticmethod(_parsePKCS8)
+
+ def _parseSSLeay(bytes):
+ privateKeyP = ASN1Parser(bytes)
+ return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
+ _parseSSLeay = staticmethod(_parseSSLeay)
+
+ def _parseASN1PrivateKey(privateKeyP):
+ version = privateKeyP.getChild(0).value[0]
+ if version != 0:
+ raise SyntaxError("Unrecognized RSAPrivateKey version")
+ n = bytesToNumber(privateKeyP.getChild(1).value)
+ e = bytesToNumber(privateKeyP.getChild(2).value)
+ d = bytesToNumber(privateKeyP.getChild(3).value)
+ p = bytesToNumber(privateKeyP.getChild(4).value)
+ q = bytesToNumber(privateKeyP.getChild(5).value)
+ dP = bytesToNumber(privateKeyP.getChild(6).value)
+ dQ = bytesToNumber(privateKeyP.getChild(7).value)
+ qInv = bytesToNumber(privateKeyP.getChild(8).value)
+ return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey)
+
+ def _parseXML(element):
+ try:
+ xmltools.checkName(element, "privateKey")
+ except SyntaxError:
+ xmltools.checkName(element, "publicKey")
+
+ #Parse attributes
+ xmltools.getReqAttribute(element, "xmlns", "http://trevp.net/rsa\Z")
+ xmltools.checkNoMoreAttributes(element)
+
+ #Parse public values (<n> and <e>)
+ n = base64ToNumber(xmltools.getText(xmltools.getChild(element, 0, "n"), xmltools.base64RegEx))
+ e = base64ToNumber(xmltools.getText(xmltools.getChild(element, 1, "e"), xmltools.base64RegEx))
+ d = 0
+ p = 0
+ q = 0
+ dP = 0
+ dQ = 0
+ qInv = 0
+ #Parse private values, if present
+ if element.childNodes.length>=3:
+ d = base64ToNumber(xmltools.getText(xmltools.getChild(element, 2, "d"), xmltools.base64RegEx))
+ p = base64ToNumber(xmltools.getText(xmltools.getChild(element, 3, "p"), xmltools.base64RegEx))
+ q = base64ToNumber(xmltools.getText(xmltools.getChild(element, 4, "q"), xmltools.base64RegEx))
+ dP = base64ToNumber(xmltools.getText(xmltools.getChild(element, 5, "dP"), xmltools.base64RegEx))
+ dQ = base64ToNumber(xmltools.getText(xmltools.getChild(element, 6, "dQ"), xmltools.base64RegEx))
+ qInv = base64ToNumber(xmltools.getText(xmltools.getLastChild(element, 7, "qInv"), xmltools.base64RegEx))
+ return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ _parseXML = staticmethod(_parseXML)
diff --git a/src/lib/tlslite/utils/RC4.py b/src/lib/tlslite/utils/RC4.py
new file mode 100755
index 000000000..550692327
--- /dev/null
+++ b/src/lib/tlslite/utils/RC4.py
@@ -0,0 +1,17 @@
+"""Abstract class for RC4."""
+
+from compat import * #For False
+
+class RC4:
+ def __init__(self, keyBytes, implementation):
+ if len(keyBytes) < 16 or len(keyBytes) > 256:
+ raise ValueError()
+ self.isBlockCipher = False
+ self.name = "rc4"
+ self.implementation = implementation
+
+ def encrypt(self, plaintext):
+ raise NotImplementedError()
+
+ def decrypt(self, ciphertext):
+ raise NotImplementedError() \ No newline at end of file
diff --git a/src/lib/tlslite/utils/RSAKey.py b/src/lib/tlslite/utils/RSAKey.py
new file mode 100755
index 000000000..37c292df5
--- /dev/null
+++ b/src/lib/tlslite/utils/RSAKey.py
@@ -0,0 +1,264 @@
+"""Abstract class for RSA."""
+
+from cryptomath import *
+
+
+class RSAKey:
+ """This is an abstract base class for RSA keys.
+
+ Particular implementations of RSA keys, such as
+ L{OpenSSL_RSAKey.OpenSSL_RSAKey},
+ L{Python_RSAKey.Python_RSAKey}, and
+ L{PyCrypto_RSAKey.PyCrypto_RSAKey},
+ inherit from this.
+
+ To create or parse an RSA key, don't use one of these classes
+ directly. Instead, use the factory functions in
+ L{tlslite.utils.keyfactory}.
+ """
+
+ def __init__(self, n=0, e=0):
+ """Create a new RSA key.
+
+ If n and e are passed in, the new key will be initialized.
+
+ @type n: int
+ @param n: RSA modulus.
+
+ @type e: int
+ @param e: RSA public exponent.
+ """
+ raise NotImplementedError()
+
+ def __len__(self):
+ """Return the length of this key in bits.
+
+ @rtype: int
+ """
+ return numBits(self.n)
+
+ def hasPrivateKey(self):
+ """Return whether or not this key has a private component.
+
+ @rtype: bool
+ """
+ raise NotImplementedError()
+
+ def hash(self):
+ """Return the cryptoID <keyHash> value corresponding to this
+ key.
+
+ @rtype: str
+ """
+ raise NotImplementedError()
+
+ def getSigningAlgorithm(self):
+ """Return the cryptoID sigAlgo value corresponding to this key.
+
+ @rtype: str
+ """
+ return "pkcs1-sha1"
+
+ def hashAndSign(self, bytes):
+ """Hash and sign the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ a PKCS1-SHA1 signature on the passed-in data.
+
+ @type bytes: str or L{array.array} of unsigned bytes
+ @param bytes: The value which will be hashed and signed.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1-SHA1 signature on the passed-in data.
+ """
+ if not isinstance(bytes, type("")):
+ bytes = bytesToString(bytes)
+ hashBytes = stringToBytes(sha.sha(bytes).digest())
+ prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
+ sigBytes = self.sign(prefixedHashBytes)
+ return sigBytes
+
+ def hashAndVerify(self, sigBytes, bytes):
+ """Hash and verify the passed-in bytes with the signature.
+
+ This verifies a PKCS1-SHA1 signature on the passed-in data.
+
+ @type sigBytes: L{array.array} of unsigned bytes
+ @param sigBytes: A PKCS1-SHA1 signature.
+
+ @type bytes: str or L{array.array} of unsigned bytes
+ @param bytes: The value which will be hashed and verified.
+
+ @rtype: bool
+ @return: Whether the signature matches the passed-in data.
+ """
+ if not isinstance(bytes, type("")):
+ bytes = bytesToString(bytes)
+ hashBytes = stringToBytes(sha.sha(bytes).digest())
+ prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
+ return self.verify(sigBytes, prefixedHashBytes)
+
+ def sign(self, bytes):
+ """Sign the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ a PKCS1 signature on the passed-in data.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be signed.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1 signature on the passed-in data.
+ """
+ if not self.hasPrivateKey():
+ raise AssertionError()
+ paddedBytes = self._addPKCS1Padding(bytes, 1)
+ m = bytesToNumber(paddedBytes)
+ if m >= self.n:
+ raise ValueError()
+ c = self._rawPrivateKeyOp(m)
+ sigBytes = numberToBytes(c)
+ return sigBytes
+
+ def verify(self, sigBytes, bytes):
+ """Verify the passed-in bytes with the signature.
+
+ This verifies a PKCS1 signature on the passed-in data.
+
+ @type sigBytes: L{array.array} of unsigned bytes
+ @param sigBytes: A PKCS1 signature.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be verified.
+
+ @rtype: bool
+ @return: Whether the signature matches the passed-in data.
+ """
+ paddedBytes = self._addPKCS1Padding(bytes, 1)
+ c = bytesToNumber(sigBytes)
+ if c >= self.n:
+ return False
+ m = self._rawPublicKeyOp(c)
+ checkBytes = numberToBytes(m)
+ return checkBytes == paddedBytes
+
+ def encrypt(self, bytes):
+ """Encrypt the passed-in bytes.
+
+ This performs PKCS1 encryption of the passed-in data.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be encrypted.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1 encryption of the passed-in data.
+ """
+ paddedBytes = self._addPKCS1Padding(bytes, 2)
+ m = bytesToNumber(paddedBytes)
+ if m >= self.n:
+ raise ValueError()
+ c = self._rawPublicKeyOp(m)
+ encBytes = numberToBytes(c)
+ return encBytes
+
+ def decrypt(self, encBytes):
+ """Decrypt the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ PKCS1 decryption of the passed-in data.
+
+ @type encBytes: L{array.array} of unsigned bytes
+ @param encBytes: The value which will be decrypted.
+
+ @rtype: L{array.array} of unsigned bytes or None.
+ @return: A PKCS1 decryption of the passed-in data or None if
+ the data is not properly formatted.
+ """
+ if not self.hasPrivateKey():
+ raise AssertionError()
+ c = bytesToNumber(encBytes)
+ if c >= self.n:
+ return None
+ m = self._rawPrivateKeyOp(c)
+ decBytes = numberToBytes(m)
+ if (len(decBytes) != numBytes(self.n)-1): #Check first byte
+ return None
+ if decBytes[0] != 2: #Check second byte
+ return None
+ for x in range(len(decBytes)-1): #Scan through for zero separator
+ if decBytes[x]== 0:
+ break
+ else:
+ return None
+ return decBytes[x+1:] #Return everything after the separator
+
+ def _rawPrivateKeyOp(self, m):
+ raise NotImplementedError()
+
+ def _rawPublicKeyOp(self, c):
+ raise NotImplementedError()
+
+ def acceptsPassword(self):
+ """Return True if the write() method accepts a password for use
+ in encrypting the private key.
+
+ @rtype: bool
+ """
+ raise NotImplementedError()
+
+ def write(self, password=None):
+ """Return a string containing the key.
+
+ @rtype: str
+ @return: A string describing the key, in whichever format (PEM
+ or XML) is native to the implementation.
+ """
+ raise NotImplementedError()
+
+ def writeXMLPublicKey(self, indent=''):
+ """Return a string containing the key.
+
+ @rtype: str
+ @return: A string describing the public key, in XML format.
+ """
+ return Python_RSAKey(self.n, self.e).write(indent)
+
+ def generate(bits):
+ """Generate a new key with the specified bit length.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ """
+ raise NotImplementedError()
+ generate = staticmethod(generate)
+
+
+ # **************************************************************************
+ # Helper Functions for RSA Keys
+ # **************************************************************************
+
+ def _addPKCS1SHA1Prefix(self, bytes):
+ prefixBytes = createByteArraySequence(\
+ [48,33,48,9,6,5,43,14,3,2,26,5,0,4,20])
+ prefixedBytes = prefixBytes + bytes
+ return prefixedBytes
+
+ def _addPKCS1Padding(self, bytes, blockType):
+ padLength = (numBytes(self.n) - (len(bytes)+3))
+ if blockType == 1: #Signature padding
+ pad = [0xFF] * padLength
+ elif blockType == 2: #Encryption padding
+ pad = createByteArraySequence([])
+ while len(pad) < padLength:
+ padBytes = getRandomBytes(padLength * 2)
+ pad = [b for b in padBytes if b != 0]
+ pad = pad[:padLength]
+ else:
+ raise AssertionError()
+
+ #NOTE: To be proper, we should add [0,blockType]. However,
+ #the zero is lost when the returned padding is converted
+ #to a number, so we don't even bother with it. Also,
+ #adding it would cause a misalignment in verify()
+ padding = createByteArraySequence([blockType] + pad + [0])
+ paddedBytes = padding + bytes
+ return paddedBytes
diff --git a/src/lib/tlslite/utils/TripleDES.py b/src/lib/tlslite/utils/TripleDES.py
new file mode 100755
index 000000000..2db45888b
--- /dev/null
+++ b/src/lib/tlslite/utils/TripleDES.py
@@ -0,0 +1,26 @@
+"""Abstract class for 3DES."""
+
+from compat import * #For True
+
+class TripleDES:
+ def __init__(self, key, mode, IV, implementation):
+ if len(key) != 24:
+ raise ValueError()
+ if mode != 2:
+ raise ValueError()
+ if len(IV) != 8:
+ raise ValueError()
+ self.isBlockCipher = True
+ self.block_size = 8
+ self.implementation = implementation
+ self.name = "3des"
+
+ #CBC-Mode encryption, returns ciphertext
+ #WARNING: *MAY* modify the input as well
+ def encrypt(self, plaintext):
+ assert(len(plaintext) % 8 == 0)
+
+ #CBC-Mode decryption, returns plaintext
+ #WARNING: *MAY* modify the input as well
+ def decrypt(self, ciphertext):
+ assert(len(ciphertext) % 8 == 0)
diff --git a/src/lib/tlslite/utils/__init__.py b/src/lib/tlslite/utils/__init__.py
new file mode 100755
index 000000000..e96b4bef8
--- /dev/null
+++ b/src/lib/tlslite/utils/__init__.py
@@ -0,0 +1,31 @@
+"""Toolkit for crypto and other stuff."""
+
+__all__ = ["AES",
+ "ASN1Parser",
+ "cipherfactory",
+ "codec",
+ "Cryptlib_AES",
+ "Cryptlib_RC4",
+ "Cryptlib_TripleDES",
+ "cryptomath: cryptomath module",
+ "dateFuncs",
+ "hmac",
+ "JCE_RSAKey",
+ "compat",
+ "keyfactory",
+ "OpenSSL_AES",
+ "OpenSSL_RC4",
+ "OpenSSL_RSAKey",
+ "OpenSSL_TripleDES",
+ "PyCrypto_AES",
+ "PyCrypto_RC4",
+ "PyCrypto_RSAKey",
+ "PyCrypto_TripleDES",
+ "Python_AES",
+ "Python_RC4",
+ "Python_RSAKey",
+ "RC4",
+ "rijndael",
+ "RSAKey",
+ "TripleDES",
+ "xmltools"]
diff --git a/src/lib/tlslite/utils/cipherfactory.py b/src/lib/tlslite/utils/cipherfactory.py
new file mode 100755
index 000000000..ccbb6b5ff
--- /dev/null
+++ b/src/lib/tlslite/utils/cipherfactory.py
@@ -0,0 +1,111 @@
+"""Factory functions for symmetric cryptography."""
+
+import os
+
+import Python_AES
+import Python_RC4
+
+import cryptomath
+
+tripleDESPresent = False
+
+if cryptomath.m2cryptoLoaded:
+ import OpenSSL_AES
+ import OpenSSL_RC4
+ import OpenSSL_TripleDES
+ tripleDESPresent = True
+
+if cryptomath.cryptlibpyLoaded:
+ import Cryptlib_AES
+ import Cryptlib_RC4
+ import Cryptlib_TripleDES
+ tripleDESPresent = True
+
+if cryptomath.pycryptoLoaded:
+ import PyCrypto_AES
+ import PyCrypto_RC4
+ import PyCrypto_TripleDES
+ tripleDESPresent = True
+
+# **************************************************************************
+# Factory Functions for AES
+# **************************************************************************
+
+def createAES(key, IV, implList=None):
+ """Create a new AES object.
+
+ @type key: str
+ @param key: A 16, 24, or 32 byte string.
+
+ @type IV: str
+ @param IV: A 16 byte string
+
+ @rtype: L{tlslite.utils.AES}
+ @return: An AES object.
+ """
+ if implList == None:
+ implList = ["cryptlib", "openssl", "pycrypto", "python"]
+
+ for impl in implList:
+ if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
+ return Cryptlib_AES.new(key, 2, IV)
+ elif impl == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_AES.new(key, 2, IV)
+ elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_AES.new(key, 2, IV)
+ elif impl == "python":
+ return Python_AES.new(key, 2, IV)
+ raise NotImplementedError()
+
+def createRC4(key, IV, implList=None):
+ """Create a new RC4 object.
+
+ @type key: str
+ @param key: A 16 to 32 byte string.
+
+ @type IV: object
+ @param IV: Ignored, whatever it is.
+
+ @rtype: L{tlslite.utils.RC4}
+ @return: An RC4 object.
+ """
+ if implList == None:
+ implList = ["cryptlib", "openssl", "pycrypto", "python"]
+
+ if len(IV) != 0:
+ raise AssertionError()
+ for impl in implList:
+ if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
+ return Cryptlib_RC4.new(key)
+ elif impl == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_RC4.new(key)
+ elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_RC4.new(key)
+ elif impl == "python":
+ return Python_RC4.new(key)
+ raise NotImplementedError()
+
+#Create a new TripleDES instance
+def createTripleDES(key, IV, implList=None):
+ """Create a new 3DES object.
+
+ @type key: str
+ @param key: A 24 byte string.
+
+ @type IV: str
+ @param IV: An 8 byte string
+
+ @rtype: L{tlslite.utils.TripleDES}
+ @return: A 3DES object.
+ """
+ if implList == None:
+ implList = ["cryptlib", "openssl", "pycrypto"]
+
+ for impl in implList:
+ if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
+ return Cryptlib_TripleDES.new(key, 2, IV)
+ elif impl == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_TripleDES.new(key, 2, IV)
+ elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_TripleDES.new(key, 2, IV)
+ raise NotImplementedError() \ No newline at end of file
diff --git a/src/lib/tlslite/utils/codec.py b/src/lib/tlslite/utils/codec.py
new file mode 100755
index 000000000..13022a0b9
--- /dev/null
+++ b/src/lib/tlslite/utils/codec.py
@@ -0,0 +1,94 @@
+"""Classes for reading/writing binary data (such as TLS records)."""
+
+from compat import *
+
+class Writer:
+ def __init__(self, length=0):
+ #If length is zero, then this is just a "trial run" to determine length
+ self.index = 0
+ self.bytes = createByteArrayZeros(length)
+
+ def add(self, x, length):
+ if self.bytes:
+ newIndex = self.index+length-1
+ while newIndex >= self.index:
+ self.bytes[newIndex] = x & 0xFF
+ x >>= 8
+ newIndex -= 1
+ self.index += length
+
+ def addFixSeq(self, seq, length):
+ if self.bytes:
+ for e in seq:
+ self.add(e, length)
+ else:
+ self.index += len(seq)*length
+
+ def addVarSeq(self, seq, length, lengthLength):
+ if self.bytes:
+ self.add(len(seq)*length, lengthLength)
+ for e in seq:
+ self.add(e, length)
+ else:
+ self.index += lengthLength + (len(seq)*length)
+
+
+class Parser:
+ def __init__(self, bytes):
+ self.bytes = bytes
+ self.index = 0
+
+ def get(self, length):
+ if self.index + length > len(self.bytes):
+ raise SyntaxError()
+ x = 0
+ for count in range(length):
+ x <<= 8
+ x |= self.bytes[self.index]
+ self.index += 1
+ return x
+
+ def getFixBytes(self, lengthBytes):
+ bytes = self.bytes[self.index : self.index+lengthBytes]
+ self.index += lengthBytes
+ return bytes
+
+ def getVarBytes(self, lengthLength):
+ lengthBytes = self.get(lengthLength)
+ return self.getFixBytes(lengthBytes)
+
+ def getFixList(self, length, lengthList):
+ l = [0] * lengthList
+ for x in range(lengthList):
+ l[x] = self.get(length)
+ return l
+
+ def getVarList(self, length, lengthLength):
+ lengthList = self.get(lengthLength)
+ if lengthList % length != 0:
+ raise SyntaxError()
+ lengthList = int(lengthList/length)
+ l = [0] * lengthList
+ for x in range(lengthList):
+ l[x] = self.get(length)
+ return l
+
+ def startLengthCheck(self, lengthLength):
+ self.lengthCheck = self.get(lengthLength)
+ self.indexCheck = self.index
+
+ def setLengthCheck(self, length):
+ self.lengthCheck = length
+ self.indexCheck = self.index
+
+ def stopLengthCheck(self):
+ if (self.index - self.indexCheck) != self.lengthCheck:
+ raise SyntaxError()
+
+ def atLengthCheck(self):
+ if (self.index - self.indexCheck) < self.lengthCheck:
+ return False
+ elif (self.index - self.indexCheck) == self.lengthCheck:
+ return True
+ else:
+ raise SyntaxError() \ No newline at end of file
diff --git a/src/lib/tlslite/utils/compat.py b/src/lib/tlslite/utils/compat.py
new file mode 100755
index 000000000..7d2d9250d
--- /dev/null
+++ b/src/lib/tlslite/utils/compat.py
@@ -0,0 +1,140 @@
+"""Miscellaneous functions to mask Python version differences."""
+
+import sys
+import os
+
+if sys.version_info < (2,2):
+ raise AssertionError("Python 2.2 or later required")
+
+if sys.version_info < (2,3):
+
+ def enumerate(collection):
+ return zip(range(len(collection)), collection)
+
+ class Set:
+ def __init__(self, seq=None):
+ self.values = {}
+ if seq:
+ for e in seq:
+ self.values[e] = None
+
+ def add(self, e):
+ self.values[e] = None
+
+ def discard(self, e):
+ if e in self.values.keys():
+ del(self.values[e])
+
+ def union(self, s):
+ ret = Set()
+ for e in self.values.keys():
+ ret.values[e] = None
+ for e in s.values.keys():
+ ret.values[e] = None
+ return ret
+
+ def issubset(self, other):
+ for e in self.values.keys():
+ if e not in other.values.keys():
+ return False
+ return True
+
+ def __nonzero__( self):
+ return len(self.values.keys())
+
+ def __contains__(self, e):
+ return e in self.values.keys()
+
+ def __iter__(self):
+ return iter(set.values.keys())
+
+
+if os.name != "java":
+
+ import array
+ def createByteArraySequence(seq):
+ return array.array('B', seq)
+ def createByteArrayZeros(howMany):
+ return array.array('B', [0] * howMany)
+ def concatArrays(a1, a2):
+ return a1+a2
+
+ def bytesToString(bytes):
+ return bytes.tostring()
+ def stringToBytes(s):
+ bytes = createByteArrayZeros(0)
+ bytes.fromstring(s)
+ return bytes
+
+ import math
+ def numBits(n):
+ if n==0:
+ return 0
+ s = "%x" % n
+ return ((len(s)-1)*4) + \
+ {'0':0, '1':1, '2':2, '3':2,
+ '4':3, '5':3, '6':3, '7':3,
+ '8':4, '9':4, 'a':4, 'b':4,
+ 'c':4, 'd':4, 'e':4, 'f':4,
+ }[s[0]]
+ return int(math.floor(math.log(n, 2))+1)
+
+ BaseException = Exception
+ import sys
+ import traceback
+ def formatExceptionTrace(e):
+ newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
+ return newStr
+
+else:
+ #Jython 2.1 is missing lots of python 2.3 stuff,
+ #which we have to emulate here:
+ #NOTE: JYTHON SUPPORT NO LONGER WORKS, DUE TO USE OF GENERATORS.
+ #THIS CODE IS LEFT IN SO THAT ONE JYTHON UPDATES TO 2.2, IT HAS A
+ #CHANCE OF WORKING AGAIN.
+
+ import java
+ import jarray
+
+ def createByteArraySequence(seq):
+ if isinstance(seq, type("")): #If it's a string, convert
+ seq = [ord(c) for c in seq]
+ return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed
+ def createByteArrayZeros(howMany):
+ return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed
+ def concatArrays(a1, a2):
+ l = list(a1)+list(a2)
+ return createByteArraySequence(l)
+
+ #WAY TOO SLOW - MUST BE REPLACED------------
+ def bytesToString(bytes):
+ return "".join([chr(b) for b in bytes])
+
+ def stringToBytes(s):
+ bytes = createByteArrayZeros(len(s))
+ for count, c in enumerate(s):
+ bytes[count] = ord(c)
+ return bytes
+ #WAY TOO SLOW - MUST BE REPLACED------------
+
+ def numBits(n):
+ if n==0:
+ return 0
+ n= 1L * n; #convert to long, if it isn't already
+ return n.__tojava__(java.math.BigInteger).bitLength()
+
+ #Adjust the string to an array of bytes
+ def stringToJavaByteArray(s):
+ bytes = jarray.zeros(len(s), 'b')
+ for count, c in enumerate(s):
+ x = ord(c)
+ if x >= 128: x -= 256
+ bytes[count] = x
+ return bytes
+
+ BaseException = java.lang.Exception
+ import sys
+ import traceback
+ def formatExceptionTrace(e):
+ newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
+ return newStr \ No newline at end of file
diff --git a/src/lib/tlslite/utils/cryptomath.py b/src/lib/tlslite/utils/cryptomath.py
new file mode 100755
index 000000000..51d6dff7c
--- /dev/null
+++ b/src/lib/tlslite/utils/cryptomath.py
@@ -0,0 +1,400 @@
+"""cryptomath module
+
+This module has basic math/crypto code."""
+
+import os
+import math
+import base64
+import binascii
+import sha
+
+from compat import *
+
+
+# **************************************************************************
+# Load Optional Modules
+# **************************************************************************
+
+# Try to load M2Crypto/OpenSSL
+try:
+ from M2Crypto import m2
+ m2cryptoLoaded = True
+
+except ImportError:
+ m2cryptoLoaded = False
+
+
+# Try to load cryptlib
+try:
+ import cryptlib_py
+ try:
+ cryptlib_py.cryptInit()
+ except cryptlib_py.CryptException, e:
+ #If tlslite and cryptoIDlib are both present,
+ #they might each try to re-initialize this,
+ #so we're tolerant of that.
+ if e[0] != cryptlib_py.CRYPT_ERROR_INITED:
+ raise
+ cryptlibpyLoaded = True
+
+except ImportError:
+ cryptlibpyLoaded = False
+
+#Try to load GMPY
+try:
+ import gmpy
+ gmpyLoaded = True
+except ImportError:
+ gmpyLoaded = False
+
+#Try to load pycrypto
+try:
+ import Crypto.Cipher.AES
+ pycryptoLoaded = True
+except ImportError:
+ pycryptoLoaded = False
+
+
+# **************************************************************************
+# PRNG Functions
+# **************************************************************************
+
+# Get os.urandom PRNG
+try:
+ os.urandom(1)
+ def getRandomBytes(howMany):
+ return stringToBytes(os.urandom(howMany))
+ prngName = "os.urandom"
+
+except:
+ # Else get cryptlib PRNG
+ if cryptlibpyLoaded:
+ def getRandomBytes(howMany):
+ randomKey = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED,
+ cryptlib_py.CRYPT_ALGO_AES)
+ cryptlib_py.cryptSetAttribute(randomKey,
+ cryptlib_py.CRYPT_CTXINFO_MODE,
+ cryptlib_py.CRYPT_MODE_OFB)
+ cryptlib_py.cryptGenerateKey(randomKey)
+ bytes = createByteArrayZeros(howMany)
+ cryptlib_py.cryptEncrypt(randomKey, bytes)
+ return bytes
+ prngName = "cryptlib"
+
+ else:
+ #Else get UNIX /dev/urandom PRNG
+ try:
+ devRandomFile = open("/dev/urandom", "rb")
+ def getRandomBytes(howMany):
+ return stringToBytes(devRandomFile.read(howMany))
+ prngName = "/dev/urandom"
+ except IOError:
+ #Else get Win32 CryptoAPI PRNG
+ try:
+ import win32prng
+ def getRandomBytes(howMany):
+ s = win32prng.getRandomBytes(howMany)
+ if len(s) != howMany:
+ raise AssertionError()
+ return stringToBytes(s)
+ prngName ="CryptoAPI"
+ except ImportError:
+ #Else no PRNG :-(
+ def getRandomBytes(howMany):
+ raise NotImplementedError("No Random Number Generator "\
+ "available.")
+ prngName = "None"
+
+# **************************************************************************
+# Converter Functions
+# **************************************************************************
+
+def bytesToNumber(bytes):
+ total = 0L
+ multiplier = 1L
+ for count in range(len(bytes)-1, -1, -1):
+ byte = bytes[count]
+ total += multiplier * byte
+ multiplier *= 256
+ return total
+
+def numberToBytes(n):
+ howManyBytes = numBytes(n)
+ bytes = createByteArrayZeros(howManyBytes)
+ for count in range(howManyBytes-1, -1, -1):
+ bytes[count] = int(n % 256)
+ n >>= 8
+ return bytes
+
+def bytesToBase64(bytes):
+ s = bytesToString(bytes)
+ return stringToBase64(s)
+
+def base64ToBytes(s):
+ s = base64ToString(s)
+ return stringToBytes(s)
+
+def numberToBase64(n):
+ bytes = numberToBytes(n)
+ return bytesToBase64(bytes)
+
+def base64ToNumber(s):
+ bytes = base64ToBytes(s)
+ return bytesToNumber(bytes)
+
+def stringToNumber(s):
+ bytes = stringToBytes(s)
+ return bytesToNumber(bytes)
+
+def numberToString(s):
+ bytes = numberToBytes(s)
+ return bytesToString(bytes)
+
+def base64ToString(s):
+ try:
+ return base64.decodestring(s)
+ except binascii.Error, e:
+ raise SyntaxError(e)
+ except binascii.Incomplete, e:
+ raise SyntaxError(e)
+
+def stringToBase64(s):
+ return base64.encodestring(s).replace("\n", "")
+
+def mpiToNumber(mpi): #mpi is an openssl-format bignum string
+ if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number
+ raise AssertionError()
+ bytes = stringToBytes(mpi[4:])
+ return bytesToNumber(bytes)
+
+def numberToMPI(n):
+ bytes = numberToBytes(n)
+ ext = 0
+ #If the high-order bit is going to be set,
+ #add an extra byte of zeros
+ if (numBits(n) & 0x7)==0:
+ ext = 1
+ length = numBytes(n) + ext
+ bytes = concatArrays(createByteArrayZeros(4+ext), bytes)
+ bytes[0] = (length >> 24) & 0xFF
+ bytes[1] = (length >> 16) & 0xFF
+ bytes[2] = (length >> 8) & 0xFF
+ bytes[3] = length & 0xFF
+ return bytesToString(bytes)
+
+
+
+# **************************************************************************
+# Misc. Utility Functions
+# **************************************************************************
+
+def numBytes(n):
+ if n==0:
+ return 0
+ bits = numBits(n)
+ return int(math.ceil(bits / 8.0))
+
+def hashAndBase64(s):
+ return stringToBase64(sha.sha(s).digest())
+
+def getBase64Nonce(numChars=22): #defaults to an 132 bit nonce
+ bytes = getRandomBytes(numChars)
+ bytesStr = "".join([chr(b) for b in bytes])
+ return stringToBase64(bytesStr)[:numChars]
+
+
+# **************************************************************************
+# Big Number Math
+# **************************************************************************
+
+def getRandomNumber(low, high):
+ if low >= high:
+ raise AssertionError()
+ howManyBits = numBits(high)
+ howManyBytes = numBytes(high)
+ lastBits = howManyBits % 8
+ while 1:
+ bytes = getRandomBytes(howManyBytes)
+ if lastBits:
+ bytes[0] = bytes[0] % (1 << lastBits)
+ n = bytesToNumber(bytes)
+ if n >= low and n < high:
+ return n
+
+def gcd(a,b):
+ a, b = max(a,b), min(a,b)
+ while b:
+ a, b = b, a % b
+ return a
+
+def lcm(a, b):
+ #This will break when python division changes, but we can't use // cause
+ #of Jython
+ return (a * b) / gcd(a, b)
+
+#Returns inverse of a mod b, zero if none
+#Uses Extended Euclidean Algorithm
+def invMod(a, b):
+ c, d = a, b
+ uc, ud = 1, 0
+ while c != 0:
+ #This will break when python division changes, but we can't use //
+ #cause of Jython
+ q = d / c
+ c, d = d-(q*c), c
+ uc, ud = ud - (q * uc), uc
+ if d == 1:
+ return ud % b
+ return 0
+
+
+if gmpyLoaded:
+ def powMod(base, power, modulus):
+ base = gmpy.mpz(base)
+ power = gmpy.mpz(power)
+ modulus = gmpy.mpz(modulus)
+ result = pow(base, power, modulus)
+ return long(result)
+
+else:
+ #Copied from Bryan G. Olson's post to comp.lang.python
+ #Does left-to-right instead of pow()'s right-to-left,
+ #thus about 30% faster than the python built-in with small bases
+ def powMod(base, power, modulus):
+ nBitScan = 5
+
+ """ Return base**power mod modulus, using multi bit scanning
+ with nBitScan bits at a time."""
+
+ #TREV - Added support for negative exponents
+ negativeResult = False
+ if (power < 0):
+ power *= -1
+ negativeResult = True
+
+ exp2 = 2**nBitScan
+ mask = exp2 - 1
+
+ # Break power into a list of digits of nBitScan bits.
+ # The list is recursive so easy to read in reverse direction.
+ nibbles = None
+ while power:
+ nibbles = int(power & mask), nibbles
+ power = power >> nBitScan
+
+ # Make a table of powers of base up to 2**nBitScan - 1
+ lowPowers = [1]
+ for i in xrange(1, exp2):
+ lowPowers.append((lowPowers[i-1] * base) % modulus)
+
+ # To exponentiate by the first nibble, look it up in the table
+ nib, nibbles = nibbles
+ prod = lowPowers[nib]
+
+ # For the rest, square nBitScan times, then multiply by
+ # base^nibble
+ while nibbles:
+ nib, nibbles = nibbles
+ for i in xrange(nBitScan):
+ prod = (prod * prod) % modulus
+ if nib: prod = (prod * lowPowers[nib]) % modulus
+
+ #TREV - Added support for negative exponents
+ if negativeResult:
+ prodInv = invMod(prod, modulus)
+ #Check to make sure the inverse is correct
+ if (prod * prodInv) % modulus != 1:
+ raise AssertionError()
+ return prodInv
+ return prod
+
+
+#Pre-calculate a sieve of the ~100 primes < 1000:
+def makeSieve(n):
+ sieve = range(n)
+ for count in range(2, int(math.sqrt(n))):
+ if sieve[count] == 0:
+ continue
+ x = sieve[count] * 2
+ while x < len(sieve):
+ sieve[x] = 0
+ x += sieve[count]
+ sieve = [x for x in sieve[2:] if x]
+ return sieve
+
+sieve = makeSieve(1000)
+
+def isPrime(n, iterations=5, display=False):
+ #Trial division with sieve
+ for x in sieve:
+ if x >= n: return True
+ if n % x == 0: return False
+ #Passed trial division, proceed to Rabin-Miller
+ #Rabin-Miller implemented per Ferguson & Schneier
+ #Compute s, t for Rabin-Miller
+ if display: print "*",
+ s, t = n-1, 0
+ while s % 2 == 0:
+ s, t = s/2, t+1
+ #Repeat Rabin-Miller x times
+ a = 2 #Use 2 as a base for first iteration speedup, per HAC
+ for count in range(iterations):
+ v = powMod(a, s, n)
+ if v==1:
+ continue
+ i = 0
+ while v != n-1:
+ if i == t-1:
+ return False
+ else:
+ v, i = powMod(v, 2, n), i+1
+ a = getRandomNumber(2, n)
+ return True
+
+def getRandomPrime(bits, display=False):
+ if bits < 10:
+ raise AssertionError()
+ #The 1.5 ensures the 2 MSBs are set
+ #Thus, when used for p,q in RSA, n will have its MSB set
+ #
+ #Since 30 is lcm(2,3,5), we'll set our test numbers to
+ #29 % 30 and keep them there
+ low = (2L ** (bits-1)) * 3/2
+ high = 2L ** bits - 30
+ p = getRandomNumber(low, high)
+ p += 29 - (p % 30)
+ while 1:
+ if display: print ".",
+ p += 30
+ if p >= high:
+ p = getRandomNumber(low, high)
+ p += 29 - (p % 30)
+ if isPrime(p, display=display):
+ return p
+
+#Unused at the moment...
+def getRandomSafePrime(bits, display=False):
+ if bits < 10:
+ raise AssertionError()
+ #The 1.5 ensures the 2 MSBs are set
+ #Thus, when used for p,q in RSA, n will have its MSB set
+ #
+ #Since 30 is lcm(2,3,5), we'll set our test numbers to
+ #29 % 30 and keep them there
+ low = (2 ** (bits-2)) * 3/2
+ high = (2 ** (bits-1)) - 30
+ q = getRandomNumber(low, high)
+ q += 29 - (q % 30)
+ while 1:
+ if display: print ".",
+ q += 30
+ if (q >= high):
+ q = getRandomNumber(low, high)
+ q += 29 - (q % 30)
+ #Ideas from Tom Wu's SRP code
+ #Do trial division on p and q before Rabin-Miller
+ if isPrime(q, 0, display=display):
+ p = (2 * q) + 1
+ if isPrime(p, display=display):
+ if isPrime(q, display=display):
+ return p
diff --git a/src/lib/tlslite/utils/dateFuncs.py b/src/lib/tlslite/utils/dateFuncs.py
new file mode 100755
index 000000000..38812ebf8
--- /dev/null
+++ b/src/lib/tlslite/utils/dateFuncs.py
@@ -0,0 +1,75 @@
+
+import os
+
+#Functions for manipulating datetime objects
+#CCYY-MM-DDThh:mm:ssZ
+def parseDateClass(s):
+ year, month, day = s.split("-")
+ day, tail = day[:2], day[2:]
+ hour, minute, second = tail[1:].split(":")
+ second = second[:2]
+ year, month, day = int(year), int(month), int(day)
+ hour, minute, second = int(hour), int(minute), int(second)
+ return createDateClass(year, month, day, hour, minute, second)
+
+
+if os.name != "java":
+ from datetime import datetime, timedelta
+
+ #Helper functions for working with a date/time class
+ def createDateClass(year, month, day, hour, minute, second):
+ return datetime(year, month, day, hour, minute, second)
+
+ def printDateClass(d):
+ #Split off fractional seconds, append 'Z'
+ return d.isoformat().split(".")[0]+"Z"
+
+ def getNow():
+ return datetime.utcnow()
+
+ def getHoursFromNow(hours):
+ return datetime.utcnow() + timedelta(hours=hours)
+
+ def getMinutesFromNow(minutes):
+ return datetime.utcnow() + timedelta(minutes=minutes)
+
+ def isDateClassExpired(d):
+ return d < datetime.utcnow()
+
+ def isDateClassBefore(d1, d2):
+ return d1 < d2
+
+else:
+ #Jython 2.1 is missing lots of python 2.3 stuff,
+ #which we have to emulate here:
+ import java
+ import jarray
+
+ def createDateClass(year, month, day, hour, minute, second):
+ c = java.util.Calendar.getInstance()
+ c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
+ c.set(year, month-1, day, hour, minute, second)
+ return c
+
+ def printDateClass(d):
+ return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \
+ (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \
+ d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND))
+
+ def getNow():
+ c = java.util.Calendar.getInstance()
+ c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
+ c.get(c.HOUR) #force refresh?
+ return c
+
+ def getHoursFromNow(hours):
+ d = getNow()
+ d.add(d.HOUR, hours)
+ return d
+
+ def isDateClassExpired(d):
+ n = getNow()
+ return d.before(n)
+
+ def isDateClassBefore(d1, d2):
+ return d1.before(d2)
diff --git a/src/lib/tlslite/utils/entropy.c b/src/lib/tlslite/utils/entropy.c
new file mode 100755
index 000000000..c627794d2
--- /dev/null
+++ b/src/lib/tlslite/utils/entropy.c
@@ -0,0 +1,173 @@
+
+#include "Python.h"
+
+
+#ifdef MS_WINDOWS
+
+/* The following #define is not needed on VC6 with the Platform SDK, and it
+may not be needed on VC7, I'm not sure. I don't think it hurts anything.*/
+#define _WIN32_WINNT 0x0400
+
+#include <windows.h>
+
+
+typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
+ LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
+ DWORD dwFlags );
+typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
+ BYTE *pbBuffer );
+typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv,\
+ DWORD dwFlags);
+
+
+static PyObject* entropy(PyObject *self, PyObject *args)
+{
+ int howMany = 0;
+ HINSTANCE hAdvAPI32 = NULL;
+ CRYPTACQUIRECONTEXTA pCryptAcquireContextA = NULL;
+ CRYPTGENRANDOM pCryptGenRandom = NULL;
+ CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
+ HCRYPTPROV hCryptProv = 0;
+ unsigned char* bytes = NULL;
+ PyObject* returnVal = NULL;
+
+
+ /* Read arguments */
+ if (!PyArg_ParseTuple(args, "i", &howMany))
+ return(NULL);
+
+ /* Obtain handle to the DLL containing CryptoAPI
+ This should not fail */
+ if( (hAdvAPI32 = GetModuleHandle("advapi32.dll")) == NULL) {
+ PyErr_Format(PyExc_SystemError,
+ "Advapi32.dll not found");
+ return NULL;
+ }
+
+ /* Obtain pointers to the CryptoAPI functions
+ This will fail on some early version of Win95 */
+ pCryptAcquireContextA = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32,\
+ "CryptAcquireContextA");
+ pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\
+ "CryptGenRandom");
+ pCryptReleaseContext = (CRYPTRELEASECONTEXT) GetProcAddress(hAdvAPI32,\
+ "CryptReleaseContext");
+ if (pCryptAcquireContextA == NULL || pCryptGenRandom == NULL ||
+ pCryptReleaseContext == NULL) {
+ PyErr_Format(PyExc_NotImplementedError,
+ "CryptoAPI not available on this version of Windows");
+ return NULL;
+ }
+
+ /* Allocate bytes */
+ if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL)
+ return PyErr_NoMemory();
+
+
+ /* Acquire context */
+ if(!pCryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ PyErr_Format(PyExc_SystemError,
+ "CryptAcquireContext failed, error %d", GetLastError());
+ PyMem_Free(bytes);
+ return NULL;
+ }
+
+ /* Get random data */
+ if(!pCryptGenRandom(hCryptProv, howMany, bytes)) {
+ PyErr_Format(PyExc_SystemError,
+ "CryptGenRandom failed, error %d", GetLastError());
+ PyMem_Free(bytes);
+ CryptReleaseContext(hCryptProv, 0);
+ return NULL;
+ }
+
+ /* Build return value */
+ returnVal = Py_BuildValue("s#", bytes, howMany);
+ PyMem_Free(bytes);
+
+ /* Release context */
+ if (!pCryptReleaseContext(hCryptProv, 0)) {
+ PyErr_Format(PyExc_SystemError,
+ "CryptReleaseContext failed, error %d", GetLastError());
+ return NULL;
+ }
+
+ return returnVal;
+}
+
+#elif defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL_H)
+
+#include <unistd.h>
+#include <fcntl.h>
+
+static PyObject* entropy(PyObject *self, PyObject *args)
+{
+ int howMany;
+ int fd;
+ unsigned char* bytes = NULL;
+ PyObject* returnVal = NULL;
+
+
+ /* Read arguments */
+ if (!PyArg_ParseTuple(args, "i", &howMany))
+ return(NULL);
+
+ /* Allocate bytes */
+ if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL)
+ return PyErr_NoMemory();
+
+ /* Open device */
+ if ((fd = open("/dev/urandom", O_RDONLY, 0)) == -1) {
+ PyErr_Format(PyExc_NotImplementedError,
+ "No entropy source found");
+ PyMem_Free(bytes);
+ return NULL;
+ }
+
+ /* Get random data */
+ if (read(fd, bytes, howMany) < howMany) {
+ PyErr_Format(PyExc_SystemError,
+ "Reading from /dev/urandom failed");
+ PyMem_Free(bytes);
+ close(fd);
+ return NULL;
+ }
+
+ /* Build return value */
+ returnVal = Py_BuildValue("s#", bytes, howMany);
+ PyMem_Free(bytes);
+
+ /* Close device */
+ close(fd);
+
+ return returnVal;
+}
+
+#else
+
+static PyObject* entropy(PyObject *self, PyObject *args)
+{
+ PyErr_Format(PyExc_NotImplementedError,
+ "Function not supported");
+ return NULL;
+}
+
+#endif
+
+
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef entropy_functions[] = {
+ {"entropy", (PyCFunction)entropy, METH_VARARGS, "Return a string of random bytes produced by a platform-specific\nentropy source."},
+ {NULL, NULL} /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+PyMODINIT_FUNC initentropy(void)
+{
+ Py_InitModule("entropy", entropy_functions);
+} \ No newline at end of file
diff --git a/src/lib/tlslite/utils/hmac.py b/src/lib/tlslite/utils/hmac.py
new file mode 100755
index 000000000..fe8feec21
--- /dev/null
+++ b/src/lib/tlslite/utils/hmac.py
@@ -0,0 +1,104 @@
+"""HMAC (Keyed-Hashing for Message Authentication) Python module.
+
+Implements the HMAC algorithm as described by RFC 2104.
+
+(This file is modified from the standard library version to do faster
+copying)
+"""
+
+def _strxor(s1, s2):
+ """Utility method. XOR the two strings s1 and s2 (must have same length).
+ """
+ return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2))
+
+# The size of the digests returned by HMAC depends on the underlying
+# hashing module used.
+digest_size = None
+
+class HMAC:
+ """RFC2104 HMAC class.
+
+ This supports the API for Cryptographic Hash Functions (PEP 247).
+ """
+
+ def __init__(self, key, msg = None, digestmod = None):
+ """Create a new HMAC object.
+
+ key: key for the keyed hash object.
+ msg: Initial input for the hash, if provided.
+ digestmod: A module supporting PEP 247. Defaults to the md5 module.
+ """
+ if digestmod is None:
+ import md5
+ digestmod = md5
+
+ if key == None: #TREVNEW - for faster copying
+ return #TREVNEW
+
+ self.digestmod = digestmod
+ self.outer = digestmod.new()
+ self.inner = digestmod.new()
+ self.digest_size = digestmod.digest_size
+
+ blocksize = 64
+ ipad = "\x36" * blocksize
+ opad = "\x5C" * blocksize
+
+ if len(key) > blocksize:
+ key = digestmod.new(key).digest()
+
+ key = key + chr(0) * (blocksize - len(key))
+ self.outer.update(_strxor(key, opad))
+ self.inner.update(_strxor(key, ipad))
+ if msg is not None:
+ self.update(msg)
+
+## def clear(self):
+## raise NotImplementedError, "clear() method not available in HMAC."
+
+ def update(self, msg):
+ """Update this hashing object with the string msg.
+ """
+ self.inner.update(msg)
+
+ def copy(self):
+ """Return a separate copy of this hashing object.
+
+ An update to this copy won't affect the original object.
+ """
+ other = HMAC(None) #TREVNEW - for faster copying
+ other.digest_size = self.digest_size #TREVNEW
+ other.digestmod = self.digestmod
+ other.inner = self.inner.copy()
+ other.outer = self.outer.copy()
+ return other
+
+ def digest(self):
+ """Return the hash value of this hashing object.
+
+ This returns a string containing 8-bit data. The object is
+ not altered in any way by this function; you can continue
+ updating the object after calling this function.
+ """
+ h = self.outer.copy()
+ h.update(self.inner.digest())
+ return h.digest()
+
+ def hexdigest(self):
+ """Like digest(), but returns a string of hexadecimal digits instead.
+ """
+ return "".join([hex(ord(x))[2:].zfill(2)
+ for x in tuple(self.digest())])
+
+def new(key, msg = None, digestmod = None):
+ """Create a new hashing object and return it.
+
+ key: The starting key for the hash.
+ msg: if available, will immediately be hashed into the object's starting
+ state.
+
+ You can now feed arbitrary strings into the object using its update()
+ method, and can ask for the hash value at any time by calling its digest()
+ method.
+ """
+ return HMAC(key, msg, digestmod)
diff --git a/src/lib/tlslite/utils/jython_compat.py b/src/lib/tlslite/utils/jython_compat.py
new file mode 100755
index 000000000..1245183a9
--- /dev/null
+++ b/src/lib/tlslite/utils/jython_compat.py
@@ -0,0 +1,195 @@
+"""Miscellaneous functions to mask Python/Jython differences."""
+
+import os
+import sha
+
+if os.name != "java":
+ BaseException = Exception
+
+ from sets import Set
+ import array
+ import math
+
+ def createByteArraySequence(seq):
+ return array.array('B', seq)
+ def createByteArrayZeros(howMany):
+ return array.array('B', [0] * howMany)
+ def concatArrays(a1, a2):
+ return a1+a2
+
+ def bytesToString(bytes):
+ return bytes.tostring()
+
+ def stringToBytes(s):
+ bytes = createByteArrayZeros(0)
+ bytes.fromstring(s)
+ return bytes
+
+ def numBits(n):
+ if n==0:
+ return 0
+ return int(math.floor(math.log(n, 2))+1)
+
+ class CertChainBase: pass
+ class SelfTestBase: pass
+ class ReportFuncBase: pass
+
+ #Helper functions for working with sets (from Python 2.3)
+ def iterSet(set):
+ return iter(set)
+
+ def getListFromSet(set):
+ return list(set)
+
+ #Factory function for getting a SHA1 object
+ def getSHA1(s):
+ return sha.sha(s)
+
+ import sys
+ import traceback
+
+ def formatExceptionTrace(e):
+ newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
+ return newStr
+
+else:
+ #Jython 2.1 is missing lots of python 2.3 stuff,
+ #which we have to emulate here:
+ import java
+ import jarray
+
+ BaseException = java.lang.Exception
+
+ def createByteArraySequence(seq):
+ if isinstance(seq, type("")): #If it's a string, convert
+ seq = [ord(c) for c in seq]
+ return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed
+ def createByteArrayZeros(howMany):
+ return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed
+ def concatArrays(a1, a2):
+ l = list(a1)+list(a2)
+ return createByteArraySequence(l)
+
+ #WAY TOO SLOW - MUST BE REPLACED------------
+ def bytesToString(bytes):
+ return "".join([chr(b) for b in bytes])
+
+ def stringToBytes(s):
+ bytes = createByteArrayZeros(len(s))
+ for count, c in enumerate(s):
+ bytes[count] = ord(c)
+ return bytes
+ #WAY TOO SLOW - MUST BE REPLACED------------
+
+ def numBits(n):
+ if n==0:
+ return 0
+ n= 1L * n; #convert to long, if it isn't already
+ return n.__tojava__(java.math.BigInteger).bitLength()
+
+ #This properly creates static methods for Jython
+ class staticmethod:
+ def __init__(self, anycallable): self.__call__ = anycallable
+
+ #Properties are not supported for Jython
+ class property:
+ def __init__(self, anycallable): pass
+
+ #True and False have to be specially defined
+ False = 0
+ True = 1
+
+ class StopIteration(Exception): pass
+
+ def enumerate(collection):
+ return zip(range(len(collection)), collection)
+
+ class Set:
+ def __init__(self, seq=None):
+ self.values = {}
+ if seq:
+ for e in seq:
+ self.values[e] = None
+
+ def add(self, e):
+ self.values[e] = None
+
+ def discard(self, e):
+ if e in self.values.keys():
+ del(self.values[e])
+
+ def union(self, s):
+ ret = Set()
+ for e in self.values.keys():
+ ret.values[e] = None
+ for e in s.values.keys():
+ ret.values[e] = None
+ return ret
+
+ def issubset(self, other):
+ for e in self.values.keys():
+ if e not in other.values.keys():
+ return False
+ return True
+
+ def __nonzero__( self):
+ return len(self.values.keys())
+
+ def __contains__(self, e):
+ return e in self.values.keys()
+
+ def iterSet(set):
+ return set.values.keys()
+
+ def getListFromSet(set):
+ return set.values.keys()
+
+ """
+ class JCE_SHA1:
+ def __init__(self, s=None):
+ self.md = java.security.MessageDigest.getInstance("SHA1")
+ if s:
+ self.update(s)
+
+ def update(self, s):
+ self.md.update(s)
+
+ def copy(self):
+ sha1 = JCE_SHA1()
+ sha1.md = self.md.clone()
+ return sha1
+
+ def digest(self):
+ digest = self.md.digest()
+ bytes = jarray.zeros(20, 'h')
+ for count in xrange(20):
+ x = digest[count]
+ if x < 0: x += 256
+ bytes[count] = x
+ return bytes
+ """
+
+ #Factory function for getting a SHA1 object
+ #The JCE_SHA1 class is way too slow...
+ #the sha.sha object we use instead is broken in the jython 2.1
+ #release, and needs to be patched
+ def getSHA1(s):
+ #return JCE_SHA1(s)
+ return sha.sha(s)
+
+
+ #Adjust the string to an array of bytes
+ def stringToJavaByteArray(s):
+ bytes = jarray.zeros(len(s), 'b')
+ for count, c in enumerate(s):
+ x = ord(c)
+ if x >= 128: x -= 256
+ bytes[count] = x
+ return bytes
+
+ import sys
+ import traceback
+
+ def formatExceptionTrace(e):
+ newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
+ return newStr
diff --git a/src/lib/tlslite/utils/keyfactory.py b/src/lib/tlslite/utils/keyfactory.py
new file mode 100755
index 000000000..5005af7f5
--- /dev/null
+++ b/src/lib/tlslite/utils/keyfactory.py
@@ -0,0 +1,243 @@
+"""Factory functions for asymmetric cryptography.
+@sort: generateRSAKey, parseXMLKey, parsePEMKey, parseAsPublicKey,
+parseAsPrivateKey
+"""
+
+from compat import *
+
+from RSAKey import RSAKey
+from Python_RSAKey import Python_RSAKey
+import cryptomath
+
+if cryptomath.m2cryptoLoaded:
+ from OpenSSL_RSAKey import OpenSSL_RSAKey
+
+if cryptomath.pycryptoLoaded:
+ from PyCrypto_RSAKey import PyCrypto_RSAKey
+
+# **************************************************************************
+# Factory Functions for RSA Keys
+# **************************************************************************
+
+def generateRSAKey(bits, implementations=["openssl", "python"]):
+ """Generate an RSA key with the specified bit length.
+
+ @type bits: int
+ @param bits: Desired bit length of the new key's modulus.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: A new RSA private key.
+ """
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_RSAKey.generate(bits)
+ elif implementation == "python":
+ return Python_RSAKey.generate(bits)
+ raise ValueError("No acceptable implementations")
+
+def parseXMLKey(s, private=False, public=False, implementations=["python"]):
+ """Parse an XML-format key.
+
+ The XML format used here is specific to tlslite and cryptoIDlib. The
+ format can store the public component of a key, or the public and
+ private components. For example::
+
+ <publicKey xmlns="http://trevp.net/rsa">
+ <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
+ <e>Aw==</e>
+ </publicKey>
+
+ <privateKey xmlns="http://trevp.net/rsa">
+ <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
+ <e>Aw==</e>
+ <d>JZ0TIgUxWXmL8KJ0VqyG1V0J3ern9pqIoB0xmy...
+ <p>5PreIj6z6ldIGL1V4+1C36dQFHNCQHJvW52GXc...
+ <q>/E/wDit8YXPCxx126zTq2ilQ3IcW54NJYyNjiZ...
+ <dP>mKc+wX8inDowEH45Qp4slRo1YveBgExKPROu6...
+ <dQ>qDVKtBz9lk0shL5PR3ickXDgkwS576zbl2ztB...
+ <qInv>j6E8EA7dNsTImaXexAmLA1DoeArsYeFAInr...
+ </privateKey>
+
+ @type s: str
+ @param s: A string containing an XML public or private key.
+
+ @type private: bool
+ @param private: If True, a L{SyntaxError} will be raised if the private
+ key component is not present.
+
+ @type public: bool
+ @param public: If True, the private key component (if present) will be
+ discarded, so this function will always return a public key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ for implementation in implementations:
+ if implementation == "python":
+ key = Python_RSAKey.parseXML(s)
+ break
+ else:
+ raise ValueError("No acceptable implementations")
+
+ return _parseKeyHelper(key, private, public)
+
+#Parse as an OpenSSL or Python key
+def parsePEMKey(s, private=False, public=False, passwordCallback=None,
+ implementations=["openssl", "python"]):
+ """Parse a PEM-format key.
+
+ The PEM format is used by OpenSSL and other tools. The
+ format is typically used to store both the public and private
+ components of a key. For example::
+
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+
+ dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH
+ dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB
+ AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc
+ esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO
+ gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl
+ aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV
+ VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV
+ CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv
+ i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP
+ wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG
+ 6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH
+ h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe
+ -----END RSA PRIVATE KEY-----
+
+ To generate a key like this with OpenSSL, run::
+
+ openssl genrsa 2048 > key.pem
+
+ This format also supports password-encrypted private keys. TLS
+ Lite can only handle password-encrypted private keys when OpenSSL
+ and M2Crypto are installed. In this case, passwordCallback will be
+ invoked to query the user for the password.
+
+ @type s: str
+ @param s: A string containing a PEM-encoded public or private key.
+
+ @type private: bool
+ @param private: If True, a L{SyntaxError} will be raised if the
+ private key component is not present.
+
+ @type public: bool
+ @param public: If True, the private key component (if present) will
+ be discarded, so this function will always return a public key.
+
+ @type passwordCallback: callable
+ @param passwordCallback: This function will be called, with no
+ arguments, if the PEM-encoded private key is password-encrypted.
+ The callback should return the password string. If the password is
+ incorrect, SyntaxError will be raised. If no callback is passed
+ and the key is password-encrypted, a prompt will be displayed at
+ the console.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ key = OpenSSL_RSAKey.parse(s, passwordCallback)
+ break
+ elif implementation == "python":
+ key = Python_RSAKey.parsePEM(s)
+ break
+ else:
+ raise ValueError("No acceptable implementations")
+
+ return _parseKeyHelper(key, private, public)
+
+
+def _parseKeyHelper(key, private, public):
+ if private:
+ if not key.hasPrivateKey():
+ raise SyntaxError("Not a private key!")
+
+ if public:
+ return _createPublicKey(key)
+
+ if private:
+ if hasattr(key, "d"):
+ return _createPrivateKey(key)
+ else:
+ return key
+
+ return key
+
+def parseAsPublicKey(s):
+ """Parse an XML or PEM-formatted public key.
+
+ @type s: str
+ @param s: A string containing an XML or PEM-encoded public or private key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA public key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ try:
+ return parsePEMKey(s, public=True)
+ except:
+ return parseXMLKey(s, public=True)
+
+def parsePrivateKey(s):
+ """Parse an XML or PEM-formatted private key.
+
+ @type s: str
+ @param s: A string containing an XML or PEM-encoded private key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA private key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ try:
+ return parsePEMKey(s, private=True)
+ except:
+ return parseXMLKey(s, private=True)
+
+def _createPublicKey(key):
+ """
+ Create a new public key. Discard any private component,
+ and return the most efficient key possible.
+ """
+ if not isinstance(key, RSAKey):
+ raise AssertionError()
+ return _createPublicRSAKey(key.n, key.e)
+
+def _createPrivateKey(key):
+ """
+ Create a new private key. Return the most efficient key possible.
+ """
+ if not isinstance(key, RSAKey):
+ raise AssertionError()
+ if not key.hasPrivateKey():
+ raise AssertionError()
+ return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP,
+ key.dQ, key.qInv)
+
+def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto",
+ "python"]):
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_RSAKey(n, e)
+ elif implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_RSAKey(n, e)
+ elif implementation == "python":
+ return Python_RSAKey(n, e)
+ raise ValueError("No acceptable implementations")
+
+def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv,
+ implementations = ["pycrypto", "python"]):
+ for implementation in implementations:
+ if implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ elif implementation == "python":
+ return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ raise ValueError("No acceptable implementations")
diff --git a/src/lib/tlslite/utils/rijndael.py b/src/lib/tlslite/utils/rijndael.py
new file mode 100755
index 000000000..cb2f54734
--- /dev/null
+++ b/src/lib/tlslite/utils/rijndael.py
@@ -0,0 +1,392 @@
+"""
+A pure python (slow) implementation of rijndael with a decent interface
+
+To include -
+
+from rijndael import rijndael
+
+To do a key setup -
+
+r = rijndael(key, block_size = 16)
+
+key must be a string of length 16, 24, or 32
+blocksize must be 16, 24, or 32. Default is 16
+
+To use -
+
+ciphertext = r.encrypt(plaintext)
+plaintext = r.decrypt(ciphertext)
+
+If any strings are of the wrong length a ValueError is thrown
+"""
+
+# ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
+# this code is public domain, unless someone makes
+# an intellectual property claim against the reference
+# code, in which case it can be made public domain by
+# deleting all the comments and renaming all the variables
+
+import copy
+import string
+
+
+
+#-----------------------
+#TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN
+#2.4.....
+import os
+if os.name != "java":
+ import exceptions
+ if hasattr(exceptions, "FutureWarning"):
+ import warnings
+ warnings.filterwarnings("ignore", category=FutureWarning, append=1)
+#-----------------------
+
+
+
+shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]],
+ [[0, 0], [1, 5], [2, 4], [3, 3]],
+ [[0, 0], [1, 7], [3, 5], [4, 4]]]
+
+# [keysize][block_size]
+num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
+
+A = [[1, 1, 1, 1, 1, 0, 0, 0],
+ [0, 1, 1, 1, 1, 1, 0, 0],
+ [0, 0, 1, 1, 1, 1, 1, 0],
+ [0, 0, 0, 1, 1, 1, 1, 1],
+ [1, 0, 0, 0, 1, 1, 1, 1],
+ [1, 1, 0, 0, 0, 1, 1, 1],
+ [1, 1, 1, 0, 0, 0, 1, 1],
+ [1, 1, 1, 1, 0, 0, 0, 1]]
+
+# produce log and alog tables, needed for multiplying in the
+# field GF(2^m) (generator = 3)
+alog = [1]
+for i in xrange(255):
+ j = (alog[-1] << 1) ^ alog[-1]
+ if j & 0x100 != 0:
+ j ^= 0x11B
+ alog.append(j)
+
+log = [0] * 256
+for i in xrange(1, 255):
+ log[alog[i]] = i
+
+# multiply two elements of GF(2^m)
+def mul(a, b):
+ if a == 0 or b == 0:
+ return 0
+ return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
+
+# substitution box based on F^{-1}(x)
+box = [[0] * 8 for i in xrange(256)]
+box[1][7] = 1
+for i in xrange(2, 256):
+ j = alog[255 - log[i]]
+ for t in xrange(8):
+ box[i][t] = (j >> (7 - t)) & 0x01
+
+B = [0, 1, 1, 0, 0, 0, 1, 1]
+
+# affine transform: box[i] <- B + A*box[i]
+cox = [[0] * 8 for i in xrange(256)]
+for i in xrange(256):
+ for t in xrange(8):
+ cox[i][t] = B[t]
+ for j in xrange(8):
+ cox[i][t] ^= A[t][j] * box[i][j]
+
+# S-boxes and inverse S-boxes
+S = [0] * 256
+Si = [0] * 256
+for i in xrange(256):
+ S[i] = cox[i][0] << 7
+ for t in xrange(1, 8):
+ S[i] ^= cox[i][t] << (7-t)
+ Si[S[i] & 0xFF] = i
+
+# T-boxes
+G = [[2, 1, 1, 3],
+ [3, 2, 1, 1],
+ [1, 3, 2, 1],
+ [1, 1, 3, 2]]
+
+AA = [[0] * 8 for i in xrange(4)]
+
+for i in xrange(4):
+ for j in xrange(4):
+ AA[i][j] = G[i][j]
+ AA[i][i+4] = 1
+
+for i in xrange(4):
+ pivot = AA[i][i]
+ if pivot == 0:
+ t = i + 1
+ while AA[t][i] == 0 and t < 4:
+ t += 1
+ assert t != 4, 'G matrix must be invertible'
+ for j in xrange(8):
+ AA[i][j], AA[t][j] = AA[t][j], AA[i][j]
+ pivot = AA[i][i]
+ for j in xrange(8):
+ if AA[i][j] != 0:
+ AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]
+ for t in xrange(4):
+ if i != t:
+ for j in xrange(i+1, 8):
+ AA[t][j] ^= mul(AA[i][j], AA[t][i])
+ AA[t][i] = 0
+
+iG = [[0] * 4 for i in xrange(4)]
+
+for i in xrange(4):
+ for j in xrange(4):
+ iG[i][j] = AA[i][j + 4]
+
+def mul4(a, bs):
+ if a == 0:
+ return 0
+ r = 0
+ for b in bs:
+ r <<= 8
+ if b != 0:
+ r = r | mul(a, b)
+ return r
+
+T1 = []
+T2 = []
+T3 = []
+T4 = []
+T5 = []
+T6 = []
+T7 = []
+T8 = []
+U1 = []
+U2 = []
+U3 = []
+U4 = []
+
+for t in xrange(256):
+ s = S[t]
+ T1.append(mul4(s, G[0]))
+ T2.append(mul4(s, G[1]))
+ T3.append(mul4(s, G[2]))
+ T4.append(mul4(s, G[3]))
+
+ s = Si[t]
+ T5.append(mul4(s, iG[0]))
+ T6.append(mul4(s, iG[1]))
+ T7.append(mul4(s, iG[2]))
+ T8.append(mul4(s, iG[3]))
+
+ U1.append(mul4(t, iG[0]))
+ U2.append(mul4(t, iG[1]))
+ U3.append(mul4(t, iG[2]))
+ U4.append(mul4(t, iG[3]))
+
+# round constants
+rcon = [1]
+r = 1
+for t in xrange(1, 30):
+ r = mul(2, r)
+ rcon.append(r)
+
+del A
+del AA
+del pivot
+del B
+del G
+del box
+del log
+del alog
+del i
+del j
+del r
+del s
+del t
+del mul
+del mul4
+del cox
+del iG
+
+class rijndael:
+ def __init__(self, key, block_size = 16):
+ if block_size != 16 and block_size != 24 and block_size != 32:
+ raise ValueError('Invalid block size: ' + str(block_size))
+ if len(key) != 16 and len(key) != 24 and len(key) != 32:
+ raise ValueError('Invalid key size: ' + str(len(key)))
+ self.block_size = block_size
+
+ ROUNDS = num_rounds[len(key)][block_size]
+ BC = block_size / 4
+ # encryption round keys
+ Ke = [[0] * BC for i in xrange(ROUNDS + 1)]
+ # decryption round keys
+ Kd = [[0] * BC for i in xrange(ROUNDS + 1)]
+ ROUND_KEY_COUNT = (ROUNDS + 1) * BC
+ KC = len(key) / 4
+
+ # copy user material bytes into temporary ints
+ tk = []
+ for i in xrange(0, KC):
+ tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) |
+ (ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3]))
+
+ # copy values into round key arrays
+ t = 0
+ j = 0
+ while j < KC and t < ROUND_KEY_COUNT:
+ Ke[t / BC][t % BC] = tk[j]
+ Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
+ j += 1
+ t += 1
+ tt = 0
+ rconpointer = 0
+ while t < ROUND_KEY_COUNT:
+ # extrapolate using phi (the round key evolution function)
+ tt = tk[KC - 1]
+ tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \
+ (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \
+ (S[ tt & 0xFF] & 0xFF) << 8 ^ \
+ (S[(tt >> 24) & 0xFF] & 0xFF) ^ \
+ (rcon[rconpointer] & 0xFF) << 24
+ rconpointer += 1
+ if KC != 8:
+ for i in xrange(1, KC):
+ tk[i] ^= tk[i-1]
+ else:
+ for i in xrange(1, KC / 2):
+ tk[i] ^= tk[i-1]
+ tt = tk[KC / 2 - 1]
+ tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \
+ (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \
+ (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
+ (S[(tt >> 24) & 0xFF] & 0xFF) << 24
+ for i in xrange(KC / 2 + 1, KC):
+ tk[i] ^= tk[i-1]
+ # copy values into round key arrays
+ j = 0
+ while j < KC and t < ROUND_KEY_COUNT:
+ Ke[t / BC][t % BC] = tk[j]
+ Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
+ j += 1
+ t += 1
+ # inverse MixColumn where needed
+ for r in xrange(1, ROUNDS):
+ for j in xrange(BC):
+ tt = Kd[r][j]
+ Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
+ U2[(tt >> 16) & 0xFF] ^ \
+ U3[(tt >> 8) & 0xFF] ^ \
+ U4[ tt & 0xFF]
+ self.Ke = Ke
+ self.Kd = Kd
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != self.block_size:
+ raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
+ Ke = self.Ke
+
+ BC = self.block_size / 4
+ ROUNDS = len(Ke) - 1
+ if BC == 4:
+ SC = 0
+ elif BC == 6:
+ SC = 1
+ else:
+ SC = 2
+ s1 = shifts[SC][1][0]
+ s2 = shifts[SC][2][0]
+ s3 = shifts[SC][3][0]
+ a = [0] * BC
+ # temporary work array
+ t = []
+ # plaintext to ints + key
+ for i in xrange(BC):
+ t.append((ord(plaintext[i * 4 ]) << 24 |
+ ord(plaintext[i * 4 + 1]) << 16 |
+ ord(plaintext[i * 4 + 2]) << 8 |
+ ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i])
+ # apply round transforms
+ for r in xrange(1, ROUNDS):
+ for i in xrange(BC):
+ a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^
+ T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
+ T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^
+ T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i]
+ t = copy.copy(a)
+ # last round is special
+ result = []
+ for i in xrange(BC):
+ tt = Ke[ROUNDS][i]
+ result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
+ return string.join(map(chr, result), '')
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != self.block_size:
+ raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
+ Kd = self.Kd
+
+ BC = self.block_size / 4
+ ROUNDS = len(Kd) - 1
+ if BC == 4:
+ SC = 0
+ elif BC == 6:
+ SC = 1
+ else:
+ SC = 2
+ s1 = shifts[SC][1][1]
+ s2 = shifts[SC][2][1]
+ s3 = shifts[SC][3][1]
+ a = [0] * BC
+ # temporary work array
+ t = [0] * BC
+ # ciphertext to ints + key
+ for i in xrange(BC):
+ t[i] = (ord(ciphertext[i * 4 ]) << 24 |
+ ord(ciphertext[i * 4 + 1]) << 16 |
+ ord(ciphertext[i * 4 + 2]) << 8 |
+ ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i]
+ # apply round transforms
+ for r in xrange(1, ROUNDS):
+ for i in xrange(BC):
+ a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^
+ T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
+ T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
+ T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i]
+ t = copy.copy(a)
+ # last round is special
+ result = []
+ for i in xrange(BC):
+ tt = Kd[ROUNDS][i]
+ result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
+ return string.join(map(chr, result), '')
+
+def encrypt(key, block):
+ return rijndael(key, len(block)).encrypt(block)
+
+def decrypt(key, block):
+ return rijndael(key, len(block)).decrypt(block)
+
+def test():
+ def t(kl, bl):
+ b = 'b' * bl
+ r = rijndael('a' * kl, bl)
+ assert r.decrypt(r.encrypt(b)) == b
+ t(16, 16)
+ t(16, 24)
+ t(16, 32)
+ t(24, 16)
+ t(24, 24)
+ t(24, 32)
+ t(32, 16)
+ t(32, 24)
+ t(32, 32)
+
diff --git a/src/lib/tlslite/utils/win32prng.c b/src/lib/tlslite/utils/win32prng.c
new file mode 100755
index 000000000..de08b3b3b
--- /dev/null
+++ b/src/lib/tlslite/utils/win32prng.c
@@ -0,0 +1,63 @@
+
+#include "Python.h"
+#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */
+#include <windows.h>
+
+
+static PyObject* getRandomBytes(PyObject *self, PyObject *args)
+{
+ int howMany;
+ HCRYPTPROV hCryptProv;
+ unsigned char* bytes = NULL;
+ PyObject* returnVal = NULL;
+
+
+ /* Read Arguments */
+ if (!PyArg_ParseTuple(args, "i", &howMany))
+ return(NULL);
+
+ /* Get Context */
+ if(CryptAcquireContext(
+ &hCryptProv,
+ NULL,
+ NULL,
+ PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT) == 0)
+ return Py_BuildValue("s#", NULL, 0);
+
+
+ /* Allocate bytes */
+ bytes = malloc(howMany);
+
+
+ /* Get random data */
+ if(CryptGenRandom(
+ hCryptProv,
+ howMany,
+ bytes) == 0)
+ returnVal = Py_BuildValue("s#", NULL, 0);
+ else
+ returnVal = Py_BuildValue("s#", bytes, howMany);
+
+ free(bytes);
+ CryptReleaseContext(hCryptProv, 0);
+
+ return returnVal;
+}
+
+
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef win32prng_functions[] = {
+ {"getRandomBytes", (PyCFunction)getRandomBytes, METH_VARARGS},
+ {NULL, NULL} /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+DL_EXPORT(void) initwin32prng(void)
+{
+ Py_InitModule("win32prng", win32prng_functions);
+}
diff --git a/src/lib/tlslite/utils/xmltools.py b/src/lib/tlslite/utils/xmltools.py
new file mode 100755
index 000000000..06f2e4307
--- /dev/null
+++ b/src/lib/tlslite/utils/xmltools.py
@@ -0,0 +1,201 @@
+"""Helper functions for XML.
+
+This module has misc. helper functions for working with XML DOM nodes."""
+
+import re
+from compat import *
+
+import os
+if os.name != "java":
+ from xml.dom import minidom
+ from xml.sax import saxutils
+
+ def parseDocument(s):
+ return minidom.parseString(s)
+else:
+ from javax.xml.parsers import *
+ import java
+
+ builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
+
+ def parseDocument(s):
+ stream = java.io.ByteArrayInputStream(java.lang.String(s).getBytes())
+ return builder.parse(stream)
+
+def parseAndStripWhitespace(s):
+ try:
+ element = parseDocument(s).documentElement
+ except BaseException, e:
+ raise SyntaxError(str(e))
+ stripWhitespace(element)
+ return element
+
+#Goes through a DOM tree and removes whitespace besides child elements,
+#as long as this whitespace is correctly tab-ified
+def stripWhitespace(element, tab=0):
+ element.normalize()
+
+ lastSpacer = "\n" + ("\t"*tab)
+ spacer = lastSpacer + "\t"
+
+ #Zero children aren't allowed (i.e. <empty/>)
+ #This makes writing output simpler, and matches Canonical XML
+ if element.childNodes.length==0: #DON'T DO len(element.childNodes) - doesn't work in Jython
+ raise SyntaxError("Empty XML elements not allowed")
+
+ #If there's a single child, it must be text context
+ if element.childNodes.length==1:
+ if element.firstChild.nodeType == element.firstChild.TEXT_NODE:
+ #If it's an empty element, remove
+ if element.firstChild.data == lastSpacer:
+ element.removeChild(element.firstChild)
+ return
+ #If not text content, give an error
+ elif element.firstChild.nodeType == element.firstChild.ELEMENT_NODE:
+ raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
+ else:
+ raise SyntaxError("Unexpected node type in XML document")
+
+ #Otherwise there's multiple child element
+ child = element.firstChild
+ while child:
+ if child.nodeType == child.ELEMENT_NODE:
+ stripWhitespace(child, tab+1)
+ child = child.nextSibling
+ elif child.nodeType == child.TEXT_NODE:
+ if child == element.lastChild:
+ if child.data != lastSpacer:
+ raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
+ elif child.data != spacer:
+ raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
+ next = child.nextSibling
+ element.removeChild(child)
+ child = next
+ else:
+ raise SyntaxError("Unexpected node type in XML document")
+
+
+def checkName(element, name):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Missing element: '%s'" % name)
+
+ if name == None:
+ return
+
+ if element.tagName != name:
+ raise SyntaxError("Wrong element name: should be '%s', is '%s'" % (name, element.tagName))
+
+def getChild(element, index, name=None):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in getChild()")
+
+ child = element.childNodes.item(index)
+ if child == None:
+ raise SyntaxError("Missing child: '%s'" % name)
+ checkName(child, name)
+ return child
+
+def getChildIter(element, index):
+ class ChildIter:
+ def __init__(self, element, index):
+ self.element = element
+ self.index = index
+
+ def next(self):
+ if self.index < len(self.element.childNodes):
+ retVal = self.element.childNodes.item(self.index)
+ self.index += 1
+ else:
+ retVal = None
+ return retVal
+
+ def checkEnd(self):
+ if self.index != len(self.element.childNodes):
+ raise SyntaxError("Too many elements under: '%s'" % self.element.tagName)
+ return ChildIter(element, index)
+
+def getChildOrNone(element, index):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in getChild()")
+ child = element.childNodes.item(index)
+ return child
+
+def getLastChild(element, index, name=None):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in getLastChild()")
+
+ child = element.childNodes.item(index)
+ if child == None:
+ raise SyntaxError("Missing child: '%s'" % name)
+ if child != element.lastChild:
+ raise SyntaxError("Too many elements under: '%s'" % element.tagName)
+ checkName(child, name)
+ return child
+
+#Regular expressions for syntax-checking attribute and element content
+nsRegEx = "http://trevp.net/cryptoID\Z"
+cryptoIDRegEx = "([a-km-z3-9]{5}\.){3}[a-km-z3-9]{5}\Z"
+urlRegEx = "http(s)?://.{1,100}\Z"
+sha1Base64RegEx = "[A-Za-z0-9+/]{27}=\Z"
+base64RegEx = "[A-Za-z0-9+/]+={0,4}\Z"
+certsListRegEx = "(0)?(1)?(2)?(3)?(4)?(5)?(6)?(7)?(8)?(9)?\Z"
+keyRegEx = "[A-Z]\Z"
+keysListRegEx = "(A)?(B)?(C)?(D)?(E)?(F)?(G)?(H)?(I)?(J)?(K)?(L)?(M)?(N)?(O)?(P)?(Q)?(R)?(S)?(T)?(U)?(V)?(W)?(X)?(Y)?(Z)?\Z"
+dateTimeRegEx = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\Z"
+shortStringRegEx = ".{1,100}\Z"
+exprRegEx = "[a-zA-Z0-9 ,()]{1,200}\Z"
+notAfterDeltaRegEx = "0|([1-9][0-9]{0,8})\Z" #A number from 0 to (1 billion)-1
+booleanRegEx = "(true)|(false)"
+
+def getReqAttribute(element, attrName, regEx=""):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in getReqAttribute()")
+
+ value = element.getAttribute(attrName)
+ if not value:
+ raise SyntaxError("Missing Attribute: " + attrName)
+ if not re.match(regEx, value):
+ raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value))
+ element.removeAttribute(attrName)
+ return str(value) #de-unicode it; this is needed for bsddb, for example
+
+def getAttribute(element, attrName, regEx=""):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in getAttribute()")
+
+ value = element.getAttribute(attrName)
+ if value:
+ if not re.match(regEx, value):
+ raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value))
+ element.removeAttribute(attrName)
+ return str(value) #de-unicode it; this is needed for bsddb, for example
+
+def checkNoMoreAttributes(element):
+ if element.nodeType != element.ELEMENT_NODE:
+ raise SyntaxError("Wrong node type in checkNoMoreAttributes()")
+
+ if element.attributes.length!=0:
+ raise SyntaxError("Extra attributes on '%s'" % element.tagName)
+
+def getText(element, regEx=""):
+ textNode = element.firstChild
+ if textNode == None:
+ raise SyntaxError("Empty element '%s'" % element.tagName)
+ if textNode.nodeType != textNode.TEXT_NODE:
+ raise SyntaxError("Non-text node: '%s'" % element.tagName)
+ if not re.match(regEx, textNode.data):
+ raise SyntaxError("Bad Text Value for '%s': '%s' " % (element.tagName, textNode.data))
+ return str(textNode.data) #de-unicode it; this is needed for bsddb, for example
+
+#Function for adding tabs to a string
+def indent(s, steps, ch="\t"):
+ tabs = ch*steps
+ if s[-1] != "\n":
+ s = tabs + s.replace("\n", "\n"+tabs)
+ else:
+ s = tabs + s.replace("\n", "\n"+tabs)
+ s = s[ : -len(tabs)]
+ return s
+
+def escape(s):
+ return saxutils.escape(s)
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 1364229be..f13f93af8 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -35,6 +35,8 @@ class Client:
False, False, False, True),
'drivers':(('-D', '<driver1>,<driver2>', "Specify tool driver set"),
False, False, False, False),
+ 'fingerprint':(('-F', '<server fingerprint>', "Server Fingerprint"),
+ False, ('communication', 'fingerprint'), False, False),
'dryrun':(('-n', False, "do not actually change the system"),
False, False, False, True),
'build': (('-B', False, "run in build mode"),
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 80b8a06a2..b049aa227 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -3,7 +3,7 @@
import difflib, logging, lxml.etree, os, popen2, re, socket, sys, ConfigParser
import xml.sax.saxutils
-import Bcfg2.Server.Core, Bcfg2.Logging
+import Bcfg2.Server.Core, Bcfg2.Logging, Bcfg2.tlslite.api
log = logging.getLogger('bcfg-admin')
@@ -13,6 +13,7 @@ colors = ['steelblue1', 'chartreuse', 'gold', 'magenta', 'indianred1', 'limegree
usage = '''
bcfg2-admin [options]
+fingerprint - print the server certificate fingerprint
init - initialize the bcfg2 repository( this is interactive; only run once )
mineentry <client> <entry type> <entry name> - mine statistics for entry information
minestruct <client> - mine statistics for extra entries
@@ -266,6 +267,16 @@ def do_compare(args):
rcs.append(False)
return not False in rcs
+def do_fingerprint():
+ '''calculate key fingerprint'''
+ cfile = '/etc/bcfg2.conf'
+ cf = ConfigParser.ConfigParser()
+ cf.read([cfile])
+ keypath = cf.get('communication', 'key')
+ x509 = Bcfg2.tlslite.api.X509()
+ x509.parse(open(keypath).read())
+ print x509.getFingerprint()
+
def do_pull(client, etype, ename):
'''Make currently recorded client state correct for entry'''
cfile = '/etc/bcfg2.conf'
@@ -485,6 +496,8 @@ if __name__ == '__main__':
do_viz(sys.argv[2:])
elif sys.argv[1] == 'compare':
do_compare(sys.argv[2:])
+ elif sys.argv[1] == 'fingerprint':
+ do_fingerprint()
else:
print usage