TxSNI-0.1.9/0000755000076500000240000000000013111435705012734 5ustar glyphstaff00000000000000TxSNI-0.1.9/PKG-INFO0000644000076500000240000000303613111435705014033 0ustar glyphstaff00000000000000Metadata-Version: 1.1 Name: TxSNI Version: 0.1.9 Summary: easy-to-use SNI endpoint for twisted Home-page: https://github.com/glyph/txsni Author: UNKNOWN Author-email: UNKNOWN License: MIT Description: txsni ===== .. image:: https://travis-ci.org/glyph/txsni.svg?branch=master :target: https://travis-ci.org/glyph/txsni Simple support for running a TLS server with Twisted. Use it like this: .. code-block:: console $ mkdir certificates $ cat private-stuff/mydomain.key.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/mydomain.crt.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/my-certificate-authority-chain.crt.pem >> \ certificates/mydomain.example.com.pem $ twistd -n web --port txsni:certificates:tcp:443 Enjoy! Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Security :: Cryptography TxSNI-0.1.9/README.rst0000644000076500000240000000110513111433640014414 0ustar glyphstaff00000000000000txsni ===== .. image:: https://travis-ci.org/glyph/txsni.svg?branch=master :target: https://travis-ci.org/glyph/txsni Simple support for running a TLS server with Twisted. Use it like this: .. code-block:: console $ mkdir certificates $ cat private-stuff/mydomain.key.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/mydomain.crt.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/my-certificate-authority-chain.crt.pem >> \ certificates/mydomain.example.com.pem $ twistd -n web --port txsni:certificates:tcp:443 Enjoy! TxSNI-0.1.9/setup.cfg0000644000076500000240000000007513111435705014557 0ustar glyphstaff00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 TxSNI-0.1.9/setup.py0000644000076500000240000000224013111435570014444 0ustar glyphstaff00000000000000 import os from setuptools import setup base_dir = os.path.dirname(__file__) with open(os.path.join(base_dir, "README.rst")) as f: long_description = f.read() setup( name="TxSNI", description="easy-to-use SNI endpoint for twisted", packages=[ "txsni", "txsni.test", "txsni.test.certs", "twisted.plugins", ], install_requires=[ "Twisted[tls]>=14.0", "pyOpenSSL>=0.14", ], version="0.1.9", long_description=long_description, license="MIT", url="https://github.com/glyph/txsni", classifiers=[ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Security :: Cryptography", ], ) TxSNI-0.1.9/twisted/0000755000076500000240000000000013111435705014417 5ustar glyphstaff00000000000000TxSNI-0.1.9/twisted/plugins/0000755000076500000240000000000013111435705016100 5ustar glyphstaff00000000000000TxSNI-0.1.9/twisted/plugins/txsni_endpoint.py0000644000076500000240000000011713111433640021512 0ustar glyphstaff00000000000000 from txsni.parser import SNIDirectoryParser dirParser = SNIDirectoryParser() TxSNI-0.1.9/txsni/0000755000076500000240000000000013111435705014101 5ustar glyphstaff00000000000000TxSNI-0.1.9/txsni/__init__.py0000644000076500000240000000005213111433640016203 0ustar glyphstaff00000000000000 """ SNI support for Twisted servers. """ TxSNI-0.1.9/txsni/maputils.py0000644000076500000240000000050613111433640016306 0ustar glyphstaff00000000000000 class Cache(object): def __init__(self, mapping): self.mapping = mapping self.cache = {} def __getitem__(self, key): if key in self.cache: return self.cache[key] else: value = self.mapping[key] self.cache[key] = value return value TxSNI-0.1.9/txsni/only_noticed_pypi_pem_after_i_wrote_this.py0000644000076500000240000000327113111434723025005 0ustar glyphstaff00000000000000 from OpenSSL.SSL import FILETYPE_PEM from twisted.internet.ssl import Certificate, KeyPair, CertificateOptions from collections import namedtuple PEMObjects = namedtuple('PEMObjects', ['certificates', 'keys']) def objectsFromPEM(pemdata): """ Load some objects from a PEM. """ certificates = [] keys = [] for line in pemdata.split(b"\n"): if line.startswith(b'-----BEGIN'): if b'CERTIFICATE' in line: blobs = certificates else: blobs = keys blobs.append(b'') blobs[-1] += line blobs[-1] += b'\n' keys = [KeyPair.load(key, FILETYPE_PEM) for key in keys] certificates = [Certificate.loadPEM(certificate) for certificate in certificates] return PEMObjects(keys=keys, certificates=certificates) def certificateOptionsFromPileOfPEM(pemdata): objects = objectsFromPEM(pemdata) if len(objects.keys) != 1: raise ValueError("Expected 1 private key, found %d" % tuple([len(objects.keys)])) privateKey = objects.keys[0] certificatesByFingerprint = dict( [(certificate.getPublicKey().keyHash(), certificate) for certificate in objects.certificates] ) if privateKey.keyHash() not in certificatesByFingerprint: raise ValueError("No certificate matching %s found") openSSLCert = certificatesByFingerprint.pop(privateKey.keyHash()).original openSSLKey = privateKey.original openSSLChain = [c.original for c in certificatesByFingerprint.values()] return CertificateOptions(certificate=openSSLCert, privateKey=openSSLKey, extraCertChain=openSSLChain) TxSNI-0.1.9/txsni/parser.py0000644000076500000240000000206313111433640015744 0ustar glyphstaff00000000000000 from os.path import expanduser from zope.interface import implementer from twisted.internet.interfaces import IStreamServerEndpointStringParser from twisted.internet.endpoints import serverFromString from twisted.plugin import IPlugin from txsni.snimap import SNIMap from txsni.maputils import Cache from txsni.snimap import HostDirectoryMap from twisted.python.filepath import FilePath from txsni.tlsendpoint import TLSEndpoint @implementer(IStreamServerEndpointStringParser, IPlugin) class SNIDirectoryParser(object): prefix = 'txsni' def parseStreamServer(self, reactor, pemdir, *args, **kw): def colonJoin(items): return ':'.join([item.replace(':', '\\:') for item in items]) sub = colonJoin(list(args) + ['='.join(item) for item in kw.items()]) subEndpoint = serverFromString(reactor, sub) contextFactory = SNIMap( Cache(HostDirectoryMap(FilePath(expanduser(pemdir)))) ) return TLSEndpoint(endpoint=subEndpoint, contextFactory=contextFactory) TxSNI-0.1.9/txsni/snimap.py0000644000076500000240000001325013111434145015740 0ustar glyphstaff00000000000000import collections from zope.interface import implementer from OpenSSL.SSL import Connection from twisted.internet.interfaces import IOpenSSLServerConnectionCreator from twisted.internet.ssl import CertificateOptions, ContextFactory from txsni.only_noticed_pypi_pem_after_i_wrote_this import ( certificateOptionsFromPileOfPEM ) class _NegotiationData(object): """ A container for the negotiation data. """ __slots__ = [ 'npnAdvertiseCallback', 'npnSelectCallback', 'alpnSelectCallback', 'alpnProtocols' ] def __init__(self): self.npnAdvertiseCallback = None self.npnSelectCallback = None self.alpnSelectCallback = None self.alpnProtocols = None def negotiateNPN(self, context): if self.npnAdvertiseCallback is None or self.npnSelectCallback is None: return context.set_npn_advertise_callback(self.npnAdvertiseCallback) context.set_npn_select_callback(self.npnSelectCallback) def negotiateALPN(self, context): if self.alpnSelectCallback is None or self.alpnProtocols is None: return context.set_alpn_select_callback(self.alpnSelectCallback) context.set_alpn_protos(self.alpnProtocols) class _ConnectionProxy(object): """ A basic proxy for an OpenSSL Connection object that returns a ContextProxy wrapping the actual OpenSSL Context whenever it's asked for. """ def __init__(self, original, factory): self._obj = original self._factory = factory def get_context(self): """ A basic override of get_context to ensure that the appropriate proxy object is returned. """ ctx = self._obj.get_context() return _ContextProxy(ctx, self._factory) def __getattr__(self, attr): return getattr(self._obj, attr) def __setattr__(self, attr, val): if attr in ('_obj', '_factory'): self.__dict__[attr] = val return setattr(self._obj, attr, val) def __delattr__(self, attr): return delattr(self._obj, attr) class _ContextProxy(object): """ A basic proxy object for the OpenSSL Context object that records the values of the NPN/ALPN callbacks, to ensure that they get set appropriately if a context is swapped out during connection setup. """ def __init__(self, original, factory): self._obj = original self._factory = factory def set_npn_advertise_callback(self, cb): self._factory._npnAdvertiseCallbackForContext(self._obj, cb) return self._obj.set_npn_advertise_callback(cb) def set_npn_select_callback(self, cb): self._factory._npnSelectCallbackForContext(self._obj, cb) return self._obj.set_npn_select_callback(cb) def set_alpn_select_callback(self, cb): self._factory._alpnSelectCallbackForContext(self._obj, cb) return self._obj.set_alpn_select_callback(cb) def set_alpn_protos(self, protocols): self._factory._alpnProtocolsForContext(self._obj, protocols) return self._obj.set_alpn_protos(protocols) def __getattr__(self, attr): return getattr(self._obj, attr) def __setattr__(self, attr, val): if attr in ('_obj', '_factory'): self.__dict__[attr] = val return setattr(self._obj, attr, val) def __delattr__(self, attr): return delattr(self._obj, attr) @implementer(IOpenSSLServerConnectionCreator) class SNIMap(object): def __init__(self, mapping): self.mapping = mapping self._negotiationDataForContext = collections.defaultdict( _NegotiationData ) try: self.context = self.mapping['DEFAULT'].getContext() except KeyError: self.context = CertificateOptions().getContext() self.context.set_tlsext_servername_callback( self.selectContext ) def selectContext(self, connection): oldContext = connection.get_context() newContext = self.mapping[connection.get_servername()].getContext() negotiationData = self._negotiationDataForContext[oldContext] negotiationData.negotiateNPN(newContext) negotiationData.negotiateALPN(newContext) connection.set_context(newContext) def serverConnectionForTLS(self, protocol): """ Construct an OpenSSL server connection. @param protocol: The protocol initiating a TLS connection. @type protocol: L{TLSMemoryBIOProtocol} @return: a connection @rtype: L{OpenSSL.SSL.Connection} """ conn = Connection(self.context, None) return _ConnectionProxy(conn, self) def _npnAdvertiseCallbackForContext(self, context, callback): self._negotiationDataForContext[context].npnAdvertiseCallback = ( callback ) def _npnSelectCallbackForContext(self, context, callback): self._negotiationDataForContext[context].npnSelectCallback = callback def _alpnSelectCallbackForContext(self, context, callback): self._negotiationDataForContext[context].alpnSelectCallback = callback def _alpnProtocolsForContext(self, context, protocols): self._negotiationDataForContext[context].alpnProtocols = protocols class HostDirectoryMap(object): def __init__(self, directoryPath): self.directoryPath = directoryPath def __getitem__(self, hostname): if hostname is None: hostname = "DEFAULT" filePath = self.directoryPath.child(hostname).siblingExtension(".pem") if filePath.isfile(): return certificateOptionsFromPileOfPEM(filePath.getContent()) else: raise KeyError("no pem file for " + hostname) TxSNI-0.1.9/txsni/test/0000755000076500000240000000000013111435705015060 5ustar glyphstaff00000000000000TxSNI-0.1.9/txsni/test/__init__.py0000644000076500000240000000000013111433640017153 0ustar glyphstaff00000000000000TxSNI-0.1.9/txsni/test/certs/0000755000076500000240000000000013111435705016200 5ustar glyphstaff00000000000000TxSNI-0.1.9/txsni/test/certs/__init__.py0000644000076500000240000000000013111433640020273 0ustar glyphstaff00000000000000TxSNI-0.1.9/txsni/test/certs/cert_builder.py0000644000076500000240000001304313111433640021212 0ustar glyphstaff00000000000000from __future__ import print_function, absolute_import from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID import datetime import uuid import os import tempfile ONE_DAY = datetime.timedelta(1, 0, 0) THIRTYISH_YEARS = datetime.timedelta(30 * 365, 0, 0) TENISH_YEARS = datetime.timedelta(10 * 365, 0, 0) # Various exportable constants that the tests can (and should!) use. CERT_DIR = tempfile.mkdtemp() ROOT_CERT_PATH = os.path.join(CERT_DIR, 'root_cert.pem') ROOT_KEY_PATH = os.path.join(CERT_DIR, 'root_cert.key') DEFAULT_CERT_PATH = os.path.join(CERT_DIR, 'DEFAULT.pem') DEFAULT_KEY_PATH = os.path.join(CERT_DIR, 'DEFAULT.key') HTTP2BIN_CERT_PATH = os.path.join(CERT_DIR, 'http2bin.org.pem') HTTP2BIN_KEY_PATH = os.path.join(CERT_DIR, 'http2bin.org.key') # A list of tuples that controls what certs get built and signed by the root. # Each tuple is (hostname, cert_path) # We'll probably never need the easy extensibility this provides, but hey, nvm! _CERTS = [ (u'localhost', DEFAULT_CERT_PATH), (u'http2bin.org', HTTP2BIN_CERT_PATH), ] def _build_root_cert(): """ Builds a single root certificate that can be used to sign the others. This root cert is basically pretty legit, except for being totally bonkers. Returns a tuple of (certificate, key) for the CA, which can be used to build the leaves. """ if os.path.isfile(ROOT_CERT_PATH) and os.path.isfile(ROOT_KEY_PATH): print("Root already exists, not regenerating.") with open(ROOT_CERT_PATH, 'rb') as f: certificate = x509.load_pem_x509_certificate( f.read(), default_backend() ) with open(ROOT_KEY_PATH, 'rb') as f: key = serialization.load_pem_private_key( f.read(), password=None, backend=default_backend() ) return certificate, key private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) public_key = private_key.public_key() builder = x509.CertificateBuilder() builder = builder.subject_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u'txsni signing service'), ])) builder = builder.issuer_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u'txsni signing service'), ])) builder = builder.not_valid_before(datetime.datetime.today() - ONE_DAY) builder = builder.not_valid_after( datetime.datetime.today() + THIRTYISH_YEARS ) builder = builder.serial_number(int(uuid.uuid4())) builder = builder.public_key(public_key) # Don't allow intermediates. builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=0), critical=True, ) certificate = builder.sign( private_key=private_key, algorithm=hashes.SHA256(), backend=default_backend() ) # Write it out. with open(ROOT_KEY_PATH, 'wb') as f: f.write( private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption() ) ) with open(ROOT_CERT_PATH, 'wb') as f: f.write( certificate.public_bytes(serialization.Encoding.PEM) ) print("Built root certificate.") return certificate, private_key def _build_single_leaf(hostname, certfile, ca_cert, ca_key): """ Builds a single leaf certificate, signed by the CA's private key. """ if os.path.isfile(certfile): print("%s already exists, not regenerating" % hostname) return private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) public_key = private_key.public_key() builder = x509.CertificateBuilder() builder = builder.subject_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, hostname), ])) builder = builder.issuer_name(ca_cert.subject) builder = builder.not_valid_before(datetime.datetime.today() - ONE_DAY) builder = builder.not_valid_after( datetime.datetime.today() + TENISH_YEARS ) builder = builder.serial_number(int(uuid.uuid4())) builder = builder.public_key(public_key) builder = builder.add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, ) builder = builder.add_extension( x509.SubjectAlternativeName([ x509.DNSName(hostname) ]), critical=True, ) certificate = builder.sign( private_key=ca_key, algorithm=hashes.SHA256(), backend=default_backend() ) # Write it out. with open(certfile, 'wb') as f: f.write( private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption() ) ) f.write( certificate.public_bytes(serialization.Encoding.PEM) ) print("Built certificate for %s" % hostname) def _build_certs(): """ Builds all certificates. """ ca_cert, ca_key = _build_root_cert() for hostname, certfile in _CERTS: _build_single_leaf(hostname, certfile, ca_cert, ca_key) if __name__ == '__main__': _build_certs() TxSNI-0.1.9/txsni/test/test_txsni.py0000644000076500000240000001725113111435452017643 0ustar glyphstaff00000000000000from __future__ import absolute_import import os from txsni.snimap import SNIMap, HostDirectoryMap from txsni.tlsendpoint import TLSEndpoint from OpenSSL.crypto import load_certificate, FILETYPE_PEM from twisted.internet import protocol, endpoints, reactor, defer, interfaces from twisted.internet.ssl import ( CertificateOptions, optionsForClientTLS, Certificate ) from twisted.python.filepath import FilePath from twisted.trial import unittest from zope.interface import implementer from .certs.cert_builder import ( ROOT_CERT_PATH, HTTP2BIN_CERT_PATH, _build_certs, CERT_DIR ) # We need some temporary certs. _build_certs() with open(ROOT_CERT_PATH, 'rb') as f: PEM_ROOT = Certificate.loadPEM(f.read()) def sni_endpoint(): """ Builds a TxSNI TLSEndpoint populated with the default certificates. These are built from cert_builder.py, and have the following certs in the SNI map: - DEFAULT.pem, which contains a SAN for 'localhost'. - http2bin.org.pem, which contains a SAN for 'http2bin.org' """ base_endpoint = endpoints.TCP4ServerEndpoint( reactor=reactor, port=0, interface='127.0.0.1', ) path = FilePath(CERT_DIR) mapping = SNIMap(HostDirectoryMap(path)) wrapper_endpoint = TLSEndpoint(base_endpoint, mapping) return wrapper_endpoint def handshake(client_factory, server_factory, hostname, server_endpoint): """ Connect a basic Twisted TLS client endpoint to the provided TxSNI TLSEndpoint. Returns a Deferred that fires when the connection has been established with a tuple of an instance of the client protocol and the listening port. """ def connect_client(listening_port): port_number = listening_port.getHost().port client = endpoints.TCP4ClientEndpoint( reactor, '127.0.0.1', port_number ) options = optionsForClientTLS( hostname=hostname, trustRoot=PEM_ROOT ) client = endpoints.wrapClientTLS(options, client) connectDeferred = client.connect(client_factory) def aggregate(client_proto): return (client_proto, listening_port) connectDeferred.addCallback(aggregate) return connectDeferred listenDeferred = server_endpoint.listen(server_factory) listenDeferred.addCallback(connect_client) return listenDeferred class WritingProtocol(protocol.Protocol): """ A really basic Twisted protocol that fires a Deferred when the TLS handshake has been completed. It detects this using dataReceived, because we can't rely on IHandshakeListener. """ def __init__(self, handshake_deferred): self.handshake_deferred = handshake_deferred def dataReceived(self, data): cert = self.transport.getPeerCertificate() if not skipNegotiation: proto = self.transport.negotiatedProtocol else: proto = None self.transport.abortConnection() self.handshake_deferred.callback((cert, proto)) self.handshake_deferred = None class WritingProtocolFactory(protocol.Factory): protocol = WritingProtocol def __init__(self, handshake_deferred): self.handshake_deferred = handshake_deferred def buildProtocol(self, addr): p = self.protocol(self.handshake_deferred) p.factory = self return p class WriteBackProtocol(protocol.Protocol): """ A really basic Twisted protocol that just writes some data to the connection. """ def connectionMade(self): self.transport.write(b'PING') self.transport.loseConnection() try: @implementer(interfaces.IProtocolNegotiationFactory) class NegotiatingFactory(protocol.Factory): """ A Twisted Protocol Factory that implements the protocol negotiation extensions """ def acceptableProtocols(self): return [b'h2', b'http/1.1'] class WritingNegotiatingFactory(WritingProtocolFactory, NegotiatingFactory): pass skipNegotiation = False except AttributeError: skipNegotiation = "IProtocolNegotiationFactory not supported" class TestSNIMap(unittest.TestCase): """ Tests of the basic SNIMap logic. """ def test_snimap_default(self): """ SNIMap preferentially loads the DEFAULT value from the mapping if it's present. """ options = CertificateOptions() mapping = {'DEFAULT': options} sni_map = SNIMap(mapping) conn = sni_map.serverConnectionForTLS(protocol.Protocol()) self.assertIs(conn.get_context()._obj, options.getContext()) def test_snimap_makes_its_own_defaults(self): """ If passed a mapping without a DEFAULT key, SNIMap will make its own default context. """ options = CertificateOptions() mapping = {'example.com': options} sni_map = SNIMap(mapping) conn = sni_map.serverConnectionForTLS(protocol.Protocol()) self.assertIsNot(conn.get_context(), options.getContext()) self.assertIsNotNone(conn.get_context()) class TestCommunication(unittest.TestCase): """ Tests that use the full Twisted logic to validate that txsni works as expected. """ def assertCertIs(self, protocol_cert, cert_path): """ Assert that ``protocol_cert`` is the same certificate as the one at ``cert_path``. """ with open(cert_path, 'rb') as f: target_cert = load_certificate(FILETYPE_PEM, f.read()) self.assertEqual( protocol_cert.digest('sha256'), target_cert.digest('sha256') ) def test_specific_certificate(self): """ When a hostname TxSNI does know about, in this case 'http2bin.org', is provided, TxSNI returns the specific certificate. """ handshake_deferred = defer.Deferred() client_factory = WritingProtocolFactory(handshake_deferred) server_factory = protocol.Factory.forProtocol(WriteBackProtocol) endpoint = sni_endpoint() d = handshake( client_factory=client_factory, server_factory=server_factory, hostname=u'http2bin.org', server_endpoint=endpoint, ) def confirm_cert(args): cert, proto = args self.assertCertIs(cert, HTTP2BIN_CERT_PATH) return d def close(args): client, port = args port.stopListening() handshake_deferred.addCallback(confirm_cert) handshake_deferred.addCallback(close) return handshake_deferred class TestNegotiationStillWorks(unittest.TestCase): """ Tests that TxSNI doesn't break protocol negotiation. """ if skipNegotiation: skip = skipNegotiation def test_specific_cert_still_negotiates(self): """ When TxSNI selects a specific cert, protocol negotiation still works. """ handshake_deferred = defer.Deferred() client_factory = WritingNegotiatingFactory(handshake_deferred) server_factory = NegotiatingFactory.forProtocol( WriteBackProtocol ) endpoint = sni_endpoint() d = handshake( client_factory=client_factory, server_factory=server_factory, hostname=u'http2bin.org', server_endpoint=endpoint, ) def confirm_cert(args): cert, proto = args self.assertEqual(proto, b'h2') return d def close(args): client, port = args port.stopListening() handshake_deferred.addCallback(confirm_cert) handshake_deferred.addCallback(close) return handshake_deferred TxSNI-0.1.9/txsni/tlsendpoint.py0000644000076500000240000000055013111433640017012 0ustar glyphstaff00000000000000from twisted.protocols.tls import TLSMemoryBIOFactory class TLSEndpoint(object): def __init__(self, endpoint, contextFactory): self.endpoint = endpoint self.contextFactory = contextFactory def listen(self, factory): return self.endpoint.listen(TLSMemoryBIOFactory( self.contextFactory, False, factory )) TxSNI-0.1.9/TxSNI.egg-info/0000755000076500000240000000000013111435705015373 5ustar glyphstaff00000000000000TxSNI-0.1.9/TxSNI.egg-info/dependency_links.txt0000644000076500000240000000000113111435705021441 0ustar glyphstaff00000000000000 TxSNI-0.1.9/TxSNI.egg-info/PKG-INFO0000644000076500000240000000303613111435705016472 0ustar glyphstaff00000000000000Metadata-Version: 1.1 Name: TxSNI Version: 0.1.9 Summary: easy-to-use SNI endpoint for twisted Home-page: https://github.com/glyph/txsni Author: UNKNOWN Author-email: UNKNOWN License: MIT Description: txsni ===== .. image:: https://travis-ci.org/glyph/txsni.svg?branch=master :target: https://travis-ci.org/glyph/txsni Simple support for running a TLS server with Twisted. Use it like this: .. code-block:: console $ mkdir certificates $ cat private-stuff/mydomain.key.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/mydomain.crt.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/my-certificate-authority-chain.crt.pem >> \ certificates/mydomain.example.com.pem $ twistd -n web --port txsni:certificates:tcp:443 Enjoy! Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Security :: Cryptography TxSNI-0.1.9/TxSNI.egg-info/requires.txt0000644000076500000240000000004313111435705017770 0ustar glyphstaff00000000000000Twisted[tls]>=14.0 pyOpenSSL>=0.14 TxSNI-0.1.9/TxSNI.egg-info/SOURCES.txt0000644000076500000240000000071013111435705017255 0ustar glyphstaff00000000000000README.rst setup.cfg setup.py TxSNI.egg-info/PKG-INFO TxSNI.egg-info/SOURCES.txt TxSNI.egg-info/dependency_links.txt TxSNI.egg-info/requires.txt TxSNI.egg-info/top_level.txt twisted/plugins/txsni_endpoint.py txsni/__init__.py txsni/maputils.py txsni/only_noticed_pypi_pem_after_i_wrote_this.py txsni/parser.py txsni/snimap.py txsni/tlsendpoint.py txsni/test/__init__.py txsni/test/test_txsni.py txsni/test/certs/__init__.py txsni/test/certs/cert_builder.pyTxSNI-0.1.9/TxSNI.egg-info/top_level.txt0000644000076500000240000000001613111435705020122 0ustar glyphstaff00000000000000twisted txsni