def test_hostname_alone(self): self.assertTrue(is_valid_hostname(b'localhost'))
def test_ip_lookalike_hostname(self): self.assertTrue(is_valid_hostname(b'192.168.example.com'))
def test_with_tld_dot(self): self.assertTrue(is_valid_hostname(b'example.com.'))
def main(): """Check if server is not vulnerable to Bleichenbacher attack""" host = "localhost" port = 4433 num_limit = 50 run_exclude = set() expected_failures = {} last_exp_tmp = None timeout = 1.0 alert = AlertDescription.bad_record_mac level = AlertLevel.fatal srv_extensions = {ExtensionType.renegotiation_info: None} no_sni = False repetitions = 100 interface = None timing = False outdir = "/tmp" cipher = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:x:X:t:n:a:l:l:o:i:C:", ["help", "no-safe-renego", "no-sni", "repeat="]) 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 == '-C': if arg[:2] == '0x': cipher = int(arg, 16) else: try: cipher = getattr(CipherSuite, arg) except AttributeError: cipher = int(arg) elif opt == '-t': timeout = float(arg) elif opt == '-a': alert = int(arg) elif opt == '-l': level = int(arg) elif opt == "-i": timing = True interface = arg elif opt == '-o': outdir = arg elif opt == "--repeat": repetitions = int(arg) elif opt == "--no-safe-renego": srv_extensions = None elif opt == "--no-sni": no_sni = 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 cln_extensions = {ExtensionType.renegotiation_info: None} if is_valid_hostname(host) and not no_sni: cln_extensions[ExtensionType.server_name] = \ SNIExtension().create(bytearray(host, 'ascii')) # RSA key exchange check if cipher not in CipherSuite.certSuites: print("Ciphersuite has to use RSA key exchange.") exit(1) conversations = {} conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child( AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() node = node.add_child(ExpectClose()) conversations["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) # check if a certain number doesn't trip up the server # (essentially a second sanity test) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={2: 1})) 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() node = node.add_child(ExpectClose()) conversations[ "sanity - static non-zero byte in random padding"] = conversation # set 2nd byte of padding to 3 (invalid value) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={1: 3})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["set PKCS#1 padding type to 3"] = conversation # set 2nd byte of padding to 1 (signing) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={1: 1})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["set PKCS#1 padding type to 1"] = conversation # test early zero in random data conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={4: 0})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in random padding"] = conversation # check if early padding separator is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-2: 0})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in last byte of random padding"] = conversation # check if separator without any random padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={2: 0})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in first byte of random padding"] = conversation # check if invalid first byte of encoded value is correctly detecte conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={0: 1})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid version number in padding"] = conversation # check if no null separator in padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-1: 1})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["no null separator in padding"] = conversation # check if no null separator in padding is detected # but with PMS set to non-zero conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child( ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1] * 48))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["no null separator in encrypted value"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child( ClientKeyExchangeGenerator(premaster_secret=bytearray([1, 1]))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["two byte long PMS (TLS version only)"] = conversation # check if no encrypted payload is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # the TLS version is always set, so we mask the real padding separator # and set the last byte of PMS to 0 node = node.add_child( ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1, 1, 0]))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["no encrypted value"] = conversation # check if too short encrypted payload is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # the TLS version is always set, so we mask the real padding separator # and set the last byte of PMS to 0 node = node.add_child( ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1, 1, 0, 3]))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["one byte encrypted value"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child( ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 47))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["too short (47-byte) pre master secret"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child( ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 4))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["very short (4-byte) pre master secret"] = conversation # check if too long PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child( ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 49))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["too long (49-byte) pre master secret"] = conversation # check if wrong TLS version number is rejected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(client_version=(2, 2))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations[ "wrong TLS version (2, 2) in pre master secret"] = conversation # check if wrong TLS version number is rejected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(client_version=(0, 0))) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations[ "wrong TLS version (0, 0) in pre master secret"] = conversation # check if too short PKCS padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # move the start of the padding forward, essentially encrypting two 0 bytes # at the beginning of the padding, but since those are transformed into a number # their existence is lost and it just like the padding was too small node = node.add_child(ClientKeyExchangeGenerator(padding_subs={ 1: 0, 2: 2 })) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["too short PKCS padding"] = conversation # check if too long PKCS padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # move the start of the padding backward, essentially encrypting no 0 bytes # at the beginning of the padding, but since those are transformed into a number # its lack is lost and it just like the padding was too big node = node.add_child(ClientKeyExchangeGenerator(padding_subs={0: 2})) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["too long PKCS padding"] = conversation # fuzz MAC in the Finshed message to make decryption fail conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(fuzz_mac(FinishedGenerator(), xors={0: 0xff})) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid MAC in Finished on pos 0"] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child( ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(fuzz_mac(FinishedGenerator(), xors={-1: 0xff})) node = node.add_child( ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid MAC in Finished on pos -1"] = 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) 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("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) elif timing: # if regular tests passed, run timing collection and analysis if TimingRunner.check_tcpdump(): timing_runner = TimingRunner( "{0}_{1}".format(sys.argv[0], CipherSuite.ietfNames[cipher]), sampled_tests, outdir, host, port, interface) 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_ip_dot(self): self.assertFalse(is_valid_hostname(b'192.168.0.1.'))
def main(): """Check if server is not vulnerable to Bleichenbacher attack""" host = "localhost" port = 4433 num_limit = None run_exclude = set() expected_failures = {} last_exp_tmp = None timeout = 1.0 alert = AlertDescription.bad_record_mac level = AlertLevel.fatal srv_extensions = {ExtensionType.renegotiation_info: None} no_sni = False repetitions = 100 interface = None timing = False outdir = "/tmp" cipher = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA affinity = None argv = sys.argv[1:] opts, args = getopt.getopt(argv, "h:p:e:x:X:t:n:a:l:l:o:i:C:", ["help", "no-safe-renego", "no-sni", "repeat=", "cpu-list="]) 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 == '-C': if arg[:2] == '0x': cipher = int(arg, 16) else: try: cipher = getattr(CipherSuite, arg) except AttributeError: cipher = int(arg) elif opt == '-t': timeout = float(arg) elif opt == '-a': alert = int(arg) elif opt == '-l': level = int(arg) elif opt == "-i": timing = True interface = arg elif opt == '-o': outdir = arg elif opt == "--repeat": repetitions = int(arg) elif opt == "--no-safe-renego": srv_extensions = None elif opt == "--no-sni": no_sni = True elif opt == "--cpu-list": affinity = 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 cln_extensions = {ExtensionType.renegotiation_info: None} if is_valid_hostname(host) and not no_sni: cln_extensions[ExtensionType.server_name] = \ SNIExtension().create(bytearray(host, 'ascii')) # RSA key exchange check if cipher not in CipherSuite.certSuites: print("Ciphersuite has to use RSA key exchange.") exit(1) conversations = OrderedDict() conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(FinishedGenerator()) node = node.add_child(ExpectChangeCipherSpec()) node = node.add_child(ExpectFinished()) node = node.add_child(ApplicationDataGenerator(bytearray(b"GET / HTTP/1.0\r\n\r\n"))) node = node.add_child(ExpectApplicationData()) node = node.add_child(AlertGenerator(AlertLevel.warning, AlertDescription.close_notify)) node = node.add_child(ExpectAlert()) node.next_sibling = ExpectClose() node = node.add_child(ExpectClose()) conversations["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) # check if a certain number doesn't trip up the server # (essentially a second sanity test) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={2: 1})) 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() node = node.add_child(ExpectClose()) conversations["sanity - static non-zero byte in random padding"] = conversation # first put tests that are the benchmark to be compared to # fuzz MAC in the Finshed message to make decryption fail conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(fuzz_mac(FinishedGenerator(), xors={0:0xff})) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid MAC in Finished on pos 0"] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(fuzz_mac(FinishedGenerator(), xors={-1:0xff})) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid MAC in Finished on pos -1"] = conversation # and for good measure, add something that sends invalid padding conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator()) node = node.add_child(ChangeCipherSpecGenerator()) node = node.add_child(fuzz_padding(FinishedGenerator(), xors={-1:0xff, -2:0x01})) node = node.add_child(TCPBufferingDisable()) node = node.add_child(TCPBufferingFlush()) node = node.add_child(ExpectAlert(level, alert)) node.add_child(ExpectClose()) conversations["invalid padding_length in Finished"] = conversation # create a CKE with PMS the runner doesn't know/use conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # use too short PMS but then change padding so that the PMS is # correct length with correct TLS version but the encryption keys # that tlsfuzzer calculates will be incorrect node = node.add_child(ClientKeyExchangeGenerator( padding_subs={-3: 0, -2: 3, -1: 3}, premaster_secret=bytearray([1] * 46))) 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(level, alert)) node.add_child(ExpectClose()) conversations["fuzzed pre master secret"] = conversation # set 2nd byte of padding to 3 (invalid value) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={1: 3})) 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(level, alert)) node.add_child(ExpectClose()) conversations["set PKCS#1 padding type to 3"] = conversation # set 2nd byte of padding to 1 (signing) conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={1: 1})) 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(level, alert)) node.add_child(ExpectClose()) conversations["set PKCS#1 padding type to 1"] = conversation # test early zero in random data conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={4: 0})) 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(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in random padding"] = conversation # check if early padding separator is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-2: 0})) 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(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in last byte of random padding"] = conversation # check if separator without any random padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={2: 0})) 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(level, alert)) node.add_child(ExpectClose()) conversations["zero byte in first byte of random padding"] = conversation # check if invalid first byte of encoded value is correctly detecte conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={0: 1})) 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(level, alert)) node.add_child(ExpectClose()) conversations["invalid version number in padding"] = conversation # check if no null separator in padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-1: 1})) 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(level, alert)) node.add_child(ExpectClose()) conversations["no null separator in padding"] = conversation # check if no null separator in padding is detected # but with PMS set to non-zero conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1] * 48))) 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(level, alert)) node.add_child(ExpectClose()) conversations["no null separator in encrypted value"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1, 1]))) 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(level, alert)) node.add_child(ExpectClose()) conversations["two byte long PMS (TLS version only)"] = conversation # check if no encrypted payload is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # the TLS version is always set, so we mask the real padding separator # and set the last byte of PMS to 0 node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1, 1, 0]))) 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(level, alert)) node.add_child(ExpectClose()) conversations["no encrypted value"] = conversation # check if too short encrypted payload is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # the TLS version is always set, so we mask the real padding separator # and set the last byte of PMS to 0 node = node.add_child(ClientKeyExchangeGenerator(padding_subs={-1: 1}, premaster_secret=bytearray([1, 1, 0, 3]))) 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(level, alert)) node.add_child(ExpectClose()) conversations["one byte encrypted value"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 47))) 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(level, alert)) node.add_child(ExpectClose()) conversations["too short (47-byte) pre master secret"] = conversation # check if too short PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 4))) 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(level, alert)) node.add_child(ExpectClose()) conversations["very short (4-byte) pre master secret"] = conversation # check if too long PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 49))) 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(level, alert)) node.add_child(ExpectClose()) conversations["too long (49-byte) pre master secret"] = conversation # check if very long PMS is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 124))) 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(level, alert)) node.add_child(ExpectClose()) conversations["very long (124-byte) pre master secret"] = conversation conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(premaster_secret=bytearray([1] * 96))) 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(level, alert)) node.add_child(ExpectClose()) conversations["very long (96-byte) pre master secret"] = conversation # check if wrong TLS version number is rejected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(client_version=(2, 2))) 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(level, alert)) node.add_child(ExpectClose()) conversations["wrong TLS version (2, 2) in pre master secret"] = conversation # check if wrong TLS version number is rejected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) node = node.add_child(ClientKeyExchangeGenerator(client_version=(0, 0))) 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(level, alert)) node.add_child(ExpectClose()) conversations["wrong TLS version (0, 0) in pre master secret"] = conversation # check if too short PKCS padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # move the start of the padding forward, essentially encrypting two 0 bytes # at the beginning of the padding, but since those are transformed into a number # their existence is lost and it just like the padding was too small node = node.add_child(ClientKeyExchangeGenerator(padding_subs={1: 0, 2: 2})) 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(level, alert)) node.add_child(ExpectClose()) conversations["too short PKCS padding"] = conversation # check if very short PKCS padding doesn't have a different behaviour conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # move the start of the padding 40 bytes towards LSB subs = {} for i in range(41): subs[i] = 0 subs[41] = 2 node = node.add_child(ClientKeyExchangeGenerator(padding_subs=subs)) 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(level, alert)) node.add_child(ExpectClose()) conversations["very short PKCS padding (40 bytes short)"] = conversation # check if too long PKCS padding is detected conversation = Connect(host, port) node = conversation ciphers = [cipher] node = node.add_child(ClientHelloGenerator(ciphers, extensions=cln_extensions)) node = node.add_child(ExpectServerHello(extensions=srv_extensions)) node = node.add_child(ExpectCertificate()) node = node.add_child(ExpectServerHelloDone()) node = node.add_child(TCPBufferingEnable()) # move the start of the padding backward, essentially encrypting no 0 bytes # at the beginning of the padding, but since those are transformed into a number # its lack is lost and it just like the padding was too big node = node.add_child(ClientKeyExchangeGenerator(padding_subs={0: 2})) 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(level, alert)) node.add_child(ExpectClose()) conversations["too long PKCS padding"] = 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] if num_limit < len(conversations): sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests))) else: sampled_tests = regular_tests ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests) 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("Test end") print(20 * '=') print("""Tests for handling of malformed encrypted values in CKE This test script checks if the server correctly handles malformed Client Key Exchange messages in RSA key exchange. When executed with `-i` it will also verify that different errors are rejected in the same amount of time; it checks for timing sidechannel. The script executes tests without \"sanity\" in name multiple times to estimate server response time. Quick reminder: when encrypting a value using PKCS#1 v1.5 standard the plaintext has the following structure, starting from most significant byte: - one byte, the version of the encryption, must be 0 - one byte, the type of encryption, must be 2 (is 1 in case of signature) - one or more bytes of random padding, with no zero bytes. The count must equal the byte size of the public key modulus less size of encrypted value and 3 (for version, type and separator) For signatures the bytes must equal 0xff - one zero byte that acts as separator between padding and encrypted value - one or more bytes that are the encrypted value, for TLS it must be 48 bytes long and the first two bytes need to equal the TLS version advertised in Client Hello All tests should exhibit the same kind of timing behaviour, but if some groups of tests are inconsistent, that points to likely place where the timing leak happens: - the control group, lack of consistency here points to Lucky 13: - 'invalid MAC in Finished on pos 0' - 'invalid MAC in Finished on pos -1' - 'fuzzed pre master secret' - this will end up with random plaintexts in record with Finished, most resembling a randomly selected PMS by the server verification: - 'set PKCS#1 padding type to 3' - 'set PKCS#1 padding type to 1' - incorrect size of encrypted value (pre-master secret), inconsistent results here suggests that the decryption leaks length of plaintext: - 'zero byte in random padding' - this creates a PMS that's 4 bytes shorter than the public key size and has a random TLS version - 'zero byte in last byte of random padding' - this creates a PMS that's one byte too long (49 bytes long), with a TLS version that's (0, major_version) - 'no null separator in padding' - as the PMS is all zero, this effectively sends a PMS that's 45 bytes long with TLS version of (0, 0) - 'two byte long PMS (TLS version only)' - 'one byte encrypted value' - the encrypted value is 3, as a correct value for first byte of TLS version - 'too short (47-byte) pre master secret' - 'very short (4-byte) pre master secret' - 'too long (49-byte) pre master secret' - 'very long (124-byte) pre master secret' - 'very long (96-byte) pre master secret' - invalid PKCS#1 v1.5 encryption padding: - 'zero byte in first byte of random padding' - this is a mix of too long PMS and invalid padding, it actually doesn't send padding at all, the padding length is zero - 'invalid version number in padding' - this sets the first byte of plaintext to 1 - 'no null separator in encrypted value' - this doesn't send a null byte separating padding and encrypted value - 'no encrypted value' - this sends a null separator, but it's the last byte of plaintext - 'too short PKCS padding' - this sends the correct encryption type in the padding (2), but one byte later than required - 'very short PKCS padding (40 bytes short)' - same as above only 40 bytes later - 'too long PKCS padding' this doesn't send the PKCS#1 v1.5 version at all, but starts with padding type - invalid TLS version in PMS, differences here suggest a leak in code checking for correctness of this value: - 'wrong TLS version (2, 2) in pre master secret' - 'wrong TLS version (0, 0) in pre master secret'""") 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) elif timing: # if regular tests passed, run timing collection and analysis if TimingRunner.check_tcpdump(): timing_runner = TimingRunner("{0}_{1}".format(sys.argv[0], 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() if ret_val == 0: print("No statistically significant difference detected") elif ret_val == 1: print("Statisticaly significant difference detected at alpha=" "0.05") else: 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 __init__(self, username=None, password=None, certChain=None, privateKey=None, use_fido2=False, domain_name=None, checker=None, settings=None, anon=False, host=None): """ For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) - use_fido2, domain_name (and user name) (FIDO2) For server authentication, you can either rely on the implicit mutual authentication performed by SRP, FIDO2, or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The constructor does not perform the TLS handshake itself, but simply stores these arguments for later. The handshake is performed only when this class needs to connect with the server. Then you should be prepared to handle TLS-specific exceptions. See the client handshake functions in :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which exceptions might be raised. :param str username: SRP or FIDO2 username. Requires the 'password' argument for SRP. :param str password: SRP password for mutual authentication. Requires the 'username' argument. :param X509CertChain certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. :param RSAKey privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. :param bool use_fido2: Indication whether or not to use FIDO2 authentication. Requires the 'domain_name' parameter or 'host' as domain name. :param str domain_name: The domain name of the server to authenticate against using FIDO2. May be omitted if host is given as a domain name. :param Checker checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. :type settings: HandshakeSettings :param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. :param bool anon: set to True if the negotiation should advertise only anonymous TLS ciphersuites. Mutually exclusive with client certificate authentication or SRP authentication :type host: str or None :param host: the hostname that the connection is made to. Can be an IP address (in which case the SNI extension won't be sent). Can include the port (in which case the port will be stripped and ignored). """ self.username = None self.password = None self.use_fido2 = False self.domain_name = None self.certChain = None self.privateKey = None self.checker = None self.anon = anon if host is not None and not self._isIP(host): # name for SNI so port can't be sent colon = host.find(':') if colon > 0: host = host[:colon] self.serverName = host if host and not is_valid_hostname(host): raise ValueError("Invalid hostname: {0}".format(host)) else: self.serverName = None domain_name = domain_name or self.serverName #SRP Authentication if username and password and not \ (certChain or privateKey or use_fido2 or domain_name): self.username = username self.password = password #Certificate Chain Authentication elif certChain and privateKey and not \ (username or password or use_fido2 or domain_name): self.certChain = certChain self.privateKey = privateKey # FIDO2 authentication elif use_fido2 and domain_name and not \ (password or certChain or privateKey): self.use_fido2 = True self.domain_name = domain_name self.username = username #No Authentication elif not password and not username and not \ certChain and not privateKey and not \ use_fido2 and not (domain_name and not host): pass else: raise ValueError("Bad parameters") self.checker = checker self.settings = settings self.tlsSession = None
def test_with_tld_dot(self): self.assertTrue(is_valid_hostname(b'example.com.'))
def test_hostname_alone(self): self.assertTrue(is_valid_hostname(b'localhost'))
def test_ip_dot(self): self.assertFalse(is_valid_hostname(b'192.168.0.1.'))
def test_ip_lookalike_hostname(self): self.assertTrue(is_valid_hostname(b'192.168.example.com'))
def test_example(self): self.assertTrue(is_valid_hostname(b'example.com'))
def serverCmd(argv): (address, privateKey, cert_chain, tacks, verifierDB, directory, reqCert, expLabel, expLength, dhparam, psk, psk_ident, psk_hash, ssl3, max_ver, tickets) = \ handleArgs(argv, "kctbvdlL", ["reqcert", "param=", "psk=", "psk-ident=", "psk-sha384", "ssl3", "max-ver=", "tickets="]) if (cert_chain and not privateKey) or (not cert_chain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if tacks and not cert_chain: raise SyntaxError("Must specify CERT with Tacks") print("I am an HTTPS test server, I will listen on %s:%d" % (address[0], address[1])) if directory: os.chdir(directory) print("Serving files from %s" % os.getcwd()) if cert_chain and privateKey: print("Using certificate and private key...") if verifierDB: print("Using verifier DB...") if tacks: print("Using Tacks...") if reqCert: print("Asking for client certificates...") ############# sessionCache = SessionCache() username = None sni = None if is_valid_hostname(address[0]): sni = address[0] settings = HandshakeSettings() settings.useExperimentalTackExtension=True settings.dhParams = dhparam if tickets is not None: settings.ticket_count = tickets if psk: settings.pskConfigs = [(psk_ident, psk, psk_hash)] settings.ticketKeys = [getRandomBytes(32)] if ssl3: settings.minVersion = (3, 0) if max_ver: settings.maxVersion = max_ver class MySimpleHTTPHandler(SimpleHTTPRequestHandler): """Buffer the header and body of HTTP message.""" wbufsize = -1 class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, connection): print("About to handshake...") activationFlags = 0 if tacks: if len(tacks) == 1: activationFlags = 1 elif len(tacks) == 2: activationFlags = 3 try: start = time_stamp() connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) connection.handshakeServer(certChain=cert_chain, privateKey=privateKey, verifierDB=verifierDB, tacks=tacks, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=[b"http/1.1"], alpn=[bytearray(b'http/1.1')], reqCert=reqCert, sni=sni) # As an example (does not work here): #nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) stop = time_stamp() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop-start) printExporter(connection, expLabel, expLength) return True httpd = MyHTTPServer(address, MySimpleHTTPHandler) httpd.serve_forever()
def serverCmd(argv): (address, privateKey, cert_chain, virtual_hosts, tacks, verifierDB, directory, reqCert, expLabel, expLength, dhparam, psk, psk_ident, psk_hash, ssl3, max_ver, tickets, cipherlist, request_pha, require_pha) = \ handleArgs(argv, "kctbvdlL", ["reqcert", "param=", "psk=", "psk-ident=", "psk-sha384", "ssl3", "max-ver=", "tickets=", "cipherlist=", "request-pha", "require-pha"]) if (cert_chain and not privateKey) or (not cert_chain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if tacks and not cert_chain: raise SyntaxError("Must specify CERT with Tacks") print("I am an HTTPS test server, I will listen on %s:%d" % (address[0], address[1])) if directory: os.chdir(directory) print("Serving files from %s" % os.getcwd()) if cert_chain and privateKey: print("Using certificate and private key...") if verifierDB: print("Using verifier DB...") if tacks: print("Using Tacks...") if reqCert: print("Asking for client certificates...") ############# sessionCache = SessionCache() username = None sni = None if is_valid_hostname(address[0]): sni = address[0] settings = HandshakeSettings() settings.useExperimentalTackExtension = True settings.dhParams = dhparam if tickets is not None: settings.ticket_count = tickets if psk: settings.pskConfigs = [(psk_ident, psk, psk_hash)] settings.ticketKeys = [getRandomBytes(32)] if ssl3: settings.minVersion = (3, 0) if max_ver: settings.maxVersion = max_ver settings.virtual_hosts = virtual_hosts if cipherlist: settings.cipherNames = [ item for cipher in cipherlist for item in cipher.split(',') ] class MySimpleHTTPHandler(SimpleHTTPRequestHandler, object): """Buffer the header and body of HTTP message.""" wbufsize = -1 def do_GET(self): """Simple override to send KeyUpdate to client.""" if self.path.startswith('/keyupdate'): for i in self.connection.send_keyupdate_request( KeyUpdateMessageType.update_requested): if i in (0, 1): continue else: raise ValueError("Invalid return from " "send_keyupdate_request") if self.path.startswith('/secret') and not request_pha: try: for i in self.connection.request_post_handshake_auth(): pass except ValueError: self.wfile.write(b'HTTP/1.0 401 Certificate authentication' b' required\r\n') self.wfile.write(b'Connection: close\r\n') self.wfile.write(b'Content-Length: 0\r\n\r\n') return self.connection.read(0, 0) if self.connection.session.clientCertChain: print(" Got client certificate in post-handshake auth: " "{0}".format(self.connection.session.clientCertChain. getFingerprint())) else: print(" No certificate from client received") self.wfile.write(b'HTTP/1.0 401 Certificate authentication' b' required\r\n') self.wfile.write(b'Connection: close\r\n') self.wfile.write(b'Content-Length: 0\r\n\r\n') return return super(MySimpleHTTPHandler, self).do_GET() class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, connection): print("About to handshake...") activationFlags = 0 if tacks: if len(tacks) == 1: activationFlags = 1 elif len(tacks) == 2: activationFlags = 3 try: start = time_stamp() connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) connection.client_cert_required = require_pha connection.handshakeServer(certChain=cert_chain, privateKey=privateKey, verifierDB=verifierDB, tacks=tacks, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=[b"http/1.1"], alpn=[bytearray(b'http/1.1')], reqCert=reqCert, sni=sni) # As an example (does not work here): #nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) try: if request_pha: for i in connection.request_post_handshake_auth(): pass except ValueError: # if we can't do PHA, we can't do it pass stop = time_stamp() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop - start) printExporter(connection, expLabel, expLength) return True httpd = MyHTTPServer(address, MySimpleHTTPHandler) httpd.serve_forever()
def test_very_long_host(self): self.assertFalse(is_valid_hostname(b'a' * 70 + b'.example.com'))
def test_very_long_host(self): self.assertFalse(is_valid_hostname(b'a' * 70 + b'.example.com'))
def test_long_hostname(self): self.assertTrue(is_valid_hostname(b'a' * 60 + b'.example.com'))
def test_long_hostname(self): self.assertTrue(is_valid_hostname(b'a' * 60 + b'.example.com'))
def serverCmd(argv): (address, privateKey, cert_chain, tacks, verifierDB, directory, reqCert, expLabel, expLength, dhparam, psk, psk_ident, psk_hash, ssl3) = \ handleArgs(argv, "kctbvdlL", ["reqcert", "param=", "psk=", "psk-ident=", "psk-sha384", "ssl3"]) if (cert_chain and not privateKey) or (not cert_chain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if tacks and not cert_chain: raise SyntaxError("Must specify CERT with Tacks") print("I am an HTTPS test server, I will listen on %s:%d" % (address[0], address[1])) if directory: os.chdir(directory) print("Serving files from %s" % os.getcwd()) if cert_chain and privateKey: print("Using certificate and private key...") if verifierDB: print("Using verifier DB...") if tacks: print("Using Tacks...") if reqCert: print("Asking for client certificates...") ############# sessionCache = SessionCache() username = None sni = None if is_valid_hostname(address[0]): sni = address[0] settings = HandshakeSettings() settings.useExperimentalTackExtension = True settings.dhParams = dhparam if psk: settings.pskConfigs = [(psk_ident, psk, psk_hash)] settings.ticketKeys = [getRandomBytes(32)] if ssl3: settings.minVersion = (3, 0) class MySimpleHTTPHandler(SimpleHTTPRequestHandler): """Buffer the header and body of HTTP message.""" wbufsize = -1 class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, connection): print("About to handshake...") activationFlags = 0 if tacks: if len(tacks) == 1: activationFlags = 1 elif len(tacks) == 2: activationFlags = 3 try: start = time.clock() connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) connection.handshakeServer(certChain=cert_chain, privateKey=privateKey, verifierDB=verifierDB, tacks=tacks, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=[b"http/1.1"], alpn=[bytearray(b'http/1.1')], reqCert=reqCert, sni=sni) # As an example (does not work here): #nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) stop = time.clock() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop - start) printExporter(connection, expLabel, expLength) return True httpd = MyHTTPServer(address, MySimpleHTTPHandler) httpd.serve_forever()
def test_example(self): self.assertTrue(is_valid_hostname(b'example.com'))
def serverCmd(argv): (address, privateKey, certChain, tacks, verifierDB, directory, reqCert, expLabel, expLength, dhparam) = handleArgs(argv, "kctbvdlL", ["reqcert", "param="]) if (certChain and not privateKey) or (not certChain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if tacks and not certChain: raise SyntaxError("Must specify CERT with Tacks") print("I am an HTTPS test server, I will listen on %s:%d" % (address[0], address[1])) if directory: os.chdir(directory) print("Serving files from %s" % os.getcwd()) if certChain and privateKey: print("Using certificate and private key...") if verifierDB: print("Using verifier DB...") if tacks: print("Using Tacks...") if reqCert: print("Asking for client certificates...") ############# sessionCache = SessionCache() username = None sni = None if is_valid_hostname(address[0]): sni = address[0] class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, connection): print("About to handshake...") activationFlags = 0 if tacks: if len(tacks) == 1: activationFlags = 1 elif len(tacks) == 2: activationFlags = 3 try: start = time.clock() settings = HandshakeSettings() settings.useExperimentalTackExtension = True settings.dhParams = dhparam connection.handshakeServer(certChain=certChain, privateKey=privateKey, verifierDB=verifierDB, tacks=tacks, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=[b"http/1.1"], alpn=[bytearray(b'http/1.1')], reqCert=reqCert, sni=sni) # As an example (does not work here): #nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) stop = time.clock() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop - start) printExporter(connection, expLabel, expLength) return True httpd = MyHTTPServer(address, SimpleHTTPRequestHandler) httpd.serve_forever()
def serverCmd(address,privateKey_, certChain_, hv=None): # key s = open(privateKey_, "r").read() # OpenSSL/m2crypto does not support RSASSA-PSS certificates privateKey = parsePEMKey(s, private=True, implementations=["python"]) # cert s = open(certChain_, "r").read() x509 = X509() x509.parse(s) certChain = X509CertChain([x509]) ############# sessionCache = SessionCache() username = None sni = None if is_valid_hostname(address[0]): sni = address[0] class SimpleTCPRequestHandler(BaseRequestHandler): def handle(self): print("In request handler") rq = self.request.recv(100) print('Received request is: '+rq) print('Echoing...') self.request.sendall(rq+rq) print('Echoing completed') class XRERequestHandler(BaseRequestHandler): def handle(self): print("In XRE request handler") xre_server_session(self.request) class MyTLSServer(ThreadingMixIn, TLSSocketServerMixIn, SocketServer.TCPServer): # customize ciphersuites and crypto back-end # from tlslite import handshakesettings # handshakesettings.CIPHER_NAMES = ["aes128gcm"] # handshakesettings.CIPHER_IMPLEMENTATIONS = ["openssl","pycrypto"] def handshake(self, connection): print("About to handshake...") activationFlags = 0 try: start = time.clock() settings = HandshakeSettings() settings.useExperimentalTackExtension=True settings.dhParams = None connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY,1) connection.handshakeServer(certChain=certChain, privateKey=privateKey, verifierDB=None, tacks=None, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=None, alpn=None, reqCert=False, sni=sni) stop = time.clock() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop-start, hv) return True # tlsd = MyTLSServer(address,SimpleTCPRequestHandler) tlsd = MyTLSServer(address,XRERequestHandler) tlsd.serve_forever()