25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

interop_test_runner 9.2 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #!/usr/bin/env python2
  2. import docker
  3. import unittest
  4. import re
  5. import time
  6. # Regex patterns used for testing
  7. # Checks if TLS 1.3 was negotiated
  8. RE_PATTERN_HELLO_TLS_13_NORESUME = "^.*Hello TLS 1.3 \(draft .*\) _o/$|^.*Hello TLS 1.3 _o/$"
  9. # Checks if TLS 1.3 was resumed
  10. RE_PATTERN_HELLO_TLS_13_RESUME = "Hello TLS 1.3 \(draft .*\) \[resumed\] _o/"
  11. # Checks if 0-RTT was used and NOT confirmed
  12. RE_PATTERN_HELLO_0RTT = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT\] _o/$"
  13. # Checks if 0-RTT was used and confirmed
  14. RE_PATTERN_HELLO_0RTT_CONFIRMED = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT confirmed\] _o/$"
  15. class Docker(object):
  16. ''' Utility class used for starting/stoping servers and clients during tests'''
  17. def __init__(self):
  18. self.d = docker.from_env()
  19. def get_ip(self, server):
  20. tris_localserver_container = self.d.containers.get(server)
  21. return tris_localserver_container.attrs['NetworkSettings']['IPAddress']
  22. def run_client(self, image_name, cmd):
  23. ''' Runs client and returns tuple (status_code, logs) '''
  24. c = self.d.containers.create(image=image_name, command=cmd)
  25. c.start()
  26. res = c.wait()
  27. ret = c.logs()
  28. c.remove()
  29. return (res['StatusCode'], ret)
  30. def run_server(self, image_name, cmd=None, ports=None, entrypoint=None):
  31. ''' Starts server and returns docker container '''
  32. c = self.d.containers.create(image=image_name, detach=True, command=cmd, ports=ports, entrypoint=entrypoint)
  33. c.start()
  34. # TODO: maybe can be done better?
  35. time.sleep(3)
  36. return c
  37. class RegexSelfTest(unittest.TestCase):
  38. ''' Ensures that those regexe's actually work '''
  39. LINE_HELLO_TLS ="\nsomestuff\nHello TLS 1.3 _o/\nsomestuff"
  40. LINE_HELLO_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nsomestuff"
  41. LINE_HELLO_RESUMED ="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff"
  42. LINE_HELLO_MIXED ="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff"
  43. LINE_HELLO_TLS_12 ="\nsomestuff\nHello TLS 1.2 (draft 22) [resumed] _o/\nsomestuff"
  44. LINE_HELLO_TLS_13_0RTT="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT] _o/\nsomestuff"
  45. LINE_HELLO_TLS_13_0RTT_CONFIRMED="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT confirmed] _o/\nsomestuff"
  46. def test_regexes(self):
  47. self.assertIsNotNone(
  48. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, RegexSelfTest.LINE_HELLO_TLS, re.MULTILINE))
  49. self.assertIsNotNone(
  50. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, RegexSelfTest.LINE_HELLO_DRAFT_TLS, re.MULTILINE))
  51. self.assertIsNotNone(
  52. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, RegexSelfTest.LINE_HELLO_RESUMED, re.MULTILINE))
  53. self.assertIsNotNone(
  54. re.search(RE_PATTERN_HELLO_0RTT, RegexSelfTest.LINE_HELLO_TLS_13_0RTT, re.MULTILINE))
  55. self.assertIsNotNone(
  56. re.search(RE_PATTERN_HELLO_0RTT_CONFIRMED, RegexSelfTest.LINE_HELLO_TLS_13_0RTT_CONFIRMED, re.MULTILINE))
  57. # negative cases
  58. # expects 1.3, but 1.2 received
  59. self.assertIsNone(
  60. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, RegexSelfTest.LINE_HELLO_TLS_12, re.MULTILINE))
  61. # expects 0-RTT
  62. self.assertIsNone(
  63. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, RegexSelfTest.LINE_HELLO_TLS_13_0RTT, re.MULTILINE))
  64. # expectes 0-RTT confirmed
  65. self.assertIsNone(
  66. re.search(RE_PATTERN_HELLO_0RTT, RegexSelfTest.LINE_HELLO_TLS_13_0RTT_CONFIRMED, re.MULTILINE))
  67. # expects resume without 0-RTT
  68. self.assertIsNone(
  69. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, RegexSelfTest.LINE_HELLO_TLS_13_0RTT, re.MULTILINE))
  70. class InteropServer(object):
  71. ''' Instantiates TRIS as a server '''
  72. TRIS_SERVER_NAME = "tris-localserver"
  73. @classmethod
  74. def setUpClass(self):
  75. self.d = Docker()
  76. self.server = self.d.run_server(self.TRIS_SERVER_NAME)
  77. @classmethod
  78. def tearDownClass(self):
  79. self.server.kill()
  80. self.server.remove()
  81. @property
  82. def server_ip(self):
  83. return self.d.get_ip(self.server.name)
  84. # Mixins for testing server functionality
  85. class ServerNominalMixin(object):
  86. ''' Nominal tests for TLS 1.3 - client tries to perform handshake with server '''
  87. def test_rsa(self):
  88. res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":"+'1443')
  89. self.assertTrue(res[0] == 0)
  90. # Check there was TLS hello without resume
  91. self.assertIsNotNone(
  92. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, res[1], re.MULTILINE))
  93. # Check there was TLS hello with resume
  94. self.assertIsNotNone(
  95. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, res[1], re.MULTILINE))
  96. def test_ecdsa(self):
  97. res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":"+'2443')
  98. self.assertTrue(res[0] == 0)
  99. # Check there was TLS hello without resume
  100. self.assertIsNotNone(
  101. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, res[1], re.MULTILINE))
  102. # Check there was TLS hello with resume
  103. self.assertIsNotNone(
  104. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, res[1], re.MULTILINE))
  105. class ServerClientAuthMixin(object):
  106. ''' Client authentication testing '''
  107. def test_client_auth(self):
  108. args = ''.join([self.server_ip+':6443',' -key client_rsa.key -cert client_rsa.crt -debug'])
  109. res = self.d.run_client(self.CLIENT_NAME, args)
  110. self.assertEqual(res[0], 0)
  111. # Check there was TLS hello without resume
  112. self.assertIsNotNone(
  113. re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, res[1], re.MULTILINE))
  114. # Check there was TLS hello with resume
  115. self.assertIsNotNone(
  116. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, res[1], re.MULTILINE))
  117. class ClientNominalMixin(object):
  118. def test_rsa(self):
  119. res = self.d.run_client(self.CLIENT_NAME, '-ecdsa=false '+self.server_ip+":1443")
  120. self.assertEqual(res[0], 0)
  121. def test_ecdsa(self):
  122. res = self.d.run_client(self.CLIENT_NAME, '-rsa=false '+self.server_ip+":2443")
  123. self.assertEqual(res[0], 0)
  124. class ClientClientAuthMixin(object):
  125. ''' Client authentication testing - tris on client side '''
  126. def test_client_auth(self):
  127. res = self.d.run_client('tris-testclient', '-rsa=false -cliauth '+self.server_ip+":6443")
  128. self.assertTrue(res[0] == 0)
  129. class ServerZeroRttMixin(object):
  130. ''' Zero RTT testing '''
  131. def test_zero_rtt(self):
  132. # rejecting 0-RTT
  133. res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":3443")
  134. self.assertEqual(res[0], 0)
  135. self.assertIsNotNone(
  136. re.search(RE_PATTERN_HELLO_TLS_13_RESUME, res[1], re.MULTILINE))
  137. self.assertIsNone(
  138. re.search(RE_PATTERN_HELLO_0RTT, res[1], re.MULTILINE))
  139. # accepting 0-RTT
  140. res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":4443")
  141. self.assertEqual(res[0], 0)
  142. self.assertIsNotNone(
  143. re.search(RE_PATTERN_HELLO_0RTT, res[1], re.MULTILINE))
  144. # confirming 0-RTT
  145. res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":5443")
  146. self.assertEqual(res[0], 0)
  147. self.assertIsNotNone(
  148. re.search(RE_PATTERN_HELLO_0RTT_CONFIRMED, res[1], re.MULTILINE))
  149. class InteropClient(object):
  150. ''' Instantiates TRIS as a client '''
  151. CLIENT_NAME = "tris-testclient"
  152. @classmethod
  153. def setUpClass(self):
  154. self.d = Docker()
  155. self.server = self.d.run_server(
  156. self.SERVER_NAME,
  157. ports={ '1443/tcp': 1443, '2443/tcp': 2443, '6443/tcp': 6443},
  158. entrypoint="/server.sh")
  159. @classmethod
  160. def tearDownClass(self):
  161. self.server.kill()
  162. self.server.remove()
  163. @property
  164. def server_ip(self):
  165. return self.d.get_ip(self.server.name)
  166. # Actual test definition
  167. # TRIS as a server
  168. class InteropServer_BoringSSL(
  169. InteropServer,
  170. ServerNominalMixin,
  171. ServerClientAuthMixin,
  172. unittest.TestCase
  173. ): CLIENT_NAME = "tls-tris:boring"
  174. class InteropServer_PicoTLS(
  175. InteropServer,
  176. ServerNominalMixin,
  177. ServerZeroRttMixin,
  178. unittest.TestCase
  179. ): CLIENT_NAME = "tls-tris:picotls"
  180. class InteropServer_NSS(
  181. InteropServer,
  182. ServerNominalMixin,
  183. ServerZeroRttMixin,
  184. unittest.TestCase
  185. ): CLIENT_NAME = "tls-tris:tstclnt"
  186. # TRIS as a client
  187. class InteropClient_BoringSSL(
  188. InteropClient,
  189. ClientNominalMixin,
  190. ClientClientAuthMixin,
  191. unittest.TestCase
  192. ): SERVER_NAME = "boring-localserver"
  193. class InteropClient_NSS(
  194. InteropClient,
  195. ClientNominalMixin,
  196. unittest.TestCase
  197. ): SERVER_NAME = "tstclnt-localserver"
  198. # TRIS as a client
  199. class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase):
  200. CLIENT_NAME = 'tris-testclient'
  201. def test_client_auth(self):
  202. # I need to block TLS v1.2 as test server needs some rework
  203. res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=false -cliauth '+self.server_ip+":6443")
  204. self.assertEqual(res[0], 0)
  205. if __name__ == '__main__':
  206. unittest.main()