Пример #1
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    srv_max_prot = None
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d",
                               ["help", "server-max-protocol="])
    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)
        elif opt == '--server-max-protocol':
            srv_max_prot = protocol_name_to_tuple(arg)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    if args:
        run_only = set(args)
    else:
        run_only = None

    conversations = {}

    # normal connection
    conversation = Connect(host, port)
    node = conversation
    if srv_max_prot == None or srv_max_prot == (3, 4):
        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()
    else:
        if dhe:
            ext = {}
            groups = [GroupName.secp256r1, GroupName.ffdhe2048]
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create(groups)
            ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension().create(RSA_SIG_ALL)
            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

    # TLS 1.3 downgrade check
    for prot in [(3, 1), (3, 2), (3, 3)]:
        if srv_max_prot is not None and prot > srv_max_prot:
            continue
        conversation = Connect(host, port)
        node = conversation
        if dhe:
            ext = {}
            groups = [GroupName.secp256r1, GroupName.ffdhe2048]
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create(groups)
            ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension().create(RSA_SIG_ALL)
            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, version=prot))
        node = node.add_child(
            ExpectServerHello(server_max_protocol=srv_max_prot))
        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())
        if prot < (3, 2):
            # 1/n-1 record splitting
            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["TLS 1.3 downgrade check for Protocol {0}".format(
            prot)] = 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'])]
    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("Check if server correctly return ServerHello Random ")
    print("with downgrade protection values to TLS1.2 and below ")
    print("clients\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_tls13(self):
     self.assertEqual((3, 4), protocol_name_to_tuple("TLS1.3"))
Пример #3
0
 def test_unknown(self):
     with self.assertRaises(ValueError):
         protocol_name_to_tuple("SSL3.1")
Пример #4
0
 def test_tls11(self):
     self.assertEqual((3, 2), protocol_name_to_tuple("TLS1.1"))
Пример #5
0
 def test_tls12(self):
     self.assertEqual((3, 3), protocol_name_to_tuple("TLS1.2"))
Пример #6
0
 def test_tls10(self):
     self.assertEqual((3, 1), protocol_name_to_tuple("TLS1.0"))
Пример #7
0
 def test_ssl3(self):
     self.assertEqual((3, 0), protocol_name_to_tuple("SSL3"))
Пример #8
0
 def test_ssl2(self):
     self.assertEqual((0, 2), protocol_name_to_tuple("SSL2"))
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    min_ver = (3, 0)

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help", "min-ver="])
    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)
        elif opt == '--min-ver':
            min_ver = protocol_name_to_tuple(arg)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    if args:
        run_only = set(args)
    else:
        run_only = None

    conversations = {}

    conversation = Connect(host, port, version=(3, 0))
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        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(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

    for c_id, name in [(0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"),
                       (0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"),
                       (0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x000B, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x000E, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"),
                       (0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"),
                       (0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"),
                       (0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"),
                       (0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"),
                       (0x002A, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"),
                       (0x002B, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"),
                       (0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"),
                       (0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"),
                       (0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"),
                       (0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA")]:
        for prot, prot_name in [((3, 3), "TLSv1.2"), ((3, 2), "TLSv1.1"),
                                ((3, 1), "TLSv1.0"), ((3, 0), "SSLv3")]:
            conversation = Connect(host, port, version=(3, 0))
            node = conversation
            ciphers = [
                c_id, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
            ]
            node = node.add_child(ClientHelloGenerator(ciphers, version=prot))
            if prot < min_ver:
                node = node.add_child(
                    ExpectAlert(AlertLevel.fatal,
                                (AlertDescription.protocol_version,
                                 AlertDescription.handshake_failure)))
                node = node.add_child(ExpectClose())
            else:
                node = node.add_child(
                    ExpectServerHello(
                        cipher=CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA))
                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\r\n\r\n")))
                node = node.add_child(ExpectApplicationData())
                node = node.add_child(
                    AlertGenerator(AlertLevel.warning,
                                   AlertDescription.close_notify))
                # allow for 1/n-1 record splitting
                node = node.add_child(ExpectApplicationData())
                record_split = node
                node.next_sibling = ExpectAlert(AlertLevel.warning,
                                                AlertDescription.close_notify)
                node.next_sibling.next_sibling = ExpectClose()
                node = record_split.add_child(record_split.next_sibling)
                node.add_child(ExpectClose())
            conversations["{0} with AES_128 in {1}".format(name, prot_name)] \
                    = conversation

            # alone
            conversation = Connect(host, port, version=(3, 0))
            node = conversation
            ciphers = [c_id, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
            node = node.add_child(ClientHelloGenerator(ciphers))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal,
                            (AlertDescription.handshake_failure,
                             AlertDescription.protocol_version)))
            node = node.add_child(ExpectClose())
            conversations["{0} in {1}".format(name, prot_name)] = 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 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("Test if export grade ciphers are rejected by server.\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 = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    min_ver = (3, 0)
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help", "min-ver="])
    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 == '-d':
            dhe = True
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        elif opt == '--min-ver':
            min_ver = protocol_name_to_tuple(arg)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    if args:
        run_only = set(args)
    else:
        run_only = None

    conversations = {}

    conversation = Connect(host, port, version=(3, 0))
    node = conversation
    ext = {}
    groups = [GroupName.ffdhe2048]
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    ext[ExtensionType.signature_algorithms] = \
        SignatureAlgorithmsExtension().create(RSA_SIG_ALL)
    ext[ExtensionType.signature_algorithms_cert] = \
        SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
    if dhe:
        ciphers = [
            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(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

    expected_cipher = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA
    if dhe:
        expected_cipher = CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA

    for c_id, name in [(0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"),
                       (0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"),
                       (0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x000B, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x000E, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"),
                       (0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
                       (0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"),
                       (0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"),
                       (0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"),
                       (0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"),
                       (0x002A, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"),
                       (0x002B, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"),
                       (0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"),
                       (0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"),
                       (0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"),
                       (0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA")]:
        for prot, prot_name in [((3, 3), "TLSv1.2"), ((3, 2), "TLSv1.1"),
                                ((3, 1), "TLSv1.0"), ((3, 0), "SSLv3")]:
            conversation = Connect(host, port, version=(3, 0))
            node = conversation
            ext = {}
            groups = [GroupName.ffdhe2048]
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create(groups)
            ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension().create(RSA_SIG_ALL)
            ext[ExtensionType.signature_algorithms_cert] = \
                SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
            ciphers = [
                c_id, expected_cipher,
                CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
            ]
            node = node.add_child(
                ClientHelloGenerator(ciphers, version=prot, extensions=ext))
            if prot < min_ver:
                node = node.add_child(
                    ExpectAlert(AlertLevel.fatal,
                                (AlertDescription.protocol_version,
                                 AlertDescription.handshake_failure)))
                node = node.add_child(ExpectClose())
            else:
                node = node.add_child(
                    ExpectServerHello(cipher=expected_cipher))
                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))
                # allow for 1/n-1 record splitting
                node = node.add_child(ExpectApplicationData())
                record_split = node
                node.next_sibling = ExpectAlert(AlertLevel.warning,
                                                AlertDescription.close_notify)
                node.next_sibling.next_sibling = ExpectClose()
                node = record_split.add_child(record_split.next_sibling)
                node.add_child(ExpectClose())
            conversations["{0} with AES_128 in {1}".format(name, prot_name)] \
                    = conversation

            # alone
            conversation = Connect(host, port, version=(3, 0))
            node = conversation
            ciphers = [c_id, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
            node = node.add_child(ClientHelloGenerator(ciphers))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal,
                            (AlertDescription.handshake_failure,
                             AlertDescription.protocol_version)))
            node = node.add_child(ExpectClose())
            conversations["{0} in {1}".format(name, prot_name)] = 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 if export grade ciphers are rejected by server.\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)