def main(): """Check if incorrect padding and MAC is rejected by server.""" host = "localhost" port = 4433 num_limit = 1024 rand_limit = None run_exclude = set() expected_failures = {} last_exp_tmp = None dhe = False cipher = None splitting = None argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:x:X:n:dC:", ["help", "random=", "1/n-1", "0/n"]) 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 == '--random': rand_limit = int(arg)//2 elif opt == '-d': dhe = True elif opt == '--1/n-1': splitting = 1 elif opt == '--0/n': splitting = 0 elif opt == '-C': if arg[:2] == '0x': cipher = int(arg, 16) else: try: cipher = getattr(CipherSuite, arg) except AttributeError: cipher = int(arg) elif opt == '--help': help_msg() sys.exit(0) else: raise ValueError("Unknown option: {0}".format(opt)) if dhe and cipher is not None: raise ValueError("-C and -d are mutually exclusive") if cipher is None: if dhe: cipher = CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA else: cipher = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA block_size = 16 if cipher in CipherSuite.tripleDESSuites: block_size = 8 if rand_limit is None: if block_size == 16: rand_limit = 4096 else: assert block_size == 8 rand_limit = 8192 dhe = cipher in CipherSuite.ecdhAllSuites or \ cipher in CipherSuite.dhAllSuites if args: run_only = set(args) else: run_only = None # if we are to execute only some tests, we need to not filter the # static ones if run_only: num_limit = None conversations = {} conversation = Connect(host, port) node = conversation if dhe: ext = {} add_dhe_extensions(ext) else: ext = None ciphers = [cipher, 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()) # handle servers that ask for client certificates node = node.add_child(ExpectCertificateRequest()) fork = node node = node.add_child(ExpectServerHelloDone()) node = node.add_child(CertificateGenerator()) # handle servers that don't ask for client certificates fork.next_sibling = ExpectServerHelloDone() # join both paths join = ClientKeyExchangeGenerator() fork.next_sibling.add_child(join) node = node.add_child(join) 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(b"GET / HTTP/1.0\r\n\r\n")) if splitting is not None: node = node.add_child(ExpectApplicationData(size=splitting)) 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() node = node.add_child(ExpectClose()) conversations["sanity"] = \ conversation # test all combinations of lengths and values for plaintexts up to 256 # bytes long with uniform content (where every byte has the same value) mono_tests = [(length, value) for length in range(block_size, 257, block_size) for value in range(256)] if not num_limit: mono_iter = mono_tests rand_len_to_generate = rand_limit else: # we want to speed up generation, so generate only as many conversations # as necessary to meet num_limit, but do that uniformely between # random payloads, mono payloads, application_data tests and handshake # tests ratio = rand_limit * 1.0 / len(mono_tests) # `num_limit / 2` because of handshake and application_data tests mono_len_to_generate = min(len(mono_tests), int(ceil((num_limit / 2) * (1 - ratio)))) rand_len_to_generate = int(ceil((num_limit) / 2 * ratio)) mono_iter = sample(mono_tests, mono_len_to_generate) mono = (StructuredRandom([(length, value)]) for length, value in mono_iter) # 2**14 is the TLS protocol max rand = structured_random_iter(rand_len_to_generate, min_length=block_size, max_length=2**14, step=block_size) if not run_only: for data in chain(mono, rand): add_app_data_conversation(conversations, host, port, cipher, dhe, data) else: for conv in run_only: if "Application Data" in conv: params = parse_structured_random_params(conv) data = StructuredRandom(params) add_app_data_conversation(conversations, host, port, cipher, dhe, data) # do th same thing but for handshake record # (note, while the type is included in the MAC, we are never # sending a valid MAC, so the server has only the record layer header to # deduce if the message needs special handling, if any) # test all combinations of lengths and values for plaintexts up to 256 # bytes long with uniform content (where every byte has the same value) mono_tests = [(length, value) for length in range(block_size, 257, block_size) for value in range(256)] if not num_limit: mono_iter = mono_tests rand_len_to_generate = rand_limit else: # we want to speed up generation, so generate only as many conversations # as necessary to meet num_limit, but do that uniformely between # random payloads, mono payloads, application_data tests and handshake # tests ratio = rand_limit * 1.0 / len(mono_tests) # `num_limit / 2` because of handshake and application_data tests mono_len_to_generate = min(len(mono_tests), int(ceil((num_limit / 2) * (1 - ratio)))) rand_len_to_generate = int(ceil((num_limit) / 2 * ratio)) mono_iter = sample(mono_tests, mono_len_to_generate) mono = (StructuredRandom([(length, value)]) for length, value in mono_iter) # 2**14 is the TLS protocol max rand = structured_random_iter(rand_len_to_generate, min_length=block_size, max_length=2**14, step=block_size) if not run_only: for data in chain(mono, rand): add_handshake_conversation(conversations, host, port, cipher, dhe, data) else: for conv in run_only: if "Handshake" in conv: params = parse_structured_random_params(conv) data = StructuredRandom(params) add_handshake_conversation(conversations, host, port, cipher, dhe, data) # 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'])] 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 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("Tester for de-padding and MAC verification\n") print("Generates plaintexts that can be incorrectly handled by de-padding") print("and MAC verification algorithms and verifies that they are handled") print("correctly and consistently.\n") print("Should be executed with multiple ciphers (especially regarding the") print("HMAC used) and TLS versions. Note: test requires CBC mode") print("ciphers.\n") print("TLS 1.0 servers should require enabling BEAST workaround, see") print("help message.\n") print("version: {0}\n".format(version)) print("Test end") 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___repr__(self): rand = StructuredRandom([(16, 0)]) self.assertEqual(str(rand), "StructuredRandom(vals=[(16, 0)])")
def test_data_with_random(self): rand = StructuredRandom([(16, None)]) self.assertEqual(len(rand.data), 16) self.assertGreater(len(set(rand.data)), 1)
def test_data(self): rand = StructuredRandom([(16, 0)]) self.assertEqual(rand.data, bytearray([0] * 16))