Esempio n. 1
0
class PortStringificationTests(unittest.TestCase):
    if interfaces.IReactorTCP(reactor, None) is not None:
        def testTCP(self):
            p = reactor.listenTCP(0, protocol.ServerFactory())
            portNo = p.getHost().port
            self.assertNotEqual(str(p).find(str(portNo)), -1,
                                "%d not found in %s" % (portNo, p))
            return p.stopListening()

    if interfaces.IReactorUDP(reactor, None) is not None:
        def testUDP(self):
            p = reactor.listenUDP(0, protocol.DatagramProtocol())
            portNo = p.getHost().port
            self.assertNotEqual(str(p).find(str(portNo)), -1,
                                "%d not found in %s" % (portNo, p))
            return p.stopListening()

    if interfaces.IReactorSSL(reactor, None) is not None and ssl:
        def testSSL(self, ssl=ssl):
            pem = util.sibpath(__file__, 'server.pem')
            p = reactor.listenSSL(0, protocol.ServerFactory(), ssl.DefaultOpenSSLContextFactory(pem, pem))
            portNo = p.getHost().port
            self.assertNotEqual(str(p).find(str(portNo)), -1,
                                "%d not found in %s" % (portNo, p))
            return p.stopListening()

        if _PY3:
            testSSL.skip = ("Re-enable once the Python 3 SSL port is done.")
Esempio n. 2
0
class PortStringificationTests(TestCase):
    @skipIf(not interfaces.IReactorTCP(reactor, None), "IReactorTCP is needed")
    def testTCP(self):
        p = reactor.listenTCP(0, protocol.ServerFactory())
        portNo = p.getHost().port
        self.assertNotEqual(
            str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
        )
        return p.stopListening()

    @skipIf(not interfaces.IReactorUDP(reactor, None), "IReactorUDP is needed")
    def testUDP(self):
        p = reactor.listenUDP(0, protocol.DatagramProtocol())
        portNo = p.getHost().port
        self.assertNotEqual(
            str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
        )
        return p.stopListening()

    @skipIf(not interfaces.IReactorSSL(reactor, None), "IReactorSSL is needed")
    @skipIf(not ssl, "SSL support is missing")
    def testSSL(self, ssl=ssl):
        pem = util.sibpath(__file__, "server.pem")
        p = reactor.listenSSL(
            0, protocol.ServerFactory(), ssl.DefaultOpenSSLContextFactory(pem, pem)
        )
        portNo = p.getHost().port
        self.assertNotEqual(
            str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
        )
        return p.stopListening()
class SpammyTLSTests(TLSTests):
    """
    Test TLS features with bytes sitting in the out buffer.
    """
    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    fillBuffer = True
class StolenTCPTests(ProperlyCloseFilesMixin, TestCase):
    """
    For SSL transports, test many of the same things which are tested for
    TCP transports.
    """
    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    def createServer(self, address, portNumber, factory):
        """
        Create an SSL server with a certificate using L{IReactorSSL.listenSSL}.
        """
        cert = ssl.PrivateCertificate.loadPEM(FilePath(certPath).getContent())
        contextFactory = cert.options()
        return reactor.listenSSL(
            portNumber, factory, contextFactory, interface=address)


    def connectClient(self, address, portNumber, clientCreator):
        """
        Create an SSL client using L{IReactorSSL.connectSSL}.
        """
        contextFactory = ssl.CertificateOptions()
        return clientCreator.connectSSL(address, portNumber, contextFactory)


    def getHandleExceptionType(self):
        """
        Return L{OpenSSL.SSL.Error} as the expected error type which will be
        raised by a write to the L{OpenSSL.SSL.Connection} object after it has
        been closed.
        """
        return SSL.Error


    def getHandleErrorCodeMatcher(self):
        """
        Return a L{hamcrest.core.matcher.Matcher} for the argument
        L{OpenSSL.SSL.Error} will be constructed with for this case.
        This is basically just a random OpenSSL implementation detail.
        It would be better if this test worked in a way which did not
        require this.
        """
        # We expect an error about how we tried to write to a shutdown
        # connection.  This is terribly implementation-specific.
        return hamcrest.contains(
            hamcrest.contains(
                hamcrest.equal_to('SSL routines'),
                hamcrest.any_of(
                    hamcrest.equal_to('SSL_write'),
                    hamcrest.equal_to('ssl_write_internal'),
                ),
                hamcrest.equal_to('protocol is shutdown'),
            ),
        )
class DefaultOpenSSLContextFactoryTests(TestCase):
    """
    Tests for L{ssl.DefaultOpenSSLContextFactory}.
    """
    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    def setUp(self):
        # pyOpenSSL Context objects aren't introspectable enough.  Pass in
        # an alternate context factory so we can inspect what is done to it.
        self.contextFactory = ssl.DefaultOpenSSLContextFactory(
            certPath, certPath, _contextFactory=FakeContext)
        self.context = self.contextFactory.getContext()


    def test_method(self):
        """
        L{ssl.DefaultOpenSSLContextFactory.getContext} returns an SSL context
        which can use SSLv3 or TLSv1 but not SSLv2.
        """
        # SSLv23_METHOD allows SSLv2, SSLv3, or TLSv1
        self.assertEqual(self.context._method, SSL.SSLv23_METHOD)

        # And OP_NO_SSLv2 disables the SSLv2 support.
        self.assertEqual(self.context._options & SSL.OP_NO_SSLv2,
                         SSL.OP_NO_SSLv2)

        # Make sure SSLv3 and TLSv1 aren't disabled though.
        self.assertFalse(self.context._options & SSL.OP_NO_SSLv3)
        self.assertFalse(self.context._options & SSL.OP_NO_TLSv1)


    def test_missingCertificateFile(self):
        """
        Instantiating L{ssl.DefaultOpenSSLContextFactory} with a certificate
        filename which does not identify an existing file results in the
        initializer raising L{OpenSSL.SSL.Error}.
        """
        self.assertRaises(
            SSL.Error,
            ssl.DefaultOpenSSLContextFactory, certPath, self.mktemp())


    def test_missingPrivateKeyFile(self):
        """
        Instantiating L{ssl.DefaultOpenSSLContextFactory} with a private key
        filename which does not identify an existing file results in the
        initializer raising L{OpenSSL.SSL.Error}.
        """
        self.assertRaises(
            SSL.Error,
            ssl.DefaultOpenSSLContextFactory, self.mktemp(), certPath)
class BufferingTests(TestCase):

    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    serverProto = None
    clientProto = None


    def tearDown(self):
        if self.serverProto.transport is not None:
            self.serverProto.transport.loseConnection()
        if self.clientProto.transport is not None:
            self.clientProto.transport.loseConnection()

        return waitUntilAllDisconnected(
            reactor, [self.serverProto, self.clientProto])


    def test_openSSLBuffering(self):
        serverProto = self.serverProto = SingleLineServerProtocol()
        clientProto = self.clientProto = RecordingClientProtocol()

        server = protocol.ServerFactory()
        client = self.client = protocol.ClientFactory()

        server.protocol = lambda: serverProto
        client.protocol = lambda: clientProto

        sCTX = ssl.DefaultOpenSSLContextFactory(certPath, certPath)
        cCTX = ssl.ClientContextFactory()

        port = reactor.listenSSL(0, server, sCTX, interface='127.0.0.1')
        self.addCleanup(port.stopListening)

        clientConnector = reactor.connectSSL('127.0.0.1', port.getHost().port,
                                             client, cCTX)
        self.addCleanup(clientConnector.disconnect)

        return clientProto.deferred.addCallback(
            self.assertEqual, b"+OK <some crap>\r\n")
Esempio n. 7
0
class ClientContextFactoryTests(TestCase):
    """
    Tests for L{ssl.ClientContextFactory}.
    """

    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    def setUp(self):
        self.contextFactory = ssl.ClientContextFactory()
        self.contextFactory._contextFactory = FakeContext
        self.context = self.contextFactory.getContext()

    def test_method(self):
        """
        L{ssl.ClientContextFactory.getContext} returns a context which can use
        SSLv3 or TLSv1 but not SSLv2.
        """
        self.assertEqual(self.context._method, SSL.SSLv23_METHOD)
        self.assertEqual(self.context._options & SSL.OP_NO_SSLv2,
                         SSL.OP_NO_SSLv2)
        self.assertFalse(self.context._options & SSL.OP_NO_SSLv3)
        self.assertFalse(self.context._options & SSL.OP_NO_TLSv1)
        L{Agent._computeHostValue} returns a string giving just the
        host name passed to it.
        """
        self.assertEquals(
            self.agent._computeHostValue('https', 'example.com', 443),
            'example.com')

    def test_hostValueNonStandardHTTPS(self):
        """
        When passed a scheme of C{'https'} and a port other than
        C{443}, L{Agent._computeHostValue} returns a string giving the
        host passed to it joined together with the port number by
        C{":"}.
        """
        self.assertEquals(
            self.agent._computeHostValue('https', 'example.com', 54321),
            'example.com:54321')


if ssl is None or not hasattr(ssl, 'DefaultOpenSSLContextFactory'):
    for case in [
            WebClientSSLTestCase, WebClientRedirectBetweenSSLandPlainText
    ]:
        case.skip = "OpenSSL not present"

if not interfaces.IReactorSSL(reactor, None):
    for case in [
            WebClientSSLTestCase, WebClientRedirectBetweenSSLandPlainText
    ]:
        case.skip = "Reactor doesn't support SSL"
Esempio n. 9
0
class ConnectionLostTests(TestCase, ContextGeneratingMixin):
    """
    SSL connection closing tests.
    """

    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    def testImmediateDisconnect(self):
        org = "twisted.test.test_ssl"
        self.setupServerAndClient((org, org + ", client"), {},
                                  (org, org + ", server"), {})

        # Set up a server, connect to it with a client, which should work since our verifiers
        # allow anything, then disconnect.
        serverProtocolFactory = protocol.ServerFactory()
        serverProtocolFactory.protocol = protocol.Protocol
        self.serverPort = serverPort = reactor.listenSSL(
            0, serverProtocolFactory, self.serverCtxFactory)

        clientProtocolFactory = protocol.ClientFactory()
        clientProtocolFactory.protocol = ImmediatelyDisconnectingProtocol
        clientProtocolFactory.connectionDisconnected = defer.Deferred()
        reactor.connectSSL(
            "127.0.0.1",
            serverPort.getHost().port,
            clientProtocolFactory,
            self.clientCtxFactory,
        )

        return clientProtocolFactory.connectionDisconnected.addCallback(
            lambda ignoredResult: self.serverPort.stopListening())

    def test_bothSidesLoseConnection(self):
        """
        Both sides of SSL connection close connection; the connections should
        close cleanly, and only after the underlying TCP connection has
        disconnected.
        """
        @implementer(interfaces.IHandshakeListener)
        class CloseAfterHandshake(protocol.Protocol):
            gotData = False

            def __init__(self):
                self.done = defer.Deferred()

            def handshakeCompleted(self):
                self.transport.loseConnection()

            def connectionLost(self, reason):
                self.done.errback(reason)
                del self.done

        org = "twisted.test.test_ssl"
        self.setupServerAndClient((org, org + ", client"), {},
                                  (org, org + ", server"), {})

        serverProtocol = CloseAfterHandshake()
        serverProtocolFactory = protocol.ServerFactory()
        serverProtocolFactory.protocol = lambda: serverProtocol
        serverPort = reactor.listenSSL(0, serverProtocolFactory,
                                       self.serverCtxFactory)
        self.addCleanup(serverPort.stopListening)

        clientProtocol = CloseAfterHandshake()
        clientProtocolFactory = protocol.ClientFactory()
        clientProtocolFactory.protocol = lambda: clientProtocol
        reactor.connectSSL(
            "127.0.0.1",
            serverPort.getHost().port,
            clientProtocolFactory,
            self.clientCtxFactory,
        )

        def checkResult(failure):
            failure.trap(ConnectionDone)

        return defer.gatherResults([
            clientProtocol.done.addErrback(checkResult),
            serverProtocol.done.addErrback(checkResult),
        ])

    def testFailedVerify(self):
        org = "twisted.test.test_ssl"
        self.setupServerAndClient((org, org + ", client"), {},
                                  (org, org + ", server"), {})

        def verify(*a):
            return False

        self.clientCtxFactory.getContext().set_verify(SSL.VERIFY_PEER, verify)

        serverConnLost = defer.Deferred()
        serverProtocol = protocol.Protocol()
        serverProtocol.connectionLost = serverConnLost.callback
        serverProtocolFactory = protocol.ServerFactory()
        serverProtocolFactory.protocol = lambda: serverProtocol
        self.serverPort = serverPort = reactor.listenSSL(
            0, serverProtocolFactory, self.serverCtxFactory)

        clientConnLost = defer.Deferred()
        clientProtocol = protocol.Protocol()
        clientProtocol.connectionLost = clientConnLost.callback
        clientProtocolFactory = protocol.ClientFactory()
        clientProtocolFactory.protocol = lambda: clientProtocol
        reactor.connectSSL(
            "127.0.0.1",
            serverPort.getHost().port,
            clientProtocolFactory,
            self.clientCtxFactory,
        )

        dl = defer.DeferredList([serverConnLost, clientConnLost],
                                consumeErrors=True)
        return dl.addCallback(self._cbLostConns)

    def _cbLostConns(self, results):
        (sSuccess, sResult), (cSuccess, cResult) = results

        self.assertFalse(sSuccess)
        self.assertFalse(cSuccess)

        acceptableErrors = [SSL.Error]

        # Rather than getting a verification failure on Windows, we are getting
        # a connection failure.  Without something like sslverify proxying
        # in-between we can't fix up the platform's errors, so let's just
        # specifically say it is only OK in this one case to keep the tests
        # passing.  Normally we'd like to be as strict as possible here, so
        # we're not going to allow this to report errors incorrectly on any
        # other platforms.

        if platform.isWindows():
            from twisted.internet.error import ConnectionLost

            acceptableErrors.append(ConnectionLost)

        sResult.trap(*acceptableErrors)
        cResult.trap(*acceptableErrors)

        return self.serverPort.stopListening()
Esempio n. 10
0
class TLSTests(TestCase):
    """
    Tests for startTLS support.

    @ivar fillBuffer: forwarded to L{LineCollector.fillBuffer}
    @type fillBuffer: C{bool}
    """

    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    fillBuffer = False

    clientProto = None
    serverProto = None

    def tearDown(self):
        if self.clientProto.transport is not None:
            self.clientProto.transport.loseConnection()
        if self.serverProto.transport is not None:
            self.serverProto.transport.loseConnection()

    def _runTest(self, clientProto, serverProto, clientIsServer=False):
        """
        Helper method to run TLS tests.

        @param clientProto: protocol instance attached to the client
            connection.
        @param serverProto: protocol instance attached to the server
            connection.
        @param clientIsServer: flag indicated if client should initiate
            startTLS instead of server.

        @return: a L{defer.Deferred} that will fire when both connections are
            lost.
        """
        self.clientProto = clientProto
        cf = self.clientFactory = protocol.ClientFactory()
        cf.protocol = lambda: clientProto
        if clientIsServer:
            cf.server = False
        else:
            cf.client = True

        self.serverProto = serverProto
        sf = self.serverFactory = protocol.ServerFactory()
        sf.protocol = lambda: serverProto
        if clientIsServer:
            sf.client = False
        else:
            sf.server = True

        port = reactor.listenTCP(0, sf, interface="127.0.0.1")
        self.addCleanup(port.stopListening)

        reactor.connectTCP("127.0.0.1", port.getHost().port, cf)

        return defer.gatherResults(
            [clientProto.deferred, serverProto.deferred])

    def test_TLS(self):
        """
        Test for server and client startTLS: client should received data both
        before and after the startTLS.
        """
        def check(ignore):
            self.assertEqual(
                self.serverFactory.lines,
                UnintelligentProtocol.pretext + UnintelligentProtocol.posttext,
            )

        d = self._runTest(UnintelligentProtocol(),
                          LineCollector(True, self.fillBuffer))
        return d.addCallback(check)

    def test_unTLS(self):
        """
        Test for server startTLS not followed by a startTLS in client: the data
        received after server startTLS should be received as raw.
        """
        def check(ignored):
            self.assertEqual(self.serverFactory.lines,
                             UnintelligentProtocol.pretext)
            self.assertTrue(self.serverFactory.rawdata,
                            "No encrypted bytes received")

        d = self._runTest(UnintelligentProtocol(),
                          LineCollector(False, self.fillBuffer))
        return d.addCallback(check)

    def test_backwardsTLS(self):
        """
        Test startTLS first initiated by client.
        """
        def check(ignored):
            self.assertEqual(
                self.clientFactory.lines,
                UnintelligentProtocol.pretext + UnintelligentProtocol.posttext,
            )

        d = self._runTest(LineCollector(True, self.fillBuffer),
                          UnintelligentProtocol(), True)
        return d.addCallback(check)
Esempio n. 11
0
                          self.mktemp(), certPath)


class ClientContextFactoryTests(unittest.TestCase):
    """
    Tests for L{ssl.ClientContextFactory}.
    """
    def setUp(self):
        self.contextFactory = ssl.ClientContextFactory()
        self.contextFactory._contextFactory = FakeContext
        self.context = self.contextFactory.getContext()

    def test_method(self):
        """
        L{ssl.ClientContextFactory.getContext} returns a context which can use
        SSLv3 or TLSv1 but not SSLv2.
        """
        self.assertEqual(self.context._method, SSL.SSLv23_METHOD)
        self.assertTrue(self.context._options & SSL.OP_NO_SSLv2)
        self.assertFalse(self.context._options & SSL.OP_NO_SSLv3)
        self.assertFalse(self.context._options & SSL.OP_NO_TLSv1)


if interfaces.IReactorSSL(reactor, None) is None:
    for tCase in [
            StolenTCPTests, TLSTests, SpammyTLSTests, BufferingTests,
            ConnectionLostTests, DefaultOpenSSLContextFactoryTests,
            ClientContextFactoryTests
    ]:
        tCase.skip = "Reactor does not support SSL, cannot run SSL tests"
Esempio n. 12
0
        def connectionMade(self):
            self.factory.input = []
            self.output = self.output[:]
            for line in self.output.pop(0):
                self.sendLine(line)

        def lineReceived(self, line):
            self.factory.input.append(line)
            [self.sendLine(l) for l in self.output.pop(0)]
            if line == b"STLS":
                self.transport.startTLS(self.context)


@skipIf(not ClientTLSContext, "OpenSSL not present")
@skipIf(not interfaces.IReactorSSL(reactor, None), "OpenSSL not present")
class POP3TLSTests(TestCase):
    """
    Tests for POP3Client's support for TLS connections.
    """
    def test_startTLS(self):
        """
        POP3Client.startTLS starts a TLS session over its existing TCP
        connection.
        """
        sf = TLSServerFactory()
        sf.protocol.output = [
            [b"+OK"],  # Server greeting
            [b"+OK", b"STLS", b"."],  # CAPA response
            [b"+OK"],  # STLS response
            [b"+OK", b"."],  # Second CAPA response
Esempio n. 13
0
class Constructors(unittest.TestCase):
    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    def test_peerFromNonSSLTransport(self):
        """
        Verify that peerFromTransport raises an exception if the transport
        passed is not actually an SSL transport.
        """
        x = self.assertRaises(CertificateError,
                              sslverify.Certificate.peerFromTransport,
                              _NotSSLTransport())
        self.failUnless(str(x).startswith("non-TLS"))

    def test_peerFromBlankSSLTransport(self):
        """
        Verify that peerFromTransport raises an exception if the transport
        passed is an SSL transport, but doesn't have a peer certificate.
        """
        x = self.assertRaises(CertificateError,
                              sslverify.Certificate.peerFromTransport,
                              _MaybeSSLTransport())
        self.failUnless(str(x).startswith("TLS"))

    def test_hostFromNonSSLTransport(self):
        """
        Verify that hostFromTransport raises an exception if the transport
        passed is not actually an SSL transport.
        """
        x = self.assertRaises(CertificateError,
                              sslverify.Certificate.hostFromTransport,
                              _NotSSLTransport())
        self.failUnless(str(x).startswith("non-TLS"))

    def test_hostFromBlankSSLTransport(self):
        """
        Verify that hostFromTransport raises an exception if the transport
        passed is an SSL transport, but doesn't have a host certificate.
        """
        x = self.assertRaises(CertificateError,
                              sslverify.Certificate.hostFromTransport,
                              _MaybeSSLTransport())
        self.failUnless(str(x).startswith("TLS"))

    def test_hostFromSSLTransport(self):
        """
        Verify that hostFromTransport successfully creates the correct
        certificate if passed a valid SSL transport.
        """
        self.assertEqual(
            sslverify.Certificate.hostFromTransport(
                _ActualSSLTransport()).serialNumber(), 12345)

    def test_peerFromSSLTransport(self):
        """
        Verify that peerFromTransport successfully creates the correct
        certificate if passed a valid SSL transport.
        """
        self.assertEqual(
            sslverify.Certificate.peerFromTransport(
                _ActualSSLTransport()).serialNumber(), 12346)
Esempio n. 14
0
class OpenSSLOptions(unittest.TestCase):
    if interfaces.IReactorSSL(reactor, None) is None:
        skip = "Reactor does not support SSL, cannot run SSL tests"

    serverPort = clientConn = None
    onServerLost = onClientLost = None

    sKey = None
    sCert = None
    cKey = None
    cCert = None

    def setUp(self):
        """
        Create class variables of client and server certificates.
        """
        self.sKey, self.sCert = makeCertificate(O=b"Server Test Certificate",
                                                CN=b"server")
        self.cKey, self.cCert = makeCertificate(O=b"Client Test Certificate",
                                                CN=b"client")
        self.caCert1 = makeCertificate(O=b"CA Test Certificate 1",
                                       CN=b"ca1")[1]
        self.caCert2 = makeCertificate(O=b"CA Test Certificate", CN=b"ca2")[1]
        self.caCerts = [self.caCert1, self.caCert2]
        self.extraCertChain = self.caCerts

    def tearDown(self):
        if self.serverPort is not None:
            self.serverPort.stopListening()
        if self.clientConn is not None:
            self.clientConn.disconnect()

        L = []
        if self.onServerLost is not None:
            L.append(self.onServerLost)
        if self.onClientLost is not None:
            L.append(self.onClientLost)

        return defer.DeferredList(L, consumeErrors=True)

    def loopback(self,
                 serverCertOpts,
                 clientCertOpts,
                 onServerLost=None,
                 onClientLost=None,
                 onData=None):
        if onServerLost is None:
            self.onServerLost = onServerLost = defer.Deferred()
        if onClientLost is None:
            self.onClientLost = onClientLost = defer.Deferred()
        if onData is None:
            onData = defer.Deferred()

        serverFactory = protocol.ServerFactory()
        serverFactory.protocol = DataCallbackProtocol
        serverFactory.onLost = onServerLost
        serverFactory.onData = onData

        clientFactory = protocol.ClientFactory()
        clientFactory.protocol = WritingProtocol
        clientFactory.onLost = onClientLost

        self.serverPort = reactor.listenSSL(0, serverFactory, serverCertOpts)
        self.clientConn = reactor.connectSSL('127.0.0.1',
                                             self.serverPort.getHost().port,
                                             clientFactory, clientCertOpts)

    def test_constructorWithOnlyPrivateKey(self):
        """
        C{privateKey} and C{certificate} make only sense if both are set.
        """
        self.assertRaises(ValueError,
                          sslverify.OpenSSLCertificateOptions,
                          privateKey=self.sKey)

    def test_constructorWithOnlyCertificate(self):
        """
        C{privateKey} and C{certificate} make only sense if both are set.
        """
        self.assertRaises(ValueError,
                          sslverify.OpenSSLCertificateOptions,
                          certificate=self.sCert)

    def test_constructorWithCertificateAndPrivateKey(self):
        """
        Specifying C{privateKey} and C{certificate} initializes correctly.
        """
        opts = sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                   certificate=self.sCert)
        self.assertEqual(opts.privateKey, self.sKey)
        self.assertEqual(opts.certificate, self.sCert)
        self.assertEqual(opts.extraCertChain, [])

    def test_constructorDoesNotAllowVerifyWithoutCACerts(self):
        """
        C{verify} must not be C{True} without specifying C{caCerts}.
        """
        self.assertRaises(ValueError,
                          sslverify.OpenSSLCertificateOptions,
                          privateKey=self.sKey,
                          certificate=self.sCert,
                          verify=True)

    def test_constructorAllowsCACertsWithoutVerify(self):
        """
        It's currently a NOP, but valid.
        """
        opts = sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                   certificate=self.sCert,
                                                   caCerts=self.caCerts)
        self.assertFalse(opts.verify)
        self.assertEqual(self.caCerts, opts.caCerts)

    def test_constructorWithVerifyAndCACerts(self):
        """
        Specifying C{verify} and C{caCerts} initializes correctly.
        """
        opts = sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                   certificate=self.sCert,
                                                   verify=True,
                                                   caCerts=self.caCerts)
        self.assertTrue(opts.verify)
        self.assertEqual(self.caCerts, opts.caCerts)

    def test_constructorSetsExtraChain(self):
        """
        Setting C{extraCertChain} works if C{certificate} and C{privateKey} are
        set along with it.
        """
        opts = sslverify.OpenSSLCertificateOptions(
            privateKey=self.sKey,
            certificate=self.sCert,
            extraCertChain=self.extraCertChain,
        )
        self.assertEqual(self.extraCertChain, opts.extraCertChain)

    def test_constructorDoesNotAllowExtraChainWithoutPrivateKey(self):
        """
        A C{extraCertChain} without C{privateKey} doesn't make sense and is
        thus rejected.
        """
        self.assertRaises(
            ValueError,
            sslverify.OpenSSLCertificateOptions,
            certificate=self.sCert,
            extraCertChain=self.extraCertChain,
        )

    def test_constructorDoesNotAllowExtraChainWithOutPrivateKey(self):
        """
        A C{extraCertChain} without C{certificate} doesn't make sense and is
        thus rejected.
        """
        self.assertRaises(
            ValueError,
            sslverify.OpenSSLCertificateOptions,
            privateKey=self.sKey,
            extraCertChain=self.extraCertChain,
        )

    def test_extraChainFilesAreAddedIfSupplied(self):
        """
        If C{extraCertChain} is set and all prerequisites are met, the
        specified chain certificates are added to C{Context}s that get
        created.
        """
        opts = sslverify.OpenSSLCertificateOptions(
            privateKey=self.sKey,
            certificate=self.sCert,
            extraCertChain=self.extraCertChain,
        )
        opts._contextFactory = FakeContext
        ctx = opts.getContext()
        self.assertEqual(self.sKey, ctx._privateKey)
        self.assertEqual(self.sCert, ctx._certificate)
        self.assertEqual(self.extraCertChain, ctx._extraCertChain)

    def test_extraChainDoesNotBreakPyOpenSSL(self):
        """
        C{extraCertChain} doesn't break C{OpenSSL.SSL.Context} creation.
        """
        opts = sslverify.OpenSSLCertificateOptions(
            privateKey=self.sKey,
            certificate=self.sCert,
            extraCertChain=self.extraCertChain,
        )
        ctx = opts.getContext()
        self.assertIsInstance(ctx, SSL.Context)

    def test_abbreviatingDistinguishedNames(self):
        """
        Check that abbreviations used in certificates correctly map to
        complete names.
        """
        self.assertEqual(
            sslverify.DN(CN=b'a', OU=b'hello'),
            sslverify.DistinguishedName(commonName=b'a',
                                        organizationalUnitName=b'hello'))
        self.assertNotEquals(
            sslverify.DN(CN=b'a', OU=b'hello'),
            sslverify.DN(CN=b'a', OU=b'hello', emailAddress=b'xxx'))
        dn = sslverify.DN(CN=b'abcdefg')
        self.assertRaises(AttributeError, setattr, dn, 'Cn', b'x')
        self.assertEqual(dn.CN, dn.commonName)
        dn.CN = b'bcdefga'
        self.assertEqual(dn.CN, dn.commonName)

    def testInspectDistinguishedName(self):
        n = sslverify.DN(commonName=b'common name',
                         organizationName=b'organization name',
                         organizationalUnitName=b'organizational unit name',
                         localityName=b'locality name',
                         stateOrProvinceName=b'state or province name',
                         countryName=b'country name',
                         emailAddress=b'email address')
        s = n.inspect()
        for k in [
                'common name', 'organization name', 'organizational unit name',
                'locality name', 'state or province name', 'country name',
                'email address'
        ]:
            self.assertIn(k, s, "%r was not in inspect output." % (k, ))
            self.assertIn(k.title(), s,
                          "%r was not in inspect output." % (k, ))

    def testInspectDistinguishedNameWithoutAllFields(self):
        n = sslverify.DN(localityName=b'locality name')
        s = n.inspect()
        for k in [
                'common name', 'organization name', 'organizational unit name',
                'state or province name', 'country name', 'email address'
        ]:
            self.assertNotIn(k, s, "%r was in inspect output." % (k, ))
            self.assertNotIn(k.title(), s, "%r was in inspect output." % (k, ))
        self.assertIn('locality name', s)
        self.assertIn('Locality Name', s)

    def test_inspectCertificate(self):
        """
        Test that the C{inspect} method of L{sslverify.Certificate} returns
        a human-readable string containing some basic information about the
        certificate.
        """
        c = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
        self.assertEqual(c.inspect().split('\n'), [
            "Certificate For Subject:",
            "               Common Name: example.twistedmatrix.com",
            "              Country Name: US",
            "             Email Address: [email protected]",
            "             Locality Name: Boston",
            "         Organization Name: Twisted Matrix Labs",
            "  Organizational Unit Name: Security",
            "    State Or Province Name: Massachusetts", "", "Issuer:",
            "               Common Name: example.twistedmatrix.com",
            "              Country Name: US",
            "             Email Address: [email protected]",
            "             Locality Name: Boston",
            "         Organization Name: Twisted Matrix Labs",
            "  Organizational Unit Name: Security",
            "    State Or Province Name: Massachusetts", "",
            "Serial Number: 12345",
            "Digest: C4:96:11:00:30:C3:EC:EE:A3:55:AA:ED:8C:84:85:18",
            "Public Key with Hash: ff33994c80812aa95a79cdb85362d054"
        ])

    def test_certificateOptionsSerialization(self):
        """
        Test that __setstate__(__getstate__()) round-trips properly.
        """
        firstOpts = sslverify.OpenSSLCertificateOptions(
            privateKey=self.sKey,
            certificate=self.sCert,
            method=SSL.SSLv3_METHOD,
            verify=True,
            caCerts=[self.sCert],
            verifyDepth=2,
            requireCertificate=False,
            verifyOnce=False,
            enableSingleUseKeys=False,
            enableSessions=False,
            fixBrokenPeers=True,
            enableSessionTickets=True)
        context = firstOpts.getContext()
        self.assertIdentical(context, firstOpts._context)
        self.assertNotIdentical(context, None)
        state = firstOpts.__getstate__()
        self.assertNotIn("_context", state)

        opts = sslverify.OpenSSLCertificateOptions()
        opts.__setstate__(state)
        self.assertEqual(opts.privateKey, self.sKey)
        self.assertEqual(opts.certificate, self.sCert)
        self.assertEqual(opts.method, SSL.SSLv3_METHOD)
        self.assertEqual(opts.verify, True)
        self.assertEqual(opts.caCerts, [self.sCert])
        self.assertEqual(opts.verifyDepth, 2)
        self.assertEqual(opts.requireCertificate, False)
        self.assertEqual(opts.verifyOnce, False)
        self.assertEqual(opts.enableSingleUseKeys, False)
        self.assertEqual(opts.enableSessions, False)
        self.assertEqual(opts.fixBrokenPeers, True)
        self.assertEqual(opts.enableSessionTickets, True)

    def test_certificateOptionsSessionTickets(self):
        """
        Enabling session tickets should not set the OP_NO_TICKET option.
        """
        opts = sslverify.OpenSSLCertificateOptions(enableSessionTickets=True)
        ctx = opts.getContext()
        self.assertEqual(0, ctx.set_options(0) & 0x00004000)

    def test_certificateOptionsSessionTicketsDisabled(self):
        """
        Enabling session tickets should set the OP_NO_TICKET option.
        """
        opts = sslverify.OpenSSLCertificateOptions(enableSessionTickets=False)
        ctx = opts.getContext()
        self.assertEqual(0x00004000, ctx.set_options(0) & 0x00004000)

    def test_allowedAnonymousClientConnection(self):
        """
        Check that anonymous connections are allowed when certificates aren't
        required on the server.
        """
        onData = defer.Deferred()
        self.loopback(
            sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                certificate=self.sCert,
                                                requireCertificate=False),
            sslverify.OpenSSLCertificateOptions(requireCertificate=False),
            onData=onData)

        return onData.addCallback(
            lambda result: self.assertEqual(result, WritingProtocol.byte))

    def test_refusedAnonymousClientConnection(self):
        """
        Check that anonymous connections are refused when certificates are
        required on the server.
        """
        onServerLost = defer.Deferred()
        onClientLost = defer.Deferred()
        self.loopback(
            sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                certificate=self.sCert,
                                                verify=True,
                                                caCerts=[self.sCert],
                                                requireCertificate=True),
            sslverify.OpenSSLCertificateOptions(requireCertificate=False),
            onServerLost=onServerLost,
            onClientLost=onClientLost)

        d = defer.DeferredList([onClientLost, onServerLost],
                               consumeErrors=True)

        def afterLost(result):
            ((cSuccess, cResult), (sSuccess, sResult)) = result
            self.failIf(cSuccess)
            self.failIf(sSuccess)
            # Win32 fails to report the SSL Error, and report a connection lost
            # instead: there is a race condition so that's not totally
            # surprising (see ticket #2877 in the tracker)
            self.assertIsInstance(cResult.value, (SSL.Error, ConnectionLost))
            self.assertIsInstance(sResult.value, SSL.Error)

        return d.addCallback(afterLost)

    def test_failedCertificateVerification(self):
        """
        Check that connecting with a certificate not accepted by the server CA
        fails.
        """
        onServerLost = defer.Deferred()
        onClientLost = defer.Deferred()
        self.loopback(
            sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                certificate=self.sCert,
                                                verify=False,
                                                requireCertificate=False),
            sslverify.OpenSSLCertificateOptions(verify=True,
                                                requireCertificate=False,
                                                caCerts=[self.cCert]),
            onServerLost=onServerLost,
            onClientLost=onClientLost)

        d = defer.DeferredList([onClientLost, onServerLost],
                               consumeErrors=True)

        def afterLost(result):
            ((cSuccess, cResult), (sSuccess, sResult)) = result
            self.failIf(cSuccess)
            self.failIf(sSuccess)

        return d.addCallback(afterLost)

    def test_successfulCertificateVerification(self):
        """
        Test a successful connection with client certificate validation on
        server side.
        """
        onData = defer.Deferred()
        self.loopback(
            sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                certificate=self.sCert,
                                                verify=False,
                                                requireCertificate=False),
            sslverify.OpenSSLCertificateOptions(verify=True,
                                                requireCertificate=True,
                                                caCerts=[self.sCert]),
            onData=onData)

        return onData.addCallback(
            lambda result: self.assertEqual(result, WritingProtocol.byte))

    def test_successfulSymmetricSelfSignedCertificateVerification(self):
        """
        Test a successful connection with validation on both server and client
        sides.
        """
        onData = defer.Deferred()
        self.loopback(
            sslverify.OpenSSLCertificateOptions(privateKey=self.sKey,
                                                certificate=self.sCert,
                                                verify=True,
                                                requireCertificate=True,
                                                caCerts=[self.cCert]),
            sslverify.OpenSSLCertificateOptions(privateKey=self.cKey,
                                                certificate=self.cCert,
                                                verify=True,
                                                requireCertificate=True,
                                                caCerts=[self.sCert]),
            onData=onData)

        return onData.addCallback(
            lambda result: self.assertEqual(result, WritingProtocol.byte))

    def test_verification(self):
        """
        Check certificates verification building custom certificates data.
        """
        clientDN = sslverify.DistinguishedName(commonName='client')
        clientKey = sslverify.KeyPair.generate()
        clientCertReq = clientKey.certificateRequest(clientDN)

        serverDN = sslverify.DistinguishedName(commonName='server')
        serverKey = sslverify.KeyPair.generate()
        serverCertReq = serverKey.certificateRequest(serverDN)

        clientSelfCertReq = clientKey.certificateRequest(clientDN)
        clientSelfCertData = clientKey.signCertificateRequest(
            clientDN, clientSelfCertReq, lambda dn: True, 132)
        clientSelfCert = clientKey.newCertificate(clientSelfCertData)

        serverSelfCertReq = serverKey.certificateRequest(serverDN)
        serverSelfCertData = serverKey.signCertificateRequest(
            serverDN, serverSelfCertReq, lambda dn: True, 516)
        serverSelfCert = serverKey.newCertificate(serverSelfCertData)

        clientCertData = serverKey.signCertificateRequest(
            serverDN, clientCertReq, lambda dn: True, 7)
        clientCert = clientKey.newCertificate(clientCertData)

        serverCertData = clientKey.signCertificateRequest(
            clientDN, serverCertReq, lambda dn: True, 42)
        serverCert = serverKey.newCertificate(serverCertData)

        onData = defer.Deferred()

        serverOpts = serverCert.options(serverSelfCert)
        clientOpts = clientCert.options(clientSelfCert)

        self.loopback(serverOpts, clientOpts, onData=onData)

        return onData.addCallback(
            lambda result: self.assertEqual(result, WritingProtocol.byte))