Example #1
0
def main():
    if len(sys.argv) < 3:
        print(usage())
        return -1
    conversations = {}
    # 16 chars: POLY1305 tag 128 bit
    # Tampering one bit suffices to damage the mac
    # The payload has to be long enough to trigger heap overflow
    n = 15000
    fuzzes = [(-1, 1)]
    for pos, val in fuzzes:
        conversation = Connect(sys.argv[1], int(sys.argv[2]))
        node = conversation
        ciphers = [CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256]
        node = node.add_child(ClientHelloGenerator(ciphers))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerKeyExchange())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(fuzz_encrypted_message(
            ApplicationDataGenerator(b"GET / HTTP/1.0\n" + n * b"A" + b"\n\n"), xors={pos:val}))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.bad_record_mac))
        node = node.add_child(ExpectClose())

        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
                conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]
        #print(conversation_name + "...")
        runner = Runner(conversation)
        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

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #2
0
def main():

    fingerprint = Scanner().scan(ip="127.0.0.1", port=4433)

    generator = Generator(fingerprint)

    good = 0
    bad = 0

    for conversation in generator:
        fuzzer = Fuzzer(conversation, fingerprint)
        for fuzzed in fuzzer:
            runner = Runner(fuzzed)

            try:
                res = runner.run()
            except:
                print(traceback.format_exc())
                res = False

            if res:
                good+=1
            else:
                bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """Test if server supports the DHE_RSA key exchange"""
    conversations = {}

    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.
                                                   renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.
                                                     renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(
        bytearray(b"GET / HTTP/1.0\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 check DHE_RSA_AES_128"] = conversation

    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
    def test_run_with_expect_and_closed_socket(self):
        node = ExpectClose()

        runner = Runner(node)
        runner.state.msg_sock = mock.MagicMock()
        runner.state.msg_sock.recvMessageBlocking = \
                mock.MagicMock(side_effect=TLSAbruptCloseError())

        runner.run()
def main():

    #
    # Test if server aborts connection upon receiving applicaiton data
    # before Finished
    #

    conversations = {}

    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=None))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(Close())

    conversations["no extensions"] = conversation

    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions={}))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(Close())

    conversations["empty extensions"] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        runner = Runner(conversation)

        print(str(conversation_name) + "...\n")
        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
        else:
            bad += 1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
    def test_run_with_generate_and_expected_closed_socket(self):
        node = ClientHelloGenerator()
        node.next_sibling = ExpectClose()

        runner = Runner(node)
        runner.state.msg_sock = mock.MagicMock()
        runner.state.msg_sock.sendMessageBlocking = \
                mock.MagicMock(side_effect=socket.error)

        # does NOT raise exception
        runner.run()
    def test_run_with_unknown_type(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=False)
        node.is_expect = mock.Mock(return_value=False)
        node.is_generator = mock.Mock(return_value=False)
        node.child = None

        runner = Runner(node)

        with self.assertRaises(AssertionError):
            runner.run()
    def test_run_with_command_node(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=True)
        node.is_expect = mock.Mock(return_value=False)
        node.is_generator = mock.Mock(return_value=False)
        node.child = None

        runner = Runner(node)

        runner.run()

        node.process.assert_called_once_with(runner.state)
    def test_run_with_generate_and_unexpected_closed_socket(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=False)
        node.is_expect = mock.Mock(return_value=False)
        node.is_generator = mock.Mock(return_value=True)
        node.child = None

        runner = Runner(node)
        runner.state.msg_sock = mock.MagicMock()
        runner.state.msg_sock.sendMessageBlocking = \
                mock.MagicMock(side_effect=socket.error)

        with self.assertRaises(AssertionError):
            runner.run()
def main():

    #
    # Test if client hello with invalid session ID gets rejected
    #

    conversations = {}

    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               session_id=bytearray(b'\x03'*33)))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations['33 byte long session id'] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(str(conversation_name) + "...\n")

        runner = Runner(conversation)

        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

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
    def test_run_with_generator_node(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=False)
        node.is_expect = mock.Mock(return_value=False)
        node.is_generator = mock.Mock(return_value=True)
        node.child = None

        runner = Runner(node)

        runner.state.msg_sock = mock.MagicMock()

        runner.run()

        node.generate.assert_called_once_with(runner.state)
        self.assertTrue(runner.state.msg_sock.sendMessageBlocking.called)
        node.post_send.assert_called_once_with(runner.state)
Example #12
0
def main():

    conversation = Connect("localhost", 4433)
    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"hello server!\n")))
    #node = node.add_child(ExpectApplicationData(bytearray(b"hello client!\n")))
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    # run the conversation
    good = 0
    bad = 0

    runner = Runner(conversation)

    res = True
    try:
        runner.run()
    except:
        print("Error while processing")
        print(traceback.format_exc())
        res = False

    if res:
        good+=1
    else:
        bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():

    #
    # Test if server aborts connection upon receiving a hello request
    #

    conversations = {}

    conversation = Connect("localhost", 4433)
    node = conversation
    # type - 0, and 3 bytes of length (0)
    node = node.add_child(RawMessageGenerator(22, bytearray(b'\x00\x00\x00\x00')))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    node = node.add_child(ExpectClose())

    conversations["hello request to server"] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        runner = Runner(conversation)

        print(str(conversation_name) + "...\n")
        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good+=1
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
    def test_run_with_expect_node_and_unexpected_message(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=False)
        node.is_expect = mock.Mock(return_value=True)
        node.is_generator = mock.Mock(return_value=False)
        node.get_all_siblings = mock.Mock(return_value=[node])
        node.is_match = mock.Mock(return_value=False)
        node.child = None

        runner = Runner(node)
        runner.state.msg_sock = mock.MagicMock()
        msg = (mock.MagicMock(name="header"), mock.MagicMock(name="parsser"))
        runner.state.msg_sock.recvMessageBlocking = \
                mock.MagicMock(return_value=msg)

        with self.assertRaises(AssertionError):
            runner.run()

        runner.state.msg_sock.sock.close.called_once_with()
    def test_run_with_expect_node(self):
        node = mock.MagicMock()
        node.is_command = mock.Mock(return_value=False)
        node.is_expect = mock.Mock(return_value=True)
        node.is_generator = mock.Mock(return_value=False)
        node.get_all_siblings = mock.Mock(return_value=[node])
        node.is_match = mock.Mock(return_value=True)
        node.child = None

        runner = Runner(node)
        runner.state.msg_sock = mock.MagicMock()
        msg = (mock.MagicMock(name="header"), mock.MagicMock(name="parser"))
        runner.state.msg_sock.recvMessageBlocking = mock.Mock(return_value=msg)

        runner.run()

        internal_message = messages.Message(msg[0].type, msg[1].bytes)

        node.is_match.called_once_with(internal_message)
        node.process.called_once_with(runner.state, internal_message)
def main():

    #
    # Test sending invalid (too short or too long) Finished messages
    #

    conversations = {}

    conver = Connect("localhost", 4433)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(pad_handshake(FinishedGenerator(), pad=bytearray(b'\xfa')))
    #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())
    node.next_sibling = ExpectClose()

    conversations["padded Finished"] = conver

    conver = Connect("localhost", 4433)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(truncate_handshake(FinishedGenerator(), 1))
    #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())
    node.next_sibling = ExpectClose()

    conversations["truncated Finished"] = conver

    # run the conversation
    good = 0
    bad = 0

    for conver_name in conversations:
        conversation = conversations[conver_name]
        runner = Runner(conversation)
        print(conver_name + "...")

        res = True
        try:
            runner.run()
        except:
            print("\n")
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """Test if server supports all common hash algorithms in DHE_RSA kex"""
    conversations = {}

    for cipher in [CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256]:
        for hash_alg in ["sha1", "sha224", "sha256", "sha384", "sha512"]:
            conversation = Connect("localhost", 4433)
            node = conversation
            ciphers = [cipher]
            sig_algs = [(getattr(HashAlgorithm, hash_alg), SignatureAlgorithm.rsa)]
            ext = SignatureAlgorithmsExtension().create(sig_algs)
            node = node.add_child(ClientHelloGenerator(ciphers,
                                                       extensions={ExtensionType.
                                                           renegotiation_info:None,
                                                           ExtensionType.
                                                           signature_algorithms:
                                                           ext}))
            node = node.add_child(ExpectServerHello(version=(3, 3),
                                                    extensions={ExtensionType.
                                                             renegotiation_info:None}))
            node = node.add_child(ExpectCertificate())
            node = node.add_child(ExpectServerKeyExchange(valid_sig_algs=sig_algs))
            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[CipherSuite.ietfNames[cipher] + " " + hash_alg
                          + " signature"] = conversation

    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #18
0
def main():
    conversations = {}

    conversation = Connect("localhost", 4433)
    node = conversation
    # any CBC will do, but the rest of code expects a 128 block cipher
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n")))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["sanity check"] = conversation

    # CVE-2016-2107 reproducer
    # to reproduce the vulnerability we need to send a packet that decrypts
    # to 32 ASCII "A" bytes
    conversation = Connect("localhost", 4433)
    node = conversation
    # any CBC will do, but the rest of code expects a 128 bit block cipher and
    # 160 bit HMAC
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # we are encrypting 4 blocks, with 3 blocks (48 bytes) being data, MAC and padding
    node = node.add_child(
                          fuzz_plaintext(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n"))
                                       ,
                                       substitutions={i : 0x31 for i in range(-1, -49, -1)}))
    node = node.add_child(ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
    node = node.add_child(ExpectClose())

    conversations["CVE-2016-2107"] = conversation


    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """check if incorrect padding is rejected by server"""
    conversations = {}

    for pos, val in [
                     (-1, 0x01),
                     (-1, 0xff),
                     (-2, 0x01),
                     (-2, 0xff),
                     (-6, 0x01),
                     (-6, 0xff),
                     (-12, 0x01),
                     (-12, 0xff),
                     (-20, 0x01),
                     (-20, 0xff),
                     # we're generating at least 20 bytes of padding
                     ]:
        conversation = Connect("localhost", 4433)
        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(fuzz_padding(ApplicationDataGenerator(
                                                        b"GET / HTTP/1.0\n\n"),
                                           xors={pos:val},
                                           min_length=20))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.bad_record_mac))
#        node.next_sibling = ExpectClose()
        node = node.add_child(ExpectClose())

        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
                conversation

    # zero-fill the padding
    conversation = Connect("localhost", 4433)
    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())
    # block size for AES-128 is 16 bytes
    # SHA-1 MAC is 20 bytes long
    # length of "GET / HTTP" is 10 bytes
    # which means the padding will be two bytes - 1 byte of padding and one
    # byte length
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(
                                                    b"GET / HTTP"),
                                       substitutions={0:0}))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.bad_record_mac))
#   node.next_sibling = ExpectClose()
    node = node.add_child(ExpectClose())

    conversations["zero-filled"] = \
            conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(conversation_name + "...")

        runner = Runner(conversation)

        res = True
        #because we don't want to abort the testing and we are reporting
        #the errors to the user, using a bare except is OK
        #pylint: disable=bare-except
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False
        #pylint: enable=bare-except

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """Test if client hello with no valid compression method gets rejected"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    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\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

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    node = node.add_child(
        ClientHelloGenerator(ciphers, compression=range(1, 256)))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.illegal_parameter))
    node.add_child(ExpectClose())

    conversations['invalid compression methods'] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    node = node.add_child(ClientHelloGenerator(ciphers, compression=[1]))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.illegal_parameter))
    node.add_child(ExpectClose())

    conversations['only deflate compression method'] = 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("Verify if server checks compressions field in ClientHello")

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():
    """Check that server propoerly rejects pkcs1 signatures in TLS 1.3"""
    hostname = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    cert = None
    private_key = None

    # algorithms to expect from server in Certificate Request
    cr_sigalgs = [
        SignatureScheme.rsa_pss_rsae_sha512,
        SignatureScheme.rsa_pss_pss_sha512,
        SignatureScheme.rsa_pss_rsae_sha384,
        SignatureScheme.rsa_pss_pss_sha384,
        SignatureScheme.rsa_pss_rsae_sha256,
        SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pkcs1_sha512,
        SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha256,
        SignatureScheme.rsa_pkcs1_sha224, SignatureScheme.rsa_pkcs1_sha1
    ]

    # algorithms to advertise in ClientHello
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256,
        SignatureScheme.rsa_pss_pss_sha256,
        SignatureScheme.rsa_pss_rsae_sha384, SignatureScheme.rsa_pss_pss_sha384
    ]

    hashalgs = hashes_to_list("sha256 sha384 sha512")

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:s:k:c:", ["help", "hash-order="])
    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 == '-s':
            cr_sigalgs = sig_algs_to_ids(arg)
        elif opt == '--hash-order':
            hashalgs = hashes_to_list(arg)
        elif opt == '-k':
            text_key = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_key = str(text_key, 'utf-8')
            private_key = parsePEMKey(text_key, private=True)
        elif opt == '-c':
            text_cert = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_cert = str(text_cert, 'utf-8')
            cert = X509()
            cert.parse(text_cert)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    if not cert or not private_key:
        raise Exception("A Client certificate and a private key are required")

    certType = cert.certAlg

    conversations = {}
    conversations_long = {}

    # sanity check for Client Certificates
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (3, 3)])
    ext[ExtensionType.supported_groups] = \
        SupportedGroupsExtension().create(groups)
    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(ExpectCertificateRequest())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(CertificateVerifyGenerator(private_key))
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    # verify the advertised hashes
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (3, 3)])
    ext[ExtensionType.supported_groups] = \
        SupportedGroupsExtension().create(groups)
    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(ExpectCertificateRequest(cr_sigalgs))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)
    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["check sigalgs in cert request"] = conversation

    for sigalg in RSA_SIG_ALL:
        # set if test should succeed or fail based on cert type,
        # advertisement and forbidden algorithms
        expectPass = True
        if certType == "rsa" and sigalg in (
                SignatureScheme.rsa_pss_pss_sha256,
                SignatureScheme.rsa_pss_pss_sha384,
                SignatureScheme.rsa_pss_pss_sha512):
            expectPass = False
        elif certType == "rsa-pss" and sigalg in (
                SignatureScheme.rsa_pss_rsae_sha256,
                SignatureScheme.rsa_pss_rsae_sha384,
                SignatureScheme.rsa_pss_rsae_sha512):
            expectPass = False
        # also verify that pkcs1 signatures are unconditionally refused
        if sigalg in ((HashAlgorithm.md5,
                       SignatureAlgorithm.rsa), SignatureScheme.rsa_pkcs1_sha1,
                      SignatureScheme.rsa_pkcs1_sha224,
                      SignatureScheme.rsa_pkcs1_sha256,
                      SignatureScheme.rsa_pkcs1_sha384,
                      SignatureScheme.rsa_pkcs1_sha512):
            expectPass = False
        # also expect failure if an algorithm is not advertized
        if sigalg not in cr_sigalgs:
            expectPass = False

        conversation = Connect(hostname, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = \
            SupportedVersionsExtension().create([(3, 4), (3, 3)])
        ext[ExtensionType.supported_groups] = \
            SupportedGroupsExtension().create(groups)
        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(ExpectCertificateRequest())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(CertificateGenerator(X509CertChain([cert])))
        # force sigalg
        node = node.add_child(
            CertificateVerifyGenerator(private_key, msg_alg=sigalg))
        node = node.add_child(FinishedGenerator())

        result = "works"
        # only signatures of matching certificate type should work
        if expectPass:
            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:
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal,
                            AlertDescription.illegal_parameter))
            node.add_child(ExpectClose())

            result = "is refused"

        conversations["check {0} signature {1}".format(
            SignatureScheme.toStr(sigalg), result)] = conversation

    # verify that rsa-pss signatures with empty, too short or too long
    # salt fail
    msgalg = sigalg_select("rsa_pss", hashalgs, cr_sigalgs, certType)
    hash_name = SignatureScheme.getHash(SignatureScheme.toRepr(msgalg))
    digest_len = getattr(tlshashlib, hash_name)().digest_size
    for saltlen in (0, digest_len - 1, digest_len + 1):
        conversation = Connect(hostname, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = \
            SupportedVersionsExtension().create([(3, 4), (3, 3)])
        ext[ExtensionType.supported_groups] = \
            SupportedGroupsExtension().create(groups)
        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(ExpectCertificateRequest())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(CertificateGenerator(X509CertChain([cert])))
        # force salt length
        node = node.add_child(
            CertificateVerifyGenerator(private_key, rsa_pss_salt_len=saltlen))
        node = node.add_child(FinishedGenerator())
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.decrypt_error))
        node.add_child(ExpectClose())

        conversations["check signature with salt length {0}".format(
            saltlen)] = conversation

    # verify that a rsa-pkcs1 signature in a rsa-pss ID envelope fails
    sigalg = sigalg_select("rsa_pkcs1", hashalgs)
    msgalg = sigalg_select("rsa_pss", hashalgs, cr_sigalgs, certType)
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (3, 3)])
    ext[ExtensionType.supported_groups] = \
        SupportedGroupsExtension().create(groups)
    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(ExpectCertificateRequest())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(
        CertificateVerifyGenerator(private_key, sig_alg=sigalg,
                                   msg_alg=msgalg))
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.decrypt_error))
    node.add_child(ExpectClose())

    scheme = SignatureScheme.toRepr(sigalg)
    conversations["check pkcs1 signature with rsa-pss envelope fails"] = \
        conversation

    # verify that a rsa-pss signature with mismatched message hash fails
    msgalg = sigalg_select("rsa_pss", hashalgs, cr_sigalgs, certType)

    # choose a similar scheme with just a different hash, doesn't need to be
    # a server supported sigalg
    hash_name = SignatureScheme.getHash(SignatureScheme.toRepr(msgalg))
    _hashalgs = [x for x in hashalgs if x != hash_name]
    sigalg = sigalg_select("rsa_pss", _hashalgs, cert_type=certType)

    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (3, 3)])
    ext[ExtensionType.supported_groups] = \
        SupportedGroupsExtension().create(groups)
    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(ExpectCertificateRequest())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(
        CertificateVerifyGenerator(private_key, sig_alg=sigalg,
                                   msg_alg=msgalg))
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.decrypt_error))
    node.add_child(ExpectClose())

    conversations["check rsa-pss signature with mismatched hash fails"] = \
        conversation

    # verify that a rsa-pss signature with mismatched MGF1 hash fails
    sigalg = sigalg_select("rsa_pss", hashalgs, cr_sigalgs, certType)

    # choose a different hash to cause mismtach
    hash_name = SignatureScheme.getHash(SignatureScheme.toRepr(msgalg))
    mgf1_hash = [x for x in hashalgs if x != hash_name][0]

    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (3, 3)])
    ext[ExtensionType.supported_groups] = \
        SupportedGroupsExtension().create(groups)
    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(ExpectCertificateRequest())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(
        CertificateVerifyGenerator(private_key,
                                   mgf1_hash=mgf1_hash,
                                   msg_alg=sigalg))
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.decrypt_error))
    node.add_child(ExpectClose())

    conversations["check rsa-pss signature with mismatched mgf1 fails"] = \
        conversation

    # check that fuzzed signatures are rejected
    for pos in range(numBytes(private_key.n)):
        for xor in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]:
            conversation = Connect(hostname, port)
            node = conversation
            ciphers = [
                CipherSuite.TLS_AES_128_GCM_SHA256,
                CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
            ]
            ext = {}
            groups = [GroupName.secp256r1]
            ext[ExtensionType.key_share] = key_share_ext_gen(groups)
            ext[ExtensionType.supported_versions] = \
                SupportedVersionsExtension().create([(3, 4), (3, 3)])
            ext[ExtensionType.supported_groups] = \
                SupportedGroupsExtension().create(groups)
            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(ExpectCertificateRequest())
            node = node.add_child(ExpectCertificate())
            node = node.add_child(ExpectCertificateVerify())
            node = node.add_child(ExpectFinished())
            node = node.add_child(CertificateGenerator(X509CertChain([cert])))
            node = node.add_child(
                CertificateVerifyGenerator(private_key,
                                           padding_xors={pos: xor}))
            node = node.add_child(FinishedGenerator())
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.decrypt_error))
            node.add_child(ExpectClose())

            scheme = SignatureScheme.toRepr(sigalg)
            conversations_long["check that fuzzed signatures are rejected." +
                               " Malformed {0} - xor {1} at {2}".format(
                                   certType, hex(xor), pos)] = conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations_long)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    short_tests = [(k, v) for k, v in conversations.items() if k != 'sanity']
    long_tests = list(conversations_long.items())
    long_sampled_tests = sample(long_tests, min(num_limit, len(long_tests)))
    regular_tests = sample(long_sampled_tests + short_tests,
                           len(long_sampled_tests) + len(short_tests))
    ordered_tests = chain(sanity_tests, regular_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except Exception:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("Test to verify that server properly accepts or refuses")
    print("signatures in TLS1.3; PKCS1 signatures are always refused.")
    print("Other signatures are accepted or refused accordingly to")
    print("the certificate type provided ('rsa' vs 'rsa-pss').\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 #22
0
def main():
    host = "localhost"
    port = 4433
    num_limit = 50
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--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 = {}

    # The payload has to be long enough to trigger heap overflow
    # Create max. length application data:
    #   2**14 (max. record size) - 28 ("GET / HTTP/1.0\r\nX-test: " + ending)
    data_length = 2**14 - 28

    # 16 bytes: POLY1305 tag 128 bit
    # Tampering one bit suffices to damage the mac
    fuzzes = [(x, 2**y) for x in range(-16, 0) for y in range(8)]
    for pos, val in fuzzes:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256]
        node = node.add_child(ClientHelloGenerator(ciphers))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerKeyExchange())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(
            fuzz_encrypted_message(
                ApplicationDataGenerator(b"GET / HTTP/1.0\r\n" + b'X-test: ' +
                                         data_length * b'A' + b'\r\n\r\n'),
                xors={pos: val}))
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
        node = node.add_child(ExpectClose())
        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
            conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256]
    node = node.add_child(ClientHelloGenerator(ciphers))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(b"GET / HTTP/1.0\r\n" + b'X-test: ' +
                                 data_length * b'A' + b'\r\n\r\n'))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    # allow for multiple application data records in response
    node = node.add_child(ExpectApplicationData())
    loop = node
    node = node.add_child(
        ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
    loop.next_sibling = node
    node.next_sibling = ExpectClose()
    conversations["sanity"] = \
        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)
        run_only_tests = [(k, v) for k, v in conversations.items()
                          if k in run_only]
        sampled_tests = sample(run_only_tests,
                               min(num_limit, len(run_only_tests)))
    else:
        regular_tests = [(k, v) for k, v in conversations.items()
                         if (k != 'sanity') and k not in run_exclude]
        sampled_tests = sample(regular_tests, min(num_limit,
                                                  len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        exception = None
        try:
            runner.run()
        except Exception as exp:
            exception = exp
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if c_name in expected_failures:
            if res:
                xpass += 1
                xpassed.append(c_name)
                print("XPASS-expected failure but test passed\n")
            else:
                if expected_failures[c_name] is not None and  \
                    expected_failures[c_name] not in str(exception):
                    bad += 1
                    failed.append(c_name)
                    print("Expected error message: {0}\n".format(
                        expected_failures[c_name]))
                else:
                    xfail += 1
                    print("OK-expected failure\n")
        else:
            if res:
                good += 1
                print("OK\n")
            else:
                bad += 1
                failed.append(c_name)

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():

    #
    # Test if client hello with garbage at the end gets rejected
    #

    host = "localhost"
    port = 4433
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    # sanity check
    conversation = Connect(host, port)
    node = conversation
    ext = {ExtensionType.renegotiation_info: None}
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    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\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

    # test if server doesn't interpret extensions past extensions length
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(
        pad_handshake(
            ClientHelloGenerator(ciphers, extensions={}),
            # empty renegotiation info
            pad=bytearray(b'\xff\x01\x00\x01\x00')))
    # responding to a malformed client hello is not correct, but tested below
    node = node.add_child(ExpectServerHello(extensions={}))
    node.next_sibling = ExpectAlert()
    node.next_sibling.next_sibling = ExpectClose()

    conversations["extension past extensions"] = conversation

    for name, pad_len, pad_byte in [
        ("small pad", 1, 0),
        ("small pad", 2, 0),
        ("small pad", 3, 0),
        ("small pad", 1, 0xff),
        ("small pad", 2, 0xff),
        ("small pad", 3, 0xff),
        ("medium pad", 256, 0),
        ("big pad", 4096, 0),
        ("huge pad", 2**16, 0),
        ("small truncate", -1, 0),
        ("small truncate", -2, 0),
        ("small truncate", -3, 0),
        ("small truncate", -4, 0),
        ("small truncate", -5, 0),
        ("small truncate", -6, 0),
            # 7 bytes truncates whole 'extensions' creating
            # a valid message
            #("small truncate", -7, 0),
        ("hello truncate", -8, 0),
        ("hello truncate", -9, 0),
        ("hello truncate", -10, 0),
        ("hello truncate", -11, 0),
        ("hello truncate", -12, 0),
        ("hello truncate", -13, 0),
    ]:

        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
        node = node.add_child(
            pad_handshake(
                ClientHelloGenerator(
                    ciphers,
                    extensions={ExtensionType.renegotiation_info: None}),
                pad_len, pad_byte))
        # we expect Alert and Close or just Close
        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        node.add_child(ExpectClose())

        if "pad" in name:
            conversations[name + ": " + str(pad_len) + " of \"" +
                          str(pad_byte) + "\" byte padding"] = conversation
        else:
            conversations[name + ": " + str(-pad_len) +
                          " of bytes truncated"] = conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          filter(lambda x: x[0] != 'sanity',
                                 conversations.items()), [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))
    failed_sorted = sorted(failed, key=natural_sort_keys)
    print("  {0}".format('\n  '.join(repr(i) for i in failed_sorted)))

    if bad > 0:
        sys.exit(1)
Example #24
0
def main():
    host = "localhost"
    port = 4433
    run_exclude = set()
    dhe = False
    sigalgs = [
        SignatureScheme.rsa_pkcs1_sha256, SignatureScheme.rsa_pkcs1_sha384,
        SignatureScheme.rsa_pkcs1_sha512, SignatureScheme.rsa_pss_rsae_sha256,
        SignatureScheme.rsa_pss_rsae_sha384,
        SignatureScheme.rsa_pss_rsae_sha512,
        SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pss_pss_sha384,
        SignatureScheme.rsa_pss_pss_sha512
    ]

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:d", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-d':
            dhe = True
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

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

    # client hello max size
    conversation = Connect(host, port)
    node = conversation
    proto = bytearray(b"A" * 255)
    lista = []
    lista.append(proto)
    for p in range(1, 255):
        lista.append(proto)
    # max size with
    if dhe:
        lista.append(bytearray(b'B' * 181))
    else:
        lista.append(bytearray(b'B' * 239))
    lista.append(bytearray(b'http/1.1'))
    # cipher suites array length 2^16-2, ciphers are two bytes
    # max number of ciphers can be 32767
    ciphers = []
    # adding ciphers from unassigned ranges
    # 0x00,0x5D-5F      Unassigned (93-95)
    for c in range(93, 96):
        ciphers.append(c)
    # 0x00,0x6E-83      Unassigned (110-131)
    for c in range(110, 132):
        ciphers.append(c)
    # 0x00,0xC6-FE      Unassigned (198-254)
    for c in range(198, 255):
        ciphers.append(c)
    # 0x01-55,*         Unassigned (255-22015)
    for c in range(255, 22016):
        ciphers.append(c)
    # 0x56,0x01-0xC0,0x00       Unassigned (22017-49152)
    # adding 10921 ciphers, from unassigned range above
    if dhe:
        # with DHE we send two valid ciphers, not one
        for c in range(22017, 32939 - 1):
            ciphers.append(c)
    else:
        for c in range(22017, 32939):
            ciphers.append(c)
    # adding ciphers we actually want to use
    if dhe:
        ciphers.append(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
        ciphers.append(CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
    else:
        ciphers.append(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA)
    ciphers.append(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
    # cipher suites array filled with 32767 2bytes values
    ext = {ExtensionType.alpn: ALPNExtension().create(lista)}
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ext[ExtensionType.signature_algorithms] = \
            SignatureAlgorithmsExtension().create(sigalgs)
        ext[ExtensionType.signature_algorithms_cert] = \
            SignatureAlgorithmsCertExtension().create(sigalgs)
    # adding session_id, compression methonds
    node = node.add_child(
        ClientHelloGenerator(ciphers,
                             session_id=bytearray(32),
                             extensions=ext,
                             compression=range(0, 255)))
    ext = {
        ExtensionType.renegotiation_info: None,
        ExtensionType.alpn: ALPNExtension().create([bytearray(b'http/1.1')])
    }
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["max client hello"] = 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 throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    regular_tests = [(k, v) for k, v in conversations.items() if k != 'sanity']
    shuffled_tests = sample(regular_tests, len(regular_tests))
    ordered_tests = chain(sanity_tests, shuffled_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("Check if server will accept Client Hello message of maximum valid")
    print("size\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():
    """Check if malformed messages related to client certs are rejected."""
    conversations = {}
    host = "localhost"
    port = 4433
    num_limit = 1000
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    private_key = None
    cert = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:k:c:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        elif opt == '-k':
            text_key = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_key = str(text_key, 'utf-8')
            private_key = parsePEMKey(text_key, private=True)
        elif opt == '-c':
            text_cert = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_cert = str(text_cert, 'utf-8')
            cert = X509()
            cert.parse(text_cert)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    if not private_key:
        raise ValueError("Specify private key file using -k")
    if not cert:
        raise ValueError("Specify certificate file using -c")

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

    # sanity check for Client Certificates
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {
        ExtensionType.signature_algorithms:
        SignatureAlgorithmsExtension().create([
            (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa)
            for x in ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
        ]),
        ExtensionType.signature_algorithms_cert:
        SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
    }
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(CertificateVerifyGenerator(private_key))
    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\n\n"))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertDescription.close_notify))
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["sanity"] = conversation

    # sanity check for no client certificate
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {
        ExtensionType.signature_algorithms:
        SignatureAlgorithmsExtension().create([
            (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa)
            for x in ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
        ]),
        ExtensionType.signature_algorithms_cert:
        SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
    }
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(None))
    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(b"GET / HTTP/1.0\n\n"))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertDescription.close_notify))
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["sanity - no client cert"] = conversation

    for i in range(1, 0x100):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ]),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        node = node.add_child(
            fuzz_message(CertificateGenerator(X509CertChain([cert])),
                         xors={7: i}))
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(CertificateVerifyGenerator(private_key))
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error))
        node = node.add_child(ExpectClose())

        conversations["fuzz certificates length with {0}".format(
            i)] = conversation

    for i in range(1, 0x100):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ]),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        node = node.add_child(
            fuzz_message(CertificateGenerator(X509CertChain([cert])),
                         xors={9: i}))
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(CertificateVerifyGenerator(private_key))
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        # If the lenght of the certificates is inconsitent, we expect decode_error.
        # If the lenght seems fine for that cert(they are parsed one by one), but
        # the payload is wrong, it will fail with bad_certificate.
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, (AlertDescription.decode_error,
                                           AlertDescription.bad_certificate)))
        node = node.add_child(ExpectClose())

        conversations["fuzz first certificate length with {0}".format(
            i)] = conversation

    # fuzz empty certificates message
    for i in range(1, 0x100):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ]),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        node = node.add_child(
            fuzz_message(CertificateGenerator(None), xors={-1: i}))
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error))
        node = node.add_child(ExpectClose())

        conversations["fuzz empty certificates length - {0}".format(
            i)] = conversation

    # sanity check for no client certificate
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {
        ExtensionType.signature_algorithms:
        SignatureAlgorithmsExtension().create([
            (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa)
            for x in ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
        ]),
        ExtensionType.signature_algorithms_cert:
        SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
    }
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(TCPBufferingEnable())
    msg = CertificateGenerator()
    msg = pad_handshake(msg, 3)
    msg = fuzz_message(msg, substitutions={-4: 3})
    node = node.add_child(msg)
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(TCPBufferingDisable())
    node = node.add_child(TCPBufferingFlush())
    # if the implementation is proper, it will reject the message
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error))
    node = node.add_child(ExpectClose())
    conversations["sanity - empty client cert"] = conversation

    # fuzz empty certificate message
    for i, j, k in ((i, j, k) for i in range(8) for j in range(8)
                    for k in range(6)):
        if i == 3 and j == 0 or k == 0 and i == 0:
            continue
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ]),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        msg = CertificateGenerator()
        # extend the handshake message to allow for modifying fictional
        # certificate_list
        msg = pad_handshake(msg, k)
        # change the overall length of the certificate_list
        subs = {6: i}
        if k >= 3:
            # if there's payload past certificate_list length tag
            # set the byte that specifies length of first certificate
            subs[9] = j
        msg = fuzz_message(msg, substitutions=subs)
        node = node.add_child(msg)
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        # when the certificate is just a string of null bytes, it's a bad
        # certificate
        # (all length tags are 3 byte long)
        if i == j + 3 and k + 3 == i + 3 and j > 0:
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal,
                            AlertDescription.bad_certificate))
        else:
            # If the lenght of the certificates is inconsitent, we expect decode_error.
            # If the lenght seems fine for that cert(they are parsed one by one), but
            # the payload is wrong, it will fail with bad_certificate.
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal,
                            (AlertDescription.decode_error,
                             AlertDescription.bad_certificate)))
        node = node.add_child(ExpectClose())

        conversations[
            "fuzz empty certificate - overall {2}, certs {0}, cert {1}".format(
                i, j, k + 3)] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {
        ExtensionType.signature_algorithms:
        SignatureAlgorithmsExtension().create([
            (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa)
            for x in ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
        ]),
        ExtensionType.signature_algorithms_cert:
        SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
    }
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(TCPBufferingEnable())
    empty_cert = X509()
    msg = CertificateGenerator(X509CertChain([cert, empty_cert]))
    node = node.add_child(msg)
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(TCPBufferingDisable())
    node = node.add_child(TCPBufferingFlush())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error))
    node = node.add_child(ExpectClose())

    conversations["Correct cert followed by an empty one"] = conversation

    # fuzz the lenght of the second certificate which is empty
    for i in range(1, 0x100):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ])
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        empty_cert = X509()
        msg = CertificateGenerator(X509CertChain([cert, empty_cert]))
        node = node.add_child(msg)
        node = node.add_child(fuzz_message(msg, xors={-1: i}))
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.decode_error))
        node = node.add_child(ExpectClose())

        conversations["fuzz second certificate(empty) length - {0}".format(
            i)] = conversation

    # Correct cert followed by 2 empty certs. Fuzz the length of the middle(2nd) cert
    for i in range(1, 0x100):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create([
                (getattr(HashAlgorithm, x), SignatureAlgorithm.rsa) for x in
                ['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5']
            ])
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello(version=(3, 3)))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        empty_cert = X509()
        msg = CertificateGenerator(X509CertChain([cert, empty_cert]))
        msg = pad_handshake(msg, 3)
        node = node.add_child(msg)
        node = node.add_child(fuzz_message(msg, xors={-4: i}))
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        # If the lenght of the certificates is inconsitent, we expect decode_error.
        # If the lenght seems fine for that cert(they are parsed one by one), but
        # the payload is wrong, it will fail with bad_certificate.
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, (AlertDescription.decode_error,
                                           AlertDescription.bad_certificate)))
        node = node.add_child(ExpectClose())

        conversations["fuzz middle certificate(empty) length - {0}".format(
            i)] = 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("Malformed Certificate test\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 or xpass:
        sys.exit(1)
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}
    conversations_long = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    for cipher in [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_CHACHA20_POLY1305_SHA256
    ]:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" *
                         (2**14 - 28) + b"\r\n\r\n")
        assert len(data) == 2**14
        node = node.add_child(ApplicationDataGenerator(data))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectApplicationData()
        node = node.next_sibling.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        conversations["max size payload in app_data, cipher {0}".format(
            CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" *
                         (2**14 - 28 + 1) + b"\r\n\r\n")
        assert len(data) == 2**14 + 1
        node = node.add_child(ApplicationDataGenerator(data))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.record_overflow)
        node.next_sibling.add_child(ExpectClose())
        conversations["too big payload in app_data, data size: 2**14 + 1, "
                      "cipher {0}".format(
                          CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" *
                         (2**14 - 28 - 8) + b"\r\n\r\n")
        assert len(data) == 2**14 - 8
        padding_size = 2**14 + 2 - len(data) - 1
        node = node.add_child(
            SetPaddingCallback(
                SetPaddingCallback.add_fixed_padding_cb(padding_size)))
        node = node.add_child(ApplicationDataGenerator(data))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.record_overflow)
        node.next_sibling.add_child(ExpectClose())
        conversations["too big plaintext, size: 2**14 - 8, with an additional"
                      " {0} bytes of padding, cipher {1}".format(
                          padding_size,
                          CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        # Finished msg will add 32 bytes
        # 3 bytes for msg length
        # 1 byte for content type
        padding_left = 2**14 - 32 - 4
        node = node.add_child(FinishedGenerator(pad_left=padding_left))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.decode_error)
        node.next_sibling.add_child(ExpectClose())
        conversations[
            "max size payload (2**14) of Finished msg, with {0} bytes"
            " of left padding, cipher {1}".format(
                padding_left, CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        # Finished msg will add 32 bytes
        # 3 bytes for msg length
        # 1 byte for content type
        padding_left = 2**14 - 32 - 4 + 1
        node = node.add_child(FinishedGenerator(pad_left=padding_left))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.record_overflow)
        node.next_sibling.add_child(ExpectClose())
        conversations[
            "too big payload (2**14 + 1) of Finished msg, with {0} bytes"
            " of left padding, cipher {1}".format(
                padding_left, CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        # 32 bytes added as finished msg payload
        # 16 bytes added after encyption
        # 3 bytes for msg length
        # 1 byte for content type
        padding_size = 2**14 + 256 - 32 - 16 - 5
        node = node.add_child(
            SetPaddingCallback(
                SetPaddingCallback.add_fixed_padding_cb(padding_size)))
        node = node.add_child(FinishedGenerator())

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.record_overflow)
        node.next_sibling.add_child(ExpectClose())
        conversations[
            "max size of Finished msg, with {0} bytes of record layer"
            " padding {1}".format(
                padding_size, CipherSuite.ietfNames[cipher])] = conversation

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(SetMaxRecordSize(2**16 - 1))
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        # 32 bytes added as finished msg payload
        # 16 bytes added after encyption
        # 3 bytes for msg length
        # 1 byte for content type
        padding_size = 2**14 + 256 - 32 - 16 - 5 + 1
        node = node.add_child(
            SetPaddingCallback(
                SetPaddingCallback.add_fixed_padding_cb(padding_size)))
        node = node.add_child(FinishedGenerator())

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.record_overflow)
        node.next_sibling.add_child(ExpectClose())
        conversations["too big Finished msg, with {0} bytes of record layer"
                      " padding, cipher {1}".format(
                          padding_size,
                          CipherSuite.ietfNames[cipher])] = conversation

    # AEAD tag fuzzed
    for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]:
        for pos in range(-1, -17, -1):
            conversation = Connect(host, port)
            node = conversation
            ciphers = [
                CipherSuite.TLS_AES_128_GCM_SHA256,
                CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
            ]
            ext = {}
            groups = [GroupName.secp256r1]
            ext[ExtensionType.key_share] = key_share_ext_gen(groups)
            ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
                .create([TLS_1_3_DRAFT, (3, 3)])
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create(groups)
            sig_algs = [
                SignatureScheme.rsa_pss_rsae_sha256,
                SignatureScheme.rsa_pss_pss_sha256
            ]
            ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
                .create(sig_algs)
            ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
                .create(RSA_SIG_ALL)
            node = node.add_child(SetMaxRecordSize(2**16 - 1))
            node = node.add_child(ClientHelloGenerator(ciphers,
                                                       extensions=ext))
            node = node.add_child(ExpectServerHello())
            node = node.add_child(ExpectChangeCipherSpec())
            node = node.add_child(ExpectEncryptedExtensions())
            node = node.add_child(ExpectCertificate())
            node = node.add_child(ExpectCertificateVerify())
            node = node.add_child(ExpectFinished())
            node = node.add_child(FinishedGenerator())
            data = bytearray(b"GET / HTTP/1.0\r\n" + b"X-test: " + b"A" *
                             (2**14 - 28 + 256 - 16) + b"\r\n\r\n")
            assert len(data) == 2**14 + 256 - 16
            msg = ApplicationDataGenerator(data)
            node = node.add_child(fuzz_encrypted_message(msg, xors={pos: val}))

            # This message is optional and may show up 0 to many times
            cycle = ExpectNewSessionTicket()
            node = node.add_child(cycle)
            node.add_child(cycle)

            node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                            AlertDescription.record_overflow)
            node.next_sibling.add_child(ExpectClose())
            conversations_long["too big ciphertext with size 2**14 + 256 and"
                               "fuzzed tag with {0} on pos {1}".format(
                                   val, pos)] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    padding_size = 2**14 - 216 - 1
    ext[ExtensionType.client_hello_padding] = PaddingExtension().create(
        padding_size)
    node = node.add_child(SetMaxRecordSize(2**16 - 1))
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["max size of ClientHello msg, with {0} bytes of padding".
                  format(padding_size)] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    padding_size = 2**14 - 216
    ext[ExtensionType.client_hello_padding] = PaddingExtension().create(
        padding_size)
    node = node.add_child(SetMaxRecordSize(2**16 - 1))
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.record_overflow))
    node.add_child(ExpectClose())
    conversations["too big ClientHello msg, with {0} bytes of padding".format(
        padding_size)] = conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations_long)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          filter(lambda x: x[0] != 'sanity',
                                 conversations.items()),
                          islice(conversations_long.items(), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except Exception:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("Verify that too big Application Data messages, with")
    print("different ciphers or fuzzed AEAD tag in TLS 1.3 communication ")
    print("are rejected with record_overflow alert.\n")
    print("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 #27
0
def main():
    """Test if server supports session ID based session resumption."""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_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\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

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())
    node = node.add_child(Connect(host, port))
    close.add_child(node)

    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None},
        resume=True))
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    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()
    node = node.add_child(ExpectClose())

    conversations["session ID resume"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        session_id=bytearray(32),
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(
        bytearray(b"GET / HTTP/1.0\n\n")))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    conversations["Client Hello with garbage session ID"] = conversation

    # run the conversation
    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          islice(filter(lambda x: x[0] != 'sanity',
                                        conversations.items()), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except 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("Basic session resumption script")
    print("Check if session resumption using session IDs works\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 #28
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '-d':
            dhe = True
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    if dhe:
        ext = {}
        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:
        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

    # verify that server does not advertise support for heartbeat extension
    conversation = Connect(host, port)
    node = conversation
    ext = {}
    ext[ExtensionType.heartbeat] = HeartbeatExtension().create(
        HeartbeatMode.PEER_ALLOWED_TO_SEND)
    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 = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["no extension in Server Hello"] = conversation

    # verify that server rejects heartbeat message
    conversation = Connect(host, port)
    node = conversation
    node = node.add_child(TCPBufferingEnable())
    ext = {}
    ext[ExtensionType.heartbeat] = HeartbeatExtension().create(
        HeartbeatMode.PEER_ALLOWED_TO_SEND)
    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 = {ExtensionType.renegotiation_info: None}
    # this assumes the server supports/negotiatiates TLS 1.2
    node = node.add_child(SetRecordVersion((3, 3)))
    node = node.add_child(HeartbeatGenerator(bytearray(b'test heartbeat')))
    node = node.add_child(TCPBufferingDisable())
    node = node.add_child(TCPBufferingFlush())
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.unexpected_message))
    node.add_child(ExpectClose())
    conversations["heartbeat in handshake without support in server"] = \
        conversation

    # verify that server rejects heartbleed message
    conversation = Connect(host, port)
    node = conversation
    node = node.add_child(TCPBufferingEnable())
    ext = {}
    ext[ExtensionType.heartbeat] = HeartbeatExtension().create(
        HeartbeatMode.PEER_ALLOWED_TO_SEND)
    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 = {ExtensionType.renegotiation_info: None}
    # this assumes the server supports/negotiatiates TLS 1.2
    node = node.add_child(SetRecordVersion((3, 3)))
    node = node.add_child(
        fuzz_message(HeartbeatGenerator(bytearray(b'test heartbeat')),
                     substitutions={
                         1: 0,
                         2: 64
                     }))
    node = node.add_child(TCPBufferingDisable())
    node = node.add_child(TCPBufferingFlush())
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.unexpected_message))
    node.add_child(ExpectClose())
    conversations["heartbleed in handshake without support in server"] = \
        conversation

    # see if post-handshake heartbeat (encrypted) is rejected too
    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)
        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:
        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(HeartbeatGenerator(bytearray(b'test heartbeat')))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.unexpected_message))
    node.add_child(ExpectClose())
    conversations["heartbeat after handshake"] = conversation

    # see if post-handshake heartbleed (encrypted) is rejected too
    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)
        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:
        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(
        fuzz_message(HeartbeatGenerator(bytearray(b'test heartbeat')),
                     substitutions={
                         1: 0,
                         2: 64
                     }))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.unexpected_message))
    node.add_child(ExpectClose())
    conversations["heartbleed after handshake"] = 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("Test to verify that server does not support heartbeat extension\n")
    print("Also verify that the server will not tolerate heartbeat messages\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)
Example #29
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--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 = {}

    # basic connection
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    ext[ExtensionType.psk_key_exchange_modes] = PskKeyExchangeModesExtension()\
        .create([PskKeyExchangeMode.psk_dhe_ke, PskKeyExchangeMode.psk_ke])
    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")))
    # ensure that the server sends at least one NST always
    node = node.add_child(ExpectNewSessionTicket())

    # but multiple ones are OK too
    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(AlertLevel.warning, AlertDescription.close_notify))
    node.next_sibling = ExpectClose()
    node.add_child(ExpectClose())
    conversations["sanity"] = conversation

    # resume a session
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    ext[ExtensionType.psk_key_exchange_modes] = PskKeyExchangeModesExtension()\
        .create([PskKeyExchangeMode.psk_dhe_ke, PskKeyExchangeMode.psk_ke])
    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")))
    # ensure that the server sends at least one NST always
    node = node.add_child(ExpectNewSessionTicket())

    # but multiple ones are OK too
    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(AlertLevel.warning, AlertDescription.close_notify))
    # server can close connection without sending alert
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(close)
    node = node.add_child(Close())
    node = node.add_child(Connect(host, port))

    # start the second handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    ext = OrderedDict(ext)
    ext[ExtensionType.pre_shared_key] = psk_session_ext_gen()
    mods = []
    mods.append(psk_ext_updater())
    node = node.add_child(
        ClientHelloGenerator(ciphers, extensions=ext, modifiers=mods))
    ext = {}
    ext[ExtensionType.supported_versions] = srv_ext_handler_supp_vers
    ext[ExtensionType.pre_shared_key] = gen_srv_ext_handler_psk()
    ext[ExtensionType.key_share] = srv_ext_handler_key_share
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    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")))
    # ensure that the server sends at least one NST always
    node = node.add_child(ExpectNewSessionTicket())

    # but multiple ones are OK too
    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(AlertLevel.warning, AlertDescription.close_notify))
    node.next_sibling = ExpectClose()
    node.add_child(ExpectClose())
    conversations["session resumption"] = 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("Basic session resumption test with TLS 1.3 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 or xpass:
        sys.exit(1)
Example #30
0
def main():
    """Check what signature algorithms server advertises"""
    hostname = "localhost"
    port = 4433
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    cert = None
    private_key = None
    ext_spec = {
        'CH': None,
        'SH': None,
        'EE': None,
        'CT': None,
        'CR': None,
        'NST': None,
        'HRR': None
    }

    sigalgs = [
        SignatureScheme.ecdsa_secp521r1_sha512,
        SignatureScheme.ecdsa_secp384r1_sha384,
        SignatureScheme.ecdsa_secp256r1_sha256,
        (HashAlgorithm.sha224, SignatureAlgorithm.ecdsa),
        (HashAlgorithm.sha1,
         SignatureAlgorithm.ecdsa), SignatureScheme.rsa_pss_rsae_sha512,
        SignatureScheme.rsa_pss_pss_sha512,
        SignatureScheme.rsa_pss_rsae_sha384,
        SignatureScheme.rsa_pss_pss_sha384,
        SignatureScheme.rsa_pss_rsae_sha256,
        SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pkcs1_sha512,
        SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha256,
        SignatureScheme.rsa_pkcs1_sha224, SignatureScheme.rsa_pkcs1_sha1
    ]

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:s:k:c:E:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        elif opt == '-s':
            sigalgs = sig_algs_to_ids(arg)
        elif opt == '-E':
            ext_spec = expected_ext_parser(arg)
        elif opt == '-k':
            text_key = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_key = str(text_key, 'utf-8')
            private_key = parsePEMKey(text_key, private=True)
        elif opt == '-c':
            text_cert = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_cert = str(text_cert, 'utf-8')
            cert = X509()
            cert.parse(text_cert)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    # sanity check for Client Certificates
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = \
        ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (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)
    ext = dict_update_non_present(ext, ext_spec['CH'])
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = dict_update_non_present(None, ext_spec['SH'])
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectChangeCipherSpec())
    ext = dict_update_non_present(None, ext_spec['EE'])
    node = node.add_child(ExpectEncryptedExtensions(extensions=ext))
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    if cert and private_key:
        # sanity check for Client Certificates
        conversation = Connect(hostname, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = \
            ClientKeyShareExtension().create(key_shares)
        ext[ExtensionType.supported_versions] = \
            SupportedVersionsExtension().create([(3, 4), (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)
        ext = dict_update_non_present(ext, ext_spec['CH'])
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        ext = dict_update_non_present(None, ext_spec['SH'])
        node = node.add_child(ExpectServerHello(extensions=ext))
        node = node.add_child(ExpectChangeCipherSpec())
        ext = dict_update_non_present(None, ext_spec['EE'])
        node = node.add_child(ExpectEncryptedExtensions(extensions=ext))
        node = node.add_child(ExpectCertificateRequest())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(CertificateGenerator(X509CertChain([cert])))
        node = node.add_child(CertificateVerifyGenerator(private_key))
        node = node.add_child(FinishedGenerator())
        node = node.add_child(
            ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectApplicationData()
        node = node.next_sibling.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        conversations["with certificate"] = conversation

    # verify the advertised hashes
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = \
        ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (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)
    ext = dict_update_non_present(ext, ext_spec['CH'])
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = dict_update_non_present(None, ext_spec['SH'])
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectChangeCipherSpec())
    ext = dict_update_non_present(None, ext_spec['EE'])
    node = node.add_child(ExpectEncryptedExtensions(extensions=ext))
    node = node.add_child(ExpectCertificateRequest(sigalgs))
    # extensions are not yet supported in Certificate messages
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)
    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["check sigalgs in cert request"] = conversation

    # verify the sent extensions
    conversation = Connect(hostname, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = \
        ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = \
        SupportedVersionsExtension().create([(3, 4), (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)
    ext = dict_update_non_present(ext, ext_spec['CH'])
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = dict_update_non_present(None, ext_spec['SH'])
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    ext = {
        ExtensionType.signature_algorithms:
        SignatureAlgorithmsExtension().create(sigalgs)
    }
    ext = dict_update_non_present(ext, ext_spec['CR'])
    node = node.add_child(ExpectCertificateRequest(extensions=ext))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(CertificateGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)
    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["verify extensions in CertificateRequest"] = 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("Test to verify if server accepts empty certificate messages and")
    print("advertises only expected signature algotithms and extensions in ")
    print("Certificate Request message\n")
    print("version: {0}\n".format(version))

    print("Test end")
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():
    """Check if server correctly handles some obviously wrong messages"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(
        ClientHelloGenerator(
            ciphers, extensions={ExtensionType.renegotiation_info: None}))
    node = node.add_child(
        ExpectServerHello(extensions={ExtensionType.renegotiation_info: None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n")))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["sanity"] = conversation

    # set the whole value post encryption to zero's
    for size in (128, 256, 384, 512):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
        node = node.add_child(
            ClientHelloGenerator(
                ciphers, extensions={ExtensionType.renegotiation_info: None}))
        node = node.add_child(
            ExpectServerHello(
                extensions={ExtensionType.renegotiation_info: None}))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(TCPBufferingEnable())
        node = node.add_child(
            ClientKeyExchangeGenerator(encrypted_premaster=bytearray(size)))
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(TCPBufferingDisable())
        node = node.add_child(TCPBufferingFlush())
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
        node.add_child(ExpectClose())

        conversations["encrypted premaster set to all zero ({0})".format(size)] =\
                conversation

    # set the encrypted premaster to the the value of server modulus
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(
        ClientHelloGenerator(
            ciphers, extensions={ExtensionType.renegotiation_info: None}))
    node = node.add_child(
        ExpectServerHello(extensions={ExtensionType.renegotiation_info: None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(TCPBufferingEnable())
    node = node.add_child(
        ClientKeyExchangeGenerator(modulus_as_encrypted_premaster=True))
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(TCPBufferingDisable())
    node = node.add_child(TCPBufferingFlush())
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
    node.add_child(ExpectClose())

    conversations["modulus as encrypted premaster"] = conversation

    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          islice(
                              filter(lambda x: x[0] != 'sanity',
                                     conversations.items()), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))
    failed_sorted = sorted(failed, key=natural_sort_keys)
    print("  {0}".format('\n  '.join(repr(i) for i in failed_sorted)))

    if bad > 0:
        sys.exit(1)
Example #32
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    coalescing = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help", "coalescing"])
    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 == '--coalescing':
            coalescing = 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
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        KeyUpdateGenerator(message_type=KeyUpdateMessageType.update_requested))
    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 = ExpectKeyUpdate(
        message_type=KeyUpdateMessageType.update_not_requested)
    node = node.next_sibling.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations[
        "post-handshake KeyUpdate msg with update_request"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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(
        KeyUpdateGenerator(
            message_type=KeyUpdateMessageType.update_not_requested))
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations[
        "post-handshake KeyUpdate msg with update_not_request"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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(
        truncate_handshake(
            KeyUpdateGenerator(
                message_type=KeyUpdateMessageType.update_requested), 1))
    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 = ExpectAlert(AlertLevel.fatal,
                                    AlertDescription.decode_error)
    node.next_sibling.add_child(ExpectClose())
    conversations["empty KeyUpdate message"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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(
        pad_handshake(
            KeyUpdateGenerator(
                message_type=KeyUpdateMessageType.update_requested), 1))
    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 = ExpectAlert(AlertLevel.fatal,
                                    AlertDescription.decode_error)
    node.next_sibling.add_child(ExpectClose())
    conversations["large KeyUpdate message"] = conversation

    for msg_type in range(2, 256):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(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(KeyUpdateGenerator(message_type=msg_type))
        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 = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.illegal_parameter)
        node.next_sibling.add_child(ExpectClose())
        conversations["invalid message_type - {0}, in KeyUpdate msg".format(
            msg_type)] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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")))
    node = node.add_child(
        KeyUpdateGenerator(message_type=KeyUpdateMessageType.update_requested))

    # 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 = ExpectKeyUpdate(
        message_type=KeyUpdateMessageType.update_not_requested)
    node = node.next_sibling.add_child(
        ApplicationDataGenerator(bytearray(b" / 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[
        "app data split, conversation with KeyUpdate msg"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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())
    number_of_ku_msg = 32
    for i in range(number_of_ku_msg):
        node = node.add_child(
            KeyUpdateGenerator(
                message_type=KeyUpdateMessageType.update_requested))
    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 = ExpectKeyUpdate(
        message_type=KeyUpdateMessageType.update_not_requested)
    node = node.next_sibling
    # the server can coalesce the respone to a single key update
    # or it may reply to every message in particular
    if not coalescing:
        for i in range(number_of_ku_msg - 1):
            node = node.add_child(
                ExpectKeyUpdate(
                    message_type=KeyUpdateMessageType.update_not_requested))

    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["multiple KeyUpdate messages"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    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())
    fragment_list = []
    keyupdate_gen = KeyUpdateGenerator(
        message_type=KeyUpdateMessageType.update_requested)
    node = node.add_child(split_message(keyupdate_gen, fragment_list, 2))
    node = node.add_child(FlushMessageList(fragment_list))
    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 = ExpectKeyUpdate(
        message_type=KeyUpdateMessageType.update_not_requested)
    node = node.next_sibling.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["fragmented keyupdate msg"] = conversation

    for fragment_len in range(1, 5):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())

        fragment_list = []
        keyupdate_gen = KeyUpdateGenerator(
            message_type=KeyUpdateMessageType.update_requested)
        node = node.add_child(
            split_message(keyupdate_gen, fragment_list, fragment_len))
        node = node.add_child(
            ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))
        node = node.add_child(FlushMessageList(fragment_list))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.unexpected_message)
        node.next_sibling.add_child(ExpectClose())
        conversations[
            "{0}/{1} fragmented keyupdate msg, appdata between".format(
                fragment_len, 5 - fragment_len)] = conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          islice(
                              filter(lambda x: x[0] != 'sanity',
                                     conversations.items()), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except 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 with KeyUpdate msg with different msg_type or fragmented msg.")
    print("Verify that server will correctly handle updating the keys")
    print("or refuse the connection with relevant Alert msg.")
    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():
    """
    Check SSLv2Hello support

    Test if the server supports SSLv2-style Client Hello messages for
    negotiating TLS connections
    """
    conversations = {}
    host = "localhost"
    port = 4433

    argv = sys.argv[1:]
    opts, argv = getopt.getopt(argv, "h:p:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))
    if argv:
        help_msg()
        raise ValueError("Unknown options: {0}".format(argv))

    # instruct RecordLayer to use SSLv2 record layer protocol (0, 2)
    conversation = Connect(host, port, version=(0, 2))
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               ssl2=True))
    ext={ExtensionType.renegotiation_info:None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(
        bytearray(b"GET / HTTP/1.0\n\n")))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.child = ExpectClose()
    # if we're doing TLSv1.0 the server should be doing 1/n-1 splitting
    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling
    node.next_sibling = ExpectClose()

    conversations["SSLv2 Client Hello"] = conversation

    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    #
    # Test if server aborts connection upon receiving application data
    # before Finished
    #

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=None))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(Close())

    conversations["sanity"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions={}))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(Close())

    conversations["empty extensions"] = conversation

    # run the conversation
    good = 0
    bad = 0
    xfail = 0
    xpass = 0
    failed = []
    xpassed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    if run_only:
        if num_limit > len(run_only):
            num_limit = len(run_only)
        regular_tests = [(k, v) for k, v in conversations.items()
                         if k in run_only]
    else:
        regular_tests = [(k, v) for k, v in conversations.items()
                         if (k != 'sanity') and k not in run_exclude]
    sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        exception = None
        try:
            runner.run()
        except Exception as exp:
            exception = exp
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if c_name in expected_failures:
            if res:
                xpass += 1
                xpassed.append(c_name)
                print("XPASS-expected failure but test passed\n")
            else:
                if expected_failures[c_name] is not None and  \
                    expected_failures[c_name] not in str(exception):
                    bad += 1
                    failed.append(c_name)
                    print("Expected error message: {0}\n".format(
                        expected_failures[c_name]))
                else:
                    xfail += 1
                    print("OK-expected failure\n")
        else:
            if res:
                good += 1
                print("OK\n")
            else:
                bad += 1
                failed.append(c_name)

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad or xpass:
        sys.exit(1)
Example #35
0
def main():
    hostname = "localhost"
    port = 4433
    number_of_alerts = 4

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            hostname = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-n':
            number_of_alerts = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    conversations = {}

    conversation = Connect(hostname, port, version=(3, 3))
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers))
    for _ in range(number_of_alerts):  # sending alerts during handshake
        node = node.add_child(
            AlertGenerator(  # alert description: 46, 41, 43
                AlertLevel.warning, AlertDescription.unsupported_certificate))
    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(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(
        ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
    node.next_sibling = ExpectClose()
    conversations["SSL Death Alert without getting alert"] = conversation

    conversation = Connect(hostname, port, version=(3, 3))
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers))
    for _ in range(number_of_alerts + 1):
        node = node.add_child(
            AlertGenerator(AlertLevel.warning,
                           AlertDescription.unsupported_certificate))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ExpectAlert(AlertLevel.fatal))
    node = node.add_child(ExpectClose())
    conversations["SSL Death Alert with getting alert"] = conversation

    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """Test if client hello with no valid compression method gets rejected"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_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\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

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               compression=range(1, 256)))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.illegal_parameter))
    node.add_child(ExpectClose())

    conversations['invalid compression methods'] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               compression=[1]))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.illegal_parameter))
    node.add_child(ExpectClose())

    conversations['only deflate compression method'] = conversation


    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          islice(filter(lambda x: x[0] != 'sanity',
                                        conversations.items()), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except Exception:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("Verify if server checks compressions field in ClientHello")
    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 #37
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ext = {}
    sigalgs = [(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha224, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha256, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha384, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha512, SignatureAlgorithm.ecdsa)]
    ext[ExtensionType.signature_algorithms] = \
            SignatureAlgorithmsExtension().create(sigalgs)
    ext[ExtensionType.signature_algorithms_cert] = \
            SignatureAlgorithmsCertExtension().create(SIG_ALL)
    curves = [GroupName.secp256r1, GroupName.secp384r1, GroupName.secp521r1]
    ext[ExtensionType.supported_groups] = \
            SupportedGroupsExtension().create(curves)
    ciphers = [
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    node = node.add_child(
        ClientHelloGenerator(ciphers, version=(3, 3), extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\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

    # all algorithms, but in order from strongest to weakest
    conversation = Connect(host, port)
    node = conversation
    ext = {}
    sigalgs = [(HashAlgorithm.sha512, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha384, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha256, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha224, SignatureAlgorithm.ecdsa),
               (HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)]
    ext[ExtensionType.signature_algorithms] = \
            SignatureAlgorithmsExtension().create(sigalgs)
    ext[ExtensionType.signature_algorithms_cert] = \
            SignatureAlgorithmsCertExtension().create(SIG_ALL)
    curves = [GroupName.secp256r1, GroupName.secp384r1, GroupName.secp521r1]
    ext[ExtensionType.supported_groups] = \
            SupportedGroupsExtension().create(curves)
    ciphers = [
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    node = node.add_child(
        ClientHelloGenerator(ciphers, version=(3, 3), extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\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 - strongest to weakest"] = conversation

    # test all hashes one by one
    for h_alg in ["sha1", "sha224", "sha256", "sha384", "sha512"]:
        conversation = Connect(host, port)
        node = conversation
        ext = {}
        sigalgs = [(getattr(HashAlgorithm, h_alg), SignatureAlgorithm.ecdsa)]
        ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension().create(sigalgs)
        ext[ExtensionType.signature_algorithms_cert] = \
                SignatureAlgorithmsCertExtension().create(SIG_ALL)
        curves = [
            GroupName.secp256r1, GroupName.secp384r1, GroupName.secp521r1
        ]
        ext[ExtensionType.supported_groups] = \
                SupportedGroupsExtension().create(curves)
        ciphers = [
            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        node = node.add_child(
            ClientHelloGenerator(ciphers, version=(3, 3), extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerKeyExchange())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(
            ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\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["connect with {0}+ecdsa only".format(
            h_alg)] = 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 the server doesn't have the TLSv1.3 limitation on ECDSA")
    print("signatures also in TLSv1.2 mode.\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 #38
0
def main():
    """check if obsolete signature algorithm is rejected by server"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    private_key = None
    cert = None
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:k:c:d", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '-d':
            dhe = True
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        elif opt == '-k':
            text_key = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_key = str(text_key, 'utf-8')
            private_key = parsePEMKey(text_key, private=True)
        elif opt == '-c':
            text_cert = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_cert = str(text_cert, 'utf-8')
            cert = X509()
            cert.parse(text_cert)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

    if not private_key:
        raise ValueError("Specify private key file using -k")
    if not cert:
        raise ValueError("Specify certificate file using -c")

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

    conversations = {}

    # sanity check for Client Certificates
    conversation = Connect(host, port)
    node = conversation
    ext = {ExtensionType.signature_algorithms :
           SignatureAlgorithmsExtension().create([
             (getattr(HashAlgorithm, x),
              SignatureAlgorithm.rsa) for x in ['sha512', 'sha384', 'sha256',
                                                'sha224', 'sha1', 'md5']]),
           ExtensionType.signature_algorithms_cert :
           SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)}
    if dhe:
        groups = [GroupName.secp256r1,
                  GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(CertificateVerifyGenerator(private_key))
    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\n\n"))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(AlertGenerator(AlertDescription.close_notify))
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["sanity"] = conversation

    # force MD5 signature on CertificateVerify
    conversation = Connect(host, port)
    node = conversation
    ext = {ExtensionType.signature_algorithms :
           SignatureAlgorithmsExtension().create([
             (getattr(HashAlgorithm, x),
              SignatureAlgorithm.rsa) for x in ['sha512', 'sha384', 'sha256',
                                                'sha224', 'sha1', 'md5']]),
           ExtensionType.signature_algorithms_cert :
           SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)}
    if dhe:
        groups = [GroupName.secp256r1,
                  GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())
    sig_type = (HashAlgorithm.md5, SignatureAlgorithm.rsa)
    node = node.add_child(CertificateVerifyGenerator(private_key,
                                                     msg_alg=sig_type
                                                     ))
    # the other side can close connection right away, add options to handle it
    node.next_sibling = ExpectClose()
    node = node.add_child(ChangeCipherSpecGenerator())
    node.next_sibling = ExpectClose()
    node = node.add_child(FinishedGenerator())
    node.next_sibling = ExpectClose()
    # we expect closure or Alert and then closure of socket
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["MD5 forced"] = conversation

    # place a real MD5 signature in CertificateVerify, but indicate a normal
    # (requested by server) in the outside structure - tests if the server
    # parses the HashInfo structure from signature or if it compares it with
    # expected value
    conversation = Connect(host, port)
    node = conversation
    ext = {ExtensionType.signature_algorithms :
           SignatureAlgorithmsExtension().create([
             (getattr(HashAlgorithm, x),
              SignatureAlgorithm.rsa) for x in ['sha512', 'sha384', 'sha256',
                                                'sha224', 'sha1', 'md5']]),
           ExtensionType.signature_algorithms_cert :
           SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)}
    if dhe:
        groups = [GroupName.secp256r1,
                  GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())
    sig_type = (HashAlgorithm.md5, SignatureAlgorithm.rsa)
    node = node.add_child(CertificateVerifyGenerator(private_key,
                                                     sig_alg=sig_type
                                                     ))
    # the other side can close connection right away, add options to handle it
    node.next_sibling = ExpectClose()
    node = node.add_child(ChangeCipherSpecGenerator())
    node.next_sibling = ExpectClose()
    node = node.add_child(FinishedGenerator())
    node.next_sibling = ExpectClose()
    # we expect closure or Alert and then closure of socket
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["MD5 forced in HashInfo"] = conversation

    # make invalid signature in CertificateVerify (TLSv1.1 style)
    conversation = Connect(host, port)
    node = conversation
    ext = {ExtensionType.signature_algorithms :
           SignatureAlgorithmsExtension().create([
             (getattr(HashAlgorithm, x),
              SignatureAlgorithm.rsa) for x in ['sha512', 'sha384', 'sha256',
                                                'sha224', 'sha1', 'md5']]),
           ExtensionType.signature_algorithms_cert :
           SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)}
    if dhe:
        groups = [GroupName.secp256r1,
                  GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        ciphers = [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    else:
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello(version=(3, 3)))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectCertificateRequest())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(CertificateVerifyGenerator(private_key,
                                                     sig_version=(3, 2)
                                                     ))
    # the other side can close connection right away, add options to handle it
    node.next_sibling = ExpectClose()
    node = node.add_child(ChangeCipherSpecGenerator())
    node.next_sibling = ExpectClose()
    node = node.add_child(FinishedGenerator())
    node.next_sibling = ExpectClose()
    # we expect closure or Alert and then closure of socket
    node = node.add_child(ExpectClose())
    node.next_sibling = ExpectAlert()
    node.next_sibling.add_child(ExpectClose())

    conversations["TLSv1.1 signature in TLSv1.2 Certificate Verify"] = \
            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("MD5 CertificateVerify (CVE-2015-7575 aka SLOTH) test\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 #39
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    # sanity conversation
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256,
        SignatureScheme.rsa_pss_pss_sha256,
        SignatureScheme.rsa_pss_rsae_sha384, SignatureScheme.rsa_pss_pss_sha384
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    # empty
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384
    ]
    for cipher in ciphers:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256,
            SignatureScheme.rsa_pss_rsae_sha384,
            SignatureScheme.rsa_pss_pss_sha384
        ]
        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(trunc_start=0, trunc_end=0))

        # This message may be sent right after server finished
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        # we do not expect any application data back
        # after malforming the Finished message
        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.decode_error)
        node = node.next_sibling.add_child(ExpectClose())
        conversations["empty - cipher %s" \
                         % (CipherSuite.ietfNames[cipher])] = conversation

    # single bit error
    scenarios = [(CipherSuite.TLS_AES_128_GCM_SHA256, 32),
                 (CipherSuite.TLS_AES_256_GCM_SHA384, 48)]
    for cipher, prf_bytes in scenarios:
        for mbit in range(8 * prf_bytes):
            mbyte = mbit // 8 + 1
            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
            ext = {}
            groups = [GroupName.secp256r1]
            ext[ExtensionType.key_share] = key_share_ext_gen(groups)
            ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
                .create([TLS_1_3_DRAFT, (3, 3)])
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create(groups)
            sig_algs = [
                SignatureScheme.rsa_pss_rsae_sha256,
                SignatureScheme.rsa_pss_pss_sha256,
                SignatureScheme.rsa_pss_rsae_sha384,
                SignatureScheme.rsa_pss_pss_sha384
            ]
            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(
                fuzz_message(FinishedGenerator(),
                             xors={-mbyte: 0x01 << mbit % 8}))
            # This message may be sent right after server finished
            cycle = ExpectNewSessionTicket()
            node = node.add_child(cycle)
            node.add_child(cycle)

            # we do not expect any application data back
            # after malforming the Finished message
            node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                            AlertDescription.decrypt_error)
            node = node.next_sibling.add_child(ExpectClose())
            conversations["single bit error - cipher %s, bit %d" \
                          % (CipherSuite.ietfNames[cipher],
                             mbit)] = conversation

    # truncation
    # cipher, start, end
    scenarios = [
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -1),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -2),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -4),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -8),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -16),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, -32),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 12),  # TLS-1.2 size
        (CipherSuite.TLS_AES_128_GCM_SHA256, 1, None),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 2, None),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 4, None),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 8, None),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 16, None),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 32, None),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -1),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -2),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -4),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -8),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -16),  # SHA-256 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, -32),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 12),  # TLS-1.2 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 1, None),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 2, None),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 4, None),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 8, None),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 16, None),  # SHA-256 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 32, None)
    ]
    for cipher, start, end in scenarios:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256,
            SignatureScheme.rsa_pss_rsae_sha384,
            SignatureScheme.rsa_pss_pss_sha384
        ]
        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(trunc_start=start, trunc_end=end))

        # This message may be sent right after server finished
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        # we do not expect any application data back
        # after malforming the Finished message
        node.next_sibling = ExpectAlert(AlertLevel.fatal,
                                        AlertDescription.decode_error)
        node = node.next_sibling.add_child(ExpectClose())
        conversations["truncation - cipher %s, start %d, end %s" \
                      % (CipherSuite.ietfNames[cipher],
                         start, end)] = conversation

    # padding
    # cipher, padding byte, left padding, right padding
    scenarios = [
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 1),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 2),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 4),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 8),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 16),  # SHA-384 size
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 32),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 48),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0,
         2**14 - 4 - 32),  # max record
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0, 0x20000),  # intermediate
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0,
         0x30000),  # bigger than max ClientHello
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 0,
         256**3 - 1 - 32),  # max handshake
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 1, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 2, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 4, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 8, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 16, 0),  # SHA-384 size
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 32, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 48, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 2**14 - 4 - 32,
         0),  # max record
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 12, 0),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 1, 1),
        (CipherSuite.TLS_AES_128_GCM_SHA256, 0, 8, 8),  # SHA-384 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 1),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 2),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 4),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 8),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 16),  # SHA-512 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 32),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 48),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0,
         2**14 - 4 - 48),  # max record
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 0x20000),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0,
         0x30000),  # bigger than max ClientHello
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0,
         256**3 - 1 - 48),  # max handshake
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 0, 12),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 1, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 2, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 4, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 8, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 16, 0),  # SHA-512 size
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 32, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 48, 0),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 2**14 - 4 - 48,
         0),  # max record
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 1, 1),
        (CipherSuite.TLS_AES_256_GCM_SHA384, 0, 8, 8)  # SHA-512 size
    ]
    for cipher, pad_byte, pad_left, pad_right in scenarios:
        # longer timeout for longer messages
        # Because the client is sending encrypted data without waiting
        # on any server response, it can actually produce data at a faster
        # rate than the server is able to process it, meaning that a server
        # that aborts only after decrypting a full handshake message may have
        # quite a few records in the queue after we, as a client have finished
        # sending them. Since tlslite-ng has the ciphers implemented in
        # pure python, they are very slow, speeds of just 71.5KiB/s for
        # AES-256-GCM are not atypical. which translates to about 4 minutes
        # to transfer this data. Set the timeout to 5 for a small margin of
        # error.
        # Note: because we still are waiting for the server to send us an alert
        # (all graph terminal nodes go through ExpectAlert), server that fails
        # to do that will still cause the whole test conversation to fail in
        # case it just closes the connection on us
        timeout = 5 if max(pad_left, pad_right) < 2**14 * 4 else 300
        conversation = Connect(host, port, timeout=timeout)
        node = conversation
        ciphers = [cipher, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        groups = [GroupName.secp256r1]
        ext[ExtensionType.key_share] = key_share_ext_gen(groups)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256,
            SignatureScheme.rsa_pss_rsae_sha384,
            SignatureScheme.rsa_pss_pss_sha384
        ]
        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())

        # (the crazy handling of the messages below is because we are
        # sending one message (Finished) in multiple records, and server
        # can abort the connection after processing any number of records)

        # conditionally wait for NewSessionTicket messages
        # this will help in case the server does send early NST (without
        # waiting for client Finished) but will abort reading of the
        # Finished after one record
        no_message = node.add_child(ExpectNoMessage(0.001))
        nst = ExpectNewSessionTicket(description='first')
        no_message.next_sibling = nst
        nst.add_child(no_message)
        node = no_message

        # alert+close can happen during sending large Finished message,
        # therefore we are specifying it as its sibling
        close_node = ExpectAlert(AlertLevel.fatal,
                                 AlertDescription.decode_error)
        close_node.add_child(ExpectClose())
        node = node.add_child(
            FinishedGenerator(pad_byte=pad_byte,
                              pad_left=pad_left,
                              pad_right=pad_right))
        node.next_sibling = close_node

        # This message may be sent right after server finished
        cycle = ExpectNewSessionTicket(description='second')
        node = node.add_child(cycle)
        node.add_child(cycle)

        # we do not expect any application data back
        # after malforming the Finished message
        node.next_sibling = close_node

        conversations["padding - cipher %s, "
                      "pad_byte %d, "
                      "pad_left %d, "
                      "pad_right %d" \
                      % (CipherSuite.ietfNames[cipher],
                         pad_byte, pad_left, pad_right)] = 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("Fuzzing TLS 1.3 Finished messages")
    print("version: {0}\n".format(version))

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))
    failed_sorted = sorted(failed, key=natural_sort_keys)
    print("  {0}".format('\n  '.join(repr(i) for i in failed_sorted)))

    if bad > 0:
        sys.exit(1)
def main():

    #
    # Test if client hello with garbage at the end gets rejected
    #

    conversations = {}

    # test if server doesn't interpret extensions past extensions length
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(pad_handshake(ClientHelloGenerator(ciphers,
                                               extensions={}),
                                        # empty renegotiation info
                                        pad=bytearray(b'\xff\x01\x00\x01\x00')))
    # responding to a malformed client hello is not correct, but tested below
    node = node.add_child(ExpectServerHello(extensions={}))
    node.next_sibling = ExpectAlert()
    node.next_sibling.next_sibling = ExpectClose()

    conversations["extension past extensions"] = conversation


    for name, pad_len, pad_byte in [
                                ("small pad", 1, 0),
                                ("small pad", 2, 0),
                                ("small pad", 3, 0),
                                ("small pad", 1, 0xff),
                                ("small pad", 2, 0xff),
                                ("small pad", 3, 0xff),
                                ("medium pad", 256, 0),
                                ("big pad", 4096, 0),
                                ("huge pad", 2**16, 0),
                                ("small truncate", -1, 0),
                                ("small truncate", -2, 0),
                                ("small truncate", -3, 0),
                                ("small truncate", -4, 0),
                                ("small truncate", -5, 0),
                                ("small truncate", -6, 0),
                                # 7 bytes truncates whole 'extensions' creating
                                # a valid message
                                #("small truncate", -7, 0),
                                ("hello truncate", -8, 0),
                                ("hello truncate", -9, 0),
                                ("hello truncate", -10, 0),
                                ("hello truncate", -11, 0),
                                ("hello truncate", -12, 0),
                                ("hello truncate", -13, 0),
                                ]:

        conversation = Connect("localhost", 4433)
        node = conversation
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
        node = node.add_child(pad_handshake(ClientHelloGenerator(ciphers,
                                                   extensions={ExtensionType.renegotiation_info: None}),
                                            pad_len, pad_byte))
        # we expect Alert and Close or just Close
        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        node.add_child(ExpectClose())

        if "pad" in name:
            conversations[name + ": " + str(pad_len) + " of \"" + str(pad_byte) +
                          "\" byte padding"] = conversation
        else:
            conversations[name + ": " + str(-pad_len) +
                          " of bytes truncated"] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(str(conversation_name) + "...\n")

        runner = Runner(conversation)

        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

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():

    #
    # Test if client hello with no valid cipher gets rejected
    #

    conversations = {}

    for name, ciphers in [
                          ("NULL_NULL cipher", [0x00]),
                          ("FORTEZZA", range(0x1c,0x1e)),
                          ("Unknown 0x0047", range(0x47, 0x60)),
                          ("EXPORT1024", range(0x60, 0x66)),
                          ("TLS_DHE_DSS_WITH_RC4_128_SHA", [0x66]),
                          ("Unknown 0x006e", range(0x6e, 0x80)),
                          ("GOST", range(0x80, 0x84)),
                          ("Unknown 0x00c6", range(0xc6, 0xff)),
                          ("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", [0xff]),
                          ("Unknown 0x0100", range(0x0100, 0x1001)),
                          ("Unknown 0x2000", range(0x2000, 0x3001)),
                          ("Unknown 0x3000", range(0x3000, 0x4001)),
                          ("Unknown 0x4000", range(0x4000, 0x5001)),
                          ("Unknown 0x5000", range(0x5000, 0x6001)),
                          ("Unknown 0x6000", range(0x6000, 0x7001)),
                          ("Unknown 0x7000", range(0x7000, 0x8001)),
                          ("Unknown 0x8000", range(0x8000, 0x9001)),
                          ("Unknwon 0x9000", range(0x9000, 0xa001)),
                          ("Unknown 0xa000", range(0xa000, 0xb001)),
                          ("Unknown 0xb000", range(0xb000, 0xc001)),
                          ("Unknown 0xc0b0", range(0xc0b0, 0xd001)),
                          ("Unknown 0xd000", range(0xd000, 0xe001)),
                          ("Unknown 0xe000", range(0xe000, 0xf001)),
                          ("Unknown 0xf000", range(0xf000, 0xffff)),
                          ]:

        conversation = Connect("localhost", 4433)
        node = conversation

        node = node.add_child(ClientHelloGenerator(ciphers))
        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()

        conversations[name] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(str(conversation_name) + "...\n")

        runner = Runner(conversation)

        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

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():

    #
    # Test interleaving of Application Data with handshake messages,
    # requires an HTTP-like server with support for client initiated
    # renegotiation
    #

    conversations = {}

    conver = Connect("localhost", 4433)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    fragment_list = []
    node = node.add_child(split_message(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                                             extensions={ExtensionType.renegotiation_info:None}),
                                        fragment_list, 30))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(FlushMessageList(fragment_list))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(bytearray(b"\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["Application data inside Client Hello"] = conver

    conver = Connect("localhost", 4433)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                                             extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    fragment_list = []
    node = node.add_child(split_message(ClientKeyExchangeGenerator(), fragment_list, 30))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(FlushMessageList(fragment_list))
    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"\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["Application data inside Client Key Exchange"] = conver

    # CCS is one byte, can't split it
    #conversations["Application data inside Change Cipher Spec"] = conver

    conver = Connect("localhost", 4433)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    fragments = []
    node = node.add_child(split_message(FinishedGenerator(), fragments, 10))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["Application data inside Finished"] = conver
    # run the conversation
    good = 0
    bad = 0

    for conver_name in conversations:
        conversation = conversations[conver_name]
        runner = Runner(conversation)
        print(conver_name + "...")

        res = True
        try:
            runner.run()
        except:
            print("\n")
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #43
0
def main():

    #
    # Test interleaving of Application Data with handshake messages,
    # requires server to support client initiated renegotiation
    #

    conversation = Connect("localhost", 4433)
    node = conversation
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                               session_id=bytearray(0),
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ApplicationDataGenerator(bytearray(b"hello server!\n")))
    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"hello server!\n")))
    #node = node.add_child(ExpectApplicationData(bytearray(b"hello client!\n")))
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    node.next_sibling.next_sibling = ExpectApplicationData()

    # run the conversation
    good = 0
    bad = 0

    runner = Runner(conversation)

    res = True
    try:
        runner.run()
    except:
        print("Error while processing")
        print(traceback.format_exc())
        res = False

    if res:
        good+=1
    else:
        bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """check if fuzzed ciphertext is rejected by server"""
    conversations = {}

    # 8 chars: explicit nonce
    # 16 chars: AES-CTR encrypt: GET / HTTP/1.0\n\n
    # 16 chars: GCM tag 128 bit
    # 1 char: experimentally determined there mysteriously is one more byte
    #         in the message.
    fuzzes = [(-i, 1) for i in range(1,8+16+16+1)]
    for pos, val in fuzzes:
        conversation = Connect("localhost", 4433)
        node = conversation
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        node = node.add_child(ClientHelloGenerator(ciphers))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(fuzz_encrypted_message(
            ApplicationDataGenerator(b"GET / HTTP/1.0\n\n"), xors={pos:val}))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.bad_record_mac))
        node = node.add_child(ExpectClose())

        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
                conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(conversation_name + "...")

        runner = Runner(conversation)

        res = True
        #because we don't want to abort the testing and we are reporting
        #the errors to the user, using a bare except is OK
        #pylint: disable=bare-except
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False
        #pylint: enable=bare-except

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    http = False
    conversations = {}

    # check if server works at all
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["sanity"] = conversation

    # check if server works with SHA384 PRF ciphersuite
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["sanity sha384 prf"] = conversation

    # check if server works at all (TLSv1.1)
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        version=(3, 2),
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        version=(3, 2),
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["sanity TLSv1.1"] = conversation

    # check if server supports extended master secret
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["extended master secret"] = conversation

    # check if server supports extended master secret with SHA384 PRF
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["extended master secret w/SHA384 PRF"] = conversation

    # check if server supports extended master secret
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        version=(3, 2),
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        version=(3, 2),
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["extended master secret in TLSv1.1"] = conversation

    # check if server doesn't default to extended master secret
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator(
        extended_master_secret=True))
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectAlert(level=AlertLevel.fatal))
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    conversations["no EMS by default"] = conversation

    # check if server uses EMS for resumed connections
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    node = node.add_child(Connect("localhost", 4433))
    close.add_child(node)
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None},
        resume=True))
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["EMS with session resume"] = conversation

    # check if server uses EMS for resumed connections and SHA384 PRF
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    node = node.add_child(Connect("localhost", 4433))
    close.add_child(node)
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None},
        resume=True))
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.add_child(Close())

    conversations["EMS with session resume and SHA384 PRF"] = conversation

    # check if server aborts session resume without EMS extension
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    node = node.add_child(Connect("localhost", 4433))
    close.add_child(node)
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.handshake_failure))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    node = node.add_child(Close())

    conversations["EMS with session resume without extension"] = conversation

    # check if server does full handshake on resumed session without EMS
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(AlertGenerator(AlertLevel.warning,
                                         AlertDescription.close_notify))
    node = node.add_child(ExpectAlert())
    close = ExpectClose()
    node.next_sibling = close
    node = node.add_child(ExpectClose())
    node = node.add_child(Close())

    node = node.add_child(Connect("localhost", 4433))
    close.add_child(node)
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None},
        resume=False))
    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())
    if http:
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\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()
    node.next_sibling.add_child(Close())
    node.add_child(Close())

    conversations["resume non-EMS session with EMS extension"] = \
            conversation

    # EMS with renegotiation
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        session_id=bytearray(0), # do not resume
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        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()
    node = node.add_child(Close())
    conversations["extended master secret with renegotiation"] = conversation

    # renegotiation in non-EMS session
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        session_id=bytearray(0), # do not resume
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        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()
    node = node.add_child(Close())
    conversations["renegotiate with EMS in session without EMS"] = conversation

    # renegotiation of non-EMS session in EMS session
    conversation = Connect("localhost", 4433)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None,
                    ExtensionType.extended_master_secret:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator(
        ciphers,
        session_id=bytearray(0), # do not resume
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(
        extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if http:
        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()
    node = node.add_child(Close())

    conversations["renegotiate without EMS in session with EMS"] = conversation


    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #46
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    groups = GroupName.allFF

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ext = {}
    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_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ApplicationDataGenerator(
        bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(AlertGenerator(AlertLevel.warning,
                                       AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    # test specific ones
    for key_share_group in groups:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        key_shares = []
        key_shares.append(key_share_gen(key_share_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([key_share_group])
        sig_algs = [SignatureScheme.rsa_pss_rsae_sha256,
                    SignatureScheme.rsa_pss_pss_sha256]
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\r\n\r\n")))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectApplicationData()
        node = node.next_sibling.add_child(AlertGenerator(AlertLevel.warning,
                                           AlertDescription.close_notify))

        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        conversations["{0}".format(GroupName.toStr(key_share_group))] = conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throught
    sanity_test = ('sanity', conversations['sanity'])
    ordered_tests = chain([sanity_test],
                          islice(filter(lambda x: x[0] != 'sanity',
                                        conversations.items()), num_limit),
                          [sanity_test])

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except 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("Basic FFDHE test with TLS 1.3 server")
    print("Check if FFDHE groups are supported on the TLS 1.3 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()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384,
        CipherSuite.TLS_CHACHA20_POLY1305_SHA256
    ]

    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    for cipher in [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_AES_256_GCM_SHA384,
            CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
            CipherSuite.TLS_AES_128_CCM_SHA256,
            CipherSuite.TLS_AES_128_CCM_8_SHA256
    ]:

        conversation = Connect(host, port)
        node = conversation
        ciphers = [cipher]

        ext = {}
        groups = [GroupName.secp256r1]
        key_shares = []
        for group in groups:
            key_shares.append(key_share_gen(group))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create(
            key_shares)
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create(groups)
        sig_algs = [
            SignatureScheme.rsa_pss_rsae_sha256,
            SignatureScheme.rsa_pss_pss_sha256
        ]
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(RSA_SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(
            ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectApplicationData()
        node = node.next_sibling.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        conversations["check connection with {0}".format(
            CipherSuite.ietfNames[cipher])] = conversation

        n = 17
        if cipher == CipherSuite.TLS_AES_128_CCM_8_SHA256:
            n = 9
        # fuzz the tag (last 16 bytes or last 8 bytes for the _8 CCM cipher)
        for val in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]:
            for pos in range(-1, -n, -1):

                # Fuzz application data
                conversation = Connect(host, port)
                node = conversation
                ciphers = [cipher]

                ext = {}
                groups = [GroupName.secp256r1]
                ext[ExtensionType.key_share] = key_share_ext_gen(groups)
                ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
                    .create([TLS_1_3_DRAFT, (3, 3)])
                ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                    .create(groups)
                sig_algs = [
                    SignatureScheme.rsa_pss_rsae_sha256,
                    SignatureScheme.rsa_pss_pss_sha256
                ]
                ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
                    .create(sig_algs)
                ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
                    .create(RSA_SIG_ALL)
                node = node.add_child(
                    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())
                msg = ApplicationDataGenerator(
                    bytearray(b"GET / HTTP/1.0\r\n\r\n"))

                node = node.add_child(
                    fuzz_encrypted_message(msg, xors={pos: val}))

                # This message is optional and may show up 0 to many times
                cycle = ExpectNewSessionTicket()
                node = node.add_child(cycle)
                node.add_child(cycle)

                node.next_sibling = ExpectAlert(
                    AlertLevel.fatal, AlertDescription.bad_record_mac)
                node = node.next_sibling.add_child(ExpectClose())

                conversations[
                    "check connection with {0} - fuzz tag on application data with {1} on pos {2}"
                    .format(CipherSuite.ietfNames[cipher], val,
                            pos)] = conversation

                # Fuzz Finished message
                conversation = Connect(host, port)
                node = conversation
                ciphers = [cipher]

                ext = {}
                groups = [GroupName.secp256r1]
                ext[ExtensionType.key_share] = key_share_ext_gen(groups)
                ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
                    .create([TLS_1_3_DRAFT, (3, 3)])
                ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                    .create(groups)
                sig_algs = [
                    SignatureScheme.rsa_pss_rsae_sha256,
                    SignatureScheme.rsa_pss_pss_sha256
                ]
                ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
                    .create(sig_algs)
                ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
                    .create(RSA_SIG_ALL)
                node = node.add_child(
                    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())
                msg = FinishedGenerator()

                node = node.add_child(
                    fuzz_encrypted_message(msg, xors={pos: val}))

                # This message is optional and may show up 0 to many times
                cycle = ExpectNewSessionTicket()
                node = node.add_child(cycle)
                node.add_child(cycle)

                node.next_sibling = ExpectAlert(
                    AlertLevel.fatal, AlertDescription.bad_record_mac)
                node = node.next_sibling.add_child(ExpectClose())

                conversations[
                    "check connection with {0} - fuzz tag on finished message with {1} on pos {2}"
                    .format(CipherSuite.ietfNames[cipher], val,
                            pos)] = 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("The test verifies that TLS 1.3 symmetric ciphers can be negotiated")
    print("and that fuzzing the authentication tag for the same ciphers")
    print("is detected by the server and causes connection failure.")
    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 #48
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ext = {}
    groups = [GroupName.secp256r1,
              GroupName.secp384r1,
              GroupName.secp521r1,
              GroupName.x25519,
              GroupName.x448]
    ext[ExtensionType.key_share] = key_share_ext_gen(groups)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [SignatureScheme.rsa_pss_rsae_sha256,
                SignatureScheme.rsa_pss_pss_sha256]
    sig_algs += ECDSA_SIG_TLS1_3_ALL
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ApplicationDataGenerator(
        bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(AlertGenerator(AlertLevel.warning,
                                       AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    for group in groups:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        ext[ExtensionType.key_share] = key_share_ext_gen([group])
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create([group])
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectEncryptedExtensions())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectCertificateVerify())
        node = node.add_child(ExpectFinished())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ApplicationDataGenerator(
            bytearray(b"GET / HTTP/1.0\r\n\r\n")))

        # This message is optional and may show up 0 to many times
        cycle = ExpectNewSessionTicket()
        node = node.add_child(cycle)
        node.add_child(cycle)

        node.next_sibling = ExpectApplicationData()
        node = node.next_sibling.add_child(AlertGenerator(AlertLevel.warning,
                                           AlertDescription.close_notify))

        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()
        conversations["sanity - {0}".format(GroupName.toRepr(group))] = conversation

        # padded representation
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        key_share = key_share_gen(group)
        key_share.key_exchange += bytearray(b'\x00')
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create([key_share])
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create([group])
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.illegal_parameter))
        node.add_child(ExpectClose())
        conversations["{0} - right 0-padded key_share"
                      .format(GroupName.toRepr(group))] = conversation

        # truncated representation
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        key_share = key_share_gen(group)
        key_share.key_exchange.pop()
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create([key_share])
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create([group])
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.illegal_parameter))
        node.add_child(ExpectClose())
        conversations["{0} - right-truncated key_share"
                      .format(GroupName.toRepr(group))] = conversation

        # key share from wrong curve
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        key_share = key_share_gen(group)
        if group == GroupName.secp256r1:
            key_share2 = key_share_gen(GroupName.secp384r1)
        else:
            key_share2 = key_share_gen(GroupName.secp256r1)
        key_share.key_exchange = key_share2.key_exchange
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create([key_share])
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create([group])
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.illegal_parameter))
        node.add_child(ExpectClose())
        conversations["{0} - key share from other curve"
                      .format(GroupName.toRepr(group))] = conversation

        # 0-point
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        ext = {}
        key_share = KeyShareEntry().create(group, bytearray(b'\x00'))
        ext[ExtensionType.key_share] = ClientKeyShareExtension().create([key_share])
        ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
            .create([TLS_1_3_DRAFT, (3, 3)])
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
            .create([group])
        ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
            .create(sig_algs)
        ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
            .create(SIG_ALL)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.illegal_parameter))
        node.add_child(ExpectClose())
        conversations["{0} - point at infinity"
                      .format(GroupName.toRepr(group))] = conversation

        if group not in (GroupName.x25519, GroupName.x448):
            # points not on curve
            conversation = Connect(host, port)
            node = conversation
            ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                       CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
            ext = {}
            key_share = key_share_gen(group)
            key_share.key_exchange[-1] ^= 0xff
            ext[ExtensionType.key_share] = ClientKeyShareExtension()\
                .create([key_share])
            ext[ExtensionType.supported_versions] = \
                SupportedVersionsExtension()\
                .create([TLS_1_3_DRAFT, (3, 3)])
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create([group])
            ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension()\
                .create(sig_algs)
            ext[ExtensionType.signature_algorithms_cert] = \
                SignatureAlgorithmsCertExtension()\
                .create(SIG_ALL)
            node = node.add_child(ClientHelloGenerator(ciphers,
                                                       extensions=ext))
            node = node.add_child(ExpectAlert(
                AlertLevel.fatal,
                AlertDescription.illegal_parameter))
            node.add_child(ExpectClose())
            conversations["{0} - point outside curve"
                          .format(GroupName.toRepr(group))] = conversation

            # all-zero point
            conversation = Connect(host, port)
            node = conversation
            ciphers = [CipherSuite.TLS_AES_128_GCM_SHA256,
                       CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
            ext = {}
            key_share = key_share_gen(group)
            key_share.key_exchange = bytearray(len(key_share.key_exchange))
            key_share.key_exchange[0] = 0x04  # SEC1 uncompressed point encoding
            ext[ExtensionType.key_share] = ClientKeyShareExtension()\
                .create([key_share])
            ext[ExtensionType.supported_versions] = \
                SupportedVersionsExtension()\
                .create([TLS_1_3_DRAFT, (3, 3)])
            ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
                .create([group])
            ext[ExtensionType.signature_algorithms] = \
                SignatureAlgorithmsExtension()\
                .create(sig_algs)
            ext[ExtensionType.signature_algorithms_cert] = \
                SignatureAlgorithmsCertExtension()\
                .create(SIG_ALL)
            node = node.add_child(ClientHelloGenerator(ciphers,
                                                       extensions=ext))
            node = node.add_child(ExpectAlert(
                AlertLevel.fatal,
                AlertDescription.illegal_parameter))
            node.add_child(ExpectClose())
            conversations["{0} - x=0, y=0"
                          .format(GroupName.toRepr(group))] = 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("Basic ECDHE curve tests in TLS 1.3")
    print("Check if invalid, malformed and incompatible curve key_shares are")
    print("rejected by server")
    print("See also: test-tls13-crfg-curves.py")
    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 #49
0
def main():
    """check if incorrect MAC hash is rejected by server"""
    conversations = {}

    for pos, val in [
        (-1, 0x01),
        (-1, 0xff),
        (-2, 0x01),
        (-2, 0xff),
        (-6, 0x01),
        (-6, 0xff),
        (-12, 0x01),
        (-12, 0xff),
        (-20, 0x01),
        (-20, 0xff),
            # SHA-1 HMAC has just 20 bytes
    ]:
        conversation = Connect("localhost", 4433)
        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(
            fuzz_mac(ApplicationDataGenerator(b"GET / HTTP/1.0\n\n"),
                     xors={pos: val}))
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
        #        node.next_sibling = ExpectClose()
        node = node.add_child(ExpectClose())

        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
                conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(conversation_name + "...")

        runner = Runner(conversation)

        res = True
        #because we don't want to abort the testing and we are reporting
        #the errors to the user, using a bare except is OK
        #pylint: disable=bare-except
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False
        #pylint: enable=bare-except

        if res:
            good += 1
            print("OK")
        else:
            bad += 1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #50
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    coalescing = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help", "coalescing"])
    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 == '--coalescing':
            coalescing = 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
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n")))

    # This message is optional and may show up 0 to many times
    cycle = ExpectNewSessionTicket()
    node = node.add_child(cycle)
    node.add_child(cycle)

    node.next_sibling = ExpectApplicationData()
    node = node.next_sibling.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))

    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()
    conversations["sanity"] = conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    ext = {}
    groups = [GroupName.secp256r1]
    key_shares = []
    for group in groups:
        key_shares.append(key_share_gen(group))
    ext[ExtensionType.key_share] = ClientKeyShareExtension().create(key_shares)
    ext[ExtensionType.supported_versions] = SupportedVersionsExtension()\
        .create([TLS_1_3_DRAFT, (3, 3)])
    ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
        .create(groups)
    sig_algs = [
        SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_pss_sha256
    ]
    ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
        .create(sig_algs)
    ext[ExtensionType.signature_algorithms_cert] = SignatureAlgorithmsCertExtension()\
        .create(RSA_SIG_ALL)
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectEncryptedExtensions())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectCertificateVerify())
    node = node.add_child(ExpectFinished())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(
        ApplicationDataGenerator(
            bytearray(b"GET /keyupdate 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 = ExpectKeyUpdate(
        message_type=KeyUpdateMessageType.update_requested)
    node = node.next_sibling.add_child(
        KeyUpdateGenerator(
            message_type=KeyUpdateMessageType.update_not_requested))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    node.next_sibling = ExpectAlert(AlertLevel.warning,
                                    AlertDescription.close_notify)
    conversations["post-handshake KeyUpdate msg from server"] = 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 throught
    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 with KeyUpdate initiated by server.")
    print("Requires the server to send KeyUpdate message when client performs")
    print("a HTTP GET for /keyupdate resource")

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad or xpass:
        sys.exit(1)
def main():

    #
    # Test if server can handle handshake protocol messages fragmented over
    # multiple records
    #

    conversations = {}

    # 2**14-49 - max size of Client Hello for OpenSSL
    # 2**16-5 - max size of extensions in TLS
    # 2**14-52 - min size of extension that will cause the message to be
    #            fragmented over multiple records
    #
    # note: None for record_len will cause the limit to be set to protocol
    # maximum - 2**14

    for name, ext_len, record_len in [
                                ("small hello", 20, None),
                                ("medium hello", 1024, None),
                                ("big, non fragmented", 2**12, None),
                                ("big, needs fragmentation", 2**14-49, None),
                                ("big, needs fragmentation", 2**14-48, None),
                                ("big, needs fragmentation", 2**15, None),
                                ("maximum size", 2**16-5, None),
                                ("small, reasonable fragmentation", 20, 1024),
                                ("medium, reasonable fragmentation", 1024, 1024),
                                ("big, reasonable fragmentation", 2**12, 1024),
                                ("small, excessive fragmentation", 20, 20),
                                ("medium, excessive fragmentation", 1024, 20),
                                ("big, excessive fragmentation", 2**12, 20),
                                ("small, maximum fragmentation", 20, 1),
                                ("medium, maximum fragmentation", 1024, 1),
                                ("maximum size without fragmentation", 2**14-53, None)]:

        padding_extension = lambda _,l=ext_len:TLSExtension().create(21,
                                                                   bytearray(l))

        conversation = Connect("localhost", 4433)
        node = conversation
        node = node.add_child(SetMaxRecordSize(record_len))
        ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                   CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
        node = node.add_child(ClientHelloGenerator(ciphers,
                                                   extensions={21: padding_extension}))
        node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\n\n")))
        node = node.add_child(ExpectApplicationData())
        # RFCs do not consider Alerts special with regards to fragmentation
        #node = node.add_child(SetMaxRecordSize(2))
        node = node.add_child(AlertGenerator(AlertLevel.warning,
                                             AlertDescription.close_notify))
        node = node.add_child(ExpectAlert())
        node.next_sibling = ExpectClose()

        if record_len is None:
            record_len = "max"
        conversations[name + ": " + str(record_len) + " fragment - " +
                      str(ext_len) + "B extension"] = conversation

    # check if records bigger than TLSPlaintext limit are rejected
    padding_extension = lambda _:TLSExtension().create(21, bytearray(2**14-52))

    conversation = Connect("localhost", 4433)
    node = conversation
    node = node.add_child(SetMaxRecordSize(2**16-1))
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={21: padding_extension}))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["non fragmented, over fragmentation limit: " + str(2**16-1) +
                  " fragment - " + str(2**14-52) + "B extension"] = conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        runner = Runner(conversation)

        print(str(conversation_name) + "...\n")
        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good+=1
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """check if zero-filled padding is accepted by server in SSLv3"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--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 = {}

    # normal connection
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 0)))
    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())
    text = b"GET / HTTP/1.0\nX-bad: aaaa\n\n"
    node = node.add_child(ApplicationDataGenerator(text))
    node = node.add_child(ExpectApplicationData())
    # BEAST 1/n-1 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

    # zero-fill SSLv3 padding
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 0)))
    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())
    text = b"GET / HTTP/1.0\nX-bad: aaaa\n\n"
    hmac_tag_length = 20  # because we're using HMAC-SHA-1
    block_size = 16  # because we're suing AES
    # make sure that padding has full block to work with
    assert (len(text) + hmac_tag_length) % block_size == 0
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(text),
                                       # set all bytes of pad to b'\x00'
                                       # with exception of the length byte
                                       substitutions=dict(
                                           (x, 0) for x in range(0, 15))))
    node = node.add_child(ExpectApplicationData())
    # BEAST 1/n-1 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["zero-filled padding in SSLv3"] = \
            conversation

    # max size SSLv3 padding
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 0)))
    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())
    text = b"GET / HTTP/1.0\nX-bad: aaaa\n\n"
    hmac_tag_length = 20
    block_size = 16
    # make sure that padding has full block to work with
    assert (len(text) + hmac_tag_length) % block_size == 0
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(text),
                                       min_length=128//8))
    node = node.add_child(ExpectApplicationData())
    # BEAST 1/n-1 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["max acceptable padding in SSLv3"] = \
            conversation

    # too much padding in SSLv3
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 0)))
    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())
    text = b"GET / HTTP/1.0\nX-bad: aaaa\n\n"
    hmac_tag_length = 20
    block_size = 16
    # make sure that padding has full block to work with
    assert (len(text) + hmac_tag_length) % block_size == 0
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(text),
                                       # make the padding longer than the block
                                       # size of the cipher
                                       min_length=(128//8)+1))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.bad_record_mac))
    node.add_child(ExpectClose())

    conversations["excessive padding in SSLv3 (valid in TLS 1.0)"] = \
            conversation

    # max size SSLv3 padding
    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    node = node.add_child(ClientHelloGenerator(ciphers, version=(3, 0)))
    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())
    text = b"GET / HTTP/1.0\r\nX-bad: a\r\n"
    # some servers put limits on size of headers, e.g. Apache 2.3 has 8190 Bytes
    for i in range(3):
        text += b"X-ba" + compatAscii2Bytes(str(i)) + b": " + \
                b"a" * (4096 - 9) + b"\r\n"
    text += b"X-ba3: " + b"a" * (4096 - 37) + b"\r\n"
    text += b"\r\n"
    assert len(text) == 2**14, len(text)
    # as SSLv3 specifies that padding must be minimal length, that means
    # there is just one valid length of padding, no need to force it
    node = node.add_child(ApplicationDataGenerator(text))
    node = node.add_child(ExpectApplicationData())
    # BEAST 1/n-1 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["max Application Data in SSLv3"] = \
            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("Script to verify different padding scenarios in SSLv3.")
    print("Check if the padding in SSLv3 follows the RFC6101\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 or xpass:
        sys.exit(1)
def main():
    """check if incorrect padding is rejected by server"""
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '-d':
            dhe = True
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    if dhe:
        ext = {}
        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:
        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(ExpectCertificateRequest())
    fork = node
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator())

    # handle servers which ask for client certificates
    fork.next_sibling = ExpectServerHelloDone()
    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(
        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 pos, val in [
                     (-1, 0x01),
                     (-1, 0xff),
                     (-2, 0x01),
                     (-2, 0xff),
                     (-6, 0x01),
                     (-6, 0xff),
                     (-12, 0x01),
                     (-12, 0xff),
                     (-20, 0x01),
                     (-20, 0xff),
                     # we're generating at least 20 bytes of padding
                     ]:
        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)
            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:
            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(ExpectCertificateRequest())
        fork = node
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(CertificateGenerator())

        # handle servers which ask for client certificates
        fork.next_sibling = ExpectServerHelloDone()
        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(fuzz_padding(ApplicationDataGenerator(
                                                        b"GET / HTTP/1.0\n\n"),
                                           xors={pos:val},
                                           min_length=20))
        node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                          AlertDescription.bad_record_mac))
#        node.next_sibling = ExpectClose()
        node = node.add_child(ExpectClose())

        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
                conversation

    # zero-fill the padding
    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)
        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:
        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(ExpectCertificateRequest())
    fork = node
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(CertificateGenerator())

    # handle servers which ask for client certificates
    fork.next_sibling = ExpectServerHelloDone()
    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())
    # block size for AES-128 is 16 bytes
    # SHA-1 MAC is 20 bytes long
    # length of "GET / HTTP" is 10 bytes
    # which means the padding will be two bytes - 1 byte of padding and one
    # byte length
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(
                                                    b"GET / HTTP"),
                                       substitutions={0:0}))
    node = node.add_child(ExpectAlert(AlertLevel.fatal,
                                      AlertDescription.bad_record_mac))
    node = node.add_child(ExpectClose())

    conversations["zero-filled"] = \
            conversation

    # run the conversation
    good = 0
    bad = 0
    xfail = 0
    xpass = 0
    failed = []
    xpassed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    if run_only:
        if num_limit > len(run_only):
            num_limit = len(run_only)
        regular_tests = [(k, v) for k, v in conversations.items() if
                          k in run_only]
    else:
        regular_tests = [(k, v) for k, v in conversations.items() if
                         (k != 'sanity') and k not in run_exclude]
    sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        exception = None
        try:
            runner.run()
        except Exception as exp:
            exception = exp
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if c_name in expected_failures:
            if res:
                xpass += 1
                xpassed.append(c_name)
                print("XPASS-expected failure but test passed\n")
            else:
                if expected_failures[c_name] is not None and  \
                    expected_failures[c_name] not in str(exception):
                        bad += 1
                        failed.append(c_name)
                        print("Expected error message: {0}\n"
                            .format(expected_failures[c_name]))
                else:
                    xfail += 1
                    print("OK-expected failure\n")
        else:
            if res:
                good += 1
                print("OK\n")
            else:
                bad += 1
                failed.append(c_name)

    print("Check if incorrect padding is rejected by server")

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2*len(sanity_tests)))
    print("SKIP: {0}".format(len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed ,key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad or xpass:
        sys.exit(1)
Example #54
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    min_ext = 52
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:m:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '-m':
            min_ext = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_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\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()
    node = node.add_child(ExpectClose())
    conversations["sanity"] = conversation

    # send client hello with large padding extension
    for i in chain(range(10), range(0x3f00, 0x4010), range(0x1ff0, 0x2010),
                   range(0x8ff0, 0x9010), range(0xff00, 0xffff - 4)):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.client_hello_padding: PaddingExtension().create(i)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["ext padding, {0} bytes".format(i)] = conversation

    # send large extension from unallocated range
    for i in chain(range(10), range(0x3f00, 0x4010), range(0x1ff0, 0x2010),
                   range(0x8ff0, 0x9010), range(0xff00, 0xffff - 4)):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {80: TLSExtension(extType=80).create(bytearray(i))}
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["ext #80, {0} bytes".format(i)] = conversation

    # send two extensions, where one is large (4096) and the other is
    # changing size
    for i in chain(range(10), range(0x2f00, 0x3010), range(0x3f00, 0x4010),
                   range(0x1ff0, 0x2010), range(0x8ff0, 0x9010),
                   range(0xef00, 0xefff - 8)):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            80: TLSExtension(extType=80).create(bytearray(i)),
            ExtensionType.client_hello_padding:
            PaddingExtension().create(0x1000)
        }
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["two ext, #80 {0} bytes".format(i)] = conversation

    # send client hello messages with multiple extensions
    for i in chain(range(10 // 4), range(0x3f00 // 4, 0x4010 // 4),
                   range(0x1ff0 // 4, 0x2010 // 4),
                   range(0x8ff0 // 4, 0x9010 // 4),
                   range(0xff00 // 4, 0x10000 // 4)):
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        high_num_ext = (ExtensionType.supports_npn, ExtensionType.tack,
                        ExtensionType.renegotiation_info)
        # increase the count if some extension points will be skipped because
        # they are meaningful
        for num in high_num_ext:
            if i + min_ext > num:
                i += 1
        ext = dict((j, TLSExtension(extType=j))
                   for j in range(min_ext, min_ext + i)
                   if j not in high_num_ext)
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["multiple extensions {0}".format(i)] = conversation

    # send client hello with large number of ciphersuites but overall
    # even length
    for i in chain(range(1, 10 // 2), range(0x3f00 // 2, 0x4010 // 2),
                   range(0x1ff0 // 2, 0x2010 // 2),
                   range(0x8ff0 // 2, 0x9010 // 2),
                   range(0xff00 // 2, 0xffff // 2 - 2)):
        conversation = Connect(host, port)
        node = conversation
        if i < 0x5600 - 0x0100:
            ciphers = list(range(0x0100, 0x0100 + i))
        else:
            ciphers = list(range(0x0100, 0x5600))
            i -= 0x5600 - 0x0100
            ciphers += list(range(0x5601, 0x5601 + i))
        ciphers += [
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA
        ]
        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\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()
        node = node.add_child(ExpectClose())
        conversations["ciphers even {0}".format(i)] = conversation

    # send client hello with large number of ciphersuites but overall
    # odd length
    for i in chain(range(1, 10 // 2), range(0x3f00 // 2, 0x4010 // 2),
                   range(0x1ff0 // 2, 0x2010 // 2),
                   range(0x8ff0 // 2, 0x9010 // 2),
                   range(0xff00 // 2, 0xffff // 2 - 2)):
        conversation = Connect(host, port)
        node = conversation
        if i < 0x5600 - 0x0100:
            ciphers = list(range(0x0100, 0x0100 + i))
        else:
            ciphers = list(range(0x0100, 0x5600))
            i -= 0x5600 - 0x0100
            ciphers += list(range(0x5601, 0x5601 + i))
        ext = {
            ExtensionType.client_hello_padding: PaddingExtension().create(1)
        }
        ciphers += [
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA
        ]
        node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["ciphers odd {0}".format(i)] = conversation

    # hello split over at least two records, first very small (2B)
    for i in chain(range(10), range(0x3f00, 0x4010), range(0x1ff0, 0x2010),
                   range(0x8ff0, 0x9010), range(0xff00, 0xffff - 4)):
        fragment_list = []
        conversation = Connect(host, port)
        node = conversation
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
        ext = {
            ExtensionType.client_hello_padding: PaddingExtension().create(i)
        }
        hello_gen = ClientHelloGenerator(ciphers, extensions=ext)
        node = node.add_child(split_message(hello_gen, fragment_list, 2))
        node = node.add_child(FlushMessageList(fragment_list))
        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\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()
        node = node.add_child(ExpectClose())
        conversations["fragmented, padding ext {0} bytes".format(
            i)] = conversation

    # sanity check fragmentation
    fragment_list = []
    conversation = Connect(host, port)
    node = conversation
    ciphers = [
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
    ]
    hello_gen = ClientHelloGenerator(ciphers)
    node = node.add_child(split_message(hello_gen, fragment_list, 2))
    node = node.add_child(FlushMessageList(fragment_list))
    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\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()
    node = node.add_child(ExpectClose())
    conversations["sanity check - fragmented"] = conversation

    # test with ciphers and extensions filled to the brim
    conversation = Connect(host, port)
    node = conversation
    i = 0xfffe // 2 - 2  # two ciphers are constant
    if i < 0x5600 - 0x0100:
        ciphers = list(range(0x0100, 0x0100 + i))
    else:
        ciphers = list(range(0x0100, 0x5600))
        i -= 0x5600 - 0x0100
        ciphers += list(range(0x5601, 0x5601 + i))
    ciphers += [
        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA
    ]
    # the 4 bytes subtracted from 0xffff are for ext ID and ext len
    ext = {80: TLSExtension(extType=80).create(bytearray(0xffff - 4))}
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    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\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()
    node = node.add_child(ExpectClose())
    conversations["huge hello"] = 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("Test end")
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():
    """Test if the server supports export grade SSLv2 ciphers"""
    conversations = {}
    host = "localhost"
    port = 4433

    argv = sys.argv[1:]

    opts, argv = getopt.getopt(argv, "h:p:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))
    if argv:
        help_msg()
        raise ValueError("Unknown options: {0}".format(argv))

    for prot_vers, proto_name in {
            (0, 2):"SSLv2",
            (3, 0):"SSLv3",
            (3, 1):"TLSv1.0"
            }.items():
        for cipher_id, cipher_name in {
                CipherSuite.SSL_CK_RC4_128_EXPORT40_WITH_MD5:"EXP-RC4-MD5",
                CipherSuite.SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
                    "EXP-RC2-CBC-MD5"
                }.items():
            # instruct RecordLayer to use SSLv2 record layer protocol (0, 2)
            conversation = Connect(host, port, version=(0, 2))
            node = conversation
            ciphers = [CipherSuite.SSL_CK_DES_192_EDE3_CBC_WITH_MD5,
                       CipherSuite.SSL_CK_RC4_128_WITH_MD5,
                       CipherSuite.SSL_CK_RC4_128_EXPORT40_WITH_MD5,
                       CipherSuite.SSL_CK_RC2_128_CBC_WITH_MD5,
                       CipherSuite.SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5,
                       CipherSuite.SSL_CK_IDEA_128_CBC_WITH_MD5,
                       CipherSuite.SSL_CK_DES_64_CBC_WITH_MD5]

            node = node.add_child(ClientHelloGenerator(ciphers,
                                                       version=prot_vers,
                                                       ssl2=True))
            # we can get a ServerHello with no ciphers:
            node = node.add_child(ExpectServerHello2())
            # or we can get an error stright away, and connection closure
            node.next_sibling = ExpectSSL2Alert(SSL2ErrorDescription.no_cipher)
            node.next_sibling.add_child(ExpectClose())
            alternative = node.next_sibling
            # or the server may close the connection right away (likely in
            # case SSLv2 is completely disabled)
            alternative.next_sibling = ExpectClose()
            alternative = alternative.next_sibling
            # or finally, we can get a TLS Alert message
            alternative.next_sibling = ExpectAlert()
            alternative.next_sibling.add_child(ExpectClose())
            # in case we got ServerHello, try to force one of the ciphers
            node = node.add_child(ClientMasterKeyGenerator(cipher=cipher_id))
            # it should result in error
            node = node.add_child(ExpectSSL2Alert())
            # or connection close
            node.next_sibling = ExpectClose()
            # in case of error, we expect the server to close connection
            node = node.add_child(ExpectClose())

            conversations["Connect with {1} {0}"
                          .format(cipher_name, proto_name)] = conversation

    good = 0
    bad = 0

    for conversation_name, conversation in conversations.items():
        print("{0} ...".format(conversation_name))

        runner = Runner(conversation)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            print("")
            res = False

        if res:
            good+=1
            print("OK\n")
        else:
            bad+=1

    print("Note: SSLv2 was officially deprecated (MUST NOT use) in 2011, see")
    print("      RFC 6176.")
    print("      If one or more of the tests fails because of error in form of")
    print("")
    print("      Unexpected message from peer: Handshake()")
    print("")
    print("      With any number inside parethensis, and the server is")
    print("      configured to not support SSLv2, it means it most")
    print("      likely is vulnerable to CVE-2015-3197 and CVE-2016-0800.")
    print("      In case it's a RC4 or 3DES cipher, you may verify that it")
    print("      really supports it using:")
    print("      test-sslv2-connection.py")
    print("")
    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    """
    Test interleaving of Application Data with handshake messages,
    requires an HTTP-like server with support for client initiated
    renegotiation
    """
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-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 == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
               CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    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\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

    conver = Connect(host, port)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    fragment_list = []
    node = node.add_child(split_message(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                                             session_id=bytearray(0),
                                                             extensions={ExtensionType.renegotiation_info:None}),
                                        fragment_list, 30))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(FlushMessageList(fragment_list))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(ApplicationDataGenerator(bytearray(b"\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["Application data inside Client Hello"] = conver

    conver = Connect(host, port)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                               session_id=bytearray(0),
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    fragment_list = []
    node = node.add_child(split_message(ClientKeyExchangeGenerator(), fragment_list, 30))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(FlushMessageList(fragment_list))
    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"\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["Application data inside Client Key Exchange"] = conver

    # CCS is one byte, can't split it
    #conversations["Application data inside Change Cipher Spec"] = conver

    conver = Connect(host, port)
    node = conver
    #ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
    #           CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
    node = node.add_child(ClientHelloGenerator(ciphers,
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    node = node.add_child(ClientHelloGenerator([CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA],
                                               session_id=bytearray(0),
                                               extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectServerHello(extensions={ExtensionType.renegotiation_info:None}))
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    fragments = []
    node = node.add_child(split_message(FinishedGenerator(), fragments, 10))
    # interleaved AppData
    node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0")))
    node = node.add_child(ExpectAlert())
    node.next_sibling = ExpectClose()

    conversations["Application data inside Finished"] = conver

    # run the conversation
    good = 0
    bad = 0
    xfail = 0
    xpass = 0
    failed = []
    xpassed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    if run_only:
        if num_limit > len(run_only):
            num_limit = len(run_only)
        regular_tests = [(k, v) for k, v in conversations.items() if
                          k in run_only]
    else:
        regular_tests = [(k, v) for k, v in conversations.items() if
                         (k != 'sanity') and k not in run_exclude]
    sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        exception = None
        try:
            runner.run()
        except Exception as exp:
            exception = exp
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if c_name in expected_failures:
            if res:
                xpass += 1
                xpassed.append(c_name)
                print("XPASS-expected failure but test passed\n")
            else:
                if expected_failures[c_name] is not None and  \
                    expected_failures[c_name] not in str(exception):
                        bad += 1
                        failed.append(c_name)
                        print("Expected error message: {0}\n"
                            .format(expected_failures[c_name]))
                else:
                    xfail += 1
                    print("OK-expected failure\n")
        else:
            if res:
                good += 1
                print("OK\n")
            else:
                bad += 1
                failed.append(c_name)

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2*len(sanity_tests)))
    print("SKIP: {0}".format(len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed ,key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():
    """check if atypical padding is accepted by server"""
    conversations = {}

    conversation = Connect("localhost", 4433)
    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())
    text = b"GET / HTTP/1.0\nX-bad: aaaa\n\n"
    hmac_tag_length = 20
    block_size = 16
    # make sure that padding has full blocks to work with
    assert (len(text) + hmac_tag_length) % block_size == 0
    node = node.add_child(fuzz_padding(ApplicationDataGenerator(text),
                                       min_length=255))
    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["256 bytes of padding"] = \
            conversation

    # run the conversation
    good = 0
    bad = 0

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(conversation_name + "...")

        runner = Runner(conversation)

        res = True
        #because we don't want to abort the testing and we are reporting
        #the errors to the user, using a bare except is OK
        #pylint: disable=bare-except
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False
        #pylint: enable=bare-except

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = str()
    dhe = False

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:x:X:n:d", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-x':
            expected_failures[arg] = None
            last_exp_tmp = str(arg)
        elif opt == '-X':
            if not last_exp_tmp:
                raise ValueError("-x has to be specified before -X")
            expected_failures[last_exp_tmp] = str(arg)
        elif opt == '-d':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    conversation = Connect(host, port)
    node = conversation
    ext = None
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext = {
            ExtensionType.supported_groups:
            SupportedGroupsExtension().create(groups),
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create(RSA_SIG_ALL),
            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))
    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 /?Renegotiation_Test=tlsfuzzer 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

    # renegotiation
    conversation = Connect(host, port)
    node = conversation

    ext = {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]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = {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)
    node = node.add_child(
        ClientHelloGenerator(ciphers, session_id=bytearray(0), extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # send GET request
    node = node.add_child(
        ApplicationDataGenerator(
            bytearray(b"GET /?Renegotiation_Test=tlsfuzzer HTTP/1.0\r\n")))
    node = node.add_child(ApplicationDataGenerator(bytearray(b'\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[
        "secure renegotiation with GET after 2nd handshake"] = conversation

    # renegotiation
    conversation = Connect(host, port)
    node = conversation

    ext = {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]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # send incomplete GET request
    node = node.add_child(
        ApplicationDataGenerator(
            bytearray(b"GET /?Renegotiation_Test=tlsfuzzer HTTP/1.0\r\n")))
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = {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)
    node = node.add_child(
        ClientHelloGenerator(ciphers, session_id=bytearray(0), extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # finish the GET request
    node = node.add_child(ApplicationDataGenerator(bytearray(b'\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["secure renegotiation with incomplete GET"] = conversation

    # insecure renegotiation
    conversation = Connect(host, port)
    node = conversation

    ext = None
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext = {
            ExtensionType.supported_groups:
            SupportedGroupsExtension().create(groups),
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create(RSA_SIG_ALL),
            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]
    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())
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = None
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext = {
            ExtensionType.supported_groups:
            SupportedGroupsExtension().create(groups),
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create(RSA_SIG_ALL),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
    node = node.add_child(
        ClientHelloGenerator(ciphers, session_id=bytearray(0), 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())
    # send GET request
    node = node.add_child(
        ApplicationDataGenerator(
            bytearray(b"GET /?Renegotiation_Test=tlsfuzzer HTTP/1.0\r\n")))
    node = node.add_child(ApplicationDataGenerator(bytearray(b'\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[
        "insecure (legacy) renegotiation with GET after 2nd handshake"] = conversation

    # insecure renegotiation
    conversation = Connect(host, port)
    node = conversation

    ext = None
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext = {
            ExtensionType.supported_groups:
            SupportedGroupsExtension().create(groups),
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create(RSA_SIG_ALL),
            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]
    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())
    # send incomplete GET request
    node = node.add_child(
        ApplicationDataGenerator(
            bytearray(b"GET /?Renegotiation_Test=tlsfuzzer HTTP/1.0\r\n")))
    # 2nd handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = None
    if dhe:
        groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext = {
            ExtensionType.supported_groups:
            SupportedGroupsExtension().create(groups),
            ExtensionType.signature_algorithms:
            SignatureAlgorithmsExtension().create(RSA_SIG_ALL),
            ExtensionType.signature_algorithms_cert:
            SignatureAlgorithmsCertExtension().create(RSA_SIG_ALL)
        }
    node = node.add_child(
        ClientHelloGenerator(ciphers, session_id=bytearray(0), 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())
    # finish the GET request
    node = node.add_child(ApplicationDataGenerator(bytearray(b'\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[
        "insecure (legacy) renegotiation with incomplete GET"] = conversation

    # sending both SCSV and renegotiation_info: cold, then renegotiated
    conversation = Connect(host, port)
    node = conversation

    # Sending both is NOT RECOMMENDED, though valid, for the first handshake...
    ext = {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,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    else:
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # ... but not for the second handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = {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,
            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, session_id=bytearray(0), extensions=ext))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure))
    node = node.add_child(ExpectClose())
    conversations[
        "sending both SCSV and renegotiation_info in renegotiated handshake"] = conversation

    # sending both SCSV and renegotiation_info: cold, then renegotiated+resumed
    conversation = Connect(host, port)
    node = conversation

    # Sending both is NOT RECOMMENDED, though valid, for the first handshake...
    ext = {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,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    else:
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    ext = {ExtensionType.renegotiation_info: None}
    node = node.add_child(ExpectServerHello(extensions=ext))
    node = node.add_child(ExpectCertificate())
    if dhe:
        node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    # ... but not for the second handshake
    node = node.add_child(ResetHandshakeHashes())
    ext = {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,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    else:
        ciphers = [
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        ]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure))
    node = node.add_child(ExpectClose())
    conversations[
        "sending both SCSV and renegotiation_info in renegotiation+resumption"] = conversation

    # non-empty initial renegotiation_info extension (with SCSV)
    conversation = Connect(host, port)
    node = conversation

    ext = {
        ExtensionType.renegotiation_info:
        RenegotiationInfoExtension().create(bytearray(b'abc'))
    }
    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))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure))
    node = node.add_child(ExpectClose())
    conversations[
        "non-empty initial renegotiation_info extension (with SCSV)"] = conversation

    # non-empty initial renegotiation_info extension (without SCSV)
    conversation = Connect(host, port)
    node = conversation

    ext = {
        ExtensionType.renegotiation_info:
        RenegotiationInfoExtension().create(bytearray(b'abc'))
    }
    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]
    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
    node = node.add_child(
        ExpectAlert(AlertLevel.fatal, AlertDescription.handshake_failure))
    node = node.add_child(ExpectClose())
    conversations[
        "non-empty initial renegotiation_info extension (without SCSV)"] = conversation

    # run the conversation
    good = 0
    bad = 0
    xfail = 0
    xpass = 0
    failed = []
    xpassed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    if run_only:
        if num_limit > len(run_only):
            num_limit = len(run_only)
        regular_tests = [(k, v) for k, v in conversations.items()
                         if k in run_only]
    else:
        regular_tests = [(k, v) for k, v in conversations.items()
                         if (k != 'sanity') and k not in run_exclude]
    sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        exception = None

        try:
            runner.run()
        except Exception as exp:
            exception = exp
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if c_name in expected_failures:
            if res:
                xpass += 1
                xpassed.append(c_name)
                print("XPASS-expected failure but test passed\n")
            else:
                if expected_failures[c_name] is not None and  \
                    expected_failures[c_name] not in str(exception):
                    bad += 1
                    failed.append(c_name)
                    print("Expected error message: {0}\n".format(
                        expected_failures[c_name]))
                else:
                    xfail += 1
                    print("OK-expected failure\n")
        else:
            if res:
                good += 1
                print("OK\n")
            else:
                bad += 1
                failed.append(c_name)

    print("Check if the server supports insecure (legacy) renegotiation")

    print("Test end")
    print(20 * '=')
    print("version: {0}".format(version))
    print(20 * '=')
    print("TOTAL: {0}".format(len(sampled_tests) + 2 * len(sanity_tests)))
    print("SKIP: {0}".format(
        len(run_exclude.intersection(conversations.keys()))))
    print("PASS: {0}".format(good))
    print("XFAIL: {0}".format(xfail))
    print("FAIL: {0}".format(bad))
    print("XPASS: {0}".format(xpass))
    print(20 * '=')
    sort = sorted(xpassed, key=natural_sort_keys)
    if len(sort):
        print("XPASSED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))
    sort = sorted(failed, key=natural_sort_keys)
    if len(sort):
        print("FAILED:\n\t{0}".format('\n\t'.join(repr(i) for i in sort)))

    if bad > 0:
        sys.exit(1)
def main():
    """check if obsolete signature algorithm is rejected by server"""
    conversations = {}
    hostname = "localhost"
    port = 4433

    argv = sys.argv[1:]
    if len(argv) != 4:
        raise ValueError("You need to specify key (-k file.pem) and "
                         "certificate (-c file.pem)")
    opts, argv = getopt.getopt(argv, "k:c:")

    for opt, arg in opts:
        if opt == '-k':
            text_key = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_key = str(text_key, 'utf-8')
            private_key = parsePEMKey(text_key, private=True)
        if opt == '-c':
            text_cert = open(arg, 'rb').read()
            if sys.version_info[0] >= 3:
                text_cert = str(text_cert, 'utf-8')
            cert = X509()
            cert.parse(text_cert)

    if not private_key:
        raise ValueError("Specify private key file using -k")
    if not cert:
        raise ValueError("Specify certificate file using -c")

    for md in ['sha1', 'sha256', 'sha384', 'sha512']:
	    conversation = Connect(hostname, port)
	    node = conversation
	    ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
		       CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
	    ext = {ExtensionType.signature_algorithms :
		   SignatureAlgorithmsExtension().create([
		     (getattr(HashAlgorithm, x),
		      SignatureAlgorithm.rsa) for x in ['sha512', 'sha384', 'sha256',
							'sha224', 'sha1', 'md5']])}
	    node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
	    node = node.add_child(ExpectServerHello(version=(3, 3)))
	    node = node.add_child(ExpectCertificate())
	    node = node.add_child(ExpectCertificateRequest())
	    node = node.add_child(ExpectServerHelloDone())
	    node = node.add_child(CertificateGenerator(X509CertChain([cert])))
	    node = node.add_child(ClientKeyExchangeGenerator())
	    node = node.add_child(CertificateVerifyGenerator(
		private_key, sig_type=(getattr(HashAlgorithm, md), SignatureAlgorithm.rsa)))
	    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\n\n"))
	    node = node.add_child(ExpectApplicationData())
	    node = node.add_child(AlertGenerator(AlertDescription.close_notify))
	    node = node.add_child(ExpectClose())
	    node.next_sibling = ExpectAlert()
	    node.next_sibling.add_child(ExpectClose())

	    conversations["check " + md] = conversation

    # run the conversation
    good = 0
    bad = 0

    print("Certificate Verify test version 2")

    for conversation_name in conversations:
        conversation = conversations[conversation_name]

        print(conversation_name + "...")

        runner = Runner(conversation)

        res = True
        #because we don't want to abort the testing and we are reporting
        #the errors to the user, using a bare except is OK
        #pylint: disable=bare-except
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False
        #pylint: enable=bare-except

        if res:
            good+=1
            print("OK")
        else:
            bad+=1

    print("Test end")
    print("successful: {0}".format(good))
    print("failed: {0}".format(bad))

    if bad > 0:
        sys.exit(1)
Example #60
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()

    argv = sys.argv[1:]
    opts, args = getopt.getopt(argv, "h:p:e:n:", ["help"])
    for opt, arg in opts:
        if opt == '-h':
            host = arg
        elif opt == '-p':
            port = int(arg)
        elif opt == '-e':
            run_exclude.add(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    conversations = {}

    # The payload has to be long enough to trigger heap overflow
    # Create max. length application data:
    #   2**14 (max. record size) - 28 ("GET / HTTP/1.0\r\nX-test: " + ending)
    data_length = 2**14 - 28

    # 16 bytes: POLY1305 tag 128 bit
    # Tampering one bit suffices to damage the mac
    fuzzes = [(x, 2**y) for x in range(-16, 0) for y in range(8)]
    for pos, val in fuzzes:
        conversation = Connect(host, port)
        node = conversation
        ciphers = [CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256]
        node = node.add_child(ClientHelloGenerator(ciphers))
        node = node.add_child(ExpectServerHello())
        node = node.add_child(ExpectCertificate())
        node = node.add_child(ExpectServerKeyExchange())
        node = node.add_child(ExpectServerHelloDone())
        node = node.add_child(ClientKeyExchangeGenerator())
        node = node.add_child(ChangeCipherSpecGenerator())
        node = node.add_child(FinishedGenerator())
        node = node.add_child(ExpectChangeCipherSpec())
        node = node.add_child(ExpectFinished())
        node = node.add_child(
            fuzz_encrypted_message(
                ApplicationDataGenerator(b"GET / HTTP/1.0\r\n" + b'X-test: ' +
                                         data_length * b'A' + b'\r\n\r\n'),
                xors={pos: val}))
        node = node.add_child(
            ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
        node = node.add_child(ExpectClose())
        conversations["XOR position " + str(pos) + " with " + str(hex(val))] = \
            conversation

    conversation = Connect(host, port)
    node = conversation
    ciphers = [CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256]
    node = node.add_child(ClientHelloGenerator(ciphers))
    node = node.add_child(ExpectServerHello())
    node = node.add_child(ExpectCertificate())
    node = node.add_child(ExpectServerKeyExchange())
    node = node.add_child(ExpectServerHelloDone())
    node = node.add_child(ClientKeyExchangeGenerator())
    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    node = node.add_child(
        ApplicationDataGenerator(b"GET / HTTP/1.0\r\n" + b'X-test: ' +
                                 data_length * b'A' + b'\r\n\r\n'))
    node = node.add_child(ExpectApplicationData())
    node = node.add_child(
        AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
    # allow for multiple application data records in response
    node = node.add_child(ExpectApplicationData())
    loop = node
    node = node.add_child(
        ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
    loop.next_sibling = node
    node.next_sibling = ExpectClose()
    conversations["sanity"] = \
        conversation

    # run the conversation
    good = 0
    bad = 0
    failed = []
    if not num_limit:
        num_limit = len(conversations)

    # make sure that sanity test is run first and last
    # to verify that server was running and kept running throughout
    sanity_tests = [('sanity', conversations['sanity'])]
    regular_tests = [(k, v) for k, v in conversations.items() if k != 'sanity']
    sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
    ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

    for c_name, c_test in ordered_tests:
        if run_only and c_name not in run_only or c_name in run_exclude:
            continue
        print("{0} ...".format(c_name))

        runner = Runner(c_test)

        res = True
        try:
            runner.run()
        except:
            print("Error while processing")
            print(traceback.format_exc())
            res = False

        if res:
            good += 1
            print("OK\n")
        else:
            bad += 1
            failed.append(c_name)

    print("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)