~ubuntu-branches/debian/sid/botan/sid

« back to all changes in this revision

Viewing changes to src/tests/tests.cpp

  • Committer: Package Import Robot
  • Author(s): Laszlo Boszormenyi (GCS)
  • Date: 2018-03-01 22:23:25 UTC
  • mfrom: (1.2.2)
  • Revision ID: package-import@ubuntu.com-20180301222325-7p7vc45gu3hta34d
Tags: 2.4.0-2
* Don't remove .doctrees from the manual if it doesn't exist.
* Don't specify parallel to debhelper.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
* (C) 2014,2015 Jack Lloyd
 
3
*
 
4
* Botan is released under the Simplified BSD License (see license.txt)
 
5
*/
 
6
 
 
7
#include "tests.h"
 
8
 
 
9
#include <sstream>
 
10
#include <fstream>
 
11
#include <iomanip>
 
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>
 
17
 
 
18
#if defined(BOTAN_HAS_BIGINT)
 
19
   #include <botan/bigint.h>
 
20
#endif
 
21
 
 
22
#if defined(BOTAN_HAS_EC_CURVE_GFP)
 
23
   #include <botan/point_gfp.h>
 
24
#endif
 
25
 
 
26
namespace Botan_Tests {
 
27
 
 
28
Test::Registration::Registration(const std::string& name, Test* test)
 
29
   {
 
30
   if(Test::global_registry().count(name) == 0)
 
31
      {
 
32
      Test::global_registry().insert(std::make_pair(name, std::unique_ptr<Test>(test)));
 
33
      }
 
34
   else
 
35
      {
 
36
      throw Test_Error("Duplicate registration of test '" + name + "'");
 
37
      }
 
38
   }
 
39
 
 
40
void Test::Result::merge(const Result& other)
 
41
   {
 
42
   if(who() != other.who())
 
43
      {
 
44
      throw Test_Error("Merging tests from different sources");
 
45
      }
 
46
 
 
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());
 
51
   }
 
52
 
 
53
void Test::Result::start_timer()
 
54
   {
 
55
   if(m_started == 0)
 
56
      {
 
57
      m_started = Test::timestamp();
 
58
      }
 
59
   }
 
60
 
 
61
void Test::Result::end_timer()
 
62
   {
 
63
   if(m_started > 0)
 
64
      {
 
65
      m_ns_taken += Test::timestamp() - m_started;
 
66
      m_started = 0;
 
67
      }
 
68
   }
 
69
 
 
70
void Test::Result::test_note(const std::string& note, const char* extra)
 
71
   {
 
72
   if(note != "")
 
73
      {
 
74
      std::ostringstream out;
 
75
      out << who() << " " << note;
 
76
      if(extra)
 
77
         {
 
78
         out << ": " << extra;
 
79
         }
 
80
      m_log.push_back(out.str());
 
81
      }
 
82
   }
 
83
 
 
84
void Test::Result::note_missing(const std::string& whatever)
 
85
   {
 
86
   static std::set<std::string> s_already_seen;
 
87
 
 
88
   if(s_already_seen.count(whatever) == 0)
 
89
      {
 
90
      test_note("Skipping tests due to missing " + whatever);
 
91
      s_already_seen.insert(whatever);
 
92
      }
 
93
   }
 
94
 
 
95
bool Test::Result::test_throws(const std::string& what, std::function<void ()> fn)
 
96
   {
 
97
   try
 
98
      {
 
99
      fn();
 
100
      return test_failure(what + " failed to throw expected exception");
 
101
      }
 
102
   catch(std::exception& e)
 
103
      {
 
104
      return test_success(what + " threw exception " + e.what());
 
105
      }
 
106
   catch(...)
 
107
      {
 
108
      return test_success(what + " threw unknown exception");
 
109
      }
 
110
   }
 
111
 
 
112
bool Test::Result::test_throws(const std::string& what, const std::string& expected, std::function<void ()> fn)
 
113
   {
 
114
   try
 
115
      {
 
116
      fn();
 
117
      return test_failure(what + " failed to throw expected exception");
 
118
      }
 
119
   catch(std::exception& e)
 
120
      {
 
121
      if(expected == e.what())
 
122
         {
 
123
         return test_success(what + " threw exception " + e.what());
 
124
         }
 
125
      else
 
126
         {
 
127
         return test_failure(what + " failed to throw an exception with the expected text:\n  Expected: " + expected +
 
128
                             "\n  Got: " + e.what());
 
129
         }
 
130
      }
 
131
   catch(...)
 
132
      {
 
133
      return test_failure(what + " failed to throw an exception with the expected text:\n  Expected: " + expected);
 
134
      }
 
135
   }
 
136
 
 
137
bool Test::Result::test_success(const std::string& note)
 
138
   {
 
139
   if(Test::log_success())
 
140
      {
 
141
      test_note(note);
 
142
      }
 
143
   ++m_tests_passed;
 
144
   return true;
 
145
   }
 
146
 
 
147
bool Test::Result::test_failure(const std::string& what, const std::string& error)
 
148
   {
 
149
   return test_failure(who() + " " + what + " with error " + error);
 
150
   }
 
151
 
 
152
void Test::Result::test_failure(const std::string& what, const uint8_t buf[], size_t buf_len)
 
153
   {
 
154
   test_failure(who() + ": " + what +
 
155
                " buf len " + std::to_string(buf_len) +
 
156
                " value " + Botan::hex_encode(buf, buf_len));
 
157
   }
 
158
 
 
159
bool Test::Result::test_failure(const std::string& err)
 
160
   {
 
161
   m_fail_log.push_back(err);
 
162
   return false;
 
163
   }
 
164
 
 
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)
 
168
   {
 
169
   if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len))
 
170
      {
 
171
      return test_failure(who() + ": " + what + " produced matching");
 
172
      }
 
173
   return test_success();
 
174
   }
 
175
 
 
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)
 
179
   {
 
180
   if(produced_size == expected_size && Botan::same_mem(produced, expected, expected_size))
 
181
      {
 
182
      return test_success();
 
183
      }
 
184
 
 
185
   std::ostringstream err;
 
186
 
 
187
   err << who();
 
188
 
 
189
   if(producer)
 
190
      {
 
191
      err << " producer '" << producer << "'";
 
192
      }
 
193
 
 
194
   err << " unexpected result for " << what;
 
195
 
 
196
   if(produced_size != expected_size)
 
197
      {
 
198
      err << " produced " << produced_size << " bytes expected " << expected_size;
 
199
      }
 
200
 
 
201
   std::vector<uint8_t> xor_diff(std::min(produced_size, expected_size));
 
202
   size_t bits_different = 0;
 
203
 
 
204
   for(size_t i = 0; i != xor_diff.size(); ++i)
 
205
      {
 
206
      xor_diff[i] = produced[i] ^ expected[i];
 
207
      bits_different += Botan::hamming_weight(xor_diff[i]);
 
208
      }
 
209
 
 
210
   err << "\nProduced: " << Botan::hex_encode(produced, produced_size)
 
211
       << "\nExpected: " << Botan::hex_encode(expected, expected_size);
 
212
 
 
213
   if(bits_different > 0)
 
214
      {
 
215
      err << "\nXOR Diff: " << Botan::hex_encode(xor_diff)
 
216
          << " (" << bits_different << " bits different)";
 
217
      }
 
218
 
 
219
   return test_failure(err.str());
 
220
   }
 
221
 
 
222
bool Test::Result::test_is_nonempty(const std::string& what_is_it, const std::string& to_examine)
 
223
   {
 
224
   if(to_examine.empty())
 
225
      {
 
226
      return test_failure(what_is_it + " was empty");
 
227
      }
 
228
   return test_success();
 
229
   }
 
230
 
 
231
bool Test::Result::test_eq(const std::string& what, const std::string& produced, const std::string& expected)
 
232
   {
 
233
   return test_is_eq(what, produced, expected);
 
234
   }
 
235
 
 
236
bool Test::Result::test_eq(const std::string& what, const char* produced, const char* expected)
 
237
   {
 
238
   return test_is_eq(what, std::string(produced), std::string(expected));
 
239
   }
 
240
 
 
241
bool Test::Result::test_eq(const std::string& what, size_t produced, size_t expected)
 
242
   {
 
243
   return test_is_eq(what, produced, expected);
 
244
   }
 
245
 
 
246
bool Test::Result::test_eq_sz(const std::string& what, size_t produced, size_t expected)
 
247
   {
 
248
   return test_is_eq(what, produced, expected);
 
249
   }
 
250
 
 
251
bool Test::Result::test_eq(const std::string& what,
 
252
                           Botan::OctetString produced,
 
253
                           Botan::OctetString expected)
 
254
   {
 
255
   std::ostringstream out;
 
256
   out << m_who << " " << what;
 
257
 
 
258
   if(produced == expected)
 
259
      {
 
260
      out << " produced expected result " << produced.as_string();
 
261
      return test_success(out.str());
 
262
      }
 
263
   else
 
264
      {
 
265
      out << " produced unexpected result '" << produced.as_string() << "' expected '" << expected.as_string() << "'";
 
266
      return test_failure(out.str());
 
267
      }
 
268
   }
 
269
 
 
270
bool Test::Result::test_lt(const std::string& what, size_t produced, size_t expected)
 
271
   {
 
272
   if(produced >= expected)
 
273
      {
 
274
      std::ostringstream err;
 
275
      err << m_who << " " << what;
 
276
      err << " unexpected result " << produced << " >= " << expected;
 
277
      return test_failure(err.str());
 
278
      }
 
279
 
 
280
   return test_success();
 
281
   }
 
282
 
 
283
bool Test::Result::test_lte(const std::string& what, size_t produced, size_t expected)
 
284
   {
 
285
   if(produced > expected)
 
286
      {
 
287
      std::ostringstream err;
 
288
      err << m_who << " " << what << " unexpected result " << produced << " > " << expected;
 
289
      return test_failure(err.str());
 
290
      }
 
291
 
 
292
   return test_success();
 
293
   }
 
294
 
 
295
bool Test::Result::test_gte(const std::string& what, size_t produced, size_t expected)
 
296
   {
 
297
   if(produced < expected)
 
298
      {
 
299
      std::ostringstream err;
 
300
      err << m_who;
 
301
      err << " " << what;
 
302
      err << " unexpected result " << produced << " < " << expected;
 
303
      return test_failure(err.str());
 
304
      }
 
305
 
 
306
   return test_success();
 
307
   }
 
308
 
 
309
bool Test::Result::test_ne(const std::string& what, const std::string& str1, const std::string& str2)
 
310
   {
 
311
   if(str1 != str2)
 
312
      {
 
313
      return test_success(str1 + " != " + str2);
 
314
      }
 
315
 
 
316
   return test_failure(who() + " " + what + " produced matching strings " + str1);
 
317
   }
 
318
 
 
319
bool Test::Result::test_ne(const std::string& what, size_t produced, size_t expected)
 
320
   {
 
321
   if(produced != expected)
 
322
      {
 
323
      return test_success();
 
324
      }
 
325
 
 
326
   std::ostringstream err;
 
327
   err << who() << " " << what << " produced " << produced << " unexpected value";
 
328
   return test_failure(err.str());
 
329
   }
 
330
 
 
331
#if defined(BOTAN_HAS_BIGINT)
 
332
bool Test::Result::test_eq(const std::string& what, const BigInt& produced, const BigInt& expected)
 
333
   {
 
334
   return test_is_eq(what, produced, expected);
 
335
   }
 
336
 
 
337
bool Test::Result::test_ne(const std::string& what, const BigInt& produced, const BigInt& expected)
 
338
   {
 
339
   if(produced != expected)
 
340
      {
 
341
      return test_success();
 
342
      }
 
343
 
 
344
   std::ostringstream err;
 
345
   err << who() << " " << what << " produced " << produced << " prohibited value";
 
346
   return test_failure(err.str());
 
347
   }
 
348
#endif
 
349
 
 
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)
 
353
   {
 
354
   //return test_is_eq(what, a, b);
 
355
   if(a == b)
 
356
      {
 
357
      return test_success();
 
358
      }
 
359
 
 
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());
 
364
   }
 
365
#endif
 
366
 
 
367
bool Test::Result::test_eq(const std::string& what, bool produced, bool expected)
 
368
   {
 
369
   return test_is_eq(what, produced, expected);
 
370
   }
 
371
 
 
372
bool Test::Result::test_rc(const std::string& func, int expected, int rc)
 
373
   {
 
374
   if(expected != rc)
 
375
      {
 
376
      std::ostringstream err;
 
377
      err << m_who;
 
378
      err << " call to " << func << " unexpectedly returned " << rc;
 
379
      err << " but expecting " << expected;
 
380
      return test_failure(err.str());
 
381
      }
 
382
 
 
383
   return test_success();
 
384
   }
 
385
 
 
386
std::vector<std::string> Test::possible_providers(const std::string&)
 
387
   {
 
388
   return Test::provider_filter({ "base" });
 
389
   }
 
390
 
 
391
//static
 
392
std::string Test::format_time(uint64_t ns)
 
393
   {
 
394
   std::ostringstream o;
 
395
 
 
396
   if(ns > 1000000000)
 
397
      {
 
398
      o << std::setprecision(2) << std::fixed << ns / 1000000000.0 << " sec";
 
399
      }
 
400
   else
 
401
      {
 
402
      o << std::setprecision(2) << std::fixed << ns / 1000000.0 << " msec";
 
403
      }
 
404
 
 
405
   return o.str();
 
406
   }
 
407
 
 
408
std::string Test::Result::result_string(bool verbose) const
 
409
   {
 
410
   if(tests_run() == 0 && !verbose)
 
411
      {
 
412
      return "";
 
413
      }
 
414
 
 
415
   std::ostringstream report;
 
416
 
 
417
   report << who() << " ran ";
 
418
 
 
419
   if(tests_run() == 0)
 
420
      {
 
421
      report << "ZERO";
 
422
      }
 
423
   else
 
424
      {
 
425
      report << tests_run();
 
426
      }
 
427
   report << " tests";
 
428
 
 
429
   if(m_ns_taken > 0)
 
430
      {
 
431
      report << " in " << format_time(m_ns_taken);
 
432
      }
 
433
 
 
434
   if(tests_failed())
 
435
      {
 
436
      report << " " << tests_failed() << " FAILED";
 
437
      }
 
438
   else
 
439
      {
 
440
      report << " all ok";
 
441
      }
 
442
 
 
443
   report << "\n";
 
444
 
 
445
   for(size_t i = 0; i != m_fail_log.size(); ++i)
 
446
      {
 
447
      report << "Failure " << (i + 1) << ": " << m_fail_log[i] << "\n";
 
448
      }
 
449
 
 
450
   if(m_fail_log.size() > 0 || tests_run() == 0 || verbose)
 
451
      {
 
452
      for(size_t i = 0; i != m_log.size(); ++i)
 
453
         {
 
454
         report << "Note " << (i + 1) << ": " << m_log[i] << "\n";
 
455
         }
 
456
      }
 
457
 
 
458
   return report.str();
 
459
   }
 
460
 
 
461
std::vector<std::string> Provider_Filter::filter(const std::vector<std::string>& in) const
 
462
   {
 
463
   if(m_provider.empty())
 
464
      {
 
465
      return in;
 
466
      }
 
467
   for(auto&& provider : in)
 
468
      {
 
469
      if(provider == m_provider)
 
470
         {
 
471
         return std::vector<std::string> { provider };
 
472
         }
 
473
      }
 
474
   return std::vector<std::string> {};
 
475
   }
 
476
 
 
477
// static Test:: functions
 
478
//static
 
479
std::map<std::string, std::unique_ptr<Test>>& Test::global_registry()
 
480
   {
 
481
   static std::map<std::string, std::unique_ptr<Test>> g_test_registry;
 
482
   return g_test_registry;
 
483
   }
 
484
 
 
485
//static
 
486
uint64_t Test::timestamp()
 
487
   {
 
488
   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
 
489
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
 
490
   }
 
491
 
 
492
//static
 
493
std::set<std::string> Test::registered_tests()
 
494
   {
 
495
   return Botan::map_keys_as_set(Test::global_registry());
 
496
   }
 
497
 
 
498
//static
 
499
Test* Test::get_test(const std::string& test_name)
 
500
   {
 
501
   auto i = Test::global_registry().find(test_name);
 
502
   if(i != Test::global_registry().end())
 
503
      {
 
504
      return i->second.get();
 
505
      }
 
506
   return nullptr;
 
507
   }
 
508
 
 
509
std::string Test::read_data_file(const std::string& path)
 
510
   {
 
511
   const std::string fsname = Test::data_file(path);
 
512
   std::ifstream file(fsname.c_str());
 
513
   if(!file.good())
 
514
      {
 
515
      throw Test_Error("Error reading from " + fsname);
 
516
      }
 
517
 
 
518
   return std::string((std::istreambuf_iterator<char>(file)),
 
519
                      std::istreambuf_iterator<char>());
 
520
   }
 
521
 
 
522
std::vector<uint8_t> Test::read_binary_data_file(const std::string& path)
 
523
   {
 
524
   const std::string fsname = Test::data_file(path);
 
525
   std::ifstream file(fsname.c_str(), std::ios::binary);
 
526
   if(!file.good())
 
527
      {
 
528
      throw Test_Error("Error reading from " + fsname);
 
529
      }
 
530
 
 
531
   std::vector<uint8_t> contents;
 
532
 
 
533
   while(file.good())
 
534
      {
 
535
      std::vector<uint8_t> buf(4096);
 
536
      file.read(reinterpret_cast<char*>(buf.data()), buf.size());
 
537
      size_t got = file.gcount();
 
538
 
 
539
      if(got == 0 && file.eof())
 
540
         {
 
541
         break;
 
542
         }
 
543
 
 
544
      contents.insert(contents.end(), buf.data(), buf.data() + got);
 
545
      }
 
546
 
 
547
   return contents;
 
548
   }
 
549
 
 
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;
 
558
 
 
559
//static
 
560
void Test::set_test_options(bool log_success,
 
561
                            bool run_online,
 
562
                            bool run_long,
 
563
                            const std::string& data_dir,
 
564
                            const std::string& pkcs11_lib,
 
565
                            const Botan_Tests::Provider_Filter& pf)
 
566
   {
 
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;
 
573
   }
 
574
 
 
575
//static
 
576
void Test::set_test_rng(std::unique_ptr<Botan::RandomNumberGenerator> rng)
 
577
   {
 
578
   m_test_rng.reset(rng.release());
 
579
   }
 
580
 
 
581
//static
 
582
std::string Test::data_file(const std::string& what)
 
583
   {
 
584
   return Test::data_dir() + "/" + what;
 
585
   }
 
586
 
 
587
//static
 
588
const std::string& Test::data_dir()
 
589
   {
 
590
   return m_data_dir;
 
591
   }
 
592
 
 
593
//static
 
594
bool Test::log_success()
 
595
   {
 
596
   return m_log_success;
 
597
   }
 
598
 
 
599
//static
 
600
bool Test::run_online_tests()
 
601
   {
 
602
   return m_run_online_tests;
 
603
   }
 
604
 
 
605
//static
 
606
bool Test::run_long_tests()
 
607
   {
 
608
   return m_run_long_tests;
 
609
   }
 
610
 
 
611
//static
 
612
std::string Test::pkcs11_lib()
 
613
   {
 
614
   return m_pkcs11_lib;
 
615
   }
 
616
 
 
617
//static
 
618
std::vector<std::string> Test::provider_filter(const std::vector<std::string>& in)
 
619
   {
 
620
   return m_provider_filter.filter(in);
 
621
   }
 
622
 
 
623
//static
 
624
Botan::RandomNumberGenerator& Test::rng()
 
625
   {
 
626
   if(!m_test_rng)
 
627
      {
 
628
      throw Test_Error("Test requires RNG but no RNG set with Test::set_test_rng");
 
629
      }
 
630
   return *m_test_rng;
 
631
   }
 
632
 
 
633
std::string Test::random_password()
 
634
   {
 
635
   const size_t len = 1 + Test::rng().next_byte() % 32;
 
636
   return Botan::hex_encode(Test::rng().random_vec(len));
 
637
   }
 
638
 
 
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) :
 
642
   m_data_src(data_src)
 
643
   {
 
644
   if(required_keys_str.empty())
 
645
      {
 
646
      throw Test_Error("Invalid test spec");
 
647
      }
 
648
 
 
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, ',');
 
651
 
 
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);
 
655
   }
 
656
 
 
657
std::vector<uint8_t> Text_Based_Test::get_req_bin(const VarMap& vars,
 
658
      const std::string& key) const
 
659
   {
 
660
   auto i = vars.find(key);
 
661
   if(i == vars.end())
 
662
      {
 
663
      throw Test_Error("Test missing variable " + key);
 
664
      }
 
665
 
 
666
   try
 
667
      {
 
668
      return Botan::hex_decode(i->second);
 
669
      }
 
670
   catch(std::exception&)
 
671
      {
 
672
      throw Test_Error("Test invalid hex input '" + i->second + "'" +
 
673
                       + " for key " + key);
 
674
      }
 
675
   }
 
676
 
 
677
std::string Text_Based_Test::get_opt_str(const VarMap& vars,
 
678
      const std::string& key, const std::string& def_value) const
 
679
 
 
680
   {
 
681
   auto i = vars.find(key);
 
682
   if(i == vars.end())
 
683
      {
 
684
      return def_value;
 
685
      }
 
686
   return i->second;
 
687
   }
 
688
 
 
689
bool Text_Based_Test::get_req_bool(const VarMap& vars, const std::string& key) const
 
690
   {
 
691
   auto i = vars.find(key);
 
692
   if(i == vars.end())
 
693
      {
 
694
      throw Test_Error("Test missing variable " + key);
 
695
      }
 
696
 
 
697
   if(i->second == "true")
 
698
      {
 
699
      return true;
 
700
      }
 
701
   else if(i->second == "false")
 
702
      {
 
703
      return false;
 
704
      }
 
705
   else
 
706
      {
 
707
      throw Test_Error("Invalid boolean for key '" + key + "' value '" + i->second + "'");
 
708
      }
 
709
   }
 
710
 
 
711
size_t Text_Based_Test::get_req_sz(const VarMap& vars, const std::string& key) const
 
712
   {
 
713
   auto i = vars.find(key);
 
714
   if(i == vars.end())
 
715
      {
 
716
      throw Test_Error("Test missing variable " + key);
 
717
      }
 
718
   return Botan::to_u32bit(i->second);
 
719
   }
 
720
 
 
721
size_t Text_Based_Test::get_opt_sz(const VarMap& vars, const std::string& key, const size_t def_value) const
 
722
   {
 
723
   auto i = vars.find(key);
 
724
   if(i == vars.end())
 
725
      {
 
726
      return def_value;
 
727
      }
 
728
   return Botan::to_u32bit(i->second);
 
729
   }
 
730
 
 
731
std::vector<uint8_t> Text_Based_Test::get_opt_bin(const VarMap& vars,
 
732
      const std::string& key) const
 
733
   {
 
734
   auto i = vars.find(key);
 
735
   if(i == vars.end())
 
736
      {
 
737
      return std::vector<uint8_t>();
 
738
      }
 
739
 
 
740
   try
 
741
      {
 
742
      return Botan::hex_decode(i->second);
 
743
      }
 
744
   catch(std::exception&)
 
745
      {
 
746
      throw Test_Error("Test invalid hex input '" + i->second + "'" +
 
747
                       + " for key " + key);
 
748
      }
 
749
   }
 
750
 
 
751
std::string Text_Based_Test::get_req_str(const VarMap& vars, const std::string& key) const
 
752
   {
 
753
   auto i = vars.find(key);
 
754
   if(i == vars.end())
 
755
      {
 
756
      throw Test_Error("Test missing variable " + key);
 
757
      }
 
758
   return i->second;
 
759
   }
 
760
 
 
761
#if defined(BOTAN_HAS_BIGINT)
 
762
Botan::BigInt Text_Based_Test::get_req_bn(const VarMap& vars,
 
763
      const std::string& key) const
 
764
   {
 
765
   auto i = vars.find(key);
 
766
   if(i == vars.end())
 
767
      {
 
768
      throw Test_Error("Test missing variable " + key);
 
769
      }
 
770
 
 
771
   try
 
772
      {
 
773
      return Botan::BigInt(i->second);
 
774
      }
 
775
   catch(std::exception&)
 
776
      {
 
777
      throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
 
778
      }
 
779
   }
 
780
 
 
781
Botan::BigInt Text_Based_Test::get_opt_bn(const VarMap& vars,
 
782
      const std::string& key,
 
783
      const Botan::BigInt& def_value) const
 
784
 
 
785
   {
 
786
   auto i = vars.find(key);
 
787
   if(i == vars.end())
 
788
      {
 
789
      return def_value;
 
790
      }
 
791
 
 
792
   try
 
793
      {
 
794
      return Botan::BigInt(i->second);
 
795
      }
 
796
   catch(std::exception&)
 
797
      {
 
798
      throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
 
799
      }
 
800
   }
 
801
#endif
 
802
 
 
803
std::string Text_Based_Test::get_next_line()
 
804
   {
 
805
   while(true)
 
806
      {
 
807
      if(m_cur == nullptr || m_cur->good() == false)
 
808
         {
 
809
         if(m_srcs.empty())
 
810
            {
 
811
            if(m_first)
 
812
               {
 
813
               const std::string full_path = Test::data_dir() + "/" + m_data_src;
 
814
               if(full_path.find(".vec") != std::string::npos)
 
815
                  {
 
816
                  m_srcs.push_back(full_path);
 
817
                  }
 
818
               else
 
819
                  {
 
820
                  const auto fs = Botan::get_files_recursive(full_path);
 
821
                  m_srcs.assign(fs.begin(), fs.end());
 
822
                  if(m_srcs.empty())
 
823
                     {
 
824
                     throw Test_Error("Error reading test data dir " + full_path);
 
825
                     }
 
826
                  }
 
827
 
 
828
               m_first = false;
 
829
               }
 
830
            else
 
831
               {
 
832
               return ""; // done
 
833
               }
 
834
            }
 
835
 
 
836
         m_cur.reset(new std::ifstream(m_srcs[0]));
 
837
         m_cur_src_name = m_srcs[0];
 
838
 
 
839
         // Reinit cpuid on new file if needed
 
840
         if(m_cpu_flags.empty() == false)
 
841
            {
 
842
            m_cpu_flags.clear();
 
843
            Botan::CPUID::initialize();
 
844
            }
 
845
 
 
846
         if(!m_cur->good())
 
847
            {
 
848
            throw Test_Error("Could not open input file '" + m_cur_src_name);
 
849
            }
 
850
 
 
851
         m_srcs.pop_front();
 
852
         }
 
853
 
 
854
      while(m_cur->good())
 
855
         {
 
856
         std::string line;
 
857
         std::getline(*m_cur, line);
 
858
 
 
859
         if(line.empty())
 
860
            {
 
861
            continue;
 
862
            }
 
863
 
 
864
         if(line[0] == '#')
 
865
            {
 
866
            if(line.compare(0, 6, "#test ") == 0)
 
867
               {
 
868
               return line;
 
869
               }
 
870
            else
 
871
               {
 
872
               continue;
 
873
               }
 
874
            }
 
875
 
 
876
         return line;
 
877
         }
 
878
      }
 
879
   }
 
880
 
 
881
namespace {
 
882
 
 
883
// strips leading and trailing but not internal whitespace
 
884
std::string strip_ws(const std::string& in)
 
885
   {
 
886
   const char* whitespace = " ";
 
887
 
 
888
   const auto first_c = in.find_first_not_of(whitespace);
 
889
   if(first_c == std::string::npos)
 
890
      {
 
891
      return "";
 
892
      }
 
893
 
 
894
   const auto last_c = in.find_last_not_of(whitespace);
 
895
 
 
896
   return in.substr(first_c, last_c - first_c + 1);
 
897
   }
 
898
 
 
899
std::vector<Botan::CPUID::CPUID_bits>
 
900
parse_cpuid_bits(const std::vector<std::string>& tok)
 
901
   {
 
902
   std::vector<Botan::CPUID::CPUID_bits> bits;
 
903
   for(size_t i = 1; i < tok.size(); ++i)
 
904
      {
 
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());
 
907
      }
 
908
 
 
909
   return bits;
 
910
   }
 
911
 
 
912
}
 
913
 
 
914
bool Text_Based_Test::skip_this_test(const std::string& /*header*/,
 
915
                                     const VarMap& /*vars*/)
 
916
   {
 
917
   return false;
 
918
   }
 
919
 
 
920
std::vector<Test::Result> Text_Based_Test::run()
 
921
   {
 
922
   std::vector<Test::Result> results;
 
923
 
 
924
   std::string header, header_or_name = m_data_src;
 
925
   VarMap vars;
 
926
   size_t test_cnt = 0;
 
927
 
 
928
   while(true)
 
929
      {
 
930
      const std::string line = get_next_line();
 
931
      if(line.empty()) // EOF
 
932
         {
 
933
         break;
 
934
         }
 
935
 
 
936
      if(line.compare(0, 6, "#test ") == 0)
 
937
         {
 
938
         std::vector<std::string> pragma_tokens = Botan::split_on(line.substr(6), ' ');
 
939
 
 
940
         if(pragma_tokens.empty())
 
941
            {
 
942
            throw Test_Error("Empty pragma found in " + m_cur_src_name);
 
943
            }
 
944
 
 
945
         if(pragma_tokens[0] != "cpuid")
 
946
            {
 
947
            throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
 
948
            }
 
949
 
 
950
         m_cpu_flags = parse_cpuid_bits(pragma_tokens);
 
951
 
 
952
         continue;
 
953
         }
 
954
      else if(line[0] == '#')
 
955
         {
 
956
         throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
 
957
         }
 
958
 
 
959
      if(line[0] == '[' && line[line.size() - 1] == ']')
 
960
         {
 
961
         header = line.substr(1, line.size() - 2);
 
962
         header_or_name = header;
 
963
         test_cnt = 0;
 
964
         vars.clear();
 
965
         continue;
 
966
         }
 
967
 
 
968
      const std::string test_id = "test " + std::to_string(test_cnt);
 
969
 
 
970
      auto equal_i = line.find_first_of('=');
 
971
 
 
972
      if(equal_i == std::string::npos)
 
973
         {
 
974
         results.push_back(Test::Result::Failure(header_or_name,
 
975
                                                 "invalid input '" + line + "'"));
 
976
         continue;
 
977
         }
 
978
 
 
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()));
 
981
 
 
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));
 
985
 
 
986
      vars[key] = val;
 
987
 
 
988
      if(key == m_output_key)
 
989
         {
 
990
         try
 
991
            {
 
992
            if(skip_this_test(header, vars))
 
993
               continue;
 
994
 
 
995
            if(possible_providers(header).empty())
 
996
               continue;
 
997
 
 
998
            ++test_cnt;
 
999
 
 
1000
            uint64_t start = Test::timestamp();
 
1001
 
 
1002
            Test::Result result = run_one_test(header, vars);
 
1003
            if(m_cpu_flags.size() > 0)
 
1004
               {
 
1005
               for(auto const& cpuid_bit : m_cpu_flags)
 
1006
                  {
 
1007
                  if(Botan::CPUID::has_cpuid_bit(cpuid_bit))
 
1008
                     {
 
1009
                     Botan::CPUID::clear_cpuid_bit(cpuid_bit);
 
1010
                     // now re-run the test
 
1011
                     result.merge(run_one_test(header, vars));
 
1012
                     }
 
1013
                  }
 
1014
               Botan::CPUID::initialize();
 
1015
               }
 
1016
            result.set_ns_consumed(Test::timestamp() - start);
 
1017
 
 
1018
            if(result.tests_failed())
 
1019
               {
 
1020
               if(header.empty())
 
1021
                  result.test_note("Test #" + std::to_string(test_cnt) + " failed");
 
1022
               else
 
1023
                  result.test_note("Test #" + std::to_string(test_cnt) + " " + header + " failed");
 
1024
               }
 
1025
            results.push_back(result);
 
1026
            }
 
1027
         catch(std::exception& e)
 
1028
            {
 
1029
            results.push_back(Test::Result::Failure(header_or_name,
 
1030
                                                    "test " + std::to_string(test_cnt) +
 
1031
                                                    " failed with exception '" + e.what() + "'"));
 
1032
            }
 
1033
 
 
1034
         if(clear_between_callbacks())
 
1035
            {
 
1036
            vars.clear();
 
1037
            }
 
1038
         }
 
1039
      }
 
1040
 
 
1041
   if(results.empty())
 
1042
      {
 
1043
      return results;
 
1044
      }
 
1045
 
 
1046
   try
 
1047
      {
 
1048
      std::vector<Test::Result> final_tests = run_final_tests();
 
1049
      results.insert(results.end(), final_tests.begin(), final_tests.end());
 
1050
      }
 
1051
   catch(std::exception& e)
 
1052
      {
 
1053
      results.push_back(Test::Result::Failure(header_or_name,
 
1054
                                              "run_final_tests exception " + std::string(e.what())));
 
1055
      }
 
1056
 
 
1057
   m_first = true;
 
1058
 
 
1059
   return results;
 
1060
   }
 
1061
 
 
1062
}