├── README.md ├── scripts ├── http_spdy_proxy.py ├── proxy.py └── spdy_http_proxy.py ├── setup.py └── src ├── __init__.py ├── c_zlib.py ├── client.py ├── error.py ├── http_common.py ├── push_tcp.py ├── server.py ├── spdy_client.py ├── spdy_common.py ├── spdy_server.py └── tlslite ├── BaseDB.py ├── Checker.py ├── FileObject.py ├── HandshakeSettings.py ├── Session.py ├── SessionCache.py ├── SharedKeyDB.py ├── TLSConnection.py ├── TLSRecordLayer.py ├── VerifierDB.py ├── X509.py ├── X509CertChain.py ├── __init__.py ├── api.py ├── constants.py ├── errors.py ├── integration ├── AsyncStateMachine.py ├── ClientHelper.py ├── HTTPTLSConnection.py ├── IMAP4_TLS.py ├── IntegrationHelper.py ├── POP3_TLS.py ├── SMTP_TLS.py ├── TLSAsyncDispatcherMixIn.py ├── TLSSocketServerMixIn.py ├── TLSTwistedProtocolWrapper.py ├── XMLRPCTransport.py └── __init__.py ├── mathtls.py ├── messages.py └── utils ├── AES.py ├── ASN1Parser.py ├── Cryptlib_AES.py ├── Cryptlib_RC4.py ├── Cryptlib_TripleDES.py ├── OpenSSL_AES.py ├── OpenSSL_RC4.py ├── OpenSSL_RSAKey.py ├── OpenSSL_TripleDES.py ├── PyCrypto_AES.py ├── PyCrypto_RC4.py ├── PyCrypto_RSAKey.py ├── PyCrypto_TripleDES.py ├── Python_AES.py ├── Python_RC4.py ├── Python_RSAKey.py ├── RC4.py ├── RSAKey.py ├── TripleDES.py ├── __init__.py ├── cipherfactory.py ├── codec.py ├── compat.py ├── cryptomath.py ├── dateFuncs.py ├── entropy.c ├── hmac.py ├── jython_compat.py ├── keyfactory.py ├── rijndael.py ├── win32prng.c └── xmltools.py /README.md: -------------------------------------------------------------------------------- 1 | This is an experimental source tree that aims to provide a working SPDY-HTTP reverse proxy, building upon the work of: 2 | 3 | * Mark Nottingham: https://github.com/mnot/nbhttp 4 | * Mike Belshe: https://github.com/mbelshe/nbhttp 5 | * Quinn Slack: https://github.com/sqs/tlslite (RFC compliance and hashlib updates) 6 | * Adam Langley: NPN patches to tlslite 7 | 8 | The place to get things going is scripts/spdy_http_proxy.py 9 | -------------------------------------------------------------------------------- /scripts/http_spdy_proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | A simple HTTP->SPDY proxy. 5 | """ 6 | 7 | # config 8 | backend_authority = None # for reverse proxies 9 | forward_proxy = None # for forward proxies with parents 10 | # end config 11 | 12 | 13 | import logging 14 | import sys 15 | from urlparse import urlsplit, urlunsplit 16 | 17 | try: # run from dist without installation 18 | sys.path.insert(0, "..") 19 | from src import Server, run 20 | from src.spdy_client import SpdyClient 21 | except ImportError: 22 | from nbhttp import Server, run 23 | from nbhttp.spdy_client import SpdyClient 24 | 25 | logging.basicConfig() 26 | log = logging.getLogger('server') 27 | log.setLevel(logging.INFO) 28 | 29 | class ProxyClient(SpdyClient): 30 | read_timeout = 10 31 | connect_timeout = 15 32 | proxy = forward_proxy 33 | 34 | def proxy_handler(method, uri, req_hdrs, s_res_start, req_pause): 35 | # can modify method, uri, req_hdrs here 36 | print uri 37 | if backend_authority: 38 | (scheme, authority, path, query, fragid) = urlsplit(uri) 39 | uri = urlunsplit((scheme, backend_authority, path, query, fragid)) 40 | def c_res_start(version, status, phrase, res_hdrs, res_pause): 41 | # can modify status, phrase, res_hdrs here 42 | res_hdrs = [(n.lower(),v.strip()) for (n,v) in res_hdrs if n.lower() not in ['connection', 'content-length', 'transfer-encoding', 'keep-alive']] 43 | res_body, res_done = s_res_start(status, phrase, res_hdrs, res_pause) 44 | # can modify res_body here 45 | return res_body, res_done 46 | req_body, req_done = client.req_start(method, uri, req_hdrs, c_res_start, req_pause) 47 | # can modify req_body here 48 | return req_body, req_done 49 | 50 | 51 | if __name__ == "__main__": 52 | import sys 53 | port = int(sys.argv[1]) 54 | client = ProxyClient() 55 | server = Server('', port, proxy_handler) 56 | run() 57 | -------------------------------------------------------------------------------- /scripts/proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | A simple HTTP proxy as a demonstration. 5 | """ 6 | 7 | 8 | import sys 9 | try: # run from dist without installation 10 | sys.path.insert(0, "..") 11 | from src import Client, Server, header_dict, run, client, schedule 12 | except ImportError: 13 | from nbhttp import Client, Server, header_dict, run, client, schedule 14 | 15 | # TODO: CONNECT support 16 | # TODO: remove headers nominated by Connection 17 | # TODO: add Via 18 | 19 | class ProxyClient(Client): 20 | read_timeout = 10 21 | connect_timeout = 15 22 | 23 | def proxy_handler(method, uri, req_hdrs, s_res_start, req_pause): 24 | # can modify method, uri, req_hdrs here 25 | def c_res_start(version, status, phrase, res_hdrs, res_pause): 26 | # can modify status, phrase, res_hdrs here 27 | res_body, res_done = s_res_start(status, phrase, res_hdrs, res_pause) 28 | # can modify res_body here 29 | return res_body, res_done 30 | c = ProxyClient(c_res_start) 31 | req_body, req_done = c.req_start(method, uri, req_hdrs, req_pause) 32 | # can modify req_body here 33 | return req_body, req_done 34 | 35 | 36 | if __name__ == "__main__": 37 | import sys 38 | port = int(sys.argv[1]) 39 | server = Server('', port, proxy_handler) 40 | run() -------------------------------------------------------------------------------- /scripts/spdy_http_proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | A simple SPDY->HTTP proxy. 5 | """ 6 | 7 | 8 | import logging 9 | import sys 10 | from urlparse import urlsplit, urlunsplit 11 | 12 | try: # run from dist without installation 13 | sys.path.insert(0, "..") 14 | from src import Client, run 15 | from src.spdy_server import SpdyServer 16 | except ImportError: 17 | from nbhttp import Client, run 18 | from nbhttp.spdy_server import SpdyServer 19 | 20 | logging.basicConfig() 21 | log = logging.getLogger('server') 22 | log.setLevel(logging.DEBUG) 23 | 24 | class ProxyClient(Client): 25 | read_timeout = 10 26 | connect_timeout = 15 27 | 28 | def proxy_handler(method, uri, req_hdrs, s_res_start, req_pause): 29 | # can modify method, uri, req_hdrs here 30 | print uri 31 | if backend_authority: 32 | (scheme, authority, path, query, fragid) = urlsplit(uri) 33 | uri = urlunsplit((scheme, backend_authority, path, query, fragid)) 34 | def c_res_start(version, status, phrase, res_hdrs, res_pause): 35 | # can modify status, phrase, res_hdrs here 36 | res_hdrs = [(n.lower(),v.strip()) for (n,v) in res_hdrs if n.lower() not in ['connection', 'content-length', 'transfer-encoding', 'keep-alive']] 37 | res_body, res_done = s_res_start(status, phrase, res_hdrs, res_pause) 38 | # can modify res_body here 39 | return res_body, res_done 40 | c = ProxyClient(c_res_start) 41 | req_body, req_done = c.req_start(method, uri, req_hdrs, req_pause) 42 | # can modify req_body here 43 | return req_body, req_done 44 | 45 | 46 | if __name__ == "__main__": 47 | import sys 48 | port = int(sys.argv[1]) 49 | try: 50 | backend_authority = sys.argv[2] 51 | except IndexError: 52 | backend_authority = None 53 | server = SpdyServer('', port, True, 'server.crt', 'server.key', proxy_handler) 54 | run() 55 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='nbhttp', 6 | version='0.7.3', 7 | description='Non-blocking HTTP components', 8 | author='Mark Nottingham', 9 | author_email='mnot@mnot.net', 10 | url='http://github.com/mnot/nbhttp/', 11 | download_url='http://github.com/mnot/nbhttp/tarball/nbhttp-0.7.3', 12 | packages=['nbhttp'], 13 | package_dir={'nbhttp': 'src'}, 14 | classifiers=[ 15 | 'Development Status :: 4 - Beta', 16 | 'Intended Audience :: Developers', 17 | 'License :: OSI Approved :: MIT License', 18 | 'Programming Language :: Python', 19 | 'Topic :: Internet :: WWW/HTTP', 20 | 'Topic :: Internet :: Proxy Servers', 21 | 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', 22 | 'Topic :: Software Development :: Libraries :: Python Modules', 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Non-blocking HTTP components. 5 | """ 6 | 7 | from client import Client 8 | from server import Server 9 | from push_tcp import run, stop, schedule 10 | from http_common import dummy, header_dict, get_hdr, \ 11 | safe_methods, idempotent_methods, hop_by_hop_hdrs 12 | -------------------------------------------------------------------------------- /src/c_zlib.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | c_zlib - zlib for Python using ctypes. 5 | 6 | This is a quick and nasty implementation of zlib using Python ctypes, in order 7 | to expose the ability to set a compression dictionary (which isn't available 8 | in the zlib module). 9 | """ 10 | 11 | __license__ = """ 12 | Copyright (c) 2009 Mark Nottingham 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | """ 32 | 33 | 34 | import ctypes as C 35 | from ctypes import util 36 | 37 | _zlib = C.cdll.LoadLibrary(util.find_library('libz')) 38 | 39 | class _z_stream(C.Structure): 40 | _fields_ = [ 41 | ("next_in", C.POINTER(C.c_ubyte)), 42 | ("avail_in", C.c_uint), 43 | ("total_in", C.c_ulong), 44 | ("next_out", C.POINTER(C.c_ubyte)), 45 | ("avail_out", C.c_uint), 46 | ("total_out", C.c_ulong), 47 | ("msg", C.c_char_p), 48 | ("state", C.c_void_p), 49 | ("zalloc", C.c_void_p), 50 | ("zfree", C.c_void_p), 51 | ("opaque", C.c_void_p), 52 | ("data_type", C.c_int), 53 | ("adler", C.c_ulong), 54 | ("reserved", C.c_ulong), 55 | 56 | ] 57 | 58 | # TODO: get zlib version with ctypes 59 | ZLIB_VERSION = C.c_char_p("1.2.3") 60 | Z_NULL = 0x00 61 | Z_OK = 0x00 62 | Z_STREAM_END = 0x01 63 | Z_NEED_DICT = 0x02 64 | Z_BUF_ERR = -0x05 65 | 66 | Z_NO_FLUSH = 0x00 67 | Z_SYNC_FLUSH = 0x02 68 | Z_FINISH = 0x04 69 | 70 | CHUNK = 1024 * 128 # FIXME: need to be smarter than this. 71 | 72 | class Compressor: 73 | def __init__(self, level=-1, dictionary=None): 74 | self.level = level 75 | self.st = _z_stream() 76 | err = _zlib.deflateInit_(C.byref(self.st), self.level, ZLIB_VERSION, C.sizeof(self.st)) 77 | assert err == Z_OK, err # FIXME: specific error 78 | if dictionary: 79 | err = _zlib.deflateSetDictionary( 80 | C.byref(self.st), 81 | C.cast(C.c_char_p(dictionary), C.POINTER(C.c_ubyte)), 82 | len(dictionary) 83 | ) 84 | assert err == Z_OK, err # FIXME: more specific error 85 | 86 | def __call__(self, input): 87 | outbuf = C.create_string_buffer(CHUNK) 88 | self.st.avail_in = len(input) 89 | self.st.next_in = C.cast(C.c_char_p(input), C.POINTER(C.c_ubyte)) 90 | self.st.next_out = C.cast(outbuf, C.POINTER(C.c_ubyte)) 91 | self.st.avail_out = CHUNK 92 | err = _zlib.deflate(C.byref(self.st), Z_SYNC_FLUSH) 93 | if err in [Z_OK, Z_STREAM_END]: 94 | return outbuf[:CHUNK-self.st.avail_out] 95 | else: 96 | raise AssertionError, err # FIXME: more specific errors? 97 | 98 | def __del__(self): 99 | err = _zlib.deflateEnd(C.byref(self.st)) 100 | #assert err == Z_OK, err # FIXME: more specific error 101 | 102 | 103 | class Decompressor: 104 | def __init__(self, dictionary=None): 105 | self.dictionary = dictionary 106 | self.st = _z_stream() 107 | err = _zlib.inflateInit2_(C.byref(self.st), 15, ZLIB_VERSION, C.sizeof(self.st)) 108 | assert err == Z_OK, err # FIXME: more specific error 109 | 110 | def __call__(self, input): 111 | outbuf = C.create_string_buffer(CHUNK) 112 | self.st.avail_in = len(input) 113 | self.st.next_in = C.cast(C.c_char_p(input), C.POINTER(C.c_ubyte)) 114 | self.st.avail_out = CHUNK 115 | self.st.next_out = C.cast(outbuf, C.POINTER(C.c_ubyte)) 116 | err = _zlib.inflate(C.byref(self.st), Z_SYNC_FLUSH) 117 | if err == Z_NEED_DICT: 118 | assert self.dictionary, "no dictionary provided" # FIXME: more specific error 119 | dict_id = _zlib.adler32( 120 | 0L, 121 | C.cast(C.c_char_p(self.dictionary), C.POINTER(C.c_ubyte)), 122 | len(self.dictionary) 123 | ) 124 | # assert dict_id == self.st.adler, 'incorrect dictionary (%s != %s)' % (dict_id, self.st.adler) 125 | err = _zlib.inflateSetDictionary( 126 | C.byref(self.st), 127 | C.cast(C.c_char_p(self.dictionary), C.POINTER(C.c_ubyte)), 128 | len(self.dictionary) 129 | ) 130 | assert err == Z_OK, err # FIXME: more specific error 131 | err = _zlib.inflate(C.byref(self.st), Z_SYNC_FLUSH) 132 | if err in [Z_OK, Z_STREAM_END]: 133 | return outbuf[:CHUNK-self.st.avail_out] 134 | else: 135 | raise AssertionError, err # FIXME: more specific error 136 | 137 | def __del__(self): 138 | err = _zlib.inflateEnd(C.byref(self.st)) 139 | assert err == Z_OK, err # FIXME: more specific error 140 | 141 | -------------------------------------------------------------------------------- /src/error.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | errors 5 | """ 6 | 7 | __author__ = "Mark Nottingham " 8 | __copyright__ = """\ 9 | Copyright (c) 2008-2010 Mark Nottingham 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | """ 29 | 30 | # General parsing errors 31 | 32 | ERR_CHUNK = { 33 | 'desc': "Chunked encoding error", 34 | } 35 | ERR_EXTRA_DATA = { 36 | 'desc': "Extra data received", 37 | } 38 | 39 | ERR_BODY_FORBIDDEN = { 40 | 'desc': "This message does not allow a body", 41 | } 42 | 43 | ERR_HTTP_VERSION = { 44 | 'desc': "Unrecognised HTTP version", # FIXME: more specific status 45 | } 46 | 47 | ERR_READ_TIMEOUT = { 48 | 'desc': "Read timeout", 49 | } 50 | 51 | ERR_TRANSFER_CODE = { 52 | 'desc': "Unknown request transfer coding", 53 | 'status': ("501", "Not Implemented"), 54 | } 55 | 56 | ERR_WHITESPACE_HDR = { 57 | 'desc': "Whitespace between request-line and first header", 58 | 'status': ("400", "Bad Request"), 59 | } 60 | 61 | ERR_TOO_MANY_MSGS = { 62 | 'desc': "Too many messages to parse", 63 | 'status': ("400", "Bad Request"), 64 | } 65 | 66 | # client-specific errors 67 | 68 | ERR_URL = { 69 | 'desc': "Unsupported or invalid URI", 70 | 'status': ("400", "Bad Request"), 71 | } 72 | ERR_LEN_REQ = { 73 | 'desc': "Content-Length required", 74 | 'status': ("411", "Length Required"), 75 | } 76 | 77 | ERR_CONNECT = { 78 | 'desc': "Connection closed", 79 | 'status': ("504", "Gateway Timeout"), 80 | } 81 | 82 | # server-specific errors 83 | 84 | ERR_HOST_REQ = { 85 | 'desc': "Host header required", 86 | } 87 | -------------------------------------------------------------------------------- /src/tlslite/BaseDB.py: -------------------------------------------------------------------------------- 1 | """Base class for SharedKeyDB and VerifierDB.""" 2 | 3 | import anydbm 4 | import thread 5 | 6 | class BaseDB: 7 | def __init__(self, filename, type): 8 | self.type = type 9 | self.filename = filename 10 | if self.filename: 11 | self.db = None 12 | else: 13 | self.db = {} 14 | self.lock = thread.allocate_lock() 15 | 16 | def create(self): 17 | """Create a new on-disk database. 18 | 19 | @raise anydbm.error: If there's a problem creating the database. 20 | """ 21 | if self.filename: 22 | self.db = anydbm.open(self.filename, "n") #raises anydbm.error 23 | self.db["--Reserved--type"] = self.type 24 | self.db.sync() 25 | else: 26 | self.db = {} 27 | 28 | def open(self): 29 | """Open a pre-existing on-disk database. 30 | 31 | @raise anydbm.error: If there's a problem opening the database. 32 | @raise ValueError: If the database is not of the right type. 33 | """ 34 | if not self.filename: 35 | raise ValueError("Can only open on-disk databases") 36 | self.db = anydbm.open(self.filename, "w") #raises anydbm.error 37 | try: 38 | if self.db["--Reserved--type"] != self.type: 39 | raise ValueError("Not a %s database" % self.type) 40 | except KeyError: 41 | raise ValueError("Not a recognized database") 42 | 43 | def __getitem__(self, username): 44 | if self.db == None: 45 | raise AssertionError("DB not open") 46 | 47 | self.lock.acquire() 48 | try: 49 | valueStr = self.db[username] 50 | finally: 51 | self.lock.release() 52 | 53 | return self._getItem(username, valueStr) 54 | 55 | def __setitem__(self, username, value): 56 | if self.db == None: 57 | raise AssertionError("DB not open") 58 | 59 | valueStr = self._setItem(username, value) 60 | 61 | self.lock.acquire() 62 | try: 63 | self.db[username] = valueStr 64 | if self.filename: 65 | self.db.sync() 66 | finally: 67 | self.lock.release() 68 | 69 | def __delitem__(self, username): 70 | if self.db == None: 71 | raise AssertionError("DB not open") 72 | 73 | self.lock.acquire() 74 | try: 75 | del(self.db[username]) 76 | if self.filename: 77 | self.db.sync() 78 | finally: 79 | self.lock.release() 80 | 81 | def __contains__(self, username): 82 | """Check if the database contains the specified username. 83 | 84 | @type username: str 85 | @param username: The username to check for. 86 | 87 | @rtype: bool 88 | @return: True if the database contains the username, False 89 | otherwise. 90 | 91 | """ 92 | if self.db == None: 93 | raise AssertionError("DB not open") 94 | 95 | self.lock.acquire() 96 | try: 97 | return self.db.has_key(username) 98 | finally: 99 | self.lock.release() 100 | 101 | def check(self, username, param): 102 | value = self.__getitem__(username) 103 | return self._checkItem(value, username, param) 104 | 105 | def keys(self): 106 | """Return a list of usernames in the database. 107 | 108 | @rtype: list 109 | @return: The usernames in the database. 110 | """ 111 | if self.db == None: 112 | raise AssertionError("DB not open") 113 | 114 | self.lock.acquire() 115 | try: 116 | usernames = self.db.keys() 117 | finally: 118 | self.lock.release() 119 | usernames = [u for u in usernames if not u.startswith("--Reserved--")] 120 | return usernames -------------------------------------------------------------------------------- /src/tlslite/Checker.py: -------------------------------------------------------------------------------- 1 | """Class for post-handshake certificate checking.""" 2 | 3 | from utils.cryptomath import hashAndBase64 4 | from X509 import X509 5 | from X509CertChain import X509CertChain 6 | from errors import * 7 | 8 | 9 | class Checker: 10 | """This class is passed to a handshake function to check the other 11 | party's certificate chain. 12 | 13 | If a handshake function completes successfully, but the Checker 14 | judges the other party's certificate chain to be missing or 15 | inadequate, a subclass of 16 | L{tlslite.errors.TLSAuthenticationError} will be raised. 17 | 18 | Currently, the Checker can check either an X.509 or a cryptoID 19 | chain (for the latter, cryptoIDlib must be installed). 20 | """ 21 | 22 | def __init__(self, cryptoID=None, protocol=None, 23 | x509Fingerprint=None, 24 | x509TrustList=None, x509CommonName=None, 25 | checkResumedSession=False): 26 | """Create a new Checker instance. 27 | 28 | You must pass in one of these argument combinations: 29 | - cryptoID[, protocol] (requires cryptoIDlib) 30 | - x509Fingerprint 31 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 32 | 33 | @type cryptoID: str 34 | @param cryptoID: A cryptoID which the other party's certificate 35 | chain must match. The cryptoIDlib module must be installed. 36 | Mutually exclusive with all of the 'x509...' arguments. 37 | 38 | @type protocol: str 39 | @param protocol: A cryptoID protocol URI which the other 40 | party's certificate chain must match. Requires the 'cryptoID' 41 | argument. 42 | 43 | @type x509Fingerprint: str 44 | @param x509Fingerprint: A hex-encoded X.509 end-entity 45 | fingerprint which the other party's end-entity certificate must 46 | match. Mutually exclusive with the 'cryptoID' and 47 | 'x509TrustList' arguments. 48 | 49 | @type x509TrustList: list of L{tlslite.X509.X509} 50 | @param x509TrustList: A list of trusted root certificates. The 51 | other party must present a certificate chain which extends to 52 | one of these root certificates. The cryptlib_py module must be 53 | installed. Mutually exclusive with the 'cryptoID' and 54 | 'x509Fingerprint' arguments. 55 | 56 | @type x509CommonName: str 57 | @param x509CommonName: The end-entity certificate's 'CN' field 58 | must match this value. For a web server, this is typically a 59 | server name such as 'www.amazon.com'. Mutually exclusive with 60 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 61 | 'x509TrustList' argument. 62 | 63 | @type checkResumedSession: bool 64 | @param checkResumedSession: If resumed sessions should be 65 | checked. This defaults to False, on the theory that if the 66 | session was checked once, we don't need to bother 67 | re-checking it. 68 | """ 69 | 70 | if cryptoID and (x509Fingerprint or x509TrustList): 71 | raise ValueError() 72 | if x509Fingerprint and x509TrustList: 73 | raise ValueError() 74 | if x509CommonName and not x509TrustList: 75 | raise ValueError() 76 | if protocol and not cryptoID: 77 | raise ValueError() 78 | if cryptoID: 79 | import cryptoIDlib #So we raise an error here 80 | if x509TrustList: 81 | import cryptlib_py #So we raise an error here 82 | self.cryptoID = cryptoID 83 | self.protocol = protocol 84 | self.x509Fingerprint = x509Fingerprint 85 | self.x509TrustList = x509TrustList 86 | self.x509CommonName = x509CommonName 87 | self.checkResumedSession = checkResumedSession 88 | 89 | def __call__(self, connection): 90 | """Check a TLSConnection. 91 | 92 | When a Checker is passed to a handshake function, this will 93 | be called at the end of the function. 94 | 95 | @type connection: L{tlslite.TLSConnection.TLSConnection} 96 | @param connection: The TLSConnection to examine. 97 | 98 | @raise tlslite.errors.TLSAuthenticationError: If the other 99 | party's certificate chain is missing or bad. 100 | """ 101 | if not self.checkResumedSession and connection.resumed: 102 | return 103 | 104 | if self.cryptoID or self.x509Fingerprint or self.x509TrustList: 105 | if connection._client: 106 | chain = connection.session.serverCertChain 107 | else: 108 | chain = connection.session.clientCertChain 109 | 110 | if self.x509Fingerprint or self.x509TrustList: 111 | if isinstance(chain, X509CertChain): 112 | if self.x509Fingerprint: 113 | if chain.getFingerprint() != self.x509Fingerprint: 114 | raise TLSFingerprintError(\ 115 | "X.509 fingerprint mismatch: %s, %s" % \ 116 | (chain.getFingerprint(), self.x509Fingerprint)) 117 | else: #self.x509TrustList 118 | if not chain.validate(self.x509TrustList): 119 | raise TLSValidationError("X.509 validation failure") 120 | if self.x509CommonName and \ 121 | (chain.getCommonName() != self.x509CommonName): 122 | raise TLSAuthorizationError(\ 123 | "X.509 Common Name mismatch: %s, %s" % \ 124 | (chain.getCommonName(), self.x509CommonName)) 125 | elif chain: 126 | raise TLSAuthenticationTypeError() 127 | else: 128 | raise TLSNoAuthenticationError() 129 | elif self.cryptoID: 130 | import cryptoIDlib.CertChain 131 | if isinstance(chain, cryptoIDlib.CertChain.CertChain): 132 | if chain.cryptoID != self.cryptoID: 133 | raise TLSFingerprintError(\ 134 | "cryptoID mismatch: %s, %s" % \ 135 | (chain.cryptoID, self.cryptoID)) 136 | if self.protocol: 137 | if not chain.checkProtocol(self.protocol): 138 | raise TLSAuthorizationError(\ 139 | "cryptoID protocol mismatch") 140 | if not chain.validate(): 141 | raise TLSValidationError("cryptoID validation failure") 142 | elif chain: 143 | raise TLSAuthenticationTypeError() 144 | else: 145 | raise TLSNoAuthenticationError() 146 | 147 | -------------------------------------------------------------------------------- /src/tlslite/FileObject.py: -------------------------------------------------------------------------------- 1 | """Class returned by TLSConnection.makefile().""" 2 | 3 | class FileObject: 4 | """This class provides a file object interface to a 5 | L{tlslite.TLSConnection.TLSConnection}. 6 | 7 | Call makefile() on a TLSConnection to create a FileObject instance. 8 | 9 | This class was copied, with minor modifications, from the 10 | _fileobject class in socket.py. Note that fileno() is not 11 | implemented.""" 12 | 13 | default_bufsize = 16384 #TREV: changed from 8192 14 | 15 | def __init__(self, sock, mode='rb', bufsize=-1): 16 | self._sock = sock 17 | self.mode = mode # Not actually used in this version 18 | if bufsize < 0: 19 | bufsize = self.default_bufsize 20 | self.bufsize = bufsize 21 | self.softspace = False 22 | if bufsize == 0: 23 | self._rbufsize = 1 24 | elif bufsize == 1: 25 | self._rbufsize = self.default_bufsize 26 | else: 27 | self._rbufsize = bufsize 28 | self._wbufsize = bufsize 29 | self._rbuf = "" # A string 30 | self._wbuf = [] # A list of strings 31 | 32 | def _getclosed(self): 33 | return self._sock is not None 34 | closed = property(_getclosed, doc="True if the file is closed") 35 | 36 | def close(self): 37 | try: 38 | if self._sock: 39 | for result in self._sock._decrefAsync(): #TREV 40 | pass 41 | finally: 42 | self._sock = None 43 | 44 | def __del__(self): 45 | try: 46 | self.close() 47 | except: 48 | # close() may fail if __init__ didn't complete 49 | pass 50 | 51 | def flush(self): 52 | if self._wbuf: 53 | buffer = "".join(self._wbuf) 54 | self._wbuf = [] 55 | self._sock.sendall(buffer) 56 | 57 | #def fileno(self): 58 | # raise NotImplementedError() #TREV 59 | 60 | def write(self, data): 61 | data = str(data) # XXX Should really reject non-string non-buffers 62 | if not data: 63 | return 64 | self._wbuf.append(data) 65 | if (self._wbufsize == 0 or 66 | self._wbufsize == 1 and '\n' in data or 67 | self._get_wbuf_len() >= self._wbufsize): 68 | self.flush() 69 | 70 | def writelines(self, list): 71 | # XXX We could do better here for very long lists 72 | # XXX Should really reject non-string non-buffers 73 | self._wbuf.extend(filter(None, map(str, list))) 74 | if (self._wbufsize <= 1 or 75 | self._get_wbuf_len() >= self._wbufsize): 76 | self.flush() 77 | 78 | def _get_wbuf_len(self): 79 | buf_len = 0 80 | for x in self._wbuf: 81 | buf_len += len(x) 82 | return buf_len 83 | 84 | def read(self, size=-1): 85 | data = self._rbuf 86 | if size < 0: 87 | # Read until EOF 88 | buffers = [] 89 | if data: 90 | buffers.append(data) 91 | self._rbuf = "" 92 | if self._rbufsize <= 1: 93 | recv_size = self.default_bufsize 94 | else: 95 | recv_size = self._rbufsize 96 | while True: 97 | data = self._sock.recv(recv_size) 98 | if not data: 99 | break 100 | buffers.append(data) 101 | return "".join(buffers) 102 | else: 103 | # Read until size bytes or EOF seen, whichever comes first 104 | buf_len = len(data) 105 | if buf_len >= size: 106 | self._rbuf = data[size:] 107 | return data[:size] 108 | buffers = [] 109 | if data: 110 | buffers.append(data) 111 | self._rbuf = "" 112 | while True: 113 | left = size - buf_len 114 | recv_size = max(self._rbufsize, left) 115 | data = self._sock.recv(recv_size) 116 | if not data: 117 | break 118 | buffers.append(data) 119 | n = len(data) 120 | if n >= left: 121 | self._rbuf = data[left:] 122 | buffers[-1] = data[:left] 123 | break 124 | buf_len += n 125 | return "".join(buffers) 126 | 127 | def readline(self, size=-1): 128 | data = self._rbuf 129 | if size < 0: 130 | # Read until \n or EOF, whichever comes first 131 | if self._rbufsize <= 1: 132 | # Speed up unbuffered case 133 | assert data == "" 134 | buffers = [] 135 | recv = self._sock.recv 136 | while data != "\n": 137 | data = recv(1) 138 | if not data: 139 | break 140 | buffers.append(data) 141 | return "".join(buffers) 142 | nl = data.find('\n') 143 | if nl >= 0: 144 | nl += 1 145 | self._rbuf = data[nl:] 146 | return data[:nl] 147 | buffers = [] 148 | if data: 149 | buffers.append(data) 150 | self._rbuf = "" 151 | while True: 152 | data = self._sock.recv(self._rbufsize) 153 | if not data: 154 | break 155 | buffers.append(data) 156 | nl = data.find('\n') 157 | if nl >= 0: 158 | nl += 1 159 | self._rbuf = data[nl:] 160 | buffers[-1] = data[:nl] 161 | break 162 | return "".join(buffers) 163 | else: 164 | # Read until size bytes or \n or EOF seen, whichever comes first 165 | nl = data.find('\n', 0, size) 166 | if nl >= 0: 167 | nl += 1 168 | self._rbuf = data[nl:] 169 | return data[:nl] 170 | buf_len = len(data) 171 | if buf_len >= size: 172 | self._rbuf = data[size:] 173 | return data[:size] 174 | buffers = [] 175 | if data: 176 | buffers.append(data) 177 | self._rbuf = "" 178 | while True: 179 | data = self._sock.recv(self._rbufsize) 180 | if not data: 181 | break 182 | buffers.append(data) 183 | left = size - buf_len 184 | nl = data.find('\n', 0, left) 185 | if nl >= 0: 186 | nl += 1 187 | self._rbuf = data[nl:] 188 | buffers[-1] = data[:nl] 189 | break 190 | n = len(data) 191 | if n >= left: 192 | self._rbuf = data[left:] 193 | buffers[-1] = data[:left] 194 | break 195 | buf_len += n 196 | return "".join(buffers) 197 | 198 | def readlines(self, sizehint=0): 199 | total = 0 200 | list = [] 201 | while True: 202 | line = self.readline() 203 | if not line: 204 | break 205 | list.append(line) 206 | total += len(line) 207 | if sizehint and total >= sizehint: 208 | break 209 | return list 210 | 211 | # Iterator protocols 212 | 213 | def __iter__(self): 214 | return self 215 | 216 | def next(self): 217 | line = self.readline() 218 | if not line: 219 | raise StopIteration 220 | return line 221 | -------------------------------------------------------------------------------- /src/tlslite/HandshakeSettings.py: -------------------------------------------------------------------------------- 1 | """Class for setting handshake parameters.""" 2 | 3 | from constants import CertificateType 4 | from utils import cryptomath 5 | from utils import cipherfactory 6 | 7 | class HandshakeSettings: 8 | """This class encapsulates various parameters that can be used with 9 | a TLS handshake. 10 | @sort: minKeySize, maxKeySize, cipherNames, certificateTypes, 11 | minVersion, maxVersion 12 | 13 | @type minKeySize: int 14 | @ivar minKeySize: The minimum bit length for asymmetric keys. 15 | 16 | If the other party tries to use SRP, RSA, or Diffie-Hellman 17 | parameters smaller than this length, an alert will be 18 | signalled. The default is 1023. 19 | 20 | @type maxKeySize: int 21 | @ivar maxKeySize: The maximum bit length for asymmetric keys. 22 | 23 | If the other party tries to use SRP, RSA, or Diffie-Hellman 24 | parameters larger than this length, an alert will be signalled. 25 | The default is 8193. 26 | 27 | @type cipherNames: list 28 | @ivar cipherNames: The allowed ciphers, in order of preference. 29 | 30 | The allowed values in this list are 'aes256', 'aes128', '3des', and 31 | 'rc4'. If these settings are used with a client handshake, they 32 | determine the order of the ciphersuites offered in the ClientHello 33 | message. 34 | 35 | If these settings are used with a server handshake, the server will 36 | choose whichever ciphersuite matches the earliest entry in this 37 | list. 38 | 39 | NOTE: If '3des' is used in this list, but TLS Lite can't find an 40 | add-on library that supports 3DES, then '3des' will be silently 41 | removed. 42 | 43 | The default value is ['aes256', 'aes128', '3des', 'rc4']. 44 | 45 | @type certificateTypes: list 46 | @ivar certificateTypes: The allowed certificate types, in order of 47 | preference. 48 | 49 | The allowed values in this list are 'x509' and 'cryptoID'. This 50 | list is only used with a client handshake. The client will 51 | advertise to the server which certificate types are supported, and 52 | will check that the server uses one of the appropriate types. 53 | 54 | NOTE: If 'cryptoID' is used in this list, but cryptoIDlib is not 55 | installed, then 'cryptoID' will be silently removed. 56 | 57 | @type minVersion: tuple 58 | @ivar minVersion: The minimum allowed SSL/TLS version. 59 | 60 | This variable can be set to (3,0) for SSL 3.0, (3,1) for 61 | TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to 62 | use a lower version, a protocol_version alert will be signalled. 63 | The default is (3,0). 64 | 65 | @type maxVersion: tuple 66 | @ivar maxVersion: The maximum allowed SSL/TLS version. 67 | 68 | This variable can be set to (3,0) for SSL 3.0, (3,1) for 69 | TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to 70 | use a higher version, a protocol_version alert will be signalled. 71 | The default is (3,2). (WARNING: Some servers may (improperly) 72 | reject clients which offer support for TLS 1.1. In this case, 73 | try lowering maxVersion to (3,1)). 74 | """ 75 | def __init__(self): 76 | self.minKeySize = 1023 77 | self.maxKeySize = 8193 78 | self.cipherNames = ["aes256", "aes128", "3des", "rc4"] 79 | self.cipherImplementations = ["cryptlib", "openssl", "pycrypto", 80 | "python"] 81 | self.certificateTypes = ["x509", "cryptoID"] 82 | self.minVersion = (3,0) 83 | self.maxVersion = (3,2) 84 | 85 | #Filters out options that are not supported 86 | def _filter(self): 87 | other = HandshakeSettings() 88 | other.minKeySize = self.minKeySize 89 | other.maxKeySize = self.maxKeySize 90 | other.cipherNames = self.cipherNames 91 | other.cipherImplementations = self.cipherImplementations 92 | other.certificateTypes = self.certificateTypes 93 | other.minVersion = self.minVersion 94 | other.maxVersion = self.maxVersion 95 | 96 | if not cipherfactory.tripleDESPresent: 97 | other.cipherNames = [e for e in self.cipherNames if e != "3des"] 98 | if len(other.cipherNames)==0: 99 | raise ValueError("No supported ciphers") 100 | 101 | try: 102 | import cryptoIDlib 103 | except ImportError: 104 | other.certificateTypes = [e for e in self.certificateTypes \ 105 | if e != "cryptoID"] 106 | if len(other.certificateTypes)==0: 107 | raise ValueError("No supported certificate types") 108 | 109 | if not cryptomath.cryptlibpyLoaded: 110 | other.cipherImplementations = [e for e in \ 111 | self.cipherImplementations if e != "cryptlib"] 112 | if not cryptomath.m2cryptoLoaded: 113 | other.cipherImplementations = [e for e in \ 114 | other.cipherImplementations if e != "openssl"] 115 | if not cryptomath.pycryptoLoaded: 116 | other.cipherImplementations = [e for e in \ 117 | other.cipherImplementations if e != "pycrypto"] 118 | if len(other.cipherImplementations)==0: 119 | raise ValueError("No supported cipher implementations") 120 | 121 | if other.minKeySize<512: 122 | raise ValueError("minKeySize too small") 123 | if other.minKeySize>16384: 124 | raise ValueError("minKeySize too large") 125 | if other.maxKeySize<512: 126 | raise ValueError("maxKeySize too small") 127 | if other.maxKeySize>16384: 128 | raise ValueError("maxKeySize too large") 129 | for s in other.cipherNames: 130 | if s not in ("aes256", "aes128", "rc4", "3des"): 131 | raise ValueError("Unknown cipher name: '%s'" % s) 132 | for s in other.cipherImplementations: 133 | if s not in ("cryptlib", "openssl", "python", "pycrypto"): 134 | raise ValueError("Unknown cipher implementation: '%s'" % s) 135 | for s in other.certificateTypes: 136 | if s not in ("x509", "cryptoID"): 137 | raise ValueError("Unknown certificate type: '%s'" % s) 138 | 139 | if other.minVersion > other.maxVersion: 140 | raise ValueError("Versions set incorrectly") 141 | 142 | if not other.minVersion in ((3,0), (3,1), (3,2)): 143 | raise ValueError("minVersion set incorrectly") 144 | 145 | if not other.maxVersion in ((3,0), (3,1), (3,2)): 146 | raise ValueError("maxVersion set incorrectly") 147 | 148 | return other 149 | 150 | def _getCertificateTypes(self): 151 | l = [] 152 | for ct in self.certificateTypes: 153 | if ct == "x509": 154 | l.append(CertificateType.x509) 155 | elif ct == "cryptoID": 156 | l.append(CertificateType.cryptoID) 157 | else: 158 | raise AssertionError() 159 | return l 160 | -------------------------------------------------------------------------------- /src/tlslite/Session.py: -------------------------------------------------------------------------------- 1 | """Class representing a TLS session.""" 2 | 3 | from utils.compat import * 4 | from mathtls import * 5 | from constants import * 6 | 7 | class Session: 8 | """ 9 | This class represents a TLS session. 10 | 11 | TLS distinguishes between connections and sessions. A new 12 | handshake creates both a connection and a session. Data is 13 | transmitted over the connection. 14 | 15 | The session contains a more permanent record of the handshake. The 16 | session can be inspected to determine handshake results. The 17 | session can also be used to create a new connection through 18 | "session resumption". If the client and server both support this, 19 | they can create a new connection based on an old session without 20 | the overhead of a full handshake. 21 | 22 | The session for a L{tlslite.TLSConnection.TLSConnection} can be 23 | retrieved from the connection's 'session' attribute. 24 | 25 | @type srpUsername: str 26 | @ivar srpUsername: The client's SRP username (or None). 27 | 28 | @type sharedKeyUsername: str 29 | @ivar sharedKeyUsername: The client's shared-key username (or 30 | None). 31 | 32 | @type clientCertChain: L{tlslite.X509CertChain.X509CertChain} or 33 | L{cryptoIDlib.CertChain.CertChain} 34 | @ivar clientCertChain: The client's certificate chain (or None). 35 | 36 | @type serverCertChain: L{tlslite.X509CertChain.X509CertChain} or 37 | L{cryptoIDlib.CertChain.CertChain} 38 | @ivar serverCertChain: The server's certificate chain (or None). 39 | """ 40 | 41 | def __init__(self): 42 | self.masterSecret = createByteArraySequence([]) 43 | self.sessionID = createByteArraySequence([]) 44 | self.cipherSuite = 0 45 | self.srpUsername = None 46 | self.sharedKeyUsername = None 47 | self.clientCertChain = None 48 | self.serverCertChain = None 49 | self.resumable = False 50 | self.sharedKey = False 51 | 52 | def _clone(self): 53 | other = Session() 54 | other.masterSecret = self.masterSecret 55 | other.sessionID = self.sessionID 56 | other.cipherSuite = self.cipherSuite 57 | other.srpUsername = self.srpUsername 58 | other.sharedKeyUsername = self.sharedKeyUsername 59 | other.clientCertChain = self.clientCertChain 60 | other.serverCertChain = self.serverCertChain 61 | other.resumable = self.resumable 62 | other.sharedKey = self.sharedKey 63 | return other 64 | 65 | def _calcMasterSecret(self, version, premasterSecret, clientRandom, 66 | serverRandom): 67 | if version == (3,0): 68 | self.masterSecret = PRF_SSL(premasterSecret, 69 | concatArrays(clientRandom, serverRandom), 48) 70 | elif version in ((3,1), (3,2)): 71 | self.masterSecret = PRF(premasterSecret, "master secret", 72 | concatArrays(clientRandom, serverRandom), 48) 73 | else: 74 | raise AssertionError() 75 | 76 | def valid(self): 77 | """If this session can be used for session resumption. 78 | 79 | @rtype: bool 80 | @return: If this session can be used for session resumption. 81 | """ 82 | return self.resumable or self.sharedKey 83 | 84 | def _setResumable(self, boolean): 85 | #Only let it be set if this isn't a shared key 86 | if not self.sharedKey: 87 | #Only let it be set to True if the sessionID is non-null 88 | if (not boolean) or (boolean and self.sessionID): 89 | self.resumable = boolean 90 | 91 | def getCipherName(self): 92 | """Get the name of the cipher used with this connection. 93 | 94 | @rtype: str 95 | @return: The name of the cipher used with this connection. 96 | Either 'aes128', 'aes256', 'rc4', or '3des'. 97 | """ 98 | if self.cipherSuite in CipherSuite.aes128Suites: 99 | return "aes128" 100 | elif self.cipherSuite in CipherSuite.aes256Suites: 101 | return "aes256" 102 | elif self.cipherSuite in CipherSuite.rc4Suites: 103 | return "rc4" 104 | elif self.cipherSuite in CipherSuite.tripleDESSuites: 105 | return "3des" 106 | else: 107 | return None 108 | 109 | def _createSharedKey(self, sharedKeyUsername, sharedKey): 110 | if len(sharedKeyUsername)>16: 111 | raise ValueError() 112 | if len(sharedKey)>47: 113 | raise ValueError() 114 | 115 | self.sharedKeyUsername = sharedKeyUsername 116 | 117 | self.sessionID = createByteArrayZeros(16) 118 | for x in range(len(sharedKeyUsername)): 119 | self.sessionID[x] = ord(sharedKeyUsername[x]) 120 | 121 | premasterSecret = createByteArrayZeros(48) 122 | sharedKey = chr(len(sharedKey)) + sharedKey 123 | for x in range(48): 124 | premasterSecret[x] = ord(sharedKey[x % len(sharedKey)]) 125 | 126 | self.masterSecret = PRF(premasterSecret, "shared secret", 127 | createByteArraySequence([]), 48) 128 | self.sharedKey = True 129 | return self 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/tlslite/SessionCache.py: -------------------------------------------------------------------------------- 1 | """Class for caching TLS sessions.""" 2 | 3 | import thread 4 | import time 5 | 6 | class SessionCache: 7 | """This class is used by the server to cache TLS sessions. 8 | 9 | Caching sessions allows the client to use TLS session resumption 10 | and avoid the expense of a full handshake. To use this class, 11 | simply pass a SessionCache instance into the server handshake 12 | function. 13 | 14 | This class is thread-safe. 15 | """ 16 | 17 | #References to these instances 18 | #are also held by the caller, who may change the 'resumable' 19 | #flag, so the SessionCache must return the same instances 20 | #it was passed in. 21 | 22 | def __init__(self, maxEntries=10000, maxAge=14400): 23 | """Create a new SessionCache. 24 | 25 | @type maxEntries: int 26 | @param maxEntries: The maximum size of the cache. When this 27 | limit is reached, the oldest sessions will be deleted as 28 | necessary to make room for new ones. The default is 10000. 29 | 30 | @type maxAge: int 31 | @param maxAge: The number of seconds before a session expires 32 | from the cache. The default is 14400 (i.e. 4 hours).""" 33 | 34 | self.lock = thread.allocate_lock() 35 | 36 | # Maps sessionIDs to sessions 37 | self.entriesDict = {} 38 | 39 | #Circular list of (sessionID, timestamp) pairs 40 | self.entriesList = [(None,None)] * maxEntries 41 | 42 | self.firstIndex = 0 43 | self.lastIndex = 0 44 | self.maxAge = maxAge 45 | 46 | def __getitem__(self, sessionID): 47 | self.lock.acquire() 48 | try: 49 | self._purge() #Delete old items, so we're assured of a new one 50 | session = self.entriesDict[sessionID] 51 | 52 | #When we add sessions they're resumable, but it's possible 53 | #for the session to be invalidated later on (if a fatal alert 54 | #is returned), so we have to check for resumability before 55 | #returning the session. 56 | 57 | if session.valid(): 58 | return session 59 | else: 60 | raise KeyError() 61 | finally: 62 | self.lock.release() 63 | 64 | 65 | def __setitem__(self, sessionID, session): 66 | self.lock.acquire() 67 | try: 68 | #Add the new element 69 | self.entriesDict[sessionID] = session 70 | self.entriesList[self.lastIndex] = (sessionID, time.time()) 71 | self.lastIndex = (self.lastIndex+1) % len(self.entriesList) 72 | 73 | #If the cache is full, we delete the oldest element to make an 74 | #empty space 75 | if self.lastIndex == self.firstIndex: 76 | del(self.entriesDict[self.entriesList[self.firstIndex][0]]) 77 | self.firstIndex = (self.firstIndex+1) % len(self.entriesList) 78 | finally: 79 | self.lock.release() 80 | 81 | #Delete expired items 82 | def _purge(self): 83 | currentTime = time.time() 84 | 85 | #Search through the circular list, deleting expired elements until 86 | #we reach a non-expired element. Since elements in list are 87 | #ordered in time, we can break once we reach the first non-expired 88 | #element 89 | index = self.firstIndex 90 | while index != self.lastIndex: 91 | if currentTime - self.entriesList[index][1] > self.maxAge: 92 | del(self.entriesDict[self.entriesList[index][0]]) 93 | index = (index+1) % len(self.entriesList) 94 | else: 95 | break 96 | self.firstIndex = index 97 | 98 | def _test(): 99 | import doctest, SessionCache 100 | return doctest.testmod(SessionCache) 101 | 102 | if __name__ == "__main__": 103 | _test() 104 | -------------------------------------------------------------------------------- /src/tlslite/SharedKeyDB.py: -------------------------------------------------------------------------------- 1 | """Class for storing shared keys.""" 2 | 3 | from utils.cryptomath import * 4 | from utils.compat import * 5 | from mathtls import * 6 | from Session import Session 7 | from BaseDB import BaseDB 8 | 9 | class SharedKeyDB(BaseDB): 10 | """This class represent an in-memory or on-disk database of shared 11 | keys. 12 | 13 | A SharedKeyDB can be passed to a server handshake function to 14 | authenticate a client based on one of the shared keys. 15 | 16 | This class is thread-safe. 17 | """ 18 | 19 | def __init__(self, filename=None): 20 | """Create a new SharedKeyDB. 21 | 22 | @type filename: str 23 | @param filename: Filename for an on-disk database, or None for 24 | an in-memory database. If the filename already exists, follow 25 | this with a call to open(). To create a new on-disk database, 26 | follow this with a call to create(). 27 | """ 28 | BaseDB.__init__(self, filename, "shared key") 29 | 30 | def _getItem(self, username, valueStr): 31 | session = Session() 32 | session._createSharedKey(username, valueStr) 33 | return session 34 | 35 | def __setitem__(self, username, sharedKey): 36 | """Add a shared key to the database. 37 | 38 | @type username: str 39 | @param username: The username to associate the shared key with. 40 | Must be less than or equal to 16 characters in length, and must 41 | not already be in the database. 42 | 43 | @type sharedKey: str 44 | @param sharedKey: The shared key to add. Must be less than 48 45 | characters in length. 46 | """ 47 | BaseDB.__setitem__(self, username, sharedKey) 48 | 49 | def _setItem(self, username, value): 50 | if len(username)>16: 51 | raise ValueError("username too long") 52 | if len(value)>=48: 53 | raise ValueError("shared key too long") 54 | return value 55 | 56 | def _checkItem(self, value, username, param): 57 | newSession = self._getItem(username, param) 58 | return value.masterSecret == newSession.masterSecret -------------------------------------------------------------------------------- /src/tlslite/VerifierDB.py: -------------------------------------------------------------------------------- 1 | """Class for storing SRP password verifiers.""" 2 | 3 | from utils.cryptomath import * 4 | from utils.compat import * 5 | import mathtls 6 | from BaseDB import BaseDB 7 | 8 | class VerifierDB(BaseDB): 9 | """This class represent an in-memory or on-disk database of SRP 10 | password verifiers. 11 | 12 | A VerifierDB can be passed to a server handshake to authenticate 13 | a client based on one of the verifiers. 14 | 15 | This class is thread-safe. 16 | """ 17 | def __init__(self, filename=None): 18 | """Create a new VerifierDB instance. 19 | 20 | @type filename: str 21 | @param filename: Filename for an on-disk database, or None for 22 | an in-memory database. If the filename already exists, follow 23 | this with a call to open(). To create a new on-disk database, 24 | follow this with a call to create(). 25 | """ 26 | BaseDB.__init__(self, filename, "verifier") 27 | 28 | def _getItem(self, username, valueStr): 29 | (N, g, salt, verifier) = valueStr.split(" ") 30 | N = base64ToNumber(N) 31 | g = base64ToNumber(g) 32 | salt = base64ToString(salt) 33 | verifier = base64ToNumber(verifier) 34 | return (N, g, salt, verifier) 35 | 36 | def __setitem__(self, username, verifierEntry): 37 | """Add a verifier entry to the database. 38 | 39 | @type username: str 40 | @param username: The username to associate the verifier with. 41 | Must be less than 256 characters in length. Must not already 42 | be in the database. 43 | 44 | @type verifierEntry: tuple 45 | @param verifierEntry: The verifier entry to add. Use 46 | L{tlslite.VerifierDB.VerifierDB.makeVerifier} to create a 47 | verifier entry. 48 | """ 49 | BaseDB.__setitem__(self, username, verifierEntry) 50 | 51 | 52 | def _setItem(self, username, value): 53 | if len(username)>=256: 54 | raise ValueError("username too long") 55 | N, g, salt, verifier = value 56 | N = numberToBase64(N) 57 | g = numberToBase64(g) 58 | salt = stringToBase64(salt) 59 | verifier = numberToBase64(verifier) 60 | valueStr = " ".join( (N, g, salt, verifier) ) 61 | return valueStr 62 | 63 | def _checkItem(self, value, username, param): 64 | (N, g, salt, verifier) = value 65 | x = mathtls.makeX(salt, username, param) 66 | v = powMod(g, x, N) 67 | return (verifier == v) 68 | 69 | 70 | def makeVerifier(username, password, bits): 71 | """Create a verifier entry which can be stored in a VerifierDB. 72 | 73 | @type username: str 74 | @param username: The username for this verifier. Must be less 75 | than 256 characters in length. 76 | 77 | @type password: str 78 | @param password: The password for this verifier. 79 | 80 | @type bits: int 81 | @param bits: This values specifies which SRP group parameters 82 | to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144, 83 | 8192). Larger values are more secure but slower. 2048 is a 84 | good compromise between safety and speed. 85 | 86 | @rtype: tuple 87 | @return: A tuple which may be stored in a VerifierDB. 88 | """ 89 | return mathtls.makeVerifier(username, password, bits) 90 | makeVerifier = staticmethod(makeVerifier) -------------------------------------------------------------------------------- /src/tlslite/X509.py: -------------------------------------------------------------------------------- 1 | """Class representing an X.509 certificate.""" 2 | 3 | from utils.ASN1Parser import ASN1Parser 4 | from utils.cryptomath import * 5 | from utils.keyfactory import _createPublicRSAKey 6 | 7 | 8 | class X509: 9 | """This class represents an X.509 certificate. 10 | 11 | @type bytes: L{array.array} of unsigned bytes 12 | @ivar bytes: The DER-encoded ASN.1 certificate 13 | 14 | @type publicKey: L{tlslite.utils.RSAKey.RSAKey} 15 | @ivar publicKey: The subject public key from the certificate. 16 | """ 17 | 18 | def __init__(self): 19 | self.bytes = createByteArraySequence([]) 20 | self.publicKey = None 21 | 22 | def parse(self, s): 23 | """Parse a PEM-encoded X.509 certificate. 24 | 25 | @type s: str 26 | @param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded 27 | certificate wrapped with "-----BEGIN CERTIFICATE-----" and 28 | "-----END CERTIFICATE-----" tags). 29 | """ 30 | 31 | start = s.find("-----BEGIN CERTIFICATE-----") 32 | end = s.find("-----END CERTIFICATE-----") 33 | if start == -1: 34 | raise SyntaxError("Missing PEM prefix") 35 | if end == -1: 36 | raise SyntaxError("Missing PEM postfix") 37 | s = s[start+len("-----BEGIN CERTIFICATE-----") : end] 38 | 39 | bytes = base64ToBytes(s) 40 | self.parseBinary(bytes) 41 | return self 42 | 43 | def parseBinary(self, bytes): 44 | """Parse a DER-encoded X.509 certificate. 45 | 46 | @type bytes: str or L{array.array} of unsigned bytes 47 | @param bytes: A DER-encoded X.509 certificate. 48 | """ 49 | 50 | if isinstance(bytes, type("")): 51 | bytes = stringToBytes(bytes) 52 | 53 | self.bytes = bytes 54 | p = ASN1Parser(bytes) 55 | 56 | #Get the tbsCertificate 57 | tbsCertificateP = p.getChild(0) 58 | 59 | #Is the optional version field present? 60 | #This determines which index the key is at. 61 | if tbsCertificateP.value[0]==0xA0: 62 | subjectPublicKeyInfoIndex = 6 63 | else: 64 | subjectPublicKeyInfoIndex = 5 65 | 66 | #Get the subjectPublicKeyInfo 67 | subjectPublicKeyInfoP = tbsCertificateP.getChild(\ 68 | subjectPublicKeyInfoIndex) 69 | 70 | #Get the algorithm 71 | algorithmP = subjectPublicKeyInfoP.getChild(0) 72 | rsaOID = algorithmP.value 73 | if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: 74 | raise SyntaxError("Unrecognized AlgorithmIdentifier") 75 | 76 | #Get the subjectPublicKey 77 | subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1) 78 | 79 | #Adjust for BIT STRING encapsulation 80 | if (subjectPublicKeyP.value[0] !=0): 81 | raise SyntaxError() 82 | subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:]) 83 | 84 | #Get the modulus and exponent 85 | modulusP = subjectPublicKeyP.getChild(0) 86 | publicExponentP = subjectPublicKeyP.getChild(1) 87 | 88 | #Decode them into numbers 89 | n = bytesToNumber(modulusP.value) 90 | e = bytesToNumber(publicExponentP.value) 91 | 92 | #Create a public key instance 93 | self.publicKey = _createPublicRSAKey(n, e) 94 | 95 | def getFingerprint(self): 96 | """Get the hex-encoded fingerprint of this certificate. 97 | 98 | @rtype: str 99 | @return: A hex-encoded fingerprint. 100 | """ 101 | return hashlib.sha1(self.bytes).hexdigest() 102 | 103 | def getCommonName(self): 104 | """Get the Subject's Common Name from the certificate. 105 | 106 | The cryptlib_py module must be installed in order to use this 107 | function. 108 | 109 | @rtype: str or None 110 | @return: The CN component of the certificate's subject DN, if 111 | present. 112 | """ 113 | import cryptlib_py 114 | import array 115 | c = cryptlib_py.cryptImportCert(self.bytes, cryptlib_py.CRYPT_UNUSED) 116 | name = cryptlib_py.CRYPT_CERTINFO_COMMONNAME 117 | try: 118 | try: 119 | length = cryptlib_py.cryptGetAttributeString(c, name, None) 120 | returnVal = array.array('B', [0] * length) 121 | cryptlib_py.cryptGetAttributeString(c, name, returnVal) 122 | returnVal = returnVal.tostring() 123 | except cryptlib_py.CryptException, e: 124 | if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: 125 | returnVal = None 126 | return returnVal 127 | finally: 128 | cryptlib_py.cryptDestroyCert(c) 129 | 130 | def writeBytes(self): 131 | return self.bytes 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/tlslite/X509CertChain.py: -------------------------------------------------------------------------------- 1 | """Class representing an X.509 certificate chain.""" 2 | 3 | from utils import cryptomath 4 | 5 | class X509CertChain: 6 | """This class represents a chain of X.509 certificates. 7 | 8 | @type x509List: list 9 | @ivar x509List: A list of L{tlslite.X509.X509} instances, 10 | starting with the end-entity certificate and with every 11 | subsequent certificate certifying the previous. 12 | """ 13 | 14 | def __init__(self, x509List=None): 15 | """Create a new X509CertChain. 16 | 17 | @type x509List: list 18 | @param x509List: A list of L{tlslite.X509.X509} instances, 19 | starting with the end-entity certificate and with every 20 | subsequent certificate certifying the previous. 21 | """ 22 | if x509List: 23 | self.x509List = x509List 24 | else: 25 | self.x509List = [] 26 | 27 | def getNumCerts(self): 28 | """Get the number of certificates in this chain. 29 | 30 | @rtype: int 31 | """ 32 | return len(self.x509List) 33 | 34 | def getEndEntityPublicKey(self): 35 | """Get the public key from the end-entity certificate. 36 | 37 | @rtype: L{tlslite.utils.RSAKey.RSAKey} 38 | """ 39 | if self.getNumCerts() == 0: 40 | raise AssertionError() 41 | return self.x509List[0].publicKey 42 | 43 | def getFingerprint(self): 44 | """Get the hex-encoded fingerprint of the end-entity certificate. 45 | 46 | @rtype: str 47 | @return: A hex-encoded fingerprint. 48 | """ 49 | if self.getNumCerts() == 0: 50 | raise AssertionError() 51 | return self.x509List[0].getFingerprint() 52 | 53 | def getCommonName(self): 54 | """Get the Subject's Common Name from the end-entity certificate. 55 | 56 | The cryptlib_py module must be installed in order to use this 57 | function. 58 | 59 | @rtype: str or None 60 | @return: The CN component of the certificate's subject DN, if 61 | present. 62 | """ 63 | if self.getNumCerts() == 0: 64 | raise AssertionError() 65 | return self.x509List[0].getCommonName() 66 | 67 | def validate(self, x509TrustList): 68 | """Check the validity of the certificate chain. 69 | 70 | This checks that every certificate in the chain validates with 71 | the subsequent one, until some certificate validates with (or 72 | is identical to) one of the passed-in root certificates. 73 | 74 | The cryptlib_py module must be installed in order to use this 75 | function. 76 | 77 | @type x509TrustList: list of L{tlslite.X509.X509} 78 | @param x509TrustList: A list of trusted root certificates. The 79 | certificate chain must extend to one of these certificates to 80 | be considered valid. 81 | """ 82 | 83 | import cryptlib_py 84 | c1 = None 85 | c2 = None 86 | lastC = None 87 | rootC = None 88 | 89 | try: 90 | rootFingerprints = [c.getFingerprint() for c in x509TrustList] 91 | 92 | #Check that every certificate in the chain validates with the 93 | #next one 94 | for cert1, cert2 in zip(self.x509List, self.x509List[1:]): 95 | 96 | #If we come upon a root certificate, we're done. 97 | if cert1.getFingerprint() in rootFingerprints: 98 | return True 99 | 100 | c1 = cryptlib_py.cryptImportCert(cert1.writeBytes(), 101 | cryptlib_py.CRYPT_UNUSED) 102 | c2 = cryptlib_py.cryptImportCert(cert2.writeBytes(), 103 | cryptlib_py.CRYPT_UNUSED) 104 | try: 105 | cryptlib_py.cryptCheckCert(c1, c2) 106 | except: 107 | return False 108 | cryptlib_py.cryptDestroyCert(c1) 109 | c1 = None 110 | cryptlib_py.cryptDestroyCert(c2) 111 | c2 = None 112 | 113 | #If the last certificate is one of the root certificates, we're 114 | #done. 115 | if self.x509List[-1].getFingerprint() in rootFingerprints: 116 | return True 117 | 118 | #Otherwise, find a root certificate that the last certificate 119 | #chains to, and validate them. 120 | lastC = cryptlib_py.cryptImportCert(self.x509List[-1].writeBytes(), 121 | cryptlib_py.CRYPT_UNUSED) 122 | for rootCert in x509TrustList: 123 | rootC = cryptlib_py.cryptImportCert(rootCert.writeBytes(), 124 | cryptlib_py.CRYPT_UNUSED) 125 | if self._checkChaining(lastC, rootC): 126 | try: 127 | cryptlib_py.cryptCheckCert(lastC, rootC) 128 | return True 129 | except: 130 | return False 131 | return False 132 | finally: 133 | if not (c1 is None): 134 | cryptlib_py.cryptDestroyCert(c1) 135 | if not (c2 is None): 136 | cryptlib_py.cryptDestroyCert(c2) 137 | if not (lastC is None): 138 | cryptlib_py.cryptDestroyCert(lastC) 139 | if not (rootC is None): 140 | cryptlib_py.cryptDestroyCert(rootC) 141 | 142 | 143 | 144 | def _checkChaining(self, lastC, rootC): 145 | import cryptlib_py 146 | import array 147 | def compareNames(name): 148 | try: 149 | length = cryptlib_py.cryptGetAttributeString(lastC, name, None) 150 | lastName = array.array('B', [0] * length) 151 | cryptlib_py.cryptGetAttributeString(lastC, name, lastName) 152 | lastName = lastName.tostring() 153 | except cryptlib_py.CryptException, e: 154 | if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: 155 | lastName = None 156 | try: 157 | length = cryptlib_py.cryptGetAttributeString(rootC, name, None) 158 | rootName = array.array('B', [0] * length) 159 | cryptlib_py.cryptGetAttributeString(rootC, name, rootName) 160 | rootName = rootName.tostring() 161 | except cryptlib_py.CryptException, e: 162 | if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: 163 | rootName = None 164 | 165 | return lastName == rootName 166 | 167 | cryptlib_py.cryptSetAttribute(lastC, 168 | cryptlib_py.CRYPT_CERTINFO_ISSUERNAME, 169 | cryptlib_py.CRYPT_UNUSED) 170 | 171 | if not compareNames(cryptlib_py.CRYPT_CERTINFO_COUNTRYNAME): 172 | return False 173 | if not compareNames(cryptlib_py.CRYPT_CERTINFO_LOCALITYNAME): 174 | return False 175 | if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONNAME): 176 | return False 177 | if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONALUNITNAME): 178 | return False 179 | if not compareNames(cryptlib_py.CRYPT_CERTINFO_COMMONNAME): 180 | return False 181 | return True -------------------------------------------------------------------------------- /src/tlslite/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | TLS Lite is a free python library that implements SSL v3, TLS v1, and 3 | TLS v1.1. TLS Lite supports non-traditional authentication methods 4 | such as SRP, shared keys, and cryptoIDs, in addition to X.509 5 | certificates. TLS Lite is pure python, however it can access OpenSSL, 6 | cryptlib, pycrypto, and GMPY for faster crypto operations. TLS Lite 7 | integrates with httplib, xmlrpclib, poplib, imaplib, smtplib, 8 | SocketServer, asyncore, and Twisted. 9 | 10 | To use, do:: 11 | 12 | from tlslite.api import * 13 | 14 | Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket, 15 | or use one of the integration classes in L{tlslite.integration}. 16 | 17 | @version: 0.3.8 18 | """ 19 | __version__ = "0.3.8" 20 | 21 | __all__ = ["api", 22 | "BaseDB", 23 | "Checker", 24 | "constants", 25 | "errors", 26 | "FileObject", 27 | "HandshakeSettings", 28 | "mathtls", 29 | "messages", 30 | "Session", 31 | "SessionCache", 32 | "SharedKeyDB", 33 | "TLSConnection", 34 | "TLSRecordLayer", 35 | "VerifierDB", 36 | "X509", 37 | "X509CertChain", 38 | "integration", 39 | "utils"] 40 | -------------------------------------------------------------------------------- /src/tlslite/api.py: -------------------------------------------------------------------------------- 1 | """Import this module for easy access to TLS Lite objects. 2 | 3 | The TLS Lite API consists of classes, functions, and variables spread 4 | throughout this package. Instead of importing them individually with:: 5 | 6 | from tlslite.TLSConnection import TLSConnection 7 | from tlslite.HandshakeSettings import HandshakeSettings 8 | from tlslite.errors import * 9 | . 10 | . 11 | 12 | It's easier to do:: 13 | 14 | from tlslite.api import * 15 | 16 | This imports all the important objects (TLSConnection, Checker, 17 | HandshakeSettings, etc.) into the global namespace. In particular, it 18 | imports:: 19 | 20 | from constants import AlertLevel, AlertDescription, Fault 21 | from errors import * 22 | from Checker import Checker 23 | from HandshakeSettings import HandshakeSettings 24 | from Session import Session 25 | from SessionCache import SessionCache 26 | from SharedKeyDB import SharedKeyDB 27 | from TLSConnection import TLSConnection 28 | from VerifierDB import VerifierDB 29 | from X509 import X509 30 | from X509CertChain import X509CertChain 31 | 32 | from integration.HTTPTLSConnection import HTTPTLSConnection 33 | from integration.POP3_TLS import POP3_TLS 34 | from integration.IMAP4_TLS import IMAP4_TLS 35 | from integration.SMTP_TLS import SMTP_TLS 36 | from integration.XMLRPCTransport import XMLRPCTransport 37 | from integration.TLSSocketServerMixIn import TLSSocketServerMixIn 38 | from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn 39 | from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper 40 | from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, 41 | gmpyLoaded, pycryptoLoaded, prngName 42 | from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, 43 | parseAsPublicKey, parsePrivateKey 44 | """ 45 | 46 | from constants import AlertLevel, AlertDescription, Fault 47 | from errors import * 48 | from Checker import Checker 49 | from HandshakeSettings import HandshakeSettings 50 | from Session import Session 51 | from SessionCache import SessionCache 52 | from SharedKeyDB import SharedKeyDB 53 | from TLSConnection import TLSConnection 54 | from VerifierDB import VerifierDB 55 | from X509 import X509 56 | from X509CertChain import X509CertChain 57 | 58 | from integration.HTTPTLSConnection import HTTPTLSConnection 59 | from integration.TLSSocketServerMixIn import TLSSocketServerMixIn 60 | from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn 61 | from integration.POP3_TLS import POP3_TLS 62 | from integration.IMAP4_TLS import IMAP4_TLS 63 | from integration.SMTP_TLS import SMTP_TLS 64 | from integration.XMLRPCTransport import XMLRPCTransport 65 | try: 66 | import twisted 67 | del(twisted) 68 | from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper 69 | except ImportError: 70 | pass 71 | 72 | from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, gmpyLoaded, \ 73 | pycryptoLoaded, prngName 74 | from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, \ 75 | parseAsPublicKey, parsePrivateKey 76 | -------------------------------------------------------------------------------- /src/tlslite/constants.py: -------------------------------------------------------------------------------- 1 | """Constants used in various places.""" 2 | 3 | class CertificateType: 4 | x509 = 0 5 | openpgp = 1 6 | cryptoID = 2 7 | 8 | class HandshakeType: 9 | hello_request = 0 10 | client_hello = 1 11 | server_hello = 2 12 | certificate = 11 13 | server_key_exchange = 12 14 | certificate_request = 13 15 | server_hello_done = 14 16 | certificate_verify = 15 17 | client_key_exchange = 16 18 | finished = 20 19 | next_protocol = 67 20 | 21 | class ContentType: 22 | change_cipher_spec = 20 23 | alert = 21 24 | handshake = 22 25 | application_data = 23 26 | all = (20,21,22,23) 27 | 28 | class ClientHelloExtension: 29 | srp = 12 30 | 31 | class AlertLevel: 32 | warning = 1 33 | fatal = 2 34 | 35 | class AlertDescription: 36 | """ 37 | @cvar bad_record_mac: A TLS record failed to decrypt properly. 38 | 39 | If this occurs during a shared-key or SRP handshake it most likely 40 | indicates a bad password. It may also indicate an implementation 41 | error, or some tampering with the data in transit. 42 | 43 | This alert will be signalled by the server if the SRP password is bad. It 44 | may also be signalled by the server if the SRP username is unknown to the 45 | server, but it doesn't wish to reveal that fact. 46 | 47 | This alert will be signalled by the client if the shared-key username is 48 | bad. 49 | 50 | @cvar handshake_failure: A problem occurred while handshaking. 51 | 52 | This typically indicates a lack of common ciphersuites between client and 53 | server, or some other disagreement (about SRP parameters or key sizes, 54 | for example). 55 | 56 | @cvar protocol_version: The other party's SSL/TLS version was unacceptable. 57 | 58 | This indicates that the client and server couldn't agree on which version 59 | of SSL or TLS to use. 60 | 61 | @cvar user_canceled: The handshake is being cancelled for some reason. 62 | 63 | """ 64 | 65 | close_notify = 0 66 | unexpected_message = 10 67 | bad_record_mac = 20 68 | decryption_failed = 21 69 | record_overflow = 22 70 | decompression_failure = 30 71 | handshake_failure = 40 72 | no_certificate = 41 #SSLv3 73 | bad_certificate = 42 74 | unsupported_certificate = 43 75 | certificate_revoked = 44 76 | certificate_expired = 45 77 | certificate_unknown = 46 78 | illegal_parameter = 47 79 | unknown_ca = 48 80 | access_denied = 49 81 | decode_error = 50 82 | decrypt_error = 51 83 | export_restriction = 60 84 | protocol_version = 70 85 | insufficient_security = 71 86 | internal_error = 80 87 | user_canceled = 90 88 | no_renegotiation = 100 89 | unknown_psk_identity = 115 90 | untrusted_srp_parameters = 122 # TODO(sqs): probably outdated wrt RFC 5054 91 | 92 | class CipherSuite: 93 | TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A 94 | TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D 95 | TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020 96 | 97 | TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B 98 | TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E 99 | TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021 100 | 101 | # TODO(sqs): No SRP DSS cipher suites 102 | 103 | TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A 104 | TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F 105 | TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 106 | TLS_RSA_WITH_RC4_128_SHA = 0x0005 107 | 108 | srpSuites = [] 109 | srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) 110 | srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) 111 | srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) 112 | def getSrpSuites(ciphers): 113 | suites = [] 114 | for cipher in ciphers: 115 | if cipher == "aes128": 116 | suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA) 117 | elif cipher == "aes256": 118 | suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA) 119 | elif cipher == "3des": 120 | suites.append(CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) 121 | return suites 122 | getSrpSuites = staticmethod(getSrpSuites) 123 | 124 | srpRsaSuites = [] 125 | srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) 126 | srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) 127 | srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) 128 | def getSrpRsaSuites(ciphers): 129 | suites = [] 130 | for cipher in ciphers: 131 | if cipher == "aes128": 132 | suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) 133 | elif cipher == "aes256": 134 | suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) 135 | elif cipher == "3des": 136 | suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) 137 | return suites 138 | getSrpRsaSuites = staticmethod(getSrpRsaSuites) 139 | 140 | rsaSuites = [] 141 | rsaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) 142 | rsaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA) 143 | rsaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA) 144 | rsaSuites.append(TLS_RSA_WITH_RC4_128_SHA) 145 | def getRsaSuites(ciphers): 146 | suites = [] 147 | for cipher in ciphers: 148 | if cipher == "aes128": 149 | suites.append(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 150 | elif cipher == "aes256": 151 | suites.append(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) 152 | elif cipher == "rc4": 153 | suites.append(CipherSuite.TLS_RSA_WITH_RC4_128_SHA) 154 | elif cipher == "3des": 155 | suites.append(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA) 156 | return suites 157 | getRsaSuites = staticmethod(getRsaSuites) 158 | 159 | tripleDESSuites = [] 160 | tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) 161 | tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) 162 | tripleDESSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) 163 | 164 | aes128Suites = [] 165 | aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) 166 | aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) 167 | aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA) 168 | 169 | aes256Suites = [] 170 | aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) 171 | aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) 172 | aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA) 173 | 174 | rc4Suites = [] 175 | rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA) 176 | 177 | 178 | class Fault: 179 | badUsername = 101 180 | badPassword = 102 181 | badA = 103 182 | clientSrpFaults = range(101,104) 183 | 184 | badVerifyMessage = 601 185 | clientCertFaults = range(601,602) 186 | 187 | badPremasterPadding = 501 188 | shortPremasterSecret = 502 189 | clientNoAuthFaults = range(501,503) 190 | 191 | badIdentifier = 401 192 | badSharedKey = 402 193 | clientSharedKeyFaults = range(401,403) 194 | 195 | badB = 201 196 | serverFaults = range(201,202) 197 | 198 | badFinished = 300 199 | badMAC = 301 200 | badPadding = 302 201 | genericFaults = range(300,303) 202 | 203 | faultAlerts = {\ 204 | badUsername: (AlertDescription.unknown_psk_identity, \ 205 | AlertDescription.bad_record_mac, \ 206 | AlertDescription.user_canceled),\ 207 | badPassword: (AlertDescription.bad_record_mac,),\ 208 | badA: (AlertDescription.illegal_parameter,),\ 209 | badIdentifier: (AlertDescription.handshake_failure,),\ 210 | badSharedKey: (AlertDescription.bad_record_mac,),\ 211 | badPremasterPadding: (AlertDescription.bad_record_mac,),\ 212 | shortPremasterSecret: (AlertDescription.bad_record_mac,),\ 213 | badVerifyMessage: (AlertDescription.decrypt_error,),\ 214 | badFinished: (AlertDescription.decrypt_error,),\ 215 | badMAC: (AlertDescription.bad_record_mac,),\ 216 | badPadding: (AlertDescription.bad_record_mac,) 217 | } 218 | 219 | faultNames = {\ 220 | badUsername: "bad username",\ 221 | badPassword: "bad password",\ 222 | badA: "bad A",\ 223 | badIdentifier: "bad identifier",\ 224 | badSharedKey: "bad sharedkey",\ 225 | badPremasterPadding: "bad premaster padding",\ 226 | shortPremasterSecret: "short premaster secret",\ 227 | badVerifyMessage: "bad verify message",\ 228 | badFinished: "bad finished message",\ 229 | badMAC: "bad MAC",\ 230 | badPadding: "bad padding" 231 | } 232 | -------------------------------------------------------------------------------- /src/tlslite/errors.py: -------------------------------------------------------------------------------- 1 | """Exception classes. 2 | @sort: TLSError, TLSAbruptCloseError, TLSAlert, TLSLocalAlert, TLSRemoteAlert, 3 | TLSAuthenticationError, TLSNoAuthenticationError, TLSAuthenticationTypeError, 4 | TLSFingerprintError, TLSAuthorizationError, TLSValidationError, TLSFaultError 5 | """ 6 | 7 | from constants import AlertDescription, AlertLevel 8 | 9 | class TLSError(Exception): 10 | """Base class for all TLS Lite exceptions.""" 11 | pass 12 | 13 | class TLSAbruptCloseError(TLSError): 14 | """The socket was closed without a proper TLS shutdown. 15 | 16 | The TLS specification mandates that an alert of some sort 17 | must be sent before the underlying socket is closed. If the socket 18 | is closed without this, it could signify that an attacker is trying 19 | to truncate the connection. It could also signify a misbehaving 20 | TLS implementation, or a random network failure. 21 | """ 22 | pass 23 | 24 | class TLSAlert(TLSError): 25 | """A TLS alert has been signalled.""" 26 | pass 27 | 28 | _descriptionStr = {\ 29 | AlertDescription.close_notify: "close_notify",\ 30 | AlertDescription.unexpected_message: "unexpected_message",\ 31 | AlertDescription.bad_record_mac: "bad_record_mac",\ 32 | AlertDescription.decryption_failed: "decryption_failed",\ 33 | AlertDescription.record_overflow: "record_overflow",\ 34 | AlertDescription.decompression_failure: "decompression_failure",\ 35 | AlertDescription.handshake_failure: "handshake_failure",\ 36 | AlertDescription.no_certificate: "no certificate",\ 37 | AlertDescription.bad_certificate: "bad_certificate",\ 38 | AlertDescription.unsupported_certificate: "unsupported_certificate",\ 39 | AlertDescription.certificate_revoked: "certificate_revoked",\ 40 | AlertDescription.certificate_expired: "certificate_expired",\ 41 | AlertDescription.certificate_unknown: "certificate_unknown",\ 42 | AlertDescription.illegal_parameter: "illegal_parameter",\ 43 | AlertDescription.unknown_ca: "unknown_ca",\ 44 | AlertDescription.access_denied: "access_denied",\ 45 | AlertDescription.decode_error: "decode_error",\ 46 | AlertDescription.decrypt_error: "decrypt_error",\ 47 | AlertDescription.export_restriction: "export_restriction",\ 48 | AlertDescription.protocol_version: "protocol_version",\ 49 | AlertDescription.insufficient_security: "insufficient_security",\ 50 | AlertDescription.internal_error: "internal_error",\ 51 | AlertDescription.user_canceled: "user_canceled",\ 52 | AlertDescription.no_renegotiation: "no_renegotiation",\ 53 | AlertDescription.unknown_psk_identity: "unknown_psk_identity", 54 | } 55 | 56 | class TLSLocalAlert(TLSAlert): 57 | """A TLS alert has been signalled by the local implementation. 58 | 59 | @type description: int 60 | @ivar description: Set to one of the constants in 61 | L{tlslite.constants.AlertDescription} 62 | 63 | @type level: int 64 | @ivar level: Set to one of the constants in 65 | L{tlslite.constants.AlertLevel} 66 | 67 | @type message: str 68 | @ivar message: Description of what went wrong. 69 | """ 70 | def __init__(self, alert, message=None): 71 | self.description = alert.description 72 | self.level = alert.level 73 | self.message = message 74 | 75 | def __str__(self): 76 | alertStr = TLSAlert._descriptionStr.get(self.description) 77 | if alertStr == None: 78 | alertStr = str(self.description) 79 | if self.message: 80 | return alertStr + ": " + self.message 81 | else: 82 | return alertStr 83 | 84 | class TLSRemoteAlert(TLSAlert): 85 | """A TLS alert has been signalled by the remote implementation. 86 | 87 | @type description: int 88 | @ivar description: Set to one of the constants in 89 | L{tlslite.constants.AlertDescription} 90 | 91 | @type level: int 92 | @ivar level: Set to one of the constants in 93 | L{tlslite.constants.AlertLevel} 94 | """ 95 | def __init__(self, alert): 96 | self.description = alert.description 97 | self.level = alert.level 98 | 99 | def __str__(self): 100 | alertStr = TLSAlert._descriptionStr.get(self.description) 101 | if alertStr == None: 102 | alertStr = str(self.description) 103 | return alertStr 104 | 105 | class TLSAuthenticationError(TLSError): 106 | """The handshake succeeded, but the other party's authentication 107 | was inadequate. 108 | 109 | This exception will only be raised when a 110 | L{tlslite.Checker.Checker} has been passed to a handshake function. 111 | The Checker will be invoked once the handshake completes, and if 112 | the Checker objects to how the other party authenticated, a 113 | subclass of this exception will be raised. 114 | """ 115 | pass 116 | 117 | class TLSNoAuthenticationError(TLSAuthenticationError): 118 | """The Checker was expecting the other party to authenticate with a 119 | certificate chain, but this did not occur.""" 120 | pass 121 | 122 | class TLSAuthenticationTypeError(TLSAuthenticationError): 123 | """The Checker was expecting the other party to authenticate with a 124 | different type of certificate chain.""" 125 | pass 126 | 127 | class TLSFingerprintError(TLSAuthenticationError): 128 | """The Checker was expecting the other party to authenticate with a 129 | certificate chain that matches a different fingerprint.""" 130 | pass 131 | 132 | class TLSAuthorizationError(TLSAuthenticationError): 133 | """The Checker was expecting the other party to authenticate with a 134 | certificate chain that has a different authorization.""" 135 | pass 136 | 137 | class TLSValidationError(TLSAuthenticationError): 138 | """The Checker has determined that the other party's certificate 139 | chain is invalid.""" 140 | pass 141 | 142 | class TLSFaultError(TLSError): 143 | """The other party responded incorrectly to an induced fault. 144 | 145 | This exception will only occur during fault testing, when a 146 | TLSConnection's fault variable is set to induce some sort of 147 | faulty behavior, and the other party doesn't respond appropriately. 148 | """ 149 | pass 150 | -------------------------------------------------------------------------------- /src/tlslite/integration/AsyncStateMachine.py: -------------------------------------------------------------------------------- 1 | """ 2 | A state machine for using TLS Lite with asynchronous I/O. 3 | """ 4 | 5 | class AsyncStateMachine: 6 | """ 7 | This is an abstract class that's used to integrate TLS Lite with 8 | asyncore and Twisted. 9 | 10 | This class signals wantsReadsEvent() and wantsWriteEvent(). When 11 | the underlying socket has become readable or writeable, the event 12 | should be passed to this class by calling inReadEvent() or 13 | inWriteEvent(). This class will then try to read or write through 14 | the socket, and will update its state appropriately. 15 | 16 | This class will forward higher-level events to its subclass. For 17 | example, when a complete TLS record has been received, 18 | outReadEvent() will be called with the decrypted data. 19 | """ 20 | 21 | def __init__(self): 22 | self._clear() 23 | 24 | def _clear(self): 25 | #These store the various asynchronous operations (i.e. 26 | #generators). Only one of them, at most, is ever active at a 27 | #time. 28 | self.handshaker = None 29 | self.closer = None 30 | self.reader = None 31 | self.writer = None 32 | 33 | #This stores the result from the last call to the 34 | #currently active operation. If 0 it indicates that the 35 | #operation wants to read, if 1 it indicates that the 36 | #operation wants to write. If None, there is no active 37 | #operation. 38 | self.result = None 39 | 40 | def _checkAssert(self, maxActive=1): 41 | #This checks that only one operation, at most, is 42 | #active, and that self.result is set appropriately. 43 | activeOps = 0 44 | if self.handshaker: 45 | activeOps += 1 46 | if self.closer: 47 | activeOps += 1 48 | if self.reader: 49 | activeOps += 1 50 | if self.writer: 51 | activeOps += 1 52 | 53 | if self.result == None: 54 | if activeOps != 0: 55 | raise AssertionError() 56 | elif self.result in (0,1): 57 | if activeOps != 1: 58 | raise AssertionError() 59 | else: 60 | raise AssertionError() 61 | if activeOps > maxActive: 62 | raise AssertionError() 63 | 64 | def wantsReadEvent(self): 65 | """If the state machine wants to read. 66 | 67 | If an operation is active, this returns whether or not the 68 | operation wants to read from the socket. If an operation is 69 | not active, this returns None. 70 | 71 | @rtype: bool or None 72 | @return: If the state machine wants to read. 73 | """ 74 | if self.result != None: 75 | return self.result == 0 76 | return None 77 | 78 | def wantsWriteEvent(self): 79 | """If the state machine wants to write. 80 | 81 | If an operation is active, this returns whether or not the 82 | operation wants to write to the socket. If an operation is 83 | not active, this returns None. 84 | 85 | @rtype: bool or None 86 | @return: If the state machine wants to write. 87 | """ 88 | if self.result != None: 89 | return self.result == 1 90 | return None 91 | 92 | def outConnectEvent(self): 93 | """Called when a handshake operation completes. 94 | 95 | May be overridden in subclass. 96 | """ 97 | pass 98 | 99 | def outCloseEvent(self): 100 | """Called when a close operation completes. 101 | 102 | May be overridden in subclass. 103 | """ 104 | pass 105 | 106 | def outReadEvent(self, readBuffer): 107 | """Called when a read operation completes. 108 | 109 | May be overridden in subclass.""" 110 | pass 111 | 112 | def outWriteEvent(self): 113 | """Called when a write operation completes. 114 | 115 | May be overridden in subclass.""" 116 | pass 117 | 118 | def inReadEvent(self): 119 | """Tell the state machine it can read from the socket.""" 120 | try: 121 | self._checkAssert() 122 | if self.handshaker: 123 | self._doHandshakeOp() 124 | elif self.closer: 125 | self._doCloseOp() 126 | elif self.reader: 127 | self._doReadOp() 128 | elif self.writer: 129 | self._doWriteOp() 130 | else: 131 | self.reader = self.tlsConnection.readAsync(16384) 132 | self._doReadOp() 133 | except: 134 | self._clear() 135 | raise 136 | 137 | def inWriteEvent(self): 138 | """Tell the state machine it can write to the socket.""" 139 | try: 140 | self._checkAssert() 141 | if self.handshaker: 142 | self._doHandshakeOp() 143 | elif self.closer: 144 | self._doCloseOp() 145 | elif self.reader: 146 | self._doReadOp() 147 | elif self.writer: 148 | self._doWriteOp() 149 | else: 150 | self.outWriteEvent() 151 | except: 152 | self._clear() 153 | raise 154 | 155 | def _doHandshakeOp(self): 156 | try: 157 | self.result = self.handshaker.next() 158 | except StopIteration: 159 | self.handshaker = None 160 | self.result = None 161 | self.outConnectEvent() 162 | 163 | def _doCloseOp(self): 164 | try: 165 | self.result = self.closer.next() 166 | except StopIteration: 167 | self.closer = None 168 | self.result = None 169 | self.outCloseEvent() 170 | 171 | def _doReadOp(self): 172 | self.result = self.reader.next() 173 | if not self.result in (0,1): 174 | readBuffer = self.result 175 | self.reader = None 176 | self.result = None 177 | self.outReadEvent(readBuffer) 178 | 179 | def _doWriteOp(self): 180 | try: 181 | self.result = self.writer.next() 182 | except StopIteration: 183 | self.writer = None 184 | self.result = None 185 | 186 | def setHandshakeOp(self, handshaker): 187 | """Start a handshake operation. 188 | 189 | @type handshaker: generator 190 | @param handshaker: A generator created by using one of the 191 | asynchronous handshake functions (i.e. handshakeServerAsync, or 192 | handshakeClientxxx(..., async=True). 193 | """ 194 | try: 195 | self._checkAssert(0) 196 | self.handshaker = handshaker 197 | self._doHandshakeOp() 198 | except: 199 | self._clear() 200 | raise 201 | 202 | def setServerHandshakeOp(self, **args): 203 | """Start a handshake operation. 204 | 205 | The arguments passed to this function will be forwarded to 206 | L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}. 207 | """ 208 | handshaker = self.tlsConnection.handshakeServerAsync(**args) 209 | self.setHandshakeOp(handshaker) 210 | 211 | def setCloseOp(self): 212 | """Start a close operation. 213 | """ 214 | try: 215 | self._checkAssert(0) 216 | self.closer = self.tlsConnection.closeAsync() 217 | self._doCloseOp() 218 | except: 219 | self._clear() 220 | raise 221 | 222 | def setWriteOp(self, writeBuffer): 223 | """Start a write operation. 224 | 225 | @type writeBuffer: str 226 | @param writeBuffer: The string to transmit. 227 | """ 228 | try: 229 | self._checkAssert(0) 230 | self.writer = self.tlsConnection.writeAsync(writeBuffer) 231 | self._doWriteOp() 232 | except: 233 | self._clear() 234 | raise 235 | 236 | -------------------------------------------------------------------------------- /src/tlslite/integration/ClientHelper.py: -------------------------------------------------------------------------------- 1 | """ 2 | A helper class for using TLS Lite with stdlib clients 3 | (httplib, xmlrpclib, imaplib, poplib). 4 | """ 5 | 6 | from tlslite.Checker import Checker 7 | 8 | class ClientHelper: 9 | """This is a helper class used to integrate TLS Lite with various 10 | TLS clients (e.g. poplib, smtplib, httplib, etc.)""" 11 | 12 | def __init__(self, 13 | username=None, password=None, sharedKey=None, 14 | certChain=None, privateKey=None, 15 | cryptoID=None, protocol=None, 16 | x509Fingerprint=None, 17 | x509TrustList=None, x509CommonName=None, 18 | settings = None): 19 | """ 20 | For client authentication, use one of these argument 21 | combinations: 22 | - username, password (SRP) 23 | - username, sharedKey (shared-key) 24 | - certChain, privateKey (certificate) 25 | 26 | For server authentication, you can either rely on the 27 | implicit mutual authentication performed by SRP or 28 | shared-keys, or you can do certificate-based server 29 | authentication with one of these argument combinations: 30 | - cryptoID[, protocol] (requires cryptoIDlib) 31 | - x509Fingerprint 32 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 33 | 34 | Certificate-based server authentication is compatible with 35 | SRP or certificate-based client authentication. It is 36 | not compatible with shared-keys. 37 | 38 | The constructor does not perform the TLS handshake itself, but 39 | simply stores these arguments for later. The handshake is 40 | performed only when this class needs to connect with the 41 | server. Then you should be prepared to handle TLS-specific 42 | exceptions. See the client handshake functions in 43 | L{tlslite.TLSConnection.TLSConnection} for details on which 44 | exceptions might be raised. 45 | 46 | @type username: str 47 | @param username: SRP or shared-key username. Requires the 48 | 'password' or 'sharedKey' argument. 49 | 50 | @type password: str 51 | @param password: SRP password for mutual authentication. 52 | Requires the 'username' argument. 53 | 54 | @type sharedKey: str 55 | @param sharedKey: Shared key for mutual authentication. 56 | Requires the 'username' argument. 57 | 58 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 59 | L{cryptoIDlib.CertChain.CertChain} 60 | @param certChain: Certificate chain for client authentication. 61 | Requires the 'privateKey' argument. Excludes the SRP or 62 | shared-key related arguments. 63 | 64 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 65 | @param privateKey: Private key for client authentication. 66 | Requires the 'certChain' argument. Excludes the SRP or 67 | shared-key related arguments. 68 | 69 | @type cryptoID: str 70 | @param cryptoID: cryptoID for server authentication. Mutually 71 | exclusive with the 'x509...' arguments. 72 | 73 | @type protocol: str 74 | @param protocol: cryptoID protocol URI for server 75 | authentication. Requires the 'cryptoID' argument. 76 | 77 | @type x509Fingerprint: str 78 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 79 | server authentication. Mutually exclusive with the 'cryptoID' 80 | and 'x509TrustList' arguments. 81 | 82 | @type x509TrustList: list of L{tlslite.X509.X509} 83 | @param x509TrustList: A list of trusted root certificates. The 84 | other party must present a certificate chain which extends to 85 | one of these root certificates. The cryptlib_py module must be 86 | installed to use this parameter. Mutually exclusive with the 87 | 'cryptoID' and 'x509Fingerprint' arguments. 88 | 89 | @type x509CommonName: str 90 | @param x509CommonName: The end-entity certificate's 'CN' field 91 | must match this value. For a web server, this is typically a 92 | server name such as 'www.amazon.com'. Mutually exclusive with 93 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 94 | 'x509TrustList' argument. 95 | 96 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 97 | @param settings: Various settings which can be used to control 98 | the ciphersuites, certificate types, and SSL/TLS versions 99 | offered by the client. 100 | """ 101 | 102 | self.username = None 103 | self.password = None 104 | self.sharedKey = None 105 | self.certChain = None 106 | self.privateKey = None 107 | self.checker = None 108 | 109 | #SRP Authentication 110 | if username and password and not \ 111 | (sharedKey or certChain or privateKey): 112 | self.username = username 113 | self.password = password 114 | 115 | #Shared Key Authentication 116 | elif username and sharedKey and not \ 117 | (password or certChain or privateKey): 118 | self.username = username 119 | self.sharedKey = sharedKey 120 | 121 | #Certificate Chain Authentication 122 | elif certChain and privateKey and not \ 123 | (username or password or sharedKey): 124 | self.certChain = certChain 125 | self.privateKey = privateKey 126 | 127 | #No Authentication 128 | elif not password and not username and not \ 129 | sharedKey and not certChain and not privateKey: 130 | pass 131 | 132 | else: 133 | raise ValueError("Bad parameters") 134 | 135 | #Authenticate the server based on its cryptoID or fingerprint 136 | if sharedKey and (cryptoID or protocol or x509Fingerprint): 137 | raise ValueError("Can't use shared keys with other forms of"\ 138 | "authentication") 139 | 140 | self.checker = Checker(cryptoID, protocol, x509Fingerprint, 141 | x509TrustList, x509CommonName) 142 | self.settings = settings 143 | 144 | self.tlsSession = None 145 | 146 | def _handshake(self, tlsConnection): 147 | if self.username and self.password: 148 | tlsConnection.handshakeClientSRP(username=self.username, 149 | password=self.password, 150 | checker=self.checker, 151 | settings=self.settings, 152 | session=self.tlsSession) 153 | elif self.username and self.sharedKey: 154 | tlsConnection.handshakeClientSharedKey(username=self.username, 155 | sharedKey=self.sharedKey, 156 | settings=self.settings) 157 | else: 158 | tlsConnection.handshakeClientCert(certChain=self.certChain, 159 | privateKey=self.privateKey, 160 | checker=self.checker, 161 | settings=self.settings, 162 | session=self.tlsSession) 163 | self.tlsSession = tlsConnection.session -------------------------------------------------------------------------------- /src/tlslite/integration/HTTPTLSConnection.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + httplib.""" 2 | 3 | import socket 4 | import httplib 5 | from tlslite.TLSConnection import TLSConnection 6 | from tlslite.integration.ClientHelper import ClientHelper 7 | 8 | 9 | class HTTPBaseTLSConnection(httplib.HTTPConnection): 10 | """This abstract class provides a framework for adding TLS support 11 | to httplib.""" 12 | 13 | default_port = 443 14 | 15 | def __init__(self, host, port=None, strict=None): 16 | if strict == None: 17 | #Python 2.2 doesn't support strict 18 | httplib.HTTPConnection.__init__(self, host, port) 19 | else: 20 | httplib.HTTPConnection.__init__(self, host, port, strict) 21 | 22 | def connect(self): 23 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 24 | if hasattr(sock, 'settimeout'): 25 | sock.settimeout(10) 26 | sock.connect((self.host, self.port)) 27 | 28 | #Use a TLSConnection to emulate a socket 29 | self.sock = TLSConnection(sock) 30 | 31 | #When httplib closes this, close the socket 32 | self.sock.closeSocket = True 33 | self._handshake(self.sock) 34 | 35 | def _handshake(self, tlsConnection): 36 | """Called to perform some sort of handshake. 37 | 38 | This method must be overridden in a subclass to do some type of 39 | handshake. This method will be called after the socket has 40 | been connected but before any data has been sent. If this 41 | method does not raise an exception, the TLS connection will be 42 | considered valid. 43 | 44 | This method may (or may not) be called every time an HTTP 45 | request is performed, depending on whether the underlying HTTP 46 | connection is persistent. 47 | 48 | @type tlsConnection: L{tlslite.TLSConnection.TLSConnection} 49 | @param tlsConnection: The connection to perform the handshake 50 | on. 51 | """ 52 | raise NotImplementedError() 53 | 54 | 55 | class HTTPTLSConnection(HTTPBaseTLSConnection, ClientHelper): 56 | """This class extends L{HTTPBaseTLSConnection} to support the 57 | common types of handshaking.""" 58 | 59 | def __init__(self, host, port=None, 60 | username=None, password=None, sharedKey=None, 61 | certChain=None, privateKey=None, 62 | cryptoID=None, protocol=None, 63 | x509Fingerprint=None, 64 | x509TrustList=None, x509CommonName=None, 65 | settings = None): 66 | """Create a new HTTPTLSConnection. 67 | 68 | For client authentication, use one of these argument 69 | combinations: 70 | - username, password (SRP) 71 | - username, sharedKey (shared-key) 72 | - certChain, privateKey (certificate) 73 | 74 | For server authentication, you can either rely on the 75 | implicit mutual authentication performed by SRP or 76 | shared-keys, or you can do certificate-based server 77 | authentication with one of these argument combinations: 78 | - cryptoID[, protocol] (requires cryptoIDlib) 79 | - x509Fingerprint 80 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 81 | 82 | Certificate-based server authentication is compatible with 83 | SRP or certificate-based client authentication. It is 84 | not compatible with shared-keys. 85 | 86 | The constructor does not perform the TLS handshake itself, but 87 | simply stores these arguments for later. The handshake is 88 | performed only when this class needs to connect with the 89 | server. Thus you should be prepared to handle TLS-specific 90 | exceptions when calling methods inherited from 91 | L{httplib.HTTPConnection} such as request(), connect(), and 92 | send(). See the client handshake functions in 93 | L{tlslite.TLSConnection.TLSConnection} for details on which 94 | exceptions might be raised. 95 | 96 | @type host: str 97 | @param host: Server to connect to. 98 | 99 | @type port: int 100 | @param port: Port to connect to. 101 | 102 | @type username: str 103 | @param username: SRP or shared-key username. Requires the 104 | 'password' or 'sharedKey' argument. 105 | 106 | @type password: str 107 | @param password: SRP password for mutual authentication. 108 | Requires the 'username' argument. 109 | 110 | @type sharedKey: str 111 | @param sharedKey: Shared key for mutual authentication. 112 | Requires the 'username' argument. 113 | 114 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 115 | L{cryptoIDlib.CertChain.CertChain} 116 | @param certChain: Certificate chain for client authentication. 117 | Requires the 'privateKey' argument. Excludes the SRP or 118 | shared-key related arguments. 119 | 120 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 121 | @param privateKey: Private key for client authentication. 122 | Requires the 'certChain' argument. Excludes the SRP or 123 | shared-key related arguments. 124 | 125 | @type cryptoID: str 126 | @param cryptoID: cryptoID for server authentication. Mutually 127 | exclusive with the 'x509...' arguments. 128 | 129 | @type protocol: str 130 | @param protocol: cryptoID protocol URI for server 131 | authentication. Requires the 'cryptoID' argument. 132 | 133 | @type x509Fingerprint: str 134 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 135 | server authentication. Mutually exclusive with the 'cryptoID' 136 | and 'x509TrustList' arguments. 137 | 138 | @type x509TrustList: list of L{tlslite.X509.X509} 139 | @param x509TrustList: A list of trusted root certificates. The 140 | other party must present a certificate chain which extends to 141 | one of these root certificates. The cryptlib_py module must be 142 | installed to use this parameter. Mutually exclusive with the 143 | 'cryptoID' and 'x509Fingerprint' arguments. 144 | 145 | @type x509CommonName: str 146 | @param x509CommonName: The end-entity certificate's 'CN' field 147 | must match this value. For a web server, this is typically a 148 | server name such as 'www.amazon.com'. Mutually exclusive with 149 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 150 | 'x509TrustList' argument. 151 | 152 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 153 | @param settings: Various settings which can be used to control 154 | the ciphersuites, certificate types, and SSL/TLS versions 155 | offered by the client. 156 | """ 157 | 158 | HTTPBaseTLSConnection.__init__(self, host, port) 159 | 160 | ClientHelper.__init__(self, 161 | username, password, sharedKey, 162 | certChain, privateKey, 163 | cryptoID, protocol, 164 | x509Fingerprint, 165 | x509TrustList, x509CommonName, 166 | settings) 167 | 168 | def _handshake(self, tlsConnection): 169 | ClientHelper._handshake(self, tlsConnection) -------------------------------------------------------------------------------- /src/tlslite/integration/IMAP4_TLS.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + imaplib.""" 2 | 3 | import socket 4 | from imaplib import IMAP4 5 | from tlslite.TLSConnection import TLSConnection 6 | from tlslite.integration.ClientHelper import ClientHelper 7 | 8 | # IMAP TLS PORT 9 | IMAP4_TLS_PORT = 993 10 | 11 | class IMAP4_TLS(IMAP4, ClientHelper): 12 | """This class extends L{imaplib.IMAP4} with TLS support.""" 13 | 14 | def __init__(self, host = '', port = IMAP4_TLS_PORT, 15 | username=None, password=None, sharedKey=None, 16 | certChain=None, privateKey=None, 17 | cryptoID=None, protocol=None, 18 | x509Fingerprint=None, 19 | x509TrustList=None, x509CommonName=None, 20 | settings=None): 21 | """Create a new IMAP4_TLS. 22 | 23 | For client authentication, use one of these argument 24 | combinations: 25 | - username, password (SRP) 26 | - username, sharedKey (shared-key) 27 | - certChain, privateKey (certificate) 28 | 29 | For server authentication, you can either rely on the 30 | implicit mutual authentication performed by SRP or 31 | shared-keys, or you can do certificate-based server 32 | authentication with one of these argument combinations: 33 | - cryptoID[, protocol] (requires cryptoIDlib) 34 | - x509Fingerprint 35 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 36 | 37 | Certificate-based server authentication is compatible with 38 | SRP or certificate-based client authentication. It is 39 | not compatible with shared-keys. 40 | 41 | The caller should be prepared to handle TLS-specific 42 | exceptions. See the client handshake functions in 43 | L{tlslite.TLSConnection.TLSConnection} for details on which 44 | exceptions might be raised. 45 | 46 | @type host: str 47 | @param host: Server to connect to. 48 | 49 | @type port: int 50 | @param port: Port to connect to. 51 | 52 | @type username: str 53 | @param username: SRP or shared-key username. Requires the 54 | 'password' or 'sharedKey' argument. 55 | 56 | @type password: str 57 | @param password: SRP password for mutual authentication. 58 | Requires the 'username' argument. 59 | 60 | @type sharedKey: str 61 | @param sharedKey: Shared key for mutual authentication. 62 | Requires the 'username' argument. 63 | 64 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 65 | L{cryptoIDlib.CertChain.CertChain} 66 | @param certChain: Certificate chain for client authentication. 67 | Requires the 'privateKey' argument. Excludes the SRP or 68 | shared-key related arguments. 69 | 70 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 71 | @param privateKey: Private key for client authentication. 72 | Requires the 'certChain' argument. Excludes the SRP or 73 | shared-key related arguments. 74 | 75 | @type cryptoID: str 76 | @param cryptoID: cryptoID for server authentication. Mutually 77 | exclusive with the 'x509...' arguments. 78 | 79 | @type protocol: str 80 | @param protocol: cryptoID protocol URI for server 81 | authentication. Requires the 'cryptoID' argument. 82 | 83 | @type x509Fingerprint: str 84 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 85 | server authentication. Mutually exclusive with the 'cryptoID' 86 | and 'x509TrustList' arguments. 87 | 88 | @type x509TrustList: list of L{tlslite.X509.X509} 89 | @param x509TrustList: A list of trusted root certificates. The 90 | other party must present a certificate chain which extends to 91 | one of these root certificates. The cryptlib_py module must be 92 | installed to use this parameter. Mutually exclusive with the 93 | 'cryptoID' and 'x509Fingerprint' arguments. 94 | 95 | @type x509CommonName: str 96 | @param x509CommonName: The end-entity certificate's 'CN' field 97 | must match this value. For a web server, this is typically a 98 | server name such as 'www.amazon.com'. Mutually exclusive with 99 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 100 | 'x509TrustList' argument. 101 | 102 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 103 | @param settings: Various settings which can be used to control 104 | the ciphersuites, certificate types, and SSL/TLS versions 105 | offered by the client. 106 | """ 107 | 108 | ClientHelper.__init__(self, 109 | username, password, sharedKey, 110 | certChain, privateKey, 111 | cryptoID, protocol, 112 | x509Fingerprint, 113 | x509TrustList, x509CommonName, 114 | settings) 115 | 116 | IMAP4.__init__(self, host, port) 117 | 118 | 119 | def open(self, host = '', port = IMAP4_TLS_PORT): 120 | """Setup connection to remote server on "host:port". 121 | 122 | This connection will be used by the routines: 123 | read, readline, send, shutdown. 124 | """ 125 | self.host = host 126 | self.port = port 127 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 128 | self.sock.connect((host, port)) 129 | self.sock = TLSConnection(self.sock) 130 | self.sock.closeSocket = True 131 | ClientHelper._handshake(self, self.sock) 132 | self.file = self.sock.makefile('rb') -------------------------------------------------------------------------------- /src/tlslite/integration/IntegrationHelper.py: -------------------------------------------------------------------------------- 1 | 2 | class IntegrationHelper: 3 | 4 | def __init__(self, 5 | username=None, password=None, sharedKey=None, 6 | certChain=None, privateKey=None, 7 | cryptoID=None, protocol=None, 8 | x509Fingerprint=None, 9 | x509TrustList=None, x509CommonName=None, 10 | settings = None): 11 | 12 | self.username = None 13 | self.password = None 14 | self.sharedKey = None 15 | self.certChain = None 16 | self.privateKey = None 17 | self.checker = None 18 | 19 | #SRP Authentication 20 | if username and password and not \ 21 | (sharedKey or certChain or privateKey): 22 | self.username = username 23 | self.password = password 24 | 25 | #Shared Key Authentication 26 | elif username and sharedKey and not \ 27 | (password or certChain or privateKey): 28 | self.username = username 29 | self.sharedKey = sharedKey 30 | 31 | #Certificate Chain Authentication 32 | elif certChain and privateKey and not \ 33 | (username or password or sharedKey): 34 | self.certChain = certChain 35 | self.privateKey = privateKey 36 | 37 | #No Authentication 38 | elif not password and not username and not \ 39 | sharedKey and not certChain and not privateKey: 40 | pass 41 | 42 | else: 43 | raise ValueError("Bad parameters") 44 | 45 | #Authenticate the server based on its cryptoID or fingerprint 46 | if sharedKey and (cryptoID or protocol or x509Fingerprint): 47 | raise ValueError("Can't use shared keys with other forms of"\ 48 | "authentication") 49 | 50 | self.checker = Checker(cryptoID, protocol, x509Fingerprint, 51 | x509TrustList, x509CommonName) 52 | self.settings = settings -------------------------------------------------------------------------------- /src/tlslite/integration/POP3_TLS.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + poplib.""" 2 | 3 | import socket 4 | from poplib import POP3 5 | from tlslite.TLSConnection import TLSConnection 6 | from tlslite.integration.ClientHelper import ClientHelper 7 | 8 | # POP TLS PORT 9 | POP3_TLS_PORT = 995 10 | 11 | class POP3_TLS(POP3, ClientHelper): 12 | """This class extends L{poplib.POP3} with TLS support.""" 13 | 14 | def __init__(self, host, port = POP3_TLS_PORT, 15 | username=None, password=None, sharedKey=None, 16 | certChain=None, privateKey=None, 17 | cryptoID=None, protocol=None, 18 | x509Fingerprint=None, 19 | x509TrustList=None, x509CommonName=None, 20 | settings=None): 21 | """Create a new POP3_TLS. 22 | 23 | For client authentication, use one of these argument 24 | combinations: 25 | - username, password (SRP) 26 | - username, sharedKey (shared-key) 27 | - certChain, privateKey (certificate) 28 | 29 | For server authentication, you can either rely on the 30 | implicit mutual authentication performed by SRP or 31 | shared-keys, or you can do certificate-based server 32 | authentication with one of these argument combinations: 33 | - cryptoID[, protocol] (requires cryptoIDlib) 34 | - x509Fingerprint 35 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 36 | 37 | Certificate-based server authentication is compatible with 38 | SRP or certificate-based client authentication. It is 39 | not compatible with shared-keys. 40 | 41 | The caller should be prepared to handle TLS-specific 42 | exceptions. See the client handshake functions in 43 | L{tlslite.TLSConnection.TLSConnection} for details on which 44 | exceptions might be raised. 45 | 46 | @type host: str 47 | @param host: Server to connect to. 48 | 49 | @type port: int 50 | @param port: Port to connect to. 51 | 52 | @type username: str 53 | @param username: SRP or shared-key username. Requires the 54 | 'password' or 'sharedKey' argument. 55 | 56 | @type password: str 57 | @param password: SRP password for mutual authentication. 58 | Requires the 'username' argument. 59 | 60 | @type sharedKey: str 61 | @param sharedKey: Shared key for mutual authentication. 62 | Requires the 'username' argument. 63 | 64 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 65 | L{cryptoIDlib.CertChain.CertChain} 66 | @param certChain: Certificate chain for client authentication. 67 | Requires the 'privateKey' argument. Excludes the SRP or 68 | shared-key related arguments. 69 | 70 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 71 | @param privateKey: Private key for client authentication. 72 | Requires the 'certChain' argument. Excludes the SRP or 73 | shared-key related arguments. 74 | 75 | @type cryptoID: str 76 | @param cryptoID: cryptoID for server authentication. Mutually 77 | exclusive with the 'x509...' arguments. 78 | 79 | @type protocol: str 80 | @param protocol: cryptoID protocol URI for server 81 | authentication. Requires the 'cryptoID' argument. 82 | 83 | @type x509Fingerprint: str 84 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 85 | server authentication. Mutually exclusive with the 'cryptoID' 86 | and 'x509TrustList' arguments. 87 | 88 | @type x509TrustList: list of L{tlslite.X509.X509} 89 | @param x509TrustList: A list of trusted root certificates. The 90 | other party must present a certificate chain which extends to 91 | one of these root certificates. The cryptlib_py module must be 92 | installed to use this parameter. Mutually exclusive with the 93 | 'cryptoID' and 'x509Fingerprint' arguments. 94 | 95 | @type x509CommonName: str 96 | @param x509CommonName: The end-entity certificate's 'CN' field 97 | must match this value. For a web server, this is typically a 98 | server name such as 'www.amazon.com'. Mutually exclusive with 99 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 100 | 'x509TrustList' argument. 101 | 102 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 103 | @param settings: Various settings which can be used to control 104 | the ciphersuites, certificate types, and SSL/TLS versions 105 | offered by the client. 106 | """ 107 | 108 | self.host = host 109 | self.port = port 110 | msg = "getaddrinfo returns an empty list" 111 | self.sock = None 112 | for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): 113 | af, socktype, proto, canonname, sa = res 114 | try: 115 | self.sock = socket.socket(af, socktype, proto) 116 | self.sock.connect(sa) 117 | except socket.error, msg: 118 | if self.sock: 119 | self.sock.close() 120 | self.sock = None 121 | continue 122 | break 123 | if not self.sock: 124 | raise socket.error, msg 125 | 126 | ### New code below (all else copied from poplib) 127 | ClientHelper.__init__(self, 128 | username, password, sharedKey, 129 | certChain, privateKey, 130 | cryptoID, protocol, 131 | x509Fingerprint, 132 | x509TrustList, x509CommonName, 133 | settings) 134 | 135 | self.sock = TLSConnection(self.sock) 136 | self.sock.closeSocket = True 137 | ClientHelper._handshake(self, self.sock) 138 | ### 139 | 140 | self.file = self.sock.makefile('rb') 141 | self._debugging = 0 142 | self.welcome = self._getresp() -------------------------------------------------------------------------------- /src/tlslite/integration/SMTP_TLS.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + smtplib.""" 2 | 3 | from smtplib import SMTP 4 | from tlslite.TLSConnection import TLSConnection 5 | from tlslite.integration.ClientHelper import ClientHelper 6 | 7 | class SMTP_TLS(SMTP): 8 | """This class extends L{smtplib.SMTP} with TLS support.""" 9 | 10 | def starttls(self, 11 | username=None, password=None, sharedKey=None, 12 | certChain=None, privateKey=None, 13 | cryptoID=None, protocol=None, 14 | x509Fingerprint=None, 15 | x509TrustList=None, x509CommonName=None, 16 | settings=None): 17 | """Puts the connection to the SMTP server into TLS mode. 18 | 19 | If the server supports TLS, this will encrypt the rest of the SMTP 20 | session. 21 | 22 | For client authentication, use one of these argument 23 | combinations: 24 | - username, password (SRP) 25 | - username, sharedKey (shared-key) 26 | - certChain, privateKey (certificate) 27 | 28 | For server authentication, you can either rely on the 29 | implicit mutual authentication performed by SRP or 30 | shared-keys, or you can do certificate-based server 31 | authentication with one of these argument combinations: 32 | - cryptoID[, protocol] (requires cryptoIDlib) 33 | - x509Fingerprint 34 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 35 | 36 | Certificate-based server authentication is compatible with 37 | SRP or certificate-based client authentication. It is 38 | not compatible with shared-keys. 39 | 40 | The caller should be prepared to handle TLS-specific 41 | exceptions. See the client handshake functions in 42 | L{tlslite.TLSConnection.TLSConnection} for details on which 43 | exceptions might be raised. 44 | 45 | @type username: str 46 | @param username: SRP or shared-key username. Requires the 47 | 'password' or 'sharedKey' argument. 48 | 49 | @type password: str 50 | @param password: SRP password for mutual authentication. 51 | Requires the 'username' argument. 52 | 53 | @type sharedKey: str 54 | @param sharedKey: Shared key for mutual authentication. 55 | Requires the 'username' argument. 56 | 57 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 58 | L{cryptoIDlib.CertChain.CertChain} 59 | @param certChain: Certificate chain for client authentication. 60 | Requires the 'privateKey' argument. Excludes the SRP or 61 | shared-key related arguments. 62 | 63 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 64 | @param privateKey: Private key for client authentication. 65 | Requires the 'certChain' argument. Excludes the SRP or 66 | shared-key related arguments. 67 | 68 | @type cryptoID: str 69 | @param cryptoID: cryptoID for server authentication. Mutually 70 | exclusive with the 'x509...' arguments. 71 | 72 | @type protocol: str 73 | @param protocol: cryptoID protocol URI for server 74 | authentication. Requires the 'cryptoID' argument. 75 | 76 | @type x509Fingerprint: str 77 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 78 | server authentication. Mutually exclusive with the 'cryptoID' 79 | and 'x509TrustList' arguments. 80 | 81 | @type x509TrustList: list of L{tlslite.X509.X509} 82 | @param x509TrustList: A list of trusted root certificates. The 83 | other party must present a certificate chain which extends to 84 | one of these root certificates. The cryptlib_py module must be 85 | installed to use this parameter. Mutually exclusive with the 86 | 'cryptoID' and 'x509Fingerprint' arguments. 87 | 88 | @type x509CommonName: str 89 | @param x509CommonName: The end-entity certificate's 'CN' field 90 | must match this value. For a web server, this is typically a 91 | server name such as 'www.amazon.com'. Mutually exclusive with 92 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 93 | 'x509TrustList' argument. 94 | 95 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 96 | @param settings: Various settings which can be used to control 97 | the ciphersuites, certificate types, and SSL/TLS versions 98 | offered by the client. 99 | """ 100 | (resp, reply) = self.docmd("STARTTLS") 101 | if resp == 220: 102 | helper = ClientHelper( 103 | username, password, sharedKey, 104 | certChain, privateKey, 105 | cryptoID, protocol, 106 | x509Fingerprint, 107 | x509TrustList, x509CommonName, 108 | settings) 109 | conn = TLSConnection(self.sock) 110 | conn.closeSocket = True 111 | helper._handshake(conn) 112 | self.sock = conn 113 | self.file = conn.makefile('rb') 114 | return (resp, reply) -------------------------------------------------------------------------------- /src/tlslite/integration/TLSAsyncDispatcherMixIn.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + asyncore.""" 2 | 3 | 4 | import asyncore 5 | from tlslite.TLSConnection import TLSConnection 6 | from AsyncStateMachine import AsyncStateMachine 7 | 8 | 9 | class TLSAsyncDispatcherMixIn(AsyncStateMachine): 10 | """This class can be "mixed in" with an 11 | L{asyncore.dispatcher} to add TLS support. 12 | 13 | This class essentially sits between the dispatcher and the select 14 | loop, intercepting events and only calling the dispatcher when 15 | applicable. 16 | 17 | In the case of handle_read(), a read operation will be activated, 18 | and when it completes, the bytes will be placed in a buffer where 19 | the dispatcher can retrieve them by calling recv(), and the 20 | dispatcher's handle_read() will be called. 21 | 22 | In the case of handle_write(), the dispatcher's handle_write() will 23 | be called, and when it calls send(), a write operation will be 24 | activated. 25 | 26 | To use this class, you must combine it with an asyncore.dispatcher, 27 | and pass in a handshake operation with setServerHandshakeOp(). 28 | 29 | Below is an example of using this class with medusa. This class is 30 | mixed in with http_channel to create http_tls_channel. Note: 31 | 1. the mix-in is listed first in the inheritance list 32 | 33 | 2. the input buffer size must be at least 16K, otherwise the 34 | dispatcher might not read all the bytes from the TLS layer, 35 | leaving some bytes in limbo. 36 | 37 | 3. IE seems to have a problem receiving a whole HTTP response in a 38 | single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't 39 | be displayed on IE. 40 | 41 | Add the following text into 'start_medusa.py', in the 'HTTP Server' 42 | section:: 43 | 44 | from tlslite.api import * 45 | s = open("./serverX509Cert.pem").read() 46 | x509 = X509() 47 | x509.parse(s) 48 | certChain = X509CertChain([x509]) 49 | 50 | s = open("./serverX509Key.pem").read() 51 | privateKey = parsePEMKey(s, private=True) 52 | 53 | class http_tls_channel(TLSAsyncDispatcherMixIn, 54 | http_server.http_channel): 55 | ac_in_buffer_size = 16384 56 | 57 | def __init__ (self, server, conn, addr): 58 | http_server.http_channel.__init__(self, server, conn, addr) 59 | TLSAsyncDispatcherMixIn.__init__(self, conn) 60 | self.tlsConnection.ignoreAbruptClose = True 61 | self.setServerHandshakeOp(certChain=certChain, 62 | privateKey=privateKey) 63 | 64 | hs.channel_class = http_tls_channel 65 | 66 | If the TLS layer raises an exception, the exception will be caught 67 | in asyncore.dispatcher, which will call close() on this class. The 68 | TLS layer always closes the TLS connection before raising an 69 | exception, so the close operation will complete right away, causing 70 | asyncore.dispatcher.close() to be called, which closes the socket 71 | and removes this instance from the asyncore loop. 72 | 73 | """ 74 | 75 | 76 | def __init__(self, sock=None): 77 | AsyncStateMachine.__init__(self) 78 | 79 | if sock: 80 | self.tlsConnection = TLSConnection(sock) 81 | 82 | #Calculate the sibling I'm being mixed in with. 83 | #This is necessary since we override functions 84 | #like readable(), handle_read(), etc., but we 85 | #also want to call the sibling's versions. 86 | for cl in self.__class__.__bases__: 87 | if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine: 88 | self.siblingClass = cl 89 | break 90 | else: 91 | raise AssertionError() 92 | 93 | def readable(self): 94 | result = self.wantsReadEvent() 95 | if result != None: 96 | return result 97 | return self.siblingClass.readable(self) 98 | 99 | def writable(self): 100 | result = self.wantsWriteEvent() 101 | if result != None: 102 | return result 103 | return self.siblingClass.writable(self) 104 | 105 | def handle_read(self): 106 | self.inReadEvent() 107 | 108 | def handle_write(self): 109 | self.inWriteEvent() 110 | 111 | def outConnectEvent(self): 112 | self.siblingClass.handle_connect(self) 113 | 114 | def outCloseEvent(self): 115 | asyncore.dispatcher.close(self) 116 | 117 | def outReadEvent(self, readBuffer): 118 | self.readBuffer = readBuffer 119 | self.siblingClass.handle_read(self) 120 | 121 | def outWriteEvent(self): 122 | self.siblingClass.handle_write(self) 123 | 124 | def recv(self, bufferSize=16384): 125 | if bufferSize < 16384 or self.readBuffer == None: 126 | raise AssertionError() 127 | returnValue = self.readBuffer 128 | self.readBuffer = None 129 | return returnValue 130 | 131 | def send(self, writeBuffer): 132 | self.setWriteOp(writeBuffer) 133 | return len(writeBuffer) 134 | 135 | def close(self): 136 | if hasattr(self, "tlsConnection"): 137 | self.setCloseOp() 138 | else: 139 | asyncore.dispatcher.close(self) -------------------------------------------------------------------------------- /src/tlslite/integration/TLSSocketServerMixIn.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + SocketServer.""" 2 | 3 | from tlslite.TLSConnection import TLSConnection 4 | 5 | class TLSSocketServerMixIn: 6 | """ 7 | This class can be mixed in with any L{SocketServer.TCPServer} to 8 | add TLS support. 9 | 10 | To use this class, define a new class that inherits from it and 11 | some L{SocketServer.TCPServer} (with the mix-in first). Then 12 | implement the handshake() method, doing some sort of server 13 | handshake on the connection argument. If the handshake method 14 | returns True, the RequestHandler will be triggered. Below is a 15 | complete example of a threaded HTTPS server:: 16 | 17 | from SocketServer import * 18 | from BaseHTTPServer import * 19 | from SimpleHTTPServer import * 20 | from tlslite.api import * 21 | 22 | s = open("./serverX509Cert.pem").read() 23 | x509 = X509() 24 | x509.parse(s) 25 | certChain = X509CertChain([x509]) 26 | 27 | s = open("./serverX509Key.pem").read() 28 | privateKey = parsePEMKey(s, private=True) 29 | 30 | sessionCache = SessionCache() 31 | 32 | class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, 33 | HTTPServer): 34 | def handshake(self, tlsConnection): 35 | try: 36 | tlsConnection.handshakeServer(certChain=certChain, 37 | privateKey=privateKey, 38 | sessionCache=sessionCache) 39 | tlsConnection.ignoreAbruptClose = True 40 | return True 41 | except TLSError, error: 42 | print "Handshake failure:", str(error) 43 | return False 44 | 45 | httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler) 46 | httpd.serve_forever() 47 | """ 48 | 49 | 50 | def finish_request(self, sock, client_address): 51 | tlsConnection = TLSConnection(sock) 52 | if self.handshake(tlsConnection) == True: 53 | self.RequestHandlerClass(tlsConnection, client_address, self) 54 | tlsConnection.close() 55 | 56 | #Implement this method to do some form of handshaking. Return True 57 | #if the handshake finishes properly and the request is authorized. 58 | def handshake(self, tlsConnection): 59 | raise NotImplementedError() -------------------------------------------------------------------------------- /src/tlslite/integration/TLSTwistedProtocolWrapper.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + Twisted.""" 2 | 3 | from twisted.protocols.policies import ProtocolWrapper, WrappingFactory 4 | from twisted.python.failure import Failure 5 | 6 | from AsyncStateMachine import AsyncStateMachine 7 | from tlslite.TLSConnection import TLSConnection 8 | from tlslite.errors import * 9 | 10 | import socket 11 | import errno 12 | 13 | 14 | #The TLSConnection is created around a "fake socket" that 15 | #plugs it into the underlying Twisted transport 16 | class _FakeSocket: 17 | def __init__(self, wrapper): 18 | self.wrapper = wrapper 19 | self.data = "" 20 | 21 | def send(self, data): 22 | ProtocolWrapper.write(self.wrapper, data) 23 | return len(data) 24 | 25 | def recv(self, numBytes): 26 | if self.data == "": 27 | raise socket.error, (errno.EWOULDBLOCK, "") 28 | returnData = self.data[:numBytes] 29 | self.data = self.data[numBytes:] 30 | return returnData 31 | 32 | class TLSTwistedProtocolWrapper(ProtocolWrapper, AsyncStateMachine): 33 | """This class can wrap Twisted protocols to add TLS support. 34 | 35 | Below is a complete example of using TLS Lite with a Twisted echo 36 | server. 37 | 38 | There are two server implementations below. Echo is the original 39 | protocol, which is oblivious to TLS. Echo1 subclasses Echo and 40 | negotiates TLS when the client connects. Echo2 subclasses Echo and 41 | negotiates TLS when the client sends "STARTTLS":: 42 | 43 | from twisted.internet.protocol import Protocol, Factory 44 | from twisted.internet import reactor 45 | from twisted.protocols.policies import WrappingFactory 46 | from twisted.protocols.basic import LineReceiver 47 | from twisted.python import log 48 | from twisted.python.failure import Failure 49 | import sys 50 | from tlslite.api import * 51 | 52 | s = open("./serverX509Cert.pem").read() 53 | x509 = X509() 54 | x509.parse(s) 55 | certChain = X509CertChain([x509]) 56 | 57 | s = open("./serverX509Key.pem").read() 58 | privateKey = parsePEMKey(s, private=True) 59 | 60 | verifierDB = VerifierDB("verifierDB") 61 | verifierDB.open() 62 | 63 | class Echo(LineReceiver): 64 | def connectionMade(self): 65 | self.transport.write("Welcome to the echo server!\\r\\n") 66 | 67 | def lineReceived(self, line): 68 | self.transport.write(line + "\\r\\n") 69 | 70 | class Echo1(Echo): 71 | def connectionMade(self): 72 | if not self.transport.tlsStarted: 73 | self.transport.setServerHandshakeOp(certChain=certChain, 74 | privateKey=privateKey, 75 | verifierDB=verifierDB) 76 | else: 77 | Echo.connectionMade(self) 78 | 79 | def connectionLost(self, reason): 80 | pass #Handle any TLS exceptions here 81 | 82 | class Echo2(Echo): 83 | def lineReceived(self, data): 84 | if data == "STARTTLS": 85 | self.transport.setServerHandshakeOp(certChain=certChain, 86 | privateKey=privateKey, 87 | verifierDB=verifierDB) 88 | else: 89 | Echo.lineReceived(self, data) 90 | 91 | def connectionLost(self, reason): 92 | pass #Handle any TLS exceptions here 93 | 94 | factory = Factory() 95 | factory.protocol = Echo1 96 | #factory.protocol = Echo2 97 | 98 | wrappingFactory = WrappingFactory(factory) 99 | wrappingFactory.protocol = TLSTwistedProtocolWrapper 100 | 101 | log.startLogging(sys.stdout) 102 | reactor.listenTCP(1079, wrappingFactory) 103 | reactor.run() 104 | 105 | This class works as follows: 106 | 107 | Data comes in and is given to the AsyncStateMachine for handling. 108 | AsyncStateMachine will forward events to this class, and we'll 109 | pass them on to the ProtocolHandler, which will proxy them to the 110 | wrapped protocol. The wrapped protocol may then call back into 111 | this class, and these calls will be proxied into the 112 | AsyncStateMachine. 113 | 114 | The call graph looks like this: 115 | - self.dataReceived 116 | - AsyncStateMachine.inReadEvent 117 | - self.out(Connect|Close|Read)Event 118 | - ProtocolWrapper.(connectionMade|loseConnection|dataReceived) 119 | - self.(loseConnection|write|writeSequence) 120 | - AsyncStateMachine.(setCloseOp|setWriteOp) 121 | """ 122 | 123 | #WARNING: IF YOU COPY-AND-PASTE THE ABOVE CODE, BE SURE TO REMOVE 124 | #THE EXTRA ESCAPING AROUND "\\r\\n" 125 | 126 | def __init__(self, factory, wrappedProtocol): 127 | ProtocolWrapper.__init__(self, factory, wrappedProtocol) 128 | AsyncStateMachine.__init__(self) 129 | self.fakeSocket = _FakeSocket(self) 130 | self.tlsConnection = TLSConnection(self.fakeSocket) 131 | self.tlsStarted = False 132 | self.connectionLostCalled = False 133 | 134 | def connectionMade(self): 135 | try: 136 | ProtocolWrapper.connectionMade(self) 137 | except TLSError, e: 138 | self.connectionLost(Failure(e)) 139 | ProtocolWrapper.loseConnection(self) 140 | 141 | def dataReceived(self, data): 142 | try: 143 | if not self.tlsStarted: 144 | ProtocolWrapper.dataReceived(self, data) 145 | else: 146 | self.fakeSocket.data += data 147 | while self.fakeSocket.data: 148 | AsyncStateMachine.inReadEvent(self) 149 | except TLSError, e: 150 | self.connectionLost(Failure(e)) 151 | ProtocolWrapper.loseConnection(self) 152 | 153 | def connectionLost(self, reason): 154 | if not self.connectionLostCalled: 155 | ProtocolWrapper.connectionLost(self, reason) 156 | self.connectionLostCalled = True 157 | 158 | 159 | def outConnectEvent(self): 160 | ProtocolWrapper.connectionMade(self) 161 | 162 | def outCloseEvent(self): 163 | ProtocolWrapper.loseConnection(self) 164 | 165 | def outReadEvent(self, data): 166 | if data == "": 167 | ProtocolWrapper.loseConnection(self) 168 | else: 169 | ProtocolWrapper.dataReceived(self, data) 170 | 171 | 172 | def setServerHandshakeOp(self, **args): 173 | self.tlsStarted = True 174 | AsyncStateMachine.setServerHandshakeOp(self, **args) 175 | 176 | def loseConnection(self): 177 | if not self.tlsStarted: 178 | ProtocolWrapper.loseConnection(self) 179 | else: 180 | AsyncStateMachine.setCloseOp(self) 181 | 182 | def write(self, data): 183 | if not self.tlsStarted: 184 | ProtocolWrapper.write(self, data) 185 | else: 186 | #Because of the FakeSocket, write operations are guaranteed to 187 | #terminate immediately. 188 | AsyncStateMachine.setWriteOp(self, data) 189 | 190 | def writeSequence(self, seq): 191 | if not self.tlsStarted: 192 | ProtocolWrapper.writeSequence(self, seq) 193 | else: 194 | #Because of the FakeSocket, write operations are guaranteed to 195 | #terminate immediately. 196 | AsyncStateMachine.setWriteOp(self, "".join(seq)) -------------------------------------------------------------------------------- /src/tlslite/integration/XMLRPCTransport.py: -------------------------------------------------------------------------------- 1 | """TLS Lite + xmlrpclib.""" 2 | 3 | import xmlrpclib 4 | import httplib 5 | from tlslite.integration.HTTPTLSConnection import HTTPTLSConnection 6 | from tlslite.integration.ClientHelper import ClientHelper 7 | 8 | 9 | class XMLRPCTransport(xmlrpclib.Transport, ClientHelper): 10 | """Handles an HTTPS transaction to an XML-RPC server.""" 11 | 12 | def __init__(self, 13 | username=None, password=None, sharedKey=None, 14 | certChain=None, privateKey=None, 15 | cryptoID=None, protocol=None, 16 | x509Fingerprint=None, 17 | x509TrustList=None, x509CommonName=None, 18 | settings=None): 19 | """Create a new XMLRPCTransport. 20 | 21 | An instance of this class can be passed to L{xmlrpclib.ServerProxy} 22 | to use TLS with XML-RPC calls:: 23 | 24 | from tlslite.api import XMLRPCTransport 25 | from xmlrpclib import ServerProxy 26 | 27 | transport = XMLRPCTransport(user="alice", password="abra123") 28 | server = ServerProxy("https://localhost", transport) 29 | 30 | For client authentication, use one of these argument 31 | combinations: 32 | - username, password (SRP) 33 | - username, sharedKey (shared-key) 34 | - certChain, privateKey (certificate) 35 | 36 | For server authentication, you can either rely on the 37 | implicit mutual authentication performed by SRP or 38 | shared-keys, or you can do certificate-based server 39 | authentication with one of these argument combinations: 40 | - cryptoID[, protocol] (requires cryptoIDlib) 41 | - x509Fingerprint 42 | - x509TrustList[, x509CommonName] (requires cryptlib_py) 43 | 44 | Certificate-based server authentication is compatible with 45 | SRP or certificate-based client authentication. It is 46 | not compatible with shared-keys. 47 | 48 | The constructor does not perform the TLS handshake itself, but 49 | simply stores these arguments for later. The handshake is 50 | performed only when this class needs to connect with the 51 | server. Thus you should be prepared to handle TLS-specific 52 | exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the 53 | client handshake functions in 54 | L{tlslite.TLSConnection.TLSConnection} for details on which 55 | exceptions might be raised. 56 | 57 | @type username: str 58 | @param username: SRP or shared-key username. Requires the 59 | 'password' or 'sharedKey' argument. 60 | 61 | @type password: str 62 | @param password: SRP password for mutual authentication. 63 | Requires the 'username' argument. 64 | 65 | @type sharedKey: str 66 | @param sharedKey: Shared key for mutual authentication. 67 | Requires the 'username' argument. 68 | 69 | @type certChain: L{tlslite.X509CertChain.X509CertChain} or 70 | L{cryptoIDlib.CertChain.CertChain} 71 | @param certChain: Certificate chain for client authentication. 72 | Requires the 'privateKey' argument. Excludes the SRP or 73 | shared-key related arguments. 74 | 75 | @type privateKey: L{tlslite.utils.RSAKey.RSAKey} 76 | @param privateKey: Private key for client authentication. 77 | Requires the 'certChain' argument. Excludes the SRP or 78 | shared-key related arguments. 79 | 80 | @type cryptoID: str 81 | @param cryptoID: cryptoID for server authentication. Mutually 82 | exclusive with the 'x509...' arguments. 83 | 84 | @type protocol: str 85 | @param protocol: cryptoID protocol URI for server 86 | authentication. Requires the 'cryptoID' argument. 87 | 88 | @type x509Fingerprint: str 89 | @param x509Fingerprint: Hex-encoded X.509 fingerprint for 90 | server authentication. Mutually exclusive with the 'cryptoID' 91 | and 'x509TrustList' arguments. 92 | 93 | @type x509TrustList: list of L{tlslite.X509.X509} 94 | @param x509TrustList: A list of trusted root certificates. The 95 | other party must present a certificate chain which extends to 96 | one of these root certificates. The cryptlib_py module must be 97 | installed to use this parameter. Mutually exclusive with the 98 | 'cryptoID' and 'x509Fingerprint' arguments. 99 | 100 | @type x509CommonName: str 101 | @param x509CommonName: The end-entity certificate's 'CN' field 102 | must match this value. For a web server, this is typically a 103 | server name such as 'www.amazon.com'. Mutually exclusive with 104 | the 'cryptoID' and 'x509Fingerprint' arguments. Requires the 105 | 'x509TrustList' argument. 106 | 107 | @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} 108 | @param settings: Various settings which can be used to control 109 | the ciphersuites, certificate types, and SSL/TLS versions 110 | offered by the client. 111 | """ 112 | 113 | ClientHelper.__init__(self, 114 | username, password, sharedKey, 115 | certChain, privateKey, 116 | cryptoID, protocol, 117 | x509Fingerprint, 118 | x509TrustList, x509CommonName, 119 | settings) 120 | 121 | 122 | def make_connection(self, host): 123 | # create a HTTPS connection object from a host descriptor 124 | host, extra_headers, x509 = self.get_host_info(host) 125 | http = HTTPTLSConnection(host, None, 126 | self.username, self.password, 127 | self.sharedKey, 128 | self.certChain, self.privateKey, 129 | self.checker.cryptoID, 130 | self.checker.protocol, 131 | self.checker.x509Fingerprint, 132 | self.checker.x509TrustList, 133 | self.checker.x509CommonName, 134 | self.settings) 135 | http2 = httplib.HTTP() 136 | http2._setup(http) 137 | return http2 -------------------------------------------------------------------------------- /src/tlslite/integration/__init__.py: -------------------------------------------------------------------------------- 1 | """Classes for integrating TLS Lite with other packages.""" 2 | 3 | __all__ = ["AsyncStateMachine", 4 | "HTTPTLSConnection", 5 | "POP3_TLS", 6 | "IMAP4_TLS", 7 | "SMTP_TLS", 8 | "XMLRPCTransport", 9 | "TLSSocketServerMixIn", 10 | "TLSAsyncDispatcherMixIn", 11 | "TLSTwistedProtocolWrapper"] 12 | 13 | try: 14 | import twisted 15 | del twisted 16 | except ImportError: 17 | del __all__[__all__.index("TLSTwistedProtocolWrapper")] 18 | -------------------------------------------------------------------------------- /src/tlslite/utils/AES.py: -------------------------------------------------------------------------------- 1 | """Abstract class for AES.""" 2 | 3 | class AES: 4 | def __init__(self, key, mode, IV, implementation): 5 | if len(key) not in (16, 24, 32): 6 | raise AssertionError() 7 | if mode != 2: 8 | raise AssertionError() 9 | if len(IV) != 16: 10 | raise AssertionError() 11 | self.isBlockCipher = True 12 | self.block_size = 16 13 | self.implementation = implementation 14 | if len(key)==16: 15 | self.name = "aes128" 16 | elif len(key)==24: 17 | self.name = "aes192" 18 | elif len(key)==32: 19 | self.name = "aes256" 20 | else: 21 | raise AssertionError() 22 | 23 | #CBC-Mode encryption, returns ciphertext 24 | #WARNING: *MAY* modify the input as well 25 | def encrypt(self, plaintext): 26 | assert(len(plaintext) % 16 == 0) 27 | 28 | #CBC-Mode decryption, returns plaintext 29 | #WARNING: *MAY* modify the input as well 30 | def decrypt(self, ciphertext): 31 | assert(len(ciphertext) % 16 == 0) -------------------------------------------------------------------------------- /src/tlslite/utils/ASN1Parser.py: -------------------------------------------------------------------------------- 1 | """Class for parsing ASN.1""" 2 | from compat import * 3 | from codec import * 4 | 5 | #Takes a byte array which has a DER TLV field at its head 6 | class ASN1Parser: 7 | def __init__(self, bytes): 8 | p = Parser(bytes) 9 | p.get(1) #skip Type 10 | 11 | #Get Length 12 | self.length = self._getASN1Length(p) 13 | 14 | #Get Value 15 | self.value = p.getFixBytes(self.length) 16 | 17 | #Assuming this is a sequence... 18 | def getChild(self, which): 19 | p = Parser(self.value) 20 | for x in range(which+1): 21 | markIndex = p.index 22 | p.get(1) #skip Type 23 | length = self._getASN1Length(p) 24 | p.getFixBytes(length) 25 | return ASN1Parser(p.bytes[markIndex : p.index]) 26 | 27 | #Decode the ASN.1 DER length field 28 | def _getASN1Length(self, p): 29 | firstLength = p.get(1) 30 | if firstLength<=127: 31 | return firstLength 32 | else: 33 | lengthLength = firstLength & 0x7F 34 | return p.get(lengthLength) 35 | -------------------------------------------------------------------------------- /src/tlslite/utils/Cryptlib_AES.py: -------------------------------------------------------------------------------- 1 | """Cryptlib AES implementation.""" 2 | 3 | from cryptomath import * 4 | from AES import * 5 | 6 | if cryptlibpyLoaded: 7 | 8 | def new(key, mode, IV): 9 | return Cryptlib_AES(key, mode, IV) 10 | 11 | class Cryptlib_AES(AES): 12 | 13 | def __init__(self, key, mode, IV): 14 | AES.__init__(self, key, mode, IV, "cryptlib") 15 | self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_AES) 16 | cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC) 17 | cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) 18 | cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) 19 | cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV) 20 | 21 | def __del__(self): 22 | cryptlib_py.cryptDestroyContext(self.context) 23 | 24 | def encrypt(self, plaintext): 25 | AES.encrypt(self, plaintext) 26 | bytes = stringToBytes(plaintext) 27 | cryptlib_py.cryptEncrypt(self.context, bytes) 28 | return bytesToString(bytes) 29 | 30 | def decrypt(self, ciphertext): 31 | AES.decrypt(self, ciphertext) 32 | bytes = stringToBytes(ciphertext) 33 | cryptlib_py.cryptDecrypt(self.context, bytes) 34 | return bytesToString(bytes) 35 | -------------------------------------------------------------------------------- /src/tlslite/utils/Cryptlib_RC4.py: -------------------------------------------------------------------------------- 1 | """Cryptlib RC4 implementation.""" 2 | 3 | from cryptomath import * 4 | from RC4 import RC4 5 | 6 | if cryptlibpyLoaded: 7 | 8 | def new(key): 9 | return Cryptlib_RC4(key) 10 | 11 | class Cryptlib_RC4(RC4): 12 | 13 | def __init__(self, key): 14 | RC4.__init__(self, key, "cryptlib") 15 | self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_RC4) 16 | cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) 17 | cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) 18 | 19 | def __del__(self): 20 | cryptlib_py.cryptDestroyContext(self.context) 21 | 22 | def encrypt(self, plaintext): 23 | bytes = stringToBytes(plaintext) 24 | cryptlib_py.cryptEncrypt(self.context, bytes) 25 | return bytesToString(bytes) 26 | 27 | def decrypt(self, ciphertext): 28 | return self.encrypt(ciphertext) -------------------------------------------------------------------------------- /src/tlslite/utils/Cryptlib_TripleDES.py: -------------------------------------------------------------------------------- 1 | """Cryptlib 3DES implementation.""" 2 | 3 | from cryptomath import * 4 | 5 | from TripleDES import * 6 | 7 | if cryptlibpyLoaded: 8 | 9 | def new(key, mode, IV): 10 | return Cryptlib_TripleDES(key, mode, IV) 11 | 12 | class Cryptlib_TripleDES(TripleDES): 13 | 14 | def __init__(self, key, mode, IV): 15 | TripleDES.__init__(self, key, mode, IV, "cryptlib") 16 | self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_3DES) 17 | cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC) 18 | cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) 19 | cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) 20 | cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV) 21 | 22 | def __del__(self): 23 | cryptlib_py.cryptDestroyContext(self.context) 24 | 25 | def encrypt(self, plaintext): 26 | TripleDES.encrypt(self, plaintext) 27 | bytes = stringToBytes(plaintext) 28 | cryptlib_py.cryptEncrypt(self.context, bytes) 29 | return bytesToString(bytes) 30 | 31 | def decrypt(self, ciphertext): 32 | TripleDES.decrypt(self, ciphertext) 33 | bytes = stringToBytes(ciphertext) 34 | cryptlib_py.cryptDecrypt(self.context, bytes) 35 | return bytesToString(bytes) -------------------------------------------------------------------------------- /src/tlslite/utils/OpenSSL_AES.py: -------------------------------------------------------------------------------- 1 | """OpenSSL/M2Crypto AES implementation.""" 2 | 3 | from cryptomath import * 4 | from AES import * 5 | 6 | if m2cryptoLoaded: 7 | 8 | def new(key, mode, IV): 9 | return OpenSSL_AES(key, mode, IV) 10 | 11 | class OpenSSL_AES(AES): 12 | 13 | def __init__(self, key, mode, IV): 14 | AES.__init__(self, key, mode, IV, "openssl") 15 | self.key = key 16 | self.IV = IV 17 | 18 | def _createContext(self, encrypt): 19 | context = m2.cipher_ctx_new() 20 | if len(self.key)==16: 21 | cipherType = m2.aes_128_cbc() 22 | if len(self.key)==24: 23 | cipherType = m2.aes_192_cbc() 24 | if len(self.key)==32: 25 | cipherType = m2.aes_256_cbc() 26 | m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) 27 | return context 28 | 29 | def encrypt(self, plaintext): 30 | AES.encrypt(self, plaintext) 31 | context = self._createContext(1) 32 | ciphertext = m2.cipher_update(context, plaintext) 33 | m2.cipher_ctx_free(context) 34 | self.IV = ciphertext[-self.block_size:] 35 | return ciphertext 36 | 37 | def decrypt(self, ciphertext): 38 | AES.decrypt(self, ciphertext) 39 | context = self._createContext(0) 40 | #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. 41 | #To work around this, we append sixteen zeros to the string, below: 42 | plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) 43 | 44 | #If this bug is ever fixed, then plaintext will end up having a garbage 45 | #plaintext block on the end. That's okay - the below code will discard it. 46 | plaintext = plaintext[:len(ciphertext)] 47 | m2.cipher_ctx_free(context) 48 | self.IV = ciphertext[-self.block_size:] 49 | return plaintext 50 | -------------------------------------------------------------------------------- /src/tlslite/utils/OpenSSL_RC4.py: -------------------------------------------------------------------------------- 1 | """OpenSSL/M2Crypto RC4 implementation.""" 2 | 3 | from cryptomath import * 4 | from RC4 import RC4 5 | 6 | if m2cryptoLoaded: 7 | 8 | def new(key): 9 | return OpenSSL_RC4(key) 10 | 11 | class OpenSSL_RC4(RC4): 12 | 13 | def __init__(self, key): 14 | RC4.__init__(self, key, "openssl") 15 | self.rc4 = m2.rc4_new() 16 | m2.rc4_set_key(self.rc4, key) 17 | 18 | def __del__(self): 19 | m2.rc4_free(self.rc4) 20 | 21 | def encrypt(self, plaintext): 22 | return m2.rc4_update(self.rc4, plaintext) 23 | 24 | def decrypt(self, ciphertext): 25 | return self.encrypt(ciphertext) 26 | -------------------------------------------------------------------------------- /src/tlslite/utils/OpenSSL_RSAKey.py: -------------------------------------------------------------------------------- 1 | """OpenSSL/M2Crypto RSA implementation.""" 2 | 3 | from cryptomath import * 4 | 5 | from RSAKey import * 6 | from Python_RSAKey import Python_RSAKey 7 | 8 | #copied from M2Crypto.util.py, so when we load the local copy of m2 9 | #we can still use it 10 | def password_callback(v, prompt1='Enter private key passphrase:', 11 | prompt2='Verify passphrase:'): 12 | from getpass import getpass 13 | while 1: 14 | try: 15 | p1=getpass(prompt1) 16 | if v: 17 | p2=getpass(prompt2) 18 | if p1==p2: 19 | break 20 | else: 21 | break 22 | except KeyboardInterrupt: 23 | return None 24 | return p1 25 | 26 | 27 | if m2cryptoLoaded: 28 | class OpenSSL_RSAKey(RSAKey): 29 | def __init__(self, n=0, e=0): 30 | self.rsa = None 31 | self._hasPrivateKey = False 32 | if (n and not e) or (e and not n): 33 | raise AssertionError() 34 | if n and e: 35 | self.rsa = m2.rsa_new() 36 | m2.rsa_set_n(self.rsa, numberToMPI(n)) 37 | m2.rsa_set_e(self.rsa, numberToMPI(e)) 38 | 39 | def __del__(self): 40 | if self.rsa: 41 | m2.rsa_free(self.rsa) 42 | 43 | def __getattr__(self, name): 44 | if name == 'e': 45 | if not self.rsa: 46 | return 0 47 | return mpiToNumber(m2.rsa_get_e(self.rsa)) 48 | elif name == 'n': 49 | if not self.rsa: 50 | return 0 51 | return mpiToNumber(m2.rsa_get_n(self.rsa)) 52 | else: 53 | raise AttributeError 54 | 55 | def hasPrivateKey(self): 56 | return self._hasPrivateKey 57 | 58 | def hash(self): 59 | return Python_RSAKey(self.n, self.e).hash() 60 | 61 | def _rawPrivateKeyOp(self, m): 62 | s = numberToString(m) 63 | byteLength = numBytes(self.n) 64 | if len(s)== byteLength: 65 | pass 66 | elif len(s) == byteLength-1: 67 | s = '\0' + s 68 | else: 69 | raise AssertionError() 70 | c = stringToNumber(m2.rsa_private_encrypt(self.rsa, s, 71 | m2.no_padding)) 72 | return c 73 | 74 | def _rawPublicKeyOp(self, c): 75 | s = numberToString(c) 76 | byteLength = numBytes(self.n) 77 | if len(s)== byteLength: 78 | pass 79 | elif len(s) == byteLength-1: 80 | s = '\0' + s 81 | else: 82 | raise AssertionError() 83 | m = stringToNumber(m2.rsa_public_decrypt(self.rsa, s, 84 | m2.no_padding)) 85 | return m 86 | 87 | def acceptsPassword(self): return True 88 | 89 | def write(self, password=None): 90 | bio = m2.bio_new(m2.bio_s_mem()) 91 | if self._hasPrivateKey: 92 | if password: 93 | def f(v): return password 94 | m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f) 95 | else: 96 | def f(): pass 97 | m2.rsa_write_key_no_cipher(self.rsa, bio, f) 98 | else: 99 | if password: 100 | raise AssertionError() 101 | m2.rsa_write_pub_key(self.rsa, bio) 102 | s = m2.bio_read(bio, m2.bio_ctrl_pending(bio)) 103 | m2.bio_free(bio) 104 | return s 105 | 106 | def writeXMLPublicKey(self, indent=''): 107 | return Python_RSAKey(self.n, self.e).write(indent) 108 | 109 | def generate(bits): 110 | key = OpenSSL_RSAKey() 111 | def f():pass 112 | key.rsa = m2.rsa_generate_key(bits, 3, f) 113 | key._hasPrivateKey = True 114 | return key 115 | generate = staticmethod(generate) 116 | 117 | def parse(s, passwordCallback=None): 118 | if s.startswith("-----BEGIN "): 119 | if passwordCallback==None: 120 | callback = password_callback 121 | else: 122 | def f(v, prompt1=None, prompt2=None): 123 | return passwordCallback() 124 | callback = f 125 | bio = m2.bio_new(m2.bio_s_mem()) 126 | try: 127 | m2.bio_write(bio, s) 128 | key = OpenSSL_RSAKey() 129 | if s.startswith("-----BEGIN RSA PRIVATE KEY-----"): 130 | def f():pass 131 | key.rsa = m2.rsa_read_key(bio, callback) 132 | if key.rsa == None: 133 | raise SyntaxError() 134 | key._hasPrivateKey = True 135 | elif s.startswith("-----BEGIN PUBLIC KEY-----"): 136 | key.rsa = m2.rsa_read_pub_key(bio) 137 | if key.rsa == None: 138 | raise SyntaxError() 139 | key._hasPrivateKey = False 140 | else: 141 | raise SyntaxError() 142 | return key 143 | finally: 144 | m2.bio_free(bio) 145 | else: 146 | raise SyntaxError() 147 | 148 | parse = staticmethod(parse) 149 | -------------------------------------------------------------------------------- /src/tlslite/utils/OpenSSL_TripleDES.py: -------------------------------------------------------------------------------- 1 | """OpenSSL/M2Crypto 3DES implementation.""" 2 | 3 | from cryptomath import * 4 | from TripleDES import * 5 | 6 | if m2cryptoLoaded: 7 | 8 | def new(key, mode, IV): 9 | return OpenSSL_TripleDES(key, mode, IV) 10 | 11 | class OpenSSL_TripleDES(TripleDES): 12 | 13 | def __init__(self, key, mode, IV): 14 | TripleDES.__init__(self, key, mode, IV, "openssl") 15 | self.key = key 16 | self.IV = IV 17 | 18 | def _createContext(self, encrypt): 19 | context = m2.cipher_ctx_new() 20 | cipherType = m2.des_ede3_cbc() 21 | m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) 22 | return context 23 | 24 | def encrypt(self, plaintext): 25 | TripleDES.encrypt(self, plaintext) 26 | context = self._createContext(1) 27 | ciphertext = m2.cipher_update(context, plaintext) 28 | m2.cipher_ctx_free(context) 29 | self.IV = ciphertext[-self.block_size:] 30 | return ciphertext 31 | 32 | def decrypt(self, ciphertext): 33 | TripleDES.decrypt(self, ciphertext) 34 | context = self._createContext(0) 35 | #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. 36 | #To work around this, we append sixteen zeros to the string, below: 37 | plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) 38 | 39 | #If this bug is ever fixed, then plaintext will end up having a garbage 40 | #plaintext block on the end. That's okay - the below code will ignore it. 41 | plaintext = plaintext[:len(ciphertext)] 42 | m2.cipher_ctx_free(context) 43 | self.IV = ciphertext[-self.block_size:] 44 | return plaintext -------------------------------------------------------------------------------- /src/tlslite/utils/PyCrypto_AES.py: -------------------------------------------------------------------------------- 1 | """PyCrypto AES implementation.""" 2 | 3 | from cryptomath import * 4 | from AES import * 5 | 6 | if pycryptoLoaded: 7 | import Crypto.Cipher.AES 8 | 9 | def new(key, mode, IV): 10 | return PyCrypto_AES(key, mode, IV) 11 | 12 | class PyCrypto_AES(AES): 13 | 14 | def __init__(self, key, mode, IV): 15 | AES.__init__(self, key, mode, IV, "pycrypto") 16 | self.context = Crypto.Cipher.AES.new(key, mode, IV) 17 | 18 | def encrypt(self, plaintext): 19 | return self.context.encrypt(plaintext) 20 | 21 | def decrypt(self, ciphertext): 22 | return self.context.decrypt(ciphertext) -------------------------------------------------------------------------------- /src/tlslite/utils/PyCrypto_RC4.py: -------------------------------------------------------------------------------- 1 | """PyCrypto RC4 implementation.""" 2 | 3 | from cryptomath import * 4 | from RC4 import * 5 | 6 | if pycryptoLoaded: 7 | import Crypto.Cipher.ARC4 8 | 9 | def new(key): 10 | return PyCrypto_RC4(key) 11 | 12 | class PyCrypto_RC4(RC4): 13 | 14 | def __init__(self, key): 15 | RC4.__init__(self, key, "pycrypto") 16 | self.context = Crypto.Cipher.ARC4.new(key) 17 | 18 | def encrypt(self, plaintext): 19 | return self.context.encrypt(plaintext) 20 | 21 | def decrypt(self, ciphertext): 22 | return self.context.decrypt(ciphertext) -------------------------------------------------------------------------------- /src/tlslite/utils/PyCrypto_RSAKey.py: -------------------------------------------------------------------------------- 1 | """PyCrypto RSA implementation.""" 2 | 3 | from cryptomath import * 4 | 5 | from RSAKey import * 6 | from Python_RSAKey import Python_RSAKey 7 | 8 | if pycryptoLoaded: 9 | 10 | from Crypto.PublicKey import RSA 11 | 12 | class PyCrypto_RSAKey(RSAKey): 13 | def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 14 | if not d: 15 | self.rsa = RSA.construct( (n, e) ) 16 | else: 17 | self.rsa = RSA.construct( (n, e, d, p, q) ) 18 | 19 | def __getattr__(self, name): 20 | return getattr(self.rsa, name) 21 | 22 | def hasPrivateKey(self): 23 | return self.rsa.has_private() 24 | 25 | def hash(self): 26 | return Python_RSAKey(self.n, self.e).hash() 27 | 28 | def _rawPrivateKeyOp(self, m): 29 | s = numberToString(m) 30 | byteLength = numBytes(self.n) 31 | if len(s)== byteLength: 32 | pass 33 | elif len(s) == byteLength-1: 34 | s = '\0' + s 35 | else: 36 | raise AssertionError() 37 | c = stringToNumber(self.rsa.decrypt((s,))) 38 | return c 39 | 40 | def _rawPublicKeyOp(self, c): 41 | s = numberToString(c) 42 | byteLength = numBytes(self.n) 43 | if len(s)== byteLength: 44 | pass 45 | elif len(s) == byteLength-1: 46 | s = '\0' + s 47 | else: 48 | raise AssertionError() 49 | m = stringToNumber(self.rsa.encrypt(s, None)[0]) 50 | return m 51 | 52 | def writeXMLPublicKey(self, indent=''): 53 | return Python_RSAKey(self.n, self.e).write(indent) 54 | 55 | def generate(bits): 56 | key = PyCrypto_RSAKey() 57 | def f(numBytes): 58 | return bytesToString(getRandomBytes(numBytes)) 59 | key.rsa = RSA.generate(bits, f) 60 | return key 61 | generate = staticmethod(generate) 62 | -------------------------------------------------------------------------------- /src/tlslite/utils/PyCrypto_TripleDES.py: -------------------------------------------------------------------------------- 1 | """PyCrypto 3DES implementation.""" 2 | 3 | from cryptomath import * 4 | from TripleDES import * 5 | 6 | if pycryptoLoaded: 7 | import Crypto.Cipher.DES3 8 | 9 | def new(key, mode, IV): 10 | return PyCrypto_TripleDES(key, mode, IV) 11 | 12 | class PyCrypto_TripleDES(TripleDES): 13 | 14 | def __init__(self, key, mode, IV): 15 | TripleDES.__init__(self, key, mode, IV, "pycrypto") 16 | self.context = Crypto.Cipher.DES3.new(key, mode, IV) 17 | 18 | def encrypt(self, plaintext): 19 | return self.context.encrypt(plaintext) 20 | 21 | def decrypt(self, ciphertext): 22 | return self.context.decrypt(ciphertext) -------------------------------------------------------------------------------- /src/tlslite/utils/Python_AES.py: -------------------------------------------------------------------------------- 1 | """Pure-Python AES implementation.""" 2 | 3 | from cryptomath import * 4 | 5 | from AES import * 6 | from rijndael import rijndael 7 | 8 | def new(key, mode, IV): 9 | return Python_AES(key, mode, IV) 10 | 11 | class Python_AES(AES): 12 | def __init__(self, key, mode, IV): 13 | AES.__init__(self, key, mode, IV, "python") 14 | self.rijndael = rijndael(key, 16) 15 | self.IV = IV 16 | 17 | def encrypt(self, plaintext): 18 | AES.encrypt(self, plaintext) 19 | 20 | plaintextBytes = stringToBytes(plaintext) 21 | chainBytes = stringToBytes(self.IV) 22 | 23 | #CBC Mode: For each block... 24 | for x in range(len(plaintextBytes)/16): 25 | 26 | #XOR with the chaining block 27 | blockBytes = plaintextBytes[x*16 : (x*16)+16] 28 | for y in range(16): 29 | blockBytes[y] ^= chainBytes[y] 30 | blockString = bytesToString(blockBytes) 31 | 32 | #Encrypt it 33 | encryptedBytes = stringToBytes(self.rijndael.encrypt(blockString)) 34 | 35 | #Overwrite the input with the output 36 | for y in range(16): 37 | plaintextBytes[(x*16)+y] = encryptedBytes[y] 38 | 39 | #Set the next chaining block 40 | chainBytes = encryptedBytes 41 | 42 | self.IV = bytesToString(chainBytes) 43 | return bytesToString(plaintextBytes) 44 | 45 | def decrypt(self, ciphertext): 46 | AES.decrypt(self, ciphertext) 47 | 48 | ciphertextBytes = stringToBytes(ciphertext) 49 | chainBytes = stringToBytes(self.IV) 50 | 51 | #CBC Mode: For each block... 52 | for x in range(len(ciphertextBytes)/16): 53 | 54 | #Decrypt it 55 | blockBytes = ciphertextBytes[x*16 : (x*16)+16] 56 | blockString = bytesToString(blockBytes) 57 | decryptedBytes = stringToBytes(self.rijndael.decrypt(blockString)) 58 | 59 | #XOR with the chaining block and overwrite the input with output 60 | for y in range(16): 61 | decryptedBytes[y] ^= chainBytes[y] 62 | ciphertextBytes[(x*16)+y] = decryptedBytes[y] 63 | 64 | #Set the next chaining block 65 | chainBytes = blockBytes 66 | 67 | self.IV = bytesToString(chainBytes) 68 | return bytesToString(ciphertextBytes) 69 | -------------------------------------------------------------------------------- /src/tlslite/utils/Python_RC4.py: -------------------------------------------------------------------------------- 1 | """Pure-Python RC4 implementation.""" 2 | 3 | from RC4 import RC4 4 | from cryptomath import * 5 | 6 | def new(key): 7 | return Python_RC4(key) 8 | 9 | class Python_RC4(RC4): 10 | def __init__(self, key): 11 | RC4.__init__(self, key, "python") 12 | keyBytes = stringToBytes(key) 13 | S = [i for i in range(256)] 14 | j = 0 15 | for i in range(256): 16 | j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256 17 | S[i], S[j] = S[j], S[i] 18 | 19 | self.S = S 20 | self.i = 0 21 | self.j = 0 22 | 23 | def encrypt(self, plaintext): 24 | plaintextBytes = stringToBytes(plaintext) 25 | S = self.S 26 | i = self.i 27 | j = self.j 28 | for x in range(len(plaintextBytes)): 29 | i = (i + 1) % 256 30 | j = (j + S[i]) % 256 31 | S[i], S[j] = S[j], S[i] 32 | t = (S[i] + S[j]) % 256 33 | plaintextBytes[x] ^= S[t] 34 | self.i = i 35 | self.j = j 36 | return bytesToString(plaintextBytes) 37 | 38 | def decrypt(self, ciphertext): 39 | return self.encrypt(ciphertext) 40 | -------------------------------------------------------------------------------- /src/tlslite/utils/Python_RSAKey.py: -------------------------------------------------------------------------------- 1 | """Pure-Python RSA implementation.""" 2 | 3 | from cryptomath import * 4 | import xmltools 5 | from ASN1Parser import ASN1Parser 6 | from RSAKey import * 7 | 8 | class Python_RSAKey(RSAKey): 9 | def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 10 | if (n and not e) or (e and not n): 11 | raise AssertionError() 12 | self.n = n 13 | self.e = e 14 | self.d = d 15 | self.p = p 16 | self.q = q 17 | self.dP = dP 18 | self.dQ = dQ 19 | self.qInv = qInv 20 | self.blinder = 0 21 | self.unblinder = 0 22 | 23 | def hasPrivateKey(self): 24 | return self.d != 0 25 | 26 | def hash(self): 27 | s = self.writeXMLPublicKey('\t\t') 28 | return hashAndBase64(s.strip()) 29 | 30 | def _rawPrivateKeyOp(self, m): 31 | #Create blinding values, on the first pass: 32 | if not self.blinder: 33 | self.unblinder = getRandomNumber(2, self.n) 34 | self.blinder = powMod(invMod(self.unblinder, self.n), self.e, 35 | self.n) 36 | 37 | #Blind the input 38 | m = (m * self.blinder) % self.n 39 | 40 | #Perform the RSA operation 41 | c = self._rawPrivateKeyOpHelper(m) 42 | 43 | #Unblind the output 44 | c = (c * self.unblinder) % self.n 45 | 46 | #Update blinding values 47 | self.blinder = (self.blinder * self.blinder) % self.n 48 | self.unblinder = (self.unblinder * self.unblinder) % self.n 49 | 50 | #Return the output 51 | return c 52 | 53 | 54 | def _rawPrivateKeyOpHelper(self, m): 55 | #Non-CRT version 56 | #c = powMod(m, self.d, self.n) 57 | 58 | #CRT version (~3x faster) 59 | s1 = powMod(m, self.dP, self.p) 60 | s2 = powMod(m, self.dQ, self.q) 61 | h = ((s1 - s2) * self.qInv) % self.p 62 | c = s2 + self.q * h 63 | return c 64 | 65 | def _rawPublicKeyOp(self, c): 66 | m = powMod(c, self.e, self.n) 67 | return m 68 | 69 | def acceptsPassword(self): return False 70 | 71 | def write(self, indent=''): 72 | if self.d: 73 | s = indent+'\n' 74 | else: 75 | s = indent+'\n' 76 | s += indent+'\t%s\n' % numberToBase64(self.n) 77 | s += indent+'\t%s\n' % numberToBase64(self.e) 78 | if self.d: 79 | s += indent+'\t%s\n' % numberToBase64(self.d) 80 | s += indent+'\t

%s

\n' % numberToBase64(self.p) 81 | s += indent+'\t%s\n' % numberToBase64(self.q) 82 | s += indent+'\t%s\n' % numberToBase64(self.dP) 83 | s += indent+'\t%s\n' % numberToBase64(self.dQ) 84 | s += indent+'\t%s\n' % numberToBase64(self.qInv) 85 | s += indent+'
' 86 | else: 87 | s += indent+'' 88 | #Only add \n if part of a larger structure 89 | if indent != '': 90 | s += '\n' 91 | return s 92 | 93 | def writeXMLPublicKey(self, indent=''): 94 | return Python_RSAKey(self.n, self.e).write(indent) 95 | 96 | def generate(bits): 97 | key = Python_RSAKey() 98 | p = getRandomPrime(bits/2, False) 99 | q = getRandomPrime(bits/2, False) 100 | t = lcm(p-1, q-1) 101 | key.n = p * q 102 | key.e = 3L #Needed to be long, for Java 103 | key.d = invMod(key.e, t) 104 | key.p = p 105 | key.q = q 106 | key.dP = key.d % (p-1) 107 | key.dQ = key.d % (q-1) 108 | key.qInv = invMod(q, p) 109 | return key 110 | generate = staticmethod(generate) 111 | 112 | def parsePEM(s, passwordCallback=None): 113 | """Parse a string containing a or , or 114 | PEM-encoded key.""" 115 | 116 | start = s.find("-----BEGIN PRIVATE KEY-----") 117 | if start != -1: 118 | end = s.find("-----END PRIVATE KEY-----") 119 | if end == -1: 120 | raise SyntaxError("Missing PEM Postfix") 121 | s = s[start+len("-----BEGIN PRIVATE KEY -----") : end] 122 | bytes = base64ToBytes(s) 123 | return Python_RSAKey._parsePKCS8(bytes) 124 | else: 125 | start = s.find("-----BEGIN RSA PRIVATE KEY-----") 126 | if start != -1: 127 | end = s.find("-----END RSA PRIVATE KEY-----") 128 | if end == -1: 129 | raise SyntaxError("Missing PEM Postfix") 130 | s = s[start+len("-----BEGIN RSA PRIVATE KEY -----") : end] 131 | bytes = base64ToBytes(s) 132 | return Python_RSAKey._parseSSLeay(bytes) 133 | raise SyntaxError("Missing PEM Prefix") 134 | parsePEM = staticmethod(parsePEM) 135 | 136 | def parseXML(s): 137 | element = xmltools.parseAndStripWhitespace(s) 138 | return Python_RSAKey._parseXML(element) 139 | parseXML = staticmethod(parseXML) 140 | 141 | def _parsePKCS8(bytes): 142 | p = ASN1Parser(bytes) 143 | 144 | version = p.getChild(0).value[0] 145 | if version != 0: 146 | raise SyntaxError("Unrecognized PKCS8 version") 147 | 148 | rsaOID = p.getChild(1).value 149 | if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: 150 | raise SyntaxError("Unrecognized AlgorithmIdentifier") 151 | 152 | #Get the privateKey 153 | privateKeyP = p.getChild(2) 154 | 155 | #Adjust for OCTET STRING encapsulation 156 | privateKeyP = ASN1Parser(privateKeyP.value) 157 | 158 | return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 159 | _parsePKCS8 = staticmethod(_parsePKCS8) 160 | 161 | def _parseSSLeay(bytes): 162 | privateKeyP = ASN1Parser(bytes) 163 | return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 164 | _parseSSLeay = staticmethod(_parseSSLeay) 165 | 166 | def _parseASN1PrivateKey(privateKeyP): 167 | version = privateKeyP.getChild(0).value[0] 168 | if version != 0: 169 | raise SyntaxError("Unrecognized RSAPrivateKey version") 170 | n = bytesToNumber(privateKeyP.getChild(1).value) 171 | e = bytesToNumber(privateKeyP.getChild(2).value) 172 | d = bytesToNumber(privateKeyP.getChild(3).value) 173 | p = bytesToNumber(privateKeyP.getChild(4).value) 174 | q = bytesToNumber(privateKeyP.getChild(5).value) 175 | dP = bytesToNumber(privateKeyP.getChild(6).value) 176 | dQ = bytesToNumber(privateKeyP.getChild(7).value) 177 | qInv = bytesToNumber(privateKeyP.getChild(8).value) 178 | return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) 179 | _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey) 180 | 181 | def _parseXML(element): 182 | try: 183 | xmltools.checkName(element, "privateKey") 184 | except SyntaxError: 185 | xmltools.checkName(element, "publicKey") 186 | 187 | #Parse attributes 188 | xmltools.getReqAttribute(element, "xmlns", "http://trevp.net/rsa\Z") 189 | xmltools.checkNoMoreAttributes(element) 190 | 191 | #Parse public values ( and ) 192 | n = base64ToNumber(xmltools.getText(xmltools.getChild(element, 0, "n"), xmltools.base64RegEx)) 193 | e = base64ToNumber(xmltools.getText(xmltools.getChild(element, 1, "e"), xmltools.base64RegEx)) 194 | d = 0 195 | p = 0 196 | q = 0 197 | dP = 0 198 | dQ = 0 199 | qInv = 0 200 | #Parse private values, if present 201 | if element.childNodes.length>=3: 202 | d = base64ToNumber(xmltools.getText(xmltools.getChild(element, 2, "d"), xmltools.base64RegEx)) 203 | p = base64ToNumber(xmltools.getText(xmltools.getChild(element, 3, "p"), xmltools.base64RegEx)) 204 | q = base64ToNumber(xmltools.getText(xmltools.getChild(element, 4, "q"), xmltools.base64RegEx)) 205 | dP = base64ToNumber(xmltools.getText(xmltools.getChild(element, 5, "dP"), xmltools.base64RegEx)) 206 | dQ = base64ToNumber(xmltools.getText(xmltools.getChild(element, 6, "dQ"), xmltools.base64RegEx)) 207 | qInv = base64ToNumber(xmltools.getText(xmltools.getLastChild(element, 7, "qInv"), xmltools.base64RegEx)) 208 | return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) 209 | _parseXML = staticmethod(_parseXML) 210 | -------------------------------------------------------------------------------- /src/tlslite/utils/RC4.py: -------------------------------------------------------------------------------- 1 | """Abstract class for RC4.""" 2 | 3 | from compat import * #For False 4 | 5 | class RC4: 6 | def __init__(self, keyBytes, implementation): 7 | if len(keyBytes) < 16 or len(keyBytes) > 256: 8 | raise ValueError() 9 | self.isBlockCipher = False 10 | self.name = "rc4" 11 | self.implementation = implementation 12 | 13 | def encrypt(self, plaintext): 14 | raise NotImplementedError() 15 | 16 | def decrypt(self, ciphertext): 17 | raise NotImplementedError() -------------------------------------------------------------------------------- /src/tlslite/utils/TripleDES.py: -------------------------------------------------------------------------------- 1 | """Abstract class for 3DES.""" 2 | 3 | from compat import * #For True 4 | 5 | class TripleDES: 6 | def __init__(self, key, mode, IV, implementation): 7 | if len(key) != 24: 8 | raise ValueError() 9 | if mode != 2: 10 | raise ValueError() 11 | if len(IV) != 8: 12 | raise ValueError() 13 | self.isBlockCipher = True 14 | self.block_size = 8 15 | self.implementation = implementation 16 | self.name = "3des" 17 | 18 | #CBC-Mode encryption, returns ciphertext 19 | #WARNING: *MAY* modify the input as well 20 | def encrypt(self, plaintext): 21 | assert(len(plaintext) % 8 == 0) 22 | 23 | #CBC-Mode decryption, returns plaintext 24 | #WARNING: *MAY* modify the input as well 25 | def decrypt(self, ciphertext): 26 | assert(len(ciphertext) % 8 == 0) 27 | -------------------------------------------------------------------------------- /src/tlslite/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Toolkit for crypto and other stuff.""" 2 | 3 | __all__ = ["AES", 4 | "ASN1Parser", 5 | "cipherfactory", 6 | "codec", 7 | "Cryptlib_AES", 8 | "Cryptlib_RC4", 9 | "Cryptlib_TripleDES", 10 | "cryptomath: cryptomath module", 11 | "dateFuncs", 12 | "hmac", 13 | "JCE_RSAKey", 14 | "compat", 15 | "keyfactory", 16 | "OpenSSL_AES", 17 | "OpenSSL_RC4", 18 | "OpenSSL_RSAKey", 19 | "OpenSSL_TripleDES", 20 | "PyCrypto_AES", 21 | "PyCrypto_RC4", 22 | "PyCrypto_RSAKey", 23 | "PyCrypto_TripleDES", 24 | "Python_AES", 25 | "Python_RC4", 26 | "Python_RSAKey", 27 | "RC4", 28 | "rijndael", 29 | "RSAKey", 30 | "TripleDES", 31 | "xmltools"] 32 | -------------------------------------------------------------------------------- /src/tlslite/utils/cipherfactory.py: -------------------------------------------------------------------------------- 1 | """Factory functions for symmetric cryptography.""" 2 | 3 | import os 4 | 5 | import Python_AES 6 | import Python_RC4 7 | 8 | import cryptomath 9 | 10 | tripleDESPresent = False 11 | 12 | if cryptomath.m2cryptoLoaded: 13 | import OpenSSL_AES 14 | import OpenSSL_RC4 15 | import OpenSSL_TripleDES 16 | tripleDESPresent = True 17 | 18 | if cryptomath.cryptlibpyLoaded: 19 | import Cryptlib_AES 20 | import Cryptlib_RC4 21 | import Cryptlib_TripleDES 22 | tripleDESPresent = True 23 | 24 | if cryptomath.pycryptoLoaded: 25 | import PyCrypto_AES 26 | import PyCrypto_RC4 27 | import PyCrypto_TripleDES 28 | tripleDESPresent = True 29 | 30 | # ************************************************************************** 31 | # Factory Functions for AES 32 | # ************************************************************************** 33 | 34 | def createAES(key, IV, implList=None): 35 | """Create a new AES object. 36 | 37 | @type key: str 38 | @param key: A 16, 24, or 32 byte string. 39 | 40 | @type IV: str 41 | @param IV: A 16 byte string 42 | 43 | @rtype: L{tlslite.utils.AES} 44 | @return: An AES object. 45 | """ 46 | if implList == None: 47 | implList = ["cryptlib", "openssl", "pycrypto", "python"] 48 | 49 | for impl in implList: 50 | if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: 51 | return Cryptlib_AES.new(key, 2, IV) 52 | elif impl == "openssl" and cryptomath.m2cryptoLoaded: 53 | return OpenSSL_AES.new(key, 2, IV) 54 | elif impl == "pycrypto" and cryptomath.pycryptoLoaded: 55 | return PyCrypto_AES.new(key, 2, IV) 56 | elif impl == "python": 57 | return Python_AES.new(key, 2, IV) 58 | raise NotImplementedError() 59 | 60 | def createRC4(key, IV, implList=None): 61 | """Create a new RC4 object. 62 | 63 | @type key: str 64 | @param key: A 16 to 32 byte string. 65 | 66 | @type IV: object 67 | @param IV: Ignored, whatever it is. 68 | 69 | @rtype: L{tlslite.utils.RC4} 70 | @return: An RC4 object. 71 | """ 72 | if implList == None: 73 | implList = ["cryptlib", "openssl", "pycrypto", "python"] 74 | 75 | if len(IV) != 0: 76 | raise AssertionError() 77 | for impl in implList: 78 | if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: 79 | return Cryptlib_RC4.new(key) 80 | elif impl == "openssl" and cryptomath.m2cryptoLoaded: 81 | return OpenSSL_RC4.new(key) 82 | elif impl == "pycrypto" and cryptomath.pycryptoLoaded: 83 | return PyCrypto_RC4.new(key) 84 | elif impl == "python": 85 | return Python_RC4.new(key) 86 | raise NotImplementedError() 87 | 88 | #Create a new TripleDES instance 89 | def createTripleDES(key, IV, implList=None): 90 | """Create a new 3DES object. 91 | 92 | @type key: str 93 | @param key: A 24 byte string. 94 | 95 | @type IV: str 96 | @param IV: An 8 byte string 97 | 98 | @rtype: L{tlslite.utils.TripleDES} 99 | @return: A 3DES object. 100 | """ 101 | if implList == None: 102 | implList = ["cryptlib", "openssl", "pycrypto"] 103 | 104 | for impl in implList: 105 | if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: 106 | return Cryptlib_TripleDES.new(key, 2, IV) 107 | elif impl == "openssl" and cryptomath.m2cryptoLoaded: 108 | return OpenSSL_TripleDES.new(key, 2, IV) 109 | elif impl == "pycrypto" and cryptomath.pycryptoLoaded: 110 | return PyCrypto_TripleDES.new(key, 2, IV) 111 | raise NotImplementedError() -------------------------------------------------------------------------------- /src/tlslite/utils/codec.py: -------------------------------------------------------------------------------- 1 | """Classes for reading/writing binary data (such as TLS records).""" 2 | 3 | from compat import * 4 | 5 | class Writer: 6 | def __init__(self, length=0): 7 | #If length is zero, then this is just a "trial run" to determine length 8 | self.index = 0 9 | self.bytes = createByteArrayZeros(length) 10 | 11 | def add(self, x, length): 12 | if self.bytes: 13 | newIndex = self.index+length-1 14 | while newIndex >= self.index: 15 | self.bytes[newIndex] = x & 0xFF 16 | x >>= 8 17 | newIndex -= 1 18 | self.index += length 19 | 20 | def addFixSeq(self, seq, length): 21 | if self.bytes: 22 | for e in seq: 23 | self.add(e, length) 24 | else: 25 | self.index += len(seq)*length 26 | 27 | def addVarSeq(self, seq, length, lengthLength): 28 | if self.bytes: 29 | self.add(len(seq)*length, lengthLength) 30 | for e in seq: 31 | self.add(e, length) 32 | else: 33 | self.index += lengthLength + (len(seq)*length) 34 | 35 | 36 | class Parser: 37 | def __init__(self, bytes): 38 | self.bytes = bytes 39 | self.index = 0 40 | 41 | def get(self, length): 42 | if self.index + length > len(self.bytes): 43 | raise SyntaxError() 44 | x = 0 45 | for count in range(length): 46 | x <<= 8 47 | x |= self.bytes[self.index] 48 | self.index += 1 49 | return x 50 | 51 | def getFixBytes(self, lengthBytes): 52 | bytes = self.bytes[self.index : self.index+lengthBytes] 53 | self.index += lengthBytes 54 | return bytes 55 | 56 | def getVarBytes(self, lengthLength): 57 | lengthBytes = self.get(lengthLength) 58 | return self.getFixBytes(lengthBytes) 59 | 60 | def getFixList(self, length, lengthList): 61 | l = [0] * lengthList 62 | for x in range(lengthList): 63 | l[x] = self.get(length) 64 | return l 65 | 66 | def getVarList(self, length, lengthLength): 67 | lengthList = self.get(lengthLength) 68 | if lengthList % length != 0: 69 | raise SyntaxError() 70 | lengthList = int(lengthList/length) 71 | l = [0] * lengthList 72 | for x in range(lengthList): 73 | l[x] = self.get(length) 74 | return l 75 | 76 | def startLengthCheck(self, lengthLength): 77 | self.lengthCheck = self.get(lengthLength) 78 | self.indexCheck = self.index 79 | 80 | def setLengthCheck(self, length): 81 | self.lengthCheck = length 82 | self.indexCheck = self.index 83 | 84 | def stopLengthCheck(self): 85 | if (self.index - self.indexCheck) != self.lengthCheck: 86 | raise SyntaxError() 87 | 88 | def atLengthCheck(self): 89 | if (self.index - self.indexCheck) < self.lengthCheck: 90 | return False 91 | elif (self.index - self.indexCheck) == self.lengthCheck: 92 | return True 93 | else: 94 | raise SyntaxError() -------------------------------------------------------------------------------- /src/tlslite/utils/compat.py: -------------------------------------------------------------------------------- 1 | """Miscellaneous functions to mask Python version differences.""" 2 | 3 | import sys 4 | import os 5 | 6 | if sys.version_info < (2,2): 7 | raise AssertionError("Python 2.2 or later required") 8 | 9 | if sys.version_info < (2,3): 10 | 11 | def enumerate(collection): 12 | return zip(range(len(collection)), collection) 13 | 14 | class Set: 15 | def __init__(self, seq=None): 16 | self.values = {} 17 | if seq: 18 | for e in seq: 19 | self.values[e] = None 20 | 21 | def add(self, e): 22 | self.values[e] = None 23 | 24 | def discard(self, e): 25 | if e in self.values.keys(): 26 | del(self.values[e]) 27 | 28 | def union(self, s): 29 | ret = Set() 30 | for e in self.values.keys(): 31 | ret.values[e] = None 32 | for e in s.values.keys(): 33 | ret.values[e] = None 34 | return ret 35 | 36 | def issubset(self, other): 37 | for e in self.values.keys(): 38 | if e not in other.values.keys(): 39 | return False 40 | return True 41 | 42 | def __nonzero__( self): 43 | return len(self.values.keys()) 44 | 45 | def __contains__(self, e): 46 | return e in self.values.keys() 47 | 48 | def __iter__(self): 49 | return iter(set.values.keys()) 50 | 51 | 52 | if os.name != "java": 53 | 54 | import array 55 | def createByteArraySequence(seq): 56 | return array.array('B', seq) 57 | def createByteArrayZeros(howMany): 58 | return array.array('B', [0] * howMany) 59 | def concatArrays(a1, a2): 60 | return a1+a2 61 | 62 | def bytesToString(bytes): 63 | return bytes.tostring() 64 | def stringToBytes(s): 65 | bytes = createByteArrayZeros(0) 66 | bytes.fromstring(s) 67 | return bytes 68 | 69 | import math 70 | def numBits(n): 71 | if n==0: 72 | return 0 73 | s = "%x" % n 74 | return ((len(s)-1)*4) + \ 75 | {'0':0, '1':1, '2':2, '3':2, 76 | '4':3, '5':3, '6':3, '7':3, 77 | '8':4, '9':4, 'a':4, 'b':4, 78 | 'c':4, 'd':4, 'e':4, 'f':4, 79 | }[s[0]] 80 | return int(math.floor(math.log(n, 2))+1) 81 | 82 | BaseException = Exception 83 | import sys 84 | import traceback 85 | def formatExceptionTrace(e): 86 | newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) 87 | return newStr 88 | 89 | else: 90 | #Jython 2.1 is missing lots of python 2.3 stuff, 91 | #which we have to emulate here: 92 | #NOTE: JYTHON SUPPORT NO LONGER WORKS, DUE TO USE OF GENERATORS. 93 | #THIS CODE IS LEFT IN SO THAT ONE JYTHON UPDATES TO 2.2, IT HAS A 94 | #CHANCE OF WORKING AGAIN. 95 | 96 | import java 97 | import jarray 98 | 99 | def createByteArraySequence(seq): 100 | if isinstance(seq, type("")): #If it's a string, convert 101 | seq = [ord(c) for c in seq] 102 | return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed 103 | def createByteArrayZeros(howMany): 104 | return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed 105 | def concatArrays(a1, a2): 106 | l = list(a1)+list(a2) 107 | return createByteArraySequence(l) 108 | 109 | #WAY TOO SLOW - MUST BE REPLACED------------ 110 | def bytesToString(bytes): 111 | return "".join([chr(b) for b in bytes]) 112 | 113 | def stringToBytes(s): 114 | bytes = createByteArrayZeros(len(s)) 115 | for count, c in enumerate(s): 116 | bytes[count] = ord(c) 117 | return bytes 118 | #WAY TOO SLOW - MUST BE REPLACED------------ 119 | 120 | def numBits(n): 121 | if n==0: 122 | return 0 123 | n= 1L * n; #convert to long, if it isn't already 124 | return n.__tojava__(java.math.BigInteger).bitLength() 125 | 126 | #Adjust the string to an array of bytes 127 | def stringToJavaByteArray(s): 128 | bytes = jarray.zeros(len(s), 'b') 129 | for count, c in enumerate(s): 130 | x = ord(c) 131 | if x >= 128: x -= 256 132 | bytes[count] = x 133 | return bytes 134 | 135 | BaseException = java.lang.Exception 136 | import sys 137 | import traceback 138 | def formatExceptionTrace(e): 139 | newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) 140 | return newStr -------------------------------------------------------------------------------- /src/tlslite/utils/dateFuncs.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | #Functions for manipulating datetime objects 5 | #CCYY-MM-DDThh:mm:ssZ 6 | def parseDateClass(s): 7 | year, month, day = s.split("-") 8 | day, tail = day[:2], day[2:] 9 | hour, minute, second = tail[1:].split(":") 10 | second = second[:2] 11 | year, month, day = int(year), int(month), int(day) 12 | hour, minute, second = int(hour), int(minute), int(second) 13 | return createDateClass(year, month, day, hour, minute, second) 14 | 15 | 16 | if os.name != "java": 17 | from datetime import datetime, timedelta 18 | 19 | #Helper functions for working with a date/time class 20 | def createDateClass(year, month, day, hour, minute, second): 21 | return datetime(year, month, day, hour, minute, second) 22 | 23 | def printDateClass(d): 24 | #Split off fractional seconds, append 'Z' 25 | return d.isoformat().split(".")[0]+"Z" 26 | 27 | def getNow(): 28 | return datetime.utcnow() 29 | 30 | def getHoursFromNow(hours): 31 | return datetime.utcnow() + timedelta(hours=hours) 32 | 33 | def getMinutesFromNow(minutes): 34 | return datetime.utcnow() + timedelta(minutes=minutes) 35 | 36 | def isDateClassExpired(d): 37 | return d < datetime.utcnow() 38 | 39 | def isDateClassBefore(d1, d2): 40 | return d1 < d2 41 | 42 | else: 43 | #Jython 2.1 is missing lots of python 2.3 stuff, 44 | #which we have to emulate here: 45 | import java 46 | import jarray 47 | 48 | def createDateClass(year, month, day, hour, minute, second): 49 | c = java.util.Calendar.getInstance() 50 | c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) 51 | c.set(year, month-1, day, hour, minute, second) 52 | return c 53 | 54 | def printDateClass(d): 55 | return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \ 56 | (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \ 57 | d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND)) 58 | 59 | def getNow(): 60 | c = java.util.Calendar.getInstance() 61 | c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) 62 | c.get(c.HOUR) #force refresh? 63 | return c 64 | 65 | def getHoursFromNow(hours): 66 | d = getNow() 67 | d.add(d.HOUR, hours) 68 | return d 69 | 70 | def isDateClassExpired(d): 71 | n = getNow() 72 | return d.before(n) 73 | 74 | def isDateClassBefore(d1, d2): 75 | return d1.before(d2) 76 | -------------------------------------------------------------------------------- /src/tlslite/utils/entropy.c: -------------------------------------------------------------------------------- 1 | 2 | #include "Python.h" 3 | 4 | 5 | #ifdef MS_WINDOWS 6 | 7 | /* The following #define is not needed on VC6 with the Platform SDK, and it 8 | may not be needed on VC7, I'm not sure. I don't think it hurts anything.*/ 9 | #define _WIN32_WINNT 0x0400 10 | 11 | #include 12 | 13 | 14 | typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ 15 | LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ 16 | DWORD dwFlags ); 17 | typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ 18 | BYTE *pbBuffer ); 19 | typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv,\ 20 | DWORD dwFlags); 21 | 22 | 23 | static PyObject* entropy(PyObject *self, PyObject *args) 24 | { 25 | int howMany = 0; 26 | HINSTANCE hAdvAPI32 = NULL; 27 | CRYPTACQUIRECONTEXTA pCryptAcquireContextA = NULL; 28 | CRYPTGENRANDOM pCryptGenRandom = NULL; 29 | CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; 30 | HCRYPTPROV hCryptProv = 0; 31 | unsigned char* bytes = NULL; 32 | PyObject* returnVal = NULL; 33 | 34 | 35 | /* Read arguments */ 36 | if (!PyArg_ParseTuple(args, "i", &howMany)) 37 | return(NULL); 38 | 39 | /* Obtain handle to the DLL containing CryptoAPI 40 | This should not fail */ 41 | if( (hAdvAPI32 = GetModuleHandle("advapi32.dll")) == NULL) { 42 | PyErr_Format(PyExc_SystemError, 43 | "Advapi32.dll not found"); 44 | return NULL; 45 | } 46 | 47 | /* Obtain pointers to the CryptoAPI functions 48 | This will fail on some early version of Win95 */ 49 | pCryptAcquireContextA = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32,\ 50 | "CryptAcquireContextA"); 51 | pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\ 52 | "CryptGenRandom"); 53 | pCryptReleaseContext = (CRYPTRELEASECONTEXT) GetProcAddress(hAdvAPI32,\ 54 | "CryptReleaseContext"); 55 | if (pCryptAcquireContextA == NULL || pCryptGenRandom == NULL || 56 | pCryptReleaseContext == NULL) { 57 | PyErr_Format(PyExc_NotImplementedError, 58 | "CryptoAPI not available on this version of Windows"); 59 | return NULL; 60 | } 61 | 62 | /* Allocate bytes */ 63 | if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL) 64 | return PyErr_NoMemory(); 65 | 66 | 67 | /* Acquire context */ 68 | if(!pCryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 69 | CRYPT_VERIFYCONTEXT)) { 70 | PyErr_Format(PyExc_SystemError, 71 | "CryptAcquireContext failed, error %d", GetLastError()); 72 | PyMem_Free(bytes); 73 | return NULL; 74 | } 75 | 76 | /* Get random data */ 77 | if(!pCryptGenRandom(hCryptProv, howMany, bytes)) { 78 | PyErr_Format(PyExc_SystemError, 79 | "CryptGenRandom failed, error %d", GetLastError()); 80 | PyMem_Free(bytes); 81 | CryptReleaseContext(hCryptProv, 0); 82 | return NULL; 83 | } 84 | 85 | /* Build return value */ 86 | returnVal = Py_BuildValue("s#", bytes, howMany); 87 | PyMem_Free(bytes); 88 | 89 | /* Release context */ 90 | if (!pCryptReleaseContext(hCryptProv, 0)) { 91 | PyErr_Format(PyExc_SystemError, 92 | "CryptReleaseContext failed, error %d", GetLastError()); 93 | return NULL; 94 | } 95 | 96 | return returnVal; 97 | } 98 | 99 | #elif defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL_H) 100 | 101 | #include 102 | #include 103 | 104 | static PyObject* entropy(PyObject *self, PyObject *args) 105 | { 106 | int howMany; 107 | int fd; 108 | unsigned char* bytes = NULL; 109 | PyObject* returnVal = NULL; 110 | 111 | 112 | /* Read arguments */ 113 | if (!PyArg_ParseTuple(args, "i", &howMany)) 114 | return(NULL); 115 | 116 | /* Allocate bytes */ 117 | if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL) 118 | return PyErr_NoMemory(); 119 | 120 | /* Open device */ 121 | if ((fd = open("/dev/urandom", O_RDONLY, 0)) == -1) { 122 | PyErr_Format(PyExc_NotImplementedError, 123 | "No entropy source found"); 124 | PyMem_Free(bytes); 125 | return NULL; 126 | } 127 | 128 | /* Get random data */ 129 | if (read(fd, bytes, howMany) < howMany) { 130 | PyErr_Format(PyExc_SystemError, 131 | "Reading from /dev/urandom failed"); 132 | PyMem_Free(bytes); 133 | close(fd); 134 | return NULL; 135 | } 136 | 137 | /* Build return value */ 138 | returnVal = Py_BuildValue("s#", bytes, howMany); 139 | PyMem_Free(bytes); 140 | 141 | /* Close device */ 142 | close(fd); 143 | 144 | return returnVal; 145 | } 146 | 147 | #else 148 | 149 | static PyObject* entropy(PyObject *self, PyObject *args) 150 | { 151 | PyErr_Format(PyExc_NotImplementedError, 152 | "Function not supported"); 153 | return NULL; 154 | } 155 | 156 | #endif 157 | 158 | 159 | 160 | /* List of functions exported by this module */ 161 | 162 | static struct PyMethodDef entropy_functions[] = { 163 | {"entropy", (PyCFunction)entropy, METH_VARARGS, "Return a string of random bytes produced by a platform-specific\nentropy source."}, 164 | {NULL, NULL} /* Sentinel */ 165 | }; 166 | 167 | 168 | /* Initialize this module. */ 169 | 170 | PyMODINIT_FUNC initentropy(void) 171 | { 172 | Py_InitModule("entropy", entropy_functions); 173 | } -------------------------------------------------------------------------------- /src/tlslite/utils/hmac.py: -------------------------------------------------------------------------------- 1 | """HMAC (Keyed-Hashing for Message Authentication) Python module. 2 | 3 | Implements the HMAC algorithm as described by RFC 2104. 4 | 5 | (This file is modified from the standard library version to do faster 6 | copying) 7 | """ 8 | 9 | def _strxor(s1, s2): 10 | """Utility method. XOR the two strings s1 and s2 (must have same length). 11 | """ 12 | return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2)) 13 | 14 | # The size of the digests returned by HMAC depends on the underlying 15 | # hashing module used. 16 | digest_size = None 17 | 18 | class HMAC: 19 | """RFC2104 HMAC class. 20 | 21 | This supports the API for Cryptographic Hash Functions (PEP 247). 22 | """ 23 | 24 | def __init__(self, key, msg = None, digestmod = None): 25 | """Create a new HMAC object. 26 | 27 | key: key for the keyed hash object. 28 | msg: Initial input for the hash, if provided. 29 | digestmod: A module supporting PEP 247. Defaults to the md5 module. 30 | """ 31 | if digestmod is None: 32 | import hashlib 33 | digestmod = hashlib.md5 34 | 35 | if key == None: #TREVNEW - for faster copying 36 | return #TREVNEW 37 | 38 | self.digestmod = digestmod 39 | self.outer = digestmod() 40 | self.inner = digestmod() 41 | self.digest_size = digestmod().digest_size 42 | 43 | blocksize = 64 44 | ipad = "\x36" * blocksize 45 | opad = "\x5C" * blocksize 46 | 47 | if len(key) > blocksize: 48 | key = digestmod(key).digest() 49 | 50 | key = key + chr(0) * (blocksize - len(key)) 51 | self.outer.update(_strxor(key, opad)) 52 | self.inner.update(_strxor(key, ipad)) 53 | if msg is not None: 54 | self.update(msg) 55 | 56 | ## def clear(self): 57 | ## raise NotImplementedError, "clear() method not available in HMAC." 58 | 59 | def update(self, msg): 60 | """Update this hashing object with the string msg. 61 | """ 62 | self.inner.update(msg) 63 | 64 | def copy(self): 65 | """Return a separate copy of this hashing object. 66 | 67 | An update to this copy won't affect the original object. 68 | """ 69 | other = HMAC(None) #TREVNEW - for faster copying 70 | other.digest_size = self.digest_size #TREVNEW 71 | other.digestmod = self.digestmod 72 | other.inner = self.inner.copy() 73 | other.outer = self.outer.copy() 74 | return other 75 | 76 | def digest(self): 77 | """Return the hash value of this hashing object. 78 | 79 | This returns a string containing 8-bit data. The object is 80 | not altered in any way by this function; you can continue 81 | updating the object after calling this function. 82 | """ 83 | h = self.outer.copy() 84 | h.update(self.inner.digest()) 85 | return h.digest() 86 | 87 | def hexdigest(self): 88 | """Like digest(), but returns a string of hexadecimal digits instead. 89 | """ 90 | return "".join([hex(ord(x))[2:].zfill(2) 91 | for x in tuple(self.digest())]) 92 | 93 | def new(key, msg = None, digestmod = None): 94 | """Create a new hashing object and return it. 95 | 96 | key: The starting key for the hash. 97 | msg: if available, will immediately be hashed into the object's starting 98 | state. 99 | 100 | You can now feed arbitrary strings into the object using its update() 101 | method, and can ask for the hash value at any time by calling its digest() 102 | method. 103 | """ 104 | return HMAC(key, msg, digestmod) 105 | -------------------------------------------------------------------------------- /src/tlslite/utils/jython_compat.py: -------------------------------------------------------------------------------- 1 | """Miscellaneous functions to mask Python/Jython differences.""" 2 | 3 | import os 4 | import sha 5 | 6 | if os.name != "java": 7 | BaseException = Exception 8 | 9 | from sets import Set 10 | import array 11 | import math 12 | 13 | def createByteArraySequence(seq): 14 | return array.array('B', seq) 15 | def createByteArrayZeros(howMany): 16 | return array.array('B', [0] * howMany) 17 | def concatArrays(a1, a2): 18 | return a1+a2 19 | 20 | def bytesToString(bytes): 21 | return bytes.tostring() 22 | 23 | def stringToBytes(s): 24 | bytes = createByteArrayZeros(0) 25 | bytes.fromstring(s) 26 | return bytes 27 | 28 | def numBits(n): 29 | if n==0: 30 | return 0 31 | return int(math.floor(math.log(n, 2))+1) 32 | 33 | class CertChainBase: pass 34 | class SelfTestBase: pass 35 | class ReportFuncBase: pass 36 | 37 | #Helper functions for working with sets (from Python 2.3) 38 | def iterSet(set): 39 | return iter(set) 40 | 41 | def getListFromSet(set): 42 | return list(set) 43 | 44 | #Factory function for getting a SHA1 object 45 | def getSHA1(s): 46 | return sha.sha(s) 47 | 48 | import sys 49 | import traceback 50 | 51 | def formatExceptionTrace(e): 52 | newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) 53 | return newStr 54 | 55 | else: 56 | #Jython 2.1 is missing lots of python 2.3 stuff, 57 | #which we have to emulate here: 58 | import java 59 | import jarray 60 | 61 | BaseException = java.lang.Exception 62 | 63 | def createByteArraySequence(seq): 64 | if isinstance(seq, type("")): #If it's a string, convert 65 | seq = [ord(c) for c in seq] 66 | return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed 67 | def createByteArrayZeros(howMany): 68 | return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed 69 | def concatArrays(a1, a2): 70 | l = list(a1)+list(a2) 71 | return createByteArraySequence(l) 72 | 73 | #WAY TOO SLOW - MUST BE REPLACED------------ 74 | def bytesToString(bytes): 75 | return "".join([chr(b) for b in bytes]) 76 | 77 | def stringToBytes(s): 78 | bytes = createByteArrayZeros(len(s)) 79 | for count, c in enumerate(s): 80 | bytes[count] = ord(c) 81 | return bytes 82 | #WAY TOO SLOW - MUST BE REPLACED------------ 83 | 84 | def numBits(n): 85 | if n==0: 86 | return 0 87 | n= 1L * n; #convert to long, if it isn't already 88 | return n.__tojava__(java.math.BigInteger).bitLength() 89 | 90 | #This properly creates static methods for Jython 91 | class staticmethod: 92 | def __init__(self, anycallable): self.__call__ = anycallable 93 | 94 | #Properties are not supported for Jython 95 | class property: 96 | def __init__(self, anycallable): pass 97 | 98 | #True and False have to be specially defined 99 | False = 0 100 | True = 1 101 | 102 | class StopIteration(Exception): pass 103 | 104 | def enumerate(collection): 105 | return zip(range(len(collection)), collection) 106 | 107 | class Set: 108 | def __init__(self, seq=None): 109 | self.values = {} 110 | if seq: 111 | for e in seq: 112 | self.values[e] = None 113 | 114 | def add(self, e): 115 | self.values[e] = None 116 | 117 | def discard(self, e): 118 | if e in self.values.keys(): 119 | del(self.values[e]) 120 | 121 | def union(self, s): 122 | ret = Set() 123 | for e in self.values.keys(): 124 | ret.values[e] = None 125 | for e in s.values.keys(): 126 | ret.values[e] = None 127 | return ret 128 | 129 | def issubset(self, other): 130 | for e in self.values.keys(): 131 | if e not in other.values.keys(): 132 | return False 133 | return True 134 | 135 | def __nonzero__( self): 136 | return len(self.values.keys()) 137 | 138 | def __contains__(self, e): 139 | return e in self.values.keys() 140 | 141 | def iterSet(set): 142 | return set.values.keys() 143 | 144 | def getListFromSet(set): 145 | return set.values.keys() 146 | 147 | """ 148 | class JCE_SHA1: 149 | def __init__(self, s=None): 150 | self.md = java.security.MessageDigest.getInstance("SHA1") 151 | if s: 152 | self.update(s) 153 | 154 | def update(self, s): 155 | self.md.update(s) 156 | 157 | def copy(self): 158 | sha1 = JCE_SHA1() 159 | sha1.md = self.md.clone() 160 | return sha1 161 | 162 | def digest(self): 163 | digest = self.md.digest() 164 | bytes = jarray.zeros(20, 'h') 165 | for count in xrange(20): 166 | x = digest[count] 167 | if x < 0: x += 256 168 | bytes[count] = x 169 | return bytes 170 | """ 171 | 172 | #Factory function for getting a SHA1 object 173 | #The JCE_SHA1 class is way too slow... 174 | #the sha.sha object we use instead is broken in the jython 2.1 175 | #release, and needs to be patched 176 | def getSHA1(s): 177 | #return JCE_SHA1(s) 178 | return sha.sha(s) 179 | 180 | 181 | #Adjust the string to an array of bytes 182 | def stringToJavaByteArray(s): 183 | bytes = jarray.zeros(len(s), 'b') 184 | for count, c in enumerate(s): 185 | x = ord(c) 186 | if x >= 128: x -= 256 187 | bytes[count] = x 188 | return bytes 189 | 190 | import sys 191 | import traceback 192 | 193 | def formatExceptionTrace(e): 194 | newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) 195 | return newStr 196 | -------------------------------------------------------------------------------- /src/tlslite/utils/win32prng.c: -------------------------------------------------------------------------------- 1 | 2 | #include "Python.h" 3 | #define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */ 4 | #include 5 | 6 | 7 | static PyObject* getRandomBytes(PyObject *self, PyObject *args) 8 | { 9 | int howMany; 10 | HCRYPTPROV hCryptProv; 11 | unsigned char* bytes = NULL; 12 | PyObject* returnVal = NULL; 13 | 14 | 15 | /* Read Arguments */ 16 | if (!PyArg_ParseTuple(args, "i", &howMany)) 17 | return(NULL); 18 | 19 | /* Get Context */ 20 | if(CryptAcquireContext( 21 | &hCryptProv, 22 | NULL, 23 | NULL, 24 | PROV_RSA_FULL, 25 | CRYPT_VERIFYCONTEXT) == 0) 26 | return Py_BuildValue("s#", NULL, 0); 27 | 28 | 29 | /* Allocate bytes */ 30 | bytes = malloc(howMany); 31 | 32 | 33 | /* Get random data */ 34 | if(CryptGenRandom( 35 | hCryptProv, 36 | howMany, 37 | bytes) == 0) 38 | returnVal = Py_BuildValue("s#", NULL, 0); 39 | else 40 | returnVal = Py_BuildValue("s#", bytes, howMany); 41 | 42 | free(bytes); 43 | CryptReleaseContext(hCryptProv, 0); 44 | 45 | return returnVal; 46 | } 47 | 48 | 49 | 50 | /* List of functions exported by this module */ 51 | 52 | static struct PyMethodDef win32prng_functions[] = { 53 | {"getRandomBytes", (PyCFunction)getRandomBytes, METH_VARARGS}, 54 | {NULL, NULL} /* Sentinel */ 55 | }; 56 | 57 | 58 | /* Initialize this module. */ 59 | 60 | DL_EXPORT(void) initwin32prng(void) 61 | { 62 | Py_InitModule("win32prng", win32prng_functions); 63 | } 64 | -------------------------------------------------------------------------------- /src/tlslite/utils/xmltools.py: -------------------------------------------------------------------------------- 1 | """Helper functions for XML. 2 | 3 | This module has misc. helper functions for working with XML DOM nodes.""" 4 | 5 | import re 6 | from compat import * 7 | 8 | import os 9 | if os.name != "java": 10 | from xml.dom import minidom 11 | from xml.sax import saxutils 12 | 13 | def parseDocument(s): 14 | return minidom.parseString(s) 15 | else: 16 | from javax.xml.parsers import * 17 | import java 18 | 19 | builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() 20 | 21 | def parseDocument(s): 22 | stream = java.io.ByteArrayInputStream(java.lang.String(s).getBytes()) 23 | return builder.parse(stream) 24 | 25 | def parseAndStripWhitespace(s): 26 | try: 27 | element = parseDocument(s).documentElement 28 | except BaseException, e: 29 | raise SyntaxError(str(e)) 30 | stripWhitespace(element) 31 | return element 32 | 33 | #Goes through a DOM tree and removes whitespace besides child elements, 34 | #as long as this whitespace is correctly tab-ified 35 | def stripWhitespace(element, tab=0): 36 | element.normalize() 37 | 38 | lastSpacer = "\n" + ("\t"*tab) 39 | spacer = lastSpacer + "\t" 40 | 41 | #Zero children aren't allowed (i.e. ) 42 | #This makes writing output simpler, and matches Canonical XML 43 | if element.childNodes.length==0: #DON'T DO len(element.childNodes) - doesn't work in Jython 44 | raise SyntaxError("Empty XML elements not allowed") 45 | 46 | #If there's a single child, it must be text context 47 | if element.childNodes.length==1: 48 | if element.firstChild.nodeType == element.firstChild.TEXT_NODE: 49 | #If it's an empty element, remove 50 | if element.firstChild.data == lastSpacer: 51 | element.removeChild(element.firstChild) 52 | return 53 | #If not text content, give an error 54 | elif element.firstChild.nodeType == element.firstChild.ELEMENT_NODE: 55 | raise SyntaxError("Bad whitespace under '%s'" % element.tagName) 56 | else: 57 | raise SyntaxError("Unexpected node type in XML document") 58 | 59 | #Otherwise there's multiple child element 60 | child = element.firstChild 61 | while child: 62 | if child.nodeType == child.ELEMENT_NODE: 63 | stripWhitespace(child, tab+1) 64 | child = child.nextSibling 65 | elif child.nodeType == child.TEXT_NODE: 66 | if child == element.lastChild: 67 | if child.data != lastSpacer: 68 | raise SyntaxError("Bad whitespace under '%s'" % element.tagName) 69 | elif child.data != spacer: 70 | raise SyntaxError("Bad whitespace under '%s'" % element.tagName) 71 | next = child.nextSibling 72 | element.removeChild(child) 73 | child = next 74 | else: 75 | raise SyntaxError("Unexpected node type in XML document") 76 | 77 | 78 | def checkName(element, name): 79 | if element.nodeType != element.ELEMENT_NODE: 80 | raise SyntaxError("Missing element: '%s'" % name) 81 | 82 | if name == None: 83 | return 84 | 85 | if element.tagName != name: 86 | raise SyntaxError("Wrong element name: should be '%s', is '%s'" % (name, element.tagName)) 87 | 88 | def getChild(element, index, name=None): 89 | if element.nodeType != element.ELEMENT_NODE: 90 | raise SyntaxError("Wrong node type in getChild()") 91 | 92 | child = element.childNodes.item(index) 93 | if child == None: 94 | raise SyntaxError("Missing child: '%s'" % name) 95 | checkName(child, name) 96 | return child 97 | 98 | def getChildIter(element, index): 99 | class ChildIter: 100 | def __init__(self, element, index): 101 | self.element = element 102 | self.index = index 103 | 104 | def next(self): 105 | if self.index < len(self.element.childNodes): 106 | retVal = self.element.childNodes.item(self.index) 107 | self.index += 1 108 | else: 109 | retVal = None 110 | return retVal 111 | 112 | def checkEnd(self): 113 | if self.index != len(self.element.childNodes): 114 | raise SyntaxError("Too many elements under: '%s'" % self.element.tagName) 115 | return ChildIter(element, index) 116 | 117 | def getChildOrNone(element, index): 118 | if element.nodeType != element.ELEMENT_NODE: 119 | raise SyntaxError("Wrong node type in getChild()") 120 | child = element.childNodes.item(index) 121 | return child 122 | 123 | def getLastChild(element, index, name=None): 124 | if element.nodeType != element.ELEMENT_NODE: 125 | raise SyntaxError("Wrong node type in getLastChild()") 126 | 127 | child = element.childNodes.item(index) 128 | if child == None: 129 | raise SyntaxError("Missing child: '%s'" % name) 130 | if child != element.lastChild: 131 | raise SyntaxError("Too many elements under: '%s'" % element.tagName) 132 | checkName(child, name) 133 | return child 134 | 135 | #Regular expressions for syntax-checking attribute and element content 136 | nsRegEx = "http://trevp.net/cryptoID\Z" 137 | cryptoIDRegEx = "([a-km-z3-9]{5}\.){3}[a-km-z3-9]{5}\Z" 138 | urlRegEx = "http(s)?://.{1,100}\Z" 139 | sha1Base64RegEx = "[A-Za-z0-9+/]{27}=\Z" 140 | base64RegEx = "[A-Za-z0-9+/]+={0,4}\Z" 141 | certsListRegEx = "(0)?(1)?(2)?(3)?(4)?(5)?(6)?(7)?(8)?(9)?\Z" 142 | keyRegEx = "[A-Z]\Z" 143 | 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" 144 | dateTimeRegEx = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\Z" 145 | shortStringRegEx = ".{1,100}\Z" 146 | exprRegEx = "[a-zA-Z0-9 ,()]{1,200}\Z" 147 | notAfterDeltaRegEx = "0|([1-9][0-9]{0,8})\Z" #A number from 0 to (1 billion)-1 148 | booleanRegEx = "(true)|(false)" 149 | 150 | def getReqAttribute(element, attrName, regEx=""): 151 | if element.nodeType != element.ELEMENT_NODE: 152 | raise SyntaxError("Wrong node type in getReqAttribute()") 153 | 154 | value = element.getAttribute(attrName) 155 | if not value: 156 | raise SyntaxError("Missing Attribute: " + attrName) 157 | if not re.match(regEx, value): 158 | raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value)) 159 | element.removeAttribute(attrName) 160 | return str(value) #de-unicode it; this is needed for bsddb, for example 161 | 162 | def getAttribute(element, attrName, regEx=""): 163 | if element.nodeType != element.ELEMENT_NODE: 164 | raise SyntaxError("Wrong node type in getAttribute()") 165 | 166 | value = element.getAttribute(attrName) 167 | if value: 168 | if not re.match(regEx, value): 169 | raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value)) 170 | element.removeAttribute(attrName) 171 | return str(value) #de-unicode it; this is needed for bsddb, for example 172 | 173 | def checkNoMoreAttributes(element): 174 | if element.nodeType != element.ELEMENT_NODE: 175 | raise SyntaxError("Wrong node type in checkNoMoreAttributes()") 176 | 177 | if element.attributes.length!=0: 178 | raise SyntaxError("Extra attributes on '%s'" % element.tagName) 179 | 180 | def getText(element, regEx=""): 181 | textNode = element.firstChild 182 | if textNode == None: 183 | raise SyntaxError("Empty element '%s'" % element.tagName) 184 | if textNode.nodeType != textNode.TEXT_NODE: 185 | raise SyntaxError("Non-text node: '%s'" % element.tagName) 186 | if not re.match(regEx, textNode.data): 187 | raise SyntaxError("Bad Text Value for '%s': '%s' " % (element.tagName, textNode.data)) 188 | return str(textNode.data) #de-unicode it; this is needed for bsddb, for example 189 | 190 | #Function for adding tabs to a string 191 | def indent(s, steps, ch="\t"): 192 | tabs = ch*steps 193 | if s[-1] != "\n": 194 | s = tabs + s.replace("\n", "\n"+tabs) 195 | else: 196 | s = tabs + s.replace("\n", "\n"+tabs) 197 | s = s[ : -len(tabs)] 198 | return s 199 | 200 | def escape(s): 201 | return saxutils.escape(s) 202 | --------------------------------------------------------------------------------