tlslite version 0.2.0 February 19, 2004 Trevor Perrin <trevp at trevp.net> http://trevp.net/tlslite ===============================================================================
TLS Lite is a free python library that implements SSL v3 and TLS v1 . TLS Lite supports non-traditional authentication methods such as SRP , shared keys, and cryptoIDs , in addition to X.509 certificates. TLS Lite is pure python, however it can access OpenSSL  or cryptlib  for faster crypto operations.
If you have questions or feedback, feel free to contact me.
On Windows, DLLs containing M2Crypto  and OpenSSL routines may be installed. Copyright notices, licenses, and acknowledgements for these libraries are at the bottom of this file.
All other code here is public domain.
- Python 2.3.2 or greater is recommended. Python versions 2.2.1 or greater should work, but haven't been tested much. The initial Python 2.3 release had a memory leak bug, and SHOULD NOT be used.
- If you have the M2Crypto interface to OpenSSL, this will be used for fast
RSA operations, fast ciphers, and accessing OpenSSL's PEM-encoded keys. The windows installer installs a DLL for M2Crypto / OpenSSL side-by-side with tlslite (so it doesn't interfere with anything else).
- If you have the cryptlib_py  interface to cryptlib, this will be used
for good randomness generation and fast ciphers.
- If you have cryptoIDlib , you can use cryptoID certificate chains for
- These modules don't need to be present at installation - you can install
them any time.
- On Windows:
- Run the installer in the 'installers' directory.
OR - Run 'python setup.py install' (this only works if your system has a compiler available).
- Anywhere else:
- Run 'python setup.py install'
- Test the Installation:
- From the distribution's root directory, run: 'python tls.py servertest ./test'
- While the test server is waiting, run: 'python tls.py clienttest ./test'
If both say "Test succeeded" at the end, you're ready to go.
Getting Started with the Command-Line Tools
tlslite comes with two command-line scripts: 'tlsdb.py' and 'tls.py'. They can be run with no arguments to see a list of commands.
'tlsdb.py' lets you manage shared key or verifier databases. These databases store usernames associated with either shared keys, or SRP password verifiers. These databases are used by a TLS server when authenticating clients with shared keys or SRP.
'tls.py' lets you run test clients and servers. It can be used for testing other TLS implementations, or as example code for using tlslite. To run an SRP server, try something like:
tlsdb.py createsrp verifierDB tlsdb.py add verifierDB alice abra123cadabra 1024 tlsdb.py add verifierDB bob swordfish 2048
tls.py serversrp localhost:443 verifierDB
Then you can try connecting to the server with:
tls.py clientsrp localhost:443 alice abra123cadabra
Getting Started with the Library
Using the library is simple. Whether you're writing a client or server, there are six steps: 1) Create a socket and connect it to the other party. 2) Construct a TLSConnection instance with the socket. 3) Call a handshake function on the TLSConnection to perform the TLS handshake. 4) Check the results to make sure you're talking to the right party. 5) Use the TLSConnection to exchange data. 6) Call close() on the TLSConnection when you're done.
TLS Lite also integrates with Python's httplib module and SocketServer framework. When used with these, some of the steps are performed for you. See the next section for details.
Step 1 - create a socket
Below demonstrates a socket connection to Amazon's secure site. It's a good idea to set the timeout value, so if the other side fails to respond you won't end up waiting forever.
from socket import * sock = socket(AF_INET, SOCK_STREAM) sock.connect( ("www.amazon.com", 443) ) sock.settimeout(10) #Only on python 2.3
Step 2 - construct a TLSConnection
import tlslite connection = tlslite.TLSConnection(sock)
Step 3 - call a handshake function (client)
If you're a client, there's several different handshake functions you can call, depending on how you want to authenticate:
connection.handshakeClientNoAuth() connection.handshakeClientCert(certChain, privateKey) connection.handshakeClientSRP("alice", "abra123cadabra") connection.handshakeClientSharedKey("alice", "PaVBVZkYqAjCQCu6UBL2xgsnZhw") connection.handshakeClientUnknown(srpCallback, certCallback)
The ClientNoAuth function is used when connecting to a site like Amazon, which doesn't require client authentication. The server will authenticate with a certificate chain.
The ClientCert function is used to do client authentication with an X.509 or cryptoID certificate chain. To use cryptoID certificate chains, you'll need the cryptoIDlib library . To use X.509 certificate chains, you'll need some way of creating these, and you'll need M2Crypto and OpenSSL to load up the PEM- encoded private key.
#Load cryptoID certChain and privateKey. Requires cryptoIDlib. import cryptoIDlib s = open("./test/clientCryptoIDChain.xml").read() certChain = cryptoIDlib.CertChain() certChain.parse(s) s = open("./test/clientCryptoIDKey.xml").read() privateKey = tlslite.parseXMLKey(s, private=True)
#Load X.509 certChain and privateKey. Requires M2Crypto/OpenSSL. s = open("./test/clientX509Cert.pem").read() x509 = tlslite.X509() x509.parse(s) certChain = tlslite.X509CertChain([x509]) s = open("./test/clientX509Key.pem").read() privateKey = tlslite.parsePEMKey(s, private=True)
The SRP and SharedKey functions both do mutual authentication with a username and password. The difference is this: SRP is slow but safer when using low- entropy passwords, since the SRP protocol is not vulnerable to offline dictionary attacks. Using shared keys is faster, but it's only safe when used with high-entropy secrets. In general, you should prefer SRP for human- memorable passwords, and use shared keys only when your performance needs outweigh the inconvenience of handling large random strings.
[WARNING: shared keys and SRP are internet-drafts; these protocols may change, which means future versions of tlslite may not be compatible with this one. This is less likely with SRP, more likely with shared-keys.]
The Unknown function is used when you're not sure if the server requires client authentication. If the server requests SRP or certificate-based authentication, the appropriate callback will be triggered, and you should return a tuple containing either a (username, password) or (certChain, privateKey), as appropriate. Alternatively, you can return None, which will cancel the handshake from an SRP callback, or cause it to continue without client authentication (if the server is willing) from a certificate callback.
If you want more control over the handshake, you can pass in a HandshakeSettings instance. For example, if you're performing SRP, but you only want to use SRP parameters of at least 2048 bits, and you only want to use the AES-256 cipher, and you only want to allow TLS (version 3.1), not SSL (version 3.0), you can do:
settings = tlslite.HandshakeSettings() settings.minKeySize = 2048 settings.cipherNames = ["aes256"] settings.minVersion = (3,1) connection.handshakeClientSRP("alice", "abra123cadabra", settings=settings)
Finally, every TLSConnection has a session object. You can try to resume a previous session by passing in the session object from the old session. If the server remembers this old session and supports resumption, the handshake will finish more quickly. Otherwise, the full handshake will be done. For example:
connection.handshakeClientSRP("alice", "abra123cadabra") . . oldSession = connection.session connection2.handshakeClientSRP("alice", "abra123cadabra", session=oldSession)
Step 3 - call a handshake function (server)
If you're a server, there's only one handshake function, but you can pass it several different parameters, depending on which types of authentication you're willing to perform.
To perform SRP authentication, you have to pass in a database of password verifiers. The VerifierDB class manages an in-memory or on-disk verifier database.
#On-disk database (use no-arg constructor if you want an in-memory DB) verifierDB = tlslite.VerifierDB("./test/verifierDB")
#Open the pre-existing database (can also 'create()' a new one) verifierDB.open()
#Add to the database N, g, salt, verifier = tlslite.makeVerifier("alice", "abra123cadabra", 2048) verifierDB["alice"] = N, g, salt, verifier
#Perform a handshake using the database connection.handshakeServer(verifierDB=verifierDB)
To perform shared key authentication, you have to pass in a database of shared keys. The SharedKeyDB class manages an in-memory or on-disk shared key database.
sharedKeyDB = tlslite.SharedKeyDB("./test/sharedkeyDB") sharedKeyDB.open() sharedKeyDB["alice"] = "PaVBVZkYqAjCQCu6UBL2xgsnZhw" connection.handshakeServer(sharedKeyDB=sharedKeyDB)
To perform authentication with a certificate and private key, the server must load these as described in the previous section, then pass them in. If the server sets the reqCert boolean to True, a certificate chain will be requested from the client.
- connection.handshakeServer(certChain=certChain, privateKey=privateKey,
You can pass in any combination of a verifier database, a shared key database, and a certificate chain/private key. The client will use one of them to authenticate. In the case of SRP and a certificate chain/private key, they both may be used.
You can also pass in a HandshakeSettings object, as described in the last section, for finer control over handshaking details. Finally, the server can maintain a SessionCache, which will allow clients to use session resumption:
sessionCache = tlslite.SessionCache() connection.handshakeServer(verifierDB=verifierDB, sessionCache=sessionCache)
It should be noted that the session cache, and the verifier and shared key databases, are all thread-safe.
Step 4 - check the results
If the handshake completes without raising an exception, authentication results will be stored in the connection's session object. The following variables will be populated if applicable, or else set to None:
connection.session.srpUsername #string connection.session.sharedKeyUsername #string connection.session.clientCertChain #X509CertChain or cryptoIDlib.CertChain connection.session.serverCertChain #X509CertChain or cryptoIDlib.CertChain
Both types of certificate chain object support the getFingerprint() function, but with a difference. X.509 objects return the end-entity fingerprint, and ignore the other certificates. CryptoID fingerprints (aka "cryptoIDs") are based on the root cryptoID certificate, so you have to call validate() on the CertChain to be sure you're really talking to the cryptoID.
To save yourself the trouble of inspecting fingerprints after the handshake, you can pass a Checker object into the handshake function. The checker will be called if the handshake completes successfully. If the other party's fingerprint isn't approved by the checker, a subclass of AuthenticationError will be raised. For example, to perform a handshake with a server based on its X.509 fingerprint, do:
- checker = tlslite.Checker(
- except tlslite.AuthenticationError:
- print "Authentication failure"
If the handshake fails for any reason, an exception will be raised. If the socket timed out or was unexpectedly closed, a socket.error will be raised. Otherwise, either a TLSLocalAlert or TLSRemoteAlert will be raised, depending on whether the local or remote implementation signalled the error. The exception object has a 'description' member which identifies the error based on the codes in RFC 2246. A TLSLocalAlert also has a 'message' string that may have more details.
Example of handling a remote alert:
- except tlslite.TLSRemoteAlert, alert:
- if alert.description == tlslite.AlertDescription.unknown_srp_username:
- print "Unknown user."
Figuring out what went wrong based on the alert may require some interpretation, particularly with remote alerts where you don't have an error string, and where the remote implementation may not be signalling alerts properly. Many alerts signal an implementation error, and so should rarely be seen in normal operation (unexpected_message, decode_error, illegal_parameter, internal_error, etc.).
Others alerts are more likely to occur. Below are some common alerts and their probable causes, and whether they are signalled by the client or server.
- Client bad_record_mac:
- bad shared key password
- Client handshake failure:
- SRP parameters are not recognized by client
- Client user_canceled:
- server isn't what the client was expecting (for example, the client might
have called handshakeClientCert(), but the server didn't request a client certificate. Or, the client might have returned None from an SRP callback).
- Client insufficient_security:
- SRP parameters are too small
- Server protocol_version:
- Server or client doesn't support your protocol version
- Server bad_record_mac:
- bad SRP username or password
- Server unknown_srp_username
- bad SRP username (bad_record_mac could be used for the same thing)
- Server handshake_failure:
- bad shared key username
- no matching cipher suites
Step 5 - exchange data
Now that you have a connection, you can call read() and write() as if it were a socket.SSL object. You can also call send(), sendall(), recv(), and makefile() as if it were a socket. These calls may raise TLSLocalAlert, TLSRemoteAlert, or socket.error, just like the handshake functions.
Once the TLS connection is closed by the other side, calls to read() will return an empty string. If the socket is closed by the other side without first closing the TLS connection, a call to read() or write() will return a socket.error.
Step 6 - close the connection
When you're finished sending data, you should call close() to shut the connection down and signal the other side you're done. When the connection is closed properly, the socket stays open and can be used for exchanging non-secure data, the session object can be used for session resumption, and the connection object can be re-used by calling another handshake function.
If a socket.error or alert is raised, the connection will be automatically closed; you don't need to call close(). Furthermore, you will probably not be able to re-use the socket, the connection object, or the session object, and you shouldn't even try.
By default, calling close() will leave the socket open. If you set the connection's closeSocket flag to True, the connection will take ownership of the socket, and close it when the connection is closed.
Using TLS Lite with httplib and SocketServer
TLS Lite comes with an HTTPTLSConnection class that extends httplib to work over SSL/TLS connections. Depending on how you construct it, it will do different types of authentication. For example:
#No authentication whatsoever h = tlslite.HTTPTLSConnection("www.amazon.com", 443) h.request("GET", "") r = h.getresponse() [...]
#Authenticate server based on its X.509 fingerprint h = tlslite.HTTPTLSConnection("www.amazon.com", 443,x509Fingerprint="e049ff930af76d43ff4c658b268786f4df1296f2")
#Authenticate server based on its cryptoID h = tlslite.HTTPTLSConnection("localhost", 443,cryptoID="dmqb6.fq345.cxk6g.5fha3")
#Mutually authenticate with SRP h = tlslite.HTTPTLSConnection("localhost", 443,username="alice", password="abra123cadabra")
#Mutually authenticate with a shared key h = tlslite.HTTPTLSConnection("localhost", 443,username="alice", sharedKey="PaVBVZkYqAjCQCu6UBL2xgsnZhw")
#Mutually authenticate with SRP, AND authenticate the server based #on its cryptoID h = tlslite.HTTPTLSConnection("localhost", 443,username="alice", password="abra123cadabra", cryptoID="dmqb6.fq345.cxk6g.5fha3")
You can also use TLS Lite to implement servers using Python's SocketServer framework. TLS Lite comes with a TLSMixIn class. You can combine this with a TCPServer such as HTTPServer. To combine them, define a new class that inherits from both of them (with the mix-in first). Then implement the handshake() method, doing some sort of server handshake on the connection argument. If the handshake method returns True, the RequestHandler will be triggered. Below is a complete example of a threaded HTTPS server.
from SocketServer import * from BaseHTTPServer import * from SimpleHTTPServer import * from tlslite import *
s = open("./serverX509Cert.pem").read() x509 = X509() x509.parse(s) certChain = X509CertChain([x509])
s = open("./serverX509Key.pem").read() privateKey = parsePEMKey(s, private=True)
sessionCache = SessionCache()
- class MyHTTPServer(ThreadingMixIn, TLSMixIn, HTTPServer):
- def handshake(self, tlsConnection):
- privateKey=privateKey, sessionCache=sessionCache)
- except TLSAlert, alert:
- print "Handshake failure:", str(alert) return False
httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler) httpd.serve_forever()
- 0.2.0 - 2/19/2004
- changed VerifierDB to take per-user parameters
- renamed tlslite -> tlslite
- 0.1.9 - 2/16/2004
- added post-handshake 'Checker'
- made compatible with Python 2.2
- made more forgiving of abrupt closure, since everyone does it: if the socket is closed while sending/recv'ing close_notify, just ignore it.
- 0.1.8 - 2/12/2004
- TLSConnections now emulate sockets, including makefile()
- HTTPTLSConnection and TLSMixIn simplified as a result
- 0.1.7 - 2/11/2004
- fixed httplib.HTTPTLSConnection with multiple requests
- fixed SocketServer to handle close_notify
- changed handshakeClientNoAuth() to ignore CertificateRequests
- changed handshakeClient() to ignore non-resumable session arguments
- 0.1.6 - 2/10/2004
- fixed httplib support
- 0.1.5 - 2/09/2004
- added support for httplib and SocketServer
- added support for SSLv3
- added support for 3DES
- cleaned up read()/write() behavior
- improved HMAC speed
- 0.1.4 - 2/06/2004
- fixed dumb bug in tls.py
- 0.1.3 - 2/05/2004
- change read() to only return requested number of bytes
- added support for shared-key and in-memory databases
- added support for PEM-encoded X.509 certificates
- added support for SSLv2 ClientHello
- fixed shutdown/re-handshaking behavior
- cleaned up handling of missing_srp_username
- renamed readString()/writeString() -> read()/write()
- added documentation
- 0.1.2 - 2/04/2004
- added clienttest/servertest functions
- improved OpenSSL cipher wrappers speed
- fixed server when it has a key, but client selects plain SRP
- fixed server to postpone errors until it has read client's messages
- fixed ServerHello to only include extension data if necessary
- 0.1.1 - 2/02/2004
- fixed close_notify behavior
- fixed handling of empty application data packets
- fixed socket reads to not consume extra bytes
- added testing functions to tls.py
- 0.1.0 - 2/01/2004
- first release
 http://www.ietf.org/rfc/rfc2246.txt  http://www.trevp.net/tls_srp/draft-ietf-tls-srp-06.html  http://www.ietf.org/internet-drafts/draft-ietf-tls-sharedkeys-02.txt  http://www.trevp.net/cryptoID/  http://www.openssl.org/  http://www.cs.auckland.ac.nz/~pgut001/cryptlib/  http://sandbox.rulemaker.net/ngps/m2/  http://trevp.net/cryptlibConverter/  http://www.trevp.net/cryptoID/
Copyright Notice and License for M2Crypto
Copyright (c) 1999-2003 Ng Pheng Siong. All rights reserved.
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation.
Copyright Notices and Licenses for OpenSSL
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact email@example.com.
Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. All advertising materials mentioning features or use of this software must display the following acknowledgment: This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org)
The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact firstname.lastname@example.org
Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project.
Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org)"
THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This product includes cryptographic software written by Eric Young (email@example.com). This product includes software written by Tim Hudson (firstname.lastname@example.org).
Original SSLeay License
Copyright (C) 1995-1998 Eric Young (email@example.com) All rights reserved.
This package is an SSL implementation written by Eric Young (firstname.lastname@example.org). The implementation was written so as to conform with Netscapes SSL.
This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (email@example.com).
Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
All advertising materials mentioning features or use of this software must display the following acknowledgement: "This product includes cryptographic software written by
Eric Young (firstname.lastname@example.org)"
The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).
If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (email@example.com)"
THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]