diff options
author | Jack Lloyd <[email protected]> | 2018-03-10 17:26:04 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-03-10 17:27:36 -0500 |
commit | 7f6b26e934b9a6015d9bf1fa236d8409ae59cf9f (patch) | |
tree | de681b005648cc376a225eb27e04eae4dc622b95 | |
parent | e742386cb340f4966e880168772975f9dd532a90 (diff) |
Add new CLI test script
-rw-r--r-- | src/cli/cli_rng.cpp | 4 | ||||
-rwxr-xr-x | src/scripts/ci_build.py | 11 | ||||
-rwxr-xr-x | src/scripts/test_cli.py | 252 |
3 files changed, 261 insertions, 6 deletions
diff --git a/src/cli/cli_rng.cpp b/src/cli/cli_rng.cpp index 1e3ecb141..78af51314 100644 --- a/src/cli/cli_rng.cpp +++ b/src/cli/cli_rng.cpp @@ -66,11 +66,11 @@ cli_make_rng(const std::string& rng_type, const std::string& hex_drbg_seed) } #endif -#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_AUTO_RNG_HMAC) +#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32) if(rng_type == "drbg") { std::unique_ptr<Botan::MessageAuthenticationCode> mac = - Botan::MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC); + Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); std::unique_ptr<Botan::Stateful_RNG> rng(new Botan::HMAC_DRBG(std::move(mac))); rng->add_entropy(drbg_seed.data(), drbg_seed.size()); diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py index a9a6afbdf..4fcdac997 100755 --- a/src/scripts/ci_build.py +++ b/src/scripts/ci_build.py @@ -378,6 +378,7 @@ def main(args=None): 'src/scripts/website.py', 'src/scripts/bench.py', 'src/scripts/test_python.py', + 'src/scripts/test_cli.py', 'src/scripts/python_unittests.py', 'src/scripts/python_unittests_unix.py'] @@ -432,11 +433,13 @@ def main(args=None): os.path.join(root_dir, 'fuzzer_corpus'), os.path.join(root_dir, 'build/fuzzer')]) - if target in ['static', 'shared'] and options.os != 'windows': + if target in ['shared', 'coverage'] and options.os != 'windows': botan_exe = os.path.join(root_dir, 'botan-cli.exe' if options.os == 'windows' else 'botan') - cmds.append([py_interp, - os.path.join(root_dir, 'src/scripts/cli_tests.py'), - botan_exe]) + + test_scripts = ['cli_tests.py', 'test_cli.py'] + for script in test_scripts: + cmds.append([py_interp, os.path.join(root_dir, 'src/scripts', script), + botan_exe]) python_tests = os.path.join(root_dir, 'src/scripts/test_python.py') diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py new file mode 100755 index 000000000..c3fe43c1a --- /dev/null +++ b/src/scripts/test_cli.py @@ -0,0 +1,252 @@ +#!/usr/bin/python + +import subprocess +import sys +import os +import logging +import optparse # pylint: disable=deprecated-module +import time +import shutil +import tempfile + +# pylint: disable=global-statement + +CLI_PATH = None +TESTS_RUN = 0 +TESTS_FAILED = 0 + +class TestLogHandler(logging.StreamHandler, object): + def emit(self, record): + # Do the default stuff first + super(TestLogHandler, self).emit(record) + if record.levelno >= logging.ERROR: + global TESTS_FAILED + TESTS_FAILED += 1 + +def setup_logging(options): + if options.verbose: + log_level = logging.DEBUG + elif options.quiet: + log_level = logging.WARNING + else: + log_level = logging.INFO + + lh = TestLogHandler(sys.stdout) + lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s')) + logging.getLogger().addHandler(lh) + logging.getLogger().setLevel(log_level) + + +def test_cli(cmd, cmd_options, expected_output=None, cmd_input=None): + global TESTS_RUN + global TESTS_FAILED + + TESTS_RUN += 1 + + logging.debug("Running %s %s", cmd, cmd_options) + + fixed_drbg_seed = "802" * 32 + + if type(cmd_options) == str: + cmd_options = cmd_options.split(' ') + + drbg_options = ['--rng-type=drbg', '--drbg-seed=' + fixed_drbg_seed] + cmdline = [CLI_PATH, cmd] + drbg_options + cmd_options + + stdout = None + stderr = None + + if cmd_input is None: + proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = proc.communicate() + else: + proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = proc.communicate(cmd_input.encode()) + + if stderr: + logging.error("Got output on stderr %s", stderr) + + output = stdout.decode('ascii').strip() + + if expected_output != None: + if output != expected_output: + logging.error("Got unexpected output running cmd %s %s - %s", cmd, cmd_options, output) + + return output + +def cli_is_prime_tests(): + test_cli("is_prime", "5", "5 is probably prime") + test_cli("is_prime", "9", "9 is composite") + test_cli("is_prime", "548950623407687320763", "548950623407687320763 is probably prime") + +def cli_gen_prime_tests(): + test_cli("gen_prime", "64", "15568813029901363223") + test_cli("gen_prime", "128", "287193909494025008847286845478788769439") + +def cli_factor_tests(): + test_cli("factor", "97", "97: 97") + test_cli("factor", "9753893489562389", "9753893489562389: 21433 455087644733") + test_cli("factor", "12019502040659149507", "12019502040659149507: 3298628633 3643787579") + +def cli_mod_inverse_tests(): + test_cli("mod_inverse", "97 802", "339") + test_cli("mod_inverse", "98 802", "0") + +def cli_base64_tests(): + test_cli("base64_enc", "-", "YmVlcyE=", "bees!") + test_cli("base64_dec", "-", "bees!", "YmVlcyE=") + +def cli_hex_tests(): + test_cli("hex_enc", "-", "6265657321", "bees!") + test_cli("hex_dec", "-", "bees!", "6265657321") + +def cli_hash_tests(): + test_cli("hash", "--algo=SHA-256", + "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 -", "") + + test_cli("hash", "--algo=SHA-256", + "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD -", "abc") + +def cli_bcrypt_tests(): + test_cli("gen_bcrypt", "--work-factor=4 s3kr1t", + "$2a$04$0.8G7o08XYwvBBWA3l0WUujtwoGZgGDzVSN8fNkNqXikcK4A3lHPS") + + test_cli("check_bcrypt", "s3kr1t $2a$04$gHX4Qg7pDSJuXiPXnmt8leyb.FFzX1Bv4rXwIj2cPSakJ8zNnhIka", + "Password is valid") + + test_cli("check_bcrypt", "santa $2a$04$gHX4Qg7pDSJuXiPXnmt8leyb.FFzX1Bv4rXwIj2cPSakJ8zNnhIka", + "Password is NOT valid") + +def cli_gen_dl_group_tests(): + + pem = """-----BEGIN X9.42 DH PARAMETERS----- +MIIBJAKBgwS7QodscwdpuPdbHL7YeFP7yRjdqWovbwD+/zSd2OMswe82XnawKsSq +FSCVAfsG75CBRZ2krSY8X/7zuIh8yoAPs87ZIQJmKJem/MdII7zndfnec+klKtHo +FX2mOFzmndWJBXv4fwWLHEGIoeCamveL2+q1M8sakNicI7U2vUkX30CxAoGDArnH +im+KLvXXL4uR97V/yDVx/Vf0Eu8Bqxkx6jFMiabzc4mhCJ+MHh2nH3U67PLAnTgR +zCQcHevoOKd/ODychEHWwqP26aKCaRUS1DLq8U/qPL7L2pTMLYu27THt7HvKfA0D +mMo6OGy9uFpJkaLVypD+LKEY/Bc540iRFQcW63ngpo0CFjgPiPatvmWssQw2AuZ9 +mFvAZ/8wal0= +-----END X9.42 DH PARAMETERS-----""" + + test_cli("gen_dl_group", "--pbits=1043", pem) + +def cli_key_tests(): + + pem = """-----BEGIN PRIVATE KEY----- +MD4CAQAwEAYHKoZIzj0CAQYFK4EEAAoEJzAlAgEBBCDYD4j2rb5lrLEMNgLmfZhb +wGf/MGbgPebBLmozAANENw== +-----END PRIVATE KEY-----""" + + tmp_dir = tempfile.mkdtemp(prefix='botan_cli') + + priv_key = os.path.join(tmp_dir, 'priv.pem') + pub_key = os.path.join(tmp_dir, 'pub.pem') + ca_cert = os.path.join(tmp_dir, 'ca.crt') + crt_req = os.path.join(tmp_dir, 'crt.req') + user_cert = os.path.join(tmp_dir, 'user.crt') + + test_cli("keygen", ["--algo=ECDSA", "--params=secp256k1"], pem) + + test_cli("keygen", ["--algo=ECDSA", "--params=secp256r1", "--output=" + priv_key], "") + + test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "") + + valid_sig = "xd3J9jtTBWFWA9ceVGYpmEB0A1DmOoxHRF7FpYW2ng/GYEH/HYljIfYzu/L5iTK6XfVePxeMr6ubCYCD9vFGIw==" + + test_cli("sign", "%s %s" % (priv_key, priv_key), valid_sig) + + test_cli("verify", [pub_key, priv_key, '-'], + "Signature is valid", valid_sig) + + test_cli("verify", [pub_key, priv_key, '-'], + "Signature is invalid", + valid_sig.replace("W", "Z")) + + test_cli("gen_self_signed", "%s CA --ca --country=VT --dns=ca.example --hash=SHA-384 --output=%s" % (priv_key, ca_cert), "") + + test_cli("cert_verify", ca_cert, "Certificate did not validate - Cannot establish trust") + + test_cli("gen_pkcs10", "%s User --output=%s" % (priv_key, crt_req)) + + test_cli("sign_cert", "%s %s %s --output=%s" % (ca_cert, priv_key, crt_req, user_cert)) + + test_cli("cert_verify", [user_cert, ca_cert], + "Certificate passes validation checks") + + test_cli("cert_verify", user_cert, + "Certificate did not validate - Certificate issuer not found") + + shutil.rmtree(tmp_dir) + +def cli_rng_tests(): + test_cli("rng", "10", "D80F88F6ADBE65ACB10C") + test_cli("rng", "16", "D80F88F6ADBE65ACB10C3602E67D985B") + test_cli("rng", "10 6", "D80F88F6ADBE65ACB10C\n1B119CC068AF") + +def cli_ec_group_info_tests(): + + # pylint: disable=line-too-long + secp256r1_info = """P = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF +A = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC +B = 5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B +G = 6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5""" + + secp256r1_pem = """-----BEGIN EC PARAMETERS----- +MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP////////// +/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6 +k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDydwN9 +gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA +/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE= +-----END EC PARAMETERS-----""" + + test_cli("ec_group_info", "secp256r1", secp256r1_info) + test_cli("ec_group_info", "--pem secp256r1", secp256r1_pem) + +def main(args=None): + if args is None: + args = sys.argv + + parser = optparse.OptionParser( + formatter=optparse.IndentedHelpFormatter(max_help_position=50)) + + parser.add_option('--verbose', action='store_true', default=False) + parser.add_option('--quiet', action='store_true', default=False) + + (options, args) = parser.parse_args(args) + + setup_logging(options) + + if len(args) != 2: + logging.error("Usage: ./cli_tests.py path_to_botan_cli") + return 1 + + if os.access(args[1], os.X_OK) != True: + logging.error("Could not access/execute %s", args[1]) + return 2 + + global CLI_PATH + CLI_PATH = args[1] + + start_time = time.time() + cli_is_prime_tests() + cli_factor_tests() + cli_mod_inverse_tests() + cli_base64_tests() + cli_hex_tests() + cli_gen_prime_tests() + cli_hash_tests() + cli_rng_tests() + cli_bcrypt_tests() + cli_gen_dl_group_tests() + cli_ec_group_info_tests() + cli_key_tests() + end_time = time.time() + + print("Ran %d tests with %d failures in %.02f seconds" % ( + TESTS_RUN, TESTS_FAILED, end_time - start_time)) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) |