1*3df9e801Stb# $OpenBSD: tlsfuzzer.py,v 1.33 2021/04/13 16:16:06 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 x448", 6958007204Stb] 7058007204Stb 71cdcbc686Stb# test-tls13-finished.py has 70 failing tests that expect a "decode_error" 72cdcbc686Stb# instead of the "decrypt_error" sent by tls13_server_finished_recv(). 73cdcbc686Stb# Both alerts appear to be reasonable in this context, so work around this 74cdcbc686Stb# in the test instead of the library. 75cdcbc686Stbdef generate_test_tls13_finished_args(): 76e9324d07Stb assertion = "Expected alert description \"decode_error\"" 77e9324d07Stb assertion += " does not match received \"decrypt_error\"" 78cdcbc686Stb paddings = [ 79cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 1), 80cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 2), 81cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 4), 82cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 8), 83cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 16), 84cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 32), 85cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 48), 86cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 2**14-4-32), 87cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 0x20000), 88cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 0x30000), 89cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 1, 0), 90cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 2, 0), 91cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 4, 0), 92cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 8, 0), 93cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 16, 0), 94cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 32, 0), 95cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 48, 0), 96cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 2**14-4-32, 0), 97cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 12, 0), 98cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 1, 1), 99cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 8, 8), 100cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 1), 101cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 2), 102cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 4), 103cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 8), 104cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 16), 105cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 32), 106cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 48), 107cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 2**14-4-48), 108cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 0x20000), 109cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 0x30000), 110cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 12), 111cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 1, 0), 112cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 2, 0), 113cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 4, 0), 114cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 8, 0), 115cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 16, 0), 116cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 32, 0), 117cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 48, 0), 118cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 2**14-4-48, 0), 119cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 1, 1), 120cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 8, 8), 121cdcbc686Stb ] 122cdcbc686Stb truncations = [ 123cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -1), 124cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -2), 125cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -4), 126cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -8), 127cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -16), 128cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, -32), 129cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 0, 12), 130cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 1, None), 131cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 2, None), 132cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 4, None), 133cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 8, None), 134cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 16, None), 135cdcbc686Stb ("TLS_AES_128_GCM_SHA256", 32, None), 136cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -1), 137cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -2), 138cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -4), 139cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -8), 140cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -16), 141cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, -32), 142cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 0, 12), 143cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 1, None), 144cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 2, None), 145cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 4, None), 146cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 8, None), 147cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 16, None), 148cdcbc686Stb ("TLS_AES_256_GCM_SHA384", 32, None), 149cdcbc686Stb ] 150cdcbc686Stb 151cdcbc686Stb args = [ 152cdcbc686Stb "-x", "empty - cipher TLS_AES_128_GCM_SHA256", "-X", assertion, 153cdcbc686Stb "-x", "empty - cipher TLS_AES_256_GCM_SHA384", "-X", assertion, 154cdcbc686Stb ] 155cdcbc686Stb padding_fmt = "padding - cipher %s, pad_byte 0, pad_left %d, pad_right %d" 156cdcbc686Stb for padding in paddings: 157cdcbc686Stb args += ["-x", padding_fmt % padding, "-X", assertion] 158cdcbc686Stb truncation_fmt = "truncation - cipher %s, start %d, end %s" 159cdcbc686Stb for truncation in truncations: 160cdcbc686Stb args += ["-x", truncation_fmt % truncation, "-X", assertion] 161cdcbc686Stb return args 162cdcbc686Stb 163c05f8ee0Stbtls13_tests = TestGroup("TLSv1.3 tests", [ 164c05f8ee0Stb Test("test-tls13-ccs.py"), 165c05f8ee0Stb Test("test-tls13-conversation.py"), 166c05f8ee0Stb Test("test-tls13-count-tickets.py"), 167c05f8ee0Stb Test("test-tls13-empty-alert.py"), 168c05f8ee0Stb Test("test-tls13-finished.py", generate_test_tls13_finished_args()), 169c05f8ee0Stb Test("test-tls13-finished-plaintext.py"), 170c05f8ee0Stb Test("test-tls13-hrr.py"), 171c05f8ee0Stb Test("test-tls13-keyshare-omitted.py"), 172c05f8ee0Stb Test("test-tls13-legacy-version.py"), 173c05f8ee0Stb Test("test-tls13-nociphers.py"), 174c05f8ee0Stb Test("test-tls13-record-padding.py"), 175c05f8ee0Stb Test("test-tls13-shuffled-extentions.py"), 176c05f8ee0Stb Test("test-tls13-zero-content-type.py"), 177c05f8ee0Stb 178c05f8ee0Stb # The skipped tests fail due to a bug in BIO_gets() which masks the retry 179c05f8ee0Stb # signalled from an SSL_read() failure. Testing with httpd(8) shows we're 180c05f8ee0Stb # handling these corner cases correctly since tls13_record_layer.c -r1.47. 181c05f8ee0Stb Test("test-tls13-zero-length-data.py", [ 182c05f8ee0Stb "-e", "zero-length app data", 183c05f8ee0Stb "-e", "zero-length app data with large padding", 184c05f8ee0Stb "-e", "zero-length app data with padding", 185c05f8ee0Stb ]), 186c05f8ee0Stb]) 187c05f8ee0Stb 18858007204Stb# Tests that take a lot of time (> ~30s on an x280) 18958007204Stbtls13_slow_tests = TestGroup("slow TLSv1.3 tests", [ 19058007204Stb # XXX: Investigate the occasional message 19158007204Stb # "Got shared secret with 1 most significant bytes equal to zero." 19258007204Stb Test("test-tls13-dhe-shared-secret-padding.py", tls13_unsupported_ciphers), 19358007204Stb 19458007204Stb Test("test-tls13-invalid-ciphers.py"), 19558007204Stb Test("test-tls13-serverhello-random.py", tls13_unsupported_ciphers), 19692edee68Stb 19792edee68Stb # Mark two tests cases as xfail for now. The tests expect an arguably 19892edee68Stb # correct decode_error while we send a decrypt_error (like fizz/boring). 19992edee68Stb Test("test-tls13-record-layer-limits.py", [ 20092edee68Stb "-x", "max size payload (2**14) of Finished msg, with 16348 bytes of left padding, cipher TLS_AES_128_GCM_SHA256", 20192edee68Stb "-x", "max size payload (2**14) of Finished msg, with 16348 bytes of left padding, cipher TLS_CHACHA20_POLY1305_SHA256", 20292edee68Stb ]), 203a590e228Stb # We don't accept an empty ECPF extension since it must advertise the 204a590e228Stb # uncompressed point format. Exclude this extension type from the test. 205a590e228Stb Test( 206a590e228Stb "test-tls13-large-number-of-extensions.py", 207a590e228Stb tls13_args = ["--exc", "11"], 208a590e228Stb ), 20958007204Stb]) 21058007204Stb 21158007204Stbtls13_extra_cert_tests = TestGroup("TLSv1.3 certificate tests", [ 21258007204Stb # need to set up client certs to run these 21358007204Stb Test("test-tls13-certificate-request.py"), 21458007204Stb Test("test-tls13-certificate-verify.py"), 21558007204Stb Test("test-tls13-ecdsa-in-certificate-verify.py"), 21658007204Stb 21758007204Stb # Test expects the server to have installed three certificates: 21858007204Stb # with P-256, P-384 and P-521 curve. Also SHA1+ECDSA is verified 21958007204Stb # to not work. 22058007204Stb Test("test-tls13-ecdsa-support.py"), 22158007204Stb]) 22258007204Stb 22358007204Stbtls13_failing_tests = TestGroup("failing TLSv1.3 tests", [ 22458007204Stb # Some tests fail because we fail later than the scripts expect us to. 22558007204Stb # With X25519, we accept weak peer public keys and fail when we actually 22658007204Stb # compute the keyshare. Other tests seem to indicate that we could be 22758007204Stb # stricter about what keyshares we accept. 2289bc11146Stb Test("test-tls13-crfg-curves.py", [ 2299bc11146Stb '-e', 'all zero x448 key share', 2309bc11146Stb '-e', 'empty x448 key share', 2319bc11146Stb '-e', 'sanity x448 with compression ansiX962_compressed_char2', 2329bc11146Stb '-e', 'sanity x448 with compression ansiX962_compressed_prime', 2339bc11146Stb '-e', 'sanity x448 with compression uncompressed', 2349bc11146Stb '-e', 'too big x448 key share', 2359bc11146Stb '-e', 'too small x448 key share', 2369bc11146Stb '-e', 'x448 key share of "1"', 2379bc11146Stb ]), 2389bc11146Stb Test("test-tls13-ecdhe-curves.py", [ 2399bc11146Stb '-e', 'sanity - x448', 2409bc11146Stb '-e', 'x448 - key share from other curve', 2419bc11146Stb '-e', 'x448 - point at infinity', 2429bc11146Stb '-e', 'x448 - right 0-padded key_share', 2439bc11146Stb '-e', 'x448 - right-truncated key_share', 2449bc11146Stb ]), 24558007204Stb 246bb0ca798Stb # The test sends records with protocol version 0x0300 instead of 0x0303 2477880f693Stb # and currently fails with OpenSSL and LibreSSL for this reason. 248bb0ca798Stb # We have the logic corresponding to NSS's fix for CVE-2020-25648 249bb0ca798Stb # https://hg.mozilla.org/projects/nss/rev/57bbefa793232586d27cee83e74411171e128361 250bb0ca798Stb # so should not be affected by this issue. 251bb0ca798Stb Test("test-tls13-multiple-ccs-messages.py"), 252bb0ca798Stb 25358007204Stb # https://github.com/openssl/openssl/issues/8369 25458007204Stb Test("test-tls13-obsolete-curves.py"), 25558007204Stb 25658007204Stb # 3 failing rsa_pss_pss tests 25758007204Stb Test("test-tls13-rsa-signatures.py"), 25858007204Stb 259e7667ed5Stb # The failing tests all expect an ri extension. What's up with that? 26058007204Stb Test("test-tls13-version-negotiation.py"), 26158007204Stb]) 26258007204Stb 26358007204Stbtls13_slow_failing_tests = TestGroup("slow, failing TLSv1.3 tests", [ 26458007204Stb # Other test failures bugs in keyshare/tlsext negotiation? 26558007204Stb Test("test-tls13-unrecognised-groups.py"), # unexpected closure 26658007204Stb 267071b89a8Stb # 5 failures: 268071b89a8Stb # 'app data split, conversation with KeyUpdate msg' 269071b89a8Stb # 'fragmented keyupdate msg' 270071b89a8Stb # 'multiple KeyUpdate messages' 271071b89a8Stb # 'post-handshake KeyUpdate msg with update_not_request' 272071b89a8Stb # 'post-handshake KeyUpdate msg with update_request' 27358007204Stb Test("test-tls13-keyupdate.py"), 274071b89a8Stb 27558007204Stb Test("test-tls13-symetric-ciphers.py"), # unexpected message from peer 27658007204Stb 27758007204Stb # 6 tests fail: 'rsa_pkcs1_{md5,sha{1,224,256,384,512}} signature' 27858007204Stb # We send server hello, but the test expects handshake_failure 27958007204Stb Test("test-tls13-pkcs-signature.py"), 28058007204Stb # 8 tests fail: 'tls13 signature rsa_pss_{pss,rsae}_sha{256,384,512} 28158007204Stb Test("test-tls13-rsapss-signatures.py"), 28258007204Stb]) 28358007204Stb 28458007204Stbtls13_unsupported_tests = TestGroup("TLSv1.3 tests for unsupported features", [ 28558007204Stb # Tests for features we don't support 28658007204Stb Test("test-tls13-0rtt-garbage.py"), 28758007204Stb Test("test-tls13-ffdhe-groups.py"), 28858007204Stb Test("test-tls13-ffdhe-sanity.py"), 28958007204Stb Test("test-tls13-psk_dhe_ke.py"), 29058007204Stb Test("test-tls13-psk_ke.py"), 29158007204Stb 29258007204Stb # need server to react to HTTP GET for /keyupdate 29358007204Stb Test("test-tls13-keyupdate-from-server.py"), 29458007204Stb 29558007204Stb # Weird test: tests servers that don't support 1.3 29658007204Stb Test("test-tls13-non-support.py"), 29758007204Stb 29858007204Stb # broken test script 29958007204Stb # UnboundLocalError: local variable 'cert' referenced before assignment 30058007204Stb Test("test-tls13-post-handshake-auth.py"), 30158007204Stb 302390e08e8Stb # ExpectNewSessionTicket 303390e08e8Stb Test("test-tls13-session-resumption.py"), 304390e08e8Stb 30558007204Stb # Server must be configured to support only rsa_pss_rsae_sha512 30658007204Stb Test("test-tls13-signature-algorithms.py"), 30758007204Stb]) 30858007204Stb 30958007204Stbtls12_exclude_legacy_protocols = [ 31058007204Stb # all these have BIO_read timeouts against TLSv1.3 31158007204Stb "-e", "Protocol (3, 0)", 31258007204Stb "-e", "Protocol (3, 0) in SSLv2 compatible ClientHello", 31358007204Stb # the following only fail with TLSv1.3 31458007204Stb "-e", "Protocol (3, 1) in SSLv2 compatible ClientHello", 31558007204Stb "-e", "Protocol (3, 2) in SSLv2 compatible ClientHello", 31658007204Stb "-e", "Protocol (3, 3) in SSLv2 compatible ClientHello", 31758007204Stb "-e", "Protocol (3, 1) with x448 group", 31858007204Stb "-e", "Protocol (3, 2) with x448 group", 31958007204Stb "-e", "Protocol (3, 3) with x448 group", 32058007204Stb] 32158007204Stb 32258007204Stbtls12_tests = TestGroup("TLSv1.2 tests", [ 32358007204Stb # Tests that pass as they are. 32458007204Stb Test("test-TLSv1_2-rejected-without-TLSv1_2.py"), 32558007204Stb Test("test-aes-gcm-nonces.py"), 32658007204Stb Test("test-chacha20.py"), 32758007204Stb Test("test-conversation.py"), 32858007204Stb Test("test-cve-2016-2107.py"), 32931b5396fStb Test("test-cve-2016-6309.py"), 33058007204Stb Test("test-dhe-rsa-key-exchange.py"), 33175204759Stb Test("test-dhe-rsa-key-exchange-with-bad-messages.py"), 33258007204Stb Test("test-early-application-data.py"), 33358007204Stb Test("test-empty-extensions.py"), 33458007204Stb Test("test-fuzzed-MAC.py"), 33558007204Stb Test("test-fuzzed-ciphertext.py"), 33658007204Stb Test("test-fuzzed-finished.py"), 33758007204Stb Test("test-fuzzed-padding.py"), 3382e9afb88Stb Test("test-fuzzed-plaintext.py"), # fails once in a while 33958007204Stb Test("test-hello-request-by-client.py"), 34058007204Stb Test("test-invalid-cipher-suites.py"), 34158007204Stb Test("test-invalid-content-type.py"), 34258007204Stb Test("test-invalid-session-id.py"), 34358007204Stb Test("test-invalid-version.py"), 34444072c60Stb Test("test-lucky13.py"), 34558007204Stb Test("test-message-skipping.py"), 34658007204Stb Test("test-no-heartbeat.py"), 34758007204Stb Test("test-sessionID-resumption.py"), 34858007204Stb Test("test-sslv2-connection.py"), 34958007204Stb Test("test-truncating-of-finished.py"), 35058007204Stb Test("test-truncating-of-kRSA-client-key-exchange.py"), 35158007204Stb Test("test-unsupported-curve-fallback.py"), 35258007204Stb Test("test-version-numbers.py"), 35358007204Stb Test("test-zero-length-data.py"), 35458007204Stb 35558007204Stb # Tests that need tweaking for unsupported features and ciphers. 35658007204Stb Test( 35758007204Stb "test-atypical-padding.py", [ 35858007204Stb "-e", "sanity - encrypt then MAC", 35958007204Stb "-e", "2^14 bytes of AppData with 256 bytes of padding (SHA1 + Encrypt then MAC)", 36058007204Stb ] 36158007204Stb ), 36258007204Stb Test( 36358007204Stb "test-dhe-rsa-key-exchange-signatures.py", [ 36458007204Stb "-e", "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA sha224 signature", 36558007204Stb "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 sha224 signature", 36658007204Stb "-e", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA sha224 signature", 36758007204Stb "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 sha224 signature", 36858007204Stb "-e", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA sha224 signature", 36958007204Stb ] 37058007204Stb ), 37158007204Stb Test("test-dhe-key-share-random.py", tls12_exclude_legacy_protocols), 37258007204Stb Test("test-export-ciphers-rejected.py", ["--min-ver", "TLSv1.0"]), 37358007204Stb Test( 37458007204Stb "test-downgrade-protection.py", 37558007204Stb tls12_args = ["--server-max-protocol", "TLSv1.2"], 37658007204Stb tls13_args = ["--server-max-protocol", "TLSv1.3"], 37758007204Stb ), 37858007204Stb Test("test-fallback-scsv.py", tls13_args = ["--tls-1.3"] ), 379dab3dacdStb 380*3df9e801Stb Test("test-invalid-compression-methods.py", [ 381*3df9e801Stb "-x", "invalid compression methods", 382*3df9e801Stb "-X", 'Expected alert description "illegal_parameter" ' 383*3df9e801Stb 'does not match received "decode_error"', 384*3df9e801Stb "-x", "only deflate compression method", 385*3df9e801Stb "-X", 'Expected alert description "illegal_parameter" ' 386*3df9e801Stb 'does not match received "decode_error"', 387*3df9e801Stb ]), 388*3df9e801Stb 389dab3dacdStb # Without --sig-algs-drop-ok, two tests fail since we do not currently 390dab3dacdStb # implement the signature_algorithms_cert extension (although we MUST). 391dab3dacdStb Test("test-sig-algs-renegotiation-resumption.py", ["--sig-algs-drop-ok"]), 392dab3dacdStb 39358007204Stb Test("test-serverhello-random.py", args = tls12_exclude_legacy_protocols), 39458007204Stb]) 39558007204Stb 39658007204Stbtls12_slow_tests = TestGroup("slow TLSv1.2 tests", [ 39758007204Stb Test("test-cve-2016-7054.py"), 39858007204Stb Test("test-dhe-no-shared-secret-padding.py", tls12_exclude_legacy_protocols), 39958007204Stb Test("test-ecdhe-padded-shared-secret.py", tls12_exclude_legacy_protocols), 40058007204Stb Test("test-ecdhe-rsa-key-share-random.py", tls12_exclude_legacy_protocols), 401f02c3a15Stb Test("test-large-hello.py"), 40258007204Stb]) 40358007204Stb 40458007204Stbtls12_failing_tests = TestGroup("failing TLSv1.2 tests", [ 40558007204Stb # no shared cipher 40658007204Stb Test("test-aesccm.py"), 40758007204Stb # need server to set up alpn 40858007204Stb Test("test-alpn-negotiation.py"), 40958007204Stb # many tests fail due to unexpected server_name extension 41058007204Stb Test("test-bleichenbacher-workaround.py"), 41158007204Stb 41258007204Stb # need client key and cert plus extra server setup 41358007204Stb Test("test-certificate-malformed.py"), 41458007204Stb Test("test-certificate-request.py"), 41558007204Stb Test("test-certificate-verify-malformed-sig.py"), 41658007204Stb Test("test-certificate-verify-malformed.py"), 41758007204Stb Test("test-certificate-verify.py"), 41858007204Stb Test("test-ecdsa-in-certificate-verify.py"), 41958007204Stb Test("test-renegotiation-disabled-client-cert.py"), 42058007204Stb Test("test-rsa-pss-sigs-on-certificate-verify.py"), 42158007204Stb Test("test-rsa-sigs-on-certificate-verify.py"), 42258007204Stb 42358007204Stb # test doesn't expect session ticket 42458007204Stb Test("test-client-compatibility.py"), 42558007204Stb # abrupt closure 42658007204Stb Test("test-client-hello-max-size.py"), 42758007204Stb # unknown signature algorithms 42858007204Stb Test("test-clienthello-md5.py"), 42958007204Stb 430208aabddStb # Tests expect an illegal_parameter or a decode_error alert. Should be 4319f325698Stb # added to ssl3_get_client_key_exchange on kex function failure. 43258007204Stb Test("test-ecdhe-rsa-key-exchange-with-bad-messages.py"), 43358007204Stb 4349f325698Stb # We send a handshake_failure due to no shared ciphers while the 4359f325698Stb # test expects to succeed. 43658007204Stb Test("test-ecdhe-rsa-key-exchange.py"), 43758007204Stb 43858007204Stb # no shared cipher 43958007204Stb Test("test-ecdsa-sig-flexibility.py"), 44058007204Stb 44158007204Stb # 29 succeed, 263 fail: 44258007204Stb # 'n extensions', 'n extensions last empty' n in 4086, 4096, 8192, 16383 44358007204Stb # 'fuzz ext length to n' n in [0..255] with the exception of 41... 44458007204Stb Test("test-extensions.py"), 44558007204Stb 44658007204Stb # Tests expect SH but we send unexpected_message or handshake_failure 44758007204Stb # 'Application data inside Client Hello' 44858007204Stb # 'Application data inside Client Key Exchange' 44958007204Stb # 'Application data inside Finished' 45058007204Stb Test("test-interleaved-application-data-and-fragmented-handshakes-in-renegotiation.py"), 45158007204Stb # Tests expect SH but we send handshake_failure 45258007204Stb # 'Application data before Change Cipher Spec' 45358007204Stb # 'Application data before Client Key Exchange' 45458007204Stb # 'Application data before Finished' 45558007204Stb Test("test-interleaved-application-data-in-renegotiation.py"), 45658007204Stb 45758007204Stb # broken test script 45858007204Stb # TypeError: '<' not supported between instances of 'int' and 'NoneType' 45958007204Stb Test("test-invalid-client-hello-w-record-overflow.py"), 46058007204Stb 46158007204Stb # Lots of failures. abrupt closure 46258007204Stb Test("test-invalid-client-hello.py"), 46358007204Stb 46458007204Stb # abrupt closure 46558007204Stb # 'encrypted premaster set to all zero (n)' n in 256 384 512 46658007204Stb Test("test-invalid-rsa-key-exchange-messages.py"), 46758007204Stb 46858007204Stb # test expects illegal_parameter, we send unrecognized_name (which seems 46958007204Stb # correct according to rfc 6066?) 47058007204Stb Test("test-invalid-server-name-extension-resumption.py"), 47158007204Stb # let through some server names without sending an alert 47258007204Stb # again illegal_parameter vs unrecognized_name 47358007204Stb Test("test-invalid-server-name-extension.py"), 47458007204Stb 47558007204Stb # 14 pass 47658007204Stb # 7 fail 47758007204Stb # 'n extensions', n in 4095, 4096, 4097, 8191, 8192, 8193, 16383, 47858007204Stb Test("test-large-number-of-extensions.py"), 47958007204Stb 48058007204Stb # 4 failures: 48158007204Stb # 'insecure (legacy) renegotiation with GET after 2nd handshake' 48258007204Stb # 'insecure (legacy) renegotiation with incomplete GET' 48358007204Stb # 'secure renegotiation with GET after 2nd handshake' 48458007204Stb # 'secure renegotiation with incomplete GET' 48558007204Stb Test("test-legacy-renegotiation.py"), 48658007204Stb 48758007204Stb # 1 failure (timeout): we don't send the unexpected_message alert 48858007204Stb # 'duplicate change cipher spec after Finished' 48958007204Stb Test("test-message-duplication.py"), 49058007204Stb 49158007204Stb # server should send status_request 49258007204Stb Test("test-ocsp-stapling.py"), 49358007204Stb 49458007204Stb # unexpected closure 49558007204Stb Test("test-openssl-3712.py"), 49658007204Stb 49758007204Stb # 3 failures: 49858007204Stb # 'big, needs fragmentation: max fragment - 16336B extension' 49958007204Stb # 'big, needs fragmentation: max fragment - 32768B extension' 50058007204Stb # 'maximum size: max fragment - 65531B extension' 50158007204Stb Test("test-record-layer-fragmentation.py"), 50258007204Stb 50358007204Stb # wants --reply-AD-size 50458007204Stb Test("test-record-size-limit.py"), 50558007204Stb 50658007204Stb # failed: 3 (expect an alert, we send AD) 50758007204Stb # 'try insecure (legacy) renegotiation with incomplete GET' 50858007204Stb # 'try secure renegotiation with GET after 2nd CH' 50958007204Stb # 'try secure renegotiation with incomplete GET' 51058007204Stb Test("test-renegotiation-disabled.py"), 51158007204Stb 51258007204Stb # 'resumption of safe session with NULL cipher' 51358007204Stb # 'resumption with cipher from old CH but not selected by server' 51458007204Stb Test("test-resumption-with-wrong-ciphers.py"), 51558007204Stb 51658007204Stb # 5 failures: 51758007204Stb # 'empty sigalgs' 51858007204Stb # 'only undefined sigalgs' 51958007204Stb # 'rsa_pss_pss_sha256 only' 52058007204Stb # 'rsa_pss_pss_sha384 only' 52158007204Stb # 'rsa_pss_pss_sha512 only' 52258007204Stb Test("test-sig-algs.py"), 52358007204Stb 52458007204Stb # 13 failures: 52558007204Stb # 'duplicated n non-rsa schemes' for n in 202 2342 8119 23741 32744 52658007204Stb # 'empty list of signature methods' 52758007204Stb # 'tolerance n RSA or ECDSA methods' for n in 215 2355 8132 23754 52858007204Stb # 'tolerance 32758 methods with sig_alg_cert' 52958007204Stb # 'tolerance max 32744 number of methods with sig_alg_cert' 53058007204Stb # 'tolerance max (32760) number of methods' 53158007204Stb Test("test-signature-algorithms.py"), 53258007204Stb 53358007204Stb # times out 53458007204Stb Test("test-ssl-death-alert.py"), 53558007204Stb 53658007204Stb # 17 pass, 13 fail. padding and truncation 53758007204Stb Test("test-truncating-of-client-hello.py"), 53858007204Stb 53958007204Stb # x448 tests need disabling plus x25519 corner cases need sorting out 54058007204Stb Test("test-x25519.py"), 54158007204Stb]) 54258007204Stb 54358007204Stbtls12_unsupported_tests = TestGroup("TLSv1.2 for unsupported features", [ 54458007204Stb # protocol_version 54558007204Stb Test("test-SSLv3-padding.py"), 546676445ddStb # we don't do RSA key exchanges 547676445ddStb Test("test-bleichenbacher-timing.py"), 5489f325698Stb # no encrypt-then-mac 5499f325698Stb Test("test-encrypt-then-mac-renegotiation.py"), 5509f325698Stb Test("test-encrypt-then-mac.py"), 5519f325698Stb # no EME support 5529f325698Stb Test("test-extended-master-secret-extension-with-client-cert.py"), 5539f325698Stb Test("test-extended-master-secret-extension.py"), 5549f325698Stb # no ffdhe 5559f325698Stb Test("test-ffdhe-expected-params.py"), 5569f325698Stb Test("test-ffdhe-negotiation.py"), 5579f325698Stb # expects the server to send the heartbeat extension 5589f325698Stb Test("test-heartbeat.py"), 55958007204Stb]) 56058007204Stb 56158007204Stb# These tests take a ton of time to fail against an 1.3 server, 56258007204Stb# so don't run them against 1.3 pending further investigation. 56358007204Stblegacy_tests = TestGroup("Legacy protocol tests", [ 56458007204Stb Test("test-sslv2-force-cipher-3des.py"), 56558007204Stb Test("test-sslv2-force-cipher-non3des.py"), 56658007204Stb Test("test-sslv2-force-cipher.py"), 56758007204Stb Test("test-sslv2-force-export-cipher.py"), 56858007204Stb Test("test-sslv2hello-protocol.py"), 56958007204Stb]) 57058007204Stb 57158007204Stball_groups = [ 57258007204Stb tls13_tests, 57358007204Stb tls13_slow_tests, 57458007204Stb tls13_extra_cert_tests, 57558007204Stb tls13_failing_tests, 57658007204Stb tls13_slow_failing_tests, 57758007204Stb tls13_unsupported_tests, 57858007204Stb tls12_tests, 57958007204Stb tls12_slow_tests, 58058007204Stb tls12_failing_tests, 58158007204Stb tls12_unsupported_tests, 58258007204Stb legacy_tests, 58358007204Stb] 58458007204Stb 58558007204Stbfailing_groups = [ 58658007204Stb tls13_failing_tests, 58758007204Stb tls13_slow_failing_tests, 58858007204Stb tls12_failing_tests, 58958007204Stb] 59058007204Stb 59158007204Stbclass TestRunner: 59258007204Stb """ Runs the given tests troups against a server and displays stats. """ 59358007204Stb 59458007204Stb def __init__( 59558007204Stb self, timing=False, verbose=False, port=4433, use_tls1_3=True, 59658007204Stb dry_run=False, tests=[], scriptdir=tlsfuzzer_scriptdir, 59758007204Stb ): 59858007204Stb self.tests = [] 59958007204Stb 60058007204Stb self.dryrun = dry_run 60158007204Stb self.use_tls1_3 = use_tls1_3 60258007204Stb self.port = str(port) 60358007204Stb self.scriptdir = scriptdir 60458007204Stb 60558007204Stb self.stats = [] 60658007204Stb self.failed = [] 60709e96d4eStb self.missing = [] 60858007204Stb 60958007204Stb self.timing = timing 61058007204Stb self.verbose = verbose 61158007204Stb 61258007204Stb def add(self, title="tests", tests=[]): 61358007204Stb # tests.sort(key=lambda test: test.name) 61458007204Stb self.tests.append(TestGroup(title, tests)) 61558007204Stb 61658007204Stb def add_group(self, group): 61758007204Stb self.tests.append(group) 61858007204Stb 61958007204Stb def run_script(self, test): 62058007204Stb script = test.name 62158007204Stb args = ["-p"] + [self.port] + test.args(self.use_tls1_3) 62258007204Stb 62358007204Stb if self.dryrun: 62458007204Stb if not self.verbose: 62558007204Stb args = [] 62658007204Stb print(script , end=' ' if args else '') 62758007204Stb print(' '.join([f"\"{arg}\"" for arg in args])) 62858007204Stb return 62958007204Stb 63058007204Stb if self.verbose: 63158007204Stb print(script) 63258007204Stb else: 63358007204Stb print(f"{script[:68]:<72}", end=" ", flush=True) 63458007204Stb start = timer() 63509e96d4eStb scriptpath = os.path.join(self.scriptdir, script) 63609e96d4eStb if not os.path.exists(scriptpath): 63709e96d4eStb self.missing.append(script) 6385bec7355Stb print("MISSING") 6395bec7355Stb return 64058007204Stb test = subprocess.run( 64109e96d4eStb ["python3", scriptpath] + args, 64258007204Stb capture_output=not self.verbose, 64358007204Stb text=True, 64458007204Stb ) 64558007204Stb end = timer() 64658007204Stb self.stats.append((script, end - start)) 64758007204Stb if test.returncode == 0: 64858007204Stb print("OK") 64958007204Stb return 65058007204Stb print("FAILED") 65158007204Stb self.failed.append(script) 65258007204Stb 65358007204Stb if self.verbose: 65458007204Stb return 65558007204Stb 65658007204Stb print('\n'.join(test.stdout.split("Test end\n", 1)[1:]), end="") 65758007204Stb 65858007204Stb def run(self): 65958007204Stb for group in self: 66058007204Stb print(f"Running {group.title} ...") 66158007204Stb for test in group: 66258007204Stb self.run_script(test) 66358007204Stb return not self.failed 66458007204Stb 66558007204Stb def __iter__(self): 66658007204Stb return iter(self.tests) 66758007204Stb 66858007204Stb def __del__(self): 66958007204Stb if self.timing and self.stats: 67058007204Stb total = 0.0 67158007204Stb for (script, time) in self.stats: 67258007204Stb print(f"{round(time, 2):6.2f} {script}") 67358007204Stb total += time 67458007204Stb print(f"{round(total, 2):6.2f} total") 67558007204Stb 67658007204Stb if self.failed: 67758007204Stb print("Failed tests:") 67858007204Stb print('\n'.join(self.failed)) 67958007204Stb 68009e96d4eStb if self.missing: 68109e96d4eStb print("Missing tests (outdated package?):") 68209e96d4eStb print('\n'.join(self.missing)) 68309e96d4eStb 68458007204Stbclass TlsServer: 68558007204Stb """ Spawns an s_server listening on localhost:port if necessary. """ 68658007204Stb 68758007204Stb def __init__(self, port=4433): 68858007204Stb self.spawn = True 68958007204Stb # Check whether a server is already listening on localhost:port 69058007204Stb self.spawn = subprocess.run( 69158007204Stb ["nc", "-c", "-z", "-T", "noverify", "localhost", str(port)], 69258007204Stb stderr=subprocess.DEVNULL, 69358007204Stb ).returncode != 0 69458007204Stb 69558007204Stb if self.spawn: 69658007204Stb self.server = subprocess.Popen( 69758007204Stb [ 69858007204Stb "openssl", 69958007204Stb "s_server", 70058007204Stb "-accept", 70158007204Stb str(port), 70266cd5d76Stb "-groups", 70366cd5d76Stb "X25519:P-256:P-521:P-384", 70458007204Stb "-key", 70558007204Stb "localhost.key", 70658007204Stb "-cert", 70758007204Stb "localhost.crt", 70858007204Stb "-www", 70958007204Stb ], 71058007204Stb stdout=subprocess.DEVNULL, 71158007204Stb stderr=subprocess.PIPE, 71258007204Stb text=True, 71358007204Stb ) 71458007204Stb 71558007204Stb # Check whether the server talks TLSv1.3 716f501c991Stb self.has_tls1_3 = True or subprocess.run( 71758007204Stb [ 71858007204Stb "nc", 71958007204Stb "-c", 72058007204Stb "-z", 72158007204Stb "-T", 72258007204Stb "noverify", 72358007204Stb "-T", 72458007204Stb "protocols=TLSv1.3", 72558007204Stb "localhost", 72658007204Stb str(port), 72758007204Stb ], 72858007204Stb stderr=subprocess.DEVNULL, 72958007204Stb ).returncode == 0 73058007204Stb 73158007204Stb self.check() 73258007204Stb 73358007204Stb def check(self): 73458007204Stb if self.spawn and self.server.poll() is not None: 73558007204Stb print(self.server.stderr.read()) 73658007204Stb raise RuntimeError( 73758007204Stb f"openssl s_server died. Return code: {self.server.returncode}." 73858007204Stb ) 73958007204Stb if self.spawn: 74058007204Stb self.server.stderr.detach() 74158007204Stb 74258007204Stb def __del__(self): 74358007204Stb if self.spawn: 74458007204Stb self.server.terminate() 74558007204Stb 74658007204Stb# Extract the arguments we pass to script 74758007204Stbdef defaultargs(script, has_tls1_3): 74858007204Stb return next( 74958007204Stb (test for group in all_groups for test in group if test.name == script), 75058007204Stb Test() 75158007204Stb ).args(has_tls1_3) 75258007204Stb 75358007204Stbdef list_or_missing(missing=True): 75458007204Stb tests = [test.name for group in all_groups for test in group] 75558007204Stb 75658007204Stb if missing: 75758007204Stb scripts = { 75858007204Stb f for f in os.listdir(tlsfuzzer_scriptdir) if f != "__pycache__" 75958007204Stb } 76058007204Stb missing = scripts - set(tests) 76158007204Stb if missing: 76258007204Stb print('\n'.join(sorted(missing))) 76358007204Stb exit(0) 76458007204Stb 76558007204Stb tests.sort() 76658007204Stb print('\n'.join(tests)) 76758007204Stb exit(0) 76858007204Stb 76958007204Stbdef usage(): 77058007204Stb print("Usage: python3 tlsfuzzer.py [-lmnstv] [-p port] [script [test...]]") 77158007204Stb print(" --help help") 77258007204Stb print(" -f run failing tests") 77358007204Stb print(" -l list tests") 77458007204Stb print(" -m list new tests after package update") 77558007204Stb print(" -n do not run tests, but list the ones that would be run") 77658007204Stb print(" -p port connect to this port - defaults to 4433") 77758007204Stb print(" -s run slow tests") 77858007204Stb print(" -t show timing stats at end") 77958007204Stb print(" -v verbose output") 78058007204Stb exit(0) 78158007204Stb 78258007204Stbdef main(): 78358007204Stb failing = False 78458007204Stb list = False 78558007204Stb missing = False 78658007204Stb dryrun = False 78758007204Stb port = 4433 78858007204Stb slow = False 78958007204Stb timing = False 79058007204Stb verbose = False 79158007204Stb 79258007204Stb argv = sys.argv[1:] 79358007204Stb opts, args = getopt.getopt(argv, "flmnp:stv", ["help"]) 79458007204Stb for opt, arg in opts: 79558007204Stb if opt == '--help': 79658007204Stb usage() 79758007204Stb elif opt == '-f': 79858007204Stb failing = True 79958007204Stb elif opt == '-l': 80058007204Stb list = True 80158007204Stb elif opt == '-m': 80258007204Stb missing = True 80358007204Stb elif opt == '-n': 80458007204Stb dryrun = True 80558007204Stb elif opt == '-p': 80658007204Stb port = int(arg) 80758007204Stb elif opt == '-s': 80858007204Stb slow = True 80958007204Stb elif opt == '-t': 81058007204Stb timing = True 81158007204Stb elif opt == '-v': 81258007204Stb verbose = True 81358007204Stb else: 81458007204Stb raise ValueError(f"Unknown option: {opt}") 81558007204Stb 81658007204Stb if not os.path.exists(tlsfuzzer_scriptdir): 81758007204Stb print("package py3-tlsfuzzer is required for this regress") 81858007204Stb exit(1) 81958007204Stb 82058007204Stb if list and failing: 82158007204Stb failing = [test.name for group in failing_groups for test in group] 82258007204Stb failing.sort() 82358007204Stb print('\n'.join(failing)) 82458007204Stb exit(0) 82558007204Stb 82658007204Stb if list or missing: 82758007204Stb list_or_missing(missing) 82858007204Stb 82958007204Stb tls_server = TlsServer(port) 83058007204Stb 83158007204Stb tests = TestRunner(timing, verbose, port, tls_server.has_tls1_3, dryrun) 83258007204Stb 83358007204Stb if args: 83458007204Stb (dir, script) = os.path.split(args[0]) 83558007204Stb if dir and not dir == '.': 83658007204Stb tests.scriptdir = dir 83758007204Stb 83858007204Stb testargs = defaultargs(script, tls_server.has_tls1_3) 83958007204Stb 84058007204Stb tests.verbose = True 84158007204Stb tests.add("test from command line", [Test(script, testargs + args[1:])]) 84258007204Stb 84358007204Stb exit(not tests.run()) 84458007204Stb 84558007204Stb if failing: 84658007204Stb if tls_server.has_tls1_3: 84758007204Stb tests.add_group(tls13_failing_tests) 84858007204Stb if slow: 84958007204Stb tests.add_group(tls13_slow_failing_tests) 85058007204Stb tests.add_group(tls12_failing_tests) 85158007204Stb 85258007204Stb if tls_server.has_tls1_3: 85358007204Stb tests.add_group(tls13_tests) 85458007204Stb if slow: 85558007204Stb tests.add_group(tls13_slow_tests) 85658007204Stb else: 85758007204Stb tests.add_group(legacy_tests) 85858007204Stb 85958007204Stb tests.add_group(tls12_tests) 86058007204Stb if slow: 86158007204Stb tests.add_group(tls12_slow_tests) 86258007204Stb 86358007204Stb success = tests.run() 86458007204Stb del tests 86558007204Stb 86658007204Stb if not success: 86758007204Stb print("FAILED") 86858007204Stb exit(1) 86958007204Stb 87058007204Stbif __name__ == "__main__": 87158007204Stb main() 872