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

randombit / botan / 19012754211

02 Nov 2025 01:10PM UTC coverage: 90.677% (+0.006%) from 90.671%
19012754211

push

github

web-flow
Merge pull request #5137 from randombit/jack/clang-tidy-includes

Remove various unused includes flagged by clang-tidy misc-include-cleaner

100457 of 110786 relevant lines covered (90.68%)

12189873.8 hits per line

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

80.37
/src/lib/x509/x509path.cpp
1
/*
2
* X.509 Certificate Path Validation
3
* (C) 2010,2011,2012,2014,2016 Jack Lloyd
4
* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/x509path.h>
10

11
#include <botan/ocsp.h>
12
#include <botan/pk_keys.h>
13
#include <botan/x509_ext.h>
14
#include <botan/internal/stl_util.h>
15
#include <algorithm>
16
#include <chrono>
17
#include <set>
18
#include <sstream>
19
#include <string>
20
#include <vector>
21

22
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
23
   #include <botan/internal/http_util.h>
24
   #include <future>
25
#endif
26

27
namespace Botan {
28

29
/*
30
* PKIX path validation
31
*/
32
CertificatePathStatusCodes PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
1,490✔
33
                                             std::chrono::system_clock::time_point ref_time,
34
                                             std::string_view hostname,
35
                                             Usage_Type usage,
36
                                             const Path_Validation_Restrictions& restrictions) {
37
   if(cert_path.empty()) {
1,490✔
38
      throw Invalid_Argument("PKIX::check_chain cert_path empty");
×
39
   }
40

41
   const bool self_signed_ee_cert = (cert_path.size() == 1);
1,490✔
42

43
   X509_Time validation_time(ref_time);
1,490✔
44

45
   CertificatePathStatusCodes cert_status(cert_path.size());
1,490✔
46

47
   // Before anything else verify the entire chain of signatures
48
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,614✔
49
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
4,124✔
50

51
      const bool at_self_signed_root = (i == cert_path.size() - 1);
4,124✔
52

53
      const X509_Certificate& subject = cert_path[i];
4,124✔
54
      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
4,124✔
55

56
      // Check the signature algorithm is known
57
      if(!subject.signature_algorithm().oid().registered_oid()) {
4,124✔
58
         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
16✔
59
      } else {
60
         std::unique_ptr<Public_Key> issuer_key;
4,108✔
61
         try {
4,108✔
62
            issuer_key = issuer.subject_public_key();
4,108✔
63
         } catch(...) {
×
64
            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
×
65
         }
×
66

67
         if(issuer_key) {
4,108✔
68
            if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
4,108✔
69
               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
65✔
70
            }
71

72
            const auto sig_status = subject.verify_signature(*issuer_key);
4,108✔
73

74
            if(sig_status.first != Certificate_Status_Code::VERIFIED) {
4,108✔
75
               status.insert(sig_status.first);
57✔
76
            } else {
77
               // Signature is valid, check if hash used was acceptable
78
               const std::string hash_used_for_signature = sig_status.second;
4,051✔
79
               BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
4,051✔
80
               const auto& trusted_hashes = restrictions.trusted_hashes();
4,051✔
81

82
               // Ignore untrusted hashes on self-signed roots
83
               if(!trusted_hashes.empty() && !at_self_signed_root) {
4,051✔
84
                  if(!trusted_hashes.contains(hash_used_for_signature)) {
2,561✔
85
                     status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
68✔
86
                  }
87
               }
88
            }
4,051✔
89
         }
4,108✔
90
      }
4,124✔
91
   }
92

93
   // If any of the signatures were invalid, return immediately; we know the
94
   // chain is invalid and signature failure is always considered the most
95
   // critical result. This does mean other problems in the certificate (eg
96
   // expired) will not be reported, but we'd have to assume any such data is
97
   // anyway arbitrary considering we couldn't verify the signature chain
98

99
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,419✔
100
      for(auto status : cert_status.at(i)) {
4,134✔
101
         // This ignores errors relating to the key or hash being weak since
102
         // these are somewhat advisory
103
         if(static_cast<uint32_t>(status) >= 5000) {
205✔
104
            return cert_status;
1,490✔
105
         }
106
      }
107
   }
108

109
   if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
1,417✔
110
      cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
14✔
111
   }
112

113
   if(!cert_path[0].allowed_usage(usage)) {
1,417✔
114
      if(usage == Usage_Type::OCSP_RESPONDER) {
3✔
115
         cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
1✔
116
      }
117
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
3✔
118
   }
119

120
   if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
1,417✔
121
      /*
122
      "If the keyCertSign bit is asserted, then the cA bit in the
123
      basic constraints extension (Section 4.2.1.9) MUST also be
124
      asserted." - RFC 5280
125

126
      We don't bother doing this check on the rest of the path since they
127
      must have the cA bit asserted or the validation will fail anyway.
128
      */
129
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
×
130
   }
131

132
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,328✔
133
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
3,911✔
134

135
      const bool at_self_signed_root = (i == cert_path.size() - 1);
3,911✔
136

137
      const X509_Certificate& subject = cert_path[i];
3,911✔
138
      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
3,911✔
139

140
      if(at_self_signed_root && (issuer.is_self_signed() == false)) {
3,911✔
141
         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
×
142
      }
143

144
      // This should never happen; it indicates a bug in path building
145
      if(subject.issuer_dn() != issuer.subject_dn()) {
3,911✔
146
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
×
147
      }
148

149
      // Check the serial number
150
      if(subject.is_serial_negative()) {
3,911✔
151
         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
32✔
152
      }
153

154
      // Check the subject's DN components' length
155

156
      for(const auto& dn_pair : subject.subject_dn().dn_info()) {
15,318✔
157
         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
11,407✔
158
         // dn_pair = <OID,str>
159
         if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
11,407✔
160
            status.insert(Certificate_Status_Code::DN_TOO_LONG);
24✔
161
         }
162
      }
163

164
      // Only warn, if trusted root is not in time range if configured this way
165
      const bool is_trusted_root_and_time_ignored =
3,911✔
166
         restrictions.ignore_trusted_root_time_range() && at_self_signed_root;
3,911✔
167
      // Check all certs for valid time range
168
      if(validation_time < subject.not_before()) {
3,911✔
169
         if(is_trusted_root_and_time_ignored) {
47✔
170
            status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID);  // only warn
2✔
171
         } else {
172
            status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
45✔
173
         }
174
      }
175

176
      if(validation_time > subject.not_after()) {
3,911✔
177
         if(is_trusted_root_and_time_ignored) {
264✔
178
            status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED);  // only warn
2✔
179
         } else {
180
            status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
262✔
181
         }
182
      }
183

184
      // Check issuer constraints
185
      if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
3,911✔
186
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
59✔
187
      }
188

189
      // Check cert extensions
190

191
      if(subject.x509_version() == 1) {
3,911✔
192
         if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
20✔
193
            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
1✔
194
         }
195
      }
196

197
      const Extensions& extensions = subject.v3_extensions();
3,911✔
198
      const auto& extensions_vec = extensions.extensions();
3,911✔
199
      if(subject.x509_version() < 3 && !extensions_vec.empty()) {
3,911✔
200
         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
32✔
201
      }
202
      for(const auto& extension : extensions_vec) {
21,021✔
203
         extension.first->validate(subject, issuer, cert_path, cert_status, i);
34,220✔
204
      }
205
      if(extensions_vec.size() != extensions.get_extension_oids().size()) {
3,911✔
206
         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
16✔
207
      }
208
   }
3,911✔
209

210
   // path len check
211
   size_t max_path_length = cert_path.size();
1,417✔
212
   for(size_t i = cert_path.size() - 1; i > 0; --i) {
3,911✔
213
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
2,494✔
214
      const X509_Certificate& subject = cert_path[i];
2,494✔
215

216
      /*
217
      * If the certificate was not self-issued, verify that max_path_length is
218
      * greater than zero and decrement max_path_length by 1.
219
      */
220
      if(subject.subject_dn() != subject.issuer_dn()) {
2,494✔
221
         if(max_path_length > 0) {
974✔
222
            max_path_length -= 1;
935✔
223
         } else {
224
            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
39✔
225
         }
226
      }
227

228
      /*
229
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
230
      * set max_path_length to the value of pathLenConstraint.
231
      */
232
      if(auto path_len_constraint = subject.path_length_constraint()) {
2,494✔
233
         max_path_length = std::min(max_path_length, *path_len_constraint);
3,267✔
234
      }
235
   }
236

237
   return cert_status;
238
}
1,490✔
239

240
namespace {
241

242
Certificate_Status_Code verify_ocsp_signing_cert(const X509_Certificate& signing_cert,
41✔
243
                                                 const X509_Certificate& ca,
244
                                                 const std::vector<X509_Certificate>& extra_certs,
245
                                                 const std::vector<Certificate_Store*>& certstores,
246
                                                 std::chrono::system_clock::time_point ref_time,
247
                                                 const Path_Validation_Restrictions& restrictions) {
248
   // RFC 6960 4.2.2.2
249
   //    [Applications] MUST reject the response if the certificate
250
   //    required to validate the signature on the response does not
251
   //    meet at least one of the following criteria:
252
   //
253
   //    1. Matches a local configuration of OCSP signing authority
254
   //       for the certificate in question, or
255
   if(restrictions.trusted_ocsp_responders()->certificate_known(signing_cert)) {
41✔
256
      return Certificate_Status_Code::OK;
257
   }
258

259
   // RFC 6960 4.2.2.2
260
   //
261
   //    2. Is the certificate of the CA that issued the certificate
262
   //       in question, or
263
   if(signing_cert == ca) {
41✔
264
      return Certificate_Status_Code::OK;
265
   }
266

267
   // RFC 6960 4.2.2.2
268
   //
269
   //    3. Includes a value of id-kp-OCSPSigning in an extended key
270
   //       usage extension and is issued by the CA that issued the
271
   //       certificate in question as stated above.
272

273
   // TODO: Implement OCSP revocation check of OCSP signer certificate
274
   // Note: This needs special care to prevent endless loops on specifically
275
   //       forged chains of OCSP responses referring to each other.
276
   //
277
   // Currently, we're disabling OCSP-based revocation checks by setting the
278
   // timeout to 0. Additionally, the library's API would not allow an
279
   // application to pass in the required "second order" OCSP responses. I.e.
280
   // "second order" OCSP checks would need to rely on `check_ocsp_online()`
281
   // which is not an option for some applications (e.g. that require a proxy
282
   // for external HTTP requests).
283
   const auto ocsp_timeout = std::chrono::milliseconds::zero();
23✔
284
   const auto relaxed_restrictions =
23✔
285
      Path_Validation_Restrictions(false /* do not enforce revocation data */,
286
                                   restrictions.minimum_key_strength(),
287
                                   false /* OCSP is not available, so don't try for intermediates */,
288
                                   restrictions.trusted_hashes());
23✔
289

290
   const auto validation_result = x509_path_validate(concat(std::vector{signing_cert}, extra_certs),
69✔
291
                                                     relaxed_restrictions,
292
                                                     certstores,
293
                                                     {} /* hostname */,
294
                                                     Botan::Usage_Type::OCSP_RESPONDER,
295
                                                     ref_time,
296
                                                     ocsp_timeout);
46✔
297

298
   return validation_result.result();
23✔
299
}
46✔
300

301
}  // namespace
302

303
CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
54✔
304
                                            const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
305
                                            const std::vector<Certificate_Store*>& certstores,
306
                                            std::chrono::system_clock::time_point ref_time,
307
                                            const Path_Validation_Restrictions& restrictions) {
308
   if(cert_path.empty()) {
54✔
309
      throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
×
310
   }
311

312
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
54✔
313

314
   for(size_t i = 0; i != cert_path.size() - 1; ++i) {
148✔
315
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
94✔
316

317
      const X509_Certificate& subject = cert_path.at(i);
94✔
318
      const X509_Certificate& ca = cert_path.at(i + 1);
94✔
319

320
      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
94✔
321
         (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
51✔
322
         try {
51✔
323
            const auto& ocsp_response = ocsp_responses.at(i);
51✔
324

325
            if(auto dummy_status = ocsp_response->dummy_status()) {
51✔
326
               // handle softfail conditions
327
               status.insert(dummy_status.value());
9✔
328
            } else if(auto signing_cert =
42✔
329
                         ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
42✔
330
                      !signing_cert) {
42✔
331
               status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
1✔
332
            } else if(auto ocsp_signing_cert_status =
41✔
333
                         verify_ocsp_signing_cert(signing_cert.value(),
41✔
334
                                                  ca,
335
                                                  concat(ocsp_response->certificates(), cert_path),
41✔
336
                                                  certstores,
337
                                                  ref_time,
338
                                                  restrictions);
41✔
339
                      ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
41✔
340
               status.insert(ocsp_signing_cert_status);
4✔
341
               status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
4✔
342
            } else {
343
               status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
37✔
344
            }
42✔
345
         } catch(Exception&) {
×
346
            status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
×
347
         }
×
348
      }
349
   }
350

351
   while(!cert_status.empty() && cert_status.back().empty()) {
97✔
352
      cert_status.pop_back();
43✔
353
   }
354

355
   return cert_status;
54✔
356
}
×
357

358
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
1,494✔
359
                                           const std::vector<std::optional<X509_CRL>>& crls,
360
                                           std::chrono::system_clock::time_point ref_time) {
361
   if(cert_path.empty()) {
1,494✔
362
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
363
   }
364

365
   CertificatePathStatusCodes cert_status(cert_path.size());
1,494✔
366
   const X509_Time validation_time(ref_time);
1,494✔
367

368
   for(size_t i = 0; i != cert_path.size() - 1; ++i) {
4,134✔
369
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
2,640✔
370

371
      if(i < crls.size() && crls[i].has_value()) {
2,640✔
372
         const X509_Certificate& subject = cert_path.at(i);
771✔
373
         const X509_Certificate& ca = cert_path.at(i + 1);
771✔
374

375
         if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
771✔
376
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
2✔
377
         }
378

379
         if(validation_time < crls[i]->this_update()) {
771✔
380
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
16✔
381
         }
382

383
         if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
771✔
384
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
18✔
385
         }
386

387
         auto ca_key = ca.subject_public_key();
771✔
388
         if(crls[i]->check_signature(*ca_key) == false) {
771✔
389
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
18✔
390
         }
391

392
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
771✔
393

394
         if(crls[i]->is_revoked(subject)) {
771✔
395
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
155✔
396
         }
397

398
         const auto dp = subject.crl_distribution_points();
771✔
399
         if(!dp.empty()) {
771✔
400
            const auto crl_idp = crls[i]->crl_issuing_distribution_point();
208✔
401

402
            if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
208✔
403
               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
208✔
404
            }
405
         }
208✔
406

407
         for(const auto& extension : crls[i]->extensions().extensions()) {
2,345✔
408
            // XXX this is wrong - the OID might be defined but the extension not full parsed
409
            // for example see #1652
410

411
            // is the extension critical and unknown?
412
            if(extension.second && !extension.first->oid_of().registered_oid()) {
1,593✔
413
               /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
414
                * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
415
                */
416
               status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
19✔
417
            }
418
         }
771✔
419
      }
1,542✔
420
   }
421

422
   while(!cert_status.empty() && cert_status.back().empty()) {
4,837✔
423
      cert_status.pop_back();
3,343✔
424
   }
425

426
   return cert_status;
1,494✔
427
}
1,494✔
428

429
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
1,488✔
430
                                           const std::vector<Certificate_Store*>& certstores,
431
                                           std::chrono::system_clock::time_point ref_time) {
432
   if(cert_path.empty()) {
1,488✔
433
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
434
   }
435

436
   if(certstores.empty()) {
1,488✔
437
      throw Invalid_Argument("PKIX::check_crl certstores empty");
×
438
   }
439

440
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
1,488✔
441

442
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,610✔
443
      for(auto* certstore : certstores) {
7,071✔
444
         crls[i] = certstore->find_crl_for(cert_path[i]);
4,150✔
445
         if(crls[i]) {
4,150✔
446
            break;
447
         }
448
      }
449
   }
450

451
   return PKIX::check_crl(cert_path, crls, ref_time);
2,976✔
452
}
1,488✔
453

454
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
455

456
CertificatePathStatusCodes PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
9✔
457
                                                   const std::vector<Certificate_Store*>& trusted_certstores,
458
                                                   std::chrono::system_clock::time_point ref_time,
459
                                                   std::chrono::milliseconds timeout,
460
                                                   const Path_Validation_Restrictions& restrictions) {
461
   if(cert_path.empty()) {
9✔
462
      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
×
463
   }
464

465
   std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
9✔
466

467
   size_t to_ocsp = 1;
9✔
468

469
   if(restrictions.ocsp_all_intermediates()) {
9✔
470
      to_ocsp = cert_path.size() - 1;
×
471
   }
472
   if(cert_path.size() == 1) {
9✔
473
      to_ocsp = 0;
×
474
   }
475

476
   for(size_t i = 0; i < to_ocsp; ++i) {
18✔
477
      const X509_Certificate& subject = cert_path.at(i);
9✔
478
      const X509_Certificate& issuer = cert_path.at(i + 1);
9✔
479

480
      if(subject.ocsp_responder().empty()) {
9✔
481
         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
16✔
482
            return OCSP::Response(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
16✔
483
         }));
484
      } else {
485
         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
2✔
486
            OCSP::Request req(issuer, BigInt::from_bytes(subject.serial_number()));
1✔
487

488
            HTTP::Response http;
1✔
489
            try {
1✔
490
               http = HTTP::POST_sync(subject.ocsp_responder(),
2✔
491
                                      "application/ocsp-request",
492
                                      req.BER_encode(),
2✔
493
                                      /*redirects*/ 1,
494
                                      timeout);
1✔
495
            } catch(std::exception&) {
×
496
               // log e.what() ?
497
            }
×
498
            if(http.status_code() != 200) {
1✔
499
               return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
×
500
            }
501
            // Check the MIME type?
502

503
            return OCSP::Response(http.body());
1✔
504
         }));
2✔
505
      }
506
   }
507

508
   std::vector<std::optional<OCSP::Response>> ocsp_responses;
9✔
509
   ocsp_responses.reserve(ocsp_response_futures.size());
9✔
510

511
   for(auto& ocsp_response_future : ocsp_response_futures) {
18✔
512
      ocsp_responses.push_back(ocsp_response_future.get());
27✔
513
   }
514

515
   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
18✔
516
}
9✔
517

518
CertificatePathStatusCodes PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
×
519
                                                  const std::vector<Certificate_Store*>& certstores,
520
                                                  Certificate_Store_In_Memory* crl_store,
521
                                                  std::chrono::system_clock::time_point ref_time,
522
                                                  std::chrono::milliseconds timeout) {
523
   if(cert_path.empty()) {
×
524
      throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
×
525
   }
526
   if(certstores.empty()) {
×
527
      throw Invalid_Argument("PKIX::check_crl_online certstores empty");
×
528
   }
529

530
   std::vector<std::future<std::optional<X509_CRL>>> future_crls;
×
531
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
×
532

533
   for(size_t i = 0; i != cert_path.size(); ++i) {
×
534
      const std::optional<X509_Certificate>& cert = cert_path.at(i);
×
535
      for(auto* certstore : certstores) {
×
536
         crls[i] = certstore->find_crl_for(*cert);
×
537
         if(crls[i].has_value()) {
×
538
            break;
539
         }
540
      }
541

542
      // TODO: check if CRL is expired and re-request?
543

544
      // Only request if we don't already have a CRL
545
      if(crls[i]) {
×
546
         /*
547
         We already have a CRL, so just insert this empty one to hold a place in the vector
548
         so that indexes match up
549
         */
550
         future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
×
551
      } else if(cert->crl_distribution_point().empty()) {
×
552
         // Avoid creating a thread for this case
553
         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
×
554
            throw Not_Implemented("No CRL distribution point for this certificate");
×
555
         }));
556
      } else {
557
         future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
×
558
            auto http = HTTP::GET_sync(cert->crl_distribution_point(),
×
559
                                       /*redirects*/ 1,
560
                                       timeout);
×
561

562
            http.throw_unless_ok();
×
563
            // check the mime type?
564
            return X509_CRL(http.body());
×
565
         }));
×
566
      }
567
   }
×
568

569
   for(size_t i = 0; i != future_crls.size(); ++i) {
×
570
      if(future_crls[i].valid()) {
×
571
         try {
×
572
            crls[i] = future_crls[i].get();
×
573
         } catch(std::exception&) {
×
574
            // crls[i] left null
575
            // todo: log exception e.what() ?
576
         }
×
577
      }
578
   }
579

580
   auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
×
581

582
   if(crl_store != nullptr) {
×
583
      for(size_t i = 0; i != crl_status.size(); ++i) {
×
584
         if(crl_status[i].contains(Certificate_Status_Code::VALID_CRL_CHECKED)) {
×
585
            // better be non-null, we supposedly validated it
586
            BOTAN_ASSERT_NOMSG(crls[i].has_value());
×
587
            crl_store->add_crl(*crls[i]);
×
588
         }
589
      }
590
   }
591

592
   return crl_status;
×
593
}
×
594

595
#endif
596

597
Certificate_Status_Code PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
×
598
                                                     const std::vector<Certificate_Store*>& trusted_certstores,
599
                                                     const X509_Certificate& end_entity,
600
                                                     const std::vector<X509_Certificate>& end_entity_extra) {
601
   if(end_entity.is_self_signed()) {
×
602
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
603
   }
604

605
   /*
606
   * This is an inelegant but functional way of preventing path loops
607
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
608
   * fingerprints in the path. If there is a duplicate, we error out.
609
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
610
   */
611
   std::set<std::string> certs_seen;
×
612

613
   cert_path.push_back(end_entity);
×
614
   certs_seen.insert(end_entity.fingerprint("SHA-256"));
×
615

616
   Certificate_Store_In_Memory ee_extras;
×
617
   for(const auto& cert : end_entity_extra) {
×
618
      ee_extras.add_certificate(cert);
×
619
   }
620

621
   // iterate until we reach a root or cannot find the issuer
622
   for(;;) {
×
623
      const X509_Certificate& last = cert_path.back();
×
624
      const X509_DN issuer_dn = last.issuer_dn();
×
625
      const std::vector<uint8_t> auth_key_id = last.authority_key_id();
×
626

627
      std::optional<X509_Certificate> issuer;
×
628
      bool trusted_issuer = false;
×
629

630
      for(Certificate_Store* store : trusted_certstores) {
×
631
         issuer = store->find_cert(issuer_dn, auth_key_id);
×
632
         if(issuer) {
×
633
            trusted_issuer = true;
634
            break;
635
         }
636
      }
637

638
      if(!issuer) {
×
639
         // fall back to searching supplemental certs
640
         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
×
641
      }
642

643
      if(!issuer) {
×
644
         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
645
      }
646

647
      const std::string fprint = issuer->fingerprint("SHA-256");
×
648

649
      if(certs_seen.contains(fprint)) {
×
650
         // we already saw this certificate -> loop
651
         return Certificate_Status_Code::CERT_CHAIN_LOOP;
652
      }
653

654
      certs_seen.insert(fprint);
×
655
      cert_path.push_back(*issuer);
×
656

657
      if(issuer->is_self_signed()) {
×
658
         if(trusted_issuer) {
×
659
            return Certificate_Status_Code::OK;
660
         } else {
661
            return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
×
662
         }
663
      }
664
   }
×
665
}
×
666

667
/**
668
 * utilities for PKIX::build_all_certificate_paths
669
 */
670
namespace {
671
// <certificate, trusted?>
672
using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>, bool>;
673
}  // namespace
674

675
/**
676
 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
677
 *
678
 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
679
 * one of the encountered errors is returned arbitrarily.
680
 *
681
 * todo add a path building function that returns detailed information on errors encountered while building
682
 * the potentially numerous path candidates.
683
 *
684
 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
685
 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
686
 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
687
 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
688
 * authority key id need not be unique among the certificates used for building the path. In such a case,
689
 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
690
 *
691
 */
692
Certificate_Status_Code PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
1,933✔
693
                                                          const std::vector<Certificate_Store*>& trusted_certstores,
694
                                                          const std::optional<X509_Certificate>& end_entity,
695
                                                          const std::vector<X509_Certificate>& end_entity_extra) {
696
   if(!cert_paths_out.empty()) {
1,933✔
697
      throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
×
698
   }
699

700
   if(end_entity->is_self_signed()) {
1,933✔
701
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
702
   }
703

704
   /*
705
    * Pile up error messages
706
    */
707
   std::vector<Certificate_Status_Code> stats;
1,736✔
708

709
   Certificate_Store_In_Memory ee_extras;
1,736✔
710
   for(const auto& cert : end_entity_extra) {
3,156✔
711
      ee_extras.add_certificate(cert);
1,420✔
712
   }
713

714
   /*
715
   * This is an inelegant but functional way of preventing path loops
716
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
717
   * fingerprints in the path. If there is a duplicate, we error out.
718
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
719
   */
720
   std::set<std::string> certs_seen;
1,736✔
721

722
   // new certs are added and removed from the path during the DFS
723
   // it is copied into cert_paths_out when we encounter a trusted root
724
   std::vector<X509_Certificate> path_so_far;
1,736✔
725

726
   // todo can we assume that the end certificate is not trusted?
727
   std::vector<cert_maybe_trusted> stack = {{end_entity, false}};
3,472✔
728

729
   while(!stack.empty()) {
9,441✔
730
      std::optional<X509_Certificate> last = stack.back().first;
7,705✔
731
      // found a deletion marker that guides the DFS, backtracking
732
      if(last == std::nullopt) {
7,705✔
733
         stack.pop_back();
2,884✔
734
         std::string fprint = path_so_far.back().fingerprint("SHA-256");
2,884✔
735
         certs_seen.erase(fprint);
2,884✔
736
         path_so_far.pop_back();
2,884✔
737
      }
2,884✔
738
      // process next cert on the path
739
      else {
740
         const bool trusted = stack.back().second;
4,821✔
741
         stack.pop_back();
4,821✔
742

743
         // certificate already seen?
744
         const std::string fprint = last->fingerprint("SHA-256");
4,821✔
745
         if(certs_seen.count(fprint) == 1) {
4,821✔
746
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
1✔
747
            // the current path ended in a loop
748
            continue;
1✔
749
         }
750

751
         // the current path ends here
752
         if(last->is_self_signed()) {
4,820✔
753
            // found a trust anchor
754
            if(trusted) {
1,692✔
755
               cert_paths_out.push_back(path_so_far);
1,491✔
756
               cert_paths_out.back().push_back(*last);
1,491✔
757

758
               continue;
1,491✔
759
            }
760
            // found an untrustworthy root
761
            else {
762
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
201✔
763
               continue;
201✔
764
            }
765
         }
766

767
         const X509_DN issuer_dn = last->issuer_dn();
3,128✔
768
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
3,128✔
769

770
         // search for trusted issuers
771
         std::vector<X509_Certificate> trusted_issuers;
3,128✔
772
         for(Certificate_Store* store : trusted_certstores) {
6,138✔
773
            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
3,010✔
774
            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
3,010✔
775
         }
3,010✔
776

777
         // search the supplemental certs
778
         std::vector<X509_Certificate> misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id);
3,128✔
779

780
         // if we could not find any issuers, the current path ends here
781
         if(trusted_issuers.empty() && misc_issuers.empty()) {
3,128✔
782
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
244✔
783
            continue;
244✔
784
         }
785

786
         // push the latest certificate onto the path_so_far
787
         path_so_far.push_back(*last);
2,884✔
788
         certs_seen.emplace(fprint);
2,884✔
789

790
         // push a deletion marker on the stack for backtracking later
791
         stack.push_back({std::optional<X509_Certificate>(), false});
2,884✔
792

793
         for(const auto& trusted_cert : trusted_issuers) {
4,638✔
794
            stack.push_back({trusted_cert, true});
3,508✔
795
         }
796

797
         for(const auto& misc : misc_issuers) {
4,215✔
798
            stack.push_back({misc, false});
2,662✔
799
         }
800
      }
7,916✔
801
   }
7,705✔
802

803
   // could not construct any potentially valid path
804
   if(cert_paths_out.empty()) {
1,736✔
805
      if(stats.empty()) {
248✔
806
         throw Internal_Error("X509 path building failed for unknown reasons");
×
807
      } else {
808
         // arbitrarily return the first error
809
         return stats[0];
248✔
810
      }
811
   } else {
812
      return Certificate_Status_Code::OK;
813
   }
814
}
5,208✔
815

816
void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
1,488✔
817
                                   const CertificatePathStatusCodes& crl_status,
818
                                   const CertificatePathStatusCodes& ocsp_status,
819
                                   const Path_Validation_Restrictions& restrictions) {
820
   if(chain_status.empty()) {
1,488✔
821
      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
×
822
   }
823

824
   for(size_t i = 0; i != chain_status.size() - 1; ++i) {
4,122✔
825
      bool had_crl = false;
2,634✔
826
      bool had_ocsp = false;
2,634✔
827

828
      if(i < crl_status.size() && !crl_status[i].empty()) {
2,634✔
829
         for(auto&& code : crl_status[i]) {
1,963✔
830
            if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
1,198✔
831
               had_crl = true;
765✔
832
            }
833
            chain_status[i].insert(code);
1,198✔
834
         }
835
      }
836

837
      if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
2,634✔
838
         for(auto&& code : ocsp_status[i]) {
74✔
839
            // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
840
            if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
39✔
841
               code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||
20✔
842
               code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) {
843
               had_ocsp = true;
19✔
844
            }
845

846
            chain_status[i].insert(code);
39✔
847
         }
848
      }
849

850
      if(had_crl == false && had_ocsp == false) {
2,634✔
851
         if((restrictions.require_revocation_information() && i == 0) ||
1,850✔
852
            (restrictions.ocsp_all_intermediates() && i > 0)) {
1,809✔
853
            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
76✔
854
         }
855
      }
856
   }
857
}
1,488✔
858

859
Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status) {
1,496✔
860
   if(cert_status.empty()) {
1,496✔
861
      throw Invalid_Argument("PKIX::overall_status empty cert status");
×
862
   }
863

864
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
865

866
   // take the "worst" error as overall
867
   for(const std::set<Certificate_Status_Code>& s : cert_status) {
5,626✔
868
      if(!s.empty()) {
4,130✔
869
         auto worst = *s.rbegin();
1,673✔
870
         // Leave informative OCSP/CRL confirmations on cert-level status only
871
         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
1,673✔
872
            overall_status = worst;
4,130✔
873
         }
874
      }
875
   }
876
   return overall_status;
1,496✔
877
}
878

879
Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
1,933✔
880
                                          const Path_Validation_Restrictions& restrictions,
881
                                          const std::vector<Certificate_Store*>& trusted_roots,
882
                                          std::string_view hostname,
883
                                          Usage_Type usage,
884
                                          std::chrono::system_clock::time_point ref_time,
885
                                          std::chrono::milliseconds ocsp_timeout,
886
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
887
   if(end_certs.empty()) {
1,933✔
888
      throw Invalid_Argument("x509_path_validate called with no subjects");
×
889
   }
890

891
   X509_Certificate end_entity = end_certs[0];
1,933✔
892
   std::vector<X509_Certificate> end_entity_extra;
1,933✔
893
   for(size_t i = 1; i < end_certs.size(); ++i) {
3,353✔
894
      end_entity_extra.push_back(end_certs[i]);
1,420✔
895
   }
896

897
   std::vector<std::vector<X509_Certificate>> cert_paths;
1,933✔
898
   Certificate_Status_Code path_building_result =
1,933✔
899
      PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
1,933✔
900

901
   // If we cannot successfully build a chain to a trusted self-signed root, stop now
902
   if(path_building_result != Certificate_Status_Code::OK) {
1,933✔
903
      return Path_Validation_Result(path_building_result);
445✔
904
   }
905

906
   std::vector<Path_Validation_Result> error_results;
1,488✔
907
   // Try validating all the potentially valid paths and return the first one to validate properly
908
   for(auto cert_path : cert_paths) {
2,449✔
909
      CertificatePathStatusCodes status = PKIX::check_chain(cert_path, ref_time, hostname, usage, restrictions);
1,488✔
910

911
      CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, trusted_roots, ref_time);
1,488✔
912

913
      CertificatePathStatusCodes ocsp_status;
1,488✔
914

915
      if(!ocsp_resp.empty()) {
1,488✔
916
         ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
30✔
917
      }
918

919
      if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) {
1,488✔
920
#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
921
         ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions);
8✔
922
#else
923
         ocsp_status.resize(1);
924
         ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
925
#endif
926
      }
927

928
      PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions);
1,488✔
929

930
      Path_Validation_Result pvd(status, std::move(cert_path));
1,488✔
931
      if(pvd.successful_validation()) {
1,488✔
932
         return pvd;
527✔
933
      } else {
934
         error_results.push_back(std::move(pvd));
961✔
935
      }
936
   }
1,488✔
937
   return error_results[0];
961✔
938
}
3,398✔
939

940
Path_Validation_Result x509_path_validate(const X509_Certificate& end_cert,
276✔
941
                                          const Path_Validation_Restrictions& restrictions,
942
                                          const std::vector<Certificate_Store*>& trusted_roots,
943
                                          std::string_view hostname,
944
                                          Usage_Type usage,
945
                                          std::chrono::system_clock::time_point when,
946
                                          std::chrono::milliseconds ocsp_timeout,
947
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
948
   std::vector<X509_Certificate> certs;
276✔
949
   certs.push_back(end_cert);
276✔
950
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
552✔
951
}
276✔
952

953
Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
954✔
954
                                          const Path_Validation_Restrictions& restrictions,
955
                                          const Certificate_Store& store,
956
                                          std::string_view hostname,
957
                                          Usage_Type usage,
958
                                          std::chrono::system_clock::time_point when,
959
                                          std::chrono::milliseconds ocsp_timeout,
960
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
961
   std::vector<Certificate_Store*> trusted_roots;
954✔
962
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
954✔
963

964
   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1,908✔
965
}
954✔
966

967
Path_Validation_Result x509_path_validate(const X509_Certificate& end_cert,
290✔
968
                                          const Path_Validation_Restrictions& restrictions,
969
                                          const Certificate_Store& store,
970
                                          std::string_view hostname,
971
                                          Usage_Type usage,
972
                                          std::chrono::system_clock::time_point when,
973
                                          std::chrono::milliseconds ocsp_timeout,
974
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
975
   std::vector<X509_Certificate> certs;
290✔
976
   certs.push_back(end_cert);
290✔
977

978
   std::vector<Certificate_Store*> trusted_roots;
290✔
979
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
290✔
980

981
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
580✔
982
}
290✔
983

984
Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
1,039✔
985
                                                           size_t key_strength,
986
                                                           bool ocsp_intermediates,
987
                                                           std::chrono::seconds max_ocsp_age,
988
                                                           std::unique_ptr<Certificate_Store> trusted_ocsp_responders,
989
                                                           bool ignore_trusted_root_time_range) :
1,039✔
990
      m_require_revocation_information(require_rev),
1,039✔
991
      m_ocsp_all_intermediates(ocsp_intermediates),
1,039✔
992
      m_minimum_key_strength(key_strength),
1,039✔
993
      m_max_ocsp_age(max_ocsp_age),
1,039✔
994
      m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)),
1,039✔
995
      m_ignore_trusted_root_time_range(ignore_trusted_root_time_range) {
1,039✔
996
   if(key_strength <= 80) {
1,039✔
997
      m_trusted_hashes.insert("SHA-1");
854✔
998
   }
999

1000
   m_trusted_hashes.insert("SHA-224");
2,078✔
1001
   m_trusted_hashes.insert("SHA-256");
2,078✔
1002
   m_trusted_hashes.insert("SHA-384");
2,078✔
1003
   m_trusted_hashes.insert("SHA-512");
2,078✔
1004
   m_trusted_hashes.insert("SHAKE-256(512)");  // Dilithium/ML-DSA
2,078✔
1005
   m_trusted_hashes.insert("SHAKE-256(912)");  // Ed448
2,078✔
1006
}
1,039✔
1007

1008
namespace {
1009
CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses) {
1,488✔
1010
   CertificatePathStatusCodes warnings;
1,488✔
1011
   for(const auto& status_set_i : all_statuses) {
5,610✔
1012
      std::set<Certificate_Status_Code> warning_set_i;
4,122✔
1013
      for(const auto& code : status_set_i) {
6,387✔
1014
         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
2,265✔
1015
            code < Certificate_Status_Code::FIRST_ERROR_STATUS) {
1016
            warning_set_i.insert(code);
68✔
1017
         }
1018
      }
1019
      warnings.push_back(warning_set_i);
4,122✔
1020
   }
4,122✔
1021
   return warnings;
1,488✔
1022
}
×
1023
}  // namespace
1024

1025
Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
1,488✔
1026
                                               std::vector<X509_Certificate>&& cert_chain) :
1,488✔
1027
      m_all_status(std::move(status)),
1,488✔
1028
      m_warnings(find_warnings(m_all_status)),
1,488✔
1029
      m_cert_path(std::move(cert_chain)),
1,488✔
1030
      m_overall(PKIX::overall_status(m_all_status)) {}
1,488✔
1031

1032
const X509_Certificate& Path_Validation_Result::trust_root() const {
10✔
1033
   if(m_cert_path.empty()) {
10✔
1034
      throw Invalid_State("Path_Validation_Result::trust_root no path set");
×
1035
   }
1036
   if(result() != Certificate_Status_Code::VERIFIED) {
10✔
1037
      throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
×
1038
   }
1039

1040
   return m_cert_path[m_cert_path.size() - 1];
10✔
1041
}
1042

1043
bool Path_Validation_Result::successful_validation() const {
2,063✔
1044
   return (result() == Certificate_Status_Code::VERIFIED || result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
2,063✔
1045
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1,332✔
1046
}
1047

1048
bool Path_Validation_Result::no_warnings() const {
38✔
1049
   for(const auto& status_set_i : m_warnings) {
151✔
1050
      if(!status_set_i.empty()) {
114✔
1051
         return false;
38✔
1052
      }
1053
   }
1054
   return true;
1055
}
1056

1057
CertificatePathStatusCodes Path_Validation_Result::warnings() const {
42✔
1058
   return m_warnings;
42✔
1059
}
1060

1061
std::string Path_Validation_Result::result_string() const {
1,311✔
1062
   return status_string(result());
1,311✔
1063
}
1064

1065
const char* Path_Validation_Result::status_string(Certificate_Status_Code code) {
1,351✔
1066
   if(const char* s = to_string(code)) {
1,351✔
1067
      return s;
1,351✔
1068
   }
1069

1070
   return "Unknown error";
1071
}
1072

1073
std::string Path_Validation_Result::warnings_string() const {
38✔
1074
   const std::string sep(", ");
38✔
1075
   std::ostringstream oss;
38✔
1076
   for(size_t i = 0; i < m_warnings.size(); i++) {
153✔
1077
      for(auto code : m_warnings[i]) {
116✔
1078
         oss << "[" << std::to_string(i) << "] " << status_string(code) << sep;
2✔
1079
      }
1080
   }
1081

1082
   std::string res = oss.str();
38✔
1083
   // remove last sep
1084
   if(res.size() >= sep.size()) {
38✔
1085
      res = res.substr(0, res.size() - sep.size());
1✔
1086
   }
1087
   return res;
76✔
1088
}
38✔
1089
}  // namespace Botan
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

© 2026 Coveralls, Inc