Example #1
0
def main():
    host = "localhost"
    port = 4433
    run_exclude = set()
    renego = 1
    status = True

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:r:", ["help", "no-status"])
    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 == '-r':
            renego = int(arg)
        elif opt == '--no-status':
            status = False
        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 = {}

    # check if status_request is recognized and supported
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ocsp = StatusRequestExtension().create()
    ext = {ExtensionType.status_request: ocsp}
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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

    # check if status_request is recognized and supported
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ocsp = StatusRequestExtension().create()
    ext = {ExtensionType.status_request: ocsp}
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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())
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    ext = {
        ExtensionType.status_request: ocsp,
        ExtensionType.renegotiation_info: None
    }
    # renegotiate
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(
        ClientHelloGenerator(ciphers, extensions=ext, session_id=bytearray()))
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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["renegotiate"] = conversation

    # check if responder_id_list is supported
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    # DER encoding of CHOICE[1] OCTETSTRING (20)
    ocsp = StatusRequestExtension().create(responder_id_list=[
        bytearray(b'\xa2\x16\x04\x14') + bytearray([(i + 2) % 256] * 20)
        for i in range(625)
    ])
    ocsp.responder_id_list += [
        bytearray(b'\xa2\x16\x04\x14') + bytearray(
            b'\x18\x70\x95\x0B\xE0\x8E\x49\x98\x76\x23\x54\xE7\xD1\xFB\x4E\x9B\xB6\x67\x5E\x2B'
        )
    ]
    ext = {ExtensionType.status_request: ocsp}
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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())
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    ext = {
        ExtensionType.status_request: ocsp,
        ExtensionType.renegotiation_info: None
    }
    # renegotiate
    for _ in range(renego):
        node = node.add_child(ResetHandshakeHashes())
        node = node.add_child(
            ClientHelloGenerator(ciphers,
                                 extensions=ext,
                                 session_id=bytearray()))
        node = node.add_child(ExpectServerHello(extensions=ext_srv))
        node = node.add_child(ExpectCertificate())
        if status:
            node = node.add_child(ExpectCertificateStatus())
        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["renegotiate with large responder_id_list"] = 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")
        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)
Example #2
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    dhe = False
    ocsp = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help", "ocsp-enabled"])
    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 == '--ocsp-enabled':
            ocsp = 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 = {}
        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_CBC_SHA,
            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(description="first"))
    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

    # sanity renegotiation test case
    conversation = Connect(host, port)
    node = conversation
    ext = OrderedDict()
    # TODO add session_ticket
    ext[ExtensionType.signature_algorithms] = \
        SignatureAlgorithmsExtension().create(SIG_ALL)
    ext[ExtensionType.signature_algorithms_cert] = \
        SignatureAlgorithmsCertExtension().create(SIG_ALL)
    groups = [GroupName.secp256r1, GroupName.ffdhe2048]
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    ext[ExtensionType.extended_master_secret] = AutoEmptyExtension()
    ext[ExtensionType.key_share] = key_share_ext_gen([GroupName.secp256r1])
    ext[ExtensionType.alpn] = \
        ALPNExtension().create([bytearray(b'http/1.1')])
    ext[ExtensionType.ec_point_formats] = \
        ECPointFormatsExtension().create([
            ECPointFormat.ansiX962_compressed_prime,
            ECPointFormat.ansiX962_compressed_char2,
            ECPointFormat.uncompressed])
    # 18 - signed_certificate_timestamp
    ext[18] = AutoEmptyExtension()
    ext[ExtensionType.status_request] = \
        StatusRequestExtension().create()
    ext[ExtensionType.post_handshake_auth] = AutoEmptyExtension()
    # yes, don't include TLS 1.3, as we want to be able to renegotiate...
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([(3, 3), (3, 2)])
    ext[ExtensionType.psk_key_exchange_modes] = \
        PskKeyExchangeModesExtension().create(
            [PskKeyExchangeMode.psk_dhe_ke, PskKeyExchangeMode.psk_ke])
    psk_settings = [(b'test', b'pre-shared key', 'sha256')]
    ext[ExtensionType.pre_shared_key] = psk_ext_gen(psk_settings)
    mods = []
    mods.append(psk_ext_updater(psk_settings))
    if dhe:
        ciphers = [
            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
            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, modifiers=mods))
    node = node.add_child(ExpectServerHello(description="first handshake"))
    node = node.add_child(ExpectCertificate())
    if ocsp:
        node = node.add_child(ExpectCertificateStatus())
    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(ResetHandshakeHashes())
    renego_exts = OrderedDict(ext)
    # use None for autogeneration of the renegotiation_info with correct
    # payload
    renego_exts[ExtensionType.renegotiation_info] = None
    if ExtensionType.pre_shared_key in renego_exts:
        # make sure the PSK is the last extension
        tmp = renego_exts[ExtensionType.pre_shared_key]
        del renego_exts[ExtensionType.pre_shared_key]
        renego_exts[ExtensionType.pre_shared_key] = tmp
    renego_ciphers = list(ciphers)
    renego_ciphers.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    node = node.add_child(
        ClientHelloGenerator(renego_ciphers,
                             extensions=renego_exts,
                             session_id=bytearray(0),
                             modifiers=mods))
    node = node.add_child(ExpectServerHello(description="second handshake"))
    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 - renegotiation"] = conversation

    # drop specific extensions in the renegotiated client hello
    # signature_algorithms and signature_algorithms_cert are covered
    # by the test-sig-algs-renegotiation-resumption.py
    for drop_ext, exp_result in [
        (ExtensionType.supported_groups, None),
        (ExtensionType.extended_master_secret,
         AlertDescription.handshake_failure),
        (ExtensionType.key_share, None),
        (ExtensionType.alpn, None),
        (ExtensionType.ec_point_formats, None),
        (18, None),  # signed_certificate_timestamp
        (ExtensionType.status_request, None),
        (ExtensionType.post_handshake_auth, None),
        (ExtensionType.supported_versions, None),
        (ExtensionType.psk_key_exchange_modes, None),
        (ExtensionType.pre_shared_key, None)
    ]:
        conversation = Connect(host, port)
        node = conversation
        ext = OrderedDict()
        ext[ExtensionType.signature_algorithms] = \
            SignatureAlgorithmsExtension().create(SIG_ALL)
        ext[ExtensionType.signature_algorithms_cert] = \
            SignatureAlgorithmsCertExtension().create(SIG_ALL)
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ext[ExtensionType.extended_master_secret] = AutoEmptyExtension()
        ext[ExtensionType.key_share] = key_share_ext_gen([GroupName.secp256r1])
        ext[ExtensionType.alpn] = \
            ALPNExtension().create([bytearray(b'http/1.1')])
        ext[ExtensionType.ec_point_formats] = \
            ECPointFormatsExtension().create([
                ECPointFormat.ansiX962_compressed_prime,
                ECPointFormat.ansiX962_compressed_char2,
                ECPointFormat.uncompressed])
        # 18 - signed_certificate_timestamp
        ext[18] = AutoEmptyExtension()
        ext[ExtensionType.status_request] = \
            StatusRequestExtension().create()
        ext[ExtensionType.post_handshake_auth] = AutoEmptyExtension()
        # yes, don't include TLS 1.3, as we want to be able to renegotiate...
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([(3, 3), (3, 2)])
        ext[ExtensionType.psk_key_exchange_modes] = \
            PskKeyExchangeModesExtension().create(
                [PskKeyExchangeMode.psk_dhe_ke, PskKeyExchangeMode.psk_ke])
        ext[ExtensionType.pre_shared_key] = psk_ext_gen([(b'test',
                                                          b'pre-shared key')])
        if dhe:
            ciphers = [
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                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(ExpectServerHello(description="first handshake"))
        node = node.add_child(ExpectCertificate())
        if ocsp:
            node = node.add_child(ExpectCertificateStatus())
        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(ResetHandshakeHashes())
        renego_exts = OrderedDict(ext)
        del renego_exts[drop_ext]
        # use None for autogeneration of the renegotiation_info with correct
        # payload
        renego_exts[ExtensionType.renegotiation_info] = None
        if ExtensionType.pre_shared_key in renego_exts:
            # make sure the PSK is the last extension
            tmp = renego_exts[ExtensionType.pre_shared_key]
            del renego_exts[ExtensionType.pre_shared_key]
            renego_exts[ExtensionType.pre_shared_key] = tmp
        renego_ciphers = list(ciphers)
        renego_ciphers.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
        node = node.add_child(
            ClientHelloGenerator(renego_ciphers,
                                 extensions=renego_exts,
                                 session_id=bytearray(0)))
        if exp_result is not None:
            node = node.add_child(ExpectAlert(AlertLevel.fatal, exp_result))
            node.add_child(ExpectClose())
        else:
            node = node.add_child(
                ExpectServerHello(description="second handshake"))
            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["drop {0} in renegotiation".format(
            ExtensionType.toStr(drop_ext))] = 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 how server behaves when the renegotiation Client Hello is")
    print("changed compared to the initial ClientHello.\n")
    print("If the renegotiation is supposed to be disabled use the")
    print("test-renegotiation-disabled.py or")
    print("test-renegotiation-disabled-client-cert.py scripts to verify")
    print("that.\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)
Example #3
0
def main():
    host = "localhost"
    port = 4433
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    dhe = False
    renego = 1
    status = True

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:r:d", ["help", "no-status"])
    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 == '-r':
            renego = int(arg)
        elif opt == '--no-status':
            status = False
        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 = {}

    # check if status_request is recognized and supported
    conversation = Connect(host, port)
    node = conversation
    ocsp = StatusRequestExtension().create()
    ext = {ExtensionType.status_request: ocsp}
    if dhe:
        groups = [GroupName.secp256r1, 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 = [
            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_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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

    # check if status_request is recognized and supported
    conversation = Connect(host, port)
    node = conversation
    ocsp = StatusRequestExtension().create()
    ext = {ExtensionType.status_request: ocsp}
    if dhe:
        groups = [GroupName.secp256r1, 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 = [
            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_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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())
    ext = {
        ExtensionType.status_request: ocsp,
        ExtensionType.renegotiation_info: None
    }
    if dhe:
        groups = [GroupName.secp256r1, 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 = [
            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA
        ]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    # renegotiate
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(
        ClientHelloGenerator(ciphers, extensions=ext, session_id=bytearray()))
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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\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["renegotiate"] = conversation

    # check if responder_id_list is supported
    conversation = Connect(host, port)
    node = conversation
    # DER encoding of CHOICE[1] OCTETSTRING (20)
    ocsp = StatusRequestExtension().create(responder_id_list=[
        bytearray(b'\xa2\x16\x04\x14') + bytearray([(i + 2) % 256] * 20)
        for i in range(625)
    ])
    ocsp.responder_id_list += [
        bytearray(b'\xa2\x16\x04\x14') + bytearray(
            b'\x18\x70\x95\x0B\xE0\x8E\x49\x98\x76\x23\x54\xE7\xD1\xFB\x4E\x9B\xB6\x67\x5E\x2B'
        )
    ]
    ext = {ExtensionType.status_request: ocsp}
    if dhe:
        groups = [GroupName.secp256r1, 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 = [
            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_srv = {ExtensionType.renegotiation_info: None}
    if status:
        ext_srv[ExtensionType.status_request] = None
    node = node.add_child(ExpectServerHello(extensions=ext_srv))
    node = node.add_child(ExpectCertificate())
    if status:
        node = node.add_child(ExpectCertificateStatus())
    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())
    ext = {
        ExtensionType.status_request: ocsp,
        ExtensionType.renegotiation_info: None
    }
    if dhe:
        groups = [GroupName.secp256r1, 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 = [
            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA
        ]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    # renegotiate
    for _ in range(renego):
        node = node.add_child(ResetHandshakeHashes())
        node = node.add_child(
            ClientHelloGenerator(ciphers,
                                 extensions=ext,
                                 session_id=bytearray()))
        node = node.add_child(ExpectServerHello(extensions=ext_srv))
        node = node.add_child(ExpectCertificate())
        if status:
            node = node.add_child(ExpectCertificateStatus())
        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\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["renegotiate with large responder_id_list"] = conversation

    # run the conversation
    good = 0
    bad = 0
    xfail = 0
    xpass = 0
    failed = []
    xpassed = []

    # 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, 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("Script to verify that OCSP stapling support is implemented in "
          "server")
    print("Also can be used to reproduce CVE-2016-6304 with the use of -r "
          "option\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)
Example #4
0
def conv_generator(conf, host, port, sni_hostname, cert=None, key=None):
    """Generate a conversation based on dict with configuration."""

    root = Connect(host, port)
    hs = HandshakeSettings()
    # only RSA is supported
    if conf['Server_authentication'] != "RSA" and \
            conf['Server_authentication'] != "anon":
        print("Substituting {0} to RSA for server auth".format(
            conf['Server_authentication']),
              file=sys.stderr)

    # get the cipher that matches the imposed restrictions
    cipher_trans = {
        "AES_128_CBC": "aes128",
        "AES_256_CBC": "aes256",
        "AES_128_GCM": "aes128gcm",
        "AES_256_GCM": "aes256gcm",
        "3DES_EDE_CBC": "3des",
        "RC4": "rc4",
        "Chacha20_Poly1305": "chacha20-poly1305"
    }

    hs.cipherNames = [cipher_trans.get(conf['Cipher'], None)]
    if hs.cipherNames == [None]:
        raise ValueError("Unknown cipher type: {0}".format(conf['Cipher']))

    mac_trans = {
        "AEAD": "aead",
        "MD5_HMAC": "md5",
        "SHA1_HMAC": "sha",
        "SHA256_HMAC": "sha256",
        "SHA384_HMAC": "sha384"
    }
    hs.macNames = [mac_trans.get(conf['Integrity'], None)]
    if hs.macNames == [None]:
        raise ValueError("Unknown integrity type: {0}".format(
            conf['Integrity']))
    if conf['Key_exchange'] == 'DHE' and \
            conf['Server_authentication'] == "anon":
        suites = CipherSuite.getAnonSuites(hs)
    elif conf['Key_exchange'] == 'ECDHE' and \
            conf['Server_authentication'] == "anon":
        suites = CipherSuite.getEcdhAnonSuites(hs)
    elif conf['Key_exchange'] == 'RSA':
        suites = CipherSuite.getCertSuites(hs)
    elif conf['Key_exchange'] == 'DHE':
        suites = CipherSuite.getDheCertSuites(hs)
    elif conf['Key_exchange'] == 'ECDHE':
        suites = CipherSuite.getEcdheCertSuites(hs)
    else:
        raise ValueError("Unknown key exchange type: {0}".format(
            conf['Key_exchange']))

    if not suites:
        raise ValueError(
            "Couldn't find matching cipher for {0} {3} {1} {2}".format(
                conf['Key_exchange'], conf['Cipher'], conf['Integrity'],
                conf['Server_authentication']))

    # session ID/resumption handling
    if conf['CH_SessionID'] == 'random':
        sess_ID = getRandomBytes(16)
    elif conf['CH_SessionID'] == 'empty':
        sess_ID = bytearray()
    else:
        raise ValueError("Unknown CH_SessionID value".format(
            conf['CH_SessionID']))

    # compression
    if conf['CH_compression'] == 'null_only':
        compress = [0]
    elif conf['CH_compression'] == 'null_and_deflate':
        compress = [0, 1]
    else:
        raise ValueError("Unknown CH_compression value: {0}".format(
            conf['CH_compression']))

    # Renegotiation Info
    if conf['CH_renegotiation_info_SCSV'] == "first":
        suites.insert(0, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    elif conf['CH_renegotiation_info_SCSV'] == "last":
        suites.append(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    elif conf['CH_renegotiation_info_SCSV'] == "absent":
        pass
    elif conf['CH_renegotiation_info_SCSV'] == "second":
        suites.append(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
        suites.append(0xeaea)  # GREASE
    else:
        raise ValueError(
            "Unexpected CH_renegotiation_info_SCSV value: {0}".format(
                conf['CH_renegotiation_info_SCSV']))

    # whether to send extensions
    if conf['CH_extensions_present'] == "false":
        ext = None
    elif conf['CH_extensions_present'] != "true":
        raise ValueError("Unexpected CH_extensions_present value: {0}".format(
            conf['CH_extensions_present']))
    else:
        ext = dict()

        # session ticket
        if conf['CH_session_ticket'] != "no_ext":
            print("Not generating session ticket extension", file=sys.stderr)

        # renegotiation info
        if conf['CH_renegotiation_info_ext'] == "true":
            ext[ExtensionType.renegotiation_info] = \
                    RenegotiationInfoExtension().create(bytearray())
        elif conf['CH_renegotiation_info_ext'] == "false":
            pass
        else:
            raise ValueError(
                "Unknown option in CH_renegotiation_info_ext: {0}".format(
                    conf['CH_renegotiation_info_ext']))

        # signature algorithms
        if conf['CH_signature_algorithms_ext'] == "false":
            pass
        elif conf['CH_signature_algorithms_ext'] != "true":
            raise ValueError(
                "Unknown option CH_signature_algorithms_ext: {0}".format(
                    conf["CH_signature_algorithms_ext"]))
        else:
            sig = conf['SKE_signature_scheme']
            if sig == "none" or sig == "no_message":
                # enter some random ones:
                ext[ExtensionType.signature_algorithms] = \
                        SignatureAlgorithmsExtension()\
                        .create([SignatureScheme.rsa_pkcs1_sha256,
                                 SignatureScheme.rsa_pss_sha256])
            else:
                if "dsa" in sig:
                    print("Changing {0} to RSA scheme".format(sig))
                    sig = sig.replace("ecdsa", "rsa")
                    sig = sig.replace("dsa", "rsa")

                sig = sig.replace("rsa_sha", "rsa_pkcs1_sha")
                sig = sig.replace("rsapss", "rsa_pss")

                if "sha224" in sig:
                    scheme = (HashAlgorithm.sha224, SignatureAlgorithm.rsa)
                else:
                    scheme = getattr(SignatureScheme, sig)

                ext[ExtensionType.signature_algorithms] = \
                        SignatureAlgorithmsExtension()\
                        .create([scheme])

        # supported groups extension
        if conf['CH_supported_groups_ext'] == "false":
            groups = [
                GroupName.ffdhe2048, GroupName.secp256r1, GroupName.x25519
            ]
            ext[ExtensionType.supported_groups] = \
                    SupportedGroupsExtension().create(groups)
            pass
        elif conf['CH_supported_groups_ext'] != "true":
            raise ValueError(
                "Unknown option in CH_supported_groups_ext: {0}".format(
                    conf['CH_supported_groups_ext']))
        else:
            if conf['SKE_dh_group'] == "no_message":
                groups = [
                    GroupName.ffdhe2048, GroupName.secp256r1, GroupName.x25519
                ]
            elif conf['SKE_dh_group'] == "ffdhe1024":
                groups = [GroupName.secp256r1, GroupName.x25519]
            else:
                groups = [getattr(GroupName, conf['SKE_dh_group'])]

            ext[ExtensionType.supported_groups] = \
                    SupportedGroupsExtension().create(groups)

        ext[ExtensionType.ec_point_formats] = \
                ECPointFormatsExtension()\
                .create([ECPointFormat.uncompressed,
                         ECPointFormat.ansiX962_compressed_char2,
                         ECPointFormat.ansiX962_compressed_prime])

        # encrypt then MAC
        if conf['CH_encrypt_then_mac_ext'] == "false":
            pass
        elif conf['CH_encrypt_then_mac_ext'] != "true":
            raise ValueError(
                "Unknown option in CH_encrypt_then_mac_ext: {0}".format(
                    conf['CH_encrypt_then_mac_ext']))
        else:
            ext[ExtensionType.encrypt_then_mac] = \
                    TLSExtension(extType=ExtensionType.encrypt_then_mac)\
                    .create(bytearray(0))

        # server name
        if conf['CH_server_name'] == "no_ext":
            pass
        elif conf['CH_server_name'] == "correct":
            ext[ExtensionType.server_name] = \
                    SNIExtension().create(sni_hostname)
        elif conf['CH_server_name'] == "mismatch":
            ext[ExtensionType.server_name] = \
                    SNIExtension().create(sni_hostname + b'.www')
        else:
            raise ValueError("Unknown option in CH_server_name: {0}".format(
                conf['CH_server_name']))

        # OCSP staple
        if conf['CH_status_request_ext'] == "false":
            pass
        elif conf['CH_status_request_ext'] != "true":
            raise ValueError(
                "Unknown option in CH_status_request_ext: {0}".format(
                    conf['CH_status_request_ext']))
        else:
            ext[ExtensionType.status_request] = \
                    StatusRequestExtension().create()

        # Extended Master Secret ext
        if conf['CH_extended_master_secret_ext'] == "false":
            pass
        elif conf['CH_extended_master_secret_ext'] != "true":
            raise ValueError(
                ("Unknown value in CH_extended_master_secret_ext"
                 ": {0}").format(conf['CH_extended_master_secret_ext']))
        else:
            ext[ExtensionType.extended_master_secret] = \
                    TLSExtension(extType=ExtensionType.extended_master_secret)\
                    .create(bytearray())

    #
    #
    node = root.add_child(
        ClientHelloGenerator(suites,
                             session_id=sess_ID,
                             compression=compress,
                             extensions=ext))

    if conf['CH_server_name'] == "mismatch":
        node = node.add_child(
            ExpectAlert(AlertLevel.warning,
                        AlertDescription.unrecognized_name))
        al_node = node
    node = node.add_child(ExpectServerHello())
    if conf['CH_server_name'] == "mismatch":
        # make the sending of warning alert node optional
        al_node.next_sibling = node
    node = node.add_child(ExpectCertificate())
    # TODO if conf['Certificate_Status_msg']
    if conf['SKE_dh_group'] != "no_message":
        node = node.add_child(ExpectServerKeyExchange())
    if conf['CR_sent'] == "true":
        node = node.add_child(ExpectCertificateRequest())
    elif conf['CR_sent'] != "false":
        raise ValueError("Unknown option in CR_sent: {0}".format(
            conf['CR_sent']))
    node = node.add_child(ExpectServerHelloDone())
    if conf['CR_sent'] == "true":
        if conf['CV_signature_scheme'] == "no_message":
            node = node.add_child(CertificateGenerator())
        else:
            node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())

    if conf['CV_signature_scheme'] != "no_message":
        sig = conf['CV_signature_scheme']
        if "dsa" in sig:
            print("Changing {0} to RSA scheme in CV".format(sig))
            sig = sig.replace("ecdsa", "rsa")
            sig = sig.replace("dsa", "rsa")

        sig = sig.replace("rsa_sha", "rsa_pkcs1_sha")
        sig = sig.replace("rsapss", "rsa_pss")
        if "sha224" in sig:
            scheme = (HashAlgorithm.sha224, SignatureAlgorithm.rsa)
        else:
            scheme = getattr(SignatureScheme, sig)
        node = node.add_child(CertificateVerifyGenerator(key, msg_alg=scheme))

    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if conf['Disconnect'] == "true":
        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(node.next_sibling)
        node = node.add_child(Connect(host, port))
        node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ResetHandshakeHashes())
    hs = HandshakeSettings()

    hs.cipherNames = [cipher_trans.get(conf['H2Cipher'], None)]
    if hs.cipherNames == [None]:
        raise ValueError("Unknown cipher type: {0}".format(conf['H2Cipher']))

    hs.macNames = [mac_trans.get(conf["H2Integrity"], None)]
    if hs.macNames == [None]:
        raise ValueError("Unknown integrity type: {0}".format(
            conf['H2Integrity']))

    if conf['H2Key_exchange'] == 'DHE' and \
            conf['H2Server_authentication'] == "anon":
        suites = CipherSuite.getAnonSuites(hs)
    elif conf['H2Key_exchange'] == "ECDHE" and \
            conf['H2Server_authentication'] == "anon":
        suites = CipherSuite.getEcdhAnonSuites(hs)
    elif conf['H2Key_exchange'] == "RSA":
        suites = CipherSuite.getCertSuites(hs)
    elif conf['H2Key_exchange'] == "DHE":
        suites = CipherSuite.getDheCertSuites(hs)
    elif conf['H2Key_exchange'] == "ECDHE":
        suites = CipherSuite.getEcdheCertSuites(hs)
    else:
        raise ValueError("Unknown key exchange type: {0}".format(
            conf['H2Key_exchange']))

    if not suites:
        raise ValueError(
            "Couldn't find matching cipher for {0} {3} {1} {2}".format(
                conf['H2Key_exchange'], conf['H2Cipher'], conf['H2Integrity'],
                conf['H2Server_authentication']))

    if conf['H2CH_SessionID'] == 'random':
        sess_ID = getRandomBytes(16)
    elif conf['H2CH_SessionID'] == 'empty':
        sess_ID = bytearray()
    elif conf['H2CH_SessionID'] == "resume":
        sess_ID = None
    else:
        raise ValueError("Unknown session id value: {0}".format(
            conf['H2CH_SessionID']))

    # compression
    if conf['H2CH_compression'] == 'null_only':
        compress = [0]
    elif conf['H2CH_compression'] == 'null_and_deflate':
        compress = [0, 1]
    else:
        raise ValueError("Unknown H2CH_compression value: {0}".format(
            conf['H2CH_compression']))

    # Renegotiation Info
    if conf['H2CH_renegotiation_info_SCSV'] == "first":
        suites.insert(0, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    elif conf['H2CH_renegotiation_info_SCSV'] == "last":
        suites.append(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    elif conf['H2CH_renegotiation_info_SCSV'] == "absent":
        pass
    elif conf['H2CH_renegotiation_info_SCSV'] == "second":
        suites.append(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
        suites.append(0xeaea)  # GREASE
    else:
        raise ValueError(
            "Unexpected H2CH_renegotiation_info_SCSV value: {0}".format(
                conf['H2CH_renegotiation_info_SCSV']))

    # whether to send extensions
    if conf['H2CH_extensions_present'] == "false":
        ext = None
    elif conf['H2CH_extensions_present'] != "true":
        raise ValueError("Unexpected CH_extensions_present value: {0}".format(
            conf['H2CH_extensions_present']))
    else:
        ext = dict()

        # session ticket
        if conf['H2CH_session_ticket'] != "no_ext":
            print("Not generating session ticket extension", file=sys.stderr)

        # renegotiation info
        if conf['H2CH_renegotiation_info_ext'] == "true":
            ext[ExtensionType.renegotiation_info] = None
        elif conf['H2CH_renegotiation_info_ext'] == "false":
            pass
        else:
            raise ValueError("Unknown option in H2CH_renegotiation_info_ext: "
                             "{0}".format(conf['H2CH_renegotiation_info_ext']))

        # signature algorithms
        if conf['H2CH_signature_algorithms_ext'] == "false":
            pass
        elif conf['H2CH_signature_algorithms_ext'] != "true":
            raise ValueError("Unknown option H2CH_signature_algorithms_ext: "
                             "{0}".format(
                                 conf["H2CH_signature_algorithms_ext"]))
        else:
            sig = conf['H2SKE_signature_scheme']
            if sig == "none" or sig == "no_message":
                # enter some random ones:
                ext[ExtensionType.signature_algorithms] = \
                        SignatureAlgorithmsExtension()\
                        .create([SignatureScheme.rsa_pkcs1_sha256,
                                 SignatureScheme.rsa_pss_sha256])
            else:
                if "dsa" in sig:
                    print("Changing {0} to RSA scheme".format(sig))
                    sig = sig.replace("ecdsa", "rsa")
                    sig = sig.replace("dsa", "rsa")

                sig = sig.replace("rsa_sha", "rsa_pkcs1_sha")
                sig = sig.replace("rsapss", "rsa_pss")

                if "sha224" in sig:
                    scheme = (HashAlgorithm.sha224, SignatureAlgorithm.rsa)
                else:
                    scheme = getattr(SignatureScheme, sig)

                ext[ExtensionType.signature_algorithms] = \
                        SignatureAlgorithmsExtension()\
                        .create([scheme])

        # supported groups extension
        if conf['H2CH_supported_groups_ext'] == "false":
            groups = [
                GroupName.ffdhe2048, GroupName.secp256r1, GroupName.x25519
            ]
            ext[ExtensionType.supported_groups] = \
                    SupportedGroupsExtension().create(groups)
            pass
        elif conf['H2CH_supported_groups_ext'] != "true":
            raise ValueError(
                "Unknown option in H2CH_supported_groups_ext: {0}".format(
                    conf['H2CH_supported_groups_ext']))
        else:
            if conf['H2SKE_dh_group'] == "no_message":
                groups = [
                    GroupName.ffdhe2048, GroupName.secp256r1, GroupName.x25519
                ]
            elif conf['H2SKE_dh_group'] == "ffdhe1024":
                groups = [GroupName.secp256r1, GroupName.x25519]
            else:
                groups = [getattr(GroupName, conf['H2SKE_dh_group'])]

            ext[ExtensionType.supported_groups] = \
                    SupportedGroupsExtension().create(groups)

        ext[ExtensionType.ec_point_formats] = \
                ECPointFormatsExtension()\
                .create([ECPointFormat.uncompressed,
                         ECPointFormat.ansiX962_compressed_char2,
                         ECPointFormat.ansiX962_compressed_prime])

        # encrypt then MAC
        if conf['H2CH_encrypt_then_mac_ext'] == "false":
            pass
        elif conf['H2CH_encrypt_then_mac_ext'] != "true":
            raise ValueError(
                "Unknown option in H2CH_encrypt_then_mac_ext: {0}".format(
                    conf['H2CH_encrypt_then_mac_ext']))
        else:
            ext[ExtensionType.encrypt_then_mac] = \
                    TLSExtension(extType=ExtensionType.encrypt_then_mac)\
                    .create(bytearray(0))

        # server name
        if conf['H2CH_server_name'] == "no_ext":
            pass
        elif conf['H2CH_server_name'] == "correct":
            ext[ExtensionType.server_name] = \
                    SNIExtension().create(sni_hostname)
        elif conf['H2CH_server_name'] == "mismatch":
            ext[ExtensionType.server_name] = \
                    SNIExtension().create(sni_hostname + b'.www')
        else:
            raise ValueError("Unknown option in H2CH_server_name: {0}".format(
                conf['H2CH_server_name']))

        # OCSP staple
        if conf['H2CH_status_request_ext'] == "false":
            pass
        elif conf['H2CH_status_request_ext'] != "true":
            raise ValueError(
                "Unknown option in H2CH_status_request_ext: {0}".format(
                    conf['H2CH_status_request_ext']))
        else:
            ext[ExtensionType.status_request] = \
                    StatusRequestExtension().create()

        # Extended Master Secret ext
        if conf['H2CH_extended_master_secret_ext'] == "false":
            pass
        elif conf['H2CH_extended_master_secret_ext'] != "true":
            raise ValueError(
                ("Unknown value in H2CH_extended_master_secret_ext"
                 ": {0}").format(conf['H2CH_extended_master_secret_ext']))
        else:
            ext[ExtensionType.extended_master_secret] = \
                    TLSExtension(extType=ExtensionType.extended_master_secret)\
                    .create(bytearray())

    node = node.add_child(
        ClientHelloGenerator(suites,
                             session_id=sess_ID,
                             compression=compress,
                             extensions=ext))
    if conf['H2CH_server_name'] == "mismatch":
        node = node.add_child(
            ExpectAlert(AlertLevel.warning,
                        AlertDescription.unrecognized_name))
        al_node = node

    if conf['H2SH_SessionID'] == "resume":
        print("doing resumption")
        node = node.add_child(ExpectServerHello(resume=True))
        if conf['H2CH_server_name'] == "mismatch":
            # make the sending of warning alert node optional
            al_node.next_sibling = node
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
        node = node.add_child(
            ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
        node.next_sibling = ExpectClose()
    else:
        node = node.add_child(ExpectServerHello())
        if conf['H2CH_server_name'] == "mismatch":
            # make the sending of warning alert node optional
            al_node.next_sibling = node
        node = node.add_child(ExpectCertificate())
        # TODO if conf['Certificate_Status_msg']
        if conf['H2SKE_dh_group'] != "no_message":
            node = node.add_child(ExpectServerKeyExchange())
        if conf['H2CR_sent'] == "true":
            node = node.add_child(ExpectCertificateRequest())
        elif conf['H2CR_sent'] != "false":
            raise ValueError("Unknown option in H2CR_sent: {0}".format(
                conf['H2CR_sent']))
        node = node.add_child(ExpectServerHelloDone())
        if conf['H2CR_sent'] == "true":
            if conf['H2CV_signature_scheme'] == "no_message":
                node = node.add_child(CertificateGenerator())
            else:
                node = node.add_child(
                    CertificateGenerator(X509CertChain([cert])))
        node = node.add_child(ClientKeyExchangeGenerator())

        if conf['H2CV_signature_scheme'] != "no_message":
            sig = conf['H2CV_signature_scheme']
            if "dsa" in sig:
                print("Changing {0} to RSA scheme in CV".format(sig))
                sig = sig.replace("ecdsa", "rsa")
                sig = sig.replace("dsa", "rsa")

            sig = sig.replace("rsa_sha", "rsa_pkcs1_sha")
            sig = sig.replace("rsapss", "rsa_pss")
            if "sha224" in sig:
                scheme = (HashAlgorithm.sha224, SignatureAlgorithm.rsa)
            else:
                scheme = getattr(SignatureScheme, sig)
            node = node.add_child(
                CertificateVerifyGenerator(key, msg_alg=scheme))

        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
        node = node.add_child(
            ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
        node.next_sibling = ExpectClose()

    return root