diff options
author | lloyd <[email protected]> | 2014-01-01 21:20:55 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-01-01 21:20:55 +0000 |
commit | 197dc467dec28a04c3b2f30da7cef122dfbb13e9 (patch) | |
tree | cdbd3ddaec051c72f0a757db461973d90c37b97a /src/tests | |
parent | 62faac373c07cfe10bc8c309e89ebdd30d8e5eaa (diff) |
Shuffle things around. Add NIST X.509 test to build.
Diffstat (limited to 'src/tests')
28 files changed, 5833 insertions, 0 deletions
diff --git a/src/tests/nist_x509.cpp b/src/tests/nist_x509.cpp new file mode 100644 index 000000000..4f75c9f88 --- /dev/null +++ b/src/tests/nist_x509.cpp @@ -0,0 +1,331 @@ +/* + Code to run the X.509v3 processing tests described in "Conformance + Testing of Relying Party Client Certificate Path Proccessing Logic", + which is available on NIST's web site. + +Known Failures/Problems + +Policy extensions are not implemented, so we skip tests #34-#53. + +Tests #75 and #76 are skipped as they make use of relatively obscure CRL +extensions which are not supported. + +In addition, please note that some of the tests have their results altered from +what the test result should be according to NIST's documentation. The changes +are clearly marked (see x509test.cpp; search for "CHANGE OF TEST RESULT") and +there are comments explaining why the results where changed. Currently, tests +#19, #65, and #67 have had their results changed from the official results. +*/ + +#include "tests.h" + +#include <botan/x509path.h> +#include <botan/init.h> + +#include <algorithm> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> +#include <map> +#include <cstdlib> + +#include <dirent.h> + +using namespace Botan; + +std::vector<std::string> dir_listing(const std::string&); + +void run_one_test(u32bit, Path_Validation_Result::Code, + std::string, std::string, + std::vector<std::string>, + std::vector<std::string>); + +std::map<size_t, Path_Validation_Result::Code> expected_results; +size_t unexp_failure, unexp_success, wrong_error, skipped; + +void populate_expected_results(); + +size_t test_nist_x509() + { + unexp_failure = unexp_success = wrong_error = skipped = 0; + + try { + + populate_expected_results(); + + const std::string root_test_dir = "src/test-data/nist_x509/"; + std::vector<std::string> test_dirs = dir_listing(root_test_dir); + std::sort(test_dirs.begin(), test_dirs.end()); + + for(size_t j = 0; j != test_dirs.size(); j++) + { + const std::string test_dir = root_test_dir + test_dirs[j] + "/"; + std::vector<std::string> all_files = dir_listing(test_dir); + + std::vector<std::string> certs, crls; + std::string root_cert, to_verify; + + for(size_t k = 0; k != all_files.size(); k++) + { + const std::string current = all_files[k]; + if(current.find("int") != std::string::npos && + current.find(".crt") != std::string::npos) + certs.push_back(test_dir + current); + else if(current.find("root.crt") != std::string::npos) + root_cert = test_dir + current; + else if(current.find("end.crt") != std::string::npos) + to_verify = test_dir + current; + else if(current.find(".crl") != std::string::npos) + crls.push_back(test_dir + current); + } + + if(expected_results.find(j+1) == expected_results.end()) + { +#if 0 + std::cout << "Testing disabled for test #" << j+1 + << " <skipped>" << std::endl; +#endif + skipped++; + continue; + } + + run_one_test(j+1, expected_results[j+1], + root_cert, to_verify, certs, crls); + } + + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + + std::cout << "Total unexpected failures: " << unexp_failure << std::endl; + std::cout << "Total unexpected successes: " << unexp_success << std::endl; + std::cout << "Total incorrect failures: " << wrong_error << std::endl; + std::cout << "Tests skipped: " << skipped << std::endl; + + return unexp_failure + unexp_success + wrong_error; + } + +void run_one_test(u32bit test_no, Path_Validation_Result::Code expected, + std::string root_cert, std::string to_verify, + std::vector<std::string> certs, + std::vector<std::string> crls) + { + std::cout << "NIST X.509 test #" << test_no << "... "; + + Certificate_Store_In_Memory store; + + store.add_certificate(X509_Certificate(root_cert)); + + X509_Certificate end_user(to_verify); + + for(size_t j = 0; j != certs.size(); j++) + store.add_certificate(X509_Certificate(certs[j])); + + for(size_t j = 0; j != crls.size(); j++) + { + DataSource_Stream in(crls[j]); + + X509_CRL crl(in); + /* + std::vector<CRL_Entry> crl_entries = crl.get_revoked(); + for(u32bit k = 0; k != crl_entries.size(); k++) + { + std::cout << "Revoked: " << std::flush; + for(u32bit l = 0; l != crl_entries[k].serial.size(); l++) + printf("%02X", crl_entries[k].serial[l]); + std::cout << std::endl; + } + */ + store.add_crl(crl); + } + + Path_Validation_Restrictions restrictions(true); + + Path_Validation_Result validation_result = + x509_path_validate(end_user, + restrictions, + store); + + Path_Validation_Result::Code result = validation_result.result(); + + if(result == expected) + { + std::cout << "passed" << std::endl; + return; + } + + const std::string result_str = Path_Validation_Result::status_string(result); + const std::string exp_str = Path_Validation_Result::status_string(expected); + + if(expected == Certificate_Status_Code::VERIFIED) + { + std::cout << "unexpected failure: " << result_str << std::endl; + unexp_failure++; + } + else if(result == Certificate_Status_Code::VERIFIED) + { + std::cout << "unexpected success: " << exp_str << std::endl; + unexp_success++; + } + else + { + std::cout << "wrong error: " << result_str << "/" << exp_str << std::endl; + wrong_error++; + } + } + +std::vector<std::string> dir_listing(const std::string& dir_name) + { + DIR* dir = opendir(dir_name.c_str()); + if(!dir) + { + std::cout << "Error, couldn't open dir " << dir_name << std::endl; + std::exit(1); + } + + std::vector<std::string> listing; + + while(true) + { + struct dirent* dir_ent = readdir(dir); + + if(dir_ent == 0) + break; + const std::string entry = dir_ent->d_name; + if(entry == "." || entry == "..") + continue; + + listing.push_back(entry); + } + closedir(dir); + + return listing; + } + +/* + The expected results are essentially the error codes that best coorespond + to the problem described in the testing documentation. + + There are a few cases where the tests say there should or should not be an + error, and I disagree. A few of the tests have test results different from + what they "should" be: these changes are marked as such, and have comments + explaining the problem at hand. +*/ +void populate_expected_results() + { + /* OK, not a super great way of doing this... */ + expected_results[1] = Certificate_Status_Code::VERIFIED; + expected_results[2] = Certificate_Status_Code::SIGNATURE_ERROR; + expected_results[3] = Certificate_Status_Code::SIGNATURE_ERROR; + expected_results[4] = Certificate_Status_Code::VERIFIED; + expected_results[5] = Certificate_Status_Code::CERT_NOT_YET_VALID; + expected_results[6] = Certificate_Status_Code::CERT_NOT_YET_VALID; + expected_results[7] = Certificate_Status_Code::VERIFIED; + expected_results[8] = Certificate_Status_Code::CERT_NOT_YET_VALID; + expected_results[9] = Certificate_Status_Code::CERT_HAS_EXPIRED; + expected_results[10] = Certificate_Status_Code::CERT_HAS_EXPIRED; + expected_results[11] = Certificate_Status_Code::CERT_HAS_EXPIRED; + expected_results[12] = Certificate_Status_Code::VERIFIED; + expected_results[13] = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + + expected_results[14] = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + expected_results[15] = Certificate_Status_Code::VERIFIED; + expected_results[16] = Certificate_Status_Code::VERIFIED; + expected_results[17] = Certificate_Status_Code::VERIFIED; + expected_results[18] = Certificate_Status_Code::VERIFIED; + + expected_results[19] = Certificate_Status_Code::CRL_NOT_FOUND; + expected_results[20] = Certificate_Status_Code::CERT_IS_REVOKED; + expected_results[21] = Certificate_Status_Code::CERT_IS_REVOKED; + + expected_results[22] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; + expected_results[23] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; + expected_results[24] = Certificate_Status_Code::VERIFIED; + expected_results[25] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; + expected_results[26] = Certificate_Status_Code::VERIFIED; + expected_results[27] = Certificate_Status_Code::VERIFIED; + expected_results[28] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; + expected_results[29] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; + expected_results[30] = Certificate_Status_Code::VERIFIED; + + expected_results[31] = Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER; + expected_results[32] = Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER; + expected_results[33] = Certificate_Status_Code::VERIFIED; + + /* + Policy tests: a little trickier because there are other inputs + which affect the result. + + In the case of the tests currently in the suite, the default + method (with acceptable policy being "any-policy" and with no + explict policy required), will almost always result in a verified + status. This is not particularly helpful. So, we should do several + different tests for each test set: + + 1) With the user policy as any-policy and no explicit policy + 2) With the user policy as any-policy and an explicit policy required + 3) With the user policy as test-policy-1 (2.16.840.1.101.3.1.48.1) and + an explict policy required + 4) With the user policy as either test-policy-1 or test-policy-2 and an + explicit policy required + + This provides reasonably good coverage of the possible outcomes. + */ + + expected_results[34] = Certificate_Status_Code::VERIFIED; + expected_results[35] = Certificate_Status_Code::VERIFIED; + expected_results[36] = Certificate_Status_Code::VERIFIED; + expected_results[37] = Certificate_Status_Code::VERIFIED; + expected_results[38] = Certificate_Status_Code::VERIFIED; + expected_results[39] = Certificate_Status_Code::VERIFIED; + expected_results[40] = Certificate_Status_Code::VERIFIED; + expected_results[41] = Certificate_Status_Code::VERIFIED; + expected_results[42] = Certificate_Status_Code::VERIFIED; + expected_results[43] = Certificate_Status_Code::VERIFIED; + expected_results[44] = Certificate_Status_Code::VERIFIED; + + //expected_results[45] = Certificate_Status_Code::EXPLICT_POLICY_REQUIRED; + //expected_results[46] = Certificate_Status_Code::ACCEPT; + //expected_results[47] = Certificate_Status_Code::EXPLICT_POLICY_REQUIRED; + + expected_results[48] = Certificate_Status_Code::VERIFIED; + expected_results[49] = Certificate_Status_Code::VERIFIED; + expected_results[50] = Certificate_Status_Code::VERIFIED; + expected_results[51] = Certificate_Status_Code::VERIFIED; + expected_results[52] = Certificate_Status_Code::VERIFIED; + expected_results[53] = Certificate_Status_Code::VERIFIED; + + expected_results[54] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[55] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[56] = Certificate_Status_Code::VERIFIED; + expected_results[57] = Certificate_Status_Code::VERIFIED; + expected_results[58] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[59] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[60] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[61] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; + expected_results[62] = Certificate_Status_Code::VERIFIED; + expected_results[63] = Certificate_Status_Code::VERIFIED; + + expected_results[64] = Certificate_Status_Code::SIGNATURE_ERROR; + + expected_results[65] = Certificate_Status_Code::CRL_NOT_FOUND; + expected_results[66] = Certificate_Status_Code::CRL_NOT_FOUND; + + expected_results[67] = Certificate_Status_Code::VERIFIED; + + expected_results[68] = Certificate_Status_Code::CERT_IS_REVOKED; + expected_results[69] = Certificate_Status_Code::CERT_IS_REVOKED; + expected_results[70] = Certificate_Status_Code::CERT_IS_REVOKED; + expected_results[71] = Certificate_Status_Code::CERT_IS_REVOKED; + expected_results[72] = Certificate_Status_Code::CRL_HAS_EXPIRED; + expected_results[73] = Certificate_Status_Code::CRL_HAS_EXPIRED; + expected_results[74] = Certificate_Status_Code::VERIFIED; + + /* These tests use weird CRL extensions which aren't supported yet */ + //expected_results[75] = ; + //expected_results[76] = ; + } diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp new file mode 100644 index 000000000..e8643334f --- /dev/null +++ b/src/tests/test_aead.cpp @@ -0,0 +1,88 @@ +#include "tests.h" + +#include <botan/hex.h> +#include <botan/aead.h> +#include <iostream> +#include <fstream> +#include <memory> + +using namespace Botan; + +namespace { + +secure_vector<byte> aead(const std::string& algo, + Cipher_Dir dir, + const secure_vector<byte>& pt, + const secure_vector<byte>& nonce, + const secure_vector<byte>& ad, + const secure_vector<byte>& key) + { + std::unique_ptr<AEAD_Mode> aead(get_aead(algo, dir)); + + aead->set_key(key); + aead->set_associated_data_vec(ad); + aead->start_vec(nonce); + + secure_vector<byte> ct = pt; + aead->finish(ct); + + return ct; + } + +bool aead_test(const std::string& algo, + const std::string& pt, + const std::string& ct, + const std::string& nonce_hex, + const std::string& ad_hex, + const std::string& key_hex) + { + auto nonce = hex_decode_locked(nonce_hex); + auto ad = hex_decode_locked(ad_hex); + auto key = hex_decode_locked(key_hex); + + size_t fail = 0; + + //std::cout << algo << " pt=" << pt << " ct=" << ct << " key=" << key_hex << " nonce=" << nonce_hex << " ad=" << ad_hex << "\n"; + + const std::string ct2 = hex_encode(aead(algo, + ENCRYPTION, + hex_decode_locked(pt), + nonce, + ad, + key)); + + if(ct != ct2) + { + std::cout << algo << " got ct " << ct2 << " expected " << ct << "\n"; + ++fail; + } + + const std::string pt2 = hex_encode(aead(algo, + DECRYPTION, + hex_decode_locked(ct2), + nonce, + ad, + key)); + + if(pt != pt2) + { + std::cout << algo << " got pt " << pt2 << " expected " << pt << "\n"; + ++fail; + } + + return (ct == ct2) && (pt == pt2); + } + +} + +size_t test_aead() + { + std::ifstream vec(CHECKS_DIR "/aead.vec"); + + return run_tests_bb(vec, "AEAD", "Out", true, + [](std::map<std::string, std::string> m) + { + return aead_test(m["AEAD"], m["In"], m["Out"], + m["Nonce"], m["AD"], m["Key"]); + }); + } diff --git a/src/tests/test_bigint.cpp b/src/tests/test_bigint.cpp new file mode 100644 index 000000000..45a20cef6 --- /dev/null +++ b/src/tests/test_bigint.cpp @@ -0,0 +1,357 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" + +#include <vector> +#include <string> +#include <fstream> +#include <iostream> +#include <cstdlib> + +#include <botan/auto_rng.h> +#include <botan/bigint.h> +#include <botan/exceptn.h> +#include <botan/numthry.h> +using namespace Botan; + +size_t check_add(const std::vector<std::string>&); +size_t check_sub(const std::vector<std::string>&); +size_t check_mul(const std::vector<std::string>&); +size_t check_sqr(const std::vector<std::string>&); +size_t check_div(const std::vector<std::string>&); +size_t check_mod(const std::vector<std::string>&, + Botan::RandomNumberGenerator& rng); +size_t check_shr(const std::vector<std::string>&); +size_t check_shl(const std::vector<std::string>&); + +size_t check_powmod(const std::vector<std::string>&); +size_t check_primetest(const std::vector<std::string>&, + Botan::RandomNumberGenerator&); + +size_t test_bigint() + { + const std::string filename = CHECKS_DIR "/mp_valid.dat"; + std::ifstream test_data(filename.c_str()); + + if(!test_data) + throw Botan::Stream_IO_Error("Couldn't open test file " + filename); + + size_t total_errors = 0; + size_t errors = 0, alg_count = 0; + std::string algorithm; + bool first = true; + size_t counter = 0; + + AutoSeeded_RNG rng; + + while(!test_data.eof()) + { + if(test_data.bad() || test_data.fail()) + throw Botan::Stream_IO_Error("File I/O error reading from " + + filename); + + std::string line; + std::getline(test_data, line); + + strip(line); + if(line.size() == 0) continue; + + // Do line continuation + while(line[line.size()-1] == '\\' && !test_data.eof()) + { + line.replace(line.size()-1, 1, ""); + std::string nextline; + std::getline(test_data, nextline); + strip(nextline); + if(nextline.size() == 0) continue; + line += nextline; + } + + if(line[0] == '[' && line[line.size() - 1] == ']') + { + if(!first) + test_report("Bigint " + algorithm, alg_count, errors); + + algorithm = line.substr(1, line.size() - 2); + + total_errors += errors; + errors = 0; + alg_count = 0; + counter = 0; + + first = false; + continue; + } + + std::vector<std::string> substr = parse(line); + +#if DEBUG + std::cout << "Testing: " << algorithm << std::endl; +#endif + + size_t new_errors = 0; + if(algorithm.find("Addition") != std::string::npos) + new_errors = check_add(substr); + else if(algorithm.find("Subtraction") != std::string::npos) + new_errors = check_sub(substr); + else if(algorithm.find("Multiplication") != std::string::npos) + new_errors = check_mul(substr); + else if(algorithm.find("Square") != std::string::npos) + new_errors = check_sqr(substr); + else if(algorithm.find("Division") != std::string::npos) + new_errors = check_div(substr); + else if(algorithm.find("Modulo") != std::string::npos) + new_errors = check_mod(substr, rng); + else if(algorithm.find("LeftShift") != std::string::npos) + new_errors = check_shl(substr); + else if(algorithm.find("RightShift") != std::string::npos) + new_errors = check_shr(substr); + else if(algorithm.find("ModExp") != std::string::npos) + new_errors = check_powmod(substr); + else if(algorithm.find("PrimeTest") != std::string::npos) + new_errors = check_primetest(substr, rng); + else + std::cout << "Unknown MPI test " << algorithm << std::endl; + + counter++; + alg_count++; + errors += new_errors; + + if(new_errors) + std::cout << "ERROR: BigInt " << algorithm << " failed test #" + << std::dec << alg_count << std::endl; + } + + return total_errors; + } + +namespace { + +// c==expected, d==a op b, e==a op= b +size_t results(std::string op, + const BigInt& a, const BigInt& b, + const BigInt& c, const BigInt& d, const BigInt& e) + { + std::string op1 = "operator" + op; + std::string op2 = op1 + "="; + + if(c == d && d == e) + return 0; + else + { + std::cout << std::endl; + + std::cout << "ERROR: " << op1 << std::endl; + + std::cout << "a = " << std::hex << a << std::endl; + std::cout << "b = " << std::hex << b << std::endl; + + std::cout << "c = " << std::hex << c << std::endl; + std::cout << "d = " << std::hex << d << std::endl; + std::cout << "e = " << std::hex << e << std::endl; + + if(d != e) + { + std::cout << "ERROR: " << op1 << " | " << op2 + << " mismatch" << std::endl; + } + return 1; + } + } + +} + +size_t check_add(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt c(args[2]); + + BigInt d = a + b; + BigInt e = a; + e += b; + + if(results("+", a, b, c, d, e)) + return 1; + + d = b + a; + e = b; + e += a; + + return results("+", a, b, c, d, e); + } + +size_t check_sub(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt c(args[2]); + + BigInt d = a - b; + BigInt e = a; + e -= b; + + return results("-", a, b, c, d, e); + } + +size_t check_mul(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt c(args[2]); + + /* + std::cout << "a = " << args[0] << "\n" + << "b = " << args[1] << std::endl; + */ + /* This makes it more likely the fast multiply algorithms will be usable, + which is what we really want to test here (the simple n^2 multiply is + pretty well tested at this point). + */ + a.grow_to(64); + b.grow_to(64); + + BigInt d = a * b; + BigInt e = a; + e *= b; + + if(results("*", a, b, c, d, e)) + return 1; + + d = b * a; + e = b; + e *= a; + + return results("*", a, b, c, d, e); + } + +size_t check_sqr(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + + a.grow_to(64); + b.grow_to(64); + + BigInt c = square(a); + BigInt d = a * a; + + return results("sqr", a, a, b, c, d); + } + +size_t check_div(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt c(args[2]); + + BigInt d = a / b; + BigInt e = a; + e /= b; + + return results("/", a, b, c, d, e); + } + +size_t check_mod(const std::vector<std::string>& args, + Botan::RandomNumberGenerator& rng) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt c(args[2]); + + BigInt d = a % b; + BigInt e = a; + e %= b; + + size_t got = results("%", a, b, c, d, e); + + if(got) return got; + + word b_word = b.word_at(0); + + /* Won't work for us, just pick one at random */ + while(b_word == 0) + for(size_t j = 0; j != 2*sizeof(word); j++) + b_word = (b_word << 4) ^ rng.next_byte(); + + b = b_word; + + c = a % b; /* we declare the BigInt % BigInt version to be correct here */ + + word d2 = a % b_word; + e = a; + e %= b_word; + + return results("%(word)", a, b, c, d2, e); + } + +size_t check_shl(const std::vector<std::string>& args) + { + BigInt a(args[0]); + size_t b = std::atoi(args[1].c_str()); + BigInt c(args[2]); + + BigInt d = a << b; + BigInt e = a; + e <<= b; + + return results("<<", a, b, c, d, e); + } + +size_t check_shr(const std::vector<std::string>& args) + { + BigInt a(args[0]); + size_t b = std::atoi(args[1].c_str()); + BigInt c(args[2]); + + BigInt d = a >> b; + BigInt e = a; + e >>= b; + + return results(">>", a, b, c, d, e); + } + +/* Make sure that (a^b)%m == r */ +size_t check_powmod(const std::vector<std::string>& args) + { + BigInt a(args[0]); + BigInt b(args[1]); + BigInt m(args[2]); + BigInt c(args[3]); + + BigInt r = power_mod(a, b, m); + + if(c != r) + { + std::cout << "ERROR: power_mod" << std::endl; + std::cout << "a = " << std::hex << a << std::endl; + std::cout << "b = " << std::hex << b << std::endl; + std::cout << "m = " << std::hex << m << std::endl; + std::cout << "c = " << std::hex << c << std::endl; + std::cout << "r = " << std::hex << r << std::endl; + return 1; + } + return 0; + } + +/* Make sure that n is prime or not prime, according to should_be_prime */ +size_t check_primetest(const std::vector<std::string>& args, + Botan::RandomNumberGenerator& rng) + { + BigInt n(args[0]); + bool should_be_prime = (args[1] == "1"); + + bool is_prime = Botan::verify_prime(n, rng); + + if(is_prime != should_be_prime) + { + std::cout << "ERROR: verify_prime" << std::endl; + std::cout << "n = " << n << std::endl; + std::cout << is_prime << " != " << should_be_prime << std::endl; + } + return 0; + } diff --git a/src/tests/test_block.cpp b/src/tests/test_block.cpp new file mode 100644 index 000000000..51241d69b --- /dev/null +++ b/src/tests/test_block.cpp @@ -0,0 +1,75 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/block_cipher.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +bool block_test(const std::string& algo, + const std::string& key_hex, + const std::string& in_hex, + const std::string& out_hex) + { + const secure_vector<byte> key = hex_decode_locked(key_hex); + const secure_vector<byte> pt = hex_decode_locked(in_hex); + const secure_vector<byte> ct = hex_decode_locked(out_hex); + + Algorithm_Factory& af = global_state().algorithm_factory(); + + const auto providers = af.providers_of(algo); + size_t fails = 0; + + for(auto provider: providers) + { + const BlockCipher* proto = af.prototype_block_cipher(algo, provider); + + if(!proto) + { + std::cout << "Unable to get " << algo << " from " << provider << "\n"; + ++fails; + continue; + } + + std::unique_ptr<BlockCipher> cipher(proto->clone()); + cipher->set_key(key); + secure_vector<byte> buf = pt; + + cipher->encrypt(buf); + + if(buf != ct) + { + std::cout << algo << " " << provider << " enc " << hex_encode(buf) << " != " << out_hex << "\n"; + ++fails; + } + + buf = ct; + + cipher->decrypt(buf); + + if(buf != pt) + { + std::cout << algo << " " << provider << " dec " << hex_encode(buf) << " != " << out_hex << "\n"; + ++fails; + } + } + + return (fails == 0); + } + +} + +size_t test_block() + { + std::ifstream vec(CHECKS_DIR "/block.vec"); + + return run_tests_bb(vec, "BlockCipher", "Out", true, + [](std::map<std::string, std::string> m) -> bool + { + return block_test(m["BlockCipher"], m["Key"], m["In"], m["Out"]); + }); + } diff --git a/src/tests/test_cryptobox.cpp b/src/tests/test_cryptobox.cpp new file mode 100644 index 000000000..9a53da74c --- /dev/null +++ b/src/tests/test_cryptobox.cpp @@ -0,0 +1,45 @@ +#include "tests.h" + +#include <botan/auto_rng.h> +#include <iostream> + +#if defined(BOTAN_HAS_CRYPTO_BOX) + #include <botan/cryptobox.h> +#endif + +using namespace Botan; + +size_t test_cryptobox() + { + size_t fails = 0; + +#if defined(BOTAN_HAS_CRYPTO_BOX) + AutoSeeded_RNG rng; + + const byte msg[] = { 0xAA, 0xBB, 0xCC }; + std::string ciphertext = CryptoBox::encrypt(msg, sizeof(msg), + "secret password", + rng); + + try + { + std::string plaintext = CryptoBox::decrypt(ciphertext, + "secret password"); + + if(plaintext.size() != sizeof(msg) || + !same_mem(reinterpret_cast<const byte*>(&plaintext[0]), msg, sizeof(msg))) + ++fails; + + } + catch(std::exception& e) + { + std::cout << "Error during Cryptobox test " << e.what() << "\n"; + ++fails; + } + + test_report("Cryptobox", 1, fails); +#endif + + return fails; + } + diff --git a/src/tests/test_cvc.cpp b/src/tests/test_cvc.cpp new file mode 100644 index 000000000..205b74ec8 --- /dev/null +++ b/src/tests/test_cvc.cpp @@ -0,0 +1,573 @@ +/* +* CVC EAC1.1 tests +* +* (C) 2008 Falko Strenzke ([email protected]) +* 2008 Jack Lloyd +*/ + +#include "tests.h" +#include <botan/build.h> + +#if defined(BOTAN_HAS_CARD_VERIFIABLE_CERTIFICATES) + +#include <iosfwd> +#include <iostream> +#include <iterator> +#include <algorithm> +#include <fstream> +#include <vector> +#include <memory> + +#include <botan/ecdsa.h> +#include <botan/rsa.h> + +#include <botan/x509cert.h> +#include <botan/x509self.h> +#include <botan/oids.h> +#include <botan/cvc_self.h> +#include <botan/cvc_cert.h> +#include <botan/cvc_ado.h> + +#define TEST_DATA_DIR CHECKS_DIR "/ecc_testdata" + +using namespace Botan; + +#define CHECK_MESSAGE(expr, print) try { if(!(expr)) std::cout << print << "\n"; } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } +#define CHECK(expr) try { if(!(expr)) std::cout << #expr << "\n"; } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } + +namespace { + +// helper functions +void helper_write_file(EAC_Signed_Object const& to_write, std::string const& file_path) + { + std::vector<byte> sv = to_write.BER_encode(); + std::ofstream cert_file(file_path.c_str(), std::ios::binary); + cert_file.write((char*)&sv[0], sv.size()); + cert_file.close(); + } + +bool helper_files_equal(std::string const& file_path1, std::string const& file_path2) + { + std::ifstream cert_1_in(file_path1.c_str()); + std::ifstream cert_2_in(file_path2.c_str()); + std::vector<byte> sv1; + std::vector<byte> sv2; + if (!cert_1_in || !cert_2_in) + { + return false; + } + while (!cert_1_in.eof()) + { + char now; + cert_1_in.read(&now, 1); + sv1.push_back(now); + } + while (!cert_2_in.eof()) + { + char now; + cert_2_in.read(&now, 1); + sv2.push_back(now); + } + if (sv1.size() == 0) + { + return false; + } + return sv1 == sv2; + } + +void test_enc_gen_selfsigned(RandomNumberGenerator& rng) + { + EAC1_1_CVC_Options opts; + //opts.cpi = 0; + opts.chr = ASN1_Chr("my_opt_chr"); // not used + opts.car = ASN1_Car("my_opt_car"); + opts.cex = ASN1_Cex("2010 08 13"); + opts.ced = ASN1_Ced("2010 07 27"); + opts.holder_auth_templ = 0xC1; + opts.hash_alg = "SHA-256"; + + // creating a non sense selfsigned cert w/o dom pars + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); + ECDSA_PrivateKey key(rng, dom_pars); + key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + EAC1_1_CVC cert = CVC_EAC::create_self_signed_cert(key, opts, rng); + + std::vector<byte> der(cert.BER_encode()); + std::ofstream cert_file; + cert_file.open(TEST_DATA_DIR "/my_cv_cert.ber", std::ios::binary); + //cert_file << der; // this is bad !!! + cert_file.write((char*)&der[0], der.size()); + cert_file.close(); + + EAC1_1_CVC cert_in(TEST_DATA_DIR "/my_cv_cert.ber"); + CHECK(cert == cert_in); + // encoding it again while it has no dp + std::vector<byte> der2(cert_in.BER_encode()); + std::ofstream cert_file2(TEST_DATA_DIR "/my_cv_cert2.ber", std::ios::binary); + cert_file2.write((char*)&der2[0], der2.size()); + cert_file2.close(); + // read both and compare them + std::ifstream cert_1_in(TEST_DATA_DIR "/my_cv_cert.ber"); + std::ifstream cert_2_in(TEST_DATA_DIR "/my_cv_cert2.ber"); + std::vector<byte> sv1; + std::vector<byte> sv2; + if (!cert_1_in || !cert_2_in) + { + CHECK_MESSAGE(false, "could not read certificate files"); + } + while (!cert_1_in.eof()) + { + char now; + + cert_1_in.read(&now, 1); + sv1.push_back(now); + } + while (!cert_2_in.eof()) + { + char now; + cert_2_in.read(&now, 1); + sv2.push_back(now); + } + CHECK(sv1.size() > 10); + CHECK_MESSAGE(sv1 == sv2, "reencoded file of cert without domain parameters is different from original"); + + //cout << "reading cert again\n"; + CHECK(cert_in.get_car().value() == "my_opt_car"); + CHECK(cert_in.get_chr().value() == "my_opt_car"); + CHECK(cert_in.get_ced().as_string() == "20100727"); + CHECK(cert_in.get_ced().readable_string() == "2010/07/27 "); + + bool ill_date_exc = false; + try + { + ASN1_Ced("1999 01 01"); + } + catch (...) + { + ill_date_exc = true; + } + CHECK(ill_date_exc); + + bool ill_date_exc2 = false; + try + { + ASN1_Ced("2100 01 01"); + } + catch (...) + { + ill_date_exc2 = true; + } + CHECK(ill_date_exc2); + //cout << "readable = '" << cert_in.get_ced().readable_string() << "'\n"; + std::unique_ptr<Public_Key> p_pk(cert_in.subject_public_key()); + ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast<ECDSA_PublicKey*>(p_pk.get()); + + // let´s see if encoding is truely implicitca, because this is what the key should have + // been set to when decoding (see above)(because it has no domain params): + + CHECK(p_ecdsa_pk->domain_format() == EC_DOMPAR_ENC_IMPLICITCA); + bool exc = false; + try + { + std::cout << "order = " << p_ecdsa_pk->domain().get_order() << std::endl; + } + catch (Invalid_State) + { + exc = true; + } + CHECK(exc); + // set them and try again + //cert_in.set_domain_parameters(dom_pars); + std::unique_ptr<Public_Key> p_pk2(cert_in.subject_public_key()); + ECDSA_PublicKey* p_ecdsa_pk2 = dynamic_cast<ECDSA_PublicKey*>(p_pk2.get()); + //p_ecdsa_pk2->set_domain_parameters(dom_pars); + CHECK(p_ecdsa_pk2->domain().get_order() == dom_pars.get_order()); + bool ver_ec = cert_in.check_signature(*p_pk2); + CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned cvc certificate"); + } + +void test_enc_gen_req(RandomNumberGenerator& rng) + { + EAC1_1_CVC_Options opts; + + //opts.cpi = 0; + opts.chr = ASN1_Chr("my_opt_chr"); + opts.hash_alg = "SHA-160"; + + // creating a non sense selfsigned cert w/o dom pars + EC_Group dom_pars(OID("1.3.132.0.8")); + ECDSA_PrivateKey key(rng, dom_pars); + key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + EAC1_1_Req req = CVC_EAC::create_cvc_req(key, opts.chr, opts.hash_alg, rng); + std::vector<byte> der(req.BER_encode()); + std::ofstream req_file(TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); + req_file.write((char*)&der[0], der.size()); + req_file.close(); + + // read and check signature... + EAC1_1_Req req_in(TEST_DATA_DIR "/my_cv_req.ber"); + //req_in.set_domain_parameters(dom_pars); + std::unique_ptr<Public_Key> p_pk(req_in.subject_public_key()); + ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast<ECDSA_PublicKey*>(p_pk.get()); + //p_ecdsa_pk->set_domain_parameters(dom_pars); + CHECK(p_ecdsa_pk->domain().get_order() == dom_pars.get_order()); + bool ver_ec = req_in.check_signature(*p_pk); + CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned (created by myself) cvc request"); + } + +void test_cvc_req_ext(RandomNumberGenerator&) + { + EAC1_1_Req req_in(TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" + //req_in.set_domain_parameters(dom_pars); + std::unique_ptr<Public_Key> p_pk(req_in.subject_public_key()); + ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast<ECDSA_PublicKey*>(p_pk.get()); + //p_ecdsa_pk->set_domain_parameters(dom_pars); + CHECK(p_ecdsa_pk->domain().get_order() == dom_pars.get_order()); + bool ver_ec = req_in.check_signature(*p_pk); + CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned (external testdata) cvc request"); + } + +void test_cvc_ado_ext(RandomNumberGenerator&) + { + EAC1_1_ADO req_in(TEST_DATA_DIR "/ado.cvcreq"); + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" + //cout << "car = " << req_in.get_car().value() << std::endl; + //req_in.set_domain_parameters(dom_pars); + } + +void test_cvc_ado_creation(RandomNumberGenerator& rng) + { + EAC1_1_CVC_Options opts; + //opts.cpi = 0; + opts.chr = ASN1_Chr("my_opt_chr"); + opts.hash_alg = "SHA-256"; + + // creating a non sense selfsigned cert w/o dom pars + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); + //cout << "mod = " << hex << dom_pars.get_curve().get_p() << std::endl; + ECDSA_PrivateKey req_key(rng, dom_pars); + req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + //EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts); + EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts.chr, opts.hash_alg, rng); + std::vector<byte> der(req.BER_encode()); + std::ofstream req_file(TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); + req_file.write((char*)&der[0], der.size()); + req_file.close(); + + // create an ado with that req + ECDSA_PrivateKey ado_key(rng, dom_pars); + EAC1_1_CVC_Options ado_opts; + ado_opts.car = ASN1_Car("my_ado_car"); + ado_opts.hash_alg = "SHA-256"; // must be equal to req´s hash alg, because ado takes his sig_algo from it´s request + + //EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts); + EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts.car, rng); + CHECK_MESSAGE(ado.check_signature(ado_key), "failure of ado verification after creation"); + + std::ofstream ado_file(TEST_DATA_DIR "/ado", std::ios::binary); + std::vector<byte> ado_der(ado.BER_encode()); + ado_file.write((char*)&ado_der[0], ado_der.size()); + ado_file.close(); + // read it again and check the signature + EAC1_1_ADO ado2(TEST_DATA_DIR "/ado"); + CHECK(ado == ado2); + //ECDSA_PublicKey* p_ado_pk = dynamic_cast<ECDSA_PublicKey*>(&ado_key); + //bool ver = ado2.check_signature(*p_ado_pk); + bool ver = ado2.check_signature(ado_key); + CHECK_MESSAGE(ver, "failure of ado verification after reloading"); + } + +void test_cvc_ado_comparison(RandomNumberGenerator& rng) + { + EAC1_1_CVC_Options opts; + //opts.cpi = 0; + opts.chr = ASN1_Chr("my_opt_chr"); + opts.hash_alg = "SHA-224"; + + // creating a non sense selfsigned cert w/o dom pars + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); + ECDSA_PrivateKey req_key(rng, dom_pars); + req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + //EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts); + EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts.chr, opts.hash_alg, rng); + + + // create an ado with that req + ECDSA_PrivateKey ado_key(rng, dom_pars); + EAC1_1_CVC_Options ado_opts; + ado_opts.car = ASN1_Car("my_ado_car1"); + ado_opts.hash_alg = "SHA-224"; // must be equal to req's hash alg, because ado takes his sig_algo from it's request + //EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts); + EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts.car, rng); + CHECK_MESSAGE(ado.check_signature(ado_key), "failure of ado verification after creation"); + // make a second one for comparison + EAC1_1_CVC_Options opts2; + //opts2.cpi = 0; + opts2.chr = ASN1_Chr("my_opt_chr"); + opts2.hash_alg = "SHA-160"; // this is the only difference + ECDSA_PrivateKey req_key2(rng, dom_pars); + req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + //EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2, rng); + EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2.chr, opts2.hash_alg, rng); + ECDSA_PrivateKey ado_key2(rng, dom_pars); + EAC1_1_CVC_Options ado_opts2; + ado_opts2.car = ASN1_Car("my_ado_car1"); + ado_opts2.hash_alg = "SHA-160"; // must be equal to req's hash alg, because ado takes his sig_algo from it's request + + EAC1_1_ADO ado2 = CVC_EAC::create_ado_req(ado_key2, req2, ado_opts2.car, rng); + CHECK_MESSAGE(ado2.check_signature(ado_key2), "failure of ado verification after creation"); + + CHECK_MESSAGE(ado != ado2, "ado's found to be equal where they are not"); + // std::ofstream ado_file(TEST_DATA_DIR "/ado"); + // std::vector<byte> ado_der(ado.BER_encode()); + // ado_file.write((char*)&ado_der[0], ado_der.size()); + // ado_file.close(); + // read it again and check the signature + + // EAC1_1_ADO ado2(TEST_DATA_DIR "/ado"); + // ECDSA_PublicKey* p_ado_pk = dynamic_cast<ECDSA_PublicKey*>(&ado_key); + // //bool ver = ado2.check_signature(*p_ado_pk); + // bool ver = ado2.check_signature(ado_key); + // CHECK_MESSAGE(ver, "failure of ado verification after reloading"); + } + +void test_eac_time(RandomNumberGenerator&) + { + EAC_Time time(std::chrono::system_clock::now()); + // std::cout << "time as std::string = " << time.as_string() << std::endl; + EAC_Time sooner("", ASN1_Tag(99)); + //X509_Time sooner("", ASN1_Tag(99)); + sooner.set_to("2007 12 12"); + // std::cout << "sooner as std::string = " << sooner.as_string() << std::endl; + EAC_Time later("2007 12 13"); + //X509_Time later("2007 12 13"); + // std::cout << "later as std::string = " << later.as_string() << std::endl; + CHECK(sooner <= later); + CHECK(sooner == sooner); + + ASN1_Cex my_cex("2007 08 01"); + my_cex.add_months(12); + CHECK(my_cex.get_year() == 2008); + CHECK_MESSAGE(my_cex.get_month() == 8, "shoult be 8, was " << my_cex.get_month()); + + my_cex.add_months(4); + CHECK(my_cex.get_year() == 2008); + CHECK(my_cex.get_month() == 12); + + my_cex.add_months(4); + CHECK(my_cex.get_year() == 2009); + CHECK(my_cex.get_month() == 4); + + my_cex.add_months(41); + CHECK(my_cex.get_year() == 2012); + CHECK(my_cex.get_month() == 9); + + + + } + +void test_ver_cvca(RandomNumberGenerator&) + { + EAC1_1_CVC req_in(TEST_DATA_DIR "/cvca01.cv.crt"); + + bool exc = false; + + std::unique_ptr<Public_Key> p_pk2(req_in.subject_public_key()); + ECDSA_PublicKey* p_ecdsa_pk2 = dynamic_cast<ECDSA_PublicKey*>(p_pk2.get()); + bool ver_ec = req_in.check_signature(*p_pk2); + CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned cvca certificate"); + + try + { + p_ecdsa_pk2->domain().get_order(); + } + catch (Invalid_State) + { + exc = true; + } + CHECK(!exc); + } + +void test_copy_and_assignment(RandomNumberGenerator&) + { + EAC1_1_CVC cert_in(TEST_DATA_DIR "/cvca01.cv.crt"); + EAC1_1_CVC cert_cp(cert_in); + EAC1_1_CVC cert_ass = cert_in; + CHECK(cert_in == cert_cp); + CHECK(cert_in == cert_ass); + + EAC1_1_ADO ado_in(TEST_DATA_DIR "/ado.cvcreq"); + //EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" + EAC1_1_ADO ado_cp(ado_in); + EAC1_1_ADO ado_ass = ado_in; + CHECK(ado_in == ado_cp); + CHECK(ado_in == ado_ass); + + EAC1_1_Req req_in(TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); + //EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" + EAC1_1_Req req_cp(req_in); + EAC1_1_Req req_ass = req_in; + CHECK(req_in == req_cp); + CHECK(req_in == req_ass); + } + +void test_eac_str_illegal_values(RandomNumberGenerator&) + { + bool exc = false; + try + { + EAC1_1_CVC(TEST_DATA_DIR "/cvca_illegal_chars.cv.crt"); + + } + catch (Decoding_Error) + { + exc = true; + } + CHECK(exc); + + bool exc2 = false; + try + { + EAC1_1_CVC(TEST_DATA_DIR "/cvca_illegal_chars2.cv.crt"); + + } + catch (Decoding_Error) + { + exc2 = true; + } + CHECK(exc2); + } + +void test_tmp_eac_str_enc(RandomNumberGenerator&) + { + bool exc = false; + try + { + ASN1_Car("abc!+-µ\n"); + } + catch (Invalid_Argument) + { + exc = true; + } + CHECK(exc); + // std::string val = car.iso_8859(); + // std::cout << "car 8859 = " << val << std::endl; + // std::cout << hex <<(unsigned char)val[1] << std::endl; + + + } + +void test_cvc_chain(RandomNumberGenerator& rng) + { + EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" + ECDSA_PrivateKey cvca_privk(rng, dom_pars); + std::string hash("SHA-224"); + ASN1_Car car("DECVCA00001"); + EAC1_1_CVC cvca_cert = DE_EAC::create_cvca(cvca_privk, hash, car, true, true, 12, rng); + std::ofstream cvca_file(TEST_DATA_DIR "/cvc_chain_cvca.cer", std::ios::binary); + std::vector<byte> cvca_sv = cvca_cert.BER_encode(); + cvca_file.write((char*)&cvca_sv[0], cvca_sv.size()); + cvca_file.close(); + + ECDSA_PrivateKey cvca_privk2(rng, dom_pars); + ASN1_Car car2("DECVCA00002"); + EAC1_1_CVC cvca_cert2 = DE_EAC::create_cvca(cvca_privk2, hash, car2, true, true, 12, rng); + EAC1_1_CVC link12 = DE_EAC::link_cvca(cvca_cert, cvca_privk, cvca_cert2, rng); + std::vector<byte> link12_sv = link12.BER_encode(); + std::ofstream link12_file(TEST_DATA_DIR "/cvc_chain_link12.cer", std::ios::binary); + link12_file.write((char*)&link12_sv[0], link12_sv.size()); + link12_file.close(); + + // verify the link + CHECK(link12.check_signature(cvca_privk)); + EAC1_1_CVC link12_reloaded(TEST_DATA_DIR "/cvc_chain_link12.cer"); + EAC1_1_CVC cvca1_reloaded(TEST_DATA_DIR "/cvc_chain_cvca.cer"); + std::unique_ptr<Public_Key> cvca1_rel_pk(cvca1_reloaded.subject_public_key()); + CHECK(link12_reloaded.check_signature(*cvca1_rel_pk)); + + // create first round dvca-req + ECDSA_PrivateKey dvca_priv_key(rng, dom_pars); + EAC1_1_Req dvca_req = DE_EAC::create_cvc_req(dvca_priv_key, ASN1_Chr("DEDVCAEPASS"), hash, rng); + std::ofstream dvca_file(TEST_DATA_DIR "/cvc_chain_dvca_req.cer", std::ios::binary); + std::vector<byte> dvca_sv = dvca_req.BER_encode(); + dvca_file.write((char*)&dvca_sv[0], dvca_sv.size()); + dvca_file.close(); + + // sign the dvca_request + EAC1_1_CVC dvca_cert1 = DE_EAC::sign_request(cvca_cert, cvca_privk, dvca_req, 1, 5, true, 3, 1, rng); + CHECK(dvca_cert1.get_car().iso_8859() == "DECVCA00001"); + CHECK(dvca_cert1.get_chr().iso_8859() == "DEDVCAEPASS00001"); + helper_write_file(dvca_cert1, TEST_DATA_DIR "/cvc_chain_dvca_cert1.cer"); + + // make a second round dvca ado request + ECDSA_PrivateKey dvca_priv_key2(rng, dom_pars); + EAC1_1_Req dvca_req2 = DE_EAC::create_cvc_req(dvca_priv_key2, ASN1_Chr("DEDVCAEPASS"), hash, rng); + std::ofstream dvca_file2(TEST_DATA_DIR "/cvc_chain_dvca_req2.cer", std::ios::binary); + std::vector<byte> dvca_sv2 = dvca_req2.BER_encode(); + dvca_file2.write((char*)&dvca_sv2[0], dvca_sv2.size()); + dvca_file2.close(); + EAC1_1_ADO dvca_ado2 = CVC_EAC::create_ado_req(dvca_priv_key, dvca_req2, + ASN1_Car(dvca_cert1.get_chr().iso_8859()), rng); + helper_write_file(dvca_ado2, TEST_DATA_DIR "/cvc_chain_dvca_ado2.cer"); + + // verify the ado and sign the request too + + std::unique_ptr<Public_Key> ap_pk(dvca_cert1.subject_public_key()); + ECDSA_PublicKey* cert_pk = dynamic_cast<ECDSA_PublicKey*>(ap_pk.get()); + + //cert_pk->set_domain_parameters(dom_pars); + //std::cout << "dvca_cert.public_point.size() = " << ec::EC2OSP(cert_pk->get_public_point(), ec::PointGFp::COMPRESSED).size() << std::endl; + EAC1_1_CVC dvca_cert1_reread(TEST_DATA_DIR "/cvc_chain_cvca.cer"); + CHECK(dvca_ado2.check_signature(*cert_pk)); + + CHECK(dvca_ado2.check_signature(dvca_priv_key)); // must also work + + EAC1_1_Req dvca_req2b = dvca_ado2.get_request(); + helper_write_file(dvca_req2b, TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer"); + CHECK(helper_files_equal(TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer", TEST_DATA_DIR "/cvc_chain_dvca_req2.cer")); + EAC1_1_CVC dvca_cert2 = DE_EAC::sign_request(cvca_cert, cvca_privk, dvca_req2b, 2, 5, true, 3, 1, rng); + CHECK(dvca_cert2.get_car().iso_8859() == "DECVCA00001"); + CHECK_MESSAGE(dvca_cert2.get_chr().iso_8859() == "DEDVCAEPASS00002", + "chr = " << dvca_cert2.get_chr().iso_8859()); + + // make a first round IS request + ECDSA_PrivateKey is_priv_key(rng, dom_pars); + EAC1_1_Req is_req = DE_EAC::create_cvc_req(is_priv_key, ASN1_Chr("DEIS"), hash, rng); + helper_write_file(is_req, TEST_DATA_DIR "/cvc_chain_is_req.cer"); + + // sign the IS request + //dvca_cert1.set_domain_parameters(dom_pars); + EAC1_1_CVC is_cert1 = DE_EAC::sign_request(dvca_cert1, dvca_priv_key, is_req, 1, 5, true, 3, 1, rng); + CHECK_MESSAGE(is_cert1.get_car().iso_8859() == "DEDVCAEPASS00001", "car = " << is_cert1.get_car().iso_8859()); + CHECK(is_cert1.get_chr().iso_8859() == "DEIS00001"); + helper_write_file(is_cert1, TEST_DATA_DIR "/cvc_chain_is_cert.cer"); + + // verify the signature of the certificate + CHECK(is_cert1.check_signature(dvca_priv_key)); + } + +} + +size_t test_cvc() + { + AutoSeeded_RNG rng; + + test_enc_gen_selfsigned(rng); + test_enc_gen_req(rng); + test_cvc_req_ext(rng); + test_cvc_ado_ext(rng); + test_cvc_ado_creation(rng); + test_cvc_ado_comparison(rng); + test_eac_time(rng); + test_ver_cvca(rng); + test_copy_and_assignment(rng); + test_eac_str_illegal_values(rng); + test_tmp_eac_str_enc(rng); + test_cvc_chain(rng); + + return 0; + } +#else +size_t test_cvc() { return 0; } +#endif diff --git a/src/tests/test_eax.cpp b/src/tests/test_eax.cpp new file mode 100644 index 000000000..45b583139 --- /dev/null +++ b/src/tests/test_eax.cpp @@ -0,0 +1,229 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" +#include <fstream> +#include <iostream> +#include <sstream> +#include <boost/regex.hpp> + +#include <botan/eax.h> +#include <botan/hex.h> +#include <botan/lookup.h> + +using namespace Botan; + +namespace { + +unsigned from_string(const std::string& s) + { + std::istringstream stream(s); + unsigned n; + stream >> n; + return n; + } + +std::string seq(unsigned n) + { + std::string s; + + for(unsigned i = 0; i != n; ++i) + { + unsigned char b = (i & 0xFF); + + const char bin2hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + s += bin2hex[(b >> 4)]; + s += bin2hex[(b & 0x0f)]; + } + + return s; + } + +size_t eax_test(const std::string& algo, + const std::string& key_str, + const std::string& nonce_str, + const std::string& header_str, + const std::string& tag_str, + const std::string& plaintext_str, + const std::string& ciphertext) + { + size_t fail = 0; + + try + { + EAX_Encryption enc(get_block_cipher(algo)); + EAX_Decryption dec(get_block_cipher(algo)); + + enc.set_key(hex_decode(key_str)); + dec.set_key(hex_decode(key_str)); + + enc.set_associated_data_vec(hex_decode(header_str)); + dec.set_associated_data_vec(hex_decode(header_str)); + + secure_vector<byte> text = hex_decode_locked(plaintext_str); + enc.start_vec(hex_decode(nonce_str)); + enc.finish(text); + + const std::string produced = hex_encode(text); + + if(produced != ciphertext + tag_str) + { + std::cout << "EAX " << algo << " " << produced << " != expected " << ciphertext << tag_str << "\n"; + ++fail; + } + + text.clear(); + text = hex_decode_locked(ciphertext); + text += hex_decode_locked(tag_str); + + dec.start_vec(hex_decode(nonce_str)); + dec.finish(text); + + const std::string decrypted = hex_encode(text); + + if(decrypted != plaintext_str) + { + std::cout << "EAX " << algo << " " << decrypted << " != expected " << plaintext_str << "\n"; + ++fail; + } + } + catch(std::exception& e) + { + std::cout << "Exception during EAX test " << e.what() << "\n"; + ++fail; + } + + return fail; + } + +std::pair<std::string, int> translate_algo(const std::string& in) + { + if(in == "aes (16 byte key)") + return std::make_pair("AES-128", 16); + + if(in == "blowfish (8 byte key)") + return std::make_pair("Blowfish", 8); + + if(in == "rc2 (8 byte key)") + return std::make_pair("RC2", 8); + + if(in == "rc5 (8 byte key)") + return std::make_pair("RC5", 8); + + if(in == "rc6 (16 byte key)") + return std::make_pair("RC6", 16); + + if(in == "safer-sk128 (16 byte key)") + return std::make_pair("SAFER-SK(10)", 16); + + if(in == "twofish (16 byte key)") + return std::make_pair("Twofish", 16); + + if(in == "des (8 byte key)") + return std::make_pair("DES", 8); + + if(in == "3des (24 byte key)") + return std::make_pair("TripleDES", 24); + + // These 3 are disabled due to differences in base algorithm. + +#if 0 + // XTEA: LTC uses little endian, Botan (and Crypto++) use big-endian + // I swapped to LE in XTEA and the vectors did match + if(in == "xtea (16 byte key)") + return std::make_pair("XTEA", 16); + + // Skipjack: LTC uses big-endian, Botan (and Crypto++) use + // little-endian I am not sure if that was the full difference + // though, was unable to replicate LTC's EAX vectors with Skipjack + if(in == "skipjack (10 byte key)") + return std::make_pair("Skipjack", 10); + + // Noekeon: uses direct keying instead of indirect + if(in == "noekeon (16 byte key)") + return std::make_pair("Noekeon", 16); + +#endif + + return std::make_pair("", 0); + } + +std::string rep(const std::string& s_in, unsigned n) + { + std::string s_out; + + for(unsigned i = 0; i != n; ++i) + s_out += s_in[i % s_in.size()]; + + return s_out; + } + +size_t eax_tests(std::istream& in) + { + std::string algo; + std::string key; + + size_t fails = 0; + size_t tests = 0; + + while(in.good()) + { + std::string line; + + std::getline(in, line); + + if(line == "") + continue; + + if(line.size() > 5 && line.substr(0, 4) == "EAX-") + { + std::pair<std::string, int> name_and_keylen = + translate_algo(line.substr(4)); + + algo = name_and_keylen.first; + key = seq(name_and_keylen.second); + } + else if(algo != "") + { + boost::regex vec_regex("^([ 0-9]{3}): (.*), (.*)$"); + + boost::smatch what; + + if(boost::regex_match(line, what, vec_regex, boost::match_extra)) + { + unsigned n = from_string(what[1]); + std::string ciphertext = what[2]; + std::string tag = what[3]; + + std::string plaintext = seq(n); + std::string header = seq(n); + std::string nonce = seq(n); + + tests += 1; + + fails += eax_test(algo, key, nonce, header, tag, + plaintext, ciphertext); + + key = rep(tag, key.size()); // repeat as needed + } + } + } + + test_report("EAX", tests, fails); + + return fails; + } + +} + +size_t test_eax() + { + // Uses a set of tests created for libtomcrypt + std::ifstream in(CHECKS_DIR "/eax.vec"); + return eax_tests(in); + } diff --git a/src/tests/test_ecc.cpp b/src/tests/test_ecc.cpp new file mode 100644 index 000000000..929e44528 --- /dev/null +++ b/src/tests/test_ecc.cpp @@ -0,0 +1,842 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" + +#include <botan/hex.h> +#include <botan/auto_rng.h> +#include <botan/bigint.h> +#include <botan/numthry.h> +#include <botan/curve_gfp.h> +#include <botan/point_gfp.h> +#include <botan/ec_group.h> +#include <botan/reducer.h> +#include <botan/oids.h> +#include <iostream> +#include <memory> + +using namespace Botan; + +#define CHECK_MESSAGE(expr, print) try { if(!(expr)) { ++fails; std::cout << print << "\n"; }} catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } +#define CHECK(expr) try { if(!(expr)) { ++fails; std::cout << #expr << "\n"; } } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } + +namespace { + +std::ostream& operator<<(std::ostream& out, const PointGFp& point) + { + out << "(" << point.get_affine_x() << " " << point.get_affine_y() << ")"; + return out; + } + +PointGFp create_random_point(RandomNumberGenerator& rng, + const CurveGFp& curve) + { + const BigInt& p = curve.get_p(); + + Modular_Reducer mod_p(p); + + while(true) + { + BigInt x(rng, p.bits()); + + BigInt x3 = mod_p.multiply(x, mod_p.square(x)); + + BigInt ax = mod_p.multiply(curve.get_a(), x); + + BigInt bx3 = mod_p.multiply(curve.get_b(), x3); + + BigInt y = mod_p.reduce(ax + bx3); + + if(ressol(y, p) > 0) + return PointGFp(curve, x, y); + } + } + +size_t test_point_turn_on_sp_red_mul() + { + size_t fails = 0; + + // setting up expected values + BigInt exp_Qx(std::string("466448783855397898016055842232266600516272889280")); + BigInt exp_Qy(std::string("1110706324081757720403272427311003102474457754220")); + BigInt exp_Qz(1); + + // performing calculation to test + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode(p_secp); + std::vector<byte> sv_a_secp = hex_decode(a_secp); + std::vector<byte> sv_b_secp = hex_decode(b_secp); + std::vector<byte> sv_G_secp_comp = hex_decode(G_secp_comp); + BigInt bi_p_secp = BigInt::decode(&sv_p_secp[0], sv_p_secp.size()); + BigInt bi_a_secp = BigInt::decode(&sv_a_secp[0], sv_a_secp.size()); + BigInt bi_b_secp = BigInt::decode(&sv_b_secp[0], sv_b_secp.size()); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP(sv_G_secp_comp, secp160r1); + + BigInt d("459183204582304"); + + PointGFp r1 = d * p_G; + CHECK(r1.get_affine_x() != 0); + + PointGFp p_G2(p_G); + + PointGFp r2 = d * p_G2; + CHECK_MESSAGE(r1 == r2, "error with point mul after extra turn on sp red mul"); + CHECK(r1.get_affine_x() != 0); + + PointGFp p_r1 = r1; + PointGFp p_r2 = r2; + + p_r1 *= 2; + p_r2 *= 2; + CHECK_MESSAGE(p_r1.get_affine_x() == p_r2.get_affine_x(), "error with mult2 after extra turn on sp red mul"); + CHECK(p_r1.get_affine_x() != 0); + CHECK(p_r2.get_affine_x() != 0); + r1 *= 2; + + r2 *= 2; + + CHECK_MESSAGE(r1 == r2, "error with mult2 after extra turn on sp red mul"); + CHECK_MESSAGE(r1.get_affine_x() == r2.get_affine_x(), "error with mult2 after extra turn on sp red mul"); + CHECK(r1.get_affine_x() != 0); + r1 += p_G; + r2 += p_G2; + + CHECK_MESSAGE(r1 == r2, "error with op+= after extra turn on sp red mul"); + + r1 += p_G; + r2 += p_G2; + + CHECK_MESSAGE(r1 == r2, "error with op+= after extra turn on sp red mul for both operands"); + r1 += p_G; + r2 += p_G2; + + CHECK_MESSAGE(r1 == r2, "error with op+= after extra turn on sp red mul for both operands"); + return fails; + } + +size_t test_coordinates() + { + size_t fails = 0; + + BigInt exp_affine_x(std::string("16984103820118642236896513183038186009872590470")); + BigInt exp_affine_y(std::string("1373093393927139016463695321221277758035357890939")); + + // precalculation + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1 (bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + PointGFp p0 = p_G; + PointGFp p1 = p_G * 2; + PointGFp point_exp(secp160r1, exp_affine_x, exp_affine_y); + if(!point_exp.on_the_curve()) + throw Internal_Error("Point not on the curve"); + + CHECK_MESSAGE( p1.get_affine_x() == exp_affine_x, " p1_x = " << p1.get_affine_x() << "\n" << "exp_x = " << exp_affine_x << "\n"); + CHECK_MESSAGE( p1.get_affine_y() == exp_affine_y, " p1_y = " << p1.get_affine_y() << "\n" << "exp_y = " << exp_affine_y << "\n"); + return fails; + } + + +/** +Test point multiplication according to +-------- +SEC 2: Test Vectors for SEC 1 +Certicom Research +Working Draft +September, 1999 +Version 0.3; +Section 2.1.2 +-------- +*/ + +size_t test_point_transformation () + { + size_t fails = 0; + + // get a vailid point + EC_Group dom_pars(OID("1.3.132.0.8")); + PointGFp p = dom_pars.get_base_point(); + + // get a copy + PointGFp q = p; + + CHECK_MESSAGE( p.get_affine_x() == q.get_affine_x(), "affine_x changed during copy"); + CHECK_MESSAGE( p.get_affine_y() == q.get_affine_y(), "affine_y changed during copy"); + return fails; + } + +size_t test_point_mult () + { + size_t fails = 0; + + EC_Group secp160r1(OIDS::lookup("secp160r1")); + + const CurveGFp& curve = secp160r1.get_curve(); + + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_G_secp_comp = hex_decode(G_secp_comp); + PointGFp p_G = OS2ECP(sv_G_secp_comp, curve); + + BigInt d_U("0xaa374ffc3ce144e6b073307972cb6d57b2a4e982"); + PointGFp Q_U = d_U * p_G; + + CHECK(Q_U.get_affine_x() == BigInt("466448783855397898016055842232266600516272889280")); + CHECK(Q_U.get_affine_y() == BigInt("1110706324081757720403272427311003102474457754220")); + return fails; + } + +size_t test_point_negative() + { + size_t fails = 0; + + // performing calculation to test + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + + PointGFp p1 = p_G *= 2; + + CHECK(p1.get_affine_x() == BigInt("16984103820118642236896513183038186009872590470")); + CHECK(p1.get_affine_y() == BigInt("1373093393927139016463695321221277758035357890939")); + + PointGFp p1_neg = p1.negate(); + + CHECK(p1_neg.get_affine_x() == BigInt("16984103820118642236896513183038186009872590470")); + CHECK(p1_neg.get_affine_y() == BigInt("88408243403763901739989511495005261618427168388")); + return fails; + } + +size_t test_zeropoint() + { + size_t fails = 0; + + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp("0xffffffffffffffffffffffffffffffff7fffffff"); + BigInt bi_a_secp("0xffffffffffffffffffffffffffffffff7ffffffc"); + BigInt bi_b_secp("0x1c97befc54bd7a8b65acf89f81d4d4adc565fa45"); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p1(secp160r1, + BigInt("16984103820118642236896513183038186009872590470"), + BigInt("1373093393927139016463695321221277758035357890939")); + + if(!p1.on_the_curve()) + throw Internal_Error("Point not on the curve"); + p1 -= p1; + + CHECK_MESSAGE( p1.is_zero(), "p - q with q = p is not zero!"); + return fails; + } + +size_t test_zeropoint_enc_dec() + { + size_t fails = 0; + + BigInt bi_p_secp("0xffffffffffffffffffffffffffffffff7fffffff"); + BigInt bi_a_secp("0xffffffffffffffffffffffffffffffff7ffffffc"); + BigInt bi_b_secp("0x1c97befc54bd7a8b65acf89f81d4d4adc565fa45"); + CurveGFp curve(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p(curve); + CHECK_MESSAGE( p.is_zero(), "by constructor created zeropoint is no zeropoint!"); + + + std::vector<byte> sv_p = unlock(EC2OSP(p, PointGFp::UNCOMPRESSED)); + PointGFp p_encdec = OS2ECP(sv_p, curve); + CHECK_MESSAGE( p == p_encdec, "encoded-decoded (uncompressed) point is not equal the original!"); + + sv_p = unlock(EC2OSP(p, PointGFp::UNCOMPRESSED)); + p_encdec = OS2ECP(sv_p, curve); + CHECK_MESSAGE( p == p_encdec, "encoded-decoded (compressed) point is not equal the original!"); + + sv_p = unlock(EC2OSP(p, PointGFp::HYBRID)); + p_encdec = OS2ECP(sv_p, curve); + CHECK_MESSAGE( p == p_encdec, "encoded-decoded (hybrid) point is not equal the original!"); + return fails; + } + +size_t test_calc_with_zeropoint() + { + size_t fails = 0; + + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp("0xffffffffffffffffffffffffffffffff7fffffff"); + BigInt bi_a_secp("0xffffffffffffffffffffffffffffffff7ffffffc"); + BigInt bi_b_secp("0x1c97befc54bd7a8b65acf89f81d4d4adc565fa45"); + CurveGFp curve(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p(curve, + BigInt("16984103820118642236896513183038186009872590470"), + BigInt("1373093393927139016463695321221277758035357890939")); + + if(!p.on_the_curve()) + throw Internal_Error("Point not on the curve"); + CHECK_MESSAGE( !p.is_zero(), "created is zeropoint, shouldn't be!"); + + PointGFp zero(curve); + CHECK_MESSAGE( zero.is_zero(), "by constructor created zeropoint is no zeropoint!"); + + PointGFp res = p + zero; + CHECK_MESSAGE( res == p, "point + zeropoint is not equal the point"); + + res = p - zero; + CHECK_MESSAGE( res == p, "point - zeropoint is not equal the point"); + + res = zero * 32432243; + CHECK_MESSAGE( res.is_zero(), "zeropoint * skalar is not a zero-point!"); + return fails; + } + +size_t test_add_point() + { + size_t fails = 0; + + // precalculation + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + + PointGFp p0 = p_G; + PointGFp p1 = p_G *= 2; + + p1 += p0; + + PointGFp expected(secp160r1, + BigInt("704859595002530890444080436569091156047721708633"), + BigInt("1147993098458695153857594941635310323215433166682")); + + CHECK(p1 == expected); + return fails; + } + +size_t test_sub_point() + { + size_t fails = 0; + + //Setting up expected values + BigInt exp_sub_x(std::string("112913490230515010376958384252467223283065196552")); + BigInt exp_sub_y(std::string("143464803917389475471159193867377888720776527730")); + BigInt exp_sub_z(std::string("562006223742588575209908669014372619804457947208")); + + // precalculation + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + + PointGFp p0 = p_G; + PointGFp p1 = p_G *= 2; + + p1 -= p0; + + PointGFp expected(secp160r1, + BigInt("425826231723888350446541592701409065913635568770"), + BigInt("203520114162904107873991457957346892027982641970")); + + CHECK(p1 == expected); + return fails; + } + +size_t test_mult_point() + { + size_t fails = 0; + + //Setting up expected values + BigInt exp_mult_x(std::string("967697346845926834906555988570157345422864716250")); + BigInt exp_mult_y(std::string("512319768365374654866290830075237814703869061656")); + + // precalculation + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + + PointGFp p0 = p_G; + PointGFp p1 = p_G *= 2; + + p1 *= p0.get_affine_x(); + + PointGFp expected(secp160r1, exp_mult_x, exp_mult_y); + + CHECK(p1 == expected); + return fails; + } + +size_t test_basic_operations() + { + size_t fails = 0; + + // precalculation + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffc"; + std::string b_secp = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; + std::string G_secp_comp = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + + PointGFp p0 = p_G; + + PointGFp expected(secp160r1, + BigInt("425826231723888350446541592701409065913635568770"), + BigInt("203520114162904107873991457957346892027982641970")); + + CHECK(p0 == expected); + + PointGFp p1 = p_G *= 2; + + CHECK(p1.get_affine_x() == BigInt("16984103820118642236896513183038186009872590470")); + CHECK(p1.get_affine_y() == BigInt("1373093393927139016463695321221277758035357890939")); + + PointGFp simplePlus= p1 + p0; + PointGFp exp_simplePlus(secp160r1, + BigInt("704859595002530890444080436569091156047721708633"), + BigInt("1147993098458695153857594941635310323215433166682")); + if(simplePlus != exp_simplePlus) + std::cout << simplePlus << " != " << exp_simplePlus << "\n"; + + PointGFp simpleMinus= p1 - p0; + PointGFp exp_simpleMinus(secp160r1, + BigInt("425826231723888350446541592701409065913635568770"), + BigInt("203520114162904107873991457957346892027982641970")); + + CHECK(simpleMinus == exp_simpleMinus); + + PointGFp simpleMult= p1 * 123456789; + + CHECK(simpleMult.get_affine_x() == BigInt("43638877777452195295055270548491599621118743290")); + CHECK(simpleMult.get_affine_y() == BigInt("56841378500012376527163928510402662349220202981")); + + // check that all initial points hasn't changed + CHECK(p1.get_affine_x() == BigInt("16984103820118642236896513183038186009872590470")); + CHECK(p1.get_affine_y() == BigInt("1373093393927139016463695321221277758035357890939")); + + CHECK(p0.get_affine_x() == BigInt("425826231723888350446541592701409065913635568770")); + CHECK(p0.get_affine_y() == BigInt("203520114162904107873991457957346892027982641970")); + return fails; + } + +size_t test_enc_dec_compressed_160() + { + size_t fails = 0; + + // Test for compressed conversion (02/03) 160bit + std::string p_secp = "ffffffffffffffffffffffffffffffff7fffffff"; + std::string a_secp = "ffffffffffffffffffffffffffffffff7ffffffC"; + std::string b_secp = "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"; + std::string G_secp_comp = "024A96B5688EF573284664698968C38BB913CBFC82"; + std::string G_order_secp_comp = "0100000000000000000001F4C8F927AED3CA752257"; + + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + std::vector<byte> sv_result = unlock(EC2OSP(p_G, PointGFp::COMPRESSED)); + + CHECK( sv_result == sv_G_secp_comp); + return fails; + } + +size_t test_enc_dec_compressed_256() + { + size_t fails = 0; + + // Test for compressed conversion (02/03) 256bit + std::string p_secp = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"; + std::string a_secp = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffFC"; + std::string b_secp = "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"; + std::string G_secp_comp = "036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"; + std::string G_order_secp_comp = "ffffffff00000000ffffffffffffffffBCE6FAADA7179E84F3B9CAC2FC632551"; + + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_comp = hex_decode ( G_secp_comp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p_G = OS2ECP ( sv_G_secp_comp, secp160r1 ); + std::vector<byte> sv_result = unlock(EC2OSP(p_G, PointGFp::COMPRESSED)); + + CHECK( sv_result == sv_G_secp_comp); + return fails; + } + + +size_t test_enc_dec_uncompressed_112() + { + size_t fails = 0; + + // Test for uncompressed conversion (04) 112bit + + std::string p_secp = "db7c2abf62e35e668076bead208b"; + std::string a_secp = "6127C24C05F38A0AAAF65C0EF02C"; + std::string b_secp = "51DEF1815DB5ED74FCC34C85D709"; + std::string G_secp_uncomp = "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97"; + std::string G_order_secp_uncomp = "36DF0AAFD8B8D7597CA10520D04B"; + + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_uncomp = hex_decode ( G_secp_uncomp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p_G = OS2ECP ( sv_G_secp_uncomp, secp160r1 ); + std::vector<byte> sv_result = unlock(EC2OSP(p_G, PointGFp::UNCOMPRESSED)); + + CHECK( sv_result == sv_G_secp_uncomp); + return fails; + } + +size_t test_enc_dec_uncompressed_521() + { + size_t fails = 0; + + // Test for uncompressed conversion(04) with big values(521 bit) + std::string p_secp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + std::string a_secp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffFC"; + std::string b_secp = "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00"; + std::string G_secp_uncomp = "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2ffA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"; + std::string G_order_secp_uncomp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"; + + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_uncomp = hex_decode ( G_secp_uncomp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + + CurveGFp secp160r1(bi_p_secp, bi_a_secp, bi_b_secp); + + PointGFp p_G = OS2ECP ( sv_G_secp_uncomp, secp160r1 ); + + std::vector<byte> sv_result = unlock(EC2OSP(p_G, PointGFp::UNCOMPRESSED)); + std::string result = hex_encode(&sv_result[0], sv_result.size()); + std::string exp_result = hex_encode(&sv_G_secp_uncomp[0], sv_G_secp_uncomp.size()); + + CHECK_MESSAGE( sv_result == sv_G_secp_uncomp, "\ncalc. result = " << result << "\nexp. result = " << exp_result << "\n"); + return fails; + } + +size_t test_enc_dec_uncompressed_521_prime_too_large() + { + size_t fails = 0; + + // Test for uncompressed conversion(04) with big values(521 bit) + std::string p_secp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; // length increased by "ff" + std::string a_secp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffFC"; + std::string b_secp = "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00"; + std::string G_secp_uncomp = "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2ffA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"; + std::string G_order_secp_uncomp = "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"; + + std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + std::vector<byte> sv_a_secp = hex_decode ( a_secp ); + std::vector<byte> sv_b_secp = hex_decode ( b_secp ); + std::vector<byte> sv_G_secp_uncomp = hex_decode ( G_secp_uncomp ); + + BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + + CurveGFp secp521r1 (bi_p_secp, bi_a_secp, bi_b_secp); + std::unique_ptr<PointGFp> p_G; + bool exc = false; + try + { + p_G = std::unique_ptr<PointGFp>(new PointGFp(OS2ECP ( sv_G_secp_uncomp, secp521r1))); + if(!p_G->on_the_curve()) + throw Internal_Error("Point not on the curve"); + } + catch (std::exception e) + { + exc = true; + } + + CHECK_MESSAGE(exc, "attempt of creation of point on curve with too high prime did not throw an exception"); + return fails; + } + +size_t test_gfp_store_restore() + { + size_t fails = 0; + + // generate point + //EC_Group dom_pars = global_config().get_ec_dompar("1.3.132.0.8"); + //EC_Group dom_pars("1.3.132.0.8"); + EC_Group dom_pars(OID("1.3.132.0.8")); + PointGFp p = dom_pars.get_base_point(); + + //store point (to std::string) + std::vector<byte> sv_mes = unlock(EC2OSP(p, PointGFp::COMPRESSED)); + PointGFp new_p = OS2ECP(sv_mes, dom_pars.get_curve()); + + CHECK_MESSAGE( p == new_p, "original and restored point are different!"); + return fails; + } + + +// maybe move this test +size_t test_cdc_curve_33() + { + size_t fails = 0; + + std::string G_secp_uncomp = "04081523d03d4f12cd02879dea4bf6a4f3a7df26ed888f10c5b2235a1274c386a2f218300dee6ed217841164533bcdc903f07a096f9fbf4ee95bac098a111f296f5830fe5c35b3e344d5df3a2256985f64fbe6d0edcc4c61d18bef681dd399df3d0194c5a4315e012e0245ecea56365baa9e8be1f7"; + + std::vector<byte> sv_G_uncomp = hex_decode ( G_secp_uncomp ); + + BigInt bi_p_secp = BigInt("2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"); + BigInt bi_a_secp("0xa377dede6b523333d36c78e9b0eaa3bf48ce93041f6d4fc34014d08f6833807498deedd4290101c5866e8dfb589485d13357b9e78c2d7fbe9fe"); + BigInt bi_b_secp("0xa9acf8c8ba617777e248509bcb4717d4db346202bf9e352cd5633731dd92a51b72a4dc3b3d17c823fcc8fbda4da08f25dea89046087342595a7"); + + CurveGFp curve(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_uncomp, curve); + bool exc = false; + try + { + if(!p_G.on_the_curve()) + throw Internal_Error("Point not on the curve"); + } + catch (std::exception) + { + exc = true; + } + CHECK(!exc); + return fails; + } + +size_t test_more_zeropoint() + { + size_t fails = 0; + + // by Falko + + std::string G = "024a96b5688ef573284664698968c38bb913cbfc82"; + std::vector<byte> sv_G_secp_comp = hex_decode ( G ); + BigInt bi_p("0xffffffffffffffffffffffffffffffff7fffffff"); + BigInt bi_a("0xffffffffffffffffffffffffffffffff7ffffffc"); + BigInt bi_b("0x1c97befc54bd7a8b65acf89f81d4d4adc565fa45"); + CurveGFp curve(bi_p, bi_a, bi_b); + + PointGFp p1(curve, + BigInt("16984103820118642236896513183038186009872590470"), + BigInt("1373093393927139016463695321221277758035357890939")); + + if(!p1.on_the_curve()) + throw Internal_Error("Point not on the curve"); + PointGFp minus_p1 = -p1; + if(!minus_p1.on_the_curve()) + throw Internal_Error("Point not on the curve"); + PointGFp shouldBeZero = p1 + minus_p1; + if(!shouldBeZero.on_the_curve()) + throw Internal_Error("Point not on the curve"); + + BigInt y1 = p1.get_affine_y(); + y1 = curve.get_p() - y1; + + CHECK_MESSAGE(p1.get_affine_x() == minus_p1.get_affine_x(), + "problem with minus_p1 : x"); + CHECK_MESSAGE(minus_p1.get_affine_y() == y1, + "problem with minus_p1 : y"); + + PointGFp zero(curve); + if(!zero.on_the_curve()) + throw Internal_Error("Point not on the curve"); + CHECK_MESSAGE(p1 + zero == p1, "addition of zero modified point"); + + CHECK_MESSAGE( shouldBeZero.is_zero(), "p - q with q = p is not zero!"); + return fails; + } + +size_t test_mult_by_order() + { + size_t fails = 0; + + // generate point + EC_Group dom_pars(OID("1.3.132.0.8")); + PointGFp p = dom_pars.get_base_point(); + PointGFp shouldBeZero = p * dom_pars.get_order(); + + CHECK_MESSAGE(shouldBeZero.is_zero(), "G * order != O"); + return fails; + } + +size_t test_point_swap(RandomNumberGenerator& rng) + { + size_t fails = 0; + + + EC_Group dom_pars(OID("1.3.132.0.8")); + + PointGFp a(create_random_point(rng, dom_pars.get_curve())); + PointGFp b(create_random_point(rng, dom_pars.get_curve())); + b *= BigInt(20); + + PointGFp c(a); + PointGFp d(b); + + d.swap(c); + CHECK(a == d); + CHECK(b == c); + return fails; + } + +/** +* This test verifies that the side channel attack resistant multiplication function +* yields the same result as the normal (insecure) multiplication via operator*= +*/ +size_t test_mult_sec_mass(RandomNumberGenerator& rng) + { + size_t fails = 0; + + + EC_Group dom_pars(OID("1.3.132.0.8")); + for(int i = 0; i<50; i++) + { + PointGFp a(create_random_point(rng, dom_pars.get_curve())); + BigInt scal(BigInt(rng, 40)); + PointGFp b = a * scal; + PointGFp c(a); + + c *= scal; + CHECK(b == c); + } + return fails; + } + +size_t test_curve_cp_ctor() + { + try + { + EC_Group dom_pars(OID("1.3.132.0.8")); + CurveGFp curve(dom_pars.get_curve()); + } + catch(...) + { + return 1; + + } + + return 0; + } + +} + +size_t test_ecc() + { + AutoSeeded_RNG rng; + + size_t fails = 0; + + fails += test_point_turn_on_sp_red_mul(); + fails += test_coordinates(); + fails += test_point_transformation (); + fails += test_point_mult (); + fails += test_point_negative(); + fails += test_zeropoint(); + fails += test_zeropoint_enc_dec(); + fails += test_calc_with_zeropoint(); + fails += test_add_point(); + fails += test_sub_point(); + fails += test_mult_point(); + fails += test_basic_operations(); + fails += test_enc_dec_compressed_160(); + fails += test_enc_dec_compressed_256(); + fails += test_enc_dec_uncompressed_112(); + fails += test_enc_dec_uncompressed_521(); + fails += test_enc_dec_uncompressed_521_prime_too_large(); + fails += test_gfp_store_restore(); + fails += test_cdc_curve_33(); + fails += test_more_zeropoint(); + fails += test_mult_by_order(); + fails += test_point_swap(rng); + fails += test_mult_sec_mass(rng); + fails += test_curve_cp_ctor(); + + test_report("ECC", 0, fails); + + return fails; + } diff --git a/src/tests/test_ecdh.cpp b/src/tests/test_ecdh.cpp new file mode 100644 index 000000000..5eb5da586 --- /dev/null +++ b/src/tests/test_ecdh.cpp @@ -0,0 +1,132 @@ +/* +* ECDH tests +* +* (C) 2007 Manuel Hartl ([email protected]) +* 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" + +#include <iostream> +#include <fstream> + +#include <botan/auto_rng.h> +#include <botan/pubkey.h> +#include <botan/ecdh.h> +#include <botan/x509self.h> +#include <botan/der_enc.h> + +using namespace Botan; + +#define CHECK_MESSAGE(expr, print) try { if(!(expr)) { ++fails; std::cout << print << "\n"; } } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } +#define CHECK(expr) try { if(!(expr)) { ++fails; std::cout << #expr << "\n"; } } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } + +namespace { + +size_t test_ecdh_normal_derivation(RandomNumberGenerator& rng) + { + size_t fails = 0; + + EC_Group dom_pars(OID("1.3.132.0.8")); + + ECDH_PrivateKey private_a(rng, dom_pars); + + ECDH_PrivateKey private_b(rng, dom_pars); //public_a.getCurve() + + PK_Key_Agreement ka(private_a, "KDF2(SHA-1)"); + PK_Key_Agreement kb(private_b, "KDF2(SHA-1)"); + + SymmetricKey alice_key = ka.derive_key(32, private_b.public_value()); + SymmetricKey bob_key = kb.derive_key(32, private_a.public_value()); + + if(alice_key != bob_key) + { + std::cout << "The two keys didn't match!\n"; + std::cout << "Alice's key was: " << alice_key.as_string() << "\n"; + std::cout << "Bob's key was: " << bob_key.as_string() << "\n"; + ++fails; + } + + return fails; + } + +size_t test_ecdh_some_dp(RandomNumberGenerator& rng) + { + size_t fails = 0; + + std::vector<std::string> oids; + oids.push_back("1.2.840.10045.3.1.7"); + oids.push_back("1.3.132.0.8"); + oids.push_back("1.2.840.10045.3.1.1"); + + for(u32bit i = 0; i< oids.size(); i++) + { + OID oid(oids[i]); + EC_Group dom_pars(oid); + + ECDH_PrivateKey private_a(rng, dom_pars); + ECDH_PrivateKey private_b(rng, dom_pars); + + PK_Key_Agreement ka(private_a, "KDF2(SHA-1)"); + PK_Key_Agreement kb(private_b, "KDF2(SHA-1)"); + + SymmetricKey alice_key = ka.derive_key(32, private_b.public_value()); + SymmetricKey bob_key = kb.derive_key(32, private_a.public_value()); + + CHECK_MESSAGE(alice_key == bob_key, "different keys - " << "Alice's key was: " << alice_key.as_string() << ", Bob's key was: " << bob_key.as_string()); + } + + return fails; + } + +size_t test_ecdh_der_derivation(RandomNumberGenerator& rng) + { + size_t fails = 0; + + std::vector<std::string> oids; + oids.push_back("1.2.840.10045.3.1.7"); + oids.push_back("1.3.132.0.8"); + oids.push_back("1.2.840.10045.3.1.1"); + + for(u32bit i = 0; i< oids.size(); i++) + { + OID oid(oids[i]); + EC_Group dom_pars(oid); + + ECDH_PrivateKey private_a(rng, dom_pars); + ECDH_PrivateKey private_b(rng, dom_pars); + + std::vector<byte> key_a = private_a.public_value(); + std::vector<byte> key_b = private_b.public_value(); + + PK_Key_Agreement ka(private_a, "KDF2(SHA-1)"); + PK_Key_Agreement kb(private_b, "KDF2(SHA-1)"); + + SymmetricKey alice_key = ka.derive_key(32, key_b); + SymmetricKey bob_key = kb.derive_key(32, key_a); + + CHECK_MESSAGE(alice_key == bob_key, "different keys - " << "Alice's key was: " << alice_key.as_string() << ", Bob's key was: " << bob_key.as_string()); + + } + + return fails; + } + +} + +size_t test_ecdh() + { + size_t fails = 0; + + AutoSeeded_RNG rng; + + fails += test_ecdh_normal_derivation(rng); + fails += test_ecdh_some_dp(rng); + fails += test_ecdh_der_derivation(rng); + + test_report("ECDH", 3, fails); + + return fails; + } diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp new file mode 100644 index 000000000..b557f0193 --- /dev/null +++ b/src/tests/test_ecdsa.cpp @@ -0,0 +1,490 @@ +/****************************************************** +* ECDSA tests * +* * +* (C) 2007 Falko Strenzke * +* Manuel Hartl * +* 2008 Jack Lloyd * +******************************************************/ + +#include "tests.h" + +#include <botan/hex.h> +#include <botan/auto_rng.h> +#include <botan/pubkey.h> +#include <botan/ecdsa.h> +#include <botan/rsa.h> +#include <botan/x509cert.h> +#include <botan/oids.h> + +#include <iostream> +#include <fstream> +#include <memory> + +using namespace Botan; + +#define TEST_DATA_DIR CHECKS_DIR "/ecc_testdata" + +#define CHECK_MESSAGE(expr, print) try { if(!(expr)) { ++fails; std::cout << print << "\n"; } } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } +#define CHECK(expr) try { if(!(expr)) { ++fails; std::cout << #expr << "\n"; } } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << "\n"; } + +namespace { + +std::string to_hex(const std::vector<byte>& bin) + { + return hex_encode(&bin[0], bin.size()); + } + +/** + +* Tests whether the the signing routine will work correctly in case +* the integer e that is constructed from the message (thus the hash +* value) is larger than n, the order of the base point. Tests the +* signing function of the pk signer object */ + +size_t test_hash_larger_than_n(RandomNumberGenerator& rng) + { + EC_Group dom_pars(OID("1.3.132.0.8")); // secp160r1 + // n = 0x0100000000000000000001f4c8f927aed3ca752257 (21 bytes) + // -> shouldn't work with SHA224 which outputs 28 bytes + + size_t fails = 0; + ECDSA_PrivateKey priv_key(rng, dom_pars); + + std::vector<byte> message(20); + for(size_t i = 0; i != message.size(); ++i) + message[i] = i; + + PK_Signer pk_signer_160(priv_key, "EMSA1_BSI(SHA-1)"); + PK_Verifier pk_verifier_160(priv_key, "EMSA1_BSI(SHA-1)"); + + PK_Signer pk_signer_224(priv_key, "EMSA1_BSI(SHA-224)"); + + // Verify we can sign and verify with SHA-160 + std::vector<byte> signature_160 = pk_signer_160.sign_message(message, rng); + + CHECK(pk_verifier_160.verify_message(message, signature_160)); + + bool signature_failed = false; + try + { + std::vector<byte> signature_224 = pk_signer_224.sign_message(message, rng); + } + catch(Encoding_Error) + { + signature_failed = true; + } + + CHECK(signature_failed); + + // now check that verification alone fails + + // sign it with the normal EMSA1 + PK_Signer pk_signer(priv_key, "EMSA1(SHA-224)"); + std::vector<byte> signature = pk_signer.sign_message(message, rng); + + PK_Verifier pk_verifier(priv_key, "EMSA1_BSI(SHA-224)"); + + // verify against EMSA1_BSI + if(pk_verifier.verify_message(message, signature)) + { + std::cout << "Corrupt ECDSA signature verified, should not have\n"; + ++fails; + } + + return fails; + } + +size_t test_decode_ecdsa_X509() + { + X509_Certificate cert(TEST_DATA_DIR "/CSCA.CSCA.csca-germany.1.crt"); + size_t fails = 0; + + CHECK_MESSAGE(OIDS::lookup(cert.signature_algorithm().oid) == "ECDSA/EMSA1(SHA-224)", "error reading signature algorithm from x509 ecdsa certificate"); + + CHECK_MESSAGE(to_hex(cert.serial_number()) == "01", "error reading serial from x509 ecdsa certificate"); + CHECK_MESSAGE(to_hex(cert.authority_key_id()) == "0096452DE588F966C4CCDF161DD1F3F5341B71E7", "error reading authority key id from x509 ecdsa certificate"); + CHECK_MESSAGE(to_hex(cert.subject_key_id()) == "0096452DE588F966C4CCDF161DD1F3F5341B71E7", "error reading Subject key id from x509 ecdsa certificate"); + + std::unique_ptr<X509_PublicKey> pubkey(cert.subject_public_key()); + bool ver_ec = cert.check_signature(*pubkey); + CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned x509-ecdsa certificate"); + + return fails; + } + +size_t test_decode_ver_link_SHA256() + { + X509_Certificate root_cert(TEST_DATA_DIR "/root2_SHA256.cer"); + X509_Certificate link_cert(TEST_DATA_DIR "/link_SHA256.cer"); + + size_t fails = 0; + std::unique_ptr<X509_PublicKey> pubkey(root_cert.subject_public_key()); + bool ver_ec = link_cert.check_signature(*pubkey); + CHECK_MESSAGE(ver_ec, "could not positively verify correct SHA256 link x509-ecdsa certificate"); + return fails; + } + +size_t test_decode_ver_link_SHA1() + { + X509_Certificate root_cert(TEST_DATA_DIR "/root_SHA1.163.crt"); + X509_Certificate link_cert(TEST_DATA_DIR "/link_SHA1.166.crt"); + + size_t fails = 0; + std::unique_ptr<X509_PublicKey> pubkey(root_cert.subject_public_key()); + bool ver_ec = link_cert.check_signature(*pubkey); + CHECK_MESSAGE(ver_ec, "could not positively verify correct SHA1 link x509-ecdsa certificate"); + return fails; + } + +size_t test_sign_then_ver(RandomNumberGenerator& rng) + { + EC_Group dom_pars(OID("1.3.132.0.8")); + ECDSA_PrivateKey ecdsa(rng, dom_pars); + + size_t fails = 0; + PK_Signer signer(ecdsa, "EMSA1(SHA-1)"); + + auto msg = hex_decode("12345678901234567890abcdef12"); + std::vector<byte> sig = signer.sign_message(msg, rng); + + PK_Verifier verifier(ecdsa, "EMSA1(SHA-1)"); + + bool ok = verifier.verify_message(msg, sig); + + if(!ok) + { + std::cout << "ERROR: Could not verify ECDSA signature\n"; + fails++; + } + + sig[0]++; + ok = verifier.verify_message(msg, sig); + + if(ok) + { + std::cout << "ERROR: Bogus ECDSA signature verified anyway\n"; + fails++; + } + + return fails; + } + +size_t test_ec_sign(RandomNumberGenerator& rng) + { + size_t fails = 0; + + try + { + EC_Group dom_pars(OID("1.3.132.0.8")); + ECDSA_PrivateKey priv_key(rng, dom_pars); + std::string pem_encoded_key = PKCS8::PEM_encode(priv_key); + + PK_Signer signer(priv_key, "EMSA1(SHA-224)"); + PK_Verifier verifier(priv_key, "EMSA1(SHA-224)"); + + for(size_t i = 0; i != 256; ++i) + signer.update(static_cast<byte>(i)); + std::vector<byte> sig = signer.signature(rng); + + for(u32bit i = 0; i != 256; ++i) + verifier.update(static_cast<byte>(i)); + if(!verifier.check_signature(sig)) + { + std::cout << "ECDSA self-test failed!"; + ++fails; + } + + // now check valid signature, different input + for(u32bit i = 1; i != 256; ++i) //starting from 1 + verifier.update(static_cast<byte>(i)); + + if(verifier.check_signature(sig)) + { + std::cout << "ECDSA with bad input passed validation"; + ++fails; + } + + // now check with original input, modified signature + + sig[sig.size()/2]++; + for(u32bit i = 0; i != 256; ++i) + verifier.update(static_cast<byte>(i)); + + if(verifier.check_signature(sig)) + { + std::cout << "ECDSA with bad signature passed validation"; + ++fails; + } + } + catch (std::exception& e) + { + std::cout << "Exception in test_ec_sign - " << e.what() << "\n"; + ++fails; + } + + return fails; + } + + +size_t test_create_pkcs8(RandomNumberGenerator& rng) + { + size_t fails = 0; + + try + { + RSA_PrivateKey rsa_key(rng, 1024); + //RSA_PrivateKey rsa_key2(1024); + //cout << "\nequal: " << (rsa_key == rsa_key2) << "\n"; + //DSA_PrivateKey key(DL_Group("dsa/jce/1024")); + + std::ofstream rsa_priv_key(TEST_DATA_DIR "/rsa_private.pkcs8.pem"); + rsa_priv_key << PKCS8::PEM_encode(rsa_key); + + EC_Group dom_pars(OID("1.3.132.0.8")); + ECDSA_PrivateKey key(rng, dom_pars); + + // later used by other tests :( + std::ofstream priv_key(TEST_DATA_DIR "/wo_dompar_private.pkcs8.pem"); + priv_key << PKCS8::PEM_encode(key); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + ++fails; + } + + return fails; + } + +size_t test_create_and_verify(RandomNumberGenerator& rng) + { + size_t fails = 0; + + EC_Group dom_pars(OID("1.3.132.0.8")); + ECDSA_PrivateKey key(rng, dom_pars); + std::ofstream priv_key(TEST_DATA_DIR "/dompar_private.pkcs8.pem"); + priv_key << PKCS8::PEM_encode(key); + + std::unique_ptr<PKCS8_PrivateKey> loaded_key(PKCS8::load_key(TEST_DATA_DIR "/wo_dompar_private.pkcs8.pem", rng)); + ECDSA_PrivateKey* loaded_ec_key = dynamic_cast<ECDSA_PrivateKey*>(loaded_key.get()); + CHECK_MESSAGE(loaded_ec_key, "the loaded key could not be converted into an ECDSA_PrivateKey"); + + std::unique_ptr<PKCS8_PrivateKey> loaded_key_1(PKCS8::load_key(TEST_DATA_DIR "/rsa_private.pkcs8.pem", rng)); + ECDSA_PrivateKey* loaded_rsa_key = dynamic_cast<ECDSA_PrivateKey*>(loaded_key_1.get()); + CHECK_MESSAGE(!loaded_rsa_key, "the loaded key is ECDSA_PrivateKey -> shouldn't be, is a RSA-Key"); + + //calc a curve which is not in the registry + + // string p_secp = "2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"; + std::string a_secp = "0a377dede6b523333d36c78e9b0eaa3bf48ce93041f6d4fc34014d08f6833807498deedd4290101c5866e8dfb589485d13357b9e78c2d7fbe9fe"; + std::string b_secp = "0a9acf8c8ba617777e248509bcb4717d4db346202bf9e352cd5633731dd92a51b72a4dc3b3d17c823fcc8fbda4da08f25dea89046087342595a7"; + std::string G_secp_comp = "04081523d03d4f12cd02879dea4bf6a4f3a7df26ed888f10c5b2235a1274c386a2f218300dee6ed217841164533bcdc903f07a096f9fbf4ee95bac098a111f296f5830fe5c35b3e344d5df3a2256985f64fbe6d0edcc4c61d18bef681dd399df3d0194c5a4315e012e0245ecea56365baa9e8be1f7"; + std::string order_g = "0e1a16196e6000000000bc7f1618d867b15bb86474418f"; + + // ::std::vector<byte> sv_p_secp = hex_decode ( p_secp ); + auto sv_a_secp = hex_decode ( a_secp ); + auto sv_b_secp = hex_decode ( b_secp ); + auto sv_G_secp_comp = hex_decode ( G_secp_comp ); + auto sv_order_g = hex_decode ( order_g ); + + // BigInt bi_p_secp = BigInt::decode ( &sv_p_secp[0], sv_p_secp.size() ); + BigInt bi_p_secp("2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"); + BigInt bi_a_secp = BigInt::decode ( &sv_a_secp[0], sv_a_secp.size() ); + BigInt bi_b_secp = BigInt::decode ( &sv_b_secp[0], sv_b_secp.size() ); + BigInt bi_order_g = BigInt::decode ( &sv_order_g[0], sv_order_g.size() ); + CurveGFp curve(bi_p_secp, bi_a_secp, bi_b_secp); + PointGFp p_G = OS2ECP ( sv_G_secp_comp, curve ); + + EC_Group dom_params(curve, p_G, bi_order_g, BigInt(1)); + if(!p_G.on_the_curve()) + throw Internal_Error("Point not on the curve"); + + ECDSA_PrivateKey key_odd_oid(rng, dom_params); + std::string key_odd_oid_str = PKCS8::PEM_encode(key_odd_oid); + + DataSource_Memory key_data_src(key_odd_oid_str); + std::unique_ptr<PKCS8_PrivateKey> loaded_key2(PKCS8::load_key(key_data_src, rng)); + + if(!dynamic_cast<ECDSA_PrivateKey*>(loaded_key.get())) + { + std::cout << "Failed to reload an ECDSA key with unusual parameter set\n"; + ++fails; + } + + return fails; + } + +size_t test_curve_registry(RandomNumberGenerator& rng) + { + std::vector<std::string> oids; + oids.push_back("1.3.132.0.8"); + oids.push_back("1.2.840.10045.3.1.1"); + oids.push_back("1.2.840.10045.3.1.2"); + oids.push_back("1.2.840.10045.3.1.3"); + oids.push_back("1.2.840.10045.3.1.4"); + oids.push_back("1.2.840.10045.3.1.5"); + oids.push_back("1.2.840.10045.3.1.6"); + oids.push_back("1.2.840.10045.3.1.7"); + oids.push_back("1.3.132.0.6"); + oids.push_back("1.3.132.0.7"); + oids.push_back("1.3.132.0.28"); + oids.push_back("1.3.132.0.29"); + oids.push_back("1.3.132.0.9"); + oids.push_back("1.3.132.0.30"); + oids.push_back("1.3.132.0.31"); + oids.push_back("1.3.132.0.32"); + oids.push_back("1.3.132.0.33"); + oids.push_back("1.3.132.0.10"); + oids.push_back("1.3.132.0.34"); + oids.push_back("1.3.132.0.35"); + //oids.push_back("1.3.6.1.4.1.8301.3.1.2.9.0.38"); + oids.push_back("1.3.36.3.3.2.8.1.1.1"); + oids.push_back("1.3.36.3.3.2.8.1.1.3"); + oids.push_back("1.3.36.3.3.2.8.1.1.5"); + oids.push_back("1.3.36.3.3.2.8.1.1.7"); + oids.push_back("1.3.36.3.3.2.8.1.1.9"); + oids.push_back("1.3.36.3.3.2.8.1.1.11"); + oids.push_back("1.3.36.3.3.2.8.1.1.13"); + + size_t fails = 0; + + unsigned int i; + for (i = 0; i < oids.size(); i++) + { + try + { + OID oid(oids[i]); + EC_Group dom_pars(oid); + ECDSA_PrivateKey ecdsa(rng, dom_pars); + + PK_Signer signer(ecdsa, "EMSA1(SHA-1)"); + PK_Verifier verifier(ecdsa, "EMSA1(SHA-1)"); + + auto msg = hex_decode("12345678901234567890abcdef12"); + std::vector<byte> sig = signer.sign_message(msg, rng); + + if(!verifier.verify_message(msg, sig)) + { + std::cout << "Failed testing ECDSA sig for curve " << oids[i] << "\n"; + ++fails; + } + } + catch(Invalid_Argument& e) + { + std::cout << "Error testing curve " << oids[i] << " - " << e.what() << "\n"; + ++fails; + } + } + return fails; + } + +size_t test_read_pkcs8(RandomNumberGenerator& rng) + { + auto msg = hex_decode("12345678901234567890abcdef12"); + size_t fails = 0; + + try + { + std::unique_ptr<PKCS8_PrivateKey> loaded_key(PKCS8::load_key(TEST_DATA_DIR "/wo_dompar_private.pkcs8.pem", rng)); + ECDSA_PrivateKey* ecdsa = dynamic_cast<ECDSA_PrivateKey*>(loaded_key.get()); + CHECK_MESSAGE(ecdsa, "the loaded key could not be converted into an ECDSA_PrivateKey"); + + PK_Signer signer(*ecdsa, "EMSA1(SHA-1)"); + + std::vector<byte> sig = signer.sign_message(msg, rng); + + PK_Verifier verifier(*ecdsa, "EMSA1(SHA-1)"); + + CHECK_MESSAGE(verifier.verify_message(msg, sig), + "generated sig could not be verified positively"); + } + catch (std::exception& e) + { + ++fails; + std::cout << "Exception in test_read_pkcs8 - " << e.what() << "\n"; + } + + try + { + std::unique_ptr<PKCS8_PrivateKey> loaded_key_nodp(PKCS8::load_key(TEST_DATA_DIR "/nodompar_private.pkcs8.pem", rng)); + // anew in each test with unregistered domain-parameters + ECDSA_PrivateKey* ecdsa_nodp = dynamic_cast<ECDSA_PrivateKey*>(loaded_key_nodp.get()); + CHECK_MESSAGE(ecdsa_nodp, "the loaded key could not be converted into an ECDSA_PrivateKey"); + + PK_Signer signer(*ecdsa_nodp, "EMSA1(SHA-1)"); + PK_Verifier verifier(*ecdsa_nodp, "EMSA1(SHA-1)"); + + std::vector<byte> signature_nodp = signer.sign_message(msg, rng); + + CHECK_MESSAGE(verifier.verify_message(msg, signature_nodp), + "generated signature could not be verified positively (no_dom)"); + + try + { + std::unique_ptr<PKCS8_PrivateKey> loaded_key_withdp( + PKCS8::load_key(TEST_DATA_DIR "/withdompar_private.pkcs8.pem", rng)); + + std::cout << "Unexpected success: loaded key with unknown OID\n"; + ++fails; + } + catch (std::exception) { /* OK */ } + } + catch (std::exception& e) + { + std::cout << "Exception in test_read_pkcs8 - " << e.what() << "\n"; + ++fails; + } + + return fails; + } + +size_t test_ecc_key_with_rfc5915_extensions(RandomNumberGenerator& rng) + { + const std::string pw = "G3bz1L1gmB5ULietOZdoLPu63D7uwTLMEk"; + + size_t fails = 0; + + try + { + std::unique_ptr<PKCS8_PrivateKey> pkcs8( + PKCS8::load_key(TEST_DATA_DIR "/ecc_private_with_rfc5915_ext.pem", rng, pw)); + + if(!dynamic_cast<ECDSA_PrivateKey*>(pkcs8.get())) + { + std::cout << "Loaded RFC 5915 key, but got something other than an ECDSA key\n"; + ++fails; + } + } + catch(std::exception& e) + { + std::cout << "Exception in " << __func__ << " - " << e.what() << "\n"; + ++fails; + } + + return fails; + } + +} + +size_t test_ecdsa() + { + size_t fails = 0; + + AutoSeeded_RNG rng; + + fails += test_hash_larger_than_n(rng); + fails += test_decode_ecdsa_X509(); + fails += test_decode_ver_link_SHA256(); + fails += test_decode_ver_link_SHA1(); + fails += test_sign_then_ver(rng); + fails += test_ec_sign(rng); + fails += test_create_pkcs8(rng); + fails += test_create_and_verify(rng); + fails += test_curve_registry(rng); + fails += test_read_pkcs8(rng); + fails += test_ecc_key_with_rfc5915_extensions(rng); + + test_report("ECDSA", 11, fails); + + return fails; + } diff --git a/src/tests/test_hash.cpp b/src/tests/test_hash.cpp new file mode 100644 index 000000000..bb5788565 --- /dev/null +++ b/src/tests/test_hash.cpp @@ -0,0 +1,60 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/hash.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +bool hash_test(const std::string& algo, + const std::string& in_hex, + const std::string& out_hex) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + const auto providers = af.providers_of(algo); + size_t fails = 0; + + for(auto provider: providers) + { + auto proto = af.prototype_hash_function(algo, provider); + + if(!proto) + { + std::cout << "Unable to get " << algo << " from " << provider << "\n"; + ++fails; + continue; + } + + std::unique_ptr<HashFunction> hash(proto->clone()); + + hash->update(hex_decode(in_hex)); + + auto h = hash->final(); + + if(h != hex_decode_locked(out_hex)) + { + std::cout << algo << " " << provider << " got " << hex_encode(h) << " != " << out_hex << "\n"; + ++fails; + } + } + + return (fails == 0); + } + +} + +size_t test_hash() + { + std::ifstream vec(CHECKS_DIR "/hash.vec"); + + return run_tests_bb(vec, "Hash", "Out", true, + [](std::map<std::string, std::string> m) -> bool + { + return hash_test(m["Hash"], m["In"], m["Out"]); + }); + } diff --git a/src/tests/test_hkdf.cpp b/src/tests/test_hkdf.cpp new file mode 100644 index 000000000..1c8532c33 --- /dev/null +++ b/src/tests/test_hkdf.cpp @@ -0,0 +1,70 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/hkdf.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +secure_vector<byte> hkdf(const std::string& algo, + const secure_vector<byte>& ikm, + const secure_vector<byte>& salt, + const secure_vector<byte>& info, + size_t L) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + const MessageAuthenticationCode* mac_proto = af.prototype_mac("HMAC(" + algo + ")"); + + if(!mac_proto) + throw std::invalid_argument("Bad HKDF hash " + algo); + + HKDF hkdf(mac_proto->clone(), mac_proto->clone()); + + hkdf.start_extract(&salt[0], salt.size()); + hkdf.extract(&ikm[0], ikm.size()); + hkdf.finish_extract(); + + secure_vector<byte> key(L); + hkdf.expand(&key[0], key.size(), &info[0], info.size()); + return key; + } + +bool hkdf_test(const std::string& algo, + const std::string& ikm, + const std::string& salt, + const std::string& info, + const std::string& okm, + size_t L) + { + const std::string got = hex_encode( + hkdf(algo, + hex_decode_locked(ikm), + hex_decode_locked(salt), + hex_decode_locked(info), + L) + ); + + if(got != okm) + std::cout << "HKDF got " << got << " expected " << okm << std::endl; + + return (got == okm); + } + +} + +size_t test_hkdf() + { + // From RFC 5869 + std::ifstream vec(CHECKS_DIR "/hkdf.vec"); + + return run_tests_bb(vec, "HKDF", "OKM", true, + [](std::map<std::string, std::string> m) -> bool + { + return hkdf_test(m["Hash"], m["IKM"], m["salt"], m["info"], + m["OKM"], to_u32bit(m["L"])); + }); + } diff --git a/src/tests/test_kdf.cpp b/src/tests/test_kdf.cpp new file mode 100644 index 000000000..9debe7966 --- /dev/null +++ b/src/tests/test_kdf.cpp @@ -0,0 +1,43 @@ +#include "tests.h" + +#include <botan/lookup.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +secure_vector<byte> kdf(const std::string& algo, + size_t outlen, + const secure_vector<byte>& secret, + const secure_vector<byte>& salt) + { + std::unique_ptr<KDF> kdf(get_kdf(algo)); + return kdf->derive_key(outlen, secret, salt); + } + +std::string kdf_test(const std::string& algo, + size_t outlen, + const std::string& secret, + const std::string& salt) + { + return hex_encode(kdf(algo, outlen, + hex_decode_locked(secret), + hex_decode_locked(salt))); + } + +} + +size_t test_kdf() + { + std::ifstream vec(CHECKS_DIR "/kdf.vec"); + + return run_tests(vec, "KDF", "Output", true, + [](std::map<std::string, std::string> m) + { + return kdf_test(m["KDF"], to_u32bit(m["OutputLen"]), + m["Secret"], m["Salt"]); + }); + } diff --git a/src/tests/test_keywrap.cpp b/src/tests/test_keywrap.cpp new file mode 100644 index 000000000..a7dcbfe75 --- /dev/null +++ b/src/tests/test_keywrap.cpp @@ -0,0 +1,90 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/hex.h> + +#if defined(BOTAN_HAS_RFC3394_KEYWRAP) + #include <botan/rfc3394.h> +#endif + +#include <iostream> + +using namespace Botan; + +namespace { + +size_t keywrap_test(const char* key_str, + const char* expected_str, + const char* kek_str) + { + size_t fail = 0; + +#if defined(BOTAN_HAS_RFC3394_KEYWRAP) + try + { + SymmetricKey key(key_str); + SymmetricKey expected(expected_str); + SymmetricKey kek(kek_str); + + Algorithm_Factory& af = global_state().algorithm_factory(); + + secure_vector<byte> enc = rfc3394_keywrap(key.bits_of(), kek, af); + + if(enc != expected.bits_of()) + { + std::cout << "NIST key wrap encryption failure: " + << hex_encode(enc) << " != " << hex_encode(expected.bits_of()) << "\n"; + fail++; + } + + secure_vector<byte> dec = rfc3394_keyunwrap(expected.bits_of(), kek, af); + + if(dec != key.bits_of()) + { + std::cout << "NIST key wrap decryption failure: " + << hex_encode(dec) << " != " << hex_encode(key.bits_of()) << "\n"; + fail++; + } + } + catch(std::exception& e) + { + std::cout << e.what() << "\n"; + fail++; + } +#endif + + return fail; + } + +} + +size_t test_keywrap() + { + size_t fails = 0; + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF", + "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5", + "000102030405060708090A0B0C0D0E0F"); + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF", + "96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D", + "000102030405060708090A0B0C0D0E0F1011121314151617"); + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF", + "64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF0001020304050607", + "031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2", + "000102030405060708090A0B0C0D0E0F1011121314151617"); + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF0001020304050607", + "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + + fails += keywrap_test("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F", + "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + + return fails; + } diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp new file mode 100644 index 000000000..31f159663 --- /dev/null +++ b/src/tests/test_mac.cpp @@ -0,0 +1,62 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/mac.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +bool mac_test(const std::string& algo, + const std::string& key_hex, + const std::string& in_hex, + const std::string& out_hex) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + const auto providers = af.providers_of(algo); + size_t fails = 0; + + for(auto provider: providers) + { + auto proto = af.prototype_mac(algo, provider); + + if(!proto) + { + std::cout << "Unable to get " << algo << " from " << provider << "\n"; + ++fails; + continue; + } + + std::unique_ptr<MessageAuthenticationCode> mac(proto->clone()); + + mac->set_key(hex_decode(key_hex)); + mac->update(hex_decode(in_hex)); + + auto h = mac->final(); + + if(h != hex_decode_locked(out_hex)) + { + std::cout << algo << " " << provider << " got " << hex_encode(h) << " != " << out_hex << "\n"; + ++fails; + } + } + + return (fails == 0); + } + +} + +size_t test_mac() + { + std::ifstream vec(CHECKS_DIR "/mac.vec"); + + return run_tests_bb(vec, "Mac", "Out", true, + [](std::map<std::string, std::string> m) -> bool + { + return mac_test(m["Mac"], m["Key"], m["In"], m["Out"]); + }); + } diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp new file mode 100644 index 000000000..1b546cea3 --- /dev/null +++ b/src/tests/test_modes.cpp @@ -0,0 +1,79 @@ +#include "tests.h" + +#include <botan/hex.h> +#include <botan/lookup.h> +#include <botan/cipher_mode.h> +#include <botan/filters.h> +#include <iostream> +#include <fstream> +#include <memory> + +using namespace Botan; + +namespace { + +secure_vector<byte> run_mode(const std::string& algo, + Cipher_Dir dir, + const secure_vector<byte>& pt, + const secure_vector<byte>& nonce, + const secure_vector<byte>& key) + { +#if 0 + std::unique_ptr<Cipher_Mode> cipher(get_cipher(algo, dir)); + + cipher->set_key(key); + cipher->start_vec(nonce); + + secure_vector<byte> ct = pt; + cipher->finish(ct); +#endif + + Pipe pipe(get_cipher(algo, SymmetricKey(key), InitializationVector(nonce), dir)); + + pipe.process_msg(pt); + + return pipe.read_all(); + } + +bool mode_test(const std::string& algo, + const std::string& pt, + const std::string& ct, + const std::string& key_hex, + const std::string& nonce_hex) + { + auto nonce = hex_decode_locked(nonce_hex); + auto key = hex_decode_locked(key_hex); + + const std::string ct2 = hex_encode(run_mode(algo, + ENCRYPTION, + hex_decode_locked(pt), + nonce, + key)); + + if(ct != ct2) + std::cout << algo << " got ct " << ct2 << " expected " << ct << "\n"; + + const std::string pt2 = hex_encode(run_mode(algo, + DECRYPTION, + hex_decode_locked(ct), + nonce, + key)); + + if(pt != pt2) + std::cout << algo << " got pt " << pt2 << " expected " << pt << "\n"; + + return (ct == ct2) && (pt == pt2); + } + +} + +size_t test_modes() + { + std::ifstream vec(CHECKS_DIR "/modes.vec"); + + return run_tests_bb(vec, "Mode", "Out", true, + [](std::map<std::string, std::string> m) + { + return mode_test(m["Mode"], m["In"], m["Out"], m["Key"], m["Nonce"]); + }); + } diff --git a/src/tests/test_ocb.cpp b/src/tests/test_ocb.cpp new file mode 100644 index 000000000..b2bd296e0 --- /dev/null +++ b/src/tests/test_ocb.cpp @@ -0,0 +1,142 @@ + +#include "tests.h" + +#include <botan/ocb.h> +#include <botan/hex.h> +#include <botan/sha2_32.h> +#include <botan/aes.h> +#include <iostream> +//#include <botan/selftest.h> + +using namespace Botan; + +// something like this should be in the library +namespace { + +std::vector<byte> ocb_decrypt(const SymmetricKey& key, + const std::vector<byte>& nonce, + const byte ct[], size_t ct_len, + const byte ad[], size_t ad_len) + { + OCB_Decryption ocb(new AES_128); + + ocb.set_key(key); + ocb.set_associated_data(ad, ad_len); + + ocb.start(&nonce[0], nonce.size()); + + secure_vector<byte> buf(ct, ct+ct_len); + ocb.finish(buf, 0); + + return unlock(buf); + } + +std::vector<byte> ocb_encrypt(const SymmetricKey& key, + const std::vector<byte>& nonce, + const byte pt[], size_t pt_len, + const byte ad[], size_t ad_len) + { + OCB_Encryption ocb(new AES_128); + + ocb.set_key(key); + ocb.set_associated_data(ad, ad_len); + + ocb.start(&nonce[0], nonce.size()); + + secure_vector<byte> buf(pt, pt+pt_len); + ocb.finish(buf, 0); + + try + { + std::vector<byte> pt2 = ocb_decrypt(key, nonce, &buf[0], buf.size(), ad, ad_len); + if(pt_len != pt2.size() || !same_mem(pt, &pt2[0], pt_len)) + std::cout << "OCB failed to decrypt correctly\n"; + } + catch(std::exception& e) + { + std::cout << "OCB round trip error - " << e.what() << "\n"; + } + + return unlock(buf); + } + +template<typename Alloc, typename Alloc2> +std::vector<byte> ocb_encrypt(const SymmetricKey& key, + const std::vector<byte>& nonce, + const std::vector<byte, Alloc>& pt, + const std::vector<byte, Alloc2>& ad) + { + return ocb_encrypt(key, nonce, &pt[0], pt.size(), &ad[0], ad.size()); + } + +template<typename Alloc, typename Alloc2> +std::vector<byte> ocb_decrypt(const SymmetricKey& key, + const std::vector<byte>& nonce, + const std::vector<byte, Alloc>& pt, + const std::vector<byte, Alloc2>& ad) + { + return ocb_decrypt(key, nonce, &pt[0], pt.size(), &ad[0], ad.size()); + } + +std::vector<byte> ocb_encrypt(OCB_Encryption& ocb, + const std::vector<byte>& nonce, + const std::vector<byte>& pt, + const std::vector<byte>& ad) + { + ocb.set_associated_data(&ad[0], ad.size()); + + ocb.start(&nonce[0], nonce.size()); + + secure_vector<byte> buf(pt.begin(), pt.end()); + ocb.finish(buf, 0); + + return unlock(buf); + } + +size_t test_ocb_long(size_t taglen, const std::string &expected) + { + OCB_Encryption ocb(new AES_128, taglen/8); + + ocb.set_key(SymmetricKey("00000000000000000000000000000000")); + + const std::vector<byte> empty; + std::vector<byte> N(12); + std::vector<byte> C; + + for(size_t i = 0; i != 128; ++i) + { + const std::vector<byte> S(i); + N[11] = i; + + C += ocb_encrypt(ocb, N, S, S); + C += ocb_encrypt(ocb, N, S, empty); + C += ocb_encrypt(ocb, N, empty, S); + } + + N[11] = 0; + const std::vector<byte> cipher = ocb_encrypt(ocb, N, empty, C); + + const std::string cipher_hex = hex_encode(cipher); + + if(cipher_hex != expected) + { + std::cout << "OCB AES-128 long test mistmatch " << cipher_hex << " != " << expected << "\n"; + return 1; + } + + return 0; + } + +} + +size_t test_ocb() + { + size_t fails = 0; + fails += test_ocb_long(128, "B2B41CBF9B05037DA7F16C24A35C1C94"); + fails += test_ocb_long(96, "1A4F0654277709A5BDA0D380"); + fails += test_ocb_long(64, "B7ECE9D381FE437F"); + test_report("OCB long", 3, fails); + return fails; + } + + diff --git a/src/tests/test_passhash.cpp b/src/tests/test_passhash.cpp new file mode 100644 index 000000000..6f66743c5 --- /dev/null +++ b/src/tests/test_passhash.cpp @@ -0,0 +1,94 @@ +#include "tests.h" + +#include <botan/auto_rng.h> +#include <iostream> + +#if defined(BOTAN_HAS_PASSHASH9) + #include <botan/passhash9.h> +#endif + +#if defined(BOTAN_HAS_BCRYPT) + #include <botan/bcrypt.h> +#endif + +using namespace Botan; + +size_t test_bcrypt() + { + size_t fails = 0; + +#if defined(BOTAN_HAS_BCRYPT) + + // Generated by jBCrypt 0.3 + if(!check_bcrypt("abc", "$2a$05$DfPyLs.G6.To9fXEFgUL1O6HpYw3jIXgPcl/L3Qt3jESuWmhxtmpS")) + { + std::cout << "Bcrypt test 1 failed\n"; + fails++; + } + + // http://www.openwall.com/lists/john-dev/2011/06/19/2 + if(!check_bcrypt("\xA3", + "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq")) + { + std::cout << "Bcrypt test 2 failed\n"; + fails++; + } + + AutoSeeded_RNG rng; + + for(u16bit level = 1; level != 5; ++level) + { + const std::string input = "some test passphrase 123"; + const std::string gen_hash = generate_bcrypt(input, rng, level); + + if(!check_bcrypt(input, gen_hash)) + { + std::cout << "Gen and check for bcrypt failed: " << gen_hash << " not valid\n"; + ++fails; + } + } + + test_report("Bcrypt", 6, fails); + +#endif + + return fails; + } + +size_t test_passhash9() + { + size_t fails = 0; + +#if defined(BOTAN_HAS_PASSHASH9) + const std::string input = "secret"; + const std::string fixed_hash = + "$9$AAAKhiHXTIUhNhbegwBXJvk03XXJdzFMy+i3GFMIBYKtthTTmXZA"; + + size_t ran = 0; + + ++ran; + if(!check_passhash9(input, fixed_hash)) + { + std::cout << "Passhash9 fixed input test failed\n"; + fails++; + } + + AutoSeeded_RNG rng; + + for(byte alg_id = 0; alg_id <= 4; ++alg_id) + { + std::string gen_hash = generate_passhash9(input, rng, 2, alg_id); + + ++ran; + if(!check_passhash9(input, gen_hash)) + { + std::cout << "Passhash9 gen and check " << static_cast<int>(alg_id) << " failed\n"; + ++fails; + } + } + + test_report("Passhash9", ran, fails); +#endif + + return fails; + } diff --git a/src/tests/test_pbkdf.cpp b/src/tests/test_pbkdf.cpp new file mode 100644 index 000000000..140190b13 --- /dev/null +++ b/src/tests/test_pbkdf.cpp @@ -0,0 +1,48 @@ +#include "tests.h" + +#include <botan/lookup.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +secure_vector<byte> pbkdf(const std::string& algo, + const std::string& pass, + const secure_vector<byte>& salt, + size_t iterations, size_t outlen) + { + std::unique_ptr<PBKDF> pbkdf(get_pbkdf(algo)); + return pbkdf->derive_key(outlen, pass, + &salt[0], salt.size(), + iterations).bits_of(); + } + +std::string pbkdf_test(const std::string& algo, + const std::string& pass, + const std::string& salt, + size_t iterations, + size_t outlen) + { + return hex_encode(pbkdf(algo, + pass, + hex_decode_locked(salt), + iterations, + outlen)); + } + +} + +size_t test_pbkdf() + { + std::ifstream vec(CHECKS_DIR "/pbkdf.vec"); + + return run_tests(vec, "PBKDF", "Output", true, + [](std::map<std::string, std::string> m) + { + return pbkdf_test(m["PBKDF"], m["Passphrase"], m["Salt"], + to_u32bit(m["Iterations"]), to_u32bit(m["OutputLen"])); + }); + } diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp new file mode 100644 index 000000000..209021f7f --- /dev/null +++ b/src/tests/test_pubkey.cpp @@ -0,0 +1,935 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" +#include "test_rng.h" + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> +#include <cstdlib> +#include <memory> + +#include <botan/botan.h> +#include <botan/oids.h> + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include <botan/x509_key.h> + #include <botan/pkcs8.h> + #include <botan/pubkey.h> +#endif + +#if defined(BOTAN_HAS_RSA) + #include <botan/rsa.h> +#endif + +#if defined(BOTAN_HAS_DSA) + #include <botan/dsa.h> +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + #include <botan/dh.h> +#endif + +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) + #include <botan/nr.h> +#endif + +#if defined(BOTAN_HAS_RW) + #include <botan/rw.h> +#endif + +#if defined(BOTAN_HAS_ELGAMAL) + #include <botan/elgamal.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +#if defined(BOTAN_HAS_ECDH) + #include <botan/ecdh.h> +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) + #include <botan/gost_3410.h> +#endif + +#if defined(BOTAN_HAS_DLIES) + #include <botan/dlies.h> + #include <botan/kdf.h> +#endif + +#include <botan/filters.h> +#include <botan/numthry.h> +using namespace Botan; + +namespace { + +BigInt to_bigint(std::string input) + { + while(input.find(' ') != std::string::npos) + input = input.erase(input.find(' '), 1); + + while(input.find('\t') != std::string::npos) + input = input.erase(input.find('\t'), 1); + + while(input.find('\n') != std::string::npos) + input = input.erase(input.find('\n'), 1); + + return BigInt::decode(reinterpret_cast<const byte*>(input.data()), + input.length(), BigInt::Hexadecimal); + } + +void dump_data(const std::vector<byte>& out, + const std::vector<byte>& expected) + { + Pipe pipe(new Hex_Encoder); + + pipe.process_msg(out); + pipe.process_msg(expected); + std::cout << "Got: " << pipe.read_all_as_string(0) << std::endl; + std::cout << "Exp: " << pipe.read_all_as_string(1) << std::endl; + } + +size_t validate_save_and_load(const Private_Key* priv_key, + RandomNumberGenerator& rng) + { + std::string name = priv_key->algo_name(); + + size_t fails = 0; + std::string pub_pem = X509::PEM_encode(*priv_key); + + try + { + DataSource_Memory input_pub(pub_pem); + std::auto_ptr<Public_Key> restored_pub(X509::load_key(input_pub)); + + if(!restored_pub.get()) + { + std::cout << "Could not recover " << name << " public key\n"; + ++fails; + } + else if(restored_pub->check_key(rng, true) == false) + { + std::cout << "Restored pubkey failed self tests " << name << "\n"; + ++fails; + } + } + catch(std::exception& e) + { + std::cout << "Exception during load of " << name + << " key: " << e.what() << "\n"; + std::cout << "PEM for pubkey was:\n" << pub_pem << "\n"; + ++fails; + } + + std::string priv_pem = PKCS8::PEM_encode(*priv_key); + + try + { + DataSource_Memory input_priv(priv_pem); + std::auto_ptr<Private_Key> restored_priv( + PKCS8::load_key(input_priv, rng)); + + if(!restored_priv.get()) + { + std::cout << "Could not recover " << name << " privlic key\n"; + ++fails; + } + else if(restored_priv->check_key(rng, true) == false) + { + std::cout << "Restored privkey failed self tests " << name << "\n"; + ++fails; + } + } + catch(std::exception& e) + { + std::cout << "Exception during load of " << name + << " key: " << e.what() << "\n"; + std::cout << "PEM for privkey was:\n" << priv_pem << "\n"; + ++fails; + } + + return fails; + } + +size_t validate_decryption(PK_Decryptor& d, const std::string& algo, + const std::vector<byte> ctext, + const std::vector<byte> ptext) + { + size_t fails = 0; + + std::vector<byte> decrypted = unlock(d.decrypt(ctext)); + + if(decrypted != ptext) + { + std::cout << "FAILED (decrypt): " << algo << std::endl; + dump_data(decrypted, ptext); + ++fails; + } + + return fails; + } + +size_t validate_encryption(PK_Encryptor& e, PK_Decryptor& d, + const std::string& algo, const std::string& input, + const std::string& random, const std::string& exp) + { + std::vector<byte> message = hex_decode(input); + std::vector<byte> expected = hex_decode(exp); + Fixed_Output_RNG rng(hex_decode(random)); + + size_t fails = 0; + + std::vector<byte> out = e.encrypt(message, rng); + if(out != expected) + { + std::cout << "FAILED (encrypt): " << algo << std::endl; + dump_data(out, expected); + ++fails; + } + + fails += validate_decryption(d, algo, out, message); + + return fails; + } + +size_t validate_signature(PK_Verifier& v, PK_Signer& s, const std::string& algo, + const std::string& input, + RandomNumberGenerator& rng, + const std::string& exp) + { + std::vector<byte> message = hex_decode(input); + std::vector<byte> expected = hex_decode(exp); + std::vector<byte> sig = s.sign_message(message, rng); + + size_t fails = 0; + + if(sig != expected) + { + std::cout << "FAILED (sign): " << algo << std::endl; + dump_data(sig, expected); + ++fails; + } + + if(!v.verify_message(message, sig)) + { + std::cout << "FAILED (verify): " << algo << std::endl; + ++fails; + } + + /* This isn't a very thorough testing method, but it will hopefully + catch any really horrible errors */ + sig[0]++; + if(v.verify_message(message, sig)) + { + std::cout << "FAILED (accepted bad sig): " << algo << std::endl; + ++fails; + } + + return fails; + } + +size_t validate_signature(PK_Verifier& v, PK_Signer& s, const std::string& algo, + const std::string& input, + const std::string& random, + const std::string& exp) + { + Fixed_Output_RNG rng(hex_decode(random)); + + return validate_signature(v, s, algo, input, rng, exp); + } + +size_t validate_kas(PK_Key_Agreement& kas, const std::string& algo, + const std::vector<byte>& pubkey, const std::string& output, + u32bit keylen) + { + std::vector<byte> expected = hex_decode(output); + + std::vector<byte> got = unlock(kas.derive_key(keylen, pubkey).bits_of()); + + size_t fails = 0; + + if(got != expected) + { + std::cout << "FAILED: " << algo << std::endl; + dump_data(got, expected); + ++fails; + } + + return fails; + } + +size_t validate_rsa_enc_pkcs8(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 4 && str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_RSA) + std::string pass; + if(str.size() == 5) pass = str[4]; + strip_newlines(pass); /* it will have a newline thanks to the messy + decoding method we use */ + + DataSource_Memory keysource(reinterpret_cast<const byte*>(str[0].c_str()), + str[0].length()); + + std::unique_ptr<Private_Key> privkey(PKCS8::load_key(keysource, rng, pass)); + + RSA_PrivateKey* rsapriv = dynamic_cast<RSA_PrivateKey*>(privkey.get()); + if(!rsapriv) + throw Invalid_Argument("Bad key load for RSA key"); + + RSA_PublicKey* rsapub = dynamic_cast<RSA_PublicKey*>(rsapriv); + + std::string eme = algo.substr(12, std::string::npos); + + PK_Encryptor_EME e(*rsapub, eme); + PK_Decryptor_EME d(*rsapriv, eme); + + return validate_encryption(e, d, algo, str[1], str[2], str[3]); +#endif + + return 0; + } + +size_t validate_rsa_enc(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 6) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_RSA) + RSA_PrivateKey privkey(rng, + to_bigint(str[1]), to_bigint(str[2]), + to_bigint(str[0])); + + RSA_PublicKey pubkey = privkey; + + std::string eme = algo.substr(6, std::string::npos); + + PK_Encryptor_EME e(pubkey, eme); + PK_Decryptor_EME d(privkey, eme); + + return validate_encryption(e, d, algo, str[3], str[4], str[5]); +#endif + + return 0; + } + +size_t validate_elg_enc(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 6 && str.size() != 7) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_ELGAMAL) + DL_Group domain(to_bigint(str[0]), to_bigint(str[1])); + ElGamal_PrivateKey privkey(rng, domain, to_bigint(str[2])); + ElGamal_PublicKey pubkey = privkey; + + std::string eme = algo.substr(8, std::string::npos); + + PK_Decryptor_EME d(privkey, eme); + + if(str.size() == 7) + { + PK_Encryptor_EME e(pubkey, eme); + return validate_encryption(e, d, algo, str[4], str[5], str[6]); + } + else + return validate_decryption(d, algo, hex_decode(str[5]), + hex_decode(str[4])); +#endif + + return 0; + } + +size_t validate_rsa_sig(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 6) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_RSA) + RSA_PrivateKey privkey(rng, + to_bigint(str[1]), to_bigint(str[2]), + to_bigint(str[0])); + + RSA_PublicKey pubkey = privkey; + + std::string emsa = algo.substr(7, std::string::npos); + + PK_Verifier v(pubkey, emsa); + PK_Signer s(privkey, emsa); + + return validate_signature(v, s, algo, str[3], str[4], str[5]); +#endif + + return 0; + } + +u32bit validate_rsa_ver(const std::string& algo, + const std::vector<std::string>& str) + { + if(str.size() != 5) /* is actually 4, parse() adds an extra empty one */ + throw std::runtime_error("Invalid input from pk_valid.dat"); + + size_t fails = 0; + +#if defined(BOTAN_HAS_RSA) + RSA_PublicKey key(to_bigint(str[1]), to_bigint(str[0])); + + std::string emsa = algo.substr(6, std::string::npos); + + PK_Verifier v(key, emsa); + + std::vector<byte> msg = hex_decode(str[2]); + std::vector<byte> sig = hex_decode(str[3]); + + if(!v.verify_message(msg, sig)) + { + std::cout << "RSA verification failed\n"; + ++fails; + } + +#endif + + return fails; + } + +size_t validate_rsa_ver_x509(const std::string& algo, + const std::vector<std::string>& str) + { + if(str.size() != 5) /* is actually 3, parse() adds extra empty ones */ + throw std::runtime_error("Invalid input from pk_valid.dat"); + + size_t fails = 0; + +#if defined(BOTAN_HAS_RSA) + DataSource_Memory keysource(reinterpret_cast<const byte*>(str[0].c_str()), + str[0].length()); + + std::unique_ptr<Public_Key> key(X509::load_key(keysource)); + + RSA_PublicKey* rsakey = dynamic_cast<RSA_PublicKey*>(key.get()); + + if(!rsakey) + throw Invalid_Argument("Bad key load for RSA public key"); + + std::string emsa = algo.substr(11, std::string::npos); + + PK_Verifier v(*rsakey, emsa); + + std::vector<byte> msg = hex_decode(str[1]); + std::vector<byte> sig = hex_decode(str[2]); + + if(!v.verify_message(msg, sig)) + { + std::cout << "RSA verification failed\n"; + ++fails; + } +#endif + + return fails; + } + +u32bit validate_rw_ver(const std::string& algo, + const std::vector<std::string>& str) + { + if(str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_RW) + RW_PublicKey key(to_bigint(str[1]), to_bigint(str[0])); + + std::string emsa = algo.substr(5, std::string::npos); + + PK_Verifier v(key, emsa); + + std::vector<byte> msg = hex_decode(str[2]); + std::vector<byte> sig = hex_decode(str[3]); + + bool passed = true; + passed = v.verify_message(msg, sig); + return (passed ? 0 : 1); +#endif + + return 2; + } + +u32bit validate_rw_sig(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_RW) + RW_PrivateKey privkey(rng, to_bigint(str[1]), to_bigint(str[2]), + to_bigint(str[0])); + RW_PublicKey pubkey = privkey; + + std::string emsa = algo.substr(3, std::string::npos); + + PK_Verifier v(pubkey, emsa); + PK_Signer s(privkey, emsa); + + + validate_signature(v, s, algo, str[3], rng, str[4]); +#endif + + return 0; + } + +u32bit validate_dsa_sig(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 4 && str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + + std::string pass; + if(str.size() == 5) pass = str[4]; + strip_newlines(pass); /* it will have a newline thanks to the messy + decoding method we use */ + +#if defined(BOTAN_HAS_DSA) + + DataSource_Memory keysource(reinterpret_cast<const byte*>(str[0].c_str()), + str[0].length()); + + std::unique_ptr<Private_Key> privkey(PKCS8::load_key(keysource, rng, pass)); + + DSA_PrivateKey* dsapriv = dynamic_cast<DSA_PrivateKey*>(privkey.get()); + if(!dsapriv) + throw Invalid_Argument("Bad key load for DSA private key"); + + DSA_PublicKey* dsapub = dynamic_cast<DSA_PublicKey*>(dsapriv); + + std::string emsa = algo.substr(4, std::string::npos); + + PK_Verifier v(*dsapub, emsa); + PK_Signer s(*dsapriv, emsa); + + + validate_signature(v, s, algo, str[1], str[2], str[3]); +#endif + + return 0; + } + +u32bit validate_ecdsa_sig(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_ECDSA) + + EC_Group group(OIDS::lookup(str[0])); + ECDSA_PrivateKey ecdsa(rng, group, to_bigint(str[1])); + + std::string emsa = algo.substr(6, std::string::npos); + + PK_Verifier v(ecdsa, emsa); + PK_Signer s(ecdsa, emsa); + + validate_signature(v, s, algo, str[2], str[3], str[4]); +#endif + + return 0; + } + +u32bit validate_gost_ver(const std::string& algo, + const std::vector<std::string>& str) + { + if(str.size() != 5) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_GOST_34_10_2001) + + EC_Group group(OIDS::lookup(str[0])); + + PointGFp public_point = OS2ECP(hex_decode(str[1]), group.get_curve()); + + GOST_3410_PublicKey gost(group, public_point); + + std::string emsa = algo.substr(13, std::string::npos); + + PK_Verifier v(gost, emsa); + + std::vector<byte> msg = hex_decode(str[2]); + std::vector<byte> sig = hex_decode(str[3]); + + bool passed = v.verify_message(msg, sig); + return (passed ? 0 : 1); +#endif + + return 0; + } + +u32bit validate_dsa_ver(const std::string& algo, + const std::vector<std::string>& str) + { + if(str.size() != 5) /* is actually 3, parse() adds extra empty ones */ + throw std::runtime_error("Invalid input from pk_valid.dat"); + + DataSource_Memory keysource(reinterpret_cast<const byte*>(str[0].c_str()), + str[0].length()); + + size_t fails = 0; + +#if defined(BOTAN_HAS_DSA) + std::unique_ptr<Public_Key> key(X509::load_key(keysource)); + + DSA_PublicKey* dsakey = dynamic_cast<DSA_PublicKey*>(key.get()); + + if(!dsakey) + { + ++fails; + std::cout << "Unable to load DSA private key during test\n"; + } + + std::string emsa = algo.substr(7, std::string::npos); + + PK_Verifier v(*dsakey, emsa); + + std::vector<byte> msg = hex_decode(str[1]); + std::vector<byte> sig = hex_decode(str[2]); + + v.set_input_format(DER_SEQUENCE); + + bool verified = v.verify_message(msg, sig); + if(!verified) + { + std::cout << "Failed to verify\n"; + ++fails; + } +#endif + + return fails; + } + +u32bit validate_nr_sig(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 8) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) + + DL_Group domain(to_bigint(str[0]), to_bigint(str[1]), to_bigint(str[2])); + NR_PrivateKey privkey(rng, domain, to_bigint(str[4])); + NR_PublicKey pubkey = privkey; + + std::string emsa = algo.substr(3, std::string::npos); + + PK_Verifier v(pubkey, emsa); + PK_Signer s(privkey, emsa); + + return validate_signature(v, s, algo, str[5], str[6], str[7]); +#endif + + return 0; + } + +u32bit validate_dh(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 5 && str.size() != 6) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + DL_Group domain(to_bigint(str[0]), to_bigint(str[1])); + + DH_PrivateKey mykey(rng, domain, to_bigint(str[2])); + DH_PublicKey otherkey(domain, to_bigint(str[3])); + + std::string kdf = algo.substr(3, std::string::npos); + + u32bit keylen = 0; + if(str.size() == 6) + keylen = to_u32bit(str[5]); + + PK_Key_Agreement kas(mykey, kdf); + + return validate_kas(kas, algo, otherkey.public_value(), str[4], keylen); +#endif + + return 0; + } + +u32bit validate_dlies(const std::string& algo, + const std::vector<std::string>& str, + RandomNumberGenerator& rng) + { + if(str.size() != 6) + throw std::runtime_error("Invalid input from pk_valid.dat"); + +#if defined(BOTAN_HAS_DLIES) + DL_Group domain(to_bigint(str[0]), to_bigint(str[1])); + + DH_PrivateKey from(rng, domain, to_bigint(str[2])); + DH_PrivateKey to(rng, domain, to_bigint(str[3])); + + const std::string opt_str = algo.substr(6, std::string::npos); + + std::vector<std::string> options = split_on(opt_str, '/'); + + if(options.size() != 3) + throw std::runtime_error("DLIES needs three options: " + opt_str); + + MessageAuthenticationCode* mac = get_mac(options[1]); + u32bit mac_key_len = to_u32bit(options[2]); + + DLIES_Encryptor e(from, + get_kdf(options[0]), + mac, mac_key_len); + + DLIES_Decryptor d(to, + get_kdf(options[0]), + mac->clone(), mac_key_len); + + e.set_other_key(to.public_value()); + + std::string empty = ""; + return validate_encryption(e, d, algo, str[4], empty, str[5]); +#endif + + return 0; + } + +} + +size_t test_pk_keygen() + { + AutoSeeded_RNG rng; + + size_t fails = 0; + +#define DL_KEY(TYPE, GROUP) \ + { \ + TYPE key(rng, DL_Group(GROUP)); \ + key.check_key(rng, true); \ + fails += validate_save_and_load(&key, rng); \ + } + +#define EC_KEY(TYPE, GROUP) \ + { \ + TYPE key(rng, EC_Group(OIDS::lookup(GROUP))); \ + key.check_key(rng, true); \ + fails += validate_save_and_load(&key, rng); \ + } + +#if defined(BOTAN_HAS_RSA) + { + RSA_PrivateKey rsa1024(rng, 1024); + rsa1024.check_key(rng, true); + fails += validate_save_and_load(&rsa1024, rng); + + RSA_PrivateKey rsa2048(rng, 2048); + rsa2048.check_key(rng, true); + fails += validate_save_and_load(&rsa2048, rng); + } +#endif + +#if defined(BOTAN_HAS_RW) + { + RW_PrivateKey rw1024(rng, 1024); + rw1024.check_key(rng, true); + fails += validate_save_and_load(&rw1024, rng); + } +#endif + +#if defined(BOTAN_HAS_DSA) + DL_KEY(DSA_PrivateKey, "dsa/jce/1024"); + DL_KEY(DSA_PrivateKey, "dsa/botan/2048"); + DL_KEY(DSA_PrivateKey, "dsa/botan/3072"); +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + DL_KEY(DH_PrivateKey, "modp/ietf/1024"); + DL_KEY(DH_PrivateKey, "modp/ietf/2048"); + DL_KEY(DH_PrivateKey, "modp/ietf/4096"); + DL_KEY(DH_PrivateKey, "dsa/jce/1024"); +#endif + +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) + DL_KEY(NR_PrivateKey, "dsa/jce/1024"); + DL_KEY(NR_PrivateKey, "dsa/botan/2048"); + DL_KEY(NR_PrivateKey, "dsa/botan/3072"); +#endif + +#if defined(BOTAN_HAS_ELGAMAL) + DL_KEY(ElGamal_PrivateKey, "modp/ietf/1024"); + DL_KEY(ElGamal_PrivateKey, "dsa/jce/1024"); + DL_KEY(ElGamal_PrivateKey, "dsa/botan/2048"); + DL_KEY(ElGamal_PrivateKey, "dsa/botan/3072"); +#endif + +#if defined(BOTAN_HAS_ECDSA) + EC_KEY(ECDSA_PrivateKey, "secp112r1"); + EC_KEY(ECDSA_PrivateKey, "secp128r1"); + EC_KEY(ECDSA_PrivateKey, "secp160r1"); + EC_KEY(ECDSA_PrivateKey, "secp192r1"); + EC_KEY(ECDSA_PrivateKey, "secp224r1"); + EC_KEY(ECDSA_PrivateKey, "secp256r1"); + EC_KEY(ECDSA_PrivateKey, "secp384r1"); + EC_KEY(ECDSA_PrivateKey, "secp521r1"); +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) + EC_KEY(GOST_3410_PrivateKey, "gost_256A"); + EC_KEY(GOST_3410_PrivateKey, "secp112r1"); + EC_KEY(GOST_3410_PrivateKey, "secp128r1"); + EC_KEY(GOST_3410_PrivateKey, "secp160r1"); + EC_KEY(GOST_3410_PrivateKey, "secp192r1"); + EC_KEY(GOST_3410_PrivateKey, "secp224r1"); + EC_KEY(GOST_3410_PrivateKey, "secp256r1"); + EC_KEY(GOST_3410_PrivateKey, "secp384r1"); + EC_KEY(GOST_3410_PrivateKey, "secp521r1"); +#endif + + return fails; + } + +size_t test_pubkey() + { + AutoSeeded_RNG rng; + const std::string filename = CHECKS_DIR "/pk_valid.dat"; + std::ifstream test_data(filename.c_str()); + + if(!test_data) + throw Botan::Stream_IO_Error("Couldn't open test file " + filename); + + size_t total_errors = 0; + size_t errors = 0, alg_count = 0, total_tests = 0; + std::string algorithm, print_algorithm; + + while(!test_data.eof()) + { + if(test_data.bad() || test_data.fail()) + throw std::runtime_error("File I/O error reading from " + filename); + + std::string line; + std::getline(test_data, line); + + strip_comments(line); + if(line.size() == 0) continue; + + // Do line continuation + while(line[line.size()-1] == '\\' && !test_data.eof()) + { + line.replace(line.size()-1, 1, ""); + std::string nextline; + std::getline(test_data, nextline); + strip_comments(nextline); + if(nextline.size() == 0) continue; + line.push_back('\n'); + line += nextline; + } + + if(line[0] == '[' && line[line.size() - 1] == ']') + { + const std::string old_algo = print_algorithm; + algorithm = line.substr(1, line.size() - 2); + print_algorithm = algorithm; + if(print_algorithm.find("_PKCS8") != std::string::npos) + print_algorithm.replace(print_algorithm.find("_PKCS8"), 6, ""); + if(print_algorithm.find("_X509") != std::string::npos) + print_algorithm.replace(print_algorithm.find("_X509"), 5, ""); + if(print_algorithm.find("_VA") != std::string::npos) + print_algorithm.replace(print_algorithm.find("_VA"), 3, ""); + + if(old_algo != print_algorithm && old_algo != "") + { + test_report(old_algo, alg_count, errors); + alg_count = 0; + total_errors += errors; + errors = 0; + } + + continue; + } + + std::vector<std::string> substr = parse(line); + + size_t new_errors = 0; + + try + { + + if(algorithm.find("DSA/") == 0) + new_errors = validate_dsa_sig(algorithm, substr, rng); + else if(algorithm.find("DSA_VA/") == 0) + new_errors = validate_dsa_ver(algorithm, substr); + + else if(algorithm.find("ECDSA/") == 0) + new_errors = validate_ecdsa_sig(algorithm, substr, rng); + + else if(algorithm.find("GOST_3410_VA/") == 0) + new_errors = validate_gost_ver(algorithm, substr); + + else if(algorithm.find("RSAES_PKCS8/") == 0) + new_errors = validate_rsa_enc_pkcs8(algorithm, substr, rng); + else if(algorithm.find("RSAVA_X509/") == 0) + new_errors = validate_rsa_ver_x509(algorithm, substr); + + else if(algorithm.find("RSAES/") == 0) + new_errors = validate_rsa_enc(algorithm, substr, rng); + else if(algorithm.find("RSASSA/") == 0) + new_errors = validate_rsa_sig(algorithm, substr, rng); + else if(algorithm.find("RSAVA/") == 0) + new_errors = validate_rsa_ver(algorithm, substr); + else if(algorithm.find("RWVA/") == 0) + new_errors = validate_rw_ver(algorithm, substr); + else if(algorithm.find("RW/") == 0) + new_errors = validate_rw_sig(algorithm, substr, rng); + else if(algorithm.find("NR/") == 0) + new_errors = validate_nr_sig(algorithm, substr, rng); + else if(algorithm.find("ElGamal/") == 0) + new_errors = validate_elg_enc(algorithm, substr, rng); + else if(algorithm.find("DH/") == 0) + new_errors = validate_dh(algorithm, substr, rng); + else if(algorithm.find("DLIES/") == 0) + new_errors = validate_dlies(algorithm, substr, rng); + else + { + std::cout << "WARNING: Unknown PK algorithm " + << algorithm << std::endl; + ++new_errors; + } + + alg_count++; + total_tests++; + errors += new_errors; + } + catch(std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + new_errors++; + } + + if(new_errors) + std::cout << "ERROR: \"" << algorithm << "\" failed test #" + << std::dec << alg_count << std::endl; + } + + test_report("Pubkey", total_tests, total_errors); + + return total_errors; + } diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp new file mode 100644 index 000000000..99b2fbf8f --- /dev/null +++ b/src/tests/test_rng.cpp @@ -0,0 +1,63 @@ +#include "tests.h" +#include "test_rng.h" + +#include <botan/libstate.h> +#include <botan/x931_rng.h> +#include <botan/aes.h> +#include <botan/des.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> +#include <deque> + +using namespace Botan; + +namespace { + +RandomNumberGenerator* get_x931(const std::string& algo, const std::string& ikm_hex) + { + const auto ikm = hex_decode(ikm_hex); + + if(algo == "X9.31-RNG(TripleDES)") + return new ANSI_X931_RNG(new TripleDES, new Fixed_Output_RNG(ikm)); + else if(algo == "X9.31-RNG(AES-128)") + return new ANSI_X931_RNG(new AES_128, new Fixed_Output_RNG(ikm)); + else if(algo == "X9.31-RNG(AES-192)") + return new ANSI_X931_RNG(new AES_192, new Fixed_Output_RNG(ikm)); + else if(algo == "X9.31-RNG(AES-256)") + return new ANSI_X931_RNG(new AES_256, new Fixed_Output_RNG(ikm)); + + return nullptr; + } + +bool x931_test(const std::string& algo, + const std::string& ikm, + const std::string& out, + size_t L) + { + std::unique_ptr<RandomNumberGenerator> x931(get_x931(algo, ikm)); + x931->reseed(0); + + const std::string got = hex_encode(x931->random_vec(L)); + + if(got != out) + { + std::cout << "X9.31 " << got << " != " << out << "\n"; + return false; + } + + return true; + } + +} + +size_t test_rngs() + { + std::ifstream vec(CHECKS_DIR "/x931.vec"); + + return run_tests_bb(vec, "RNG", "Out", true, + [](std::map<std::string, std::string> m) -> bool + { + return x931_test(m["RNG"], m["IKM"], m["Out"], to_u32bit(m["L"])); + }); + } diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h new file mode 100644 index 000000000..b3da5f08a --- /dev/null +++ b/src/tests/test_rng.h @@ -0,0 +1,61 @@ + +#ifndef BOTAN_TESTS_FIXED_RNG_H__ +#define BOTAN_TESTS_FIXED_RNG_H__ + +#include <deque> +#include <string> +#include <stdexcept> +#include <botan/rng.h> +#include <botan/hex.h> + +using Botan::byte; + +class Fixed_Output_RNG : public Botan::RandomNumberGenerator + { + public: + bool is_seeded() const { return !buf.empty(); } + + byte random() + { + if(!is_seeded()) + throw std::runtime_error("Out of bytes"); + + byte out = buf.front(); + buf.pop_front(); + return out; + } + + void reseed(size_t) {} + + void randomize(byte out[], size_t len) + { + for(size_t j = 0; j != len; j++) + out[j] = random(); + } + + void add_entropy(const byte b[], size_t s) + { + buf.insert(buf.end(), b, b + s); + } + + std::string name() const { return "Fixed_Output_RNG"; } + + void clear() throw() {} + + Fixed_Output_RNG(const std::vector<byte>& in) + { + buf.insert(buf.end(), in.begin(), in.end()); + } + + Fixed_Output_RNG(const std::string& in_str) + { + std::vector<byte> in = Botan::hex_decode(in_str); + buf.insert(buf.end(), in.begin(), in.end()); + } + + Fixed_Output_RNG() {} + private: + std::deque<byte> buf; + }; + +#endif diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp new file mode 100644 index 000000000..9846fb521 --- /dev/null +++ b/src/tests/test_stream.cpp @@ -0,0 +1,71 @@ +#include "tests.h" + +#include <botan/libstate.h> +#include <botan/stream_cipher.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +bool stream_test(const std::string& algo, + const std::string& key_hex, + const std::string& in_hex, + const std::string& out_hex, + const std::string& nonce_hex) + { + const secure_vector<byte> key = hex_decode_locked(key_hex); + const secure_vector<byte> pt = hex_decode_locked(in_hex); + const secure_vector<byte> ct = hex_decode_locked(out_hex); + const secure_vector<byte> nonce = hex_decode_locked(nonce_hex); + + Algorithm_Factory& af = global_state().algorithm_factory(); + + const auto providers = af.providers_of(algo); + size_t fails = 0; + + for(auto provider: providers) + { + const StreamCipher* proto = af.prototype_stream_cipher(algo, provider); + + if(!proto) + { + std::cout << "Unable to get " << algo << " from " << provider << "\n"; + ++fails; + continue; + } + + std::unique_ptr<StreamCipher> cipher(proto->clone()); + cipher->set_key(key); + + if(nonce.size()) + cipher->set_iv(&nonce[0], nonce.size()); + + secure_vector<byte> buf = pt; + + cipher->encrypt(buf); + + if(buf != ct) + { + std::cout << algo << " " << provider << " enc " << hex_encode(buf) << " != " << out_hex << "\n"; + ++fails; + } + } + + return (fails == 0); + } + +} + +size_t test_stream() + { + std::ifstream vec(CHECKS_DIR "/stream.vec"); + + return run_tests_bb(vec, "StreamCipher", "Out", true, + [](std::map<std::string, std::string> m) -> bool + { + return stream_test(m["StreamCipher"], m["Key"], m["In"], m["Out"], m["Nonce"]); + }); + } diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp new file mode 100644 index 000000000..af3627217 --- /dev/null +++ b/src/tests/test_tls.cpp @@ -0,0 +1,281 @@ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS) + +#include <botan/auto_rng.h> +#include <botan/tls_server.h> +#include <botan/tls_client.h> +#include <botan/pkcs10.h> +#include <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/x509_ca.h> +#include <botan/hex.h> + +#include <iostream> +#include <vector> +#include <memory> + +using namespace Botan; + +namespace { + +class Credentials_Manager_Test : public Botan::Credentials_Manager + { + public: + Credentials_Manager_Test(const X509_Certificate& server_cert, + const X509_Certificate& ca_cert, + Private_Key* server_key) : + m_server_cert(server_cert), + m_ca_cert(ca_cert), + m_key(server_key) + { + auto store = new Certificate_Store_In_Memory; + store->add_certificate(m_ca_cert); + m_stores.push_back(store); + } + + std::vector<Certificate_Store*> + trusted_certificate_authorities(const std::string&, + const std::string&) override + { + return m_stores; + } + + std::vector<X509_Certificate> cert_chain( + const std::vector<std::string>& cert_key_types, + const std::string& type, + const std::string&) override + { + std::vector<X509_Certificate> chain; + + if(type == "tls-server") + { + bool have_match = false; + for(size_t i = 0; i != cert_key_types.size(); ++i) + if(cert_key_types[i] == m_key->algo_name()) + have_match = true; + + if(have_match) + { + chain.push_back(m_server_cert); + chain.push_back(m_ca_cert); + } + } + + return chain; + } + + void verify_certificate_chain( + const std::string& type, + const std::string& purported_hostname, + const std::vector<Botan::X509_Certificate>& cert_chain) override + { + try + { + Credentials_Manager::verify_certificate_chain(type, + purported_hostname, + cert_chain); + } + catch(std::exception& e) + { + std::cout << "Certificate verification failed - " << e.what() << " - but will ignore\n"; + } + } + + Private_Key* private_key_for(const X509_Certificate&, + const std::string&, + const std::string&) override + { + return m_key; + } + public: + X509_Certificate m_server_cert, m_ca_cert; + Private_Key* m_key; + std::vector<Certificate_Store*> m_stores; + }; + +Credentials_Manager* create_creds(RandomNumberGenerator& rng) + { + std::auto_ptr<Private_Key> ca_key(new RSA_PrivateKey(rng, 1024)); + + X509_Cert_Options ca_opts; + ca_opts.common_name = "Test CA"; + ca_opts.country = "US"; + ca_opts.CA_key(1); + + X509_Certificate ca_cert = + X509::create_self_signed_cert(ca_opts, + *ca_key, + "SHA-256", + rng); + + Private_Key* server_key = new RSA_PrivateKey(rng, 1024); + + X509_Cert_Options server_opts; + server_opts.common_name = "localhost"; + server_opts.country = "US"; + + PKCS10_Request req = X509::create_cert_req(server_opts, + *server_key, + "SHA-256", + rng); + + X509_CA ca(ca_cert, *ca_key, "SHA-256"); + + auto now = std::chrono::system_clock::now(); + X509_Time start_time(now); + typedef std::chrono::duration<int, std::ratio<31556926>> years; + X509_Time end_time(now + years(1)); + + X509_Certificate server_cert = ca.sign_request(req, + rng, + start_time, + end_time); + + return new Credentials_Manager_Test(server_cert, ca_cert, server_key); + } + + +size_t test_handshake() + { + AutoSeeded_RNG rng; + TLS::Policy default_policy; + + std::auto_ptr<Credentials_Manager> creds(create_creds(rng)); + + TLS::Session_Manager_In_Memory server_sessions(rng); + TLS::Session_Manager_In_Memory client_sessions(rng); + + std::vector<byte> c2s_q, s2c_q, c2s_data, s2c_data; + + auto handshake_complete = [](const TLS::Session& session) -> bool + { + if(false) + { + std::cout << "Handshake complete, " << session.version().to_string() + << " using " << session.ciphersuite().to_string() << "\n"; + + if(!session.session_id().empty()) + std::cout << "Session ID " << hex_encode(session.session_id()) << "\n"; + + if(!session.session_ticket().empty()) + std::cout << "Session ticket " << hex_encode(session.session_ticket()) << "\n"; + } + + return true; + }; + + auto print_alert = [&](TLS::Alert alert, const byte[], size_t) + { + if(alert.is_valid()) + std::cout << "Server recvd alert " << alert.type_string() << "\n"; + }; + + auto save_server_data = [&](const byte buf[], size_t sz) + { + c2s_data.insert(c2s_data.end(), buf, buf+sz); + }; + + auto save_client_data = [&](const byte buf[], size_t sz) + { + s2c_data.insert(s2c_data.end(), buf, buf+sz); + }; + + TLS::Server server([&](const byte buf[], size_t sz) + { s2c_q.insert(s2c_q.end(), buf, buf+sz); }, + save_server_data, + print_alert, + handshake_complete, + server_sessions, + *creds, + default_policy, + rng); + + TLS::Client client([&](const byte buf[], size_t sz) + { c2s_q.insert(c2s_q.end(), buf, buf+sz); }, + save_client_data, + print_alert, + handshake_complete, + client_sessions, + *creds, + default_policy, + rng); + + while(true) + { + if(client.is_active()) + client.send("1"); + if(server.is_active()) + server.send("2"); + + /* + * Use this as a temp value to hold the queues as otherwise they + * might end up appending more in response to messages during the + * handshake. + */ + std::vector<byte> input; + std::swap(c2s_q, input); + + try + { + server.received_data(&input[0], input.size()); + } + catch(std::exception& e) + { + std::cout << "Server error - " << e.what() << "\n"; + break; + } + + input.clear(); + std::swap(s2c_q, input); + + try + { + client.received_data(&input[0], input.size()); + } + catch(std::exception& e) + { + std::cout << "Client error - " << e.what() << "\n"; + break; + } + + if(c2s_data.size()) + { + if(c2s_data[0] != '1') + { + std::cout << "Error\n"; + return 1; + } + } + + if(s2c_data.size()) + { + if(s2c_data[0] != '2') + { + std::cout << "Error\n"; + return 1; + } + } + + if(s2c_data.size() && c2s_data.size()) + break; + } + + return 0; + } + +} + +size_t test_tls() + { + size_t errors = 0; + + errors += test_handshake(); + + return errors; + } + +#else +size_t test_tls() { return 0; } +#endif diff --git a/src/tests/test_transform.cpp b/src/tests/test_transform.cpp new file mode 100644 index 000000000..dc54d1761 --- /dev/null +++ b/src/tests/test_transform.cpp @@ -0,0 +1,50 @@ +#include "tests.h" + +#include <botan/botan.h> +#include <botan/transform.h> +#include <botan/threefish.h> +#include <botan/benchmark.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +namespace { + +Transformation* get_transform(const std::string& algo) + { + throw std::runtime_error("Unknown transform " + algo); + } + +secure_vector<byte> transform_test(const std::string& algo, + const secure_vector<byte>& nonce, + const secure_vector<byte>& key, + const secure_vector<byte>& in) + { + std::unique_ptr<Transformation> transform(get_transform(algo)); + + transform->set_key(key); + transform->start_vec(nonce); + + secure_vector<byte> out = in; + transform->update(out, 0); + + return out; + } + +} + +size_t test_transform() + { + std::ifstream vec(CHECKS_DIR "/transform.vec"); + + return run_tests(vec, "Transform", "Output", true, + [](std::map<std::string, std::string> m) + { + return hex_encode(transform_test(m["Transform"], + hex_decode_locked(m["Nonce"]), + hex_decode_locked(m["Key"]), + hex_decode_locked(m["Input"]))); + }); + } diff --git a/src/tests/test_x509.cpp b/src/tests/test_x509.cpp new file mode 100644 index 000000000..cd0beec5b --- /dev/null +++ b/src/tests/test_x509.cpp @@ -0,0 +1,255 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "tests.h" + +#include <botan/filters.h> +#include <botan/auto_rng.h> + +#if defined(BOTAN_HAS_RSA) + #include <botan/rsa.h> +#endif + +#if defined(BOTAN_HAS_DSA) + #include <botan/dsa.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + #include <botan/x509self.h> + #include <botan/x509path.h> + #include <botan/x509_ca.h> + #include <botan/pkcs10.h> +#endif + +using namespace Botan; + +#include <iostream> +#include <memory> + + +#if defined(BOTAN_HAS_X509_CERTIFICATES) && \ + defined(BOTAN_HAS_RSA) && \ + defined(BOTAN_HAS_DSA) + +namespace { + +u64bit key_id(const Public_Key* key) + { + Pipe pipe(new Hash_Filter("SHA-1", 8)); + pipe.start_msg(); + pipe.write(key->algo_name()); + pipe.write(key->algorithm_identifier().parameters); + pipe.write(key->x509_subject_public_key()); + pipe.end_msg(); + + secure_vector<byte> output = pipe.read_all(); + + if(output.size() != 8) + throw Internal_Error("Public_Key::key_id: Incorrect output size"); + + u64bit id = 0; + for(u32bit j = 0; j != 8; ++j) + id = (id << 8) | output[j]; + return id; + } + + +/* Return some option sets */ +X509_Cert_Options ca_opts() + { + X509_Cert_Options opts("Test CA/US/Botan Project/Testing"); + + opts.uri = "http://botan.randombit.net"; + opts.dns = "botan.randombit.net"; + opts.email = "[email protected]"; + + opts.CA_key(1); + + return opts; + } + +X509_Cert_Options req_opts1() + { + X509_Cert_Options opts("Test User 1/US/Botan Project/Testing"); + + opts.uri = "http://botan.randombit.net"; + opts.dns = "botan.randombit.net"; + opts.email = "[email protected]"; + + return opts; + } + +X509_Cert_Options req_opts2() + { + X509_Cert_Options opts("Test User 2/US/Botan Project/Testing"); + + opts.uri = "http://botan.randombit.net"; + opts.dns = "botan.randombit.net"; + opts.email = "[email protected]"; + + opts.add_ex_constraint("PKIX.EmailProtection"); + + return opts; + } + +u32bit check_against_copy(const Private_Key& orig, + RandomNumberGenerator& rng) + { + Private_Key* copy_priv = PKCS8::copy_key(orig, rng); + Public_Key* copy_pub = X509::copy_key(orig); + + const std::string passphrase= "I need work! -Mr. T"; + DataSource_Memory enc_source(PKCS8::PEM_encode(orig, rng, passphrase)); + Private_Key* copy_priv_enc = PKCS8::load_key(enc_source, rng, + passphrase); + + u64bit orig_id = key_id(&orig); + u64bit pub_id = key_id(copy_pub); + u64bit priv_id = key_id(copy_priv); + u64bit priv_enc_id = key_id(copy_priv_enc); + + delete copy_pub; + delete copy_priv; + delete copy_priv_enc; + + if(orig_id != pub_id || orig_id != priv_id || orig_id != priv_enc_id) + { + std::cout << "Failed copy check for " << orig.algo_name() << "\n"; + return 1; + } + return 0; + } + +} + +size_t test_x509() + { + AutoSeeded_RNG rng; + const std::string hash_fn = "SHA-256"; + + size_t fails = 0; + + /* Create the CA's key and self-signed cert */ + RSA_PrivateKey ca_key(rng, 2048); + + X509_Certificate ca_cert = X509::create_self_signed_cert(ca_opts(), + ca_key, + hash_fn, + rng); + /* Create user #1's key and cert request */ + DSA_PrivateKey user1_key(rng, DL_Group("dsa/botan/2048")); + + PKCS10_Request user1_req = X509::create_cert_req(req_opts1(), + user1_key, + "SHA-1", + rng); + + /* Create user #2's key and cert request */ +#if defined(BOTAN_HAS_ECDSA) + EC_Group ecc_domain(OID("1.2.840.10045.3.1.7")); + ECDSA_PrivateKey user2_key(rng, ecc_domain); +#else + RSA_PrivateKey user2_key(rng, 1536); +#endif + + PKCS10_Request user2_req = X509::create_cert_req(req_opts2(), + user2_key, + hash_fn, + rng); + + /* Create the CA object */ + X509_CA ca(ca_cert, ca_key, hash_fn); + + /* Sign the requests to create the certs */ + X509_Certificate user1_cert = + ca.sign_request(user1_req, rng, + X509_Time("2008-01-01"), X509_Time("2100-01-01")); + + X509_Certificate user2_cert = ca.sign_request(user2_req, rng, + X509_Time("2008-01-01"), + X509_Time("2100-01-01")); + X509_CRL crl1 = ca.new_crl(rng); + + /* Verify the certs */ + Certificate_Store_In_Memory store; + + store.add_certificate(ca_cert); + + Path_Validation_Restrictions restrictions; + + Path_Validation_Result result_u1 = x509_path_validate(user1_cert, restrictions, store); + if(!result_u1.successful_validation()) + { + std::cout << "FAILED: User cert #1 did not validate - " + << result_u1.result_string() << std::endl; + ++fails; + } + + Path_Validation_Result result_u2 = x509_path_validate(user2_cert, restrictions, store); + if(!result_u2.successful_validation()) + { + std::cout << "FAILED: User cert #2 did not validate - " + << result_u2.result_string() << std::endl; + ++fails; + } + + store.add_crl(crl1); + + std::vector<CRL_Entry> revoked; + revoked.push_back(CRL_Entry(user1_cert, CESSATION_OF_OPERATION)); + revoked.push_back(user2_cert); + + X509_CRL crl2 = ca.update_crl(crl1, revoked, rng); + + store.add_crl(crl2); + + result_u1 = x509_path_validate(user1_cert, restrictions, store); + if(result_u1.result() != Certificate_Status_Code::CERT_IS_REVOKED) + { + std::cout << "FAILED: User cert #1 was not revoked - " + << result_u1.result_string() << std::endl; + ++fails; + } + + result_u2 = x509_path_validate(user2_cert, restrictions, store); + if(result_u2.result() != Certificate_Status_Code::CERT_IS_REVOKED) + { + std::cout << "FAILED: User cert #2 was not revoked - " + << result_u2.result_string() << std::endl; + ++fails; + } + + revoked.clear(); + revoked.push_back(CRL_Entry(user1_cert, REMOVE_FROM_CRL)); + X509_CRL crl3 = ca.update_crl(crl2, revoked, rng); + + store.add_crl(crl3); + + result_u1 = x509_path_validate(user1_cert, restrictions, store); + if(!result_u1.successful_validation()) + { + std::cout << "FAILED: User cert #1 was not un-revoked - " + << result_u1.result_string() << std::endl; + ++fails; + } + + check_against_copy(ca_key, rng); + check_against_copy(user1_key, rng); + check_against_copy(user2_key, rng); + + return fails; + } + +#else + +size_t test_x590() { return 0; } + +#endif + diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp new file mode 100644 index 000000000..49f55c165 --- /dev/null +++ b/src/tests/tests.cpp @@ -0,0 +1,190 @@ +#include "tests.h" +#include <iostream> + +size_t run_tests(const std::vector<test_fn>& tests) + { + size_t fails = 0; + + for(auto& test : tests) + { + try + { + fails += test(); + } + catch(std::exception& e) + { + std::cout << "Exception escaped test: " << e.what() << "\n"; + ++fails; + } + catch(...) + { + std::cout << "Exception escaped test\n"; + ++fails; + } + } + + test_report("Tests", 0, fails); + + return fails; + } + +void test_report(const std::string& name, size_t ran, size_t failed) + { + std::cout << name; + + if(ran > 0) + std::cout << " " << ran << " tests"; + + if(failed) + std::cout << " " << failed << " FAILs\n"; + else + std::cout << " all ok\n"; + } + +size_t run_tests_bb(std::istream& src, + const std::string& name_key, + const std::string& output_key, + bool clear_between_cb, + std::function<bool (std::map<std::string, std::string>)> cb) + { + if(!src.good()) + { + std::cout << "Could not open input file for " << name_key << "\n"; + return 1; + } + + std::map<std::string, std::string> vars; + size_t test_fails = 0, algo_fail = 0; + size_t test_count = 0, algo_count = 0; + + std::string fixed_name; + + while(src.good()) + { + std::string line; + std::getline(src, line); + + if(line == "") + continue; + + if(line[0] == '#') + continue; + + if(line[0] == '[' && line[line.size()-1] == ']') + { + if(fixed_name != "") + test_report(fixed_name, algo_count, algo_fail); + + test_count += algo_count; + test_fails += algo_fail; + algo_count = 0; + algo_fail = 0; + fixed_name = line.substr(1, line.size() - 2); + vars[name_key] = fixed_name; + continue; + } + + const std::string key = line.substr(0, line.find_first_of(' ')); + const std::string val = line.substr(line.find_last_of(' ') + 1, std::string::npos); + + vars[key] = val; + + if(key == name_key) + fixed_name.clear(); + + if(key == output_key) + { + //std::cout << vars[name_key] << " " << test_cnt << "\n"; + ++algo_count; + try + { + if(!cb(vars)) + { + std::cout << vars[name_key] << " test " << algo_count << " failed\n"; + ++algo_fail; + } + } + catch(std::exception& e) + { + std::cout << vars[name_key] << " test " << algo_count << " failed: " << e.what() << "\n"; + ++algo_fail; + } + + if(clear_between_cb) + { + vars.clear(); + vars[name_key] = fixed_name; + } + } + } + + if(fixed_name != "" && (algo_count > 0 || algo_fail > 0)) + test_report(fixed_name, algo_count, algo_fail); + + test_count += algo_count; + test_fails += algo_fail; + + test_report(name_key, test_count, test_fails); + + return test_fails; + } + +size_t run_tests(std::istream& src, + const std::string& name_key, + const std::string& output_key, + bool clear_between_cb, + std::function<std::string (std::map<std::string, std::string>)> cb) + { + return run_tests_bb(src, name_key, output_key, clear_between_cb, + [name_key,output_key,cb](std::map<std::string, std::string> vars) + { + const std::string got = cb(vars); + if(got != vars[output_key]) + { + std::cout << name_key << ' ' << vars[name_key] << " got " << got + << " expected " << vars[output_key] << std::endl; + return false; + } + return true; + }); + } + +size_t run_all_tests() + { + std::vector<test_fn> all_tests; + + all_tests.push_back(test_block); + all_tests.push_back(test_stream); + all_tests.push_back(test_hash); + all_tests.push_back(test_mac); + + all_tests.push_back(test_modes); + + all_tests.push_back(test_aead); + all_tests.push_back(test_ocb); + all_tests.push_back(test_eax); + + all_tests.push_back(test_pbkdf); + all_tests.push_back(test_kdf); + all_tests.push_back(test_hkdf); + all_tests.push_back(test_keywrap); + all_tests.push_back(test_transform); + + all_tests.push_back(test_rngs); + all_tests.push_back(test_passhash9); + all_tests.push_back(test_bcrypt); + all_tests.push_back(test_cryptobox); + + all_tests.push_back(test_bigint); + all_tests.push_back(test_pubkey); + + all_tests.push_back(test_ecc); + all_tests.push_back(test_ecdsa); + all_tests.push_back(test_ecdh); + all_tests.push_back(test_pk_keygen); + all_tests.push_back(test_cvc); + all_tests.push_back(test_x509); + all_tests.push_back(test_tls); + + return run_tests(all_tests); + } diff --git a/src/tests/tests.h b/src/tests/tests.h new file mode 100644 index 000000000..cb1e0f585 --- /dev/null +++ b/src/tests/tests.h @@ -0,0 +1,77 @@ + +#ifndef BOTAN_TESTS_H__ +#define BOTAN_TESTS_H__ + +#include <functional> +#include <istream> +#include <map> +#include <string> +#include <vector> + +#include "../common.h" + +size_t run_tests_bb(std::istream& src, + const std::string& name_key, + const std::string& output_key, + bool clear_between_cb, + std::function<bool (std::map<std::string, std::string>)> cb); + +size_t run_tests(std::istream& src, + const std::string& name_key, + const std::string& output_key, + bool clear_between_cb, + std::function<std::string (std::map<std::string, std::string>)> cb); + +// Run a list of tests +typedef std::function<size_t ()> test_fn; + +size_t run_tests(const std::vector<test_fn>& tests); +void test_report(const std::string& name, size_t ran, size_t failed); + +#define TEST(expr, msg) do { if(!(expr)) { ++fails; std::cout << msg; } while(0) + +#define CHECKS_DIR "src/test-data/" + +size_t run_all_tests(); + +// Tests using reader framework above +size_t test_block(); +size_t test_stream(); +size_t test_hash(); +size_t test_mac(); + +size_t test_modes(); + +size_t test_rngs(); + +size_t test_hkdf(); +size_t test_pbkdf(); +size_t test_kdf(); +size_t test_aead(); +size_t test_transform(); + +// One off tests +size_t test_ocb(); +size_t test_eax(); +size_t test_keywrap(); +size_t test_bcrypt(); +size_t test_passhash9(); +size_t test_cryptobox(); + +size_t test_bigint(); + +size_t test_pubkey(); +size_t test_pk_keygen(); + +size_t test_ecc(); + +size_t test_ecdsa(); +size_t test_ecdh(); + +size_t test_x509(); +size_t test_cvc(); + +size_t test_tls(); +size_t test_nist_x509(); + +#endif |