• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5356685938

23 Jun 2023 01:39PM UTC coverage: 91.746% (+0.02%) from 91.728%
5356685938

push

github

randombit
Merge GH #3595 Apply clang-tidy more universally

Previously clang-tidy ruleset disabled certain rules for the cli and
tests. Remove these exceptions, and fix the relevant warnings.

Also fix compile_commands.json which had previously not provided information for
the examples, BoGo shim, or fuzzers. As a result, clang-tidy was effectively
blind to them. Fix various clang-tidy findings in these files.

Additionally fix clang-tidy warnings that were in Boost or Sqlite3 specific
code, which had been accidentally omitted in past checks.

Modify the nightly clang-tidy run to additionally check the examples, fuzzers,
shim, and Sqlite3/Boost specific code.

78183 of 85217 relevant lines covered (91.75%)

12364366.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.25
/src/tests/test_x509_path.cpp
1
/*
2
* (C) 2006,2011,2012,2014,2015 Jack Lloyd
3
* (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_X509_CERTIFICATES)
11
   #include <botan/data_src.h>
12
   #include <botan/exceptn.h>
13
   #include <botan/pkcs10.h>
14
   #include <botan/x509_crl.h>
15
   #include <botan/x509_key.h>
16
   #include <botan/x509path.h>
17
   #include <botan/internal/calendar.h>
18
   #include <botan/internal/filesystem.h>
19
   #include <botan/internal/fmt.h>
20
   #include <botan/internal/parsing.h>
21

22
   #include <algorithm>
23
   #include <fstream>
24
   #include <limits>
25
   #include <map>
26
   #include <string>
27
   #include <vector>
28
#endif
29

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
35

36
   #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
37

38
std::map<std::string, std::string> read_results(const std::string& results_file, const char delim = ':') {
6✔
39
   std::ifstream in(results_file);
6✔
40
   if(!in.good()) {
6✔
41
      throw Test_Error("Failed reading " + results_file);
×
42
   }
43

44
   std::map<std::string, std::string> m;
6✔
45
   std::string line;
6✔
46
   while(in.good()) {
420✔
47
      std::getline(in, line);
414✔
48
      if(line.empty()) {
414✔
49
         continue;
14✔
50
      }
51
      if(line[0] == '#') {
409✔
52
         continue;
9✔
53
      }
54

55
      std::vector<std::string> parts = Botan::split_on(line, delim);
400✔
56

57
      if(parts.size() != 2) {
400✔
58
         throw Test_Error("Invalid line " + line);
×
59
      }
60

61
      m[parts[0]] = parts[1];
400✔
62
   }
400✔
63

64
   return m;
12✔
65
}
6✔
66

67
std::set<Botan::Certificate_Status_Code> flatten(const Botan::CertificatePathStatusCodes& codes) {
4✔
68
   std::set<Botan::Certificate_Status_Code> result;
4✔
69

70
   for(const auto& statuses : codes) {
16✔
71
      result.insert(statuses.begin(), statuses.end());
12✔
72
   }
73

74
   return result;
4✔
75
}
×
76

77
class X509test_Path_Validation_Tests final : public Test {
×
78
   public:
79
      std::vector<Test::Result> run() override {
1✔
80
         std::vector<Test::Result> results;
1✔
81

82
         // Test certs generated by https://github.com/yymax/x509test
83

84
         std::map<std::string, std::string> expected = read_results(Test::data_file("x509/x509test/expected.txt"));
2✔
85

86
         // Current tests use SHA-1
87
         const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
88

89
         Botan::X509_Certificate root(Test::data_file("x509/x509test/root.pem"));
2✔
90
         Botan::Certificate_Store_In_Memory trusted;
1✔
91
         trusted.add_certificate(root);
1✔
92

93
         auto validation_time = Botan::calendar_point(2016, 10, 21, 4, 20, 0).to_std_timepoint();
1✔
94

95
         for(auto i = expected.begin(); i != expected.end(); ++i) {
38✔
96
            Test::Result result("X509test path validation");
37✔
97
            result.start_timer();
37✔
98
            const std::string filename = i->first;
37✔
99
            const std::string expected_result = i->second;
37✔
100

101
            std::vector<Botan::X509_Certificate> certs = load_cert_file(Test::data_file("x509/x509test/" + filename));
74✔
102

103
            if(certs.empty()) {
37✔
104
               throw Test_Error("Failed to read certs from " + filename);
×
105
            }
106

107
            Botan::Path_Validation_Result path_result = Botan::x509_path_validate(
37✔
108
               certs, restrictions, trusted, "www.tls.test", Botan::Usage_Type::TLS_SERVER_AUTH, validation_time);
37✔
109

110
            if(path_result.successful_validation() && path_result.trust_root() != root) {
37✔
111
               path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
×
112
            }
113

114
            result.test_eq("test " + filename, path_result.result_string(), expected_result);
74✔
115
            result.test_eq("test no warnings string", path_result.warnings_string(), "");
74✔
116
            result.confirm("test no warnings", path_result.no_warnings());
74✔
117
            result.end_timer();
37✔
118
            results.push_back(result);
37✔
119
         }
103✔
120

121
         // test softfail
122
         {
1✔
123
            Test::Result result("X509test path validation softfail");
1✔
124
            result.start_timer();
1✔
125

126
            // this certificate must not have a OCSP URL
127
            const std::string filename = "ValidAltName.pem";
1✔
128
            std::vector<Botan::X509_Certificate> certs = load_cert_file(Test::data_file("x509/x509test/" + filename));
2✔
129
            if(certs.empty()) {
1✔
130
               throw Test_Error("Failed to read certs from " + filename);
×
131
            }
132

133
            Botan::Path_Validation_Result path_result =
1✔
134
               Botan::x509_path_validate(certs,
135
                                         restrictions,
136
                                         trusted,
137
                                         "www.tls.test",
138
                                         Botan::Usage_Type::TLS_SERVER_AUTH,
139
                                         validation_time,
140
                                         /* activate check_ocsp_online */ std::chrono::milliseconds(1000),
1✔
141
                                         {});
1✔
142

143
            if(path_result.successful_validation() && path_result.trust_root() != root) {
1✔
144
               path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
×
145
            }
146

147
            // certificate verification succeed even if no OCSP URL (softfail)
148
            result.confirm("test success", path_result.successful_validation());
2✔
149
            result.test_eq("test " + filename, path_result.result_string(), "Verified");
2✔
150
      #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
151
            // if softfail, there is warnings
152
            result.confirm("test warnings", !path_result.no_warnings());
2✔
153
            result.test_eq("test warnings string", path_result.warnings_string(), "[0] OCSP URL not available");
3✔
154
      #endif
155
            result.end_timer();
1✔
156
            results.push_back(result);
1✔
157
         }
2✔
158

159
         return results;
2✔
160
      }
1✔
161

162
   private:
163
      static std::vector<Botan::X509_Certificate> load_cert_file(const std::string& filename) {
38✔
164
         Botan::DataSource_Stream in(filename);
38✔
165

166
         std::vector<Botan::X509_Certificate> certs;
38✔
167
         while(!in.end_of_data()) {
200✔
168
            try {
162✔
169
               certs.emplace_back(in);
162✔
170
            } catch(Botan::Decoding_Error&) {}
38✔
171
         }
172

173
         return certs;
38✔
174
      }
38✔
175
};
176

177
BOTAN_REGISTER_TEST("x509", "x509_path_x509test", X509test_Path_Validation_Tests);
178

179
class NIST_Path_Validation_Tests final : public Test {
×
180
   public:
181
      std::vector<Test::Result> run() override;
182
};
183

184
std::vector<Test::Result> NIST_Path_Validation_Tests::run() {
1✔
185
   if(Botan::has_filesystem_impl() == false) {
1✔
186
      return {Test::Result::Note("NIST path validation", "Skipping due to missing filesystem access")};
×
187
   }
188

189
   std::vector<Test::Result> results;
1✔
190

191
   /**
192
   * Code to run the X.509v3 processing tests described in "Conformance
193
   *  Testing of Relying Party Client Certificate Path Proccessing Logic",
194
   *  which is available on NIST's web site.
195
   *
196
   * Known Failures/Problems:
197
   *  - Policy extensions are not implemented, so we skip tests #34-#53.
198
   *  - Tests #75 and #76 are skipped as they make use of relatively
199
   *    obscure CRL extensions which are not supported.
200
   */
201
   const std::string nist_test_dir = Test::data_dir() + "/x509/nist";
1✔
202

203
   std::map<std::string, std::string> expected = read_results(Test::data_file("x509/nist/expected.txt"));
2✔
204

205
   const Botan::X509_Certificate root_cert(nist_test_dir + "/root.crt");
1✔
206
   const Botan::X509_CRL root_crl(nist_test_dir + "/root.crl");
1✔
207

208
   const auto validation_time = Botan::calendar_point(2018, 4, 1, 9, 30, 33).to_std_timepoint();
1✔
209

210
   for(auto i = expected.begin(); i != expected.end(); ++i) {
72✔
211
      Test::Result result("NIST path validation");
71✔
212
      result.start_timer();
71✔
213

214
      const std::string test_name = i->first;
71✔
215

216
      try {
71✔
217
         const std::string expected_result = i->second;
71✔
218

219
         const std::string test_dir = Botan::fmt("{}/{}", nist_test_dir, test_name);
71✔
220

221
         const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
71✔
222

223
         if(all_files.empty()) {
71✔
224
            result.test_failure("No test files found in " + test_dir);
×
225
            results.push_back(result);
×
226
            continue;
×
227
         }
228

229
         Botan::Certificate_Store_In_Memory store;
71✔
230

231
         store.add_certificate(root_cert);
71✔
232
         store.add_crl(root_crl);
71✔
233

234
         for(const auto& file : all_files) {
361✔
235
            if(file.find(".crt") != std::string::npos && file != "end.crt") {
290✔
236
               store.add_certificate(Botan::X509_Certificate(file));
181✔
237
            } else if(file.find(".crl") != std::string::npos) {
109✔
238
               Botan::DataSource_Stream in(file, true);
109✔
239
               Botan::X509_CRL crl(in);
109✔
240
               store.add_crl(crl);
109✔
241
            }
109✔
242
         }
243

244
         Botan::X509_Certificate end_user(test_dir + "/end.crt");
71✔
245

246
         // 1024 bit root cert
247
         Botan::Path_Validation_Restrictions restrictions(true, 80);
142✔
248

249
         Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
71✔
250
            end_user, restrictions, store, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
71✔
251

252
         result.test_eq(test_name + " path validation result", validation_result.result_string(), expected_result);
142✔
253
      } catch(std::exception& e) {
173✔
254
         result.test_failure(test_name, e.what());
×
255
      }
×
256

257
      result.end_timer();
71✔
258
      results.push_back(result);
71✔
259
   }
71✔
260

261
   return results;
1✔
262
}
2✔
263

264
BOTAN_REGISTER_TEST("x509", "x509_path_nist", NIST_Path_Validation_Tests);
265

266
class Extended_Path_Validation_Tests final : public Test {
×
267
   public:
268
      std::vector<Test::Result> run() override;
269
};
270

271
std::vector<Test::Result> Extended_Path_Validation_Tests::run() {
1✔
272
   if(Botan::has_filesystem_impl() == false) {
1✔
273
      return {Test::Result::Note("Extended x509 path validation", "Skipping due to missing filesystem access")};
×
274
   }
275

276
   std::vector<Test::Result> results;
1✔
277

278
   const std::string extended_x509_test_dir = Test::data_dir() + "/x509/extended";
1✔
279

280
   std::map<std::string, std::string> expected = read_results(Test::data_file("x509/extended/expected.txt"));
2✔
281

282
   auto validation_time = Botan::calendar_point(2017, 9, 1, 9, 30, 33).to_std_timepoint();
1✔
283

284
   for(auto i = expected.begin(); i != expected.end(); ++i) {
4✔
285
      const std::string test_name = i->first;
3✔
286
      const std::string expected_result = i->second;
3✔
287

288
      const std::string test_dir = Botan::fmt("{}/{}", extended_x509_test_dir, test_name);
3✔
289

290
      Test::Result result("Extended X509 path validation");
3✔
291
      result.start_timer();
3✔
292

293
      const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
3✔
294

295
      if(all_files.empty()) {
3✔
296
         result.test_failure("No test files found in " + test_dir);
×
297
         results.push_back(result);
×
298
         continue;
×
299
      }
300

301
      Botan::Certificate_Store_In_Memory store;
3✔
302

303
      for(const auto& file : all_files) {
13✔
304
         if(file.find(".crt") != std::string::npos && file != "end.crt") {
10✔
305
            store.add_certificate(Botan::X509_Certificate(file));
10✔
306
         }
307
      }
308

309
      Botan::X509_Certificate end_user(test_dir + "/end.crt");
3✔
310

311
      Botan::Path_Validation_Restrictions restrictions;
6✔
312
      Botan::Path_Validation_Result validation_result =
3✔
313
         Botan::x509_path_validate(end_user, restrictions, store, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
3✔
314

315
      result.test_eq(test_name + " path validation result", validation_result.result_string(), expected_result);
6✔
316

317
      result.end_timer();
3✔
318
      results.push_back(result);
3✔
319
   }
6✔
320

321
   return results;
1✔
322
}
2✔
323

324
BOTAN_REGISTER_TEST("x509", "x509_path_extended", Extended_Path_Validation_Tests);
325

326
class PSS_Path_Validation_Tests : public Test {
×
327
   public:
328
      std::vector<Test::Result> run() override;
329
};
330

331
std::vector<Test::Result> PSS_Path_Validation_Tests::run() {
1✔
332
   if(Botan::has_filesystem_impl() == false) {
1✔
333
      return {Test::Result::Note("RSA-PSS X509 signature validation", "Skipping due to missing filesystem access")};
×
334
   }
335

336
   std::vector<Test::Result> results;
1✔
337

338
   const std::string pss_x509_test_dir = Test::data_dir() + "/x509/pss_certs";
1✔
339

340
   std::map<std::string, std::string> expected = read_results(Test::data_file("x509/pss_certs/expected.txt"));
2✔
341

342
   std::map<std::string, std::string> validation_times =
1✔
343
      read_results(Test::data_file("x509/pss_certs/validation_times.txt"));
2✔
344

345
   auto validation_times_iter = validation_times.begin();
1✔
346
   for(auto i = expected.begin(); i != expected.end(); ++i) {
119✔
347
      const std::string test_name = i->first;
118✔
348
      const std::string expected_result = i->second;
118✔
349

350
      const std::string test_dir = Botan::fmt("{}/{}", pss_x509_test_dir, test_name);
118✔
351

352
      Test::Result result("RSA-PSS X509 signature validation");
118✔
353
      result.start_timer();
118✔
354

355
      const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
118✔
356

357
      if(all_files.empty()) {
118✔
358
         result.test_failure("No test files found in " + test_dir);
×
359
         results.push_back(result);
×
360
         continue;
×
361
      }
362

363
      std::optional<Botan::X509_CRL> crl;
118✔
364
      std::optional<Botan::X509_Certificate> end;
118✔
365
      std::optional<Botan::X509_Certificate> root;
118✔
366
      Botan::Certificate_Store_In_Memory store;
118✔
367
      std::optional<Botan::PKCS10_Request> csr;
118✔
368

369
      const auto validation_year = Botan::to_u32bit((validation_times_iter++)->second);
118✔
370

371
      const auto validation_time = Botan::calendar_point(validation_year, 0, 0, 0, 0, 0).to_std_timepoint();
118✔
372

373
      for(const auto& file : all_files) {
345✔
374
         if(file.find("end.crt") != std::string::npos) {
227✔
375
            end = Botan::X509_Certificate(file);
113✔
376
         } else if(file.find("root.crt") != std::string::npos) {
114✔
377
            root = Botan::X509_Certificate(file);
97✔
378
            store.add_certificate(*root);
97✔
379
         } else if(file.find(".crl") != std::string::npos) {
17✔
380
            crl = Botan::X509_CRL(file);
6✔
381
         } else if(file.find(".csr") != std::string::npos) {
11✔
382
            csr = Botan::PKCS10_Request(file);
5✔
383
         }
384
      }
385

386
      if(end && crl && root)  // CRL tests
118✔
387
      {
388
         const std::vector<Botan::X509_Certificate> cert_path = {*end, *root};
18✔
389
         const std::vector<std::optional<Botan::X509_CRL>> crls = {crl};
18✔
390
         auto crl_status = Botan::PKIX::check_crl(
6✔
391
            cert_path,
392
            crls,
393
            validation_time);  // alternatively we could just call crl.check_signature( root_pubkey )
6✔
394

395
         result.test_eq(test_name + " check_crl result",
12✔
396
                        Botan::Path_Validation_Result::status_string(Botan::PKIX::overall_status(crl_status)),
397
                        expected_result);
398
      } else if(end && root)  // CRT chain tests
118✔
399
      {
400
         // sha-1 is used
401
         Botan::Path_Validation_Restrictions restrictions(false, 80);
182✔
402

403
         Botan::Path_Validation_Result validation_result =
91✔
404
            Botan::x509_path_validate(*end, restrictions, store, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
91✔
405

406
         result.test_eq(test_name + " path validation result", validation_result.result_string(), expected_result);
182✔
407
      } else if(end && !root)  // CRT self signed tests
112✔
408
      {
409
         auto pubkey = end->subject_public_key();
16✔
410
         result.test_eq(test_name + " verify signature", end->check_signature(*pubkey), !!(std::stoi(expected_result)));
32✔
411
      } else if(csr)  // PKCS#10 Request
21✔
412
      {
413
         auto pubkey = csr->subject_public_key();
5✔
414
         result.test_eq(test_name + " verify signature", csr->check_signature(*pubkey), !!(std::stoi(expected_result)));
10✔
415
      }
5✔
416

417
      result.end_timer();
118✔
418
      results.push_back(result);
118✔
419
   }
454✔
420

421
   return results;
1✔
422
}
2✔
423

424
BOTAN_REGISTER_TEST("x509", "x509_path_rsa_pss", PSS_Path_Validation_Tests);
425

426
class Validate_V1Cert_Test final : public Test {
×
427
   public:
428
      std::vector<Test::Result> run() override;
429
};
430

431
std::vector<Test::Result> Validate_V1Cert_Test::run() {
1✔
432
   if(Botan::has_filesystem_impl() == false) {
1✔
433
      return {Test::Result::Note("BSI path validation", "Skipping due to missing filesystem access")};
×
434
   }
435

436
   std::vector<Test::Result> results;
1✔
437

438
   const std::string root_crt = Test::data_file("/x509/misc/v1ca/root.pem");
1✔
439
   const std::string int_crt = Test::data_file("/x509/misc/v1ca/int.pem");
1✔
440
   const std::string ee_crt = Test::data_file("/x509/misc/v1ca/ee.pem");
1✔
441

442
   auto validation_time = Botan::calendar_point(2019, 4, 19, 23, 0, 0).to_std_timepoint();
1✔
443

444
   Botan::X509_Certificate root(root_crt);
1✔
445
   Botan::X509_Certificate intermediate(int_crt);
1✔
446
   Botan::X509_Certificate ee_cert(ee_crt);
1✔
447

448
   Botan::Certificate_Store_In_Memory trusted;
1✔
449
   trusted.add_certificate(root);
1✔
450

451
   std::vector<Botan::X509_Certificate> chain = {ee_cert, intermediate};
3✔
452

453
   Botan::Path_Validation_Restrictions restrictions;
2✔
454
   Botan::Path_Validation_Result validation_result =
1✔
455
      Botan::x509_path_validate(chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
456

457
   Test::Result result("Verifying using v1 certificate");
1✔
458
   result.test_eq("Path validation result", validation_result.result_string(), "Verified");
2✔
459

460
   Botan::Certificate_Store_In_Memory empty;
1✔
461

462
   std::vector<Botan::X509_Certificate> new_chain = {ee_cert, intermediate, root};
4✔
463

464
   Botan::Path_Validation_Result validation_result2 =
1✔
465
      Botan::x509_path_validate(new_chain, restrictions, empty, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
466

467
   result.test_eq("Path validation result", validation_result2.result_string(), "Cannot establish trust");
3✔
468

469
   return {result};
2✔
470
}
4✔
471

472
BOTAN_REGISTER_TEST("x509", "x509_v1_ca", Validate_V1Cert_Test);
473

474
class Validate_V2Uid_in_V1_Test final : public Test {
×
475
   public:
476
      std::vector<Test::Result> run() override;
477
};
478

479
std::vector<Test::Result> Validate_V2Uid_in_V1_Test::run() {
1✔
480
   if(Botan::has_filesystem_impl() == false) {
1✔
481
      return {Test::Result::Note("Path validation", "Skipping due to missing filesystem access")};
×
482
   }
483

484
   std::vector<Test::Result> results;
1✔
485

486
   const std::string root_crt = Test::data_file("/x509/v2-in-v1/root.pem");
1✔
487
   const std::string int_crt = Test::data_file("/x509/v2-in-v1/int.pem");
1✔
488
   const std::string ee_crt = Test::data_file("/x509/v2-in-v1/leaf.pem");
1✔
489

490
   auto validation_time = Botan::calendar_point(2020, 1, 1, 1, 0, 0).to_std_timepoint();
1✔
491

492
   Botan::X509_Certificate root(root_crt);
1✔
493
   Botan::X509_Certificate intermediate(int_crt);
1✔
494
   Botan::X509_Certificate ee_cert(ee_crt);
1✔
495

496
   Botan::Certificate_Store_In_Memory trusted;
1✔
497
   trusted.add_certificate(root);
1✔
498

499
   std::vector<Botan::X509_Certificate> chain = {ee_cert, intermediate};
3✔
500

501
   Botan::Path_Validation_Restrictions restrictions;
2✔
502
   Botan::Path_Validation_Result validation_result =
1✔
503
      Botan::x509_path_validate(chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
504

505
   Test::Result result("Verifying v1 certificate using v2 uid fields");
1✔
506
   result.test_eq("Path validation failed", validation_result.successful_validation(), false);
1✔
507
   result.test_eq(
3✔
508
      "Path validation result", validation_result.result_string(), "Encountered v2 identifiers in v1 certificate");
2✔
509

510
   return {result};
2✔
511
}
4✔
512

513
BOTAN_REGISTER_TEST("x509", "x509_v2uid_in_v1", Validate_V2Uid_in_V1_Test);
514

515
class Validate_Name_Constraint_SAN_Test final : public Test {
×
516
   public:
517
      std::vector<Test::Result> run() override;
518
};
519

520
std::vector<Test::Result> Validate_Name_Constraint_SAN_Test::run() {
1✔
521
   if(Botan::has_filesystem_impl() == false) {
1✔
522
      return {Test::Result::Note("Path validation", "Skipping due to missing filesystem access")};
×
523
   }
524

525
   std::vector<Test::Result> results;
1✔
526

527
   const std::string root_crt = Test::data_file("/x509/name_constraint_san/root.pem");
1✔
528
   const std::string int_crt = Test::data_file("/x509/name_constraint_san/int.pem");
1✔
529
   const std::string ee_crt = Test::data_file("/x509/name_constraint_san/leaf.pem");
1✔
530

531
   auto validation_time = Botan::calendar_point(2020, 1, 1, 1, 0, 0).to_std_timepoint();
1✔
532

533
   Botan::X509_Certificate root(root_crt);
1✔
534
   Botan::X509_Certificate intermediate(int_crt);
1✔
535
   Botan::X509_Certificate ee_cert(ee_crt);
1✔
536

537
   Botan::Certificate_Store_In_Memory trusted;
1✔
538
   trusted.add_certificate(root);
1✔
539

540
   std::vector<Botan::X509_Certificate> chain = {ee_cert, intermediate};
3✔
541

542
   Botan::Path_Validation_Restrictions restrictions;
2✔
543
   Botan::Path_Validation_Result validation_result =
1✔
544
      Botan::x509_path_validate(chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
545

546
   Test::Result result("Verifying certificate with alternative SAN violating name constraint");
1✔
547
   result.test_eq("Path validation failed", validation_result.successful_validation(), false);
1✔
548
   result.test_eq(
3✔
549
      "Path validation result", validation_result.result_string(), "Certificate does not pass name constraint");
2✔
550

551
   return {result};
2✔
552
}
4✔
553

554
BOTAN_REGISTER_TEST("x509", "x509_name_constraint_san", Validate_Name_Constraint_SAN_Test);
555

556
class Validate_Name_Constraint_CaseInsensitive final : public Test {
×
557
   public:
558
      std::vector<Test::Result> run() override;
559
};
560

561
std::vector<Test::Result> Validate_Name_Constraint_CaseInsensitive::run() {
1✔
562
   if(Botan::has_filesystem_impl() == false) {
1✔
563
      return {Test::Result::Note("Path validation", "Skipping due to missing filesystem access")};
×
564
   }
565

566
   std::vector<Test::Result> results;
1✔
567

568
   const std::string root_crt = Test::data_file("/x509/misc/name_constraint_ci/root.pem");
1✔
569
   const std::string int_crt = Test::data_file("/x509/misc/name_constraint_ci/int.pem");
1✔
570
   const std::string ee_crt = Test::data_file("/x509/misc/name_constraint_ci/leaf.pem");
1✔
571

572
   auto validation_time = Botan::calendar_point(2021, 5, 8, 1, 0, 0).to_std_timepoint();
1✔
573

574
   Botan::X509_Certificate root(root_crt);
1✔
575
   Botan::X509_Certificate intermediate(int_crt);
1✔
576
   Botan::X509_Certificate ee_cert(ee_crt);
1✔
577

578
   Botan::Certificate_Store_In_Memory trusted;
1✔
579
   trusted.add_certificate(root);
1✔
580

581
   std::vector<Botan::X509_Certificate> chain = {ee_cert, intermediate};
3✔
582

583
   Botan::Path_Validation_Restrictions restrictions;
2✔
584
   Botan::Path_Validation_Result validation_result =
1✔
585
      Botan::x509_path_validate(chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
586

587
   Test::Result result("DNS name constraints are case insensitive");
1✔
588
   result.test_eq("Path validation succeeded", validation_result.successful_validation(), true);
1✔
589

590
   return {result};
2✔
591
}
4✔
592

593
BOTAN_REGISTER_TEST("x509", "x509_name_constraint_ci", Validate_Name_Constraint_CaseInsensitive);
594

595
class Validate_Name_Constraint_NoCheckSelf final : public Test {
×
596
   public:
597
      std::vector<Test::Result> run() override;
598
};
599

600
std::vector<Test::Result> Validate_Name_Constraint_NoCheckSelf::run() {
1✔
601
   if(Botan::has_filesystem_impl() == false) {
1✔
602
      return {Test::Result::Note("Path validation", "Skipping due to missing filesystem access")};
×
603
   }
604

605
   std::vector<Test::Result> results;
1✔
606

607
   const std::string root_crt = Test::data_file("/x509/misc/nc_skip_self/root.pem");
1✔
608
   const std::string int_crt = Test::data_file("/x509/misc/nc_skip_self/int.pem");
1✔
609
   const std::string ee_crt = Test::data_file("/x509/misc/nc_skip_self/leaf.pem");
1✔
610

611
   auto validation_time = Botan::calendar_point(2021, 5, 8, 1, 0, 0).to_std_timepoint();
1✔
612

613
   Botan::X509_Certificate root(root_crt);
1✔
614
   Botan::X509_Certificate intermediate(int_crt);
1✔
615
   Botan::X509_Certificate ee_cert(ee_crt);
1✔
616

617
   Botan::Certificate_Store_In_Memory trusted;
1✔
618
   trusted.add_certificate(root);
1✔
619

620
   std::vector<Botan::X509_Certificate> chain = {ee_cert, intermediate};
3✔
621

622
   Botan::Path_Validation_Restrictions restrictions;
2✔
623
   Botan::Path_Validation_Result validation_result =
1✔
624
      Botan::x509_path_validate(chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
1✔
625

626
   Test::Result result("Name constraints do not apply to the certificate which includes them");
1✔
627
   result.test_eq("Path validation succeeded", validation_result.successful_validation(), true);
1✔
628

629
   return {result};
2✔
630
}
4✔
631

632
BOTAN_REGISTER_TEST("x509", "x509_name_constraint_no_check_self", Validate_Name_Constraint_NoCheckSelf);
633

634
class BSI_Path_Validation_Tests final : public Test
×
635

636
{
637
   public:
638
      std::vector<Test::Result> run() override;
639
};
640

641
std::vector<Test::Result> BSI_Path_Validation_Tests::run() {
1✔
642
   if(Botan::has_filesystem_impl() == false) {
1✔
643
      return {Test::Result::Note("BSI path validation", "Skipping due to missing filesystem access")};
×
644
   }
645

646
   std::vector<Test::Result> results;
1✔
647

648
   const std::string bsi_test_dir = Test::data_dir() + "/x509/bsi";
1✔
649

650
   const std::map<std::string, std::string> expected = read_results(Test::data_file("/x509/bsi/expected.txt"), '$');
2✔
651

652
   for(const auto& i : expected) {
54✔
653
      const std::string test_name = i.first;
53✔
654
      std::string expected_result = i.second;
53✔
655

656
      #if !defined(BOTAN_HAS_MD5)
657
      if(expected_result == "Hash function used is considered too weak for security")
658
         expected_result = "Certificate signed with unknown/unavailable algorithm";
659
      #endif
660

661
      const std::string test_dir = Botan::fmt("{}/{}", bsi_test_dir, test_name);
53✔
662

663
      Test::Result result("BSI path validation");
53✔
664
      result.start_timer();
53✔
665

666
      const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
53✔
667

668
      if(all_files.empty()) {
53✔
669
         result.test_failure("No test files found in " + test_dir);
×
670
         results.push_back(result);
×
671
         continue;
×
672
      }
673

674
      Botan::Certificate_Store_In_Memory trusted;
53✔
675
      std::vector<Botan::X509_Certificate> certs;
53✔
676

677
      auto validation_time = Botan::calendar_point(2017, 8, 19, 12, 0, 0).to_std_timepoint();
53✔
678

679
      // By convention: if CRL is a substring if the directory name,
680
      // we need to check the CRLs
681
      bool use_crl = false;
53✔
682
      if(test_dir.find("CRL") != std::string::npos) {
53✔
683
         use_crl = true;
16✔
684
      }
685

686
      try {
53✔
687
         for(const auto& file : all_files) {
437✔
688
            // found a trust anchor
689
            if(file.find("TA") != std::string::npos) {
388✔
690
               trusted.add_certificate(Botan::X509_Certificate(file));
49✔
691
            }
692
            // found the target certificate. It needs to be at the front of certs
693
            else if(file.find("TC") != std::string::npos) {
339✔
694
               certs.insert(certs.begin(), Botan::X509_Certificate(file));
53✔
695
            }
696
            // found a certificate that might be part of a valid certificate chain to the trust anchor
697
            else if(file.find(".crt") != std::string::npos) {
286✔
698
               certs.push_back(Botan::X509_Certificate(file));
110✔
699
            } else if(file.find(".crl") != std::string::npos) {
231✔
700
               trusted.add_crl(Botan::X509_CRL(file));
28✔
701
            }
702
         }
703

704
         Botan::Path_Validation_Restrictions restrictions(use_crl, 79, use_crl);
98✔
705

706
         /*
707
          * Following the test document, the test are executed 16 times with
708
          * randomly chosen order of the available certificates. However, the target
709
          * certificate needs to stay in front.
710
          * For certain test, the order in which the certificates are given to
711
          * the validation function may be relevant, i.e. if issuer DNs are
712
          * ambiguous.
713
          */
714
         struct random_bit_generator {
49✔
715
               using result_type = size_t;
716

717
               static constexpr result_type min() { return 0; }
718

719
               static constexpr result_type max() { return std::numeric_limits<size_t>::max(); }
720

721
               result_type operator()() {
80✔
722
                  size_t s;
80✔
723
                  Test::rng().randomize(reinterpret_cast<uint8_t*>(&s), sizeof(s));
80✔
724
                  return s;
80✔
725
               }
726
         } rbg;
727

728
         for(size_t r = 0; r < 16; r++) {
833✔
729
            std::shuffle(++(certs.begin()), certs.end(), rbg);
784✔
730

731
            Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
784✔
732
               certs, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
784✔
733

734
            // We expect to be warned
735
            if(expected_result.find("Warning: ") == 0) {
784✔
736
               std::string stripped = expected_result.substr(std::string("Warning: ").size());
32✔
737
               bool found_warning = false;
32✔
738
               for(const auto& warning_set : validation_result.warnings()) {
128✔
739
                  for(const auto& warning : warning_set) {
128✔
740
                     std::string warning_str(Botan::to_string(warning));
32✔
741
                     if(stripped == warning_str) {
32✔
742
                        result.test_eq(test_name + " path validation result", warning_str, stripped);
32✔
743
                        found_warning = true;
32✔
744
                     }
745
                  }
32✔
746
               }
32✔
747
               if(!found_warning) {
32✔
748
                  result.test_failure(test_name, "Did not receive the expected warning: " + stripped);
×
749
               }
750
            } else {
32✔
751
               result.test_eq(
752✔
752
                  test_name + " path validation result", validation_result.result_string(), expected_result);
2,096✔
753
            }
754
         }
784✔
755
      }
49✔
756

757
      /* Some certificates are rejected when executing the X509_Certificate constructor
758
       * by throwing a Decoding_Error exception.
759
       */
760
      catch(const Botan::Exception& e) {
4✔
761
         if(e.error_type() == Botan::ErrorType::DecodingFailure) {
4✔
762
            result.test_eq(test_name + " path validation result", e.what(), expected_result);
12✔
763
         } else {
764
            result.test_failure(test_name, e.what());
×
765
         }
766
      }
4✔
767

768
      result.end_timer();
53✔
769
      results.push_back(result);
53✔
770
   }
152✔
771

772
   return results;
1✔
773
}
2✔
774

775
BOTAN_REGISTER_TEST("x509", "x509_path_bsi", BSI_Path_Validation_Tests);
776

777
class Path_Validation_With_OCSP_Tests final : public Test {
×
778
   public:
779
      static Botan::X509_Certificate load_test_X509_cert(const std::string& path) {
24✔
780
         return Botan::X509_Certificate(Test::data_file(path));
48✔
781
      }
782

783
      static std::optional<Botan::OCSP::Response> load_test_OCSP_resp(const std::string& path) {
12✔
784
         return Botan::OCSP::Response(Test::read_binary_data_file(path));
24✔
785
      }
786

787
      static Test::Result validate_with_ocsp_with_next_update_without_max_age() {
1✔
788
         Test::Result result("path check with ocsp with next_update w/o max_age");
1✔
789
         Botan::Certificate_Store_In_Memory trusted;
1✔
790

791
         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false);
2✔
792

793
         auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
1✔
794
         auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
1✔
795
         auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
1✔
796
         trusted.add_certificate(trust_root);
1✔
797

798
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
799

800
         std::optional<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
3✔
801

802
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
5✔
803
                               const Botan::Certificate_Status_Code expected) {
804
            const auto path_result = Botan::x509_path_validate(cert_path,
8✔
805
                                                               restrictions,
4✔
806
                                                               trusted,
4✔
807
                                                               "",
808
                                                               Botan::Usage_Type::UNSPECIFIED,
809
                                                               valid_time,
810
                                                               std::chrono::milliseconds(0),
4✔
811
                                                               {ocsp});
4✔
812

813
            return result.confirm(std::string("Status: '") + Botan::to_string(expected) + "' should match '" +
12✔
814
                                     Botan::to_string(path_result.result()) + "'",
8✔
815
                                  path_result.result() == expected);
8✔
816
         };
4✔
817

818
         check_path(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
1✔
819
                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
820
         check_path(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
1✔
821
                    Botan::Certificate_Status_Code::OK);
822
         check_path(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
1✔
823
                    Botan::Certificate_Status_Code::OK);
824
         check_path(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
1✔
825
                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
826

827
         return result;
2✔
828
      }
1✔
829

830
      static Test::Result validate_with_ocsp_with_next_update_with_max_age() {
1✔
831
         Test::Result result("path check with ocsp with next_update with max_age");
1✔
832
         Botan::Certificate_Store_In_Memory trusted;
1✔
833

834
         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false, std::chrono::minutes(59));
1✔
835

836
         auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
1✔
837
         auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
1✔
838
         auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
1✔
839
         trusted.add_certificate(trust_root);
1✔
840

841
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
842

843
         auto ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
1✔
844

845
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
5✔
846
                               const Botan::Certificate_Status_Code expected) {
847
            const auto path_result = Botan::x509_path_validate(cert_path,
8✔
848
                                                               restrictions,
4✔
849
                                                               trusted,
4✔
850
                                                               "",
851
                                                               Botan::Usage_Type::UNSPECIFIED,
852
                                                               valid_time,
853
                                                               std::chrono::milliseconds(0),
4✔
854
                                                               {ocsp});
8✔
855

856
            return result.confirm(std::string("Status: '") + Botan::to_string(expected) + "' should match '" +
12✔
857
                                     Botan::to_string(path_result.result()) + "'",
8✔
858
                                  path_result.result() == expected);
8✔
859
         };
4✔
860

861
         check_path(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
1✔
862
                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
863
         check_path(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
1✔
864
                    Botan::Certificate_Status_Code::OK);
865
         check_path(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
1✔
866
                    Botan::Certificate_Status_Code::OK);
867
         check_path(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
1✔
868
                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
869

870
         return result;
2✔
871
      }
1✔
872

873
      static Test::Result validate_with_ocsp_without_next_update_without_max_age() {
1✔
874
         Test::Result result("path check with ocsp w/o next_update w/o max_age");
1✔
875
         Botan::Certificate_Store_In_Memory trusted;
1✔
876

877
         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false);
2✔
878

879
         auto ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
1✔
880
         auto ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
1✔
881
         auto trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
1✔
882

883
         trusted.add_certificate(trust_root);
1✔
884

885
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
886

887
         auto ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
1✔
888

889
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
4✔
890
                               const Botan::Certificate_Status_Code expected) {
891
            const auto path_result = Botan::x509_path_validate(cert_path,
6✔
892
                                                               restrictions,
3✔
893
                                                               trusted,
3✔
894
                                                               "",
895
                                                               Botan::Usage_Type::UNSPECIFIED,
896
                                                               valid_time,
897
                                                               std::chrono::milliseconds(0),
3✔
898
                                                               {ocsp});
6✔
899

900
            return result.confirm(std::string("Status: '") + Botan::to_string(expected) + "' should match '" +
9✔
901
                                     Botan::to_string(path_result.result()) + "'",
6✔
902
                                  path_result.result() == expected);
6✔
903
         };
3✔
904

905
         check_path(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
1✔
906
                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
907
         check_path(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
1✔
908
                    Botan::Certificate_Status_Code::OK);
909
         check_path(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(), Botan::Certificate_Status_Code::OK);
1✔
910

911
         return result;
2✔
912
      }
1✔
913

914
      static Test::Result validate_with_ocsp_without_next_update_with_max_age() {
1✔
915
         Test::Result result("path check with ocsp w/o next_update with max_age");
1✔
916
         Botan::Certificate_Store_In_Memory trusted;
1✔
917

918
         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false, std::chrono::minutes(59));
1✔
919

920
         auto ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
1✔
921
         auto ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
1✔
922
         auto trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
1✔
923

924
         trusted.add_certificate(trust_root);
1✔
925

926
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
927

928
         auto ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
1✔
929

930
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
4✔
931
                               const Botan::Certificate_Status_Code expected) {
932
            const auto path_result = Botan::x509_path_validate(cert_path,
6✔
933
                                                               restrictions,
3✔
934
                                                               trusted,
3✔
935
                                                               "",
936
                                                               Botan::Usage_Type::UNSPECIFIED,
937
                                                               valid_time,
938
                                                               std::chrono::milliseconds(0),
3✔
939
                                                               {ocsp});
6✔
940

941
            return result.confirm(std::string("Status: '") + Botan::to_string(expected) + "' should match '" +
9✔
942
                                     Botan::to_string(path_result.result()) + "'",
6✔
943
                                  path_result.result() == expected);
6✔
944
         };
3✔
945

946
         check_path(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
1✔
947
                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
948
         check_path(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
1✔
949
                    Botan::Certificate_Status_Code::OK);
950
         check_path(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(),
1✔
951
                    Botan::Certificate_Status_Code::OCSP_IS_TOO_OLD);
952

953
         return result;
2✔
954
      }
1✔
955

956
      static Test::Result validate_with_ocsp_with_authorized_responder() {
1✔
957
         Test::Result result("path check with ocsp response from authorized responder certificate");
1✔
958
         Botan::Certificate_Store_In_Memory trusted;
1✔
959

960
         auto restrictions = Botan::Path_Validation_Restrictions(true,   // require revocation info
1✔
961
                                                                 110,    // minimum key strength
962
                                                                 true);  // OCSP for all intermediates
2✔
963

964
         auto ee = load_test_X509_cert("x509/ocsp/bdr.pem");
1✔
965
         auto ca = load_test_X509_cert("x509/ocsp/bdr-int.pem");
1✔
966
         auto trust_root = load_test_X509_cert("x509/ocsp/bdr-root.pem");
1✔
967

968
         // These OCSP responses are signed by an authorized OCSP responder
969
         // certificate issued by `ca` and `trust_root` respectively. Note that
970
         // the responder certificates contain the "OCSP No Check" extension,
971
         // meaning that they themselves do not need a revocation check via OCSP.
972
         auto ocsp_ee = load_test_OCSP_resp("x509/ocsp/bdr-ocsp-resp.der");
1✔
973
         auto ocsp_ca = load_test_OCSP_resp("x509/ocsp/bdr-int-ocsp-resp.der");
1✔
974

975
         trusted.add_certificate(trust_root);
1✔
976
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
977

978
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
4✔
979
                               const Botan::Certificate_Status_Code expected) {
980
            const auto path_result = Botan::x509_path_validate(cert_path,
9✔
981
                                                               restrictions,
3✔
982
                                                               trusted,
3✔
983
                                                               "",
984
                                                               Botan::Usage_Type::UNSPECIFIED,
985
                                                               valid_time,
986
                                                               std::chrono::milliseconds(0),
3✔
987
                                                               {ocsp_ee, ocsp_ca});
9✔
988

989
            return result.confirm(std::string("Status: '") + Botan::to_string(expected) + "' should match '" +
9✔
990
                                     Botan::to_string(path_result.result()) + "'",
6✔
991
                                  path_result.result() == expected);
6✔
992
         };
3✔
993

994
         check_path(Botan::calendar_point(2022, 9, 18, 16, 30, 0).to_std_timepoint(),
1✔
995
                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
996
         check_path(Botan::calendar_point(2022, 9, 19, 16, 30, 0).to_std_timepoint(),
1✔
997
                    Botan::Certificate_Status_Code::OK);
998
         check_path(Botan::calendar_point(2022, 9, 20, 16, 30, 0).to_std_timepoint(),
1✔
999
                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
1000

1001
         return result;
1✔
1002
      }
3✔
1003

1004
      static Test::Result validate_with_ocsp_with_authorized_responder_without_keyusage() {
1✔
1005
         Test::Result result(
1✔
1006
            "path check with ocsp response from authorized responder certificate (without sufficient key usage)");
1✔
1007
         Botan::Certificate_Store_In_Memory trusted;
1✔
1008

1009
         auto restrictions = Botan::Path_Validation_Restrictions(true,    // require revocation info
1✔
1010
                                                                 110,     // minimum key strength
1011
                                                                 false);  // OCSP for all intermediates
2✔
1012

1013
         // See `src/scripts/mychain_creater.sh` if you need to recreate those
1014
         auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem");
1✔
1015
         auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem");
1✔
1016
         auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem");
1✔
1017

1018
         auto ocsp_ee_delegate = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee_delegate_signed.der").value();
2✔
1019
         auto ocsp_ee_delegate_malformed =
1✔
1020
            load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee_delegate_signed_malformed.der").value();
2✔
1021

1022
         trusted.add_certificate(trust_root);
1✔
1023
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
1024

1025
         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
4✔
1026
                               const Botan::OCSP::Response& ocsp_ee,
1027
                               const Botan::Certificate_Status_Code expected,
1028
                               const std::optional<Botan::Certificate_Status_Code> also_expected = std::nullopt) {
1029
            const auto path_result = Botan::x509_path_validate(cert_path,
9✔
1030
                                                               restrictions,
3✔
1031
                                                               trusted,
3✔
1032
                                                               "",
1033
                                                               Botan::Usage_Type::UNSPECIFIED,
1034
                                                               valid_time,
1035
                                                               std::chrono::milliseconds(0),
3✔
1036
                                                               {ocsp_ee});
3✔
1037

1038
            result.test_is_eq("should result in expected validation status code", path_result.result(), expected);
3✔
1039
            if(also_expected) {
3✔
1040
               result.confirm("Secondary error is also present",
6✔
1041
                              flatten(path_result.all_statuses()).contains(also_expected.value()));
6✔
1042
            }
1043
         };
3✔
1044

1045
         check_path(Botan::calendar_point(2022, 9, 22, 23, 30, 0).to_std_timepoint(),
1✔
1046
                    ocsp_ee_delegate,
1047
                    Botan::Certificate_Status_Code::VERIFIED);
1048
         check_path(Botan::calendar_point(2022, 10, 8, 23, 30, 0).to_std_timepoint(),
1✔
1049
                    ocsp_ee_delegate,
1050
                    Botan::Certificate_Status_Code::CERT_HAS_EXPIRED,
1051
                    Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
1✔
1052
         check_path(Botan::calendar_point(2022, 9, 22, 23, 30, 0).to_std_timepoint(),
1✔
1053
                    ocsp_ee_delegate_malformed,
1054
                    Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE,
1055
                    Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
1✔
1056

1057
         return result;
1✔
1058
      }
1✔
1059

1060
      static Test::Result validate_with_forged_ocsp_using_self_signed_cert() {
1✔
1061
         Test::Result result("path check with forged ocsp using self-signed certificate");
1✔
1062
         Botan::Certificate_Store_In_Memory trusted;
1✔
1063

1064
         auto restrictions = Botan::Path_Validation_Restrictions(true,    // require revocation info
1✔
1065
                                                                 110,     // minimum key strength
1066
                                                                 false);  // OCSP for all intermediates
2✔
1067

1068
         auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
1✔
1069
         auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
1✔
1070
         auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
1✔
1071
         trusted.add_certificate(trust_root);
1✔
1072

1073
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
1074

1075
         auto check_path = [&](const std::string& forged_ocsp,
3✔
1076
                               const Botan::Certificate_Status_Code expected,
1077
                               const Botan::Certificate_Status_Code also_expected) {
1078
            auto ocsp = load_test_OCSP_resp(forged_ocsp);
2✔
1079
            const auto path_result =
2✔
1080
               Botan::x509_path_validate(cert_path,
4✔
1081
                                         restrictions,
2✔
1082
                                         trusted,
2✔
1083
                                         "",
1084
                                         Botan::Usage_Type::UNSPECIFIED,
1085
                                         Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
2✔
1086
                                         std::chrono::milliseconds(0),
2✔
1087
                                         {ocsp});
2✔
1088

1089
            result.test_is_eq(
2✔
1090
               "Path validation with forged OCSP response should fail with", path_result.result(), expected);
2✔
1091
            result.confirm("Secondary error is also present",
6✔
1092
                           flatten(path_result.all_statuses()).contains(also_expected));
4✔
1093
            result.test_note(std::string("Failed with: ") + Botan::to_string(path_result.result()));
4✔
1094
         };
4✔
1095

1096
         // In both cases the path validation should detect the forged OCSP
1097
         // response and generate an appropriate error. By no means it should
1098
         // follow the unauthentic OCSP response.
1099
         check_path("x509/ocsp/randombit_ocsp_forged_valid.der",
1✔
1100
                    Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
1101
                    Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
1102
         check_path("x509/ocsp/randombit_ocsp_forged_revoked.der",
1✔
1103
                    Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
1104
                    Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
1105

1106
         return result;
1✔
1107
      }
1✔
1108

1109
      static Test::Result validate_with_ocsp_self_signed_by_intermediate_cert() {
1✔
1110
         Test::Result result(
1✔
1111
            "path check with ocsp response for intermediate that is (maliciously) self-signed by the intermediate");
1✔
1112
         Botan::Certificate_Store_In_Memory trusted;
1✔
1113

1114
         auto restrictions = Botan::Path_Validation_Restrictions(true,   // require revocation info
1✔
1115
                                                                 110,    // minimum key strength
1116
                                                                 true);  // OCSP for all intermediates
2✔
1117

1118
         // See `src/scripts/mychain_creater.sh` if you need to recreate those
1119
         auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem");
1✔
1120
         auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem");
1✔
1121
         auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem");
1✔
1122

1123
         // this OCSP response for EE is valid (signed by intermediate cert)
1124
         auto ocsp_ee = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee.der");
1✔
1125

1126
         // this OCSP response for Intermediate is malicious (signed by intermediate itself)
1127
         auto ocsp_ca = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_int_self_signed.der");
1✔
1128

1129
         trusted.add_certificate(trust_root);
1✔
1130
         const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
4✔
1131

1132
         const auto path_result =
1✔
1133
            Botan::x509_path_validate(cert_path,
3✔
1134
                                      restrictions,
1135
                                      trusted,
1136
                                      "",
1137
                                      Botan::Usage_Type::UNSPECIFIED,
1138
                                      Botan::calendar_point(2022, 9, 22, 22, 30, 0).to_std_timepoint(),
1✔
1139
                                      std::chrono::milliseconds(0),
1✔
1140
                                      {ocsp_ee, ocsp_ca});
2✔
1141
         result.confirm("should reject intermediate OCSP response",
2✔
1142
                        path_result.result() == Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
1✔
1143
         result.test_note(std::string("Failed with: ") + Botan::to_string(path_result.result()));
2✔
1144

1145
         return result;
1✔
1146
      }
3✔
1147

1148
      std::vector<Test::Result> run() override {
1✔
1149
         return {validate_with_ocsp_with_next_update_without_max_age(),
1✔
1150
                 validate_with_ocsp_with_next_update_with_max_age(),
1151
                 validate_with_ocsp_without_next_update_without_max_age(),
1152
                 validate_with_ocsp_without_next_update_with_max_age(),
1153
                 validate_with_ocsp_with_authorized_responder(),
1154
                 validate_with_ocsp_with_authorized_responder_without_keyusage(),
1155
                 validate_with_forged_ocsp_using_self_signed_cert(),
1156
                 validate_with_ocsp_self_signed_by_intermediate_cert()};
9✔
1157
      }
1158
};
1159

1160
BOTAN_REGISTER_TEST("x509", "x509_path_with_ocsp", Path_Validation_With_OCSP_Tests);
1161

1162
   #endif
1163

1164
   #if defined(BOTAN_HAS_ECDSA)
1165

1166
class CVE_2020_0601_Tests final : public Test {
×
1167
   public:
1168
      std::vector<Test::Result> run() override {
1✔
1169
         Test::Result result("CVE-2020-0601");
1✔
1170
         auto ca_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/ca.pem"));
2✔
1171
         auto fake_ca_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/fake_ca.pem"));
2✔
1172
         auto ee_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/ee.pem"));
2✔
1173

1174
         Botan::Certificate_Store_In_Memory trusted;
1✔
1175
         trusted.add_certificate(ca_crt);
1✔
1176

1177
         const auto restrictions = Botan::Path_Validation_Restrictions(false, 80, false);
2✔
1178

1179
         const auto valid_time = Botan::calendar_point(2020, 1, 20, 0, 0, 0).to_std_timepoint();
1✔
1180

1181
         const auto path_result1 = Botan::x509_path_validate(std::vector<Botan::X509_Certificate>{ee_crt, fake_ca_crt},
4✔
1182
                                                             restrictions,
1183
                                                             trusted,
1184
                                                             "",
1185
                                                             Botan::Usage_Type::UNSPECIFIED,
1186
                                                             valid_time,
1187
                                                             std::chrono::milliseconds(0),
1✔
1188
                                                             {});
3✔
1189

1190
         result.confirm("Validation failed", !path_result1.successful_validation());
2✔
1191

1192
         result.confirm("Expected status",
2✔
1193
                        path_result1.result() == Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
1✔
1194

1195
         const auto path_result2 = Botan::x509_path_validate(std::vector<Botan::X509_Certificate>{ee_crt},
3✔
1196
                                                             restrictions,
1197
                                                             trusted,
1198
                                                             "",
1199
                                                             Botan::Usage_Type::UNSPECIFIED,
1200
                                                             valid_time,
1201
                                                             std::chrono::milliseconds(0),
1✔
1202
                                                             {});
3✔
1203

1204
         result.confirm("Validation failed", !path_result2.successful_validation());
2✔
1205

1206
         result.confirm("Expected status",
2✔
1207
                        path_result2.result() == Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
1✔
1208

1209
         // Verify the signature from the bad CA is actually correct
1210
         Botan::Certificate_Store_In_Memory frusted;
1✔
1211
         frusted.add_certificate(fake_ca_crt);
1✔
1212

1213
         const auto path_result3 = Botan::x509_path_validate(std::vector<Botan::X509_Certificate>{ee_crt},
3✔
1214
                                                             restrictions,
1215
                                                             frusted,
1216
                                                             "",
1217
                                                             Botan::Usage_Type::UNSPECIFIED,
1218
                                                             valid_time,
1219
                                                             std::chrono::milliseconds(0),
1✔
1220
                                                             {});
3✔
1221

1222
         result.confirm("Validation succeeded", path_result3.successful_validation());
2✔
1223

1224
         return {result};
3✔
1225
      }
1✔
1226
};
1227

1228
BOTAN_REGISTER_TEST("x509", "x509_cve_2020_0601", CVE_2020_0601_Tests);
1229

1230
   #endif
1231

1232
   #if defined(BOTAN_HAS_XMSS_RFC8391)
1233

1234
class XMSS_Path_Validation_Tests final : public Test {
×
1235
   public:
1236
      static Test::Result validate_self_signed(const std::string& name, const std::string& file) {
2✔
1237
         Test::Result result(name);
2✔
1238

1239
         Botan::Path_Validation_Restrictions restrictions;
4✔
1240
         auto self_signed = Botan::X509_Certificate(Test::data_dir() + "/x509/xmss/" + file);
4✔
1241

1242
         auto cert_path = std::vector<Botan::X509_Certificate>{self_signed};
4✔
1243
         auto valid_time = Botan::calendar_point(2019, 10, 8, 4, 45, 0).to_std_timepoint();
2✔
1244

1245
         auto status = Botan::PKIX::overall_status(
2✔
1246
            Botan::PKIX::check_chain(cert_path, valid_time, "", Botan::Usage_Type::UNSPECIFIED, restrictions));
4✔
1247
         result.test_eq("Cert validation status", Botan::to_string(status), "Verified");
2✔
1248
         return result;
2✔
1249
      }
2✔
1250

1251
      std::vector<Test::Result> run() override {
1✔
1252
         if(Botan::has_filesystem_impl() == false) {
1✔
1253
            return {Test::Result::Note("XMSS path validation", "Skipping due to missing filesystem access")};
×
1254
         }
1255

1256
         return {
1✔
1257
            validate_self_signed("XMSS path validation with certificate created by ISARA corp", "xmss_isara_root.pem"),
1258
            validate_self_signed("XMSS path validation with certificate created by BouncyCastle",
1259
                                 "xmss_bouncycastle_sha256_10_root.pem")};
7✔
1260
      }
1261
};
1262

1263
BOTAN_REGISTER_TEST("x509", "x509_path_xmss", XMSS_Path_Validation_Tests);
1264

1265
   #endif
1266

1267
#endif
1268

1269
}  // namespace
1270

1271
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc