def test_filterForVersion_with_SSL3_ciphers(self):
        suites = [
            CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
        ]

        filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 0))

        self.assertEqual(
            filtered,
            [
                CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
            ],
        )

        filtered = CipherSuite.filterForVersion(suites, (3, 3), (3, 3))

        self.assertEqual(
            filtered,
            [
                CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
            ],
        )
 def test_getTLS13Suites(self):
     hs = HandshakeSettings()
     hs.maxVersion = (3, 4)
     self.assertEqual(CipherSuite.getTLS13Suites(hs),
                      [CipherSuite.TLS_AES_256_GCM_SHA384,
                       CipherSuite.TLS_AES_128_GCM_SHA256,
                       CipherSuite.TLS_CHACHA20_POLY1305_SHA256])
Example #3
0
 def test_getTLS13Suites(self):
     hs = HandshakeSettings()
     hs.maxVersion = (3, 4)
     self.assertEqual(CipherSuite.getTLS13Suites(hs),
                      [CipherSuite.TLS_AES_256_GCM_SHA384,
                       CipherSuite.TLS_AES_128_GCM_SHA256,
                       CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
                       CipherSuite.TLS_AES_128_CCM_SHA256])
Example #4
0
    def test_filterForVersion_with_SSL3_ciphers(self):
        suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_RC4_128_MD5]

        filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 0))

        self.assertEqual(filtered,
                         [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_RC4_128_MD5])

        filtered = CipherSuite.filterForVersion(suites, (3, 3), (3, 3))

        self.assertEqual(filtered,
                         [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_RC4_128_MD5])
Example #5
0
    def test_getEcdsaSuites(self):
        hs = HandshakeSettings()
        hs.keyExchangeNames = ["ecdhe_ecdsa"]
        hs.cipherNames = ["aes128"]

        filtered = CipherSuite.getEcdsaSuites(hs)

        self.assertEqual(filtered,
                         [CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
                          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA])
Example #6
0
    def test_filterForVersion_with_TLS_1_3(self):
        suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                  CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                  CipherSuite.TLS_AES_128_GCM_SHA256]

        filtered = CipherSuite.filterForVersion(suites, (3, 4), (3, 4))

        self.assertEqual(filtered,
                         [CipherSuite.TLS_AES_128_GCM_SHA256])
    def test_filterForVersion_with_TLS_1_3(self):
        suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                  CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                  CipherSuite.TLS_AES_128_GCM_SHA256]

        filtered = CipherSuite.filterForVersion(suites, (3, 4), (3, 4))

        self.assertEqual(filtered,
                         [CipherSuite.TLS_AES_128_GCM_SHA256])
Example #8
0
    def test_filter_for_certificate_with_no_cert(self):
        orig_ciphers = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                        CipherSuite.TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA,
                        CipherSuite.TLS_AES_128_GCM_SHA256]

        new_ciphers = CipherSuite.filter_for_certificate(
            orig_ciphers,
            None)

        self.assertFalse(orig_ciphers is new_ciphers)
        self.assertEqual(new_ciphers,
                         [CipherSuite.TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA,
                          CipherSuite.TLS_AES_128_GCM_SHA256])
    def test_filterForVersion_with_TLS_1_2_ciphers(self):
        suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                  CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
                  CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                  CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256]

        filtered = CipherSuite.filterForVersion(suites, (3, 2), (3, 2))

        self.assertEqual(filtered,
                         [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                          CipherSuite.TLS_RSA_WITH_RC4_128_MD5])
Example #10
0
    def test_filterForVersion_with_TLS_1_2_ciphers(self):
        suites = [
            CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
            CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256
        ]

        filtered = CipherSuite.filterForVersion(suites, (3, 2), (3, 2))

        self.assertEqual(filtered, [
            CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
            CipherSuite.TLS_RSA_WITH_RC4_128_MD5
        ])
Example #11
0
    def test_filter_for_certificate_with_ecdsa(self):
        cert_list = mock.MagicMock()
        cert = mock.MagicMock()
        cert.certAlg = "ecdsa"
        cert_list.x509List = [cert]

        orig_ciphers = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                        CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
                        CipherSuite.TLS_AES_128_GCM_SHA256]

        new_ciphers = CipherSuite.filter_for_certificate(
            orig_ciphers, cert_list)

        self.assertEqual(new_ciphers,
                         [CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
                          CipherSuite.TLS_AES_128_GCM_SHA256])
    def test_filterForVersion_with_unknown_ciphers(self):
        suites = [0, 0xFFFE]

        filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 3))

        self.assertEqual(filtered, [])
Example #13
0
    def test___init__(self):
        cipherSuites = CipherSuite()

        self.assertIsNotNone(cipherSuites)
Example #14
0
    def test_filterForVersion_with_unknown_ciphers(self):
        suites = [0, 0xfffe]

        filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 3))

        self.assertEqual(filtered, [])
Example #15
0
def main():
    host = "localhost"
    port = 4433
    num_limit = None
    run_exclude = set()
    expected_failures = {}
    last_exp_tmp = None
    timing = False
    outdir = "/tmp"
    repetitions = 100
    interface = None
    quick = False
    cipher = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA
    affinity = None
    ciphertext_len = 512

    argv = sys.argv[1:]
    opts, args = getopt.getopt(
        argv, "h:p:e:x:X:n:l:o:i:C:",
        ["help", "repeat=", "quick", "cpu-list=", "payload-len="])
    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 == '-C':
            if arg[:2] == '0x':
                cipher = int(arg, 16)
            else:
                try:
                    cipher = getattr(CipherSuite, arg)
                except AttributeError:
                    cipher = int(arg)
        elif opt == '-n':
            num_limit = int(arg)
        elif opt == '-l':
            level = int(arg)
        elif opt == "-i":
            timing = True
            interface = arg
        elif opt == '-o':
            outdir = arg
        elif opt == "--payload-len":
            ciphertext_len = int(arg)
        elif opt == "--repeat":
            repetitions = int(arg)
        elif opt == '--help':
            help_msg()
            sys.exit(0)
        elif opt == '--quick':
            quick = True
        elif opt == '--cpu-list':
            affinity = arg
        else:
            raise ValueError("Unknown option: {0}".format(opt))

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

    mac_sizes = {
        "md5": 16,
        "sha": 20,
        "sha256": 32,
        "sha384": 48,
    }

    if CipherSuite.canonicalMacName(cipher) not in mac_sizes:
        print("Unsupported MAC, exiting")
        exit(1)

    # group conversations that should have the same timing signature
    if quick:
        groups = {"quick - wrong MAC": {}}
    else:
        groups = {"wrong padding and MAC": {}, "MAC out of bounds": {}}

    dhe = cipher in CipherSuite.ecdhAllSuites
    ext = {}
    ext[ExtensionType.signature_algorithms] = \
        SignatureAlgorithmsExtension().create(SIG_ALL)
    ext[ExtensionType.signature_algorithms_cert] = \
        SignatureAlgorithmsCertExtension().create(SIG_ALL)
    if dhe:
        sup_groups = [GroupName.secp256r1, GroupName.ffdhe2048]
        ext[ExtensionType.supported_groups] = SupportedGroupsExtension() \
            .create(sup_groups)

    # first run sanity test and verify that server supports this ciphersuite
    conversation = Connect(host, port)
    node = conversation
    ciphers = [cipher]
    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(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(AlertLevel.warning, AlertDescription.close_notify))
    node.next_sibling = ExpectClose()
    node = node.add_child(ExpectClose())
    for group_name in groups:
        groups[group_name]["sanity"] = conversation
    runner = Runner(conversation)
    try:
        runner.run()
    except Exception as exp:
        # Exception means the server rejected the ciphersuite
        print("Failing on {0} because server does not support it. ".format(
            CipherSuite.ietfNames[cipher]))
        print(20 * '=')
        exit(1)

    # assume block length of 16 if not 3des
    block_len = 8 if CipherSuite.canonicalCipherName(cipher) == "3des" else 16
    mac_len = mac_sizes[CipherSuite.canonicalMacName(cipher)]
    invert_mac = {}
    for index in range(0, mac_len):
        invert_mac[index] = 0xff

    if quick:
        # iterate over min/max padding and first/last byte MAC error
        for pad_len, error_pos in product([1, 256], [0, -1]):
            payload_len = ciphertext_len - mac_len - pad_len - block_len

            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher]
            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_padding(fuzz_mac(ApplicationDataGenerator(
                    bytearray(payload_len)),
                                      xors={error_pos: 0xff}),
                             min_length=pad_len))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
            node = node.add_child(ExpectClose())
            groups["quick - wrong MAC"][
                "wrong MAC at pos {0}, padding length {1}".format(
                    error_pos, pad_len)] = conversation

        # iterate over min/max padding and first/last byte of padding error
        for pad_len, error_pos in product([256], [0, 254]):
            payload_len = ciphertext_len - mac_len - pad_len - block_len

            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher]
            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_padding(ApplicationDataGenerator(bytearray(payload_len)),
                             min_length=pad_len,
                             xors={(error_pos): 0xff}))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
            node = node.add_child(ExpectClose())
            groups["quick - wrong MAC"][
                "wrong pad at pos {0}, padding length {1}".format(
                    error_pos, pad_len)] = conversation

    else:
        # iterate over: padding length with incorrect MAC
        #               invalid padding length with correct MAC
        for pad_len in range(1, 257):

            # ciphertext 1 has 512 bytes, calculate payload size
            payload_len = ciphertext_len - mac_len - pad_len - block_len

            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher]
            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_padding(fuzz_mac(ApplicationDataGenerator(
                    bytearray(payload_len)),
                                      xors=invert_mac),
                             min_length=pad_len))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
            node = node.add_child(ExpectClose())
            groups["wrong padding and MAC"][
                "wrong MAC, padding length {0}".format(pad_len)] = conversation

            # incorrect padding of length 255 (256 with the length byte)
            # with error byte iterated over the length of padding

            # avoid changing the padding length byte
            if pad_len < 256:
                payload_len = ciphertext_len - mac_len - 256 - block_len

                conversation = Connect(host, port)
                node = conversation
                ciphers = [cipher]
                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_padding(ApplicationDataGenerator(
                        bytearray(payload_len)),
                                 min_length=256,
                                 xors={(pad_len - 1): 0xff}))
                node = node.add_child(
                    ExpectAlert(AlertLevel.fatal,
                                AlertDescription.bad_record_mac))
                node = node.add_child(ExpectClose())
                groups["wrong padding and MAC"][
                    "padding length 255 (256 with the length byte), padding error at position {0}"
                    .format(pad_len - 1)] = conversation

            # ciphertext 2 has 128 bytes and broken padding to make server check mac "before" the plaintext
            padding = 128 - 1 - block_len - mac_len
            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher]
            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_padding(ApplicationDataGenerator(bytearray(1)),
                             substitutions={
                                 -1: pad_len - 1,
                                 0: 0
                             },
                             min_length=padding))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
            node = node.add_child(ExpectClose())
            groups["MAC out of bounds"]["padding length byte={0}".format(
                pad_len - 1)] = conversation

        # iterate over MAC length and fuzz byte by byte
        payload_len = ciphertext_len - mac_len - block_len - 256
        for mac_index in range(0, mac_len):
            conversation = Connect(host, port)
            node = conversation
            ciphers = [cipher]
            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_padding(fuzz_mac(ApplicationDataGenerator(
                    bytearray(payload_len)),
                                      xors={mac_index: 0xff}),
                             min_length=256))
            node = node.add_child(
                ExpectAlert(AlertLevel.fatal, AlertDescription.bad_record_mac))
            node = node.add_child(ExpectClose())
            groups["wrong padding and MAC"][
                "padding length 255 (256 with the length byte), incorrect MAC at pos {0}"
                .format(mac_index)] = conversation

    for group_name, conversations in groups.items():

        # 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)
        if not sampled_tests:
            continue

        print("Running tests for {0}".format(CipherSuite.ietfNames[cipher]))

        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("Lucky 13 attack check for {0} {1}".format(
            group_name, CipherSuite.ietfNames[cipher]))

        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 or xpass:
            sys.exit(1)
        elif timing:
            # if regular tests passed, run timing collection and analysis
            if TimingRunner.check_tcpdump():
                timing_runner = TimingRunner(
                    "{0}_v{1}_{2}_{3}".format(sys.argv[0], version, group_name,
                                              CipherSuite.ietfNames[cipher]),
                    sampled_tests, outdir, host, port, interface, affinity)
                print("Running timing tests...")
                timing_runner.generate_log(run_only, run_exclude, repetitions)
                ret_val = timing_runner.run()
                print("Statistical analysis exited with {0}".format(ret_val))
            else:
                print(
                    "Could not run timing tests because tcpdump is not present!"
                )
                sys.exit(1)
            print(20 * '=')
 def test_getTLS13Suites_with_TLS1_2(self):
     hs = HandshakeSettings()
     hs.maxVersion = (3, 4)
     self.assertEqual(CipherSuite.getTLS13Suites(hs, (3, 3)),
                      [])
Example #17
0
def conv_generator(conf, host, port, sni_hostname, cert=None, key=None):
    """Generate a conversation based on dict with configuration."""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    node = node.add_child(ChangeCipherSpecGenerator())
    node = node.add_child(FinishedGenerator())
    node = node.add_child(ExpectChangeCipherSpec())
    node = node.add_child(ExpectFinished())
    if conf['Disconnect'] == "true":
        node = node.add_child(
            AlertGenerator(AlertLevel.warning, AlertDescription.close_notify))
        node = node.add_child(
            ExpectAlert(AlertLevel.warning, AlertDescription.close_notify))
        node.next_sibling = ExpectClose()
        node = node.add_child(node.next_sibling)
        node = node.add_child(Connect(host, port))
        node = node.add_child(ResetRenegotiationInfo())
    node = node.add_child(ResetHandshakeHashes())
    hs = HandshakeSettings()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return root
Example #18
0
 def test_getTLS13Suites_with_TLS1_2(self):
     hs = HandshakeSettings()
     hs.maxVersion = (3, 4)
     self.assertEqual(CipherSuite.getTLS13Suites(hs, (3, 3)),
                      [])