Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / internet / test / test_tls.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Tests for implementations of L{ITLSTransport}.
6 """
7
8 __metaclass__ = type
9
10 import sys, operator
11
12 from zope.interface import implements
13
14 from twisted.internet.test.reactormixins import ReactorBuilder, EndpointCreator
15 from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol
16 from twisted.internet.interfaces import (
17     IReactorSSL, ITLSTransport, IStreamClientEndpoint)
18 from twisted.internet.defer import Deferred, DeferredList
19 from twisted.internet.endpoints import (
20     SSL4ServerEndpoint, SSL4ClientEndpoint, TCP4ClientEndpoint)
21 from twisted.internet.error import ConnectionClosed
22 from twisted.internet.task import Cooperator
23 from twisted.trial.unittest import TestCase, SkipTest
24 from twisted.python.runtime import platform
25
26 from twisted.internet.test.test_core import ObjectModelIntegrationMixin
27 from twisted.internet.test.test_tcp import (
28     StreamTransportTestsMixin, AbortConnectionMixin)
29 from twisted.internet.test.connectionmixins import ConnectionTestsMixin
30 from twisted.internet.test.connectionmixins import BrokenContextFactory
31
32 try:
33     from OpenSSL.crypto import FILETYPE_PEM
34 except ImportError:
35     FILETYPE_PEM = None
36 else:
37     from twisted.internet.ssl import PrivateCertificate, KeyPair
38     from twisted.internet.ssl import ClientContextFactory
39
40
41 class TLSMixin:
42     requiredInterfaces = [IReactorSSL]
43
44     if platform.isWindows():
45         msg = (
46             "For some reason, these reactors don't deal with SSL "
47             "disconnection correctly on Windows.  See #3371.")
48         skippedReactors = {
49             "twisted.internet.glib2reactor.Glib2Reactor": msg,
50             "twisted.internet.gtk2reactor.Gtk2Reactor": msg}
51
52
53 class ContextGeneratingMixin(object):
54     _certificateText = (
55         "-----BEGIN CERTIFICATE-----\n"
56         "MIIDBjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzER\n"
57         "MA8GA1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQD\n"
58         "ExtNMkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5n\n"
59         "cHNAcG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzEL\n"
60         "MAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhv\n"
61         "c3QxHTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEB\n"
62         "BQADSwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh\n"
63         "5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQC\n"
64         "MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRl\n"
65         "MB0GA1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7\n"
66         "hyNp65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoT\n"
67         "CE0yQ3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlw\n"
68         "dG8gQ2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3Qx\n"
69         "LmNvbYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6\n"
70         "BoJuVwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++\n"
71         "7QGG/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JE\n"
72         "WUQ9Ho4EzbYCOQ==\n"
73         "-----END CERTIFICATE-----\n")
74
75     _privateKeyText = (
76         "-----BEGIN RSA PRIVATE KEY-----\n"
77         "MIIBPAIBAAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh\n"
78         "5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAQJBAIqm/bz4NA1H++Vx5Ewx\n"
79         "OcKp3w19QSaZAwlGRtsUxrP7436QjnREM3Bm8ygU11BjkPVmtrKm6AayQfCHqJoT\n"
80         "ZIECIQDW0BoMoL0HOYM/mrTLhaykYAVqgIeJsPjvkEhTFXWBuQIhAM3deFAvWNu4\n"
81         "nklUQ37XsCT2c9tmNt1LAT+slG2JOTTRAiAuXDtC/m3NYVwyHfFm+zKHRzHkClk2\n"
82         "HjubeEgjpj32AQIhAJqMGTaZVOwevTXvvHwNEH+vRWsAYU/gbx+OQB+7VOcBAiEA\n"
83         "oolb6NMg/R3enNPvS1O4UU1H8wpaF77L4yiSWlE0p4w=\n"
84         "-----END RSA PRIVATE KEY-----\n")
85
86
87     def getServerContext(self):
88         """
89         Return a new SSL context suitable for use in a test server.
90         """
91         cert = PrivateCertificate.load(
92             self._certificateText,
93             KeyPair.load(self._privateKeyText, FILETYPE_PEM),
94             FILETYPE_PEM)
95         return cert.options()
96
97
98     def getClientContext(self):
99         return ClientContextFactory()
100
101
102
103 class StartTLSClientEndpoint(object):
104     """
105     An endpoint which wraps another one and adds a TLS layer immediately when
106     connections are set up.
107
108     @ivar wrapped: A L{IStreamClientEndpoint} provider which will be used to
109         really set up connections.
110
111     @ivar contextFactory: A L{ContextFactory} to use to do TLS.
112     """
113     implements(IStreamClientEndpoint)
114
115     def __init__(self, wrapped, contextFactory):
116         self.wrapped = wrapped
117         self.contextFactory = contextFactory
118
119
120     def connect(self, factory):
121         """
122         Establish a connection using a protocol build by C{factory} and
123         immediately start TLS on it.  Return a L{Deferred} which fires with the
124         protocol instance.
125         """
126         # This would be cleaner when we have ITransport.switchProtocol, which
127         # will be added with ticket #3204:
128         class WrapperFactory(ServerFactory):
129             def buildProtocol(wrapperSelf, addr):
130                 protocol = factory.buildProtocol(addr)
131                 def connectionMade(orig=protocol.connectionMade):
132                     protocol.transport.startTLS(self.contextFactory)
133                     orig()
134                 protocol.connectionMade = connectionMade
135                 return protocol
136
137         return self.wrapped.connect(WrapperFactory())
138
139
140
141 class StartTLSClientCreator(EndpointCreator, ContextGeneratingMixin):
142     """
143     Create L{ITLSTransport.startTLS} endpoint for the client, and normal SSL
144     for server just because it's easier.
145     """
146     def server(self, reactor):
147         """
148         Construct an SSL server endpoint.  This should be be constructing a TCP
149         server endpoint which immediately calls C{startTLS} instead, but that
150         is hard.
151         """
152         return SSL4ServerEndpoint(reactor, 0, self.getServerContext())
153
154
155     def client(self, reactor, serverAddress):
156         """
157         Construct a TCP client endpoint wrapped to immediately start TLS.
158         """
159         return StartTLSClientEndpoint(
160             TCP4ClientEndpoint(
161                 reactor, '127.0.0.1', serverAddress.port),
162             ClientContextFactory())
163
164
165
166 class BadContextTestsMixin(object):
167     """
168     Mixin for L{ReactorBuilder} subclasses which defines a helper for testing
169     the handling of broken context factories.
170     """
171     def _testBadContext(self, useIt):
172         """
173         Assert that the exception raised by a broken context factory's
174         C{getContext} method is raised by some reactor method.  If it is not, an
175         exception will be raised to fail the test.
176
177         @param useIt: A two-argument callable which will be called with a
178             reactor and a broken context factory and which is expected to raise
179             the same exception as the broken context factory's C{getContext}
180             method.
181         """
182         reactor = self.buildReactor()
183         exc = self.assertRaises(
184             ValueError, useIt, reactor, BrokenContextFactory())
185         self.assertEqual(BrokenContextFactory.message, str(exc))
186
187
188
189 class StartTLSClientTestsMixin(TLSMixin, ReactorBuilder, ConnectionTestsMixin):
190     """
191     Tests for TLS connections established using L{ITLSTransport.startTLS} (as
192     opposed to L{IReactorSSL.connectSSL} or L{IReactorSSL.listenSSL}).
193     """
194     endpoints = StartTLSClientCreator()
195
196
197
198 class SSLCreator(EndpointCreator, ContextGeneratingMixin):
199     """
200     Create SSL endpoints.
201     """
202     def server(self, reactor):
203         """
204         Create an SSL server endpoint on a TCP/IP-stack allocated port.
205         """
206         return SSL4ServerEndpoint(reactor, 0, self.getServerContext())
207
208
209     def client(self, reactor, serverAddress):
210         """
211         Create an SSL client endpoint which will connect localhost on
212         the port given by C{serverAddress}.
213
214         @type serverAddress: L{IPv4Address}
215         """
216         return SSL4ClientEndpoint(
217             reactor, '127.0.0.1', serverAddress.port,
218             ClientContextFactory())
219
220
221 class SSLClientTestsMixin(TLSMixin, ReactorBuilder, ContextGeneratingMixin,
222                           ConnectionTestsMixin, BadContextTestsMixin):
223     """
224     Mixin defining tests relating to L{ITLSTransport}.
225     """
226     endpoints = SSLCreator()
227
228     def test_badContext(self):
229         """
230         If the context factory passed to L{IReactorSSL.connectSSL} raises an
231         exception from its C{getContext} method, that exception is raised by
232         L{IReactorSSL.connectSSL}.
233         """
234         def useIt(reactor, contextFactory):
235             return reactor.connectSSL(
236                 "127.0.0.1", 1234, ClientFactory(), contextFactory)
237         self._testBadContext(useIt)
238
239
240     def test_disconnectAfterWriteAfterStartTLS(self):
241         """
242         L{ITCPTransport.loseConnection} ends a connection which was set up with
243         L{ITLSTransport.startTLS} and which has recently been written to.  This
244         is intended to verify that a socket send error masked by the TLS
245         implementation doesn't prevent the connection from being reported as
246         closed.
247         """
248         class ShortProtocol(Protocol):
249             def connectionMade(self):
250                 if not ITLSTransport.providedBy(self.transport):
251                     # Functionality isn't available to be tested.
252                     finished = self.factory.finished
253                     self.factory.finished = None
254                     finished.errback(SkipTest("No ITLSTransport support"))
255                     return
256
257                 # Switch the transport to TLS.
258                 self.transport.startTLS(self.factory.context)
259                 # Force TLS to really get negotiated.  If nobody talks, nothing
260                 # will happen.
261                 self.transport.write("x")
262
263             def dataReceived(self, data):
264                 # Stuff some bytes into the socket.  This mostly has the effect
265                 # of causing the next write to fail with ENOTCONN or EPIPE.
266                 # With the pyOpenSSL implementation of ITLSTransport, the error
267                 # is swallowed outside of the control of Twisted.
268                 self.transport.write("y")
269                 # Now close the connection, which requires a TLS close alert to
270                 # be sent.
271                 self.transport.loseConnection()
272
273             def connectionLost(self, reason):
274                 # This is the success case.  The client and the server want to
275                 # get here.
276                 finished = self.factory.finished
277                 if finished is not None:
278                     self.factory.finished = None
279                     finished.callback(reason)
280
281         reactor = self.buildReactor()
282
283         serverFactory = ServerFactory()
284         serverFactory.finished = Deferred()
285         serverFactory.protocol = ShortProtocol
286         serverFactory.context = self.getServerContext()
287
288         clientFactory = ClientFactory()
289         clientFactory.finished = Deferred()
290         clientFactory.protocol = ShortProtocol
291         clientFactory.context = self.getClientContext()
292         clientFactory.context.method = serverFactory.context.method
293
294         lostConnectionResults = []
295         finished = DeferredList(
296             [serverFactory.finished, clientFactory.finished],
297             consumeErrors=True)
298         def cbFinished(results):
299             lostConnectionResults.extend([results[0][1], results[1][1]])
300         finished.addCallback(cbFinished)
301
302         port = reactor.listenTCP(0, serverFactory, interface='127.0.0.1')
303         self.addCleanup(port.stopListening)
304
305         connector = reactor.connectTCP(
306             port.getHost().host, port.getHost().port, clientFactory)
307         self.addCleanup(connector.disconnect)
308
309         finished.addCallback(lambda ign: reactor.stop())
310         self.runReactor(reactor)
311         lostConnectionResults[0].trap(ConnectionClosed)
312         lostConnectionResults[1].trap(ConnectionClosed)
313
314
315
316 class TLSPortTestsBuilder(TLSMixin, ContextGeneratingMixin,
317                           ObjectModelIntegrationMixin, BadContextTestsMixin,
318                           StreamTransportTestsMixin, ReactorBuilder):
319     """
320     Tests for L{IReactorSSL.listenSSL}
321     """
322     def getListeningPort(self, reactor, factory):
323         """
324         Get a TLS port from a reactor.
325         """
326         return reactor.listenSSL(0, factory, self.getServerContext())
327
328
329     def getExpectedStartListeningLogMessage(self, port, factory):
330         """
331         Get the message expected to be logged when a TLS port starts listening.
332         """
333         return "%s (TLS) starting on %d" % (factory, port.getHost().port)
334
335
336     def getExpectedConnectionLostLogMsg(self, port):
337         """
338         Get the expected connection lost message for a TLS port.
339         """
340         return "(TLS Port %s Closed)" % (port.getHost().port,)
341
342
343     def test_badContext(self):
344         """
345         If the context factory passed to L{IReactorSSL.listenSSL} raises an
346         exception from its C{getContext} method, that exception is raised by
347         L{IReactorSSL.listenSSL}.
348         """
349         def useIt(reactor, contextFactory):
350             return reactor.listenSSL(0, ServerFactory(), contextFactory)
351         self._testBadContext(useIt)
352
353
354
355 globals().update(SSLClientTestsMixin.makeTestCaseClasses())
356 globals().update(StartTLSClientTestsMixin.makeTestCaseClasses())
357 globals().update(TLSPortTestsBuilder().makeTestCaseClasses())
358
359
360
361 class AbortSSLConnectionTest(ReactorBuilder, AbortConnectionMixin, ContextGeneratingMixin):
362     """
363     C{abortConnection} tests using SSL.
364     """
365     requiredInterfaces = (IReactorSSL,)
366     endpoints = SSLCreator()
367
368     def buildReactor(self):
369         reactor = ReactorBuilder.buildReactor(self)
370         try:
371             from twisted.protocols import tls
372         except ImportError:
373             return reactor
374
375         # Patch twisted.protocols.tls to use this reactor, until we get
376         # around to fixing #5206, or the TLS code uses an explicit reactor:
377         cooperator = Cooperator(
378             scheduler=lambda x: reactor.callLater(0.00001, x))
379         self.patch(tls, "cooperate", cooperator.cooperate)
380         return reactor
381
382
383     def setUp(self):
384         if FILETYPE_PEM is None:
385             raise SkipTest("OpenSSL not available.")
386
387 globals().update(AbortSSLConnectionTest.makeTestCaseClasses())
388
389 class OldTLSDeprecationTest(TestCase):
390     """
391     Tests for the deprecation of L{twisted.internet._oldtls}, the implementation
392     module for L{IReactorSSL} used when only an old version of pyOpenSSL is
393     available.
394     """
395     def test_warning(self):
396         """
397         The use of L{twisted.internet._oldtls} is deprecated, and emits a
398         L{DeprecationWarning}.
399         """
400         # Since _oldtls depends on OpenSSL, just skip this test if it isn't
401         # installed on the system.  Faking it would be error prone.
402         try:
403             import OpenSSL
404         except ImportError:
405             raise SkipTest("OpenSSL not available.")
406
407         # Change the apparent version of OpenSSL to one support for which is
408         # deprecated.  And have it change back again after the test.
409         self.patch(OpenSSL, '__version__', '0.5')
410
411         # If the module was already imported, the import statement below won't
412         # execute its top-level code.  Take it out of sys.modules so the import
413         # system re-evaluates it.  Arrange to put the original back afterwards.
414         # Also handle the case where it hasn't yet been imported.
415         try:
416             oldtls = sys.modules['twisted.internet._oldtls']
417         except KeyError:
418             self.addCleanup(sys.modules.pop, 'twisted.internet._oldtls')
419         else:
420             del sys.modules['twisted.internet._oldtls']
421             self.addCleanup(
422                 operator.setitem, sys.modules, 'twisted.internet._oldtls',
423                 oldtls)
424
425         # The actual test.
426         import twisted.internet._oldtls
427         warnings = self.flushWarnings()
428         self.assertEqual(warnings[0]['category'], DeprecationWarning)
429         self.assertEqual(
430             warnings[0]['message'],
431             "Support for pyOpenSSL 0.5 is deprecated.  "
432             "Upgrade to pyOpenSSL 0.10 or newer.")