2
* (C) 2014,2015 Jack Lloyd
4
* Botan is released under the Simplified BSD License (see license.txt)
12
#include <botan/hex.h>
13
#include <botan/parsing.h>
14
#include <botan/internal/filesystem.h>
15
#include <botan/internal/bit_ops.h>
16
#include <botan/internal/stl_util.h>
18
#if defined(BOTAN_HAS_BIGINT)
19
#include <botan/bigint.h>
22
#if defined(BOTAN_HAS_EC_CURVE_GFP)
23
#include <botan/point_gfp.h>
26
namespace Botan_Tests {
28
Test::Registration::Registration(const std::string& name, Test* test)
30
if(Test::global_registry().count(name) == 0)
32
Test::global_registry().insert(std::make_pair(name, std::unique_ptr<Test>(test)));
36
throw Test_Error("Duplicate registration of test '" + name + "'");
40
void Test::Result::merge(const Result& other)
42
if(who() != other.who())
44
throw Test_Error("Merging tests from different sources");
47
m_ns_taken += other.m_ns_taken;
48
m_tests_passed += other.m_tests_passed;
49
m_fail_log.insert(m_fail_log.end(), other.m_fail_log.begin(), other.m_fail_log.end());
50
m_log.insert(m_log.end(), other.m_log.begin(), other.m_log.end());
53
void Test::Result::start_timer()
57
m_started = Test::timestamp();
61
void Test::Result::end_timer()
65
m_ns_taken += Test::timestamp() - m_started;
70
void Test::Result::test_note(const std::string& note, const char* extra)
74
std::ostringstream out;
75
out << who() << " " << note;
80
m_log.push_back(out.str());
84
void Test::Result::note_missing(const std::string& whatever)
86
static std::set<std::string> s_already_seen;
88
if(s_already_seen.count(whatever) == 0)
90
test_note("Skipping tests due to missing " + whatever);
91
s_already_seen.insert(whatever);
95
bool Test::Result::test_throws(const std::string& what, std::function<void ()> fn)
100
return test_failure(what + " failed to throw expected exception");
102
catch(std::exception& e)
104
return test_success(what + " threw exception " + e.what());
108
return test_success(what + " threw unknown exception");
112
bool Test::Result::test_throws(const std::string& what, const std::string& expected, std::function<void ()> fn)
117
return test_failure(what + " failed to throw expected exception");
119
catch(std::exception& e)
121
if(expected == e.what())
123
return test_success(what + " threw exception " + e.what());
127
return test_failure(what + " failed to throw an exception with the expected text:\n Expected: " + expected +
128
"\n Got: " + e.what());
133
return test_failure(what + " failed to throw an exception with the expected text:\n Expected: " + expected);
137
bool Test::Result::test_success(const std::string& note)
139
if(Test::log_success())
147
bool Test::Result::test_failure(const std::string& what, const std::string& error)
149
return test_failure(who() + " " + what + " with error " + error);
152
void Test::Result::test_failure(const std::string& what, const uint8_t buf[], size_t buf_len)
154
test_failure(who() + ": " + what +
155
" buf len " + std::to_string(buf_len) +
156
" value " + Botan::hex_encode(buf, buf_len));
159
bool Test::Result::test_failure(const std::string& err)
161
m_fail_log.push_back(err);
165
bool Test::Result::test_ne(const std::string& what,
166
const uint8_t produced[], size_t produced_len,
167
const uint8_t expected[], size_t expected_len)
169
if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len))
171
return test_failure(who() + ": " + what + " produced matching");
173
return test_success();
176
bool Test::Result::test_eq(const char* producer, const std::string& what,
177
const uint8_t produced[], size_t produced_size,
178
const uint8_t expected[], size_t expected_size)
180
if(produced_size == expected_size && Botan::same_mem(produced, expected, expected_size))
182
return test_success();
185
std::ostringstream err;
191
err << " producer '" << producer << "'";
194
err << " unexpected result for " << what;
196
if(produced_size != expected_size)
198
err << " produced " << produced_size << " bytes expected " << expected_size;
201
std::vector<uint8_t> xor_diff(std::min(produced_size, expected_size));
202
size_t bits_different = 0;
204
for(size_t i = 0; i != xor_diff.size(); ++i)
206
xor_diff[i] = produced[i] ^ expected[i];
207
bits_different += Botan::hamming_weight(xor_diff[i]);
210
err << "\nProduced: " << Botan::hex_encode(produced, produced_size)
211
<< "\nExpected: " << Botan::hex_encode(expected, expected_size);
213
if(bits_different > 0)
215
err << "\nXOR Diff: " << Botan::hex_encode(xor_diff)
216
<< " (" << bits_different << " bits different)";
219
return test_failure(err.str());
222
bool Test::Result::test_is_nonempty(const std::string& what_is_it, const std::string& to_examine)
224
if(to_examine.empty())
226
return test_failure(what_is_it + " was empty");
228
return test_success();
231
bool Test::Result::test_eq(const std::string& what, const std::string& produced, const std::string& expected)
233
return test_is_eq(what, produced, expected);
236
bool Test::Result::test_eq(const std::string& what, const char* produced, const char* expected)
238
return test_is_eq(what, std::string(produced), std::string(expected));
241
bool Test::Result::test_eq(const std::string& what, size_t produced, size_t expected)
243
return test_is_eq(what, produced, expected);
246
bool Test::Result::test_eq_sz(const std::string& what, size_t produced, size_t expected)
248
return test_is_eq(what, produced, expected);
251
bool Test::Result::test_eq(const std::string& what,
252
Botan::OctetString produced,
253
Botan::OctetString expected)
255
std::ostringstream out;
256
out << m_who << " " << what;
258
if(produced == expected)
260
out << " produced expected result " << produced.as_string();
261
return test_success(out.str());
265
out << " produced unexpected result '" << produced.as_string() << "' expected '" << expected.as_string() << "'";
266
return test_failure(out.str());
270
bool Test::Result::test_lt(const std::string& what, size_t produced, size_t expected)
272
if(produced >= expected)
274
std::ostringstream err;
275
err << m_who << " " << what;
276
err << " unexpected result " << produced << " >= " << expected;
277
return test_failure(err.str());
280
return test_success();
283
bool Test::Result::test_lte(const std::string& what, size_t produced, size_t expected)
285
if(produced > expected)
287
std::ostringstream err;
288
err << m_who << " " << what << " unexpected result " << produced << " > " << expected;
289
return test_failure(err.str());
292
return test_success();
295
bool Test::Result::test_gte(const std::string& what, size_t produced, size_t expected)
297
if(produced < expected)
299
std::ostringstream err;
302
err << " unexpected result " << produced << " < " << expected;
303
return test_failure(err.str());
306
return test_success();
309
bool Test::Result::test_ne(const std::string& what, const std::string& str1, const std::string& str2)
313
return test_success(str1 + " != " + str2);
316
return test_failure(who() + " " + what + " produced matching strings " + str1);
319
bool Test::Result::test_ne(const std::string& what, size_t produced, size_t expected)
321
if(produced != expected)
323
return test_success();
326
std::ostringstream err;
327
err << who() << " " << what << " produced " << produced << " unexpected value";
328
return test_failure(err.str());
331
#if defined(BOTAN_HAS_BIGINT)
332
bool Test::Result::test_eq(const std::string& what, const BigInt& produced, const BigInt& expected)
334
return test_is_eq(what, produced, expected);
337
bool Test::Result::test_ne(const std::string& what, const BigInt& produced, const BigInt& expected)
339
if(produced != expected)
341
return test_success();
344
std::ostringstream err;
345
err << who() << " " << what << " produced " << produced << " prohibited value";
346
return test_failure(err.str());
350
#if defined(BOTAN_HAS_EC_CURVE_GFP)
351
bool Test::Result::test_eq(const std::string& what,
352
const Botan::PointGFp& a, const Botan::PointGFp& b)
354
//return test_is_eq(what, a, b);
357
return test_success();
360
std::ostringstream err;
361
err << who() << " " << what << " a=(" << a.get_affine_x() << "," << a.get_affine_y() << ")"
362
<< " b=(" << b.get_affine_x() << "," << b.get_affine_y();
363
return test_failure(err.str());
367
bool Test::Result::test_eq(const std::string& what, bool produced, bool expected)
369
return test_is_eq(what, produced, expected);
372
bool Test::Result::test_rc(const std::string& func, int expected, int rc)
376
std::ostringstream err;
378
err << " call to " << func << " unexpectedly returned " << rc;
379
err << " but expecting " << expected;
380
return test_failure(err.str());
383
return test_success();
386
std::vector<std::string> Test::possible_providers(const std::string&)
388
return Test::provider_filter({ "base" });
392
std::string Test::format_time(uint64_t ns)
394
std::ostringstream o;
398
o << std::setprecision(2) << std::fixed << ns / 1000000000.0 << " sec";
402
o << std::setprecision(2) << std::fixed << ns / 1000000.0 << " msec";
408
std::string Test::Result::result_string(bool verbose) const
410
if(tests_run() == 0 && !verbose)
415
std::ostringstream report;
417
report << who() << " ran ";
425
report << tests_run();
431
report << " in " << format_time(m_ns_taken);
436
report << " " << tests_failed() << " FAILED";
445
for(size_t i = 0; i != m_fail_log.size(); ++i)
447
report << "Failure " << (i + 1) << ": " << m_fail_log[i] << "\n";
450
if(m_fail_log.size() > 0 || tests_run() == 0 || verbose)
452
for(size_t i = 0; i != m_log.size(); ++i)
454
report << "Note " << (i + 1) << ": " << m_log[i] << "\n";
461
std::vector<std::string> Provider_Filter::filter(const std::vector<std::string>& in) const
463
if(m_provider.empty())
467
for(auto&& provider : in)
469
if(provider == m_provider)
471
return std::vector<std::string> { provider };
474
return std::vector<std::string> {};
477
// static Test:: functions
479
std::map<std::string, std::unique_ptr<Test>>& Test::global_registry()
481
static std::map<std::string, std::unique_ptr<Test>> g_test_registry;
482
return g_test_registry;
486
uint64_t Test::timestamp()
488
auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
489
return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
493
std::set<std::string> Test::registered_tests()
495
return Botan::map_keys_as_set(Test::global_registry());
499
Test* Test::get_test(const std::string& test_name)
501
auto i = Test::global_registry().find(test_name);
502
if(i != Test::global_registry().end())
504
return i->second.get();
509
std::string Test::read_data_file(const std::string& path)
511
const std::string fsname = Test::data_file(path);
512
std::ifstream file(fsname.c_str());
515
throw Test_Error("Error reading from " + fsname);
518
return std::string((std::istreambuf_iterator<char>(file)),
519
std::istreambuf_iterator<char>());
522
std::vector<uint8_t> Test::read_binary_data_file(const std::string& path)
524
const std::string fsname = Test::data_file(path);
525
std::ifstream file(fsname.c_str(), std::ios::binary);
528
throw Test_Error("Error reading from " + fsname);
531
std::vector<uint8_t> contents;
535
std::vector<uint8_t> buf(4096);
536
file.read(reinterpret_cast<char*>(buf.data()), buf.size());
537
size_t got = file.gcount();
539
if(got == 0 && file.eof())
544
contents.insert(contents.end(), buf.data(), buf.data() + got);
550
// static member variables of Test
551
std::unique_ptr<Botan::RandomNumberGenerator> Test::m_test_rng;
552
std::string Test::m_data_dir;
553
bool Test::m_log_success = false;
554
bool Test::m_run_online_tests = false;
555
bool Test::m_run_long_tests = false;
556
std::string Test::m_pkcs11_lib;
557
Botan_Tests::Provider_Filter Test::m_provider_filter;
560
void Test::set_test_options(bool log_success,
563
const std::string& data_dir,
564
const std::string& pkcs11_lib,
565
const Botan_Tests::Provider_Filter& pf)
567
m_data_dir = data_dir;
568
m_log_success = log_success;
569
m_run_online_tests = run_online;
570
m_run_long_tests = run_long;
571
m_pkcs11_lib = pkcs11_lib;
572
m_provider_filter = pf;
576
void Test::set_test_rng(std::unique_ptr<Botan::RandomNumberGenerator> rng)
578
m_test_rng.reset(rng.release());
582
std::string Test::data_file(const std::string& what)
584
return Test::data_dir() + "/" + what;
588
const std::string& Test::data_dir()
594
bool Test::log_success()
596
return m_log_success;
600
bool Test::run_online_tests()
602
return m_run_online_tests;
606
bool Test::run_long_tests()
608
return m_run_long_tests;
612
std::string Test::pkcs11_lib()
618
std::vector<std::string> Test::provider_filter(const std::vector<std::string>& in)
620
return m_provider_filter.filter(in);
624
Botan::RandomNumberGenerator& Test::rng()
628
throw Test_Error("Test requires RNG but no RNG set with Test::set_test_rng");
633
std::string Test::random_password()
635
const size_t len = 1 + Test::rng().next_byte() % 32;
636
return Botan::hex_encode(Test::rng().random_vec(len));
639
Text_Based_Test::Text_Based_Test(const std::string& data_src,
640
const std::string& required_keys_str,
641
const std::string& optional_keys_str) :
644
if(required_keys_str.empty())
646
throw Test_Error("Invalid test spec");
649
std::vector<std::string> required_keys = Botan::split_on(required_keys_str, ',');
650
std::vector<std::string> optional_keys = Botan::split_on(optional_keys_str, ',');
652
m_required_keys.insert(required_keys.begin(), required_keys.end());
653
m_optional_keys.insert(optional_keys.begin(), optional_keys.end());
654
m_output_key = required_keys.at(required_keys.size() - 1);
657
std::vector<uint8_t> Text_Based_Test::get_req_bin(const VarMap& vars,
658
const std::string& key) const
660
auto i = vars.find(key);
663
throw Test_Error("Test missing variable " + key);
668
return Botan::hex_decode(i->second);
670
catch(std::exception&)
672
throw Test_Error("Test invalid hex input '" + i->second + "'" +
673
+ " for key " + key);
677
std::string Text_Based_Test::get_opt_str(const VarMap& vars,
678
const std::string& key, const std::string& def_value) const
681
auto i = vars.find(key);
689
bool Text_Based_Test::get_req_bool(const VarMap& vars, const std::string& key) const
691
auto i = vars.find(key);
694
throw Test_Error("Test missing variable " + key);
697
if(i->second == "true")
701
else if(i->second == "false")
707
throw Test_Error("Invalid boolean for key '" + key + "' value '" + i->second + "'");
711
size_t Text_Based_Test::get_req_sz(const VarMap& vars, const std::string& key) const
713
auto i = vars.find(key);
716
throw Test_Error("Test missing variable " + key);
718
return Botan::to_u32bit(i->second);
721
size_t Text_Based_Test::get_opt_sz(const VarMap& vars, const std::string& key, const size_t def_value) const
723
auto i = vars.find(key);
728
return Botan::to_u32bit(i->second);
731
std::vector<uint8_t> Text_Based_Test::get_opt_bin(const VarMap& vars,
732
const std::string& key) const
734
auto i = vars.find(key);
737
return std::vector<uint8_t>();
742
return Botan::hex_decode(i->second);
744
catch(std::exception&)
746
throw Test_Error("Test invalid hex input '" + i->second + "'" +
747
+ " for key " + key);
751
std::string Text_Based_Test::get_req_str(const VarMap& vars, const std::string& key) const
753
auto i = vars.find(key);
756
throw Test_Error("Test missing variable " + key);
761
#if defined(BOTAN_HAS_BIGINT)
762
Botan::BigInt Text_Based_Test::get_req_bn(const VarMap& vars,
763
const std::string& key) const
765
auto i = vars.find(key);
768
throw Test_Error("Test missing variable " + key);
773
return Botan::BigInt(i->second);
775
catch(std::exception&)
777
throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
781
Botan::BigInt Text_Based_Test::get_opt_bn(const VarMap& vars,
782
const std::string& key,
783
const Botan::BigInt& def_value) const
786
auto i = vars.find(key);
794
return Botan::BigInt(i->second);
796
catch(std::exception&)
798
throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
803
std::string Text_Based_Test::get_next_line()
807
if(m_cur == nullptr || m_cur->good() == false)
813
const std::string full_path = Test::data_dir() + "/" + m_data_src;
814
if(full_path.find(".vec") != std::string::npos)
816
m_srcs.push_back(full_path);
820
const auto fs = Botan::get_files_recursive(full_path);
821
m_srcs.assign(fs.begin(), fs.end());
824
throw Test_Error("Error reading test data dir " + full_path);
836
m_cur.reset(new std::ifstream(m_srcs[0]));
837
m_cur_src_name = m_srcs[0];
839
// Reinit cpuid on new file if needed
840
if(m_cpu_flags.empty() == false)
843
Botan::CPUID::initialize();
848
throw Test_Error("Could not open input file '" + m_cur_src_name);
857
std::getline(*m_cur, line);
866
if(line.compare(0, 6, "#test ") == 0)
883
// strips leading and trailing but not internal whitespace
884
std::string strip_ws(const std::string& in)
886
const char* whitespace = " ";
888
const auto first_c = in.find_first_not_of(whitespace);
889
if(first_c == std::string::npos)
894
const auto last_c = in.find_last_not_of(whitespace);
896
return in.substr(first_c, last_c - first_c + 1);
899
std::vector<Botan::CPUID::CPUID_bits>
900
parse_cpuid_bits(const std::vector<std::string>& tok)
902
std::vector<Botan::CPUID::CPUID_bits> bits;
903
for(size_t i = 1; i < tok.size(); ++i)
905
const std::vector<Botan::CPUID::CPUID_bits> more = Botan::CPUID::bit_from_string(tok[i]);
906
bits.insert(bits.end(), more.begin(), more.end());
914
bool Text_Based_Test::skip_this_test(const std::string& /*header*/,
915
const VarMap& /*vars*/)
920
std::vector<Test::Result> Text_Based_Test::run()
922
std::vector<Test::Result> results;
924
std::string header, header_or_name = m_data_src;
930
const std::string line = get_next_line();
931
if(line.empty()) // EOF
936
if(line.compare(0, 6, "#test ") == 0)
938
std::vector<std::string> pragma_tokens = Botan::split_on(line.substr(6), ' ');
940
if(pragma_tokens.empty())
942
throw Test_Error("Empty pragma found in " + m_cur_src_name);
945
if(pragma_tokens[0] != "cpuid")
947
throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
950
m_cpu_flags = parse_cpuid_bits(pragma_tokens);
954
else if(line[0] == '#')
956
throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
959
if(line[0] == '[' && line[line.size() - 1] == ']')
961
header = line.substr(1, line.size() - 2);
962
header_or_name = header;
968
const std::string test_id = "test " + std::to_string(test_cnt);
970
auto equal_i = line.find_first_of('=');
972
if(equal_i == std::string::npos)
974
results.push_back(Test::Result::Failure(header_or_name,
975
"invalid input '" + line + "'"));
979
std::string key = strip_ws(std::string(line.begin(), line.begin() + equal_i - 1));
980
std::string val = strip_ws(std::string(line.begin() + equal_i + 1, line.end()));
982
if(m_required_keys.count(key) == 0 && m_optional_keys.count(key) == 0)
983
results.push_back(Test::Result::Failure(header_or_name,
984
test_id + " failed unknown key " + key));
988
if(key == m_output_key)
992
if(skip_this_test(header, vars))
995
if(possible_providers(header).empty())
1000
uint64_t start = Test::timestamp();
1002
Test::Result result = run_one_test(header, vars);
1003
if(m_cpu_flags.size() > 0)
1005
for(auto const& cpuid_bit : m_cpu_flags)
1007
if(Botan::CPUID::has_cpuid_bit(cpuid_bit))
1009
Botan::CPUID::clear_cpuid_bit(cpuid_bit);
1010
// now re-run the test
1011
result.merge(run_one_test(header, vars));
1014
Botan::CPUID::initialize();
1016
result.set_ns_consumed(Test::timestamp() - start);
1018
if(result.tests_failed())
1021
result.test_note("Test #" + std::to_string(test_cnt) + " failed");
1023
result.test_note("Test #" + std::to_string(test_cnt) + " " + header + " failed");
1025
results.push_back(result);
1027
catch(std::exception& e)
1029
results.push_back(Test::Result::Failure(header_or_name,
1030
"test " + std::to_string(test_cnt) +
1031
" failed with exception '" + e.what() + "'"));
1034
if(clear_between_callbacks())
1048
std::vector<Test::Result> final_tests = run_final_tests();
1049
results.insert(results.end(), final_tests.begin(), final_tests.end());
1051
catch(std::exception& e)
1053
results.push_back(Test::Result::Failure(header_or_name,
1054
"run_final_tests exception " + std::string(e.what())));