From cf05aea092fad448c2f4a8e8b66159237096ba8e Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 11 Nov 2015 05:43:01 -0500 Subject: Update and consolidate the test framework. The tests previously had used 4 to 6 different schemes internally (the vec file reader framework, Catch, the old InSiTo Boost.Test tests, the PK/BigInt tests which escaped the rewrite in 1.11.7, plus a number of one-offs). Converge on a design that works everywhere, and update all the things. Fix also a few bugs found by the test changes: SHA-512-256 name incorrect, OpenSSL RC4 name incorrect, signature of FFI function botan_pubkey_destroy was wrong. --- src/tests/tests.h | 482 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 315 insertions(+), 167 deletions(-) (limited to 'src/tests/tests.h') diff --git a/src/tests/tests.h b/src/tests/tests.h index 1e496ccb2..c431eb6bd 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -1,3 +1,4 @@ + /* * (C) 2014,2015 Jack Lloyd * (C) 2015 Simon Warta (Kullo GmbH) @@ -10,105 +11,340 @@ #include #include +#include + +#if defined(BOTAN_HAS_BIGINT) + #include +#endif + +#if defined(BOTAN_HAS_EC_CURVE_GFP) + #include +#endif + +#include #include -#include #include +#include +#include +#include #include +#include #include -#include -#include -Botan::RandomNumberGenerator& test_rng(); +namespace Botan_Tests { -size_t run_tests_bb(std::istream& src, - const std::string& name_key, - const std::string& output_key, - bool clear_between_cb, - std::function)> cb); +using Botan::byte; -size_t run_tests(std::istream& src, - const std::string& name_key, - const std::string& output_key, - bool clear_between_cb, - std::function)> cb); +#if defined(BOTAN_HAS_BIGINT) +using Botan::BigInt; +#endif -size_t run_tests(const std::string& filename, - const std::string& name_key, - const std::string& output_key, - bool clear_between_cb, - std::function)> cb); +/* +* A generic test which retuns a set of results when run. +* The tests may not all have the same type (for example test +* "block" returns results for "AES-128" and "AES-256"). +* +* For most test cases you want Text_Based_Test derived below +*/ +class Test + { + public: -size_t run_tests_in_dir(const std::string& dir, std::function fn); + /* + * Some number of test results, all associated with who() + */ + class Result + { + public: + Result(const std::string& who = "") : m_who(who) {} + + size_t tests_passed() const { return m_tests_passed; } + size_t tests_failed() const { return m_fail_log.size(); } + size_t tests_run() const { return tests_passed() + tests_failed(); } + bool any_results() const { return tests_run() > 0; } + + const std::string& who() const { return m_who; } + std::string result_string() const; + + static Result Failure(const std::string& who, + const std::string& what) + { + Result r(who); + r.test_failure(what); + return r; + } + + static Result Note(const std::string& who, + const std::string& what) + { + Result r(who); + r.test_note(what); + return r; + } + + void merge(const Result& other); + + void test_note(const std::string& note); + + void note_missing(const std::string& thing); + + bool test_success(const std::string& note = ""); + + bool test_failure(const std::string& err); + + bool test_failure(const char* what, const char* error); + + void test_failure(const char* what, const uint8_t buf[], size_t buf_len); + + template + void test_failure(const char* what, const std::vector& buf) + { + test_failure(what, buf.data(), buf.size()); + } + + bool confirm(const char* what, bool expr) + { + return test_eq(what, expr, true); + } + + template + bool test_is_eq(const T& produced, const T& expected) + { + return test_is_eq(nullptr, produced, expected); + } + + template + bool test_is_eq(const char* what, const T& produced, const T& expected) + { + std::ostringstream out; + out << m_who; + if(what) + out << " " << what; + + if(produced == expected) + { + out << " produced expected result " << produced; + return test_success(out.str()); + } + else + { + out << " produced unexpected result " << produced << " expected " << expected; + return test_failure(out.str()); + } + } + + bool test_eq(const char* what, const char* produced, const char* expected); + bool test_eq(const char* what, const std::string& produced, const std::string& expected); + bool test_eq(const char* what, bool produced, bool expected); + + bool test_eq(const char* what, size_t produced, size_t expected); + bool test_lt(const char* what, size_t produced, size_t expected); + bool test_gte(const char* what, size_t produced, size_t expected); + + bool test_rc_ok(const char* func, int rc); + bool test_rc_fail(const char* func, const char* why, int rc); + +#if defined(BOTAN_HAS_BIGINT) + bool test_eq(const char* what, const BigInt& produced, const BigInt& expected); + bool test_ne(const char* what, const BigInt& produced, const BigInt& expected); +#endif -// Run a list of tests -typedef std::function test_fn; +#if defined(BOTAN_HAS_EC_CURVE_GFP) + bool test_eq(const char* what, const Botan::PointGFp& a, const Botan::PointGFp& b); +#endif -size_t run_tests(const std::vector>& tests); -void test_report(const std::string& name, size_t ran, size_t failed); + bool test_eq(const char* producer, const char* what, + const uint8_t produced[], size_t produced_len, + const uint8_t expected[], size_t expected_len); + + bool test_ne(const char* what, + const uint8_t produced[], size_t produced_len, + const uint8_t expected[], size_t expected_len); + + template + bool test_eq(const char* what, + const std::vector& produced, + const std::vector& expected) + { + return test_eq(nullptr, what, + produced.data(), produced.size(), + expected.data(), expected.size()); + } + + template + bool test_eq(const std::string& producer, const char* what, + const std::vector& produced, + const std::vector& expected) + { + return test_eq(producer.c_str(), what, + produced.data(), produced.size(), + expected.data(), expected.size()); + } + + template + bool test_eq(const char* what, + const std::vector& produced, + const char* expected_hex) + { + const std::vector expected = Botan::hex_decode(expected_hex); + return test_eq(nullptr, what, + produced.data(), produced.size(), + expected.data(), expected.size()); + } + + template + bool test_ne(const char* what, + const std::vector& produced, + const std::vector& expected) + { + return test_ne(what, + produced.data(), produced.size(), + expected.data(), expected.size()); + } + + bool test_throws(const std::string& what, std::function fn); + + void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; } + + private: + std::string m_who; + uint64_t m_ns_taken = 0; + size_t m_tests_passed = 0; + std::vector m_fail_log; + std::vector m_log; + }; + + class Registration + { + public: + Registration(const std::string& name, Test* test); + }; -class Test_State - { - public: - void started(const std::string& /*msg*/) { m_tests_run++; } + virtual std::vector run() = 0; + virtual ~Test() {} + + static std::vector run_test(const std::string& what, bool fail_if_missing); + + static std::map>& global_registry(); - void test_ran(const char* msg); + static std::set registered_tests(); - void failure(const char* test, const std::string& what_failed) + static Test* get_test(const std::string& test_name); + + static std::string data_dir(const std::string& what); + static std::string data_file(const std::string& what); + + template + static std::vector mutate_vec(const std::vector& v, bool maybe_resize = false) { - std::cout << "FAIL " << test << " " << what_failed << "\n"; - m_tests_failed++; + auto& rng = Test::rng(); + + std::vector r = v; + + if(maybe_resize && (r.empty() || rng.next_byte() < 32)) + { + // TODO: occasionally truncate, insert at random index + const size_t add = 1 + (rng.next_byte() % 16); + r.resize(r.size() + add); + rng.randomize(&r[r.size() - add], add); + } + + if(r.size() > 0) + { + const size_t offset = rng.get_random() % r.size(); + r[offset] ^= rng.next_nonzero_byte(); + } + + return r; } - size_t ran() const { return m_tests_run; } - size_t failed() const { return m_tests_failed; } + static void setup_tests(size_t soak, bool log_succcss, Botan::RandomNumberGenerator* rng); + + static size_t soak_level(); + static bool log_success(); + + static Botan::RandomNumberGenerator& rng(); + static std::string random_password(); + static uint64_t timestamp(); // nanoseconds arbitrary epoch + + private: + static Botan::RandomNumberGenerator* m_test_rng; + static size_t m_soak_level; + static bool m_log_success; + }; + +/* +* Register the test with the runner +*/ +#define BOTAN_REGISTER_TEST(type, Test_Class) namespace { Test::Registration reg_ ## Test_Class ## _tests(type, new Test_Class); } + +/* +* A test based on reading an input file which contains key/value pairs +* Special note: the last value in required_key (there must be at least +* one), is the output key. This triggers the callback. +* +* Calls run_one_test with the variables set. If an ini-style [header] +* is used in the file, then header will be set to that value. This allows +* splitting up tests between [valid] and [invalid] tests, or different +* related algorithms tested in the same file. Use the protected get_XXX +* functions to retrieve formatted values from the VarMap +* +* If most of your tests are text-based but you find yourself with a few +* odds-and-ends tests that you want to do, override run_final_tests which +* can test whatever it likes and returns a vector of Results. +*/ +class Text_Based_Test : public Test + { + public: + Text_Based_Test(const std::string& input_file, + const std::vector& required_keys, + const std::vector& optional_keys = {}); + + Text_Based_Test(const std::string& algo, + const std::string& input_file, + const std::vector& required_keys, + const std::vector& optional_keys = {}); + + virtual bool clear_between_callbacks() const { return true; } + + std::vector run() override; + protected: + typedef std::unordered_map VarMap; + std::string get_next_line(); + + virtual Test::Result run_one_test(const std::string& header, + const VarMap& vars) = 0; + + virtual std::vector run_final_tests() { return std::vector(); } + + std::vector get_req_bin(const VarMap& vars, const std::string& key) const; + std::vector get_opt_bin(const VarMap& vars, const std::string& key) const; + +#if defined(BOTAN_HAS_BIGINT) + Botan::BigInt get_req_bn(const VarMap& vars, const std::string& key) const; +#endif + + std::string get_req_str(const VarMap& vars, const std::string& key) const; + std::string get_opt_str(const VarMap& vars, const std::string& key, const std::string& def_value) const; + + size_t get_req_sz(const VarMap& vars, const std::string& key) const; + size_t get_opt_sz(const VarMap& vars, const std::string& key, const size_t def_value) const; + + std::string algo_name() const { return m_algo; } private: - size_t m_tests_run = 0, m_tests_failed = 0; + std::string m_algo; + std::string m_data_dir; + std::set m_required_keys; + std::set m_optional_keys; + std::string m_output_key; + bool m_clear_between_cb = false; + + bool m_first = true; + std::unique_ptr m_cur; + std::deque m_srcs; }; -#define BOTAN_CONFIRM_NOTHROW(block) do { \ - try { block } \ - catch(std::exception& e) { \ - _test.failure(BOTAN_CURRENT_FUNCTION, e.what()); \ - } } while(0) \ - -#define BOTAN_TEST(lhs, rhs, msg) do { \ - _test.started(msg); \ - BOTAN_CONFIRM_NOTHROW({ \ - const auto lhs_val = lhs; \ - const auto rhs_val = rhs; \ - const bool cmp = lhs_val == rhs_val; \ - if(!cmp) \ - { \ - std::ostringstream fmt; \ - fmt << "expr '" << #lhs << " == " << #rhs << "' false, " \ - << "actually " << lhs_val << " " << rhs_val \ - << " (" << msg << ")"; \ - _test.failure(BOTAN_CURRENT_FUNCTION, fmt.str()); \ - } \ - }); \ - } while(0) - -#define BOTAN_CONFIRM(expr, msg) do { \ - _test.started(msg); \ - BOTAN_CONFIRM_NOTHROW({ \ - const bool expr_val = expr; \ - if(!expr_val) \ - { \ - std::ostringstream fmt; \ - fmt << "expr '" << #expr << " false (" << msg << ")"; \ - _test.failure(BOTAN_CURRENT_FUNCTION, fmt.str()); \ - } \ - }); \ - } while(0) - -#define BOTAN_TEST_CASE(name, descr, block) size_t test_ ## name() { \ - Test_State _test; \ - BOTAN_CONFIRM_NOTHROW(block); \ - test_report(descr, _test.ran(), _test.failed()); \ - return _test.failed(); \ - } - -//#define TEST(expr, msg) do { if(!(expr)) { ++fails; std::cout << msg; } while(0) +} #define TEST_DATA_DIR "src/tests/data" #define TEST_DATA_DIR_PK "src/tests/data/pubkey" @@ -116,92 +352,4 @@ class Test_State #define TEST_OUTDATA_DIR "src/tests/outdata" -int test_main(int argc, char* argv[]); - -// 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_pbkdf(); -size_t test_kdf(); -size_t test_aead(); -size_t test_transform(); - -size_t test_rsa(); -size_t test_rw(); -size_t test_dsa(); -size_t test_nr(); -size_t test_dh(); -size_t test_dlies(); -size_t test_elgamal(); -size_t test_ecc_pointmul(); -size_t test_ecc_random(); -size_t test_ecdsa(); -size_t test_gost_3410(); -size_t test_curve25519(); -size_t test_gf2m(); -size_t test_mceliece(); -size_t test_mce(); - -// One off tests -size_t test_ocb(); -size_t test_keywrap(); -size_t test_bcrypt(); -size_t test_passhash9(); -size_t test_cryptobox(); -size_t test_tss(); -size_t test_rfc6979(); - -size_t test_pk_keygen(); - -size_t test_bigint(); - -size_t test_ecc_unit(); -size_t test_ecc_randomized(); -size_t test_ecdsa_unit(); -size_t test_ecdh_unit(); - -size_t test_x509(); -size_t test_x509_x509test(); -size_t test_cvc(); - -size_t test_tls(); - -size_t test_nist_x509(); - -size_t test_srp6(); -size_t test_compression(); - -size_t test_fuzzer(); - -#define SKIP_TEST(testname) \ - size_t test_ ## testname() { \ - std::cout << "Skipping tests: " << # testname << std::endl; \ - return 0; \ - } \ - -/* - * Warn if a test requires loading more modules than necessary to build - * the lib. E.g. - * $ ./configure.py --no-autoload --enable-modules='ocb' - * $ make - * $ ./botan-test ocb - * warns the user whereas - * $ ./configure.py --no-autoload --enable-modules='ocb,aes' - * $ make - * $ ./botan-test ocb - * runs the test. - */ -#define UNTESTED_WARNING(testname) \ - size_t test_ ## testname() { \ - std::cout << "Skipping tests: " << # testname << std::endl; \ - std::cout << "WARNING: " << # testname << " has been compiled " \ - << "but is not tested due to other missing modules." \ - << std::endl; \ - return 0; \ - } \ - #endif -- cgit v1.2.3 From 75dbef27700d22478a5845476bd7b120f72cfe2c Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 11 Nov 2015 08:36:09 -0500 Subject: Fix occasional test fails Increase the iterations of the BigInt::random_integer test. Since things get slow quickly, leave the larger range tests to higher soak levels. In TLS, if the corrupted data causes an exception immediately that's ok because it's corrupted data. --- src/tests/test_bigint.cpp | 36 +++++++++++++++++++++++++++++++----- src/tests/tests.cpp | 17 +++++++++++++++++ src/tests/tests.h | 4 ++++ src/tests/unit_tls.cpp | 20 ++++++++++++++++++-- 4 files changed, 70 insertions(+), 7 deletions(-) (limited to 'src/tests/tests.h') diff --git a/src/tests/test_bigint.cpp b/src/tests/test_bigint.cpp index 07158fee1..96ec035fe 100644 --- a/src/tests/test_bigint.cpp +++ b/src/tests/test_bigint.cpp @@ -80,11 +80,29 @@ class BigInt_Unit_Tests : public Test { Test::Result result("BigInt::random_integer"); - const size_t ITERATIONS = 1000; + result.start_timer(); - for(size_t range_min : { 0, 10, 100 }) + const size_t ITERATIONS = 5000; + + std::vector min_ranges{ 0 }; + std::vector max_ranges{ 10 }; + + // This gets slow quickly: + if(Test::soak_level() > 10) { - for(size_t range_max : { 0, 10, 100 }) + min_ranges.push_back(10); + max_ranges.push_back(100); + + if(Test::soak_level() > 50) + { + min_ranges.push_back(79); + max_ranges.push_back(293); + } + } + + for(size_t range_min : min_ranges) + { + for(size_t range_max : max_ranges) { if(range_min >= range_max) continue; @@ -104,14 +122,22 @@ class BigInt_Unit_Tests : public Test double ratio = static_cast(counts[i]) / ITERATIONS; double dev = std::min(ratio, std::fabs(1.0 - ratio)); - if(dev > .15) + if(dev < .15) { - result.test_failure("random_integer distribution" + std::to_string(dev)); + result.test_success("distribution within expected range"); + } + else + { + result.test_failure("distribution " + std::to_string(dev) + + " outside expected range with count" + + std::to_string(counts[i])); } } } } + result.end_timer(); + return result; } }; diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 704ff36a8..3137c5ba7 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -37,6 +37,23 @@ void Test::Result::merge(const Result& other) m_log.insert(m_log.end(), other.m_log.begin(), other.m_log.end()); } +void Test::Result::start_timer() + { + if(m_started == 0) + { + m_started = Test::timestamp(); + } + } + +void Test::Result::end_timer() + { + if(m_started > 0) + { + m_ns_taken += Test::timestamp() - m_started; + m_started = 0; + } + } + void Test::Result::test_note(const std::string& note) { if(note != "") diff --git a/src/tests/tests.h b/src/tests/tests.h index c431eb6bd..09d89459d 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -206,8 +206,12 @@ class Test void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; } + void start_timer(); + void end_timer(); + private: std::string m_who; + uint64_t m_started = 0; uint64_t m_ns_taken = 0; size_t m_tests_passed = 0; std::vector m_fail_log; diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 6c15ec4e9..8502352fc 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -173,6 +173,8 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, { bool handshake_done = false; + result.test_note("Test round " + std::to_string(r)); + auto handshake_complete = [&](const Botan::TLS::Session& session) -> bool { handshake_done = true; @@ -279,7 +281,14 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, } catch(std::exception& e) { - result.test_failure("server error", e.what()); + if(corrupt_server_data) + { + result.test_note("corruption caused server exception"); + } + else + { + result.test_failure("server error", e.what()); + } continue; } @@ -294,7 +303,14 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, } catch(std::exception& e) { - result.test_failure("client error", e.what()); + if(corrupt_client_data) + { + result.test_note("corruption caused client exception"); + } + else + { + result.test_failure("client error", e.what()); + } continue; } -- cgit v1.2.3 From 3dbcfb6297acfdb8818742acfb0fa9ffe70bcdbc Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 11 Nov 2015 10:52:33 -0500 Subject: Avoid calling get_files_recursive on a possible non-directory. The Boost directory iterator throws an exception in that case. Interestingly the current VC version does not seem to. In the interests of portability just avoid this operation rather than trying to hack around it in the Boost fs version. GH #328 --- src/tests/tests.cpp | 17 +++++++++-------- src/tests/tests.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src/tests/tests.h') diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 3137c5ba7..55c9e3824 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -484,10 +484,10 @@ std::string Test::random_password() return Botan::hex_encode(Test::rng().random_vec(len)); } -Text_Based_Test::Text_Based_Test(const std::string& data_dir, +Text_Based_Test::Text_Based_Test(const std::string& data_src, const std::vector& required_keys, const std::vector& optional_keys) : - m_data_dir(data_dir) + m_data_src(data_src) { if(required_keys.empty()) throw std::runtime_error("Invalid test spec"); @@ -498,11 +498,11 @@ Text_Based_Test::Text_Based_Test(const std::string& data_dir, } Text_Based_Test::Text_Based_Test(const std::string& algo, - const std::string& data_dir, + const std::string& data_src, const std::vector& required_keys, const std::vector& optional_keys) : m_algo(algo), - m_data_dir(data_dir) + m_data_src(data_src) { if(required_keys.empty()) throw std::runtime_error("Invalid test spec"); @@ -611,15 +611,16 @@ std::string Text_Based_Test::get_next_line() { if(m_first) { - std::vector fs = Botan::get_files_recursive(m_data_dir); - - if(fs.empty() && m_data_dir.find(".vec") != std::string::npos) + if(m_data_src.find(".vec") != std::string::npos) { - m_srcs.push_back(m_data_dir); + m_srcs.push_back(m_data_src); } else { + const auto fs = Botan::get_files_recursive(m_data_src); m_srcs.assign(fs.begin(), fs.end()); + if(m_srcs.empty()) + throw std::runtime_error("Error reading test data dir " + m_data_src); } m_first = false; diff --git a/src/tests/tests.h b/src/tests/tests.h index 09d89459d..273035cbf 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -337,7 +337,7 @@ class Text_Based_Test : public Test std::string algo_name() const { return m_algo; } private: std::string m_algo; - std::string m_data_dir; + std::string m_data_src; std::set m_required_keys; std::set m_optional_keys; std::string m_output_key; -- cgit v1.2.3 From e2480dedcabf44fcc41641785c81bcafb16727fc Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Mon, 16 Nov 2015 16:18:42 -0500 Subject: Add helper for test output files. Remove last uses of hardcoded data dir macros --- src/tests/test_cvc.cpp | 66 +++++++++++++++++++++++------------------------ src/tests/test_fuzzer.cpp | 4 +-- src/tests/tests.cpp | 17 ++++++++++-- src/tests/tests.h | 33 +++++++++++++++++++----- src/tests/unit_ecdsa.cpp | 28 ++++++++++---------- 5 files changed, 88 insertions(+), 60 deletions(-) (limited to 'src/tests/tests.h') diff --git a/src/tests/test_cvc.cpp b/src/tests/test_cvc.cpp index 74b91d0ed..dc4b50ebd 100644 --- a/src/tests/test_cvc.cpp +++ b/src/tests/test_cvc.cpp @@ -19,8 +19,6 @@ #include #include -#define CVC_TEST_DATA_DIR TEST_DATA_DIR "/ecc" - #endif namespace Botan_Tests { @@ -124,22 +122,22 @@ Test::Result test_enc_gen_selfsigned() std::vector der(cert.BER_encode()); std::ofstream cert_file; - cert_file.open(CVC_TEST_DATA_DIR "/my_cv_cert.ber", std::ios::binary); + cert_file.open(Test::data_file("ecc/my_cv_cert.ber"), std::ios::binary); cert_file.write((char*)der.data(), der.size()); cert_file.close(); - EAC1_1_CVC cert_in(CVC_TEST_DATA_DIR "/my_cv_cert.ber"); + EAC1_1_CVC cert_in(Test::data_file("ecc/my_cv_cert.ber")); result.confirm("reloaded cert matches", cert_in == cert); // encoding it again while it has no dp std::vector der2(cert_in.BER_encode()); - std::ofstream cert_file2(CVC_TEST_DATA_DIR "/my_cv_cert2.ber", std::ios::binary); + std::ofstream cert_file2(Test::data_file("ecc/my_cv_cert2.ber"), std::ios::binary); cert_file2.write((char*)der2.data(), der2.size()); cert_file2.close(); // read both and compare them - std::ifstream cert_1_in(CVC_TEST_DATA_DIR "/my_cv_cert.ber"); - std::ifstream cert_2_in(CVC_TEST_DATA_DIR "/my_cv_cert2.ber"); + std::ifstream cert_1_in(Test::data_file("ecc/my_cv_cert.ber")); + std::ifstream cert_2_in(Test::data_file("ecc/my_cv_cert2.ber")); std::vector sv1; std::vector sv2; if (!cert_1_in || cert_2_in) @@ -225,12 +223,12 @@ Test::Result test_enc_gen_req() key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); EAC1_1_Req req = CVC_EAC::create_cvc_req(key, opts.chr, opts.hash_alg, Test::rng()); std::vector der(req.BER_encode()); - std::ofstream req_file(CVC_TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); + std::ofstream req_file(Test::data_file("ecc/my_cv_req.ber"), std::ios::binary); req_file.write((char*)der.data(), der.size()); req_file.close(); // read and check signature... - EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/my_cv_req.ber"); + EAC1_1_Req req_in(Test::data_file("ecc/my_cv_req.ber")); //req_in.set_domain_parameters(dom_pars); std::unique_ptr p_pk(req_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast(p_pk.get()); @@ -243,7 +241,7 @@ Test::Result test_enc_gen_req() Test::Result test_cvc_req_ext() { - EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); + EAC1_1_Req req_in(Test::data_file("ecc/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 p_pk(req_in.subject_public_key()); @@ -271,7 +269,7 @@ Test::Result test_cvc_ado_creation() //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, Test::rng()); std::vector der(req.BER_encode()); - std::ofstream req_file(CVC_TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); + std::ofstream req_file(Test::data_file("ecc/my_cv_req.ber"), std::ios::binary); req_file.write((char*)der.data(), der.size()); req_file.close(); @@ -285,12 +283,12 @@ Test::Result test_cvc_ado_creation() EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts.car, Test::rng()); result.confirm("ADO signature verifies", ado.check_signature(ado_key)); - std::ofstream ado_file(CVC_TEST_DATA_DIR "/ado", std::ios::binary); + std::ofstream ado_file(Test::data_file("ecc/ado"), std::ios::binary); std::vector ado_der(ado.BER_encode()); ado_file.write((char*)ado_der.data(), ado_der.size()); ado_file.close(); // read it again and check the signature - EAC1_1_ADO ado2(CVC_TEST_DATA_DIR "/ado"); + EAC1_1_ADO ado2(Test::data_file("ecc/ado")); result.confirm("ADOs match", ado == ado2); result.confirm("ADO signature valid", ado2.check_signature(ado_key)); @@ -341,13 +339,13 @@ Test::Result test_cvc_ado_comparison() result.confirm("ADO signature after creation", ado2.check_signature(ado_key2)); result.confirm("ADOs should not be equal", ado != ado2); - // std::ofstream ado_file(CVC_TEST_DATA_DIR "/ado"); + // std::ofstream ado_file(Test::data_file("ecc/ado")); // std::vector ado_der(ado.BER_encode()); // ado_file.write((char*)ado_der.data(), ado_der.size()); // ado_file.close(); // read it again and check the signature - // EAC1_1_ADO ado2(CVC_TEST_DATA_DIR "/ado"); + // EAC1_1_ADO ado2(Test::data_file("ecc/ado")); // ECDSA_PublicKey* p_ado_pk = dynamic_cast(&ado_key); // //bool ver = ado2.check_signature(*p_ado_pk); // bool ver = ado2.check_signature(ado_key); @@ -396,7 +394,7 @@ Test::Result test_ver_cvca() { Test::Result result("CVC"); - EAC1_1_CVC cvc(CVC_TEST_DATA_DIR "/cvca01.cv.crt"); + EAC1_1_CVC cvc(Test::data_file("ecc/cvca01.cv.crt")); std::unique_ptr p_pk2(cvc.subject_public_key()); result.confirm("verified CVCA cert", cvc.check_signature(*p_pk2)); @@ -419,20 +417,20 @@ Test::Result test_copy_and_assignment() { Test::Result result("CVC"); - EAC1_1_CVC cert_in(CVC_TEST_DATA_DIR "/cvca01.cv.crt"); + EAC1_1_CVC cert_in(Test::data_file("ecc/cvca01.cv.crt")); EAC1_1_CVC cert_cp(cert_in); EAC1_1_CVC cert_ass = cert_in; result.confirm("same cert", cert_in == cert_cp); result.confirm("same cert", cert_in == cert_ass); - EAC1_1_ADO ado_in(CVC_TEST_DATA_DIR "/ado.cvcreq"); + EAC1_1_ADO ado_in(Test::data_file("ecc/ado.cvcreq")); EAC1_1_ADO ado_cp(ado_in); EAC1_1_ADO ado_ass = ado_in; result.confirm("same", ado_in == ado_cp); result.confirm("same", ado_in == ado_ass); - EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); + EAC1_1_Req req_in(Test::data_file("ecc/DE1_flen_chars_cvcRequest_ECDSA.der")); EAC1_1_Req req_cp(req_in); EAC1_1_Req req_ass = req_in; result.confirm("same", req_in == req_cp); @@ -447,14 +445,14 @@ Test::Result test_eac_str_illegal_values() try { - EAC1_1_CVC(CVC_TEST_DATA_DIR "/cvca_illegal_chars.cv.crt"); + EAC1_1_CVC(Test::data_file("ecc/cvca_illegal_chars.cv.crt")); result.test_failure("Accepted invalid EAC 1.1 CVC"); } catch (Decoding_Error) {} try { - EAC1_1_CVC(CVC_TEST_DATA_DIR "/cvca_illegal_chars2.cv.crt"); + EAC1_1_CVC(Test::data_file("ecc/cvca_illegal_chars2.cv.crt")); result.test_failure("Accepted invalid EAC 1.1 CVC #2"); } catch (Decoding_Error) {} @@ -484,7 +482,7 @@ Test::Result test_cvc_chain() 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, Test::rng()); - std::ofstream cvca_file(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer", std::ios::binary); + std::ofstream cvca_file(Test::data_file("ecc/cvc_chain_cvca.cer"), std::ios::binary); std::vector cvca_sv = cvca_cert.BER_encode(); cvca_file.write((char*)cvca_sv.data(), cvca_sv.size()); cvca_file.close(); @@ -494,21 +492,21 @@ Test::Result test_cvc_chain() EAC1_1_CVC cvca_cert2 = DE_EAC::create_cvca(cvca_privk2, hash, car2, true, true, 12, Test::rng()); EAC1_1_CVC link12 = DE_EAC::link_cvca(cvca_cert, cvca_privk, cvca_cert2, Test::rng()); std::vector link12_sv = link12.BER_encode(); - std::ofstream link12_file(CVC_TEST_DATA_DIR "/cvc_chain_link12.cer", std::ios::binary); + std::ofstream link12_file(Test::data_file("ecc/cvc_chain_link12.cer"), std::ios::binary); link12_file.write((char*)link12_sv.data(), link12_sv.size()); link12_file.close(); // verify the link result.confirm("signature valid", link12.check_signature(cvca_privk)); - EAC1_1_CVC link12_reloaded(CVC_TEST_DATA_DIR "/cvc_chain_link12.cer"); - EAC1_1_CVC cvca1_reloaded(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer"); + EAC1_1_CVC link12_reloaded(Test::data_file("ecc/cvc_chain_link12.cer")); + EAC1_1_CVC cvca1_reloaded(Test::data_file("ecc/cvc_chain_cvca.cer")); std::unique_ptr cvca1_rel_pk(cvca1_reloaded.subject_public_key()); result.confirm("signature valid", link12_reloaded.check_signature(*cvca1_rel_pk)); // create first round dvca-req ECDSA_PrivateKey dvca_priv_key(Test::rng(), dom_pars); EAC1_1_Req dvca_req = DE_EAC::create_cvc_req(dvca_priv_key, ASN1_Chr("DEDVCAEPASS"), hash, Test::rng()); - std::ofstream dvca_file(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req.cer", std::ios::binary); + std::ofstream dvca_file(Test::data_file("ecc/cvc_chain_dvca_req.cer"), std::ios::binary); std::vector dvca_sv = dvca_req.BER_encode(); dvca_file.write((char*)dvca_sv.data(), dvca_sv.size()); dvca_file.close(); @@ -517,18 +515,18 @@ Test::Result test_cvc_chain() EAC1_1_CVC dvca_cert1 = DE_EAC::sign_request(cvca_cert, cvca_privk, dvca_req, 1, 5, true, 3, 1, Test::rng()); result.test_eq("DVCA car", dvca_cert1.get_car().iso_8859(), "DECVCA00001"); result.test_eq("DVCA chr", dvca_cert1.get_chr().iso_8859(), "DEDVCAEPASS00001"); - helper_write_file(dvca_cert1, CVC_TEST_DATA_DIR "/cvc_chain_dvca_cert1.cer"); + helper_write_file(dvca_cert1, Test::data_file("ecc/cvc_chain_dvca_cert1.cer")); // make a second round dvca ado request ECDSA_PrivateKey dvca_priv_key2(Test::rng(), dom_pars); EAC1_1_Req dvca_req2 = DE_EAC::create_cvc_req(dvca_priv_key2, ASN1_Chr("DEDVCAEPASS"), hash, Test::rng()); - std::ofstream dvca_file2(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2.cer", std::ios::binary); + std::ofstream dvca_file2(Test::data_file("ecc/cvc_chain_dvca_req2.cer"), std::ios::binary); std::vector dvca_sv2 = dvca_req2.BER_encode(); dvca_file2.write((char*)dvca_sv2.data(), 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()), Test::rng()); - helper_write_file(dvca_ado2, CVC_TEST_DATA_DIR "/cvc_chain_dvca_ado2.cer"); + helper_write_file(dvca_ado2, Test::data_file("ecc/cvc_chain_dvca_ado2.cer")); // verify the ado and sign the request too @@ -536,13 +534,13 @@ Test::Result test_cvc_chain() ECDSA_PublicKey* cert_pk = dynamic_cast(ap_pk.get()); //cert_pk->set_domain_parameters(dom_pars); - EAC1_1_CVC dvca_cert1_reread(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer"); + EAC1_1_CVC dvca_cert1_reread(Test::data_file("ecc/cvc_chain_cvca.cer")); result.confirm("signature valid", dvca_ado2.check_signature(*cert_pk)); result.confirm("signature valid", dvca_ado2.check_signature(dvca_priv_key)); // must also work EAC1_1_Req dvca_req2b = dvca_ado2.get_request(); - helper_write_file(dvca_req2b, CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer"); - result.confirm("files match", helper_files_equal(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer", CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2.cer")); + helper_write_file(dvca_req2b, Test::data_file("ecc/cvc_chain_dvca_req2b.cer")); + result.confirm("files match", helper_files_equal(Test::data_file("ecc/cvc_chain_dvca_req2b.cer"), Test::data_file("ecc/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, Test::rng()); result.test_eq("DVCA car", dvca_cert2.get_car().iso_8859(), "DECVCA00001"); result.test_eq("DVCA chr", dvca_cert2.get_chr().iso_8859(), "DEDVCAEPASS00002"); @@ -550,14 +548,14 @@ Test::Result test_cvc_chain() // make a first round IS request ECDSA_PrivateKey is_priv_key(Test::rng(), dom_pars); EAC1_1_Req is_req = DE_EAC::create_cvc_req(is_priv_key, ASN1_Chr("DEIS"), hash, Test::rng()); - helper_write_file(is_req, CVC_TEST_DATA_DIR "/cvc_chain_is_req.cer"); + helper_write_file(is_req, Test::data_file("ecc/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, Test::rng()); result.test_eq("EAC 1.1 CVC car", is_cert1.get_car().iso_8859(), "DEDVCAEPASS00001"); result.test_eq("EAC 1.1 CVC chr", is_cert1.get_chr().iso_8859(), "DEIS00001"); - helper_write_file(is_cert1, CVC_TEST_DATA_DIR "/cvc_chain_is_cert.cer"); + helper_write_file(is_cert1, Test::data_file("ecc/cvc_chain_is_cert.cer")); // verify the signature of the certificate result.confirm("valid signature", is_cert1.check_signature(dvca_priv_key)); diff --git a/src/tests/test_fuzzer.cpp b/src/tests/test_fuzzer.cpp index 7ce972a33..91d301826 100644 --- a/src/tests/test_fuzzer.cpp +++ b/src/tests/test_fuzzer.cpp @@ -35,15 +35,13 @@ class Fuzzer_Input_Tests : public Test #if defined(BOTAN_HAS_X509_CERTIFICATES) Test::Result test_x509_fuzz() { - const std::string TEST_DATA_DIR_FUZZ_X509 = TEST_DATA_DIR "/fuzz/x509"; - Test::Result result("X.509 fuzzing"); std::vector files; try { - files = Botan::get_files_recursive(TEST_DATA_DIR_FUZZ_X509); + files = Botan::get_files_recursive(Test::data_dir("fuzz/x509")); } catch(Botan::No_Filesystem_Access) { diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 294ef303b..e421d245c 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -14,6 +14,9 @@ namespace Botan_Tests { +#define TEST_DATA_DIR "src/tests/data" +#define TEST_OUTDATA_DIR "src/tests/outdata" + Test::Registration::Registration(const std::string& name, Test* test) { if(Test::global_registry().count(name) == 0) @@ -54,11 +57,15 @@ void Test::Result::end_timer() } } -void Test::Result::test_note(const std::string& note) +void Test::Result::test_note(const std::string& note, const char* extra) { if(note != "") { - m_log.push_back(who() + " " + note); + std::ostringstream out; + out << who() << " " << note; + if(extra) + out << ": " << extra; + m_log.push_back(out.str()); } } @@ -445,6 +452,12 @@ std::string Test::data_file(const std::string& what) return std::string(TEST_DATA_DIR) + "/" + what; } +//static +std::string Test::full_path_for_output_file(const std::string& base) + { + return std::string(TEST_OUTDATA_DIR) + "/" + base; + } + // static member variables of Test Botan::RandomNumberGenerator* Test::m_test_rng = nullptr; size_t Test::m_soak_level = 0; diff --git a/src/tests/tests.h b/src/tests/tests.h index 273035cbf..9f511c4fb 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -82,9 +82,31 @@ class Test return r; } + static Result OfExpectedFailure(bool expecting_failure, + const Test::Result& result) + { + if(!expecting_failure) + { + return result; + } + + if(result.tests_failed() == 0) + { + Result r = result; + r.test_failure("Expected this test to fail, but it did not"); + return r; + } + else + { + Result r(result.who()); + r.test_note("Got expected failure " + result.result_string()); + return r; + } + } + void merge(const Result& other); - void test_note(const std::string& note); + void test_note(const std::string& note, const char* extra = nullptr); void note_missing(const std::string& thing); @@ -119,7 +141,9 @@ class Test std::ostringstream out; out << m_who; if(what) + { out << " " << what; + } if(produced == expected) { @@ -237,6 +261,7 @@ class Test static std::string data_dir(const std::string& what); static std::string data_file(const std::string& what); + static std::string full_path_for_output_file(const std::string& base); template static std::vector mutate_vec(const std::vector& v, bool maybe_resize = false) @@ -350,10 +375,4 @@ class Text_Based_Test : public Test } -#define TEST_DATA_DIR "src/tests/data" -#define TEST_DATA_DIR_PK "src/tests/data/pubkey" -#define TEST_DATA_DIR_ECC "src/tests/data/ecc" - -#define TEST_OUTDATA_DIR "src/tests/outdata" - #endif diff --git a/src/tests/unit_ecdsa.cpp b/src/tests/unit_ecdsa.cpp index f47d0ae71..ed9096083 100644 --- a/src/tests/unit_ecdsa.cpp +++ b/src/tests/unit_ecdsa.cpp @@ -90,7 +90,7 @@ Test::Result test_hash_larger_than_n() Test::Result test_decode_ecdsa_X509() { Test::Result result("ECDSA Unit"); - Botan::X509_Certificate cert(TEST_DATA_DIR_ECC "/CSCA.CSCA.csca-germany.1.crt"); + Botan::X509_Certificate cert(Test::data_file("ecc/CSCA.CSCA.csca-germany.1.crt")); result.test_eq("correct signature oid", Botan::OIDS::lookup(cert.signature_algorithm().oid), "ECDSA/EMSA1(SHA-224)"); @@ -107,8 +107,8 @@ Test::Result test_decode_ecdsa_X509() Test::Result test_decode_ver_link_SHA256() { Test::Result result("ECDSA Unit"); - Botan::X509_Certificate root_cert(TEST_DATA_DIR_ECC "/root2_SHA256.cer"); - Botan::X509_Certificate link_cert(TEST_DATA_DIR_ECC "/link_SHA256.cer"); + Botan::X509_Certificate root_cert(Test::data_file("ecc/root2_SHA256.cer")); + Botan::X509_Certificate link_cert(Test::data_file("ecc/link_SHA256.cer")); std::unique_ptr pubkey(root_cert.subject_public_key()); result.confirm("verified self-signed signature", link_cert.check_signature(*pubkey)); @@ -117,8 +117,8 @@ Test::Result test_decode_ver_link_SHA256() Test::Result test_decode_ver_link_SHA1() { - Botan::X509_Certificate root_cert(TEST_DATA_DIR_ECC "/root_SHA1.163.crt"); - Botan::X509_Certificate link_cert(TEST_DATA_DIR_ECC "/link_SHA1.166.crt"); + Botan::X509_Certificate root_cert(Test::data_file("ecc/root_SHA1.163.crt")); + Botan::X509_Certificate link_cert(Test::data_file("ecc/link_SHA1.166.crt")); Test::Result result("ECDSA Unit"); std::unique_ptr pubkey(root_cert.subject_public_key()); @@ -206,7 +206,7 @@ Test::Result test_create_pkcs8() #if defined(BOTAN_HAS_RSA) Botan::RSA_PrivateKey rsa_key(Test::rng(), 1024); - std::ofstream rsa_priv_key(TEST_OUTDATA_DIR "/rsa_private.pkcs8.pem"); + std::ofstream rsa_priv_key(Test::full_path_for_output_file("rsa_private.pkcs8.pem")); rsa_priv_key << Botan::PKCS8::PEM_encode(rsa_key); #endif @@ -214,7 +214,7 @@ Test::Result test_create_pkcs8() Botan::ECDSA_PrivateKey key(Test::rng(), dom_pars); // later used by other tests :( - std::ofstream priv_key(TEST_OUTDATA_DIR "/wo_dompar_private.pkcs8.pem"); + std::ofstream priv_key(Test::full_path_for_output_file("wo_dompar_private.pkcs8.pem")); priv_key << Botan::PKCS8::PEM_encode(key); } catch (std::exception& e) @@ -231,15 +231,15 @@ Test::Result test_create_and_verify() Botan::EC_Group dom_pars(Botan::OID("1.3.132.0.8")); Botan::ECDSA_PrivateKey key(Test::rng(), dom_pars); - std::ofstream priv_key(TEST_OUTDATA_DIR "/dompar_private.pkcs8.pem"); + std::ofstream priv_key(Test::full_path_for_output_file("dompar_private.pkcs8.pem")); priv_key << Botan::PKCS8::PEM_encode(key); - std::unique_ptr loaded_key(Botan::PKCS8::load_key(TEST_OUTDATA_DIR "/wo_dompar_private.pkcs8.pem", Test::rng())); + std::unique_ptr loaded_key(Botan::PKCS8::load_key(Test::full_path_for_output_file("wo_dompar_private.pkcs8.pem"), Test::rng())); Botan::ECDSA_PrivateKey* loaded_ec_key = dynamic_cast(loaded_key.get()); result.confirm("the loaded key could not be converted into an ECDSA_PrivateKey", loaded_ec_key); #if defined(BOTAN_HAS_RSA) - std::unique_ptr loaded_key_1(Botan::PKCS8::load_key(TEST_OUTDATA_DIR "/rsa_private.pkcs8.pem", Test::rng())); + std::unique_ptr loaded_key_1(Botan::PKCS8::load_key(Test::full_path_for_output_file("rsa_private.pkcs8.pem"), Test::rng())); Botan::ECDSA_PrivateKey* loaded_rsa_key = dynamic_cast(loaded_key_1.get()); result.test_eq("loaded key type corrected", loaded_key_1->algo_name(), "RSA"); result.confirm("RSA key cannot be casted to ECDSA", !loaded_rsa_key); @@ -336,7 +336,7 @@ Test::Result test_read_pkcs8() try { - std::unique_ptr loaded_key(Botan::PKCS8::load_key(TEST_OUTDATA_DIR "/wo_dompar_private.pkcs8.pem", Test::rng())); + std::unique_ptr loaded_key(Botan::PKCS8::load_key(Test::full_path_for_output_file("wo_dompar_private.pkcs8.pem"), Test::rng())); Botan::ECDSA_PrivateKey* ecdsa = dynamic_cast(loaded_key.get()); result.confirm("key loaded", ecdsa); @@ -355,7 +355,7 @@ Test::Result test_read_pkcs8() try { - std::unique_ptr loaded_key_nodp(Botan::PKCS8::load_key(TEST_DATA_DIR_ECC "/nodompar_private.pkcs8.pem", Test::rng())); + std::unique_ptr loaded_key_nodp(Botan::PKCS8::load_key(Test::data_file("ecc/nodompar_private.pkcs8.pem"), Test::rng())); // anew in each test with unregistered domain-parameters Botan::ECDSA_PrivateKey* ecdsa_nodp = dynamic_cast(loaded_key_nodp.get()); result.confirm("key loaded", ecdsa_nodp); @@ -370,7 +370,7 @@ Test::Result test_read_pkcs8() try { std::unique_ptr loaded_key_withdp( - Botan::PKCS8::load_key(TEST_DATA_DIR_ECC "/withdompar_private.pkcs8.pem", Test::rng())); + Botan::PKCS8::load_key(Test::data_file("ecc/withdompar_private.pkcs8.pem"), Test::rng())); result.test_failure("loaded key with unknown OID"); } @@ -394,7 +394,7 @@ Test::Result test_ecc_key_with_rfc5915_extensions() try { std::unique_ptr pkcs8( - Botan::PKCS8::load_key(TEST_DATA_DIR_ECC "/ecc_private_with_rfc5915_ext.pem", Test::rng())); + Botan::PKCS8::load_key(Test::data_file("ecc/ecc_private_with_rfc5915_ext.pem"), Test::rng())); result.confirm("loaded RFC 5914 key", pkcs8.get()); result.test_eq("key is ECDSA", pkcs8->algo_name(), "ECDSA"); -- cgit v1.2.3