faset over fra Z3950 til google books
This commit is contained in:
235
python/gdata/tlslite/integration/AsyncStateMachine.py
Normal file
235
python/gdata/tlslite/integration/AsyncStateMachine.py
Normal file
@@ -0,0 +1,235 @@
|
||||
"""
|
||||
A state machine for using TLS Lite with asynchronous I/O.
|
||||
"""
|
||||
|
||||
class AsyncStateMachine:
|
||||
"""
|
||||
This is an abstract class that's used to integrate TLS Lite with
|
||||
asyncore and Twisted.
|
||||
|
||||
This class signals wantsReadsEvent() and wantsWriteEvent(). When
|
||||
the underlying socket has become readable or writeable, the event
|
||||
should be passed to this class by calling inReadEvent() or
|
||||
inWriteEvent(). This class will then try to read or write through
|
||||
the socket, and will update its state appropriately.
|
||||
|
||||
This class will forward higher-level events to its subclass. For
|
||||
example, when a complete TLS record has been received,
|
||||
outReadEvent() will be called with the decrypted data.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._clear()
|
||||
|
||||
def _clear(self):
|
||||
#These store the various asynchronous operations (i.e.
|
||||
#generators). Only one of them, at most, is ever active at a
|
||||
#time.
|
||||
self.handshaker = None
|
||||
self.closer = None
|
||||
self.reader = None
|
||||
self.writer = None
|
||||
|
||||
#This stores the result from the last call to the
|
||||
#currently active operation. If 0 it indicates that the
|
||||
#operation wants to read, if 1 it indicates that the
|
||||
#operation wants to write. If None, there is no active
|
||||
#operation.
|
||||
self.result = None
|
||||
|
||||
def _checkAssert(self, maxActive=1):
|
||||
#This checks that only one operation, at most, is
|
||||
#active, and that self.result is set appropriately.
|
||||
activeOps = 0
|
||||
if self.handshaker:
|
||||
activeOps += 1
|
||||
if self.closer:
|
||||
activeOps += 1
|
||||
if self.reader:
|
||||
activeOps += 1
|
||||
if self.writer:
|
||||
activeOps += 1
|
||||
|
||||
if self.result == None:
|
||||
if activeOps != 0:
|
||||
raise AssertionError()
|
||||
elif self.result in (0,1):
|
||||
if activeOps != 1:
|
||||
raise AssertionError()
|
||||
else:
|
||||
raise AssertionError()
|
||||
if activeOps > maxActive:
|
||||
raise AssertionError()
|
||||
|
||||
def wantsReadEvent(self):
|
||||
"""If the state machine wants to read.
|
||||
|
||||
If an operation is active, this returns whether or not the
|
||||
operation wants to read from the socket. If an operation is
|
||||
not active, this returns None.
|
||||
|
||||
@rtype: bool or None
|
||||
@return: If the state machine wants to read.
|
||||
"""
|
||||
if self.result != None:
|
||||
return self.result == 0
|
||||
return None
|
||||
|
||||
def wantsWriteEvent(self):
|
||||
"""If the state machine wants to write.
|
||||
|
||||
If an operation is active, this returns whether or not the
|
||||
operation wants to write to the socket. If an operation is
|
||||
not active, this returns None.
|
||||
|
||||
@rtype: bool or None
|
||||
@return: If the state machine wants to write.
|
||||
"""
|
||||
if self.result != None:
|
||||
return self.result == 1
|
||||
return None
|
||||
|
||||
def outConnectEvent(self):
|
||||
"""Called when a handshake operation completes.
|
||||
|
||||
May be overridden in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
def outCloseEvent(self):
|
||||
"""Called when a close operation completes.
|
||||
|
||||
May be overridden in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
def outReadEvent(self, readBuffer):
|
||||
"""Called when a read operation completes.
|
||||
|
||||
May be overridden in subclass."""
|
||||
pass
|
||||
|
||||
def outWriteEvent(self):
|
||||
"""Called when a write operation completes.
|
||||
|
||||
May be overridden in subclass."""
|
||||
pass
|
||||
|
||||
def inReadEvent(self):
|
||||
"""Tell the state machine it can read from the socket."""
|
||||
try:
|
||||
self._checkAssert()
|
||||
if self.handshaker:
|
||||
self._doHandshakeOp()
|
||||
elif self.closer:
|
||||
self._doCloseOp()
|
||||
elif self.reader:
|
||||
self._doReadOp()
|
||||
elif self.writer:
|
||||
self._doWriteOp()
|
||||
else:
|
||||
self.reader = self.tlsConnection.readAsync(16384)
|
||||
self._doReadOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def inWriteEvent(self):
|
||||
"""Tell the state machine it can write to the socket."""
|
||||
try:
|
||||
self._checkAssert()
|
||||
if self.handshaker:
|
||||
self._doHandshakeOp()
|
||||
elif self.closer:
|
||||
self._doCloseOp()
|
||||
elif self.reader:
|
||||
self._doReadOp()
|
||||
elif self.writer:
|
||||
self._doWriteOp()
|
||||
else:
|
||||
self.outWriteEvent()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def _doHandshakeOp(self):
|
||||
try:
|
||||
self.result = self.handshaker.next()
|
||||
except StopIteration:
|
||||
self.handshaker = None
|
||||
self.result = None
|
||||
self.outConnectEvent()
|
||||
|
||||
def _doCloseOp(self):
|
||||
try:
|
||||
self.result = self.closer.next()
|
||||
except StopIteration:
|
||||
self.closer = None
|
||||
self.result = None
|
||||
self.outCloseEvent()
|
||||
|
||||
def _doReadOp(self):
|
||||
self.result = self.reader.next()
|
||||
if not self.result in (0,1):
|
||||
readBuffer = self.result
|
||||
self.reader = None
|
||||
self.result = None
|
||||
self.outReadEvent(readBuffer)
|
||||
|
||||
def _doWriteOp(self):
|
||||
try:
|
||||
self.result = self.writer.next()
|
||||
except StopIteration:
|
||||
self.writer = None
|
||||
self.result = None
|
||||
|
||||
def setHandshakeOp(self, handshaker):
|
||||
"""Start a handshake operation.
|
||||
|
||||
@type handshaker: generator
|
||||
@param handshaker: A generator created by using one of the
|
||||
asynchronous handshake functions (i.e. handshakeServerAsync, or
|
||||
handshakeClientxxx(..., async=True).
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.handshaker = handshaker
|
||||
self._doHandshakeOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def setServerHandshakeOp(self, **args):
|
||||
"""Start a handshake operation.
|
||||
|
||||
The arguments passed to this function will be forwarded to
|
||||
L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}.
|
||||
"""
|
||||
handshaker = self.tlsConnection.handshakeServerAsync(**args)
|
||||
self.setHandshakeOp(handshaker)
|
||||
|
||||
def setCloseOp(self):
|
||||
"""Start a close operation.
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.closer = self.tlsConnection.closeAsync()
|
||||
self._doCloseOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def setWriteOp(self, writeBuffer):
|
||||
"""Start a write operation.
|
||||
|
||||
@type writeBuffer: str
|
||||
@param writeBuffer: The string to transmit.
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.writer = self.tlsConnection.writeAsync(writeBuffer)
|
||||
self._doWriteOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
163
python/gdata/tlslite/integration/ClientHelper.py
Normal file
163
python/gdata/tlslite/integration/ClientHelper.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
A helper class for using TLS Lite with stdlib clients
|
||||
(httplib, xmlrpclib, imaplib, poplib).
|
||||
"""
|
||||
|
||||
from gdata.tlslite.Checker import Checker
|
||||
|
||||
class ClientHelper:
|
||||
"""This is a helper class used to integrate TLS Lite with various
|
||||
TLS clients (e.g. poplib, smtplib, httplib, etc.)"""
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
"""
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Then you should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.sharedKey = None
|
||||
self.certChain = None
|
||||
self.privateKey = None
|
||||
self.checker = None
|
||||
|
||||
#SRP Authentication
|
||||
if username and password and not \
|
||||
(sharedKey or certChain or privateKey):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
#Shared Key Authentication
|
||||
elif username and sharedKey and not \
|
||||
(password or certChain or privateKey):
|
||||
self.username = username
|
||||
self.sharedKey = sharedKey
|
||||
|
||||
#Certificate Chain Authentication
|
||||
elif certChain and privateKey and not \
|
||||
(username or password or sharedKey):
|
||||
self.certChain = certChain
|
||||
self.privateKey = privateKey
|
||||
|
||||
#No Authentication
|
||||
elif not password and not username and not \
|
||||
sharedKey and not certChain and not privateKey:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise ValueError("Bad parameters")
|
||||
|
||||
#Authenticate the server based on its cryptoID or fingerprint
|
||||
if sharedKey and (cryptoID or protocol or x509Fingerprint):
|
||||
raise ValueError("Can't use shared keys with other forms of"\
|
||||
"authentication")
|
||||
|
||||
self.checker = Checker(cryptoID, protocol, x509Fingerprint,
|
||||
x509TrustList, x509CommonName)
|
||||
self.settings = settings
|
||||
|
||||
self.tlsSession = None
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
if self.username and self.password:
|
||||
tlsConnection.handshakeClientSRP(username=self.username,
|
||||
password=self.password,
|
||||
checker=self.checker,
|
||||
settings=self.settings,
|
||||
session=self.tlsSession)
|
||||
elif self.username and self.sharedKey:
|
||||
tlsConnection.handshakeClientSharedKey(username=self.username,
|
||||
sharedKey=self.sharedKey,
|
||||
settings=self.settings)
|
||||
else:
|
||||
tlsConnection.handshakeClientCert(certChain=self.certChain,
|
||||
privateKey=self.privateKey,
|
||||
checker=self.checker,
|
||||
settings=self.settings,
|
||||
session=self.tlsSession)
|
||||
self.tlsSession = tlsConnection.session
|
169
python/gdata/tlslite/integration/HTTPTLSConnection.py
Normal file
169
python/gdata/tlslite/integration/HTTPTLSConnection.py
Normal file
@@ -0,0 +1,169 @@
|
||||
"""TLS Lite + httplib."""
|
||||
|
||||
import socket
|
||||
import httplib
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
|
||||
class HTTPBaseTLSConnection(httplib.HTTPConnection):
|
||||
"""This abstract class provides a framework for adding TLS support
|
||||
to httplib."""
|
||||
|
||||
default_port = 443
|
||||
|
||||
def __init__(self, host, port=None, strict=None):
|
||||
if strict == None:
|
||||
#Python 2.2 doesn't support strict
|
||||
httplib.HTTPConnection.__init__(self, host, port)
|
||||
else:
|
||||
httplib.HTTPConnection.__init__(self, host, port, strict)
|
||||
|
||||
def connect(self):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if hasattr(sock, 'settimeout'):
|
||||
sock.settimeout(10)
|
||||
sock.connect((self.host, self.port))
|
||||
|
||||
#Use a TLSConnection to emulate a socket
|
||||
self.sock = TLSConnection(sock)
|
||||
|
||||
#When httplib closes this, close the socket
|
||||
self.sock.closeSocket = True
|
||||
self._handshake(self.sock)
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
"""Called to perform some sort of handshake.
|
||||
|
||||
This method must be overridden in a subclass to do some type of
|
||||
handshake. This method will be called after the socket has
|
||||
been connected but before any data has been sent. If this
|
||||
method does not raise an exception, the TLS connection will be
|
||||
considered valid.
|
||||
|
||||
This method may (or may not) be called every time an HTTP
|
||||
request is performed, depending on whether the underlying HTTP
|
||||
connection is persistent.
|
||||
|
||||
@type tlsConnection: L{tlslite.TLSConnection.TLSConnection}
|
||||
@param tlsConnection: The connection to perform the handshake
|
||||
on.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class HTTPTLSConnection(HTTPBaseTLSConnection, ClientHelper):
|
||||
"""This class extends L{HTTPBaseTLSConnection} to support the
|
||||
common types of handshaking."""
|
||||
|
||||
def __init__(self, host, port=None,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
"""Create a new HTTPTLSConnection.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Thus you should be prepared to handle TLS-specific
|
||||
exceptions when calling methods inherited from
|
||||
L{httplib.HTTPConnection} such as request(), connect(), and
|
||||
send(). See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
HTTPBaseTLSConnection.__init__(self, host, port)
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
ClientHelper._handshake(self, tlsConnection)
|
132
python/gdata/tlslite/integration/IMAP4_TLS.py
Normal file
132
python/gdata/tlslite/integration/IMAP4_TLS.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""TLS Lite + imaplib."""
|
||||
|
||||
import socket
|
||||
from imaplib import IMAP4
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
# IMAP TLS PORT
|
||||
IMAP4_TLS_PORT = 993
|
||||
|
||||
class IMAP4_TLS(IMAP4, ClientHelper):
|
||||
"""This class extends L{imaplib.IMAP4} with TLS support."""
|
||||
|
||||
def __init__(self, host = '', port = IMAP4_TLS_PORT,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new IMAP4_TLS.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
IMAP4.__init__(self, host, port)
|
||||
|
||||
|
||||
def open(self, host = '', port = IMAP4_TLS_PORT):
|
||||
"""Setup connection to remote server on "host:port".
|
||||
|
||||
This connection will be used by the routines:
|
||||
read, readline, send, shutdown.
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((host, port))
|
||||
self.sock = TLSConnection(self.sock)
|
||||
self.sock.closeSocket = True
|
||||
ClientHelper._handshake(self, self.sock)
|
||||
self.file = self.sock.makefile('rb')
|
52
python/gdata/tlslite/integration/IntegrationHelper.py
Normal file
52
python/gdata/tlslite/integration/IntegrationHelper.py
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
class IntegrationHelper:
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.sharedKey = None
|
||||
self.certChain = None
|
||||
self.privateKey = None
|
||||
self.checker = None
|
||||
|
||||
#SRP Authentication
|
||||
if username and password and not \
|
||||
(sharedKey or certChain or privateKey):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
#Shared Key Authentication
|
||||
elif username and sharedKey and not \
|
||||
(password or certChain or privateKey):
|
||||
self.username = username
|
||||
self.sharedKey = sharedKey
|
||||
|
||||
#Certificate Chain Authentication
|
||||
elif certChain and privateKey and not \
|
||||
(username or password or sharedKey):
|
||||
self.certChain = certChain
|
||||
self.privateKey = privateKey
|
||||
|
||||
#No Authentication
|
||||
elif not password and not username and not \
|
||||
sharedKey and not certChain and not privateKey:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise ValueError("Bad parameters")
|
||||
|
||||
#Authenticate the server based on its cryptoID or fingerprint
|
||||
if sharedKey and (cryptoID or protocol or x509Fingerprint):
|
||||
raise ValueError("Can't use shared keys with other forms of"\
|
||||
"authentication")
|
||||
|
||||
self.checker = Checker(cryptoID, protocol, x509Fingerprint,
|
||||
x509TrustList, x509CommonName)
|
||||
self.settings = settings
|
142
python/gdata/tlslite/integration/POP3_TLS.py
Normal file
142
python/gdata/tlslite/integration/POP3_TLS.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""TLS Lite + poplib."""
|
||||
|
||||
import socket
|
||||
from poplib import POP3
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
# POP TLS PORT
|
||||
POP3_TLS_PORT = 995
|
||||
|
||||
class POP3_TLS(POP3, ClientHelper):
|
||||
"""This class extends L{poplib.POP3} with TLS support."""
|
||||
|
||||
def __init__(self, host, port = POP3_TLS_PORT,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new POP3_TLS.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
self.host = host
|
||||
self.port = port
|
||||
msg = "getaddrinfo returns an empty list"
|
||||
self.sock = None
|
||||
for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
try:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
self.sock.connect(sa)
|
||||
except socket.error, msg:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
continue
|
||||
break
|
||||
if not self.sock:
|
||||
raise socket.error, msg
|
||||
|
||||
### New code below (all else copied from poplib)
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
self.sock = TLSConnection(self.sock)
|
||||
self.sock.closeSocket = True
|
||||
ClientHelper._handshake(self, self.sock)
|
||||
###
|
||||
|
||||
self.file = self.sock.makefile('rb')
|
||||
self._debugging = 0
|
||||
self.welcome = self._getresp()
|
114
python/gdata/tlslite/integration/SMTP_TLS.py
Normal file
114
python/gdata/tlslite/integration/SMTP_TLS.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""TLS Lite + smtplib."""
|
||||
|
||||
from smtplib import SMTP
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
class SMTP_TLS(SMTP):
|
||||
"""This class extends L{smtplib.SMTP} with TLS support."""
|
||||
|
||||
def starttls(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Puts the connection to the SMTP server into TLS mode.
|
||||
|
||||
If the server supports TLS, this will encrypt the rest of the SMTP
|
||||
session.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
(resp, reply) = self.docmd("STARTTLS")
|
||||
if resp == 220:
|
||||
helper = ClientHelper(
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
conn = TLSConnection(self.sock)
|
||||
conn.closeSocket = True
|
||||
helper._handshake(conn)
|
||||
self.sock = conn
|
||||
self.file = conn.makefile('rb')
|
||||
return (resp, reply)
|
139
python/gdata/tlslite/integration/TLSAsyncDispatcherMixIn.py
Normal file
139
python/gdata/tlslite/integration/TLSAsyncDispatcherMixIn.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""TLS Lite + asyncore."""
|
||||
|
||||
|
||||
import asyncore
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from AsyncStateMachine import AsyncStateMachine
|
||||
|
||||
|
||||
class TLSAsyncDispatcherMixIn(AsyncStateMachine):
|
||||
"""This class can be "mixed in" with an
|
||||
L{asyncore.dispatcher} to add TLS support.
|
||||
|
||||
This class essentially sits between the dispatcher and the select
|
||||
loop, intercepting events and only calling the dispatcher when
|
||||
applicable.
|
||||
|
||||
In the case of handle_read(), a read operation will be activated,
|
||||
and when it completes, the bytes will be placed in a buffer where
|
||||
the dispatcher can retrieve them by calling recv(), and the
|
||||
dispatcher's handle_read() will be called.
|
||||
|
||||
In the case of handle_write(), the dispatcher's handle_write() will
|
||||
be called, and when it calls send(), a write operation will be
|
||||
activated.
|
||||
|
||||
To use this class, you must combine it with an asyncore.dispatcher,
|
||||
and pass in a handshake operation with setServerHandshakeOp().
|
||||
|
||||
Below is an example of using this class with medusa. This class is
|
||||
mixed in with http_channel to create http_tls_channel. Note:
|
||||
1. the mix-in is listed first in the inheritance list
|
||||
|
||||
2. the input buffer size must be at least 16K, otherwise the
|
||||
dispatcher might not read all the bytes from the TLS layer,
|
||||
leaving some bytes in limbo.
|
||||
|
||||
3. IE seems to have a problem receiving a whole HTTP response in a
|
||||
single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't
|
||||
be displayed on IE.
|
||||
|
||||
Add the following text into 'start_medusa.py', in the 'HTTP Server'
|
||||
section::
|
||||
|
||||
from tlslite.api import *
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
class http_tls_channel(TLSAsyncDispatcherMixIn,
|
||||
http_server.http_channel):
|
||||
ac_in_buffer_size = 16384
|
||||
|
||||
def __init__ (self, server, conn, addr):
|
||||
http_server.http_channel.__init__(self, server, conn, addr)
|
||||
TLSAsyncDispatcherMixIn.__init__(self, conn)
|
||||
self.tlsConnection.ignoreAbruptClose = True
|
||||
self.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey)
|
||||
|
||||
hs.channel_class = http_tls_channel
|
||||
|
||||
If the TLS layer raises an exception, the exception will be caught
|
||||
in asyncore.dispatcher, which will call close() on this class. The
|
||||
TLS layer always closes the TLS connection before raising an
|
||||
exception, so the close operation will complete right away, causing
|
||||
asyncore.dispatcher.close() to be called, which closes the socket
|
||||
and removes this instance from the asyncore loop.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, sock=None):
|
||||
AsyncStateMachine.__init__(self)
|
||||
|
||||
if sock:
|
||||
self.tlsConnection = TLSConnection(sock)
|
||||
|
||||
#Calculate the sibling I'm being mixed in with.
|
||||
#This is necessary since we override functions
|
||||
#like readable(), handle_read(), etc., but we
|
||||
#also want to call the sibling's versions.
|
||||
for cl in self.__class__.__bases__:
|
||||
if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine:
|
||||
self.siblingClass = cl
|
||||
break
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
def readable(self):
|
||||
result = self.wantsReadEvent()
|
||||
if result != None:
|
||||
return result
|
||||
return self.siblingClass.readable(self)
|
||||
|
||||
def writable(self):
|
||||
result = self.wantsWriteEvent()
|
||||
if result != None:
|
||||
return result
|
||||
return self.siblingClass.writable(self)
|
||||
|
||||
def handle_read(self):
|
||||
self.inReadEvent()
|
||||
|
||||
def handle_write(self):
|
||||
self.inWriteEvent()
|
||||
|
||||
def outConnectEvent(self):
|
||||
self.siblingClass.handle_connect(self)
|
||||
|
||||
def outCloseEvent(self):
|
||||
asyncore.dispatcher.close(self)
|
||||
|
||||
def outReadEvent(self, readBuffer):
|
||||
self.readBuffer = readBuffer
|
||||
self.siblingClass.handle_read(self)
|
||||
|
||||
def outWriteEvent(self):
|
||||
self.siblingClass.handle_write(self)
|
||||
|
||||
def recv(self, bufferSize=16384):
|
||||
if bufferSize < 16384 or self.readBuffer == None:
|
||||
raise AssertionError()
|
||||
returnValue = self.readBuffer
|
||||
self.readBuffer = None
|
||||
return returnValue
|
||||
|
||||
def send(self, writeBuffer):
|
||||
self.setWriteOp(writeBuffer)
|
||||
return len(writeBuffer)
|
||||
|
||||
def close(self):
|
||||
if hasattr(self, "tlsConnection"):
|
||||
self.setCloseOp()
|
||||
else:
|
||||
asyncore.dispatcher.close(self)
|
59
python/gdata/tlslite/integration/TLSSocketServerMixIn.py
Normal file
59
python/gdata/tlslite/integration/TLSSocketServerMixIn.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""TLS Lite + SocketServer."""
|
||||
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
|
||||
class TLSSocketServerMixIn:
|
||||
"""
|
||||
This class can be mixed in with any L{SocketServer.TCPServer} to
|
||||
add TLS support.
|
||||
|
||||
To use this class, define a new class that inherits from it and
|
||||
some L{SocketServer.TCPServer} (with the mix-in first). Then
|
||||
implement the handshake() method, doing some sort of server
|
||||
handshake on the connection argument. If the handshake method
|
||||
returns True, the RequestHandler will be triggered. Below is a
|
||||
complete example of a threaded HTTPS server::
|
||||
|
||||
from SocketServer import *
|
||||
from BaseHTTPServer import *
|
||||
from SimpleHTTPServer import *
|
||||
from tlslite.api import *
|
||||
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
sessionCache = SessionCache()
|
||||
|
||||
class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn,
|
||||
HTTPServer):
|
||||
def handshake(self, tlsConnection):
|
||||
try:
|
||||
tlsConnection.handshakeServer(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
sessionCache=sessionCache)
|
||||
tlsConnection.ignoreAbruptClose = True
|
||||
return True
|
||||
except TLSError, error:
|
||||
print "Handshake failure:", str(error)
|
||||
return False
|
||||
|
||||
httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
||||
"""
|
||||
|
||||
|
||||
def finish_request(self, sock, client_address):
|
||||
tlsConnection = TLSConnection(sock)
|
||||
if self.handshake(tlsConnection) == True:
|
||||
self.RequestHandlerClass(tlsConnection, client_address, self)
|
||||
tlsConnection.close()
|
||||
|
||||
#Implement this method to do some form of handshaking. Return True
|
||||
#if the handshake finishes properly and the request is authorized.
|
||||
def handshake(self, tlsConnection):
|
||||
raise NotImplementedError()
|
196
python/gdata/tlslite/integration/TLSTwistedProtocolWrapper.py
Normal file
196
python/gdata/tlslite/integration/TLSTwistedProtocolWrapper.py
Normal file
@@ -0,0 +1,196 @@
|
||||
"""TLS Lite + Twisted."""
|
||||
|
||||
from twisted.protocols.policies import ProtocolWrapper, WrappingFactory
|
||||
from twisted.python.failure import Failure
|
||||
|
||||
from AsyncStateMachine import AsyncStateMachine
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.errors import *
|
||||
|
||||
import socket
|
||||
import errno
|
||||
|
||||
|
||||
#The TLSConnection is created around a "fake socket" that
|
||||
#plugs it into the underlying Twisted transport
|
||||
class _FakeSocket:
|
||||
def __init__(self, wrapper):
|
||||
self.wrapper = wrapper
|
||||
self.data = ""
|
||||
|
||||
def send(self, data):
|
||||
ProtocolWrapper.write(self.wrapper, data)
|
||||
return len(data)
|
||||
|
||||
def recv(self, numBytes):
|
||||
if self.data == "":
|
||||
raise socket.error, (errno.EWOULDBLOCK, "")
|
||||
returnData = self.data[:numBytes]
|
||||
self.data = self.data[numBytes:]
|
||||
return returnData
|
||||
|
||||
class TLSTwistedProtocolWrapper(ProtocolWrapper, AsyncStateMachine):
|
||||
"""This class can wrap Twisted protocols to add TLS support.
|
||||
|
||||
Below is a complete example of using TLS Lite with a Twisted echo
|
||||
server.
|
||||
|
||||
There are two server implementations below. Echo is the original
|
||||
protocol, which is oblivious to TLS. Echo1 subclasses Echo and
|
||||
negotiates TLS when the client connects. Echo2 subclasses Echo and
|
||||
negotiates TLS when the client sends "STARTTLS"::
|
||||
|
||||
from twisted.internet.protocol import Protocol, Factory
|
||||
from twisted.internet import reactor
|
||||
from twisted.protocols.policies import WrappingFactory
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
from twisted.python import log
|
||||
from twisted.python.failure import Failure
|
||||
import sys
|
||||
from tlslite.api import *
|
||||
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
verifierDB = VerifierDB("verifierDB")
|
||||
verifierDB.open()
|
||||
|
||||
class Echo(LineReceiver):
|
||||
def connectionMade(self):
|
||||
self.transport.write("Welcome to the echo server!\\r\\n")
|
||||
|
||||
def lineReceived(self, line):
|
||||
self.transport.write(line + "\\r\\n")
|
||||
|
||||
class Echo1(Echo):
|
||||
def connectionMade(self):
|
||||
if not self.transport.tlsStarted:
|
||||
self.transport.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
verifierDB=verifierDB)
|
||||
else:
|
||||
Echo.connectionMade(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
pass #Handle any TLS exceptions here
|
||||
|
||||
class Echo2(Echo):
|
||||
def lineReceived(self, data):
|
||||
if data == "STARTTLS":
|
||||
self.transport.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
verifierDB=verifierDB)
|
||||
else:
|
||||
Echo.lineReceived(self, data)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
pass #Handle any TLS exceptions here
|
||||
|
||||
factory = Factory()
|
||||
factory.protocol = Echo1
|
||||
#factory.protocol = Echo2
|
||||
|
||||
wrappingFactory = WrappingFactory(factory)
|
||||
wrappingFactory.protocol = TLSTwistedProtocolWrapper
|
||||
|
||||
log.startLogging(sys.stdout)
|
||||
reactor.listenTCP(1079, wrappingFactory)
|
||||
reactor.run()
|
||||
|
||||
This class works as follows:
|
||||
|
||||
Data comes in and is given to the AsyncStateMachine for handling.
|
||||
AsyncStateMachine will forward events to this class, and we'll
|
||||
pass them on to the ProtocolHandler, which will proxy them to the
|
||||
wrapped protocol. The wrapped protocol may then call back into
|
||||
this class, and these calls will be proxied into the
|
||||
AsyncStateMachine.
|
||||
|
||||
The call graph looks like this:
|
||||
- self.dataReceived
|
||||
- AsyncStateMachine.inReadEvent
|
||||
- self.out(Connect|Close|Read)Event
|
||||
- ProtocolWrapper.(connectionMade|loseConnection|dataReceived)
|
||||
- self.(loseConnection|write|writeSequence)
|
||||
- AsyncStateMachine.(setCloseOp|setWriteOp)
|
||||
"""
|
||||
|
||||
#WARNING: IF YOU COPY-AND-PASTE THE ABOVE CODE, BE SURE TO REMOVE
|
||||
#THE EXTRA ESCAPING AROUND "\\r\\n"
|
||||
|
||||
def __init__(self, factory, wrappedProtocol):
|
||||
ProtocolWrapper.__init__(self, factory, wrappedProtocol)
|
||||
AsyncStateMachine.__init__(self)
|
||||
self.fakeSocket = _FakeSocket(self)
|
||||
self.tlsConnection = TLSConnection(self.fakeSocket)
|
||||
self.tlsStarted = False
|
||||
self.connectionLostCalled = False
|
||||
|
||||
def connectionMade(self):
|
||||
try:
|
||||
ProtocolWrapper.connectionMade(self)
|
||||
except TLSError, e:
|
||||
self.connectionLost(Failure(e))
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def dataReceived(self, data):
|
||||
try:
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.dataReceived(self, data)
|
||||
else:
|
||||
self.fakeSocket.data += data
|
||||
while self.fakeSocket.data:
|
||||
AsyncStateMachine.inReadEvent(self)
|
||||
except TLSError, e:
|
||||
self.connectionLost(Failure(e))
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
if not self.connectionLostCalled:
|
||||
ProtocolWrapper.connectionLost(self, reason)
|
||||
self.connectionLostCalled = True
|
||||
|
||||
|
||||
def outConnectEvent(self):
|
||||
ProtocolWrapper.connectionMade(self)
|
||||
|
||||
def outCloseEvent(self):
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def outReadEvent(self, data):
|
||||
if data == "":
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
else:
|
||||
ProtocolWrapper.dataReceived(self, data)
|
||||
|
||||
|
||||
def setServerHandshakeOp(self, **args):
|
||||
self.tlsStarted = True
|
||||
AsyncStateMachine.setServerHandshakeOp(self, **args)
|
||||
|
||||
def loseConnection(self):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
else:
|
||||
AsyncStateMachine.setCloseOp(self)
|
||||
|
||||
def write(self, data):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.write(self, data)
|
||||
else:
|
||||
#Because of the FakeSocket, write operations are guaranteed to
|
||||
#terminate immediately.
|
||||
AsyncStateMachine.setWriteOp(self, data)
|
||||
|
||||
def writeSequence(self, seq):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.writeSequence(self, seq)
|
||||
else:
|
||||
#Because of the FakeSocket, write operations are guaranteed to
|
||||
#terminate immediately.
|
||||
AsyncStateMachine.setWriteOp(self, "".join(seq))
|
137
python/gdata/tlslite/integration/XMLRPCTransport.py
Normal file
137
python/gdata/tlslite/integration/XMLRPCTransport.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""TLS Lite + xmlrpclib."""
|
||||
|
||||
import xmlrpclib
|
||||
import httplib
|
||||
from gdata.tlslite.integration.HTTPTLSConnection import HTTPTLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
|
||||
class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
|
||||
"""Handles an HTTPS transaction to an XML-RPC server."""
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new XMLRPCTransport.
|
||||
|
||||
An instance of this class can be passed to L{xmlrpclib.ServerProxy}
|
||||
to use TLS with XML-RPC calls::
|
||||
|
||||
from tlslite.api import XMLRPCTransport
|
||||
from xmlrpclib import ServerProxy
|
||||
|
||||
transport = XMLRPCTransport(user="alice", password="abra123")
|
||||
server = ServerProxy("https://localhost", transport)
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Thus you should be prepared to handle TLS-specific
|
||||
exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the
|
||||
client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
|
||||
def make_connection(self, host):
|
||||
# create a HTTPS connection object from a host descriptor
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
http = HTTPTLSConnection(host, None,
|
||||
self.username, self.password,
|
||||
self.sharedKey,
|
||||
self.certChain, self.privateKey,
|
||||
self.checker.cryptoID,
|
||||
self.checker.protocol,
|
||||
self.checker.x509Fingerprint,
|
||||
self.checker.x509TrustList,
|
||||
self.checker.x509CommonName,
|
||||
self.settings)
|
||||
http2 = httplib.HTTP()
|
||||
http2._setup(http)
|
||||
return http2
|
17
python/gdata/tlslite/integration/__init__.py
Normal file
17
python/gdata/tlslite/integration/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Classes for integrating TLS Lite with other packages."""
|
||||
|
||||
__all__ = ["AsyncStateMachine",
|
||||
"HTTPTLSConnection",
|
||||
"POP3_TLS",
|
||||
"IMAP4_TLS",
|
||||
"SMTP_TLS",
|
||||
"XMLRPCTransport",
|
||||
"TLSSocketServerMixIn",
|
||||
"TLSAsyncDispatcherMixIn",
|
||||
"TLSTwistedProtocolWrapper"]
|
||||
|
||||
try:
|
||||
import twisted
|
||||
del twisted
|
||||
except ImportError:
|
||||
del __all__[__all__.index("TLSTwistedProtocolWrapper")]
|
Reference in New Issue
Block a user