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

randombit / botan / 11631334564

01 Nov 2024 02:54PM UTC coverage: 91.031% (-0.04%) from 91.072%
11631334564

Pull #4422

github

web-flow
Merge c66020351 into bd045b1a0
Pull Request #4422: Chore: Centralize some Repository Config

90350 of 99252 relevant lines covered (91.03%)

9489034.7 hits per line

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

80.53
/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
#if defined(BOTAN_HAS_ECC_KEY)
28
   #include <botan/ecc_key.h>
29
#endif
30

31
namespace Botan {
32

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

45
   const bool self_signed_ee_cert = (cert_path.size() == 1);
1,421✔
46

47
   X509_Time validation_time(ref_time);
1,421✔
48

49
   CertificatePathStatusCodes cert_status(cert_path.size());
1,421✔
50

51
   // Before anything else verify the entire chain of signatures
52
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,319✔
53
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
3,898✔
54

55
      const bool at_self_signed_root = (i == cert_path.size() - 1);
3,898✔
56

57
      const X509_Certificate& subject = cert_path[i];
3,898✔
58
      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
3,898✔
59

60
      // Check the signature algorithm is known
61
      if(!subject.signature_algorithm().oid().registered_oid()) {
3,898✔
62
         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
16✔
63
      } else {
64
         std::unique_ptr<Public_Key> issuer_key;
3,882✔
65
         try {
3,882✔
66
            issuer_key = issuer.subject_public_key();
3,882✔
67
         } catch(...) {
×
68
            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
×
69
         }
×
70

71
         if(issuer_key) {
3,882✔
72
            if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
3,882✔
73
               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
65✔
74
            }
75

76
            const auto sig_status = subject.verify_signature(*issuer_key);
3,882✔
77

78
            if(sig_status.first != Certificate_Status_Code::VERIFIED) {
3,882✔
79
               status.insert(sig_status.first);
57✔
80
            } else {
81
               // Signature is valid, check if hash used was acceptable
82
               const std::string hash_used_for_signature = sig_status.second;
3,825✔
83
               BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
3,825✔
84
               const auto& trusted_hashes = restrictions.trusted_hashes();
3,825✔
85

86
               // Ignore untrusted hashes on self-signed roots
87
               if(!trusted_hashes.empty() && !at_self_signed_root) {
3,825✔
88
                  if(!trusted_hashes.contains(hash_used_for_signature)) {
2,404✔
89
                     status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
68✔
90
                  }
91
               }
92
            }
3,825✔
93
         }
3,882✔
94
      }
3,898✔
95
   }
96

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

103
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,124✔
104
      for(auto status : cert_status.at(i)) {
3,908✔
105
         // This ignores errors relating to the key or hash being weak since
106
         // these are somewhat advisory
107
         if(static_cast<uint32_t>(status) >= 5000) {
205✔
108
            return cert_status;
1,421✔
109
         }
110
      }
111
   }
112

113
   if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
1,348✔
114
      cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
14✔
115
   }
116

117
   if(!cert_path[0].allowed_usage(usage)) {
1,348✔
118
      if(usage == Usage_Type::OCSP_RESPONDER) {
3✔
119
         cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
1✔
120
      }
121
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
3✔
122
   }
123

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

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

136
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,033✔
137
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
3,685✔
138

139
      const bool at_self_signed_root = (i == cert_path.size() - 1);
3,685✔
140

141
      const X509_Certificate& subject = cert_path[i];
3,685✔
142
      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
3,685✔
143

144
      if(at_self_signed_root && (issuer.is_self_signed() == false)) {
3,685✔
145
         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
×
146
      }
147

148
      // This should never happen; it indicates a bug in path building
149
      if(subject.issuer_dn() != issuer.subject_dn()) {
3,685✔
150
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
×
151
      }
152

153
      // Check the serial number
154
      if(subject.is_serial_negative()) {
3,685✔
155
         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
32✔
156
      }
157

158
      // Check the subject's DN components' length
159

160
      for(const auto& dn_pair : subject.subject_dn().dn_info()) {
14,190✔
161
         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
10,505✔
162
         // dn_pair = <OID,str>
163
         if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
10,505✔
164
            status.insert(Certificate_Status_Code::DN_TOO_LONG);
24✔
165
         }
166
      }
167

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

180
      if(validation_time > subject.not_after()) {
3,685✔
181
         if(is_trusted_root_and_time_ignored) {
258✔
182
            status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED);  // only warn
2✔
183
         } else {
184
            status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
256✔
185
         }
186
      }
187

188
      // Check issuer constraints
189
      if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
3,685✔
190
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
59✔
191
      }
192

193
      // Check cert extensions
194

195
      if(subject.x509_version() == 1) {
3,685✔
196
         if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
20✔
197
            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
1✔
198
         }
199
      }
200

201
      const Extensions& extensions = subject.v3_extensions();
3,685✔
202
      const auto& extensions_vec = extensions.extensions();
3,685✔
203
      if(subject.x509_version() < 3 && !extensions_vec.empty()) {
3,685✔
204
         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
32✔
205
      }
206
      for(auto& extension : extensions_vec) {
19,403✔
207
         extension.first->validate(subject, issuer, cert_path, cert_status, i);
15,718✔
208
      }
209
      if(extensions_vec.size() != extensions.get_extension_oids().size()) {
3,685✔
210
         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
16✔
211
      }
212
   }
3,685✔
213

214
   // path len check
215
   size_t max_path_length = cert_path.size();
1,348✔
216
   for(size_t i = cert_path.size() - 1; i > 0; --i) {
3,685✔
217
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
2,337✔
218
      const X509_Certificate& subject = cert_path[i];
2,337✔
219

220
      /*
221
      * If the certificate was not self-issued, verify that max_path_length is
222
      * greater than zero and decrement max_path_length by 1.
223
      */
224
      if(subject.subject_dn() != subject.issuer_dn()) {
2,337✔
225
         if(max_path_length > 0) {
974✔
226
            --max_path_length;
923✔
227
         } else {
228
            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
51✔
229
         }
230
      }
231

232
      /*
233
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
234
      * set max_path_length to the value of pathLenConstraint.
235
      */
236
      if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length) {
2,337✔
237
         max_path_length = subject.path_limit();
1,165✔
238
      }
239
   }
240

241
   return cert_status;
242
}
1,421✔
243

244
namespace {
245

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

263
   // RFC 6960 4.2.2.2
264
   //
265
   //    2. Is the certificate of the CA that issued the certificate
266
   //       in question, or
267
   if(signing_cert == ca) {
40✔
268
      return Certificate_Status_Code::OK;
269
   }
270

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

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

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

302
   return validation_result.result();
23✔
303
}
46✔
304

305
}  // namespace
306

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

316
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
54✔
317

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

321
      const X509_Certificate& subject = cert_path.at(i);
94✔
322
      const X509_Certificate& ca = cert_path.at(i + 1);
94✔
323

324
      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
94✔
325
         (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
51✔
326
         try {
51✔
327
            const auto& ocsp_response = ocsp_responses.at(i);
51✔
328

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

355
   while(!cert_status.empty() && cert_status.back().empty()) {
97✔
356
      cert_status.pop_back();
43✔
357
   }
358

359
   return cert_status;
54✔
360
}
×
361

362
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
1,425✔
363
                                           const std::vector<std::optional<X509_CRL>>& crls,
364
                                           std::chrono::system_clock::time_point ref_time) {
365
   if(cert_path.empty()) {
1,425✔
366
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
367
   }
368

369
   CertificatePathStatusCodes cert_status(cert_path.size());
1,425✔
370
   const X509_Time validation_time(ref_time);
1,425✔
371

372
   for(size_t i = 0; i != cert_path.size() - 1; ++i) {
3,908✔
373
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
2,483✔
374

375
      if(i < crls.size() && crls[i].has_value()) {
2,483✔
376
         const X509_Certificate& subject = cert_path.at(i);
773✔
377
         const X509_Certificate& ca = cert_path.at(i + 1);
773✔
378

379
         if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
773✔
380
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
2✔
381
         }
382

383
         if(validation_time < crls[i]->this_update()) {
773✔
384
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
16✔
385
         }
386

387
         if(validation_time > crls[i]->next_update()) {
773✔
388
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
18✔
389
         }
390

391
         auto ca_key = ca.subject_public_key();
773✔
392
         if(crls[i]->check_signature(*ca_key) == false) {
773✔
393
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
18✔
394
         }
395

396
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
773✔
397

398
         if(crls[i]->is_revoked(subject)) {
773✔
399
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
157✔
400
         }
401

402
         const auto dp = subject.crl_distribution_points();
773✔
403
         if(!dp.empty()) {
773✔
404
            const auto crl_idp = crls[i]->crl_issuing_distribution_point();
208✔
405

406
            if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
208✔
407
               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
208✔
408
            }
409
         }
208✔
410

411
         for(const auto& extension : crls[i]->extensions().extensions()) {
2,349✔
412
            // XXX this is wrong - the OID might be defined but the extention not full parsed
413
            // for example see #1652
414

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

426
   while(!cert_status.empty() && cert_status.back().empty()) {
4,540✔
427
      cert_status.pop_back();
3,115✔
428
   }
429

430
   return cert_status;
1,425✔
431
}
1,425✔
432

433
CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
1,419✔
434
                                           const std::vector<Certificate_Store*>& certstores,
435
                                           std::chrono::system_clock::time_point ref_time) {
436
   if(cert_path.empty()) {
1,419✔
437
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
×
438
   }
439

440
   if(certstores.empty()) {
1,419✔
441
      throw Invalid_Argument("PKIX::check_crl certstores empty");
×
442
   }
443

444
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
1,419✔
445

446
   for(size_t i = 0; i != cert_path.size(); ++i) {
5,315✔
447
      for(auto certstore : certstores) {
6,615✔
448
         crls[i] = certstore->find_crl_for(cert_path[i]);
7,848✔
449
         if(crls[i]) {
3,924✔
450
            break;
451
         }
452
      }
453
   }
454

455
   return PKIX::check_crl(cert_path, crls, ref_time);
2,838✔
456
}
1,419✔
457

458
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
459

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

469
   std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
9✔
470

471
   size_t to_ocsp = 1;
9✔
472

473
   if(restrictions.ocsp_all_intermediates()) {
9✔
474
      to_ocsp = cert_path.size() - 1;
×
475
   }
476
   if(cert_path.size() == 1) {
9✔
477
      to_ocsp = 0;
×
478
   }
479

480
   for(size_t i = 0; i < to_ocsp; ++i) {
18✔
481
      const X509_Certificate& subject = cert_path.at(i);
9✔
482
      const X509_Certificate& issuer = cert_path.at(i + 1);
9✔
483

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

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

507
            return OCSP::Response(http.body());
×
508
         }));
2✔
509
      }
510
   }
511

512
   std::vector<std::optional<OCSP::Response>> ocsp_responses;
9✔
513
   ocsp_responses.reserve(ocsp_response_futures.size());
9✔
514

515
   for(auto& ocsp_response_future : ocsp_response_futures) {
18✔
516
      ocsp_responses.push_back(ocsp_response_future.get());
27✔
517
   }
518

519
   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
18✔
520
}
9✔
521

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

534
   std::vector<std::future<std::optional<X509_CRL>>> future_crls;
×
535
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
×
536

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

546
      // TODO: check if CRL is expired and re-request?
547

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

566
            http.throw_unless_ok();
×
567
            // check the mime type?
568
            return X509_CRL(http.body());
×
569
         }));
×
570
      }
571
   }
×
572

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

584
   auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
×
585

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

596
   return crl_status;
×
597
}
×
598

599
#endif
600

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

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

617
   cert_path.push_back(end_entity);
×
618
   certs_seen.insert(end_entity.fingerprint("SHA-256"));
×
619

620
   Certificate_Store_In_Memory ee_extras;
×
621
   for(const auto& cert : end_entity_extra) {
×
622
      ee_extras.add_certificate(cert);
×
623
   }
624

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

631
      std::optional<X509_Certificate> issuer;
×
632
      bool trusted_issuer = false;
×
633

634
      for(Certificate_Store* store : trusted_certstores) {
×
635
         issuer = store->find_cert(issuer_dn, auth_key_id);
×
636
         if(issuer) {
×
637
            trusted_issuer = true;
638
            break;
639
         }
640
      }
641

642
      if(!issuer) {
×
643
         // fall back to searching supplemental certs
644
         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
×
645
      }
646

647
      if(!issuer) {
×
648
         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
649
      }
650

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

653
      if(certs_seen.contains(fprint)) {
×
654
         // we already saw this certificate -> loop
655
         return Certificate_Status_Code::CERT_CHAIN_LOOP;
656
      }
657

658
      certs_seen.insert(fprint);
×
659
      cert_path.push_back(*issuer);
×
660

661
      if(issuer->is_self_signed()) {
×
662
         if(trusted_issuer) {
×
663
            return Certificate_Status_Code::OK;
664
         } else {
665
            return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
×
666
         }
667
      }
668
   }
×
669
}
×
670

671
/**
672
 * utilities for PKIX::build_all_certificate_paths
673
 */
674
namespace {
675
// <certificate, trusted?>
676
using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>, bool>;
677
}  // namespace
678

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

704
   if(end_entity->is_self_signed()) {
1,608✔
705
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
706
   }
707

708
   /*
709
    * Pile up error messages
710
    */
711
   std::vector<Certificate_Status_Code> stats;
1,591✔
712

713
   Certificate_Store_In_Memory ee_extras;
1,591✔
714
   for(const auto& cert : end_entity_extra) {
2,835✔
715
      ee_extras.add_certificate(cert);
1,244✔
716
   }
717

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

726
   // new certs are added and removed from the path during the DFS
727
   // it is copied into cert_paths_out when we encounter a trusted root
728
   std::vector<X509_Certificate> path_so_far;
1,591✔
729

730
   // todo can we assume that the end certificate is not trusted?
731
   std::vector<cert_maybe_trusted> stack = {{end_entity, false}};
3,182✔
732

733
   while(!stack.empty()) {
8,729✔
734
      std::optional<X509_Certificate> last = stack.back().first;
7,138✔
735
      // found a deletion marker that guides the DFS, backtracing
736
      if(last == std::nullopt) {
7,138✔
737
         stack.pop_back();
2,673✔
738
         std::string fprint = path_so_far.back().fingerprint("SHA-256");
2,673✔
739
         certs_seen.erase(fprint);
2,673✔
740
         path_so_far.pop_back();
2,673✔
741
      }
2,673✔
742
      // process next cert on the path
743
      else {
744
         const bool trusted = stack.back().second;
4,465✔
745
         stack.pop_back();
4,465✔
746

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

755
         // the current path ends here
756
         if(last->is_self_signed()) {
4,464✔
757
            // found a trust anchor
758
            if(trusted) {
1,623✔
759
               cert_paths_out.push_back(path_so_far);
1,422✔
760
               cert_paths_out.back().push_back(*last);
1,422✔
761

762
               continue;
1,422✔
763
            }
764
            // found an untrustworthy root
765
            else {
766
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
201✔
767
               continue;
201✔
768
            }
769
         }
770

771
         const X509_DN issuer_dn = last->issuer_dn();
2,841✔
772
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
2,841✔
773

774
         // search for trusted issuers
775
         std::vector<X509_Certificate> trusted_issuers;
2,841✔
776
         for(Certificate_Store* store : trusted_certstores) {
5,695✔
777
            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
2,854✔
778
            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
2,854✔
779
         }
2,854✔
780

781
         // search the supplemental certs
782
         std::vector<X509_Certificate> misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id);
2,841✔
783

784
         // if we could not find any issuers, the current path ends here
785
         if(trusted_issuers.empty() && misc_issuers.empty()) {
2,841✔
786
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
168✔
787
            continue;
168✔
788
         }
789

790
         // push the latest certificate onto the path_so_far
791
         path_so_far.push_back(*last);
2,673✔
792
         certs_seen.emplace(fprint);
2,673✔
793

794
         // push a deletion marker on the stack for backtracing later
795
         stack.push_back({std::optional<X509_Certificate>(), false});
2,673✔
796

797
         for(const auto& trusted_cert : trusted_issuers) {
4,358✔
798
            stack.push_back({trusted_cert, true});
3,370✔
799
         }
800

801
         for(const auto& misc : misc_issuers) {
3,862✔
802
            stack.push_back({misc, false});
2,378✔
803
         }
804
      }
7,241✔
805
   }
7,138✔
806

807
   // could not construct any potentially valid path
808
   if(cert_paths_out.empty()) {
1,591✔
809
      if(stats.empty()) {
172✔
810
         throw Internal_Error("X509 path building failed for unknown reasons");
×
811
      } else {
812
         // arbitrarily return the first error
813
         return stats[0];
172✔
814
      }
815
   } else {
816
      return Certificate_Status_Code::OK;
817
   }
818
}
4,773✔
819

820
void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
1,419✔
821
                                   const CertificatePathStatusCodes& crl,
822
                                   const CertificatePathStatusCodes& ocsp,
823
                                   const Path_Validation_Restrictions& restrictions) {
824
   if(chain_status.empty()) {
1,419✔
825
      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
×
826
   }
827

828
   for(size_t i = 0; i != chain_status.size() - 1; ++i) {
3,896✔
829
      bool had_crl = false, had_ocsp = false;
2,477✔
830

831
      if(i < crl.size() && !crl[i].empty()) {
2,477✔
832
         for(auto&& code : crl[i]) {
1,969✔
833
            if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
1,202✔
834
               had_crl = true;
767✔
835
            }
836
            chain_status[i].insert(code);
1,202✔
837
         }
838
      }
839

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

849
            chain_status[i].insert(code);
39✔
850
         }
851
      }
852

853
      if(had_crl == false && had_ocsp == false) {
2,477✔
854
         if((restrictions.require_revocation_information() && i == 0) ||
1,691✔
855
            (restrictions.ocsp_all_intermediates() && i > 0)) {
1,650✔
856
            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
76✔
857
         }
858
      }
859
   }
860
}
1,419✔
861

862
Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status) {
1,427✔
863
   if(cert_status.empty()) {
1,427✔
864
      throw Invalid_Argument("PKIX::overall_status empty cert status");
×
865
   }
866

867
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
868

869
   // take the "worst" error as overall
870
   for(const std::set<Certificate_Status_Code>& s : cert_status) {
5,331✔
871
      if(!s.empty()) {
3,904✔
872
         auto worst = *s.rbegin();
1,617✔
873
         // Leave informative OCSP/CRL confirmations on cert-level status only
874
         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
1,617✔
875
            overall_status = worst;
3,904✔
876
         }
877
      }
878
   }
879
   return overall_status;
1,427✔
880
}
881

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

894
   X509_Certificate end_entity = end_certs[0];
1,608✔
895
   std::vector<X509_Certificate> end_entity_extra;
1,608✔
896
   for(size_t i = 1; i < end_certs.size(); ++i) {
2,852✔
897
      end_entity_extra.push_back(end_certs[i]);
1,244✔
898
   }
899

900
   std::vector<std::vector<X509_Certificate>> cert_paths;
1,608✔
901
   Certificate_Status_Code path_building_result =
1,608✔
902
      PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
1,608✔
903

904
   // If we cannot successfully build a chain to a trusted self-signed root, stop now
905
   if(path_building_result != Certificate_Status_Code::OK) {
1,608✔
906
      return Path_Validation_Result(path_building_result);
189✔
907
   }
908

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

914
      CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, trusted_roots, ref_time);
1,419✔
915

916
      CertificatePathStatusCodes ocsp_status;
1,419✔
917

918
      if(!ocsp_resp.empty()) {
1,419✔
919
         ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
30✔
920
      }
921

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

931
      PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions);
1,419✔
932

933
      Path_Validation_Result pvd(status, std::move(cert_path));
1,419✔
934
      if(pvd.successful_validation()) {
1,419✔
935
         return pvd;
520✔
936
      } else {
937
         error_results.push_back(std::move(pvd));
899✔
938
      }
939
   }
1,419✔
940
   return error_results[0];
899✔
941
}
3,004✔
942

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

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

967
   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1,760✔
968
}
880✔
969

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

981
   std::vector<Certificate_Store*> trusted_roots;
297✔
982
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
297✔
983

984
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
594✔
985
}
297✔
986

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

1003
   m_trusted_hashes.insert("SHA-224");
1,416✔
1004
   m_trusted_hashes.insert("SHA-256");
1,416✔
1005
   m_trusted_hashes.insert("SHA-384");
1,416✔
1006
   m_trusted_hashes.insert("SHA-512");
1,416✔
1007
   m_trusted_hashes.insert("SHAKE-256(512)");  // Dilithium/ML-DSA
1,416✔
1008
   m_trusted_hashes.insert("SHAKE-256(912)");  // Ed448
1,416✔
1009
}
708✔
1010

1011
namespace {
1012
CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses) {
1,419✔
1013
   CertificatePathStatusCodes warnings;
1,419✔
1014
   for(const auto& status_set_i : all_statuses) {
5,315✔
1015
      std::set<Certificate_Status_Code> warning_set_i;
3,896✔
1016
      for(const auto& code : status_set_i) {
6,113✔
1017
         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
2,217✔
1018
            code < Certificate_Status_Code::FIRST_ERROR_STATUS) {
1019
            warning_set_i.insert(code);
68✔
1020
         }
1021
      }
1022
      warnings.push_back(warning_set_i);
3,896✔
1023
   }
3,896✔
1024
   return warnings;
1,419✔
1025
}
×
1026
}  // namespace
1027

1028
Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
1,419✔
1029
                                               std::vector<X509_Certificate>&& cert_chain) :
1,419✔
1030
      m_all_status(std::move(status)),
1,419✔
1031
      m_warnings(find_warnings(m_all_status)),
1,419✔
1032
      m_cert_path(std::move(cert_chain)),
1,419✔
1033
      m_overall(PKIX::overall_status(m_all_status)) {}
1,419✔
1034

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

1043
   return m_cert_path[m_cert_path.size() - 1];
10✔
1044
}
1045

1046
bool Path_Validation_Result::successful_validation() const {
1,664✔
1047
   return (result() == Certificate_Status_Code::VERIFIED || result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1,664✔
1048
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
953✔
1049
}
1050

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

1060
CertificatePathStatusCodes Path_Validation_Result::warnings() const {
42✔
1061
   return m_warnings;
42✔
1062
}
1063

1064
std::string Path_Validation_Result::result_string() const {
1,058✔
1065
   return status_string(result());
1,058✔
1066
}
1067

1068
const char* Path_Validation_Result::status_string(Certificate_Status_Code code) {
1,101✔
1069
   if(const char* s = to_string(code)) {
1,101✔
1070
      return s;
1,101✔
1071
   }
1072

1073
   return "Unknown error";
1074
}
1075

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

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

© 2025 Coveralls, Inc