def test_process_with_size(self): node = SetMaxRecordSize(2048) state = ConnectionState() state.msg_sock = mock.MagicMock() node.process(state) self.assertEqual(2048, state.msg_sock.recordSize)
def test_process(self): node = SetMaxRecordSize() state = ConnectionState() state.msg_sock = mock.MagicMock() state.msg_sock.recordSize = 1024 node.process(state) self.assertEqual(2**14, state.msg_sock.recordSize)
def main(): # # Test if server can handle handshake protocol messages fragmented over # multiple records # host = "localhost" port = 4433 num_limit = None run_exclude = set() expected_failures = {} last_exp_tmp = None argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:n:x:X:", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-n': num_limit = int(arg) elif opt == '-x': expected_failures[arg] = None last_exp_tmp = str(arg) elif opt == '-X': if not last_exp_tmp: raise ValueError("-x has to be specified before -X") expected_failures[last_exp_tmp] = str(arg) elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {21: TLSExtension().create(21, bytearray(10))} node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child( ExpectServerHello(extensions={ExtensionType.renegotiation_info: None})) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # 2**14-49 - max size of Client Hello for OpenSSL # 2**16-5 - max size of extensions in TLS # 2**14-52 - min size of extension that will cause the message to be # fragmented over multiple records # # note: None for record_len will cause the limit to be set to protocol # maximum - 2**14 for name, ext_len, record_len in [ ("small hello", 20, None), ("medium hello", 1024, None), ("medium hello, pow2 fragmentation", 1024, 127), ("medium hello, pow2 fragmentation", 1024, 128), ("medium hello, pow2 fragmentation", 1024, 128), ("medium hello, pow2 fragmentation", 1024, 255), ("medium hello, pow2 fragmentation", 1024, 256), ("medium hello, pow2 fragmentation", 1024, 257), ("big, non fragmented", 2**12, None), ("big, needs fragmentation", 2**14 - 49, None), ("big, needs fragmentation", 2**14 - 48, None), ("big, needs fragmentation", 2**15, None), ("maximum size", 2**16 - 5, None), ("small, reasonable fragmentation", 20, 1024), ("medium, reasonable fragmentation", 1024, 1024), ("big, reasonable fragmentation", 2**12, 1024), ("small, excessive fragmentation", 20, 20), ("medium, excessive fragmentation", 1024, 20), ("big, excessive fragmentation", 2**12, 20), ("small, maximum fragmentation", 20, 1), ("medium, maximum fragmentation", 1024, 1), ("maximum size without fragmentation", 2**14 - 53, None) ]: conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(record_len)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {21: TLSExtension().create(21, bytearray(ext_len))} node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child( ExpectServerHello( extensions={ExtensionType.renegotiation_info: None})) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))) node = node.add_child(ExpectApplicationData()) # XXX RFCs do NOT consider Alerts special with regards to fragmentation node = node.add_child(SetMaxRecordSize(2)) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() if record_len is None: record_len = "max" conversations[name + ": " + str(record_len) + " fragment - " + str(ext_len) + "B extension"] = conversation # check if records bigger than TLSPlaintext limit are rejected padding_extension = TLSExtension().create(21, bytearray(2**14 - 52)) conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, extensions={21: padding_extension})) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["non fragmented, over fragmentation limit: " + str(2**16 - 1) + " fragment - " + str(2**14 - 52) + "B extension"] = conversation # run the conversation good = 0 bad = 0 xfail = 0 xpass = 0 failed = [] xpassed = [] if not num_limit: num_limit = len(conversations) # make sure that sanity test is run first and last # to verify that server was running and kept running throughout sanity_tests = [('sanity', conversations['sanity'])] if run_only: if num_limit > len(run_only): num_limit = len(run_only) regular_tests = [(k, v) for k, v in conversations.items() if k in run_only] else: regular_tests = [(k, v) for k, v in conversations.items() if (k != 'sanity') and k not in run_exclude] sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests))) ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True exception = None try: runner.run() except Exception as exp: exception = exp print("Error while processing") print(traceback.format_exc()) res = False if c_name in expected_failures: if res: xpass += 1 xpassed.append(c_name) print("XPASS-expected failure but test passed\n") else: if expected_failures[c_name] is not None and \ expected_failures[c_name] not in str(exception): bad += 1 failed.append(c_name) print("Expected error message: {0}\n".format( expected_failures[c_name])) else: xfail += 1 print("OK-expected failure\n") else: if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Test end") print(20 * '=') print("version: {0}".format(version)) print(20 * '=') print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests))) print("SKIP: {0}".format( len(run_exclude.intersection(conversations.keys())))) print("PASS: {0}".format(good)) print("XFAIL: {0}".format(xfail)) print("FAIL: {0}".format(bad)) print("XPASS: {0}".format(xpass)) print(20 * '=') sort = sorted(xpassed, key=natural_sort_keys) if len(sort): print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) sort = sorted(failed, key=natural_sort_keys) if len(sort): print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) if bad or xpass: sys.exit(1)
def main(): host = "localhost" port = 4433 num_limit = None run_exclude = set() argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-n': num_limit = int(arg) elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversations_long = {} conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectApplicationData() node = node.next_sibling.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["sanity"] = conversation for cipher in [ CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_CHACHA20_POLY1305_SHA256 ]: conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28) + b"\r\n\r\n") assert len(data) == 2**14 node = node.add_child(ApplicationDataGenerator(data)) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectApplicationData() node = node.next_sibling.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["max size payload in app_data, cipher {0}".format( CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1 node = node.add_child(ApplicationDataGenerator(data)) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations["too big payload in app_data, data size: 2**14 + 1, " "cipher {0}".format( CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 - 8) + b"\r\n\r\n") assert len(data) == 2**14 - 8 padding_size = 2**14 + 2 - len(data) - 1 node = node.add_child( SetPaddingCallback( SetPaddingCallback.add_fixed_padding_cb(padding_size))) node = node.add_child(ApplicationDataGenerator(data)) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations["too big plaintext, size: 2**14 - 8, with an additional" " {0} bytes of padding, cipher {1}".format( padding_size, CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) # Finished msg will add 32 bytes # 3 bytes for msg length # 1 byte for content type padding_left = 2**14 - 32 - 4 node = node.add_child(FinishedGenerator(pad_left=padding_left)) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error) node.next_sibling.add_child(ExpectClose()) conversations[ "max size payload (2**14) of Finished msg, with {0} bytes" " of left padding, cipher {1}".format( padding_left, CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) # Finished msg will add 32 bytes # 3 bytes for msg length # 1 byte for content type padding_left = 2**14 - 32 - 4 + 1 node = node.add_child(FinishedGenerator(pad_left=padding_left)) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations[ "too big payload (2**14 + 1) of Finished msg, with {0} bytes" " of left padding, cipher {1}".format( padding_left, CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) # 32 bytes added as finished msg payload # 16 bytes added after encryption # 3 bytes for msg length # 1 byte for content type padding_size = 2**14 + 256 - 32 - 16 - 5 node = node.add_child( SetPaddingCallback( SetPaddingCallback.add_fixed_padding_cb(padding_size))) node = node.add_child(FinishedGenerator()) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations[ "max size of Finished msg, with {0} bytes of record layer" " padding {1}".format( padding_size, CipherSuite.ietfNames[cipher])] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create( key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) # 32 bytes added as finished msg payload # 16 bytes added after encryption # 3 bytes for msg length # 1 byte for content type padding_size = 2**14 + 256 - 32 - 16 - 5 + 1 node = node.add_child( SetPaddingCallback( SetPaddingCallback.add_fixed_padding_cb(padding_size))) node = node.add_child(FinishedGenerator()) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations["too big Finished msg, with {0} bytes of record layer" " padding, cipher {1}".format( padding_size, CipherSuite.ietfNames[cipher])] = conversation # AEAD tag fuzzed for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]: for pos in range(-1, -17, -1): conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {} groups = [GroupName.secp256r1] ext[ExtensionType.key_share] = key_share_ext_gen(groups) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 256 - 16) + b"\r\n\r\n") assert len(data) == 2**14 + 256 - 16 msg = ApplicationDataGenerator(data) node = node.add_child(fuzz_encrypted_message(msg, xors={pos: val})) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow) node.next_sibling.add_child(ExpectClose()) conversations_long["too big ciphertext with size 2**14 + 256 and" "fuzzed tag with {0} on pos {1}".format( val, pos)] = conversation conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) padding_size = 2**14 - 216 - 1 ext[ExtensionType.client_hello_padding] = PaddingExtension().create( padding_size) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectEncryptedExtensions()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectCertificateVerify()) node = node.add_child(ExpectFinished()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) # This message is optional and may show up 0 to many times cycle = ExpectNewSessionTicket() node = node.add_child(cycle) node.add_child(cycle) node.next_sibling = ExpectApplicationData() node = node.next_sibling.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["max size of ClientHello msg, with {0} bytes of padding". format(padding_size)] = conversation conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {} groups = [GroupName.secp256r1] key_shares = [] for group in groups: key_shares.append(key_share_gen(group)) ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares) ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\ .create([TLS_1_3_DRAFT, (3, 3)]) ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) sig_algs = [ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256 ] ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\ .create(sig_algs) ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\ .create(RSA_SIG_ALL) padding_size = 2**14 - 216 ext[ExtensionType.client_hello_padding] = PaddingExtension().create( padding_size) node = node.add_child(SetMaxRecordSize(2**16 - 1)) node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) node.add_child(ExpectClose()) conversations["too big ClientHello msg, with {0} bytes of padding".format( padding_size)] = conversation # run the conversation good = 0 bad = 0 failed = [] if not num_limit: num_limit = len(conversations_long) # make sure that sanity test is run first and last # to verify that server was running and kept running throughout sanity_tests = [('sanity', conversations['sanity'])] short_tests = [(k, v) for k, v in conversations.items() if k != 'sanity'] long_tests = list(conversations_long.items()) long_sampled_tests = sample(long_tests, min(num_limit, len(long_tests))) regular_tests = sample(long_sampled_tests + short_tests, len(long_sampled_tests) + len(short_tests)) ordered_tests = chain(sanity_tests, regular_tests, sanity_tests) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True try: runner.run() except Exception: print("Error while processing") print(traceback.format_exc()) res = False if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Verify that too big Application Data messages, with") print("different ciphers or fuzzed AEAD tag in TLS 1.3 communication ") print("are rejected with record_overflow alert.\n") print("Note that there are three limits: one for TLSCiphertext (encrypted") print("records, as visible on the line), a second one for") print("TLSInnertPlaintext (the data that the ciphertext decrypts to) and") print("a third one for TLSPlaintext (the records that are not encrypted,") print("like ClientHello).") print("This test checks all three of them.\n") print("See RFC 8446 Section 5.1, 5.2 and 5.4.\n") print("version: {0}\n".format(version)) print("Test end") print("successful: {0}".format(good)) print("failed: {0}".format(bad)) failed_sorted = sorted(failed, key=natural_sort_keys) print(" {0}".format('\n '.join(repr(i) for i in failed_sorted))) if bad > 0: sys.exit(1)
def main(): host = "localhost" port = 4433 num_limit = 50 run_exclude = set() expected_failures = {} last_exp_tmp = None extra_exts = False argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help", "extra-exts"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-x': expected_failures[arg] = None last_exp_tmp = str(arg) elif opt == '-X': if not last_exp_tmp: raise ValueError("-x has to be specified before -X") expected_failures[last_exp_tmp] = str(arg) elif opt == '-n': num_limit = int(arg) elif opt == '--extra-exts': extra_exts = True elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation if not extra_exts: exts = None else: exts = {} exts[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create([GroupName.secp256r1, GroupName.ffdhe2048, GroupName.x25519]) exts[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) exts[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # reject ciphers in TLS1.1 conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, version=(3, 2), extensions=exts)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure)) node.add_child(ExpectClose()) conversations["Chacha20 in TLS1.1"] = conversation # empty application data message acceptance conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray())) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["empty app data"] = conversation # 1/n-1 message splitting conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray(b"G"))) node = node.add_child( ApplicationDataGenerator(bytearray(b"ET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["1/n-1 record splitting"] = conversation # plaintext just under the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28) + b"\r\n\r\n") assert len(data) == 2**14 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) # allow for multiple application data records in response node = node.add_child(ExpectApplicationData()) loop = node node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) loop.next_sibling = node node.next_sibling = ExpectClose() conversations["max size plaintext"] = conversation # plaintext over the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext - max compress"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) node.add_child(ExpectClose()) conversations["too big plaintext - above TLSCompressed max"] = conversation # fuzz the tag (16 last bytes) for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]: for pos in range(-1, -17, -1): conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) msg = ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n")) node = node.add_child(fuzz_encrypted_message(msg, xors={pos: val})) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["fuzz tag with {0} on pos {1}".format(val, pos)] \ = conversation # too small message handling for val in range(16): conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=exts)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) # any byte value will do, a1 chosen at random msg = PlaintextMessageGenerator(ContentType.application_data, bytearray([0xa1] * val)) node = node.add_child(msg) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["{0} bytes long ciphertext".format(val)] \ = conversation # run the conversation good = 0 bad = 0 xfail = 0 xpass = 0 failed = [] xpassed = [] if not num_limit: num_limit = len(conversations) # make sure that sanity test is run first and last # to verify that server was running and kept running throughout sanity_tests = [('sanity', conversations['sanity'])] if run_only: if num_limit > len(run_only): num_limit = len(run_only) regular_tests = [(k, v) for k, v in conversations.items() if k in run_only] else: regular_tests = [(k, v) for k, v in conversations.items() if (k != 'sanity') and k not in run_exclude] sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests))) ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests) for c_name, c_test in ordered_tests: print("{0} ...".format(c_name)) runner = Runner(c_test) res = True exception = None try: runner.run() except Exception as exp: exception = exp print("Error while processing") print(traceback.format_exc()) res = False if c_name in expected_failures: if res: xpass += 1 xpassed.append(c_name) print("XPASS-expected failure but test passed\n") else: if expected_failures[c_name] is not None and \ expected_failures[c_name] not in str(exception): bad += 1 failed.append(c_name) print("Expected error message: {0}\n".format( expected_failures[c_name])) else: xfail += 1 print("OK-expected failure\n") else: if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Test end") print(20 * '=') print("version: {0}".format(version)) print(20 * '=') print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests))) print("SKIP: {0}".format( len(run_exclude.intersection(conversations.keys())))) print("PASS: {0}".format(good)) print("XFAIL: {0}".format(xfail)) print("FAIL: {0}".format(bad)) print("XPASS: {0}".format(xpass)) print(20 * '=') sort = sorted(xpassed, key=natural_sort_keys) if len(sort): print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) sort = sorted(failed, key=natural_sort_keys) if len(sort): print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) if bad > 0: sys.exit(1)
def main(): # # Test if server can handle handshake protocol messages fragmented over # multiple records # host = "localhost" port = 4433 run_exclude = set() argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {21: TLSExtension().create(21, bytearray(10))} node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child( ExpectServerHello(extensions={ExtensionType.renegotiation_info: None})) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # 2**14-49 - max size of Client Hello for OpenSSL # 2**16-5 - max size of extensions in TLS # 2**14-52 - min size of extension that will cause the message to be # fragmented over multiple records # # note: None for record_len will cause the limit to be set to protocol # maximum - 2**14 for name, ext_len, record_len in [ ("small hello", 20, None), ("medium hello", 1024, None), ("medium hello, pow2 fragmentation", 1024, 127), ("medium hello, pow2 fragmentation", 1024, 128), ("medium hello, pow2 fragmentation", 1024, 128), ("medium hello, pow2 fragmentation", 1024, 255), ("medium hello, pow2 fragmentation", 1024, 256), ("medium hello, pow2 fragmentation", 1024, 257), ("big, non fragmented", 2**12, None), ("big, needs fragmentation", 2**14 - 49, None), ("big, needs fragmentation", 2**14 - 48, None), ("big, needs fragmentation", 2**15, None), ("maximum size", 2**16 - 5, None), ("small, reasonable fragmentation", 20, 1024), ("medium, reasonable fragmentation", 1024, 1024), ("big, reasonable fragmentation", 2**12, 1024), ("small, excessive fragmentation", 20, 20), ("medium, excessive fragmentation", 1024, 20), ("big, excessive fragmentation", 2**12, 20), ("small, maximum fragmentation", 20, 1), ("medium, maximum fragmentation", 1024, 1), ("maximum size without fragmentation", 2**14 - 53, None) ]: conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(record_len)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] ext = {21: TLSExtension().create(21, bytearray(ext_len))} node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child( ExpectServerHello( extensions={ExtensionType.renegotiation_info: None})) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))) node = node.add_child(ExpectApplicationData()) # XXX RFCs do NOT consider Alerts special with regards to fragmentation node = node.add_child(SetMaxRecordSize(2)) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() if record_len is None: record_len = "max" conversations[name + ": " + str(record_len) + " fragment - " + str(ext_len) + "B extension"] = conversation # check if records bigger than TLSPlaintext limit are rejected padding_extension = TLSExtension().create(21, bytearray(2**14 - 52)) conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, extensions={21: padding_extension})) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["non fragmented, over fragmentation limit: " + str(2**16 - 1) + " fragment - " + str(2**14 - 52) + "B extension"] = conversation # run the conversation good = 0 bad = 0 failed = [] # make sure that sanity test is run first and last # to verify that server was running and kept running throught sanity_test = ('sanity', conversations['sanity']) ordered_tests = chain([sanity_test], filter(lambda x: x[0] != 'sanity', conversations.items()), [sanity_test]) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True try: runner.run() except: print("Error while processing") print(traceback.format_exc()) res = False if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Test end") print("successful: {0}".format(good)) print("failed: {0}".format(bad)) failed_sorted = sorted(failed, key=natural_sort_keys) print(" {0}".format('\n '.join(repr(i) for i in failed_sorted))) if bad > 0: sys.exit(1)
def main(): host = "localhost" port = 4433 num_limit = None run_exclude = set() dhe = False argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:n:d", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-n': num_limit = int(arg) elif opt == '-d': dhe = True elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_RSA_WITH_AES_256_CCM, CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_RSA_WITH_AES_256_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # reject ciphers in TLS1.1 conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_RSA_WITH_AES_256_CCM, CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_RSA_WITH_AES_256_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 2))) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure)) node.add_child(ExpectClose()) conversations["AES-CCM in TLS1.1"] = conversation # empty application data message acceptance node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray())) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["empty app data"] = conversation # empty application data message acceptance with _8 ciphers node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray())) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["empty app data with _8 ciphers"] = conversation # 1/n-1 message splitting conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray(b"G"))) node = node.add_child( ApplicationDataGenerator(bytearray(b"ET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["1/n-1 record splitting"] = conversation # plaintext just under the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28) + b"\r\n\r\n") assert len(data) == 2**14 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) # allow for multiple application data records in response node = node.add_child(ExpectApplicationData()) loop = node node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) loop.next_sibling = node node.next_sibling = ExpectClose() conversations["max size plaintext"] = conversation # plaintext over the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28) + b"\r\n\r\n") assert len(data) == 2**14 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) # allow for multiple application data records in response node = node.add_child(ExpectApplicationData()) loop = node node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) loop.next_sibling = node node.next_sibling = ExpectClose() conversations["max size plaintext with _8 ciphers"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext with _8 ciphers"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext - max compress"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations[ "too big plaintext - max compress with _8 ciphers"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) node.add_child(ExpectClose()) conversations["too big plaintext - above TLSCompressed max"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) node.add_child(ExpectClose()) conversations[ "too big plaintext - above TLSCompressed max with _8 ciphers"] = conversation # fuzz the tag (last 16 bytes or last 8 bytes in case of _8 ciphers) for n in [17, 9]: for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]: for pos in range(-1, -n, -1): conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) if n == 17: ciphers = [ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM ] else: ciphers = [ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 ] else: ext = None if n == 17: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CCM] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_256_CCM_8] node = node.add_child( ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) msg = ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n")) node = node.add_child( fuzz_encrypted_message(msg, xors={pos: val})) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["fuzz tag with {0} on pos {1} - using cipher with {2} byte tag".format(val, pos, n-1)] \ = conversation # too small message handling for val in range(16): conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) # any byte value will do, a1 chosen at random msg = PlaintextMessageGenerator(ContentType.application_data, bytearray([0xa1] * val)) node = node.add_child(msg) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["{0} bytes long ciphertext".format(val)] \ = conversation # too small message handling against _8 ciphers for val in range(8): conversation = Connect(host, port) node = conversation ext = {} if dhe: groups = [GroupName.secp256r1, GroupName.ffdhe2048] ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\ .create(groups) ext[ExtensionType.signature_algorithms] = \ SignatureAlgorithmsExtension().create(SIG_ALL) ext[ExtensionType.signature_algorithms_cert] = \ SignatureAlgorithmsCertExtension().create(SIG_ALL) ciphers = [ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] else: ext = None ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CCM_8, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) # any byte value will do, a1 chosen at random msg = PlaintextMessageGenerator(ContentType.application_data, bytearray([0xa1] * val)) node = node.add_child(msg) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["{0} bytes long ciphertext against _8 ciphers".format(val)] \ = conversation # run the conversation good = 0 bad = 0 failed = [] if not num_limit: num_limit = len(conversations) # make sure that sanity test is run first and last # to verify that server was running and kept running throughout sanity_tests = [('sanity', conversations['sanity'])] regular_tests = [(k, v) for k, v in conversations.items() if k != 'sanity'] sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests))) ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True try: runner.run() except: print("Error while processing") print(traceback.format_exc()) res = False if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("This script runs fuzzing tests against TLS1.2 AES-CCM ciphers") print("version: {0}\n".format(version)) print("Test end") print("successful: {0}".format(good)) print("failed: {0}".format(bad)) failed_sorted = sorted(failed, key=natural_sort_keys) print(" {0}".format('\n '.join(repr(i) for i in failed_sorted))) if bad > 0: sys.exit(1)
def main(): # # Test if server can handle handshake protocol messages fragmented over # multiple records # conversations = {} # 2**14-49 - max size of Client Hello for OpenSSL # 2**16-5 - max size of extensions in TLS # 2**14-52 - min size of extension that will cause the message to be # fragmented over multiple records # # note: None for record_len will cause the limit to be set to protocol # maximum - 2**14 for name, ext_len, record_len in [ ("small hello", 20, None), ("medium hello", 1024, None), ("big, non fragmented", 2**12, None), ("big, needs fragmentation", 2**14 - 49, None), ("big, needs fragmentation", 2**14 - 48, None), ("big, needs fragmentation", 2**15, None), ("maximum size", 2**16 - 5, None), ("small, reasonable fragmentation", 20, 1024), ("medium, reasonable fragmentation", 1024, 1024), ("big, reasonable fragmentation", 2**12, 1024), ("small, excessive fragmentation", 20, 20), ("medium, excessive fragmentation", 1024, 20), ("big, excessive fragmentation", 2**12, 20), ("small, maximum fragmentation", 20, 1), ("medium, maximum fragmentation", 1024, 1), ("maximum size without fragmentation", 2**14 - 53, None) ]: padding_extension = lambda _, l=ext_len: TLSExtension().create( 21, bytearray(l)) conversation = Connect("localhost", 4433) node = conversation node = node.add_child(SetMaxRecordSize(record_len)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, extensions={21: padding_extension})) node = node.add_child( ExpectServerHello( extensions={ExtensionType.renegotiation_info: None})) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))) node = node.add_child(ExpectApplicationData()) # RFCs do not consider Alerts special with regards to fragmentation #node = node.add_child(SetMaxRecordSize(2)) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() if record_len is None: record_len = "max" conversations[name + ": " + str(record_len) + " fragment - " + str(ext_len) + "B extension"] = conversation # check if records bigger than TLSPlaintext limit are rejected padding_extension = lambda _: TLSExtension().create( 21, bytearray(2**14 - 52)) conversation = Connect("localhost", 4433) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child( ClientHelloGenerator(ciphers, extensions={21: padding_extension})) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["non fragmented, over fragmentation limit: " + str(2**16 - 1) + " fragment - " + str(2**14 - 52) + "B extension"] = conversation # run the conversation good = 0 bad = 0 for conversation_name in conversations: conversation = conversations[conversation_name] runner = Runner(conversation) print(str(conversation_name) + "...\n") res = True try: runner.run() except: print("Error while processing") print(traceback.format_exc()) res = False if res: good += 1 else: bad += 1 print("Test end") print("successful: {0}".format(good)) print("failed: {0}".format(bad)) if bad > 0: sys.exit(1)
def main(): host = "localhost" port = 4433 num_limit = None run_exclude = set() argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-n': num_limit = int(arg) elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # reject ciphers in TLS1.1 conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 2))) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure)) node.add_child(ExpectClose()) conversations["Chacha20 in TLS1.1"] = conversation # empty application data message acceptance conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray())) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["empty app data"] = conversation # 1/n-1 message splitting conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray(b"G"))) node = node.add_child( ApplicationDataGenerator(bytearray(b"ET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) node.next_sibling = ExpectClose() conversations["1/n-1 record splitting"] = conversation # plaintext just under the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28) + b"\r\n\r\n") assert len(data) == 2**14 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) # allow for multiple application data records in response node = node.add_child(ExpectApplicationData()) loop = node node = node.add_child( ExpectAlert(AlertLevel.warning, AlertDescription.close_notify)) loop.next_sibling = node node.next_sibling = ExpectClose() conversations["max size plaintext"] = conversation # plaintext over the maximum permissible conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, [ AlertDescription.decompression_failure, AlertDescription.record_overflow ])) node.add_child(ExpectClose()) conversations["too big plaintext - max compress"] = conversation conversation = Connect(host, port) node = conversation node = node.add_child(SetMaxRecordSize(2**16 - 1)) ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" * (2**14 + 1024 - 28 + 1) + b"\r\n\r\n") assert len(data) == 2**14 + 1024 + 1 node = node.add_child(ApplicationDataGenerator(data)) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) node.add_child(ExpectClose()) conversations["too big plaintext - above TLSCompressed max"] = conversation # fuzz the tag (16 last bytes) for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]: for pos in range(-1, -17, -1): conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) msg = ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n")) node = node.add_child(fuzz_encrypted_message(msg, xors={pos: val})) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["fuzz tag with {0} on pos {1}".format(val, pos)] \ = conversation # too small message handling for val in range(16): conversation = Connect(host, port) node = conversation ciphers = [ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] node = node.add_child(ClientHelloGenerator(ciphers)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) # any byte value will do, a1 chosen at random msg = PlaintextMessageGenerator(ContentType.application_data, bytearray([0xa1] * val)) node = node.add_child(msg) node = node.add_child( ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac)) node.add_child(ExpectClose()) conversations["{0} bytes long ciphertext".format(val)] \ = conversation # run the conversation good = 0 bad = 0 failed = [] if not num_limit: num_limit = len(conversations) # make sure that sanity test is run first and last # to verify that server was running and kept running throught sanity_test = ('sanity', conversations['sanity']) ordered_tests = chain([sanity_test], islice( filter(lambda x: x[0] != 'sanity', conversations.items()), num_limit), [sanity_test]) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True try: runner.run() except: print("Error while processing") print(traceback.format_exc()) res = False if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Test end") print("successful: {0}".format(good)) print("failed: {0}".format(bad)) failed_sorted = sorted(failed, key=natural_sort_keys) print(" {0}".format('\n '.join(repr(i) for i in failed_sorted))) if bad > 0: sys.exit(1)
def main(): host = "localhost" port = 4433 num_limit = None run_exclude = set() expected_failures = {} last_exp_tmp = None dhe = False argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help"]) for opt, arg in opts: if opt == '-h': host = arg elif opt == '-p': port = int(arg) elif opt == '-e': run_exclude.add(arg) elif opt == '-x': expected_failures[arg] = None last_exp_tmp = str(arg) elif opt == '-X': if not last_exp_tmp: raise ValueError("-x has to be specified before -X") expected_failures[last_exp_tmp] = str(arg) elif opt == '-n': num_limit = int(arg) elif opt == '-d': dhe = True elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if args: run_only = set(args) else: run_only = None conversations = {} conversation = Connect(host, port) node = conversation if dhe: ext = {} add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ext = None ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectServerHello()) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["sanity"] = conversation # first check if the server supports it conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["negotiate heartbeat extension"] = conversation # finally check if the server will reply to a heartbeat conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: HeartbeatExtension().create(HeartbeatMode.PEER_ALLOWED_TO_SEND), ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["negotiate and check for peer_allowed_to_send"] = \ conversation # send valid heartbeat, check for reply conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b'heartbeat test'))) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b'heartbeat test'))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["check if server replies to heartbeats after handshake"] = \ conversation # verify that padding is minimal in responses conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b'heartbeat test'))) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b'heartbeat test'), padding_size=16)) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["check padding size in response"] = \ conversation # send valid empty heartbeat, check for reply conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b''))) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b''))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["empty heartbeat"] = \ conversation # verify that server replies with minimal size of padding conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b''))) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b''), padding_size=16)) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["empty heartbeat - verify padding size"] = \ conversation # check if server will reply to requests even if we set that we don't # want any requests conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_NOT_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b'heartbeat test'))) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b'heartbeat test'))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["heartbeat with peer_not_allowed_to_send"] = \ conversation # check if invalid modes are rejected by server # 1 and 2 are allocated for mode in chain([0], range(3, 256)): conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(mode)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) node = node.add_child(ExpectAlert(AlertLevel.fatal, AlertDescription.illegal_parameter)) node.add_child(ExpectClose()) conversations["invalid mode in extension - {0}".format(mode)] = \ conversation # invalid type of heartbeat type for hb_type in chain([0], range(2, 256)): conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) # in case the heartbeat is malformed, the server is expected to drop # it silently node = node.add_child(HeartbeatGenerator(bytearray(b'heartbeat test'), message_type=hb_type)) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["invalid heartbeat type - {0}".format(hb_type)] = \ conversation for pad_len in range(0, 16): # check too small padding with empty payload conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b''), padding_length=pad_len)) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["empty heartbeat, small padding - {0}".format(pad_len)] = \ conversation conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(HeartbeatGenerator(bytearray(b'some payload?'), padding_length=pad_len)) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["heartbeat with small padding - {0}".format(pad_len)] = \ conversation # check if server replies with small padding even if we send large one # and that if we send too large padding, the server rejects the message # as malformed for pad_size in [17, 32, 64, 128, 1024, 2**14-3-14, 2**14-3-13]: conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) if pad_size > 2**13: node = node.add_child(SetMaxRecordSize(2**16)) node = node.add_child(HeartbeatGenerator(bytearray(b'heartbeat test'), padding_length=pad_size)) if pad_size > 2**14-3-14: node = node.add_child(ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow)) else: node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectHeartbeat(payload=bytearray(b'heartbeat test'), padding_size=16)) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["large padding ({0}) check padding size in response" .format(pad_size)] = \ conversation # fuzz the payload_length in message # the length is in the 2nd and 3rd byte from the beginning # and the minimal size is 13, but then too small paddings are tested above # so actual minimal size that is worth testing is 29 for subs in [{1: 255, 2: 255}, {1: 0, 2: 255}, {1: 0, 2: 29}, {1: 0, 2: 30}, {1: 0, 2: 31}, {1: 0, 2: 32}, {1: 0, 2: 64}, {1: 1, 2: 0}, {1: 63, 2: 252}, {1: 63, 2: 253}, {1: 63, 2: 254}, {1: 63, 2: 254}, {1: 64, 2: 0}]: conversation = Connect(host, port) node = conversation ext = {ExtensionType.heartbeat: HeartbeatExtension() .create(HeartbeatMode.PEER_ALLOWED_TO_SEND)} if dhe: add_dhe_extensions(ext) ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] else: ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext)) ext = {ExtensionType.heartbeat: None, ExtensionType.renegotiation_info: None} node = node.add_child(ExpectServerHello(extensions=ext)) node = node.add_child(ExpectCertificate()) if dhe: node = node.add_child(ExpectServerKeyExchange()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( fuzz_message(HeartbeatGenerator(bytearray(b'some payload?')), substitutions=subs)) node = node.add_child(ApplicationDataGenerator( bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() conversations["heartbeat with fuzzed payload length - {0}" .format(subs)] = \ conversation # run the conversation good = 0 bad = 0 xfail = 0 xpass = 0 failed = [] xpassed = [] if not num_limit: num_limit = len(conversations) # make sure that sanity test is run first and last # to verify that server was running and kept running throughout sanity_tests = [('sanity', conversations['sanity'])] if run_only: if num_limit > len(run_only): num_limit = len(run_only) regular_tests = [(k, v) for k, v in conversations.items() if k in run_only] else: regular_tests = [(k, v) for k, v in conversations.items() if (k != 'sanity') and k not in run_exclude] sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests))) ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests) for c_name, c_test in ordered_tests: if run_only and c_name not in run_only or c_name in run_exclude: continue print("{0} ...".format(c_name)) runner = Runner(c_test) res = True exception = None try: runner.run() except Exception as exp: exception = exp print("Error while processing") print(traceback.format_exc()) res = False if c_name in expected_failures: if res: xpass += 1 xpassed.append(c_name) print("XPASS-expected failure but test passed\n") else: if expected_failures[c_name] is not None and \ expected_failures[c_name] not in str(exception): bad += 1 failed.append(c_name) print("Expected error message: {0}\n" .format(expected_failures[c_name])) else: xfail += 1 print("OK-expected failure\n") else: if res: good += 1 print("OK\n") else: bad += 1 failed.append(c_name) print("Check if the heartbeat extension is implemented correctly") print("Also checks if the server is not vulnerable to heartbleed") print("(CVE-2014-0160)\n") print("Test end") print(20 * '=') print("version: {0}".format(version)) print(20 * '=') print("TOTAL: {0}".format(len(sampled_tests) + 2*len(sanity_tests))) print("SKIP: {0}".format(len(run_exclude.intersection(conversations.keys())))) print("PASS: {0}".format(good)) print("XFAIL: {0}".format(xfail)) print("FAIL: {0}".format(bad)) print("XPASS: {0}".format(xpass)) print(20 * '=') sort = sorted(xpassed ,key=natural_sort_keys) if len(sort): print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) sort = sorted(failed, key=natural_sort_keys) if len(sort): print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort))) if bad > 0: sys.exit(1)
def test___init__(self): node = SetMaxRecordSize() self.assertIsNotNone(node)