1*071b89a8Stb# $OpenBSD: tlsfuzzer.py,v 1.2 2020/05/21 19:08:32 tb Exp $ 258007204Stb# 358007204Stb# Copyright (c) 2020 Theo Buehler <tb@openbsd.org> 458007204Stb# 558007204Stb# Permission to use, copy, modify, and distribute this software for any 658007204Stb# purpose with or without fee is hereby granted, provided that the above 758007204Stb# copyright notice and this permission notice appear in all copies. 858007204Stb# 958007204Stb# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1058007204Stb# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1158007204Stb# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1258007204Stb# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1358007204Stb# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1458007204Stb# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1558007204Stb# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1658007204Stb 1758007204Stbimport getopt 1858007204Stbimport os 1958007204Stbimport subprocess 2058007204Stbimport sys 2158007204Stbfrom timeit import default_timer as timer 2258007204Stb 2358007204Stbtlsfuzzer_scriptdir = "/usr/local/share/tlsfuzzer/scripts/" 2458007204Stb 2558007204Stbclass Test: 2658007204Stb """ 2758007204Stb Represents a tlsfuzzer test script. 2858007204Stb name: the script's name 2958007204Stb args: arguments to feed to the script 3058007204Stb tls12_args: override args for a TLSv1.2 server 3158007204Stb tls13_args: override args for a TLSv1.3 server 3258007204Stb 3358007204Stb XXX Add client cert support. 3458007204Stb """ 3558007204Stb def __init__(self, name="", args=[], tls12_args=[], tls13_args=[]): 3658007204Stb self.name = name 3758007204Stb self.tls12_args = args 3858007204Stb self.tls13_args = args 3958007204Stb if tls12_args: 4058007204Stb self.tls12_args = tls12_args 4158007204Stb if tls13_args: 4258007204Stb self.tls13_args = tls13_args 4358007204Stb 4458007204Stb def args(self, has_tls1_3: True): 4558007204Stb if has_tls1_3: 4658007204Stb return self.tls13_args 4758007204Stb else: 4858007204Stb return self.tls12_args 4958007204Stb 5058007204Stb def __repr__(self): 5158007204Stb return "<Test: %s tls12_args: %s tls13_args: %s>" % ( 5258007204Stb self.name, self.tls12_args, tls13_args 5358007204Stb ) 5458007204Stb 5558007204Stbclass TestGroup: 5658007204Stb """ A group of Test objects to be run by TestRunner.""" 5758007204Stb def __init__(self, title="Tests", tests=[]): 5858007204Stb self.title = title 5958007204Stb self.tests = tests 6058007204Stb 6158007204Stb def __iter__(self): 6258007204Stb return iter(self.tests) 6358007204Stb 6458007204Stb# argument to pass to several tests 6558007204Stbtls13_unsupported_ciphers = [ 6658007204Stb "-e", "TLS 1.3 with ffdhe2048", 6758007204Stb "-e", "TLS 1.3 with ffdhe3072", 6858007204Stb "-e", "TLS 1.3 with secp521r1", # XXX: why is this curve problematic? 6958007204Stb "-e", "TLS 1.3 with x448", 7058007204Stb] 7158007204Stb 7258007204Stbtls13_tests = TestGroup("TLSv1.3 tests", [ 7358007204Stb Test("test-tls13-ccs.py"), 7458007204Stb Test("test-tls13-conversation.py"), 7558007204Stb Test("test-tls13-count-tickets.py"), 7658007204Stb Test("test-tls13-empty-alert.py"), 7758007204Stb Test("test-tls13-finished-plaintext.py"), 7858007204Stb Test("test-tls13-hrr.py"), 7958007204Stb Test("test-tls13-legacy-version.py"), 8058007204Stb Test("test-tls13-nociphers.py"), 8158007204Stb Test("test-tls13-record-padding.py"), 8258007204Stb]) 8358007204Stb 8458007204Stb# Tests that take a lot of time (> ~30s on an x280) 8558007204Stbtls13_slow_tests = TestGroup("slow TLSv1.3 tests", [ 8658007204Stb # XXX: Investigate the occasional message 8758007204Stb # "Got shared secret with 1 most significant bytes equal to zero." 8858007204Stb Test("test-tls13-dhe-shared-secret-padding.py", tls13_unsupported_ciphers), 8958007204Stb 9058007204Stb Test("test-tls13-invalid-ciphers.py"), 9158007204Stb Test("test-tls13-serverhello-random.py", tls13_unsupported_ciphers), 9258007204Stb]) 9358007204Stb 9458007204Stbtls13_extra_cert_tests = TestGroup("TLSv1.3 certificate tests", [ 9558007204Stb # need to set up client certs to run these 9658007204Stb Test("test-tls13-certificate-request.py"), 9758007204Stb Test("test-tls13-certificate-verify.py"), 9858007204Stb Test("test-tls13-ecdsa-in-certificate-verify.py"), 9958007204Stb 10058007204Stb # Test expects the server to have installed three certificates: 10158007204Stb # with P-256, P-384 and P-521 curve. Also SHA1+ECDSA is verified 10258007204Stb # to not work. 10358007204Stb Test("test-tls13-ecdsa-support.py"), 10458007204Stb]) 10558007204Stb 10658007204Stbtls13_failing_tests = TestGroup("failing TLSv1.3 tests", [ 10758007204Stb # Some tests fail because we fail later than the scripts expect us to. 10858007204Stb # With X25519, we accept weak peer public keys and fail when we actually 10958007204Stb # compute the keyshare. Other tests seem to indicate that we could be 11058007204Stb # stricter about what keyshares we accept. 11158007204Stb Test("test-tls13-crfg-curves.py"), 11258007204Stb Test("test-tls13-ecdhe-curves.py"), 11358007204Stb 11458007204Stb # Expects a missing_extensions alert 11558007204Stb # AssertionError: Unexpected message from peer: Handshake(server_hello) 11658007204Stb Test("test-tls13-keyshare-omitted.py"), 11758007204Stb 11858007204Stb # https://github.com/openssl/openssl/issues/8369 11958007204Stb Test("test-tls13-obsolete-curves.py"), 12058007204Stb 12158007204Stb # 3 failing rsa_pss_pss tests 12258007204Stb Test("test-tls13-rsa-signatures.py"), 12358007204Stb 12458007204Stb # AssertionError: Unexpected message from peer: ChangeCipherSpec() 12558007204Stb # Most failing tests expect the CCS right before finished. 12658007204Stb # What's up with that? 12758007204Stb Test("test-tls13-version-negotiation.py"), 12858007204Stb 12958007204Stb # The following three tests fail due to broken pipe. 13058007204Stb # AssertionError: Unexpected closure from peer: 13158007204Stb # 'zero-length app data' 13258007204Stb # 'zero-length app data with large padding' 13358007204Stb # 'zero-length app data with padding' 13458007204Stb Test("test-tls13-zero-length-data.py"), 13558007204Stb]) 13658007204Stb 13758007204Stbtls13_slow_failing_tests = TestGroup("slow, failing TLSv1.3 tests", [ 13858007204Stb # After adding record overflow alert, 14 tests fail because 13958007204Stb # they expect ExpectNewSessionTicket(). 14058007204Stb Test("test-tls13-record-layer-limits.py" ), 14158007204Stb 14258007204Stb # Other test failures bugs in keyshare/tlsext negotiation? 14358007204Stb Test("test-tls13-shuffled-extentions.py"), # should reject 2nd CH 14458007204Stb Test("test-tls13-unrecognised-groups.py"), # unexpected closure 14558007204Stb 146*071b89a8Stb # 5 failures: 147*071b89a8Stb # 'app data split, conversation with KeyUpdate msg' 148*071b89a8Stb # 'fragmented keyupdate msg' 149*071b89a8Stb # 'multiple KeyUpdate messages' 150*071b89a8Stb # 'post-handshake KeyUpdate msg with update_not_request' 151*071b89a8Stb # 'post-handshake KeyUpdate msg with update_request' 15258007204Stb Test("test-tls13-keyupdate.py"), 153*071b89a8Stb 15458007204Stb Test("test-tls13-symetric-ciphers.py"), # unexpected message from peer 15558007204Stb 15658007204Stb # 70 fail and 644 pass. For some reason the tests expect a decode_error 15758007204Stb # but we send a decrypt_error after the CBS_mem_equal() fails in 15858007204Stb # tls13_server_finished_recv() (which is correct). 15958007204Stb Test("test-tls13-finished.py"), # decrypt_error -> decode_error? 16058007204Stb 16158007204Stb # The following two tests fail Test (skip empty extensions for the first one): 16258007204Stb # 'empty unassigned extensions, ids in range from 2 to 4118' 16358007204Stb # 'unassigned extensions with random payload, ids in range from 2 to 1046' 16458007204Stb Test("test-tls13-large-number-of-extensions.py"), # 2 fail: empty/random payload 16558007204Stb 16658007204Stb # 6 tests fail: 'rsa_pkcs1_{md5,sha{1,224,256,384,512}} signature' 16758007204Stb # We send server hello, but the test expects handshake_failure 16858007204Stb Test("test-tls13-pkcs-signature.py"), 16958007204Stb # 8 tests fail: 'tls13 signature rsa_pss_{pss,rsae}_sha{256,384,512} 17058007204Stb Test("test-tls13-rsapss-signatures.py"), 17158007204Stb 17258007204Stb # ExpectNewSessionTicket 17358007204Stb Test("test-tls13-session-resumption.py"), 17458007204Stb]) 17558007204Stb 17658007204Stbtls13_unsupported_tests = TestGroup("TLSv1.3 tests for unsupported features", [ 17758007204Stb # Tests for features we don't support 17858007204Stb Test("test-tls13-0rtt-garbage.py"), 17958007204Stb Test("test-tls13-ffdhe-groups.py"), 18058007204Stb Test("test-tls13-ffdhe-sanity.py"), 18158007204Stb Test("test-tls13-psk_dhe_ke.py"), 18258007204Stb Test("test-tls13-psk_ke.py"), 18358007204Stb 18458007204Stb # need server to react to HTTP GET for /keyupdate 18558007204Stb Test("test-tls13-keyupdate-from-server.py"), 18658007204Stb 18758007204Stb # Weird test: tests servers that don't support 1.3 18858007204Stb Test("test-tls13-non-support.py"), 18958007204Stb 19058007204Stb # broken test script 19158007204Stb # UnboundLocalError: local variable 'cert' referenced before assignment 19258007204Stb Test("test-tls13-post-handshake-auth.py"), 19358007204Stb 19458007204Stb # Server must be configured to support only rsa_pss_rsae_sha512 19558007204Stb Test("test-tls13-signature-algorithms.py"), 19658007204Stb]) 19758007204Stb 19858007204Stbtls12_exclude_legacy_protocols = [ 19958007204Stb # all these have BIO_read timeouts against TLSv1.3 20058007204Stb "-e", "Protocol (3, 0)", 20158007204Stb "-e", "Protocol (3, 0) in SSLv2 compatible ClientHello", 20258007204Stb # the following only fail with TLSv1.3 20358007204Stb "-e", "Protocol (3, 1) in SSLv2 compatible ClientHello", 20458007204Stb "-e", "Protocol (3, 2) in SSLv2 compatible ClientHello", 20558007204Stb "-e", "Protocol (3, 3) in SSLv2 compatible ClientHello", 20658007204Stb "-e", "Protocol (3, 1) with secp521r1 group", # XXX 20758007204Stb "-e", "Protocol (3, 1) with x448 group", 20858007204Stb "-e", "Protocol (3, 2) with secp521r1 group", # XXX 20958007204Stb "-e", "Protocol (3, 2) with x448 group", 21058007204Stb "-e", "Protocol (3, 3) with secp521r1 group", # XXX 21158007204Stb "-e", "Protocol (3, 3) with x448 group", 21258007204Stb] 21358007204Stb 21458007204Stbtls12_tests = TestGroup("TLSv1.2 tests", [ 21558007204Stb # Tests that pass as they are. 21658007204Stb Test("test-TLSv1_2-rejected-without-TLSv1_2.py"), 21758007204Stb Test("test-aes-gcm-nonces.py"), 21858007204Stb Test("test-chacha20.py"), 21958007204Stb Test("test-conversation.py"), 22058007204Stb Test("test-cve-2016-2107.py"), 22158007204Stb Test("test-dhe-rsa-key-exchange.py"), 22258007204Stb Test("test-early-application-data.py"), 22358007204Stb Test("test-empty-extensions.py"), 22458007204Stb Test("test-fuzzed-MAC.py"), 22558007204Stb Test("test-fuzzed-ciphertext.py"), 22658007204Stb Test("test-fuzzed-finished.py"), 22758007204Stb Test("test-fuzzed-padding.py"), 22858007204Stb Test("test-hello-request-by-client.py"), 22958007204Stb Test("test-invalid-cipher-suites.py"), 23058007204Stb Test("test-invalid-content-type.py"), 23158007204Stb Test("test-invalid-session-id.py"), 23258007204Stb Test("test-invalid-version.py"), 23358007204Stb Test("test-message-skipping.py"), 23458007204Stb Test("test-no-heartbeat.py"), 23558007204Stb Test("test-sessionID-resumption.py"), 23658007204Stb Test("test-sslv2-connection.py"), 23758007204Stb Test("test-truncating-of-finished.py"), 23858007204Stb Test("test-truncating-of-kRSA-client-key-exchange.py"), 23958007204Stb Test("test-unsupported-curve-fallback.py"), 24058007204Stb Test("test-version-numbers.py"), 24158007204Stb Test("test-zero-length-data.py"), 24258007204Stb 24358007204Stb # Tests that need tweaking for unsupported features and ciphers. 24458007204Stb Test( 24558007204Stb "test-atypical-padding.py", [ 24658007204Stb "-e", "sanity - encrypt then MAC", 24758007204Stb "-e", "2^14 bytes of AppData with 256 bytes of padding (SHA1 + Encrypt then MAC)", 24858007204Stb ] 24958007204Stb ), 25058007204Stb Test( 25158007204Stb "test-dhe-rsa-key-exchange-signatures.py", [ 25258007204Stb "-e", "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA sha224 signature", 25358007204Stb "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 sha224 signature", 25458007204Stb "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA sha224 signature", 25558007204Stb "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 sha224 signature", 25658007204Stb "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA sha224 signature", 25758007204Stb ] 25858007204Stb ), 25958007204Stb Test("test-dhe-key-share-random.py", tls12_exclude_legacy_protocols), 26058007204Stb Test("test-export-ciphers-rejected.py", ["--min-ver", "TLSv1.0"]), 26158007204Stb Test( 26258007204Stb "test-downgrade-protection.py", 26358007204Stb tls12_args = ["--server-max-protocol", "TLSv1.2"], 26458007204Stb tls13_args = ["--server-max-protocol", "TLSv1.3"], 26558007204Stb ), 26658007204Stb Test("test-fallback-scsv.py", tls13_args = ["--tls-1.3"] ), 26758007204Stb Test("test-serverhello-random.py", args = tls12_exclude_legacy_protocols), 26858007204Stb]) 26958007204Stb 27058007204Stbtls12_slow_tests = TestGroup("slow TLSv1.2 tests", [ 27158007204Stb Test("test-cve-2016-7054.py"), 27258007204Stb Test("test-dhe-no-shared-secret-padding.py", tls12_exclude_legacy_protocols), 27358007204Stb Test("test-ecdhe-padded-shared-secret.py", tls12_exclude_legacy_protocols), 27458007204Stb Test("test-ecdhe-rsa-key-share-random.py", tls12_exclude_legacy_protocols), 27558007204Stb # This test has some failures once in a while. 27658007204Stb Test("test-fuzzed-plaintext.py"), 27758007204Stb]) 27858007204Stb 27958007204Stbtls12_failing_tests = TestGroup("failing TLSv1.2 tests", [ 28058007204Stb # no shared cipher 28158007204Stb Test("test-aesccm.py"), 28258007204Stb # need server to set up alpn 28358007204Stb Test("test-alpn-negotiation.py"), 28458007204Stb # many tests fail due to unexpected server_name extension 28558007204Stb Test("test-bleichenbacher-workaround.py"), 28658007204Stb 28758007204Stb # need client key and cert plus extra server setup 28858007204Stb Test("test-certificate-malformed.py"), 28958007204Stb Test("test-certificate-request.py"), 29058007204Stb Test("test-certificate-verify-malformed-sig.py"), 29158007204Stb Test("test-certificate-verify-malformed.py"), 29258007204Stb Test("test-certificate-verify.py"), 29358007204Stb Test("test-ecdsa-in-certificate-verify.py"), 29458007204Stb Test("test-renegotiation-disabled-client-cert.py"), 29558007204Stb Test("test-rsa-pss-sigs-on-certificate-verify.py"), 29658007204Stb Test("test-rsa-sigs-on-certificate-verify.py"), 29758007204Stb 29858007204Stb # test doesn't expect session ticket 29958007204Stb Test("test-client-compatibility.py"), 30058007204Stb # abrupt closure 30158007204Stb Test("test-client-hello-max-size.py"), 30258007204Stb # unknown signature algorithms 30358007204Stb Test("test-clienthello-md5.py"), 30458007204Stb # abrupt closure 30558007204Stb Test("test-cve-2016-6309.py"), 30658007204Stb 30758007204Stb # failing tests are fixed by sending illegal_parameter alert after 30858007204Stb # DH_compute_keyTest() in ssl_srvr.c 30958007204Stb Test("test-dhe-rsa-key-exchange-with-bad-messages.py"), 31058007204Stb # Tests expect an illegal_parameter alert 31158007204Stb Test("test-ecdhe-rsa-key-exchange-with-bad-messages.py"), 31258007204Stb 31358007204Stb # We send a handshake_failure while the test expects to succeed 31458007204Stb Test("test-ecdhe-rsa-key-exchange.py"), 31558007204Stb 31658007204Stb # unsupported? 31758007204Stb Test("test-extended-master-secret-extension-with-client-cert.py"), 31858007204Stb 31958007204Stb # no shared cipher 32058007204Stb Test("test-ecdsa-sig-flexibility.py"), 32158007204Stb 32258007204Stb # unsupported 32358007204Stb Test("test-encrypt-then-mac-renegotiation.py"), 32458007204Stb Test("test-encrypt-then-mac.py"), 32558007204Stb Test("test-extended-master-secret-extension.py"), 32658007204Stb Test("test-ffdhe-negotiation.py"), 32758007204Stb # unsupported. Expects the server to send the heartbeat extension 32858007204Stb Test("test-heartbeat.py"), 32958007204Stb 33058007204Stb # 29 succeed, 263 fail: 33158007204Stb # 'n extensions', 'n extensions last empty' n in 4086, 4096, 8192, 16383 33258007204Stb # 'fuzz ext length to n' n in [0..255] with the exception of 41... 33358007204Stb Test("test-extensions.py"), 33458007204Stb 33558007204Stb # Tests expect SH but we send unexpected_message or handshake_failure 33658007204Stb # 'Application data inside Client Hello' 33758007204Stb # 'Application data inside Client Key Exchange' 33858007204Stb # 'Application data inside Finished' 33958007204Stb Test("test-interleaved-application-data-and-fragmented-handshakes-in-renegotiation.py"), 34058007204Stb # Tests expect SH but we send handshake_failure 34158007204Stb # 'Application data before Change Cipher Spec' 34258007204Stb # 'Application data before Client Key Exchange' 34358007204Stb # 'Application data before Finished' 34458007204Stb Test("test-interleaved-application-data-in-renegotiation.py"), 34558007204Stb 34658007204Stb # broken test script 34758007204Stb # TypeError: '<' not supported between instances of 'int' and 'NoneType' 34858007204Stb Test("test-invalid-client-hello-w-record-overflow.py"), 34958007204Stb 35058007204Stb # Lots of failures. abrupt closure 35158007204Stb Test("test-invalid-client-hello.py"), 35258007204Stb 35358007204Stb # Test expects illegal_parameter, we send decode_error in ssl_srvr.c:1016 35458007204Stb # Need to check that this is correct. 35558007204Stb Test("test-invalid-compression-methods.py"), 35658007204Stb 35758007204Stb # abrupt closure 35858007204Stb # 'encrypted premaster set to all zero (n)' n in 256 384 512 35958007204Stb Test("test-invalid-rsa-key-exchange-messages.py"), 36058007204Stb 36158007204Stb # test expects illegal_parameter, we send unrecognized_name (which seems 36258007204Stb # correct according to rfc 6066?) 36358007204Stb Test("test-invalid-server-name-extension-resumption.py"), 36458007204Stb # let through some server names without sending an alert 36558007204Stb # again illegal_parameter vs unrecognized_name 36658007204Stb Test("test-invalid-server-name-extension.py"), 36758007204Stb 36858007204Stb Test("test-large-hello.py"), 36958007204Stb 37058007204Stb # 14 pass 37158007204Stb # 7 fail 37258007204Stb # 'n extensions', n in 4095, 4096, 4097, 8191, 8192, 8193, 16383, 37358007204Stb Test("test-large-number-of-extensions.py"), 37458007204Stb 37558007204Stb # 4 failures: 37658007204Stb # 'insecure (legacy) renegotiation with GET after 2nd handshake' 37758007204Stb # 'insecure (legacy) renegotiation with incomplete GET' 37858007204Stb # 'secure renegotiation with GET after 2nd handshake' 37958007204Stb # 'secure renegotiation with incomplete GET' 38058007204Stb Test("test-legacy-renegotiation.py"), 38158007204Stb 38258007204Stb # 1 failure (timeout): we don't send the unexpected_message alert 38358007204Stb # 'duplicate change cipher spec after Finished' 38458007204Stb Test("test-message-duplication.py"), 38558007204Stb 38658007204Stb # server should send status_request 38758007204Stb Test("test-ocsp-stapling.py"), 38858007204Stb 38958007204Stb # unexpected closure 39058007204Stb Test("test-openssl-3712.py"), 39158007204Stb 39258007204Stb # 3 failures: 39358007204Stb # 'big, needs fragmentation: max fragment - 16336B extension' 39458007204Stb # 'big, needs fragmentation: max fragment - 32768B extension' 39558007204Stb # 'maximum size: max fragment - 65531B extension' 39658007204Stb Test("test-record-layer-fragmentation.py"), 39758007204Stb 39858007204Stb # wants --reply-AD-size 39958007204Stb Test("test-record-size-limit.py"), 40058007204Stb 40158007204Stb # failed: 3 (expect an alert, we send AD) 40258007204Stb # 'try insecure (legacy) renegotiation with incomplete GET' 40358007204Stb # 'try secure renegotiation with GET after 2nd CH' 40458007204Stb # 'try secure renegotiation with incomplete GET' 40558007204Stb Test("test-renegotiation-disabled.py"), 40658007204Stb 40758007204Stb # 'resumption of safe session with NULL cipher' 40858007204Stb # 'resumption with cipher from old CH but not selected by server' 40958007204Stb Test("test-resumption-with-wrong-ciphers.py"), 41058007204Stb 41158007204Stb # 5 failures: 41258007204Stb # 'empty sigalgs' 41358007204Stb # 'only undefined sigalgs' 41458007204Stb # 'rsa_pss_pss_sha256 only' 41558007204Stb # 'rsa_pss_pss_sha384 only' 41658007204Stb # 'rsa_pss_pss_sha512 only' 41758007204Stb Test("test-sig-algs.py"), 41858007204Stb 41958007204Stb # 13 failures: 42058007204Stb # 'duplicated n non-rsa schemes' for n in 202 2342 8119 23741 32744 42158007204Stb # 'empty list of signature methods' 42258007204Stb # 'tolerance n RSA or ECDSA methods' for n in 215 2355 8132 23754 42358007204Stb # 'tolerance 32758 methods with sig_alg_cert' 42458007204Stb # 'tolerance max 32744 number of methods with sig_alg_cert' 42558007204Stb # 'tolerance max (32760) number of methods' 42658007204Stb Test("test-signature-algorithms.py"), 42758007204Stb 42858007204Stb # times out 42958007204Stb Test("test-ssl-death-alert.py"), 43058007204Stb 43158007204Stb # 17 pass, 13 fail. padding and truncation 43258007204Stb Test("test-truncating-of-client-hello.py"), 43358007204Stb 43458007204Stb # x448 tests need disabling plus x25519 corner cases need sorting out 43558007204Stb Test("test-x25519.py"), 43658007204Stb]) 43758007204Stb 43858007204Stbtls12_unsupported_tests = TestGroup("TLSv1.2 for unsupported features", [ 43958007204Stb # protocol_version 44058007204Stb Test("test-SSLv3-padding.py"), 44158007204Stb]) 44258007204Stb 44358007204Stb# These tests take a ton of time to fail against an 1.3 server, 44458007204Stb# so don't run them against 1.3 pending further investigation. 44558007204Stblegacy_tests = TestGroup("Legacy protocol tests", [ 44658007204Stb Test("test-sslv2-force-cipher-3des.py"), 44758007204Stb Test("test-sslv2-force-cipher-non3des.py"), 44858007204Stb Test("test-sslv2-force-cipher.py"), 44958007204Stb Test("test-sslv2-force-export-cipher.py"), 45058007204Stb Test("test-sslv2hello-protocol.py"), 45158007204Stb]) 45258007204Stb 45358007204Stball_groups = [ 45458007204Stb tls13_tests, 45558007204Stb tls13_slow_tests, 45658007204Stb tls13_extra_cert_tests, 45758007204Stb tls13_failing_tests, 45858007204Stb tls13_slow_failing_tests, 45958007204Stb tls13_unsupported_tests, 46058007204Stb tls12_tests, 46158007204Stb tls12_slow_tests, 46258007204Stb tls12_failing_tests, 46358007204Stb tls12_unsupported_tests, 46458007204Stb legacy_tests, 46558007204Stb] 46658007204Stb 46758007204Stbfailing_groups = [ 46858007204Stb tls13_failing_tests, 46958007204Stb tls13_slow_failing_tests, 47058007204Stb tls12_failing_tests, 47158007204Stb] 47258007204Stb 47358007204Stbclass TestRunner: 47458007204Stb """ Runs the given tests troups against a server and displays stats. """ 47558007204Stb 47658007204Stb def __init__( 47758007204Stb self, timing=False, verbose=False, port=4433, use_tls1_3=True, 47858007204Stb dry_run=False, tests=[], scriptdir=tlsfuzzer_scriptdir, 47958007204Stb ): 48058007204Stb self.tests = [] 48158007204Stb 48258007204Stb self.dryrun = dry_run 48358007204Stb self.use_tls1_3 = use_tls1_3 48458007204Stb self.port = str(port) 48558007204Stb self.scriptdir = scriptdir 48658007204Stb 48758007204Stb self.stats = [] 48858007204Stb self.failed = [] 48958007204Stb 49058007204Stb self.timing = timing 49158007204Stb self.verbose = verbose 49258007204Stb 49358007204Stb def add(self, title="tests", tests=[]): 49458007204Stb # tests.sort(key=lambda test: test.name) 49558007204Stb self.tests.append(TestGroup(title, tests)) 49658007204Stb 49758007204Stb def add_group(self, group): 49858007204Stb self.tests.append(group) 49958007204Stb 50058007204Stb def run_script(self, test): 50158007204Stb script = test.name 50258007204Stb args = ["-p"] + [self.port] + test.args(self.use_tls1_3) 50358007204Stb 50458007204Stb if self.dryrun: 50558007204Stb if not self.verbose: 50658007204Stb args = [] 50758007204Stb print(script , end=' ' if args else '') 50858007204Stb print(' '.join([f"\"{arg}\"" for arg in args])) 50958007204Stb return 51058007204Stb 51158007204Stb if self.verbose: 51258007204Stb print(script) 51358007204Stb else: 51458007204Stb print(f"{script[:68]:<72}", end=" ", flush=True) 51558007204Stb start = timer() 51658007204Stb test = subprocess.run( 51758007204Stb ["python3", os.path.join(self.scriptdir, script)] + args, 51858007204Stb capture_output=not self.verbose, 51958007204Stb text=True, 52058007204Stb ) 52158007204Stb end = timer() 52258007204Stb self.stats.append((script, end - start)) 52358007204Stb if test.returncode == 0: 52458007204Stb print("OK") 52558007204Stb return 52658007204Stb print("FAILED") 52758007204Stb self.failed.append(script) 52858007204Stb 52958007204Stb if self.verbose: 53058007204Stb return 53158007204Stb 53258007204Stb print('\n'.join(test.stdout.split("Test end\n", 1)[1:]), end="") 53358007204Stb 53458007204Stb def run(self): 53558007204Stb for group in self: 53658007204Stb print(f"Running {group.title} ...") 53758007204Stb for test in group: 53858007204Stb self.run_script(test) 53958007204Stb return not self.failed 54058007204Stb 54158007204Stb def __iter__(self): 54258007204Stb return iter(self.tests) 54358007204Stb 54458007204Stb def __del__(self): 54558007204Stb if self.timing and self.stats: 54658007204Stb total = 0.0 54758007204Stb for (script, time) in self.stats: 54858007204Stb print(f"{round(time, 2):6.2f} {script}") 54958007204Stb total += time 55058007204Stb print(f"{round(total, 2):6.2f} total") 55158007204Stb 55258007204Stb if self.failed: 55358007204Stb print("Failed tests:") 55458007204Stb print('\n'.join(self.failed)) 55558007204Stb 55658007204Stbclass TlsServer: 55758007204Stb """ Spawns an s_server listening on localhost:port if necessary. """ 55858007204Stb 55958007204Stb def __init__(self, port=4433): 56058007204Stb self.spawn = True 56158007204Stb # Check whether a server is already listening on localhost:port 56258007204Stb self.spawn = subprocess.run( 56358007204Stb ["nc", "-c", "-z", "-T", "noverify", "localhost", str(port)], 56458007204Stb stderr=subprocess.DEVNULL, 56558007204Stb ).returncode != 0 56658007204Stb 56758007204Stb if self.spawn: 56858007204Stb self.server = subprocess.Popen( 56958007204Stb [ 57058007204Stb "openssl", 57158007204Stb "s_server", 57258007204Stb "-accept", 57358007204Stb str(port), 57458007204Stb "-key", 57558007204Stb "localhost.key", 57658007204Stb "-cert", 57758007204Stb "localhost.crt", 57858007204Stb "-www", 57958007204Stb ], 58058007204Stb stdout=subprocess.DEVNULL, 58158007204Stb stderr=subprocess.PIPE, 58258007204Stb text=True, 58358007204Stb ) 58458007204Stb 58558007204Stb # Check whether the server talks TLSv1.3 58658007204Stb self.has_tls1_3 = subprocess.run( 58758007204Stb [ 58858007204Stb "nc", 58958007204Stb "-c", 59058007204Stb "-z", 59158007204Stb "-T", 59258007204Stb "noverify", 59358007204Stb "-T", 59458007204Stb "protocols=TLSv1.3", 59558007204Stb "localhost", 59658007204Stb str(port), 59758007204Stb ], 59858007204Stb stderr=subprocess.DEVNULL, 59958007204Stb ).returncode == 0 60058007204Stb 60158007204Stb self.check() 60258007204Stb 60358007204Stb def check(self): 60458007204Stb if self.spawn and self.server.poll() is not None: 60558007204Stb print(self.server.stderr.read()) 60658007204Stb raise RuntimeError( 60758007204Stb f"openssl s_server died. Return code: {self.server.returncode}." 60858007204Stb ) 60958007204Stb if self.spawn: 61058007204Stb self.server.stderr.detach() 61158007204Stb 61258007204Stb def __del__(self): 61358007204Stb if self.spawn: 61458007204Stb self.server.terminate() 61558007204Stb 61658007204Stb# Extract the arguments we pass to script 61758007204Stbdef defaultargs(script, has_tls1_3): 61858007204Stb return next( 61958007204Stb (test for group in all_groups for test in group if test.name == script), 62058007204Stb Test() 62158007204Stb ).args(has_tls1_3) 62258007204Stb 62358007204Stbdef list_or_missing(missing=True): 62458007204Stb tests = [test.name for group in all_groups for test in group] 62558007204Stb 62658007204Stb if missing: 62758007204Stb scripts = { 62858007204Stb f for f in os.listdir(tlsfuzzer_scriptdir) if f != "__pycache__" 62958007204Stb } 63058007204Stb missing = scripts - set(tests) 63158007204Stb if missing: 63258007204Stb print('\n'.join(sorted(missing))) 63358007204Stb exit(0) 63458007204Stb 63558007204Stb tests.sort() 63658007204Stb print('\n'.join(tests)) 63758007204Stb exit(0) 63858007204Stb 63958007204Stbdef usage(): 64058007204Stb print("Usage: python3 tlsfuzzer.py [-lmnstv] [-p port] [script [test...]]") 64158007204Stb print(" --help help") 64258007204Stb print(" -f run failing tests") 64358007204Stb print(" -l list tests") 64458007204Stb print(" -m list new tests after package update") 64558007204Stb print(" -n do not run tests, but list the ones that would be run") 64658007204Stb print(" -p port connect to this port - defaults to 4433") 64758007204Stb print(" -s run slow tests") 64858007204Stb print(" -t show timing stats at end") 64958007204Stb print(" -v verbose output") 65058007204Stb exit(0) 65158007204Stb 65258007204Stbdef main(): 65358007204Stb failing = False 65458007204Stb list = False 65558007204Stb missing = False 65658007204Stb dryrun = False 65758007204Stb port = 4433 65858007204Stb slow = False 65958007204Stb timing = False 66058007204Stb verbose = False 66158007204Stb 66258007204Stb argv = sys.argv[1:] 66358007204Stb opts, args = getopt.getopt(argv, "flmnp:stv", ["help"]) 66458007204Stb for opt, arg in opts: 66558007204Stb if opt == '--help': 66658007204Stb usage() 66758007204Stb elif opt == '-f': 66858007204Stb failing = True 66958007204Stb elif opt == '-l': 67058007204Stb list = True 67158007204Stb elif opt == '-m': 67258007204Stb missing = True 67358007204Stb elif opt == '-n': 67458007204Stb dryrun = True 67558007204Stb elif opt == '-p': 67658007204Stb port = int(arg) 67758007204Stb elif opt == '-s': 67858007204Stb slow = True 67958007204Stb elif opt == '-t': 68058007204Stb timing = True 68158007204Stb elif opt == '-v': 68258007204Stb verbose = True 68358007204Stb else: 68458007204Stb raise ValueError(f"Unknown option: {opt}") 68558007204Stb 68658007204Stb if not os.path.exists(tlsfuzzer_scriptdir): 68758007204Stb print("package py3-tlsfuzzer is required for this regress") 68858007204Stb exit(1) 68958007204Stb 69058007204Stb if list and failing: 69158007204Stb failing = [test.name for group in failing_groups for test in group] 69258007204Stb failing.sort() 69358007204Stb print('\n'.join(failing)) 69458007204Stb exit(0) 69558007204Stb 69658007204Stb if list or missing: 69758007204Stb list_or_missing(missing) 69858007204Stb 69958007204Stb tls_server = TlsServer(port) 70058007204Stb 70158007204Stb tests = TestRunner(timing, verbose, port, tls_server.has_tls1_3, dryrun) 70258007204Stb 70358007204Stb if args: 70458007204Stb (dir, script) = os.path.split(args[0]) 70558007204Stb if dir and not dir == '.': 70658007204Stb tests.scriptdir = dir 70758007204Stb 70858007204Stb testargs = defaultargs(script, tls_server.has_tls1_3) 70958007204Stb 71058007204Stb tests.verbose = True 71158007204Stb tests.add("test from command line", [Test(script, testargs + args[1:])]) 71258007204Stb 71358007204Stb exit(not tests.run()) 71458007204Stb 71558007204Stb if failing: 71658007204Stb if tls_server.has_tls1_3: 71758007204Stb tests.add_group(tls13_failing_tests) 71858007204Stb if slow: 71958007204Stb tests.add_group(tls13_slow_failing_tests) 72058007204Stb tests.add_group(tls12_failing_tests) 72158007204Stb 72258007204Stb if tls_server.has_tls1_3: 72358007204Stb tests.add_group(tls13_tests) 72458007204Stb if slow: 72558007204Stb tests.add_group(tls13_slow_tests) 72658007204Stb else: 72758007204Stb tests.add_group(legacy_tests) 72858007204Stb 72958007204Stb tests.add_group(tls12_tests) 73058007204Stb if slow: 73158007204Stb tests.add_group(tls12_slow_tests) 73258007204Stb 73358007204Stb success = tests.run() 73458007204Stb del tests 73558007204Stb 73658007204Stb if not success: 73758007204Stb print("FAILED") 73858007204Stb exit(1) 73958007204Stb 74058007204Stbif __name__ == "__main__": 74158007204Stb main() 742