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

randombit / botan / 23962911357

03 Apr 2026 09:29PM UTC coverage: 89.493% (-0.008%) from 89.501%
23962911357

push

github

web-flow
Merge pull request #5518 from randombit/jack/fix-unknown-ext-handling

Improve handling of X509 extensions which fail to parse

105491 of 117876 relevant lines covered (89.49%)

11552856.41 hits per line

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

86.47
/src/lib/x509/x509path.cpp
1
/*
2
* X.509 Certificate Path Validation
3
* (C) 2010,2011,2012,2014,2016,2026 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/assert.h>
12
#include <botan/ocsp.h>
13
#include <botan/pk_keys.h>
14
#include <botan/x509_ext.h>
15
#include <botan/internal/concat_util.h>
16
#include <algorithm>
17
#include <chrono>
18
#include <set>
19
#include <sstream>
20
#include <string>
21
#include <unordered_set>
22
#include <vector>
23

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

29
namespace Botan {
30

31
namespace {
32

33
/**
34
 * Lazy DFS iterator that yields certificate paths one at a time.
35
 *
36
 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
37
 *
38
 * Basically, a DFS is performed starting from the end certificate. A stack (vector)
39
 * serves to control the DFS. At the beginning of each iteration, a pair is popped from
40
 * the stack that contains (1) the next certificate to add to the path (2) a bool that
41
 * indicates if the certificate is part of a trusted certstore. Ideally, we follow the
42
 * unique issuer of the current certificate until a trusted root is reached. However, the
43
 * issuer DN + authority key id need not be unique among the certificates used for
44
 * building the path. In such a case, we consider all the matching issuers by pushing
45
 * <IssuerCert, trusted?> on the stack for each of them.
46
 *
47
 * Each call to next() resumes the search and returns the next discovered path, or nullopt
48
 * when the search space is exhausted.
49
*/
50
class CertificatePathBuilder {
51
   public:
52
      CertificatePathBuilder(const std::vector<Certificate_Store*>& trusted_certstores,
2,883✔
53
                             const X509_Certificate& end_entity,
54
                             const std::vector<X509_Certificate>& end_entity_extra,
55
                             bool require_self_signed = false) :
2,883✔
56
            m_trusted_certstores(trusted_certstores), m_require_self_signed(require_self_signed) {
2,883✔
57
         if(std::ranges::any_of(trusted_certstores, [](auto* ptr) { return ptr == nullptr; })) {
5,766✔
58
            throw Invalid_Argument("Certificate store list must not contain nullptr");
×
59
         }
60

61
         for(const auto& cert : end_entity_extra) {
5,734✔
62
            if(!cert_in_any_trusted_store(cert)) {
5,702✔
63
               m_ee_extras.add_certificate(cert);
2,601✔
64
            }
65
         }
66

67
         m_stack.push_back({end_entity, cert_in_any_trusted_store(end_entity)});
2,883✔
68
      }
2,883✔
69

70
      std::optional<std::vector<X509_Certificate>> next() {
4,574✔
71
         while(!m_stack.empty()) {
16,813✔
72
            auto [last, trusted] = std::move(m_stack.back());  // move before pop_back
24,094✔
73
            m_stack.pop_back();
14,662✔
74

75
            // Found a deletion marker that guides the DFS, backtracking
76
            if(!last.has_value()) {
14,662✔
77
               m_certs_seen.erase(m_path_so_far.back().tag());
5,230✔
78
               m_path_so_far.pop_back();
5,230✔
79
               continue;
5,230✔
80
            }
81

82
            // Certificate already seen in this path?
83
            const auto tag = last->tag();
9,432✔
84
            if(m_certs_seen.contains(tag)) {
9,432✔
85
               if(!m_error.has_value()) {
1,677✔
86
                  m_error = Certificate_Status_Code::CERT_CHAIN_LOOP;
1,677✔
87
               }
88
               continue;
1,677✔
89
            }
90

91
            // A valid path has been discovered. It includes endpoints that may end
92
            // with either a self-signed or a non-self-signed certificate. For
93
            // certificates that are not self-signed, additional paths could
94
            // potentially extend from the current one.
95
            if(trusted) {
7,755✔
96
               auto path = m_path_so_far;
2,570✔
97
               path.push_back(*last);
2,570✔
98
               push_issuers(*last);
2,570✔
99

100
               if(!m_require_self_signed || last->is_self_signed()) {
2,570✔
101
                  return path;
2,423✔
102
               }
103

104
               /*
105
               This unconditionally overwrites the error because it's likely the most
106
               informative error in this context - we found a path that seemed entirely
107
               suitable, except that self-signed roots are required so it was skipped.
108
               */
109
               m_error = Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
147✔
110
               continue;
147✔
111
            }
2,570✔
112

113
            if(last->is_self_signed()) {
5,185✔
114
               if(!m_error.has_value()) {
251✔
115
                  m_error = Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
250✔
116
               }
117
               continue;
251✔
118
            }
119

120
            push_issuers(*last);
4,934✔
121
         }
14,662✔
122

123
         return std::nullopt;
2,151✔
124
      }
125

126
      /**
127
      * Return the first error encountered during path building
128
      *
129
      * Only used as a last resort if there were no successful paths
130
      */
131
      Certificate_Status_Code error() const {
472✔
132
         if(m_error.has_value()) {
472✔
133
            // Confirm it is an actual error code and not accidentally OK...
134
            BOTAN_ASSERT_NOMSG(static_cast<uint32_t>(m_error.value()) >= 3000);
472✔
135
            return m_error.value();
136
         } else {
137
            return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
138
         }
139
      }
140

141
   private:
142
      bool cert_in_any_trusted_store(const X509_Certificate& cert) const {
5,734✔
143
         return std::ranges::any_of(m_trusted_certstores,
11,468✔
144
                                    [&](const Certificate_Store* store) { return store->contains(cert); });
5,425✔
145
      }
146

147
      void push_issuers(const X509_Certificate& cert) {
7,504✔
148
         const X509_DN& issuer_dn = cert.issuer_dn();
7,504✔
149
         const std::vector<uint8_t>& auth_key_id = cert.authority_key_id();
7,504✔
150

151
         // Search for trusted issuers
152
         std::vector<X509_Certificate> trusted_issuers;
7,504✔
153
         for(const Certificate_Store* store : m_trusted_certstores) {
14,926✔
154
            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
7,422✔
155
            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
7,422✔
156
         }
7,422✔
157

158
         // Search the supplemental certs
159
         const std::vector<X509_Certificate> misc_issuers = m_ee_extras.find_all_certs(issuer_dn, auth_key_id);
7,504✔
160

161
         // If we could not find any issuers, the current path ends here
162
         if(trusted_issuers.empty() && misc_issuers.empty()) {
7,504✔
163
            if(!m_error.has_value()) {
243✔
164
               m_error = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
243✔
165
            }
166
            return;
243✔
167
         }
168

169
         m_path_so_far.push_back(cert);
7,261✔
170
         m_certs_seen.emplace(cert.tag());
7,261✔
171

172
         // Push a deletion marker on the stack for backtracking later
173
         m_stack.push_back({std::nullopt, false});
7,261✔
174

175
         for(const auto& trusted_cert : trusted_issuers) {
12,216✔
176
            m_stack.push_back({trusted_cert, true});
9,910✔
177
         }
178
         for(const auto& misc : misc_issuers) {
9,599✔
179
            m_stack.push_back({misc, false});
4,676✔
180
         }
181
      }
7,504✔
182

183
      const std::vector<Certificate_Store*> m_trusted_certstores;
184
      const bool m_require_self_signed;
185
      Certificate_Store_In_Memory m_ee_extras;
186
      std::vector<std::pair<std::optional<X509_Certificate>, bool>> m_stack;
187
      std::vector<X509_Certificate> m_path_so_far;
188
      std::unordered_set<X509_Certificate::Tag, X509_Certificate::TagHash> m_certs_seen;
189
      std::optional<Certificate_Status_Code> m_error;
190
};
191

192
}  // namespace
193

194
/*
195
* PKIX path validation
196
*/
197
CertificatePathStatusCodes PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
2,404✔
198
                                             std::chrono::system_clock::time_point ref_time,
199
                                             std::string_view hostname,
200
                                             Usage_Type usage,
201
                                             const Path_Validation_Restrictions& restrictions) {
202
   if(cert_path.empty()) {
2,404✔
203
      throw Invalid_Argument("PKIX::check_chain cert_path empty");
×
204
   }
205

206
   const bool is_end_entity_trust_anchor = (cert_path.size() == 1);
2,404✔
207

208
   const X509_Time validation_time(ref_time);
2,404✔
209

210
   CertificatePathStatusCodes cert_status(cert_path.size());
2,404✔
211

212
   // Before anything else verify the entire chain of signatures
213
   for(size_t i = 0; i != cert_path.size(); ++i) {
9,341✔
214
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
6,937✔
215

216
      const bool at_trust_anchor = (i == cert_path.size() - 1);
6,937✔
217

218
      const X509_Certificate& subject = cert_path[i];
6,937✔
219

220
      // If using intermediate CAs as trust anchors, the signature of the trust
221
      // anchor cannot be verified since the issuer is not part of the
222
      // certificate chain
223
      if(!restrictions.require_self_signed_trust_anchors() && at_trust_anchor && !subject.is_self_signed()) {
6,937✔
224
         continue;
5✔
225
      }
226

227
      const X509_Certificate& issuer = cert_path[at_trust_anchor ? (i) : (i + 1)];
6,932✔
228

229
      // Check the signature algorithm is known
230
      if(!subject.signature_algorithm().oid().registered_oid()) {
6,932✔
231
         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
32✔
232
      } else {
233
         std::unique_ptr<Public_Key> issuer_key;
6,900✔
234
         try {
6,900✔
235
            issuer_key = issuer.subject_public_key();
6,900✔
236
         } catch(...) {
×
237
            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
×
238
         }
×
239

240
         if(issuer_key) {
6,900✔
241
            if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
6,900✔
242
               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
65✔
243
            }
244

245
            const auto sig_status = subject.verify_signature(*issuer_key);
6,900✔
246

247
            if(sig_status.first != Certificate_Status_Code::VERIFIED) {
6,900✔
248
               status.insert(sig_status.first);
108✔
249
            } else {
250
               // Signature is valid, check if hash used was acceptable
251
               const std::string hash_used_for_signature = sig_status.second;
6,792✔
252
               BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
6,792✔
253
               const auto& trusted_hashes = restrictions.trusted_hashes();
6,792✔
254

255
               // Ignore untrusted hashes on self-signed roots
256
               if(!trusted_hashes.empty() && !at_trust_anchor) {
6,792✔
257
                  if(!trusted_hashes.contains(hash_used_for_signature)) {
4,394✔
258
                     status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
100✔
259
                  }
260
               }
261
            }
6,792✔
262
         }
6,900✔
263
      }
6,937✔
264
   }
265

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

272
   for(size_t i = 0; i != cert_path.size(); ++i) {
8,964✔
273
      for(auto status : cert_status.at(i)) {
6,864✔
274
         // This ignores errors relating to the key or hash being weak since
275
         // these are somewhat advisory
276
         if(static_cast<uint32_t>(status) >= 5000) {
304✔
277
            return cert_status;
2,404✔
278
         }
279
      }
280
   }
281

282
   if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
2,264✔
283
      cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
27✔
284
   }
285

286
   if(!cert_path[0].allowed_usage(usage)) {
2,264✔
287
      if(usage == Usage_Type::OCSP_RESPONDER) {
5✔
288
         cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
1✔
289
      }
290
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
5✔
291
   }
292

293
   if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
2,264✔
294
      /*
295
      "If the keyCertSign bit is asserted, then the cA bit in the
296
      basic constraints extension (Section 4.2.1.9) MUST also be
297
      asserted." - RFC 5280
298

299
      We don't bother doing this check on the rest of the path since they
300
      must have the cA bit asserted or the validation will fail anyway.
301
      */
302
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
×
303
   }
304

305
   for(size_t i = 0; i != cert_path.size(); ++i) {
8,787✔
306
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
6,523✔
307

308
      const bool at_trust_anchor = (i == cert_path.size() - 1);
6,523✔
309

310
      const X509_Certificate& subject = cert_path[i];
6,523✔
311
      const auto issuer = [&]() -> std::optional<X509_Certificate> {
19,569✔
312
         if(!at_trust_anchor) {
6,523✔
313
            return cert_path[i + 1];
4,259✔
314
         } else if(subject.is_self_signed()) {
2,264✔
315
            return cert_path[i];
2,259✔
316
         } else {
317
            return {};  // Non self-signed trust anchors have no checkable issuers.
5✔
318
         }
319
      }();
6,523✔
320

321
      if(restrictions.require_self_signed_trust_anchors() && !issuer.has_value()) {
6,523✔
322
         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
×
323
      }
324

325
      // This should never happen; it indicates a bug in path building
326
      if(issuer.has_value() && subject.issuer_dn() != issuer->subject_dn()) {
6,523✔
327
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
×
328
      }
329

330
      // Check the serial number
331
      if(subject.is_serial_negative()) {
6,523✔
332
         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
64✔
333
      }
334

335
      // Check the subject's DN components' length
336

337
      for(const auto& dn_pair : subject.subject_dn().dn_info()) {
24,596✔
338
         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
18,073✔
339
         // dn_pair = <OID,str>
340
         if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
18,073✔
341
            status.insert(Certificate_Status_Code::DN_TOO_LONG);
40✔
342
         }
343
      }
344

345
      // If so configured, allow trust anchors outside the validity period with
346
      // a warning rather than a hard error
347
      const bool enforce_validity_period = !at_trust_anchor || !restrictions.ignore_trusted_root_time_range();
6,523✔
348
      // Check all certs for valid time range
349
      if(validation_time < subject.not_before()) {
6,523✔
350
         if(enforce_validity_period) {
82✔
351
            status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
80✔
352
         } else {
353
            status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID);  // only warn
2✔
354
         }
355
      }
356

357
      if(validation_time > subject.not_after()) {
6,523✔
358
         if(enforce_validity_period) {
310✔
359
            status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
308✔
360
         } else {
361
            status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED);  // only warn
2✔
362
         }
363
      }
364

365
      // Check issuer constraints
366
      if(issuer.has_value() && !issuer->is_CA_cert() && !is_end_entity_trust_anchor) {
6,523✔
367
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
118✔
368
      }
369

370
      // Check cert extensions
371

372
      if(subject.x509_version() == 1) {
6,523✔
373
         if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
37✔
374
            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
1✔
375
         }
376
      }
377

378
      const Extensions& extensions = subject.v3_extensions();
6,523✔
379
      const auto& extensions_vec = extensions.extensions();
6,523✔
380
      if(subject.x509_version() < 3 && !extensions_vec.empty()) {
6,523✔
381
         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
64✔
382
      }
383

384
      for(const auto& extension : extensions_vec) {
34,727✔
385
         extension.first->validate(subject, issuer, cert_path, cert_status, i);
28,204✔
386
      }
387

388
      if(extensions_vec.size() != extensions.get_extension_oids().size()) {
6,523✔
389
         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
32✔
390
      }
391
   }
13,041✔
392

393
   // path len check
394
   size_t max_path_length = cert_path.size();
2,264✔
395
   for(size_t i = cert_path.size() - 1; i > 0; --i) {
6,523✔
396
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
4,259✔
397
      const X509_Certificate& subject = cert_path[i];
4,259✔
398

399
      /*
400
      * If the certificate was not self-issued, verify that max_path_length is
401
      * greater than zero and decrement max_path_length by 1.
402
      */
403
      if(subject.subject_dn() != subject.issuer_dn()) {
4,259✔
404
         if(max_path_length > 0) {
1,885✔
405
            max_path_length -= 1;
1,807✔
406
         } else {
407
            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
78✔
408
         }
409
      }
410

411
      /*
412
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
413
      * set max_path_length to the value of pathLenConstraint.
414
      */
415
      if(auto path_len_constraint = subject.path_length_constraint()) {
4,259✔
416
         max_path_length = std::min(max_path_length, *path_len_constraint);
5,464✔
417
      }
418
   }
419

420
   return cert_status;
421
}
2,404✔
422

423
namespace {
424

425
Certificate_Status_Code verify_ocsp_signing_cert(const X509_Certificate& signing_cert,
43✔
426
                                                 const X509_Certificate& ca,
427
                                                 const std::vector<X509_Certificate>& extra_certs,
428
                                                 const std::vector<Certificate_Store*>& certstores,
429
                                                 std::chrono::system_clock::time_point ref_time,
430
                                                 const Path_Validation_Restrictions& restrictions) {
431
   // RFC 6960 4.2.2.2
432
   //    [Applications] MUST reject the response if the certificate
433
   //    required to validate the signature on the response does not
434
   //    meet at least one of the following criteria:
435
   //
436
   //    1. Matches a local configuration of OCSP signing authority
437
   //       for the certificate in question, or
438
   if(restrictions.trusted_ocsp_responders()->contains(signing_cert)) {
43✔
439
      return Certificate_Status_Code::OK;
440
   }
441

442
   // RFC 6960 4.2.2.2
443
   //
444
   //    2. Is the certificate of the CA that issued the certificate
445
   //       in question, or
446
   if(signing_cert == ca) {
43✔
447
      return Certificate_Status_Code::OK;
448
   }
449

450
   // RFC 6960 4.2.2.2
451
   //
452
   //    3. Includes a value of id-kp-OCSPSigning in an extended key
453
   //       usage extension and is issued by the CA that issued the
454
   //       certificate in question as stated above.
455

456
   // TODO: Implement OCSP revocation check of OCSP signer certificate
457
   // Note: This needs special care to prevent endless loops on specifically
458
   //       forged chains of OCSP responses referring to each other.
459
   //
460
   // Currently, we're disabling OCSP-based revocation checks by setting the
461
   // timeout to 0. Additionally, the library's API would not allow an
462
   // application to pass in the required "second order" OCSP responses. I.e.
463
   // "second order" OCSP checks would need to rely on `check_ocsp_online()`
464
   // which is not an option for some applications (e.g. that require a proxy
465
   // for external HTTP requests).
466
   const auto ocsp_timeout = std::chrono::milliseconds::zero();
23✔
467
   const auto relaxed_restrictions =
23✔
468
      Path_Validation_Restrictions(false /* do not enforce revocation data */,
469
                                   restrictions.minimum_key_strength(),
470
                                   false /* OCSP is not available, so don't try for intermediates */,
471
                                   restrictions.trusted_hashes());
23✔
472

473
   const auto validation_result = x509_path_validate(concat(std::vector{signing_cert}, extra_certs),
69✔
474
                                                     relaxed_restrictions,
475
                                                     certstores,
476
                                                     {} /* hostname */,
477
                                                     Botan::Usage_Type::OCSP_RESPONDER,
478
                                                     ref_time,
479
                                                     ocsp_timeout);
46✔
480

481
   return validation_result.result();
23✔
482
}
46✔
483

484
std::set<Certificate_Status_Code> evaluate_ocsp_response(const OCSP::Response& ocsp_response,
54✔
485
                                                         const X509_Certificate& subject,
486
                                                         const X509_Certificate& ca,
487
                                                         const std::vector<X509_Certificate>& cert_path,
488
                                                         const std::vector<Certificate_Store*>& certstores,
489
                                                         std::chrono::system_clock::time_point ref_time,
490
                                                         const Path_Validation_Restrictions& restrictions) {
491
   // Handle softfail conditions (eg. OCSP unavailable)
492
   if(auto dummy_status = ocsp_response.dummy_status()) {
54✔
493
      return {dummy_status.value()};
10✔
494
   }
495

496
   // Find the certificate that signed this OCSP response
497
   auto signing_cert = ocsp_response.find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
44✔
498
   if(!signing_cert) {
44✔
499
      return {Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND};
1✔
500
   }
501

502
   // Verify the signing certificate is trusted
503
   auto cert_status = verify_ocsp_signing_cert(
43✔
504
      signing_cert.value(), ca, concat(ocsp_response.certificates(), cert_path), certstores, ref_time, restrictions);
43✔
505
   if(cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
43✔
506
      return {cert_status, Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED};
4✔
507
   }
508

509
   // Verify the cryptographic signature on the OCSP response
510
   auto sig_status = ocsp_response.verify_signature(signing_cert.value());
39✔
511
   if(sig_status != Certificate_Status_Code::OCSP_SIGNATURE_OK) {
39✔
512
      return {sig_status};
1✔
513
   }
514

515
   // All checks passed, return the certificate's revocation status
516
   return {ocsp_response.status_for(ca, subject, ref_time, restrictions.max_ocsp_age())};
38✔
517
}
44✔
518

519
}  // namespace
520

521
CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
57✔
522
                                            const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
523
                                            const std::vector<Certificate_Store*>& certstores,
524
                                            std::chrono::system_clock::time_point ref_time,
525
                                            const Path_Validation_Restrictions& restrictions) {
526
   if(cert_path.empty()) {
57✔
527
      throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
×
528
   }
529

530
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
57✔
531

532
   for(size_t i = 0; i != cert_path.size() - 1; ++i) {
156✔
533
      const X509_Certificate& subject = cert_path.at(i);
99✔
534
      const X509_Certificate& ca = cert_path.at(i + 1);
99✔
535

536
      if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
99✔
537
         ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) {
54✔
538
         try {
54✔
539
            cert_status.at(i) = evaluate_ocsp_response(
162✔
540
               ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
108✔
541
         } catch(Exception&) {
×
542
            cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
×
543
         }
×
544
      }
545
   }
546

547
   while(!cert_status.empty() && cert_status.back().empty()) {
102✔
548
      cert_status.pop_back();
45✔
549
   }
550

551
   return cert_status;
57✔
552
}
×
553

554
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
2,407✔
555
                                           const std::vector<std::optional<X509_CRL>>& crls,
556
                                           std::chrono::system_clock::time_point ref_time) {
557
   if(cert_path.empty()) {
2,407✔
558
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
559
   }
560

561
   CertificatePathStatusCodes cert_status(cert_path.size());
2,407✔
562
   const X509_Time validation_time(ref_time);
2,407✔
563

564
   for(size_t i = 0; i != cert_path.size() - 1; ++i) {
6,944✔
565
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
4,537✔
566

567
      if(i < crls.size() && crls[i].has_value()) {
4,537✔
568
         const X509_Certificate& subject = cert_path.at(i);
1,387✔
569
         const X509_Certificate& ca = cert_path.at(i + 1);
1,387✔
570

571
         if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
1,387✔
572
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
4✔
573
         }
574

575
         if(validation_time < crls[i]->this_update()) {
1,387✔
576
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
32✔
577
         }
578

579
         if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
1,387✔
580
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
36✔
581
         }
582

583
         auto ca_key = ca.subject_public_key();
1,387✔
584
         if(crls[i]->check_signature(*ca_key) == false) {
1,387✔
585
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
35✔
586
         }
587

588
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
1,387✔
589

590
         if(crls[i]->is_revoked(subject)) {
1,387✔
591
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
275✔
592
         }
593

594
         const auto dp = subject.crl_distribution_points();
1,387✔
595
         if(!dp.empty()) {
1,387✔
596
            const auto crl_idp = crls[i]->crl_issuing_distribution_point();
416✔
597

598
            if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
416✔
599
               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
416✔
600
            }
601
         }
416✔
602

603
         for(const auto& [extension, critical] : crls[i]->extensions().extensions()) {
4,229✔
604
            if(critical) {
2,842✔
605
               /* NIST Certificate Path Validation Testing document: "When an implementation does
606
               * not recognize a critical extension in the crlExtensions field, it shall assume
607
               * that identified certificates have been revoked and are no longer valid"
608
               */
609
               if(dynamic_cast<const Cert_Extension::Unknown_Extension*>(extension.get()) != nullptr) {
72✔
610
                  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
40✔
611
               }
612
            }
613
         }
1,387✔
614
      }
2,774✔
615
   }
616

617
   while(!cert_status.empty() && cert_status.back().empty()) {
7,925✔
618
      cert_status.pop_back();
5,518✔
619
   }
620

621
   return cert_status;
2,407✔
622
}
2,407✔
623

624
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
2,401✔
625
                                           const std::vector<Certificate_Store*>& certstores,
626
                                           std::chrono::system_clock::time_point ref_time) {
627
   if(cert_path.empty()) {
2,401✔
628
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
629
   }
630

631
   if(certstores.empty()) {
2,401✔
632
      throw Invalid_Argument("PKIX::check_crl certstores empty");
×
633
   }
634

635
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
2,401✔
636

637
   for(size_t i = 0; i != cert_path.size(); ++i) {
9,333✔
638
      for(auto* certstore : certstores) {
11,801✔
639
         crls[i] = certstore->find_crl_for(cert_path[i]);
6,976✔
640
         if(crls[i]) {
6,976✔
641
            break;
642
         }
643
      }
644
   }
645

646
   return PKIX::check_crl(cert_path, crls, ref_time);
4,802✔
647
}
2,401✔
648

649
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
650

651
CertificatePathStatusCodes PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
10✔
652
                                                   const std::vector<Certificate_Store*>& trusted_certstores,
653
                                                   std::chrono::system_clock::time_point ref_time,
654
                                                   std::chrono::milliseconds timeout,
655
                                                   const Path_Validation_Restrictions& restrictions) {
656
   if(cert_path.empty()) {
10✔
657
      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
×
658
   }
659

660
   std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
10✔
661

662
   size_t to_ocsp = 1;
10✔
663

664
   if(restrictions.ocsp_all_intermediates()) {
10✔
665
      to_ocsp = cert_path.size() - 1;
×
666
   }
667
   if(cert_path.size() == 1) {
10✔
668
      to_ocsp = 0;
×
669
   }
670

671
   for(size_t i = 0; i < to_ocsp; ++i) {
20✔
672
      const X509_Certificate& subject = cert_path.at(i);
10✔
673
      const X509_Certificate& issuer = cert_path.at(i + 1);
10✔
674

675
      if(subject.ocsp_responder().empty()) {
10✔
676
         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
18✔
677
            return OCSP::Response(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
18✔
678
         }));
679
      } else {
680
         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
2✔
681
            const OCSP::Request req(issuer, BigInt::from_bytes(subject.serial_number()));
1✔
682

683
            HTTP::Response http;
1✔
684
            try {
1✔
685
               http = HTTP::POST_sync(subject.ocsp_responder(),
2✔
686
                                      "application/ocsp-request",
687
                                      req.BER_encode(),
2✔
688
                                      /*redirects*/ 1,
689
                                      timeout);
1✔
690
            } catch(std::exception&) {
×
691
               // log e.what() ?
692
            }
×
693
            if(http.status_code() != 200) {
1✔
694
               return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
×
695
            }
696
            // Check the MIME type?
697

698
            return OCSP::Response(http.body());
1✔
699
         }));
2✔
700
      }
701
   }
702

703
   std::vector<std::optional<OCSP::Response>> ocsp_responses;
10✔
704
   ocsp_responses.reserve(ocsp_response_futures.size());
10✔
705

706
   for(auto& ocsp_response_future : ocsp_response_futures) {
20✔
707
      ocsp_responses.push_back(ocsp_response_future.get());
20✔
708
   }
709

710
   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
20✔
711
}
10✔
712

713
CertificatePathStatusCodes PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
×
714
                                                  const std::vector<Certificate_Store*>& certstores,
715
                                                  Certificate_Store_In_Memory* crl_store,
716
                                                  std::chrono::system_clock::time_point ref_time,
717
                                                  std::chrono::milliseconds timeout) {
718
   if(cert_path.empty()) {
×
719
      throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
×
720
   }
721
   if(certstores.empty()) {
×
722
      throw Invalid_Argument("PKIX::check_crl_online certstores empty");
×
723
   }
724

725
   std::vector<std::future<std::optional<X509_CRL>>> future_crls;
×
726
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
×
727

728
   for(size_t i = 0; i != cert_path.size(); ++i) {
×
729
      const std::optional<X509_Certificate>& cert = cert_path.at(i);
×
730
      for(auto* certstore : certstores) {
×
731
         crls[i] = certstore->find_crl_for(*cert);
×
732
         if(crls[i].has_value()) {
×
733
            break;
734
         }
735
      }
736

737
      // TODO: check if CRL is expired and re-request?
738

739
      // Only request if we don't already have a CRL
740
      if(crls[i]) {
×
741
         /*
742
         We already have a CRL, so just insert this empty one to hold a place in the vector
743
         so that indexes match up
744
         */
745
         future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
×
746
      } else if(cert->crl_distribution_point().empty()) {
×
747
         // Avoid creating a thread for this case
748
         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
×
749
            throw Not_Implemented("No CRL distribution point for this certificate");
×
750
         }));
751
      } else {
752
         future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
×
753
            auto http = HTTP::GET_sync(cert->crl_distribution_point(),
×
754
                                       /*redirects*/ 1,
755
                                       timeout);
×
756

757
            http.throw_unless_ok();
×
758
            // check the mime type?
759
            return X509_CRL(http.body());
×
760
         }));
×
761
      }
762
   }
×
763

764
   for(size_t i = 0; i != future_crls.size(); ++i) {
×
765
      if(future_crls[i].valid()) {
×
766
         try {
×
767
            crls[i] = future_crls[i].get();
×
768
         } catch(std::exception&) {
×
769
            // crls[i] left null
770
            // todo: log exception e.what() ?
771
         }
×
772
      }
773
   }
774

775
   auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
×
776

777
   if(crl_store != nullptr) {
×
778
      for(size_t i = 0; i != crl_status.size(); ++i) {
×
779
         if(crl_status[i].contains(Certificate_Status_Code::VALID_CRL_CHECKED)) {
×
780
            // better be non-null, we supposedly validated it
781
            BOTAN_ASSERT_NOMSG(crls[i].has_value());
×
782
            crl_store->add_crl(*crls[i]);
×
783
         }
784
      }
785
   }
786

787
   return crl_status;
×
788
}
×
789

790
#endif
791

792
Certificate_Status_Code PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
5✔
793
                                                     const std::vector<Certificate_Store*>& trusted_certstores,
794
                                                     const X509_Certificate& end_entity,
795
                                                     const std::vector<X509_Certificate>& end_entity_extra) {
796
   CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra);
5✔
797

798
   std::vector<X509_Certificate> first_path;
5✔
799

800
   while(auto path = builder.next()) {
13✔
801
      BOTAN_ASSERT_NOMSG(path->empty() == false);
11✔
802

803
      // Prefer paths ending in self-signed certificates.
804
      if(path->back().is_self_signed()) {
11✔
805
         cert_path.insert(cert_path.end(), path->begin(), path->end());
3✔
806
         return Certificate_Status_Code::OK;
3✔
807
      }
808

809
      // Save the first path for later just in case we find nothing better
810
      if(first_path.empty()) {
8✔
811
         first_path = std::move(*path);
4✔
812
      }
813
   }
11✔
814

815
   if(!first_path.empty()) {
2✔
816
      // We found a path, it's not self-signed but it's as good as can be formed...
817
      cert_path.insert(cert_path.end(), first_path.begin(), first_path.end());
2✔
818
      return Certificate_Status_Code::OK;
2✔
819
   }
820

821
   // Failed to build any path at all
822
   return builder.error();
×
823
}
5✔
824

825
Certificate_Status_Code PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
5✔
826
                                                          const std::vector<Certificate_Store*>& trusted_certstores,
827
                                                          const X509_Certificate& end_entity,
828
                                                          const std::vector<X509_Certificate>& end_entity_extra) {
829
   if(!cert_paths_out.empty()) {
5✔
830
      throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
×
831
   }
832
   CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra);
5✔
833

834
   while(auto path = builder.next()) {
16✔
835
      BOTAN_ASSERT_NOMSG(path->empty() == false);
11✔
836
      cert_paths_out.push_back(std::move(*path));
11✔
837
   }
11✔
838

839
   if(!cert_paths_out.empty()) {
5✔
840
      // Was able to generate at least one potential path
841
      return Certificate_Status_Code::OK;
842
   } else {
843
      // Could not construct any potentially valid path...
844
      return builder.error();
×
845
   }
846
}
5✔
847

848
void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
2,401✔
849
                                   const CertificatePathStatusCodes& crl_status,
850
                                   const CertificatePathStatusCodes& ocsp_status,
851
                                   const Path_Validation_Restrictions& restrictions) {
852
   if(chain_status.empty()) {
2,401✔
853
      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
×
854
   }
855

856
   for(size_t i = 0; i != chain_status.size() - 1; ++i) {
6,932✔
857
      bool had_crl = false;
4,531✔
858
      bool had_ocsp = false;
4,531✔
859

860
      if(i < crl_status.size() && !crl_status[i].empty()) {
4,531✔
861
         for(auto&& code : crl_status[i]) {
3,595✔
862
            if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
2,214✔
863
               had_crl = true;
1,381✔
864
            }
865
            chain_status[i].insert(code);
2,214✔
866
         }
867
      }
868

869
      if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
4,531✔
870
         for(auto&& code : ocsp_status[i]) {
76✔
871
            // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
872
            if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
40✔
873
               code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||
20✔
874
               code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) {
875
               had_ocsp = true;
20✔
876
            }
877

878
            chain_status[i].insert(code);
40✔
879
         }
880
      }
881

882
      if(had_crl == false && had_ocsp == false) {
4,531✔
883
         if((restrictions.require_revocation_information() && i == 0) ||
3,130✔
884
            (restrictions.ocsp_all_intermediates() && i > 0)) {
3,054✔
885
            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
143✔
886
         }
887
      }
888
   }
889
}
2,401✔
890

891
Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status) {
2,410✔
892
   if(cert_status.empty()) {
2,410✔
893
      throw Invalid_Argument("PKIX::overall_status empty cert status");
×
894
   }
895

896
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
897

898
   // take the "worst" error as overall
899
   for(const std::set<Certificate_Status_Code>& s : cert_status) {
9,353✔
900
      if(!s.empty()) {
6,943✔
901
         auto worst = *s.rbegin();
2,845✔
902
         // Leave informative OCSP/CRL confirmations on cert-level status only
903
         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
2,845✔
904
            overall_status = worst;
6,943✔
905
         }
906
      }
907
   }
908
   return overall_status;
2,410✔
909
}
910

911
Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
2,873✔
912
                                          const Path_Validation_Restrictions& restrictions,
913
                                          const std::vector<Certificate_Store*>& trusted_roots,
914
                                          std::string_view hostname,
915
                                          Usage_Type usage,
916
                                          std::chrono::system_clock::time_point ref_time,
917
                                          std::chrono::milliseconds ocsp_timeout,
918
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
919
   if(end_certs.empty()) {
2,873✔
920
      throw Invalid_Argument("x509_path_validate called with no subjects");
×
921
   }
922

923
   const X509_Certificate& end_entity = end_certs[0];
2,873✔
924
   std::vector<X509_Certificate> end_entity_extra;
2,873✔
925
   for(size_t i = 1; i < end_certs.size(); ++i) {
5,674✔
926
      end_entity_extra.push_back(end_certs[i]);
2,801✔
927
   }
928

929
   const bool require_self_signed = restrictions.require_self_signed_trust_anchors();
2,873✔
930

931
   CertificatePathBuilder builder(trusted_roots, end_entity, end_entity_extra, require_self_signed);
2,873✔
932

933
   constexpr size_t max_paths = 128;
2,873✔
934

935
   std::optional<Path_Validation_Result> first_path_error;
2,873✔
936
   size_t paths_checked = 0;
2,873✔
937

938
   while(auto cert_path = builder.next()) {
4,545✔
939
      BOTAN_ASSERT_NOMSG(cert_path->empty() == false);
2,401✔
940

941
      paths_checked += 1;
2,401✔
942
      if(paths_checked > max_paths) {
2,401✔
943
         break;
944
      }
945

946
      CertificatePathStatusCodes status = PKIX::check_chain(*cert_path, ref_time, hostname, usage, restrictions);
2,401✔
947

948
      const CertificatePathStatusCodes crl_status = PKIX::check_crl(*cert_path, trusted_roots, ref_time);
2,401✔
949

950
      CertificatePathStatusCodes ocsp_status;
2,401✔
951

952
      if(!ocsp_resp.empty()) {
2,401✔
953
         ocsp_status = PKIX::check_ocsp(*cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
30✔
954
      }
955

956
      if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) {
2,401✔
957
#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
958
         ocsp_status = PKIX::check_ocsp_online(*cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions);
9✔
959
#else
960
         ocsp_status.resize(1);
961
         ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
962
#endif
963
      }
964

965
      PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions);
2,401✔
966

967
      Path_Validation_Result pvd(status, std::move(*cert_path));
2,401✔
968
      if(pvd.successful_validation()) {
2,401✔
969
         return pvd;
729✔
970
      } else if(!first_path_error.has_value()) {
1,672✔
971
         // Save the errors from the first path we attempted
972
         first_path_error = std::move(pvd);
1,672✔
973
      }
974
   }
4,802✔
975

976
   if(first_path_error.has_value()) {
2,144✔
977
      // We found at least one path, but none of them verified
978
      // Return arbitrarily the error from the first path attempted
979
      return first_path_error.value();
1,672✔
980
   } else {
981
      // Failed to build any path at all
982
      return Path_Validation_Result(builder.error());
472✔
983
   }
984
}
5,251✔
985

986
Path_Validation_Result x509_path_validate(const X509_Certificate& end_cert,
283✔
987
                                          const Path_Validation_Restrictions& restrictions,
988
                                          const std::vector<Certificate_Store*>& trusted_roots,
989
                                          std::string_view hostname,
990
                                          Usage_Type usage,
991
                                          std::chrono::system_clock::time_point when,
992
                                          std::chrono::milliseconds ocsp_timeout,
993
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
994
   std::vector<X509_Certificate> certs;
283✔
995
   certs.push_back(end_cert);
283✔
996
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
566✔
997
}
283✔
998

999
Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
1,951✔
1000
                                          const Path_Validation_Restrictions& restrictions,
1001
                                          const Certificate_Store& store,
1002
                                          std::string_view hostname,
1003
                                          Usage_Type usage,
1004
                                          std::chrono::system_clock::time_point when,
1005
                                          std::chrono::milliseconds ocsp_timeout,
1006
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
1007
   std::vector<Certificate_Store*> trusted_roots;
1,951✔
1008
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
1,951✔
1009

1010
   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1,951✔
1011
}
1,951✔
1012

1013
Path_Validation_Result x509_path_validate(const X509_Certificate& end_cert,
214✔
1014
                                          const Path_Validation_Restrictions& restrictions,
1015
                                          const Certificate_Store& store,
1016
                                          std::string_view hostname,
1017
                                          Usage_Type usage,
1018
                                          std::chrono::system_clock::time_point when,
1019
                                          std::chrono::milliseconds ocsp_timeout,
1020
                                          const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
1021
   std::vector<X509_Certificate> certs;
214✔
1022
   certs.push_back(end_cert);
214✔
1023

1024
   std::vector<Certificate_Store*> trusted_roots;
214✔
1025
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
214✔
1026

1027
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
428✔
1028
}
214✔
1029

1030
Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
1,039✔
1031
                                                           size_t key_strength,
1032
                                                           bool ocsp_intermediates,
1033
                                                           std::chrono::seconds max_ocsp_age,
1034
                                                           std::unique_ptr<Certificate_Store> trusted_ocsp_responders,
1035
                                                           bool ignore_trusted_root_time_range,
1036
                                                           bool require_self_signed_trust_anchors) :
1,039✔
1037
      m_require_revocation_information(require_rev),
1,039✔
1038
      m_ocsp_all_intermediates(ocsp_intermediates),
1,039✔
1039
      m_minimum_key_strength(key_strength),
1,039✔
1040
      m_max_ocsp_age(max_ocsp_age),
1,039✔
1041
      m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)),
1,039✔
1042
      m_ignore_trusted_root_time_range(ignore_trusted_root_time_range),
1,039✔
1043
      m_require_self_signed_trust_anchors(require_self_signed_trust_anchors) {
1,039✔
1044
   if(key_strength <= 80) {
1,039✔
1045
      m_trusted_hashes.insert("SHA-1");
828✔
1046
   }
1047

1048
   m_trusted_hashes.insert("SHA-224");
2,078✔
1049
   m_trusted_hashes.insert("SHA-256");
2,078✔
1050
   m_trusted_hashes.insert("SHA-384");
2,078✔
1051
   m_trusted_hashes.insert("SHA-512");
2,078✔
1052
   m_trusted_hashes.insert("SHAKE-256(512)");  // Dilithium/ML-DSA
2,078✔
1053
   m_trusted_hashes.insert("SHAKE-256(912)");  // Ed448
2,078✔
1054
}
1,039✔
1055

1056
namespace {
1057
CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses) {
2,402✔
1058
   CertificatePathStatusCodes warnings;
2,402✔
1059
   for(const auto& status_set_i : all_statuses) {
9,337✔
1060
      std::set<Certificate_Status_Code> warning_set_i;
6,935✔
1061
      for(const auto& code : status_set_i) {
10,823✔
1062
         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
3,888✔
1063
            code < Certificate_Status_Code::FIRST_ERROR_STATUS) {
1064
            warning_set_i.insert(code);
117✔
1065
         }
1066
      }
1067
      warnings.push_back(warning_set_i);
6,935✔
1068
   }
6,935✔
1069
   return warnings;
2,402✔
1070
}
×
1071
}  // namespace
1072

1073
Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
2,402✔
1074
                                               std::vector<X509_Certificate>&& cert_chain) :
2,402✔
1075
      m_all_status(std::move(status)),
2,402✔
1076
      m_warnings(find_warnings(m_all_status)),
2,402✔
1077
      m_cert_path(std::move(cert_chain)),
2,402✔
1078
      m_overall(PKIX::overall_status(m_all_status)) {}
2,402✔
1079

1080
const X509_Certificate& Path_Validation_Result::trust_root() const {
22✔
1081
   if(m_cert_path.empty()) {
22✔
1082
      throw Invalid_State("Path_Validation_Result::trust_root no path set");
×
1083
   }
1084
   if(result() != Certificate_Status_Code::VERIFIED) {
22✔
1085
      throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
×
1086
   }
1087

1088
   return m_cert_path[m_cert_path.size() - 1];
22✔
1089
}
1090

1091
bool Path_Validation_Result::successful_validation() const {
3,032✔
1092
   return (result() == Certificate_Status_Code::VERIFIED || result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
3,032✔
1093
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
2,080✔
1094
}
1095

1096
bool Path_Validation_Result::no_warnings() const {
76✔
1097
   for(const auto& status_set_i : m_warnings) {
302✔
1098
      if(!status_set_i.empty()) {
228✔
1099
         return false;
76✔
1100
      }
1101
   }
1102
   return true;
1103
}
1104

1105
CertificatePathStatusCodes Path_Validation_Result::warnings() const {
74✔
1106
   return m_warnings;
74✔
1107
}
1108

1109
std::string Path_Validation_Result::result_string() const {
2,210✔
1110
   return status_string(result());
2,210✔
1111
}
1112

1113
const char* Path_Validation_Result::status_string(Certificate_Status_Code code) {
2,251✔
1114
   if(const char* s = to_string(code)) {
2,251✔
1115
      return s;
2,251✔
1116
   }
1117

1118
   return "Unknown error";
1119
}
1120

1121
std::string Path_Validation_Result::warnings_string() const {
76✔
1122
   const std::string sep(", ");
76✔
1123
   std::ostringstream oss;
76✔
1124
   for(size_t i = 0; i < m_warnings.size(); i++) {
306✔
1125
      for(auto code : m_warnings[i]) {
232✔
1126
         oss << "[" << std::to_string(i) << "] " << status_string(code) << sep;
4✔
1127
      }
1128
   }
1129

1130
   std::string res = oss.str();
76✔
1131
   // remove last sep
1132
   if(res.size() >= sep.size()) {
76✔
1133
      res = res.substr(0, res.size() - sep.size());
2✔
1134
   }
1135
   return res;
152✔
1136
}
76✔
1137
}  // 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