summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/regress/lib/libssl/tlsfuzzer/Makefile45
-rw-r--r--src/regress/lib/libssl/tlsfuzzer/tlsfuzzer.py736
2 files changed, 781 insertions, 0 deletions
diff --git a/src/regress/lib/libssl/tlsfuzzer/Makefile b/src/regress/lib/libssl/tlsfuzzer/Makefile
new file mode 100644
index 0000000000..224fc96c5c
--- /dev/null
+++ b/src/regress/lib/libssl/tlsfuzzer/Makefile
@@ -0,0 +1,45 @@
1# $OpenBSD: Makefile,v 1.1 2020/05/21 10:38:43 tb Exp $
2
3.if !exists(/usr/local/share/tlsfuzzer)
4regress:
5 @echo package py3-tlsfuzzer is required for this regress
6 @echo SKIPPED
7.else
8
9localhost.key localhost.crt:
10 openssl req -x509 -newkey rsa -keyout localhost.key -out localhost.crt \
11 -subj /CN=localhost -nodes -batch
12
13certs: localhost.key localhost.crt
14
15CLEANFILES += localhost.key localhost.crt
16
17PORT ?= 4433
18SLOW = -s
19TIMING = # -t
20VERBOSE = # -v
21
22all: certs
23 python3 ${.CURDIR}/tlsfuzzer.py ${SLOW} ${TIMING} ${VERBOSE}
24
25failing: certs
26 python3 ${.CURDIR}/tlsfuzzer.py -f ${SLOW} ${TIMING} ${VERBOSE}
27
28
29port: certs
30 python3 ${.CURDIR}/tlsfuzzer.py ${SLOW} ${TIMING} ${VERBOSE} -p ${PORT}
31
32list:
33 @python3 ${.CURDIR}/tlsfuzzer.py -l
34
35list-failing:
36 @python3 ${.CURDIR}/tlsfuzzer.py -l -f
37
38missing:
39 @python3 ${.CURDIR}/tlsfuzzer.py -m
40
41.PHONY: all certs failing list list-failing missing port
42
43.endif
44
45.include <bsd.regress.mk>
diff --git a/src/regress/lib/libssl/tlsfuzzer/tlsfuzzer.py b/src/regress/lib/libssl/tlsfuzzer/tlsfuzzer.py
new file mode 100644
index 0000000000..1098e049f6
--- /dev/null
+++ b/src/regress/lib/libssl/tlsfuzzer/tlsfuzzer.py
@@ -0,0 +1,736 @@
1# $OpenBSD: tlsfuzzer.py,v 1.1 2020/05/21 10:38:43 tb Exp $
2#
3# Copyright (c) 2020 Theo Buehler <tb@openbsd.org>
4#
5# Permission to use, copy, modify, and distribute this software for any
6# purpose with or without fee is hereby granted, provided that the above
7# copyright notice and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17import getopt
18import os
19import subprocess
20import sys
21from timeit import default_timer as timer
22
23tlsfuzzer_scriptdir = "/usr/local/share/tlsfuzzer/scripts/"
24
25class Test:
26 """
27 Represents a tlsfuzzer test script.
28 name: the script's name
29 args: arguments to feed to the script
30 tls12_args: override args for a TLSv1.2 server
31 tls13_args: override args for a TLSv1.3 server
32
33 XXX Add client cert support.
34 """
35 def __init__(self, name="", args=[], tls12_args=[], tls13_args=[]):
36 self.name = name
37 self.tls12_args = args
38 self.tls13_args = args
39 if tls12_args:
40 self.tls12_args = tls12_args
41 if tls13_args:
42 self.tls13_args = tls13_args
43
44 def args(self, has_tls1_3: True):
45 if has_tls1_3:
46 return self.tls13_args
47 else:
48 return self.tls12_args
49
50 def __repr__(self):
51 return "<Test: %s tls12_args: %s tls13_args: %s>" % (
52 self.name, self.tls12_args, tls13_args
53 )
54
55class TestGroup:
56 """ A group of Test objects to be run by TestRunner."""
57 def __init__(self, title="Tests", tests=[]):
58 self.title = title
59 self.tests = tests
60
61 def __iter__(self):
62 return iter(self.tests)
63
64# argument to pass to several tests
65tls13_unsupported_ciphers = [
66 "-e", "TLS 1.3 with ffdhe2048",
67 "-e", "TLS 1.3 with ffdhe3072",
68 "-e", "TLS 1.3 with secp521r1", # XXX: why is this curve problematic?
69 "-e", "TLS 1.3 with x448",
70]
71
72tls13_tests = TestGroup("TLSv1.3 tests", [
73 Test("test-tls13-ccs.py"),
74 Test("test-tls13-conversation.py"),
75 Test("test-tls13-count-tickets.py"),
76 Test("test-tls13-empty-alert.py"),
77 Test("test-tls13-finished-plaintext.py"),
78 Test("test-tls13-hrr.py"),
79 Test("test-tls13-legacy-version.py"),
80 Test("test-tls13-nociphers.py"),
81 Test("test-tls13-record-padding.py"),
82])
83
84# Tests that take a lot of time (> ~30s on an x280)
85tls13_slow_tests = TestGroup("slow TLSv1.3 tests", [
86 # XXX: Investigate the occasional message
87 # "Got shared secret with 1 most significant bytes equal to zero."
88 Test("test-tls13-dhe-shared-secret-padding.py", tls13_unsupported_ciphers),
89
90 Test("test-tls13-invalid-ciphers.py"),
91 Test("test-tls13-serverhello-random.py", tls13_unsupported_ciphers),
92])
93
94tls13_extra_cert_tests = TestGroup("TLSv1.3 certificate tests", [
95 # need to set up client certs to run these
96 Test("test-tls13-certificate-request.py"),
97 Test("test-tls13-certificate-verify.py"),
98 Test("test-tls13-ecdsa-in-certificate-verify.py"),
99
100 # Test expects the server to have installed three certificates:
101 # with P-256, P-384 and P-521 curve. Also SHA1+ECDSA is verified
102 # to not work.
103 Test("test-tls13-ecdsa-support.py"),
104])
105
106tls13_failing_tests = TestGroup("failing TLSv1.3 tests", [
107 # Some tests fail because we fail later than the scripts expect us to.
108 # With X25519, we accept weak peer public keys and fail when we actually
109 # compute the keyshare. Other tests seem to indicate that we could be
110 # stricter about what keyshares we accept.
111 Test("test-tls13-crfg-curves.py"),
112 Test("test-tls13-ecdhe-curves.py"),
113
114 # Expects a missing_extensions alert
115 # AssertionError: Unexpected message from peer: Handshake(server_hello)
116 Test("test-tls13-keyshare-omitted.py"),
117
118 # https://github.com/openssl/openssl/issues/8369
119 Test("test-tls13-obsolete-curves.py"),
120
121 # 3 failing rsa_pss_pss tests
122 Test("test-tls13-rsa-signatures.py"),
123
124 # AssertionError: Unexpected message from peer: ChangeCipherSpec()
125 # Most failing tests expect the CCS right before finished.
126 # What's up with that?
127 Test("test-tls13-version-negotiation.py"),
128
129 # The following three tests fail due to broken pipe.
130 # AssertionError: Unexpected closure from peer:
131 # 'zero-length app data'
132 # 'zero-length app data with large padding'
133 # 'zero-length app data with padding'
134 Test("test-tls13-zero-length-data.py"),
135])
136
137tls13_slow_failing_tests = TestGroup("slow, failing TLSv1.3 tests", [
138 # After adding record overflow alert, 14 tests fail because
139 # they expect ExpectNewSessionTicket().
140 Test("test-tls13-record-layer-limits.py" ),
141
142 # Other test failures bugs in keyshare/tlsext negotiation?
143 Test("test-tls13-shuffled-extentions.py"), # should reject 2nd CH
144 Test("test-tls13-unrecognised-groups.py"), # unexpected closure
145
146 # systematic failures?
147 # TLSBadRecordMAC("Invalid tag, decryption failure")
148 Test("test-tls13-keyupdate.py"),
149 Test("test-tls13-symetric-ciphers.py"), # unexpected message from peer
150
151 # 70 fail and 644 pass. For some reason the tests expect a decode_error
152 # but we send a decrypt_error after the CBS_mem_equal() fails in
153 # tls13_server_finished_recv() (which is correct).
154 Test("test-tls13-finished.py"), # decrypt_error -> decode_error?
155
156 # The following two tests fail Test (skip empty extensions for the first one):
157 # 'empty unassigned extensions, ids in range from 2 to 4118'
158 # 'unassigned extensions with random payload, ids in range from 2 to 1046'
159 Test("test-tls13-large-number-of-extensions.py"), # 2 fail: empty/random payload
160
161 # 6 tests fail: 'rsa_pkcs1_{md5,sha{1,224,256,384,512}} signature'
162 # We send server hello, but the test expects handshake_failure
163 Test("test-tls13-pkcs-signature.py"),
164 # 8 tests fail: 'tls13 signature rsa_pss_{pss,rsae}_sha{256,384,512}
165 Test("test-tls13-rsapss-signatures.py"),
166
167 # ExpectNewSessionTicket
168 Test("test-tls13-session-resumption.py"),
169])
170
171tls13_unsupported_tests = TestGroup("TLSv1.3 tests for unsupported features", [
172 # Tests for features we don't support
173 Test("test-tls13-0rtt-garbage.py"),
174 Test("test-tls13-ffdhe-groups.py"),
175 Test("test-tls13-ffdhe-sanity.py"),
176 Test("test-tls13-psk_dhe_ke.py"),
177 Test("test-tls13-psk_ke.py"),
178
179 # need server to react to HTTP GET for /keyupdate
180 Test("test-tls13-keyupdate-from-server.py"),
181
182 # Weird test: tests servers that don't support 1.3
183 Test("test-tls13-non-support.py"),
184
185 # broken test script
186 # UnboundLocalError: local variable 'cert' referenced before assignment
187 Test("test-tls13-post-handshake-auth.py"),
188
189 # Server must be configured to support only rsa_pss_rsae_sha512
190 Test("test-tls13-signature-algorithms.py"),
191])
192
193tls12_exclude_legacy_protocols = [
194 # all these have BIO_read timeouts against TLSv1.3
195 "-e", "Protocol (3, 0)",
196 "-e", "Protocol (3, 0) in SSLv2 compatible ClientHello",
197 # the following only fail with TLSv1.3
198 "-e", "Protocol (3, 1) in SSLv2 compatible ClientHello",
199 "-e", "Protocol (3, 2) in SSLv2 compatible ClientHello",
200 "-e", "Protocol (3, 3) in SSLv2 compatible ClientHello",
201 "-e", "Protocol (3, 1) with secp521r1 group", # XXX
202 "-e", "Protocol (3, 1) with x448 group",
203 "-e", "Protocol (3, 2) with secp521r1 group", # XXX
204 "-e", "Protocol (3, 2) with x448 group",
205 "-e", "Protocol (3, 3) with secp521r1 group", # XXX
206 "-e", "Protocol (3, 3) with x448 group",
207]
208
209tls12_tests = TestGroup("TLSv1.2 tests", [
210 # Tests that pass as they are.
211 Test("test-TLSv1_2-rejected-without-TLSv1_2.py"),
212 Test("test-aes-gcm-nonces.py"),
213 Test("test-chacha20.py"),
214 Test("test-conversation.py"),
215 Test("test-cve-2016-2107.py"),
216 Test("test-dhe-rsa-key-exchange.py"),
217 Test("test-early-application-data.py"),
218 Test("test-empty-extensions.py"),
219 Test("test-fuzzed-MAC.py"),
220 Test("test-fuzzed-ciphertext.py"),
221 Test("test-fuzzed-finished.py"),
222 Test("test-fuzzed-padding.py"),
223 Test("test-hello-request-by-client.py"),
224 Test("test-invalid-cipher-suites.py"),
225 Test("test-invalid-content-type.py"),
226 Test("test-invalid-session-id.py"),
227 Test("test-invalid-version.py"),
228 Test("test-message-skipping.py"),
229 Test("test-no-heartbeat.py"),
230 Test("test-sessionID-resumption.py"),
231 Test("test-sslv2-connection.py"),
232 Test("test-truncating-of-finished.py"),
233 Test("test-truncating-of-kRSA-client-key-exchange.py"),
234 Test("test-unsupported-curve-fallback.py"),
235 Test("test-version-numbers.py"),
236 Test("test-zero-length-data.py"),
237
238 # Tests that need tweaking for unsupported features and ciphers.
239 Test(
240 "test-atypical-padding.py", [
241 "-e", "sanity - encrypt then MAC",
242 "-e", "2^14 bytes of AppData with 256 bytes of padding (SHA1 + Encrypt then MAC)",
243 ]
244 ),
245 Test(
246 "test-dhe-rsa-key-exchange-signatures.py", [
247 "-e", "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA sha224 signature",
248 "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 sha224 signature",
249 "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA sha224 signature",
250 "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 sha224 signature",
251 "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA sha224 signature",
252 ]
253 ),
254 Test("test-dhe-key-share-random.py", tls12_exclude_legacy_protocols),
255 Test("test-export-ciphers-rejected.py", ["--min-ver", "TLSv1.0"]),
256 Test(
257 "test-downgrade-protection.py",
258 tls12_args = ["--server-max-protocol", "TLSv1.2"],
259 tls13_args = ["--server-max-protocol", "TLSv1.3"],
260 ),
261 Test("test-fallback-scsv.py", tls13_args = ["--tls-1.3"] ),
262 Test("test-serverhello-random.py", args = tls12_exclude_legacy_protocols),
263])
264
265tls12_slow_tests = TestGroup("slow TLSv1.2 tests", [
266 Test("test-cve-2016-7054.py"),
267 Test("test-dhe-no-shared-secret-padding.py", tls12_exclude_legacy_protocols),
268 Test("test-ecdhe-padded-shared-secret.py", tls12_exclude_legacy_protocols),
269 Test("test-ecdhe-rsa-key-share-random.py", tls12_exclude_legacy_protocols),
270 # This test has some failures once in a while.
271 Test("test-fuzzed-plaintext.py"),
272])
273
274tls12_failing_tests = TestGroup("failing TLSv1.2 tests", [
275 # no shared cipher
276 Test("test-aesccm.py"),
277 # need server to set up alpn
278 Test("test-alpn-negotiation.py"),
279 # many tests fail due to unexpected server_name extension
280 Test("test-bleichenbacher-workaround.py"),
281
282 # need client key and cert plus extra server setup
283 Test("test-certificate-malformed.py"),
284 Test("test-certificate-request.py"),
285 Test("test-certificate-verify-malformed-sig.py"),
286 Test("test-certificate-verify-malformed.py"),
287 Test("test-certificate-verify.py"),
288 Test("test-ecdsa-in-certificate-verify.py"),
289 Test("test-renegotiation-disabled-client-cert.py"),
290 Test("test-rsa-pss-sigs-on-certificate-verify.py"),
291 Test("test-rsa-sigs-on-certificate-verify.py"),
292
293 # test doesn't expect session ticket
294 Test("test-client-compatibility.py"),
295 # abrupt closure
296 Test("test-client-hello-max-size.py"),
297 # unknown signature algorithms
298 Test("test-clienthello-md5.py"),
299 # abrupt closure
300 Test("test-cve-2016-6309.py"),
301
302 # failing tests are fixed by sending illegal_parameter alert after
303 # DH_compute_keyTest() in ssl_srvr.c
304 Test("test-dhe-rsa-key-exchange-with-bad-messages.py"),
305 # Tests expect an illegal_parameter alert
306 Test("test-ecdhe-rsa-key-exchange-with-bad-messages.py"),
307
308 # We send a handshake_failure while the test expects to succeed
309 Test("test-ecdhe-rsa-key-exchange.py"),
310
311 # unsupported?
312 Test("test-extended-master-secret-extension-with-client-cert.py"),
313
314 # no shared cipher
315 Test("test-ecdsa-sig-flexibility.py"),
316
317 # unsupported
318 Test("test-encrypt-then-mac-renegotiation.py"),
319 Test("test-encrypt-then-mac.py"),
320 Test("test-extended-master-secret-extension.py"),
321 Test("test-ffdhe-negotiation.py"),
322 # unsupported. Expects the server to send the heartbeat extension
323 Test("test-heartbeat.py"),
324
325 # 29 succeed, 263 fail:
326 # 'n extensions', 'n extensions last empty' n in 4086, 4096, 8192, 16383
327 # 'fuzz ext length to n' n in [0..255] with the exception of 41...
328 Test("test-extensions.py"),
329
330 # Tests expect SH but we send unexpected_message or handshake_failure
331 # 'Application data inside Client Hello'
332 # 'Application data inside Client Key Exchange'
333 # 'Application data inside Finished'
334 Test("test-interleaved-application-data-and-fragmented-handshakes-in-renegotiation.py"),
335 # Tests expect SH but we send handshake_failure
336 # 'Application data before Change Cipher Spec'
337 # 'Application data before Client Key Exchange'
338 # 'Application data before Finished'
339 Test("test-interleaved-application-data-in-renegotiation.py"),
340
341 # broken test script
342 # TypeError: '<' not supported between instances of 'int' and 'NoneType'
343 Test("test-invalid-client-hello-w-record-overflow.py"),
344
345 # Lots of failures. abrupt closure
346 Test("test-invalid-client-hello.py"),
347
348 # Test expects illegal_parameter, we send decode_error in ssl_srvr.c:1016
349 # Need to check that this is correct.
350 Test("test-invalid-compression-methods.py"),
351
352 # abrupt closure
353 # 'encrypted premaster set to all zero (n)' n in 256 384 512
354 Test("test-invalid-rsa-key-exchange-messages.py"),
355
356 # test expects illegal_parameter, we send unrecognized_name (which seems
357 # correct according to rfc 6066?)
358 Test("test-invalid-server-name-extension-resumption.py"),
359 # let through some server names without sending an alert
360 # again illegal_parameter vs unrecognized_name
361 Test("test-invalid-server-name-extension.py"),
362
363 Test("test-large-hello.py"),
364
365 # 14 pass
366 # 7 fail
367 # 'n extensions', n in 4095, 4096, 4097, 8191, 8192, 8193, 16383,
368 Test("test-large-number-of-extensions.py"),
369
370 # 4 failures:
371 # 'insecure (legacy) renegotiation with GET after 2nd handshake'
372 # 'insecure (legacy) renegotiation with incomplete GET'
373 # 'secure renegotiation with GET after 2nd handshake'
374 # 'secure renegotiation with incomplete GET'
375 Test("test-legacy-renegotiation.py"),
376
377 # 1 failure (timeout): we don't send the unexpected_message alert
378 # 'duplicate change cipher spec after Finished'
379 Test("test-message-duplication.py"),
380
381 # server should send status_request
382 Test("test-ocsp-stapling.py"),
383
384 # unexpected closure
385 Test("test-openssl-3712.py"),
386
387 # 3 failures:
388 # 'big, needs fragmentation: max fragment - 16336B extension'
389 # 'big, needs fragmentation: max fragment - 32768B extension'
390 # 'maximum size: max fragment - 65531B extension'
391 Test("test-record-layer-fragmentation.py"),
392
393 # wants --reply-AD-size
394 Test("test-record-size-limit.py"),
395
396 # failed: 3 (expect an alert, we send AD)
397 # 'try insecure (legacy) renegotiation with incomplete GET'
398 # 'try secure renegotiation with GET after 2nd CH'
399 # 'try secure renegotiation with incomplete GET'
400 Test("test-renegotiation-disabled.py"),
401
402 # 'resumption of safe session with NULL cipher'
403 # 'resumption with cipher from old CH but not selected by server'
404 Test("test-resumption-with-wrong-ciphers.py"),
405
406 # 5 failures:
407 # 'empty sigalgs'
408 # 'only undefined sigalgs'
409 # 'rsa_pss_pss_sha256 only'
410 # 'rsa_pss_pss_sha384 only'
411 # 'rsa_pss_pss_sha512 only'
412 Test("test-sig-algs.py"),
413
414 # 13 failures:
415 # 'duplicated n non-rsa schemes' for n in 202 2342 8119 23741 32744
416 # 'empty list of signature methods'
417 # 'tolerance n RSA or ECDSA methods' for n in 215 2355 8132 23754
418 # 'tolerance 32758 methods with sig_alg_cert'
419 # 'tolerance max 32744 number of methods with sig_alg_cert'
420 # 'tolerance max (32760) number of methods'
421 Test("test-signature-algorithms.py"),
422
423 # times out
424 Test("test-ssl-death-alert.py"),
425
426 # 17 pass, 13 fail. padding and truncation
427 Test("test-truncating-of-client-hello.py"),
428
429 # x448 tests need disabling plus x25519 corner cases need sorting out
430 Test("test-x25519.py"),
431])
432
433tls12_unsupported_tests = TestGroup("TLSv1.2 for unsupported features", [
434 # protocol_version
435 Test("test-SSLv3-padding.py"),
436])
437
438# These tests take a ton of time to fail against an 1.3 server,
439# so don't run them against 1.3 pending further investigation.
440legacy_tests = TestGroup("Legacy protocol tests", [
441 Test("test-sslv2-force-cipher-3des.py"),
442 Test("test-sslv2-force-cipher-non3des.py"),
443 Test("test-sslv2-force-cipher.py"),
444 Test("test-sslv2-force-export-cipher.py"),
445 Test("test-sslv2hello-protocol.py"),
446])
447
448all_groups = [
449 tls13_tests,
450 tls13_slow_tests,
451 tls13_extra_cert_tests,
452 tls13_failing_tests,
453 tls13_slow_failing_tests,
454 tls13_unsupported_tests,
455 tls12_tests,
456 tls12_slow_tests,
457 tls12_failing_tests,
458 tls12_unsupported_tests,
459 legacy_tests,
460]
461
462failing_groups = [
463 tls13_failing_tests,
464 tls13_slow_failing_tests,
465 tls12_failing_tests,
466]
467
468class TestRunner:
469 """ Runs the given tests troups against a server and displays stats. """
470
471 def __init__(
472 self, timing=False, verbose=False, port=4433, use_tls1_3=True,
473 dry_run=False, tests=[], scriptdir=tlsfuzzer_scriptdir,
474 ):
475 self.tests = []
476
477 self.dryrun = dry_run
478 self.use_tls1_3 = use_tls1_3
479 self.port = str(port)
480 self.scriptdir = scriptdir
481
482 self.stats = []
483 self.failed = []
484
485 self.timing = timing
486 self.verbose = verbose
487
488 def add(self, title="tests", tests=[]):
489 # tests.sort(key=lambda test: test.name)
490 self.tests.append(TestGroup(title, tests))
491
492 def add_group(self, group):
493 self.tests.append(group)
494
495 def run_script(self, test):
496 script = test.name
497 args = ["-p"] + [self.port] + test.args(self.use_tls1_3)
498
499 if self.dryrun:
500 if not self.verbose:
501 args = []
502 print(script , end=' ' if args else '')
503 print(' '.join([f"\"{arg}\"" for arg in args]))
504 return
505
506 if self.verbose:
507 print(script)
508 else:
509 print(f"{script[:68]:<72}", end=" ", flush=True)
510 start = timer()
511 test = subprocess.run(
512 ["python3", os.path.join(self.scriptdir, script)] + args,
513 capture_output=not self.verbose,
514 text=True,
515 )
516 end = timer()
517 self.stats.append((script, end - start))
518 if test.returncode == 0:
519 print("OK")
520 return
521 print("FAILED")
522 self.failed.append(script)
523
524 if self.verbose:
525 return
526
527 print('\n'.join(test.stdout.split("Test end\n", 1)[1:]), end="")
528
529 def run(self):
530 for group in self:
531 print(f"Running {group.title} ...")
532 for test in group:
533 self.run_script(test)
534 return not self.failed
535
536 def __iter__(self):
537 return iter(self.tests)
538
539 def __del__(self):
540 if self.timing and self.stats:
541 total = 0.0
542 for (script, time) in self.stats:
543 print(f"{round(time, 2):6.2f} {script}")
544 total += time
545 print(f"{round(total, 2):6.2f} total")
546
547 if self.failed:
548 print("Failed tests:")
549 print('\n'.join(self.failed))
550
551class TlsServer:
552 """ Spawns an s_server listening on localhost:port if necessary. """
553
554 def __init__(self, port=4433):
555 self.spawn = True
556 # Check whether a server is already listening on localhost:port
557 self.spawn = subprocess.run(
558 ["nc", "-c", "-z", "-T", "noverify", "localhost", str(port)],
559 stderr=subprocess.DEVNULL,
560 ).returncode != 0
561
562 if self.spawn:
563 self.server = subprocess.Popen(
564 [
565 "openssl",
566 "s_server",
567 "-accept",
568 str(port),
569 "-key",
570 "localhost.key",
571 "-cert",
572 "localhost.crt",
573 "-www",
574 ],
575 stdout=subprocess.DEVNULL,
576 stderr=subprocess.PIPE,
577 text=True,
578 )
579
580 # Check whether the server talks TLSv1.3
581 self.has_tls1_3 = subprocess.run(
582 [
583 "nc",
584 "-c",
585 "-z",
586 "-T",
587 "noverify",
588 "-T",
589 "protocols=TLSv1.3",
590 "localhost",
591 str(port),
592 ],
593 stderr=subprocess.DEVNULL,
594 ).returncode == 0
595
596 self.check()
597
598 def check(self):
599 if self.spawn and self.server.poll() is not None:
600 print(self.server.stderr.read())
601 raise RuntimeError(
602 f"openssl s_server died. Return code: {self.server.returncode}."
603 )
604 if self.spawn:
605 self.server.stderr.detach()
606
607 def __del__(self):
608 if self.spawn:
609 self.server.terminate()
610
611# Extract the arguments we pass to script
612def defaultargs(script, has_tls1_3):
613 return next(
614 (test for group in all_groups for test in group if test.name == script),
615 Test()
616 ).args(has_tls1_3)
617
618def list_or_missing(missing=True):
619 tests = [test.name for group in all_groups for test in group]
620
621 if missing:
622 scripts = {
623 f for f in os.listdir(tlsfuzzer_scriptdir) if f != "__pycache__"
624 }
625 missing = scripts - set(tests)
626 if missing:
627 print('\n'.join(sorted(missing)))
628 exit(0)
629
630 tests.sort()
631 print('\n'.join(tests))
632 exit(0)
633
634def usage():
635 print("Usage: python3 tlsfuzzer.py [-lmnstv] [-p port] [script [test...]]")
636 print(" --help help")
637 print(" -f run failing tests")
638 print(" -l list tests")
639 print(" -m list new tests after package update")
640 print(" -n do not run tests, but list the ones that would be run")
641 print(" -p port connect to this port - defaults to 4433")
642 print(" -s run slow tests")
643 print(" -t show timing stats at end")
644 print(" -v verbose output")
645 exit(0)
646
647def main():
648 failing = False
649 list = False
650 missing = False
651 dryrun = False
652 port = 4433
653 slow = False
654 timing = False
655 verbose = False
656
657 argv = sys.argv[1:]
658 opts, args = getopt.getopt(argv, "flmnp:stv", ["help"])
659 for opt, arg in opts:
660 if opt == '--help':
661 usage()
662 elif opt == '-f':
663 failing = True
664 elif opt == '-l':
665 list = True
666 elif opt == '-m':
667 missing = True
668 elif opt == '-n':
669 dryrun = True
670 elif opt == '-p':
671 port = int(arg)
672 elif opt == '-s':
673 slow = True
674 elif opt == '-t':
675 timing = True
676 elif opt == '-v':
677 verbose = True
678 else:
679 raise ValueError(f"Unknown option: {opt}")
680
681 if not os.path.exists(tlsfuzzer_scriptdir):
682 print("package py3-tlsfuzzer is required for this regress")
683 exit(1)
684
685 if list and failing:
686 failing = [test.name for group in failing_groups for test in group]
687 failing.sort()
688 print('\n'.join(failing))
689 exit(0)
690
691 if list or missing:
692 list_or_missing(missing)
693
694 tls_server = TlsServer(port)
695
696 tests = TestRunner(timing, verbose, port, tls_server.has_tls1_3, dryrun)
697
698 if args:
699 (dir, script) = os.path.split(args[0])
700 if dir and not dir == '.':
701 tests.scriptdir = dir
702
703 testargs = defaultargs(script, tls_server.has_tls1_3)
704
705 tests.verbose = True
706 tests.add("test from command line", [Test(script, testargs + args[1:])])
707
708 exit(not tests.run())
709
710 if failing:
711 if tls_server.has_tls1_3:
712 tests.add_group(tls13_failing_tests)
713 if slow:
714 tests.add_group(tls13_slow_failing_tests)
715 tests.add_group(tls12_failing_tests)
716
717 if tls_server.has_tls1_3:
718 tests.add_group(tls13_tests)
719 if slow:
720 tests.add_group(tls13_slow_tests)
721 else:
722 tests.add_group(legacy_tests)
723
724 tests.add_group(tls12_tests)
725 if slow:
726 tests.add_group(tls12_slow_tests)
727
728 success = tests.run()
729 del tests
730
731 if not success:
732 print("FAILED")
733 exit(1)
734
735if __name__ == "__main__":
736 main()