예제 #1
0
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)
예제 #2
0
    def test___repr__(self):
        rand = StructuredRandom([(16, 0)])

        self.assertEqual(str(rand), "StructuredRandom(vals=[(16, 0)])")
예제 #3
0
    def test_data_with_random(self):
        rand = StructuredRandom([(16, None)])

        self.assertEqual(len(rand.data), 16)
        self.assertGreater(len(set(rand.data)), 1)
예제 #4
0
    def test_data(self):
        rand = StructuredRandom([(16, 0)])

        self.assertEqual(rand.data, bytearray([0] * 16))