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

randombit / botan / 20160266155

12 Dec 2025 08:01AM UTC coverage: 90.357% (-0.001%) from 90.358%
20160266155

push

github

web-flow
Merge pull request #5172 from randombit/jack/fix-clang-tidy-misc-const-correctness

Fix and enable clang-tidy warning misc-const-correctness

100951 of 111725 relevant lines covered (90.36%)

12817908.54 hits per line

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

93.88
/src/tests/unit_x509.cpp
1
/*
2
* (C) 2009,2019 Jack Lloyd
3
* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_X509_CERTIFICATES)
11
   #include <botan/ber_dec.h>
12
   #include <botan/der_enc.h>
13
   #include <botan/pk_algs.h>
14
   #include <botan/pkcs10.h>
15
   #include <botan/pkcs8.h>
16
   #include <botan/x509_ca.h>
17
   #include <botan/x509_ext.h>
18
   #include <botan/x509path.h>
19
   #include <botan/x509self.h>
20
   #include <botan/internal/calendar.h>
21

22
   #if defined(BOTAN_HAS_ECC_GROUP)
23
      #include <botan/ec_group.h>
24
   #endif
25
#endif
26

27
namespace Botan_Tests {
28

29
namespace {
30

31
#if defined(BOTAN_HAS_X509_CERTIFICATES)
32

33
Botan::X509_Time from_date(const int y, const int m, const int d) {
334✔
34
   const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
334✔
35

36
   const Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
334✔
37
   return Botan::X509_Time(t.to_std_timepoint());
334✔
38
}
39

40
/* Return some option sets */
41
Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "") {
112✔
42
   Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
112✔
43

44
   opts.uri = "https://botan.randombit.net";
112✔
45
   opts.dns = "botan.randombit.net";
112✔
46
   opts.email = "testing@randombit.net";
112✔
47
   opts.set_padding_scheme(sig_padding);
112✔
48

49
   opts.CA_key(1);
112✔
50

51
   return opts;
112✔
52
}
×
53

54
Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& sig_padding = "") {
36✔
55
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
36✔
56

57
   opts.uri = "https://botan.randombit.net";
36✔
58
   opts.dns = "botan.randombit.net";
36✔
59
   opts.email = "testing@randombit.net";
36✔
60
   opts.set_padding_scheme(sig_padding);
36✔
61

62
   opts.not_before("160101200000Z");
36✔
63
   opts.not_after("300101200000Z");
36✔
64

65
   opts.challenge = "zoom";
36✔
66

67
   if(algo == "RSA") {
36✔
68
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
6✔
69
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
30✔
70
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
12✔
71
   }
72

73
   return opts;
36✔
74
}
×
75

76
Botan::X509_Cert_Options req_opts2(const std::string& sig_padding = "") {
11✔
77
   Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
11✔
78

79
   opts.uri = "https://botan.randombit.net";
11✔
80
   opts.dns = "botan.randombit.net";
11✔
81
   opts.email = "testing@randombit.net";
11✔
82
   opts.set_padding_scheme(sig_padding);
11✔
83

84
   opts.add_ex_constraint("PKIX.EmailProtection");
11✔
85

86
   return opts;
11✔
87
}
×
88

89
Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "") {
55✔
90
   Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
55✔
91

92
   opts.uri = "https://botan.randombit.net";
55✔
93
   opts.dns = "botan.randombit.net";
55✔
94
   opts.email = "testing@randombit.net";
55✔
95
   opts.set_padding_scheme(sig_padding);
55✔
96

97
   opts.more_org_units.push_back("IT");
110✔
98
   opts.more_org_units.push_back("Security");
110✔
99
   opts.more_dns.push_back("www.botan.randombit.net");
110✔
100

101
   return opts;
55✔
102
}
×
103

104
std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo, Botan::RandomNumberGenerator& rng) {
99✔
105
   const std::string params = [&] {
198✔
106
      // Here we override defaults as needed
107
      if(algo == "RSA") {
99✔
108
         return "1024";
109
      }
110
      if(algo == "GOST-34.10") {
89✔
111
   #if defined(BOTAN_HAS_ECC_GROUP)
112
         if(Botan::EC_Group::supports_named_group("gost_256A")) {
8✔
113
            return "gost_256A";
114
         }
115
   #endif
116
         return "secp256r1";
×
117
      }
118
      if(algo == "ECKCDSA" || algo == "ECGDSA") {
81✔
119
         return "brainpool256r1";
120
      }
121
      if(algo == "HSS-LMS") {
65✔
122
         return "SHA-256,HW(5,4),HW(5,4)";
123
      }
124
      if(algo == "SLH-DSA") {
57✔
125
         return "SLH-DSA-SHA2-128f";
2✔
126
      }
127
      return "";  // default "" means choose acceptable algo-specific params
128
   }();
99✔
129

130
   try {
99✔
131
      return Botan::create_private_key(algo, rng, params);
99✔
132
   } catch(Botan::Not_Implemented&) {
×
133
      return {};
×
134
   }
×
135
}
99✔
136

137
Test::Result test_cert_status_strings() {
1✔
138
   Test::Result result("Certificate_Status_Code to_string");
1✔
139

140
   std::set<std::string> seen;
1✔
141

142
   result.test_eq("Same string",
1✔
143
                  Botan::to_string(Botan::Certificate_Status_Code::OK),
144
                  Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
145

146
   const Botan::Certificate_Status_Code codes[]{
1✔
147
      Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
148
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
149
      Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
150
      Botan::Certificate_Status_Code::OCSP_NO_HTTP,
151

152
      Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
153
      Botan::Certificate_Status_Code::DN_TOO_LONG,
154

155
      Botan::Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK,
156
      Botan::Certificate_Status_Code::NO_MATCHING_CRLDP,
157
      Botan::Certificate_Status_Code::UNTRUSTED_HASH,
158
      Botan::Certificate_Status_Code::NO_REVOCATION_DATA,
159
      Botan::Certificate_Status_Code::CERT_NOT_YET_VALID,
160
      Botan::Certificate_Status_Code::CERT_HAS_EXPIRED,
161
      Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID,
162
      Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED,
163
      Botan::Certificate_Status_Code::CRL_NOT_YET_VALID,
164
      Botan::Certificate_Status_Code::CRL_HAS_EXPIRED,
165
      Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
166
      Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST,
167
      Botan::Certificate_Status_Code::CERT_CHAIN_LOOP,
168
      Botan::Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT,
169
      Botan::Certificate_Status_Code::CHAIN_NAME_MISMATCH,
170
      Botan::Certificate_Status_Code::POLICY_ERROR,
171
      Botan::Certificate_Status_Code::DUPLICATE_CERT_POLICY,
172
      Botan::Certificate_Status_Code::INVALID_USAGE,
173
      Botan::Certificate_Status_Code::CERT_CHAIN_TOO_LONG,
174
      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER,
175
      Botan::Certificate_Status_Code::NAME_CONSTRAINT_ERROR,
176
      Botan::Certificate_Status_Code::IPADDR_BLOCKS_ERROR,
177
      Botan::Certificate_Status_Code::AS_BLOCKS_ERROR,
178
      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER,
179
      Botan::Certificate_Status_Code::OCSP_CERT_NOT_LISTED,
180
      Botan::Certificate_Status_Code::OCSP_BAD_STATUS,
181
      Botan::Certificate_Status_Code::CERT_NAME_NOMATCH,
182
      Botan::Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION,
183
      Botan::Certificate_Status_Code::DUPLICATE_CERT_EXTENSION,
184
      Botan::Certificate_Status_Code::EXT_IN_V1_V2_CERT,
185
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR,
186
      Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND,
187
      Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE,
188
      Botan::Certificate_Status_Code::OCSP_RESPONSE_INVALID,
189
      Botan::Certificate_Status_Code::CERT_IS_REVOKED,
190
      Botan::Certificate_Status_Code::CRL_BAD_SIGNATURE,
191
      Botan::Certificate_Status_Code::SIGNATURE_ERROR,
192
      Botan::Certificate_Status_Code::CERT_PUBKEY_INVALID,
193
      Botan::Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN,
194
      Botan::Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS,
195
   };
196

197
   for(const auto code : codes) {
47✔
198
      const std::string s = Botan::to_string(code);
46✔
199
      result.confirm("String is long enough to be informative", s.size() > 12);
92✔
200
      result.test_eq("No duplicates", seen.count(s), 0);
46✔
201
      seen.insert(s);
46✔
202
   }
46✔
203

204
   return result;
1✔
205
}
1✔
206

207
Test::Result test_x509_extension() {
1✔
208
   Test::Result result("X509 Extensions API");
1✔
209

210
   Botan::Extensions extn;
1✔
211

212
   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
1✔
213
   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
1✔
214

215
   extn.add(std::make_unique<Botan::Cert_Extension::Basic_Constraints>(true), true);
2✔
216

217
   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
2✔
218
   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
2✔
219
   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
2✔
220
   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
2✔
221

222
   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "30060101FF020100");
2✔
223

224
   result.test_throws("Extension::get_extension_bits throws if not set", [&]() { extn.get_extension_bits(oid_skid); });
3✔
225

226
   result.test_throws("Extension::add throws on second add",
2✔
227
                      [&]() { extn.add(std::make_unique<Botan::Cert_Extension::Basic_Constraints>(false), false); });
2✔
228

229
   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "30060101FF020100");
2✔
230

231
   result.confirm("Returns false since extension already existed",
1✔
232
                  !extn.add_new(std::make_unique<Botan::Cert_Extension::Basic_Constraints>(false), false));
2✔
233

234
   result.confirm("Basic constraints is still critical", extn.critical_extension_set(oid_bc));
2✔
235

236
   extn.replace(std::make_unique<Botan::Cert_Extension::Basic_Constraints>(false), false);
2✔
237
   result.confirm("Replaced basic constraints is not critical", !extn.critical_extension_set(oid_bc));
2✔
238
   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "3000");
2✔
239

240
   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
2✔
241
   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
2✔
242
   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
2✔
243
   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
2✔
244

245
   return result;
2✔
246
}
2✔
247

248
Test::Result test_x509_dates() {
1✔
249
   Test::Result result("X509 Time");
1✔
250

251
   Botan::X509_Time time;
1✔
252
   result.confirm("unset time not set", !time.time_is_set());
2✔
253
   time = Botan::X509_Time("080201182200Z", Botan::ASN1_Type::UtcTime);
1✔
254
   result.confirm("time set after construction", time.time_is_set());
2✔
255
   result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
2✔
256

257
   time = Botan::X509_Time("200305100350Z", Botan::ASN1_Type::UtcTime);
1✔
258
   result.test_eq("UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
259

260
   time = Botan::X509_Time("200305100350Z");
1✔
261
   result.test_eq(
3✔
262
      "UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
263

264
   time = Botan::X509_Time("20200305100350Z");
1✔
265
   result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string",
3✔
266
                  time.readable_string(),
2✔
267
                  "2020/03/05 10:03:50 UTC");
268

269
   time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Type::GeneralizedTime);
1✔
270
   result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
271

272
   // Dates that are valid per X.500 but rejected as unsupported
273
   const std::string valid_but_unsup[]{
1✔
274
      "0802010000-0000",
275
      "0802011724+0000",
276
      "0406142334-0500",
277
      "9906142334+0500",
278
      "0006142334-0530",
279
      "0006142334+0530",
280

281
      "080201000000-0000",
282
      "080201172412+0000",
283
      "040614233433-0500",
284
      "990614233444+0500",
285
      "000614233455-0530",
286
      "000614233455+0530",
287
   };
13✔
288

289
   // valid length 13
290
   const std::string valid_utc[]{
1✔
291
      "080201000000Z",
292
      "080201172412Z",
293
      "040614233433Z",
294
      "990614233444Z",
295
      "000614233455Z",
296
   };
6✔
297

298
   const std::string invalid_utc[]{
1✔
299
      "",
300
      " ",
301
      "2008`02-01",
302
      "9999-02-01",
303
      "2000-02-01 17",
304
      "999921",
305

306
      // No seconds
307
      "0802010000Z",
308
      "0802011724Z",
309
      "0406142334Z",
310
      "9906142334Z",
311
      "0006142334Z",
312

313
      // valid length 13 -> range check
314
      "080201000061Z",  // seconds too big (61)
315
      "080201000060Z",  // seconds too big (60, leap seconds not covered by the standard)
316
      "0802010000-1Z",  // seconds too small (-1)
317
      "080201006000Z",  // minutes too big (60)
318
      "080201240000Z",  // hours too big (24:00)
319

320
      // valid length 13 -> invalid numbers
321
      "08020123112 Z",
322
      "08020123112!Z",
323
      "08020123112,Z",
324
      "08020123112\nZ",
325
      "080201232 33Z",
326
      "080201232!33Z",
327
      "080201232,33Z",
328
      "080201232\n33Z",
329
      "0802012 3344Z",
330
      "0802012!3344Z",
331
      "0802012,3344Z",
332
      "08022\n334455Z",
333
      "08022 334455Z",
334
      "08022!334455Z",
335
      "08022,334455Z",
336
      "08022\n334455Z",
337
      "082 33445511Z",
338
      "082!33445511Z",
339
      "082,33445511Z",
340
      "082\n33445511Z",
341
      "2 2211221122Z",
342
      "2!2211221122Z",
343
      "2,2211221122Z",
344
      "2\n2211221122Z",
345

346
      // wrong time zone
347
      "080201000000",
348
      "080201000000z",
349

350
      // Fractional seconds
351
      "170217180154.001Z",
352

353
      // Timezone offset
354
      "170217180154+0100",
355

356
      // Extra digits
357
      "17021718015400Z",
358

359
      // Non-digits
360
      "17021718015aZ",
361

362
      // Trailing garbage
363
      "170217180154Zlongtrailinggarbage",
364

365
      // Swapped type
366
      "20170217180154Z",
367
   };
49✔
368

369
   // valid length 15
370
   const std::string valid_generalized_time[]{
1✔
371
      "20000305100350Z",
372
   };
2✔
373

374
   const std::string invalid_generalized[]{
1✔
375
      // No trailing Z
376
      "20000305100350",
377

378
      // No seconds
379
      "200003051003Z",
380

381
      // Fractional seconds
382
      "20000305100350.001Z",
383

384
      // Timezone offset
385
      "20170217180154+0100",
386

387
      // Extra digits
388
      "2017021718015400Z",
389

390
      // Non-digits
391
      "2017021718015aZ",
392

393
      // Trailing garbage
394
      "20170217180154Zlongtrailinggarbage",
395

396
      // Swapped type
397
      "170217180154Z",
398
   };
9✔
399

400
   for(const auto& v : valid_but_unsup) {
13✔
401
      result.test_throws("valid but unsupported", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
108✔
402
   }
403

404
   for(const auto& v : valid_utc) {
6✔
405
      const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
406
   }
5✔
407

408
   for(const auto& v : valid_generalized_time) {
2✔
409
      const Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
410
   }
1✔
411

412
   for(const auto& v : invalid_utc) {
49✔
413
      result.test_throws("invalid", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
432✔
414
   }
415

416
   for(const auto& v : invalid_generalized) {
9✔
417
      result.test_throws("invalid", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime); });
72✔
418
   }
419

420
   return result;
1✔
421
}
80✔
422

423
Test::Result test_x509_encode_authority_info_access_extension() {
1✔
424
   Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension");
1✔
425

426
   #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
427
   auto rng = Test::new_rng(__func__);
1✔
428

429
   const std::string sig_algo{"RSA"};
1✔
430
   const std::string hash_fn{"SHA-256"};
1✔
431
   const std::string padding_method{"PKCS1v15(SHA-256)"};
1✔
432

433
   // CA Issuer information
434
   const std::vector<std::string> ca_issuers = {
1✔
435
      "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt",
436
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?"};
1✔
437

438
   // OCSP
439
   const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"};
1✔
440

441
   // create a CA
442
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
443
   result.require("CA key", ca_key != nullptr);
1✔
444
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
445
   const Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
446

447
   // create a certificate with only caIssuer information
448
   auto key = make_a_private_key(sig_algo, *rng);
1✔
449

450
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
451
   opts1.extensions.add(std::make_unique<Botan::Cert_Extension::Authority_Information_Access>("", ca_issuers));
2✔
452

453
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
454

455
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
456

457
   if(!result.test_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
458
      return result;
459
   }
460

461
   for(const auto& ca_issuer : cert.ca_issuers()) {
3✔
462
      result.confirm("CA issuer URI present in certificate",
4✔
463
                     std::ranges::find(ca_issuers, ca_issuer) != ca_issuers.end());
4✔
464
   }
1✔
465

466
   result.confirm("no OCSP url available", cert.ocsp_responder().empty());
2✔
467

468
   // create a certificate with only OCSP URI information
469
   Botan::X509_Cert_Options opts2 = req_opts1(sig_algo);
1✔
470
   opts2.extensions.add(std::make_unique<Botan::Cert_Extension::Authority_Information_Access>(ocsp_uri));
2✔
471

472
   req = Botan::X509::create_cert_req(opts2, *key, hash_fn, *rng);
1✔
473

474
   cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
475

476
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
477
   result.confirm("no CA Issuer URI available", cert.ca_issuers().empty());
2✔
478
   result.test_eq("OCSP responder URI matches", cert.ocsp_responder(), std::string(ocsp_uri));
3✔
479

480
   // create a certificate with OCSP URI and CA Issuer information
481
   Botan::X509_Cert_Options opts3 = req_opts1(sig_algo);
1✔
482
   opts3.extensions.add(std::make_unique<Botan::Cert_Extension::Authority_Information_Access>(ocsp_uri, ca_issuers));
2✔
483

484
   req = Botan::X509::create_cert_req(opts3, *key, hash_fn, *rng);
1✔
485

486
   cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
487

488
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
489
   result.confirm("CA Issuer URI available", !cert.ca_issuers().empty());
2✔
490
   #endif
491

492
   return result;
1✔
493
}
4✔
494

495
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
496

497
Test::Result test_crl_dn_name() {
1✔
498
   Test::Result result("CRL DN name");
1✔
499

500
      // See GH #1252
501

502
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
503
   auto rng = Test::new_rng(__func__);
1✔
504

505
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
506

507
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/opcuactt_ca.der"));
2✔
508

509
   Botan::DataSource_Stream key_input(Test::data_file("x509/misc/opcuactt_ca.pem"));
2✔
510
   auto key = Botan::PKCS8::load_key(key_input);
1✔
511
   const Botan::X509_CA ca(cert, *key, "SHA-256", *rng);
1✔
512

513
   const Botan::X509_CRL crl = ca.new_crl(*rng);
1✔
514

515
   result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
2✔
516

517
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
3✔
518
      #endif
519

520
   return result;
2✔
521
}
3✔
522

523
Test::Result test_rdn_multielement_set_name() {
1✔
524
   Test::Result result("DN with multiple elements in RDN");
1✔
525

526
   // GH #2611
527

528
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/rdn_set.crt"));
2✔
529

530
   result.confirm("issuer DN contains expected name components", cert.issuer_dn().get_attributes().size() == 4);
2✔
531
   result.confirm("subject DN contains expected name components", cert.subject_dn().get_attributes().size() == 4);
2✔
532

533
   return result;
1✔
534
}
1✔
535

536
Test::Result test_rsa_oaep() {
1✔
537
   Test::Result result("RSA OAEP decoding");
1✔
538

539
      #if defined(BOTAN_HAS_RSA)
540
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
541

542
   auto public_key = cert.subject_public_key();
1✔
543
   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
1✔
544
   const auto& pk_info = cert.subject_public_key_algo();
1✔
545

546
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
547
      #endif
548

549
   return result;
2✔
550
}
1✔
551

552
Test::Result test_x509_decode_list() {
1✔
553
   Test::Result result("X509_Certificate list decode");
1✔
554

555
   Botan::DataSource_Stream input(Test::data_file("x509/misc/cert_seq.der"), true);
2✔
556

557
   Botan::BER_Decoder dec(input);
1✔
558
   std::vector<Botan::X509_Certificate> certs;
1✔
559
   dec.decode_list(certs);
1✔
560

561
   result.test_eq("Expected number of certs in list", certs.size(), 2);
1✔
562

563
   result.test_eq("Expected cert 1 CN", certs[0].subject_dn().get_first_attribute("CN"), "CA1-PP.01.02");
2✔
564
   result.test_eq("Expected cert 2 CN", certs[1].subject_dn().get_first_attribute("CN"), "User1-PP.01.02");
2✔
565

566
   return result;
1✔
567
}
1✔
568

569
Test::Result test_x509_utf8() {
1✔
570
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
571

572
   try {
1✔
573
      const Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
574

575
      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
576
      const std::string organization =
1✔
577
         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
578
         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
1✔
579
      const std::string organization_unit =
1✔
580
         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
581
         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
1✔
582
      const std::string common_name =
1✔
583
         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
584
         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
1✔
585
      const std::string location = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
586

587
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
588

589
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
590
      result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
2✔
591
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
592
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
593
   } catch(const Botan::Decoding_Error& ex) {
1✔
594
      result.test_failure(ex.what());
×
595
   }
×
596

597
   return result;
1✔
598
}
×
599

600
Test::Result test_x509_bmpstring() {
1✔
601
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
602

603
   try {
1✔
604
      const Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
605

606
      // UTF-8 encoded fields of test certificate (contains cyrillic and greek letters)
607
      const std::string organization = "\x6E\x65\xCF\x87\xCF\xB5\x6E\x69\xCF\x89";
1✔
608
      const std::string common_name =
1✔
609
         "\xC3\xA8\x6E\xC7\x9D\xD0\xAF\x20\xD0\x9C\xC7\x9D\xD0\xB9\xD0\xB7\xD1\x8D\xD0\xBB";
1✔
610

611
      // UTF-8 encoded fields of test certificate (contains only ASCII characters)
612
      const std::string location = "Berlin";
1✔
613

614
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
615

616
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
617
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
618
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
619
   } catch(const Botan::Decoding_Error& ex) {
1✔
620
      result.test_failure(ex.what());
×
621
   }
×
622

623
   return result;
1✔
624
}
×
625

626
Test::Result test_x509_teletex() {
1✔
627
   Test::Result result("X509 with TeletexString encoded fields");
1✔
628

629
   try {
1✔
630
      const Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
631

632
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
633

634
      const std::string common_name = "neam Gesellschaft f\xc3\xbcr Kommunikationsl\xc3\xb6sungen mbH";
1✔
635

636
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
637
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
638
   } catch(const Botan::Decoding_Error& ex) {
1✔
639
      result.test_failure(ex.what());
×
640
   }
×
641

642
   return result;
1✔
643
}
×
644

645
Test::Result test_x509_authority_info_access_extension() {
1✔
646
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
647

648
   // contains no AIA extension
649
   const Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
650

651
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
652
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
653

654
   // contains AIA extension with 1 CA issuer URL and 1 OCSP responder
655
   const Botan::X509_Certificate aia_cert(Test::data_file("x509/misc/contains_authority_info_access.pem"));
2✔
656

657
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
658

659
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
660
   if(result.tests_failed() > 0) {
1✔
661
      return result;
662
   }
663

664
   result.test_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
2✔
665
   result.test_eq("OCSP responder URL matches", aia_cert.ocsp_responder(), "http://gp.symcd.com");
2✔
666

667
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
668
   const Botan::X509_Certificate aia_cert_2ca(
1✔
669
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
670

671
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
672

673
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
674
   if(result.tests_failed() > 0) {
1✔
675
      return result;
676
   }
677

678
   result.test_eq(
2✔
679
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
680
   result.test_eq(
2✔
681
      "CA issuer URL matches",
682
      ca_issuers2[1],
1✔
683
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
684
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
2✔
685

686
   return result;
1✔
687
}
1✔
688

689
Test::Result test_parse_rsa_pss_cert() {
1✔
690
   Test::Result result("X509 RSA-PSS certificate");
1✔
691

692
   // See https://github.com/randombit/botan/issues/3019 for background
693

694
   try {
1✔
695
      const Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
696
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
697
   } catch(Botan::Exception& e) {
1✔
698
      result.test_failure("Parsing failed", e.what());
×
699
   }
×
700

701
   return result;
1✔
702
}
×
703

704
Test::Result test_verify_gost2012_cert() {
1✔
705
   Test::Result result("X509 GOST-2012 certificates");
1✔
706

707
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
708
   try {
1✔
709
      if(Botan::EC_Group::supports_named_group("gost_256A")) {
1✔
710
         const Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
711
         const Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
712

713
         Botan::Certificate_Store_In_Memory trusted;
1✔
714
         trusted.add_certificate(root_cert);
1✔
715

716
         const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
2✔
717
         const Botan::Path_Validation_Result validation_result =
1✔
718
            Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
719

720
         result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
721
      }
1✔
722
   } catch(const Botan::Decoding_Error& e) {
×
723
      result.test_failure(e.what());
×
724
   }
×
725
      #endif
726

727
   return result;
1✔
728
}
×
729

730
   /*
731
 * @brief checks the configurability of the RSA-PSS signature scheme
732
 *
733
 * For the other algorithms than RSA, only one padding is supported right now.
734
 */
735
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
736
Test::Result test_padding_config() {
1✔
737
   Test::Result test_result("X509 Padding Config");
1✔
738

739
   auto rng = Test::new_rng(__func__);
1✔
740

741
   Botan::DataSource_Stream key_stream(Test::data_file("x509/misc/rsa_key.pem"));
2✔
742
   auto sk = Botan::PKCS8::load_key(key_stream);
1✔
743

744
   // Create X509 CA certificate; PKCS1v15 is used for signing by default
745
   Botan::X509_Cert_Options opt("TEST CA");
1✔
746
   opt.CA_key();
1✔
747

748
   const Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
749
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
750
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
751
                       "RSA/PKCS1v15(SHA-512)");
752

753
   // Create X509 CA certificate; RSA-PSS is explicitly set
754
   opt.set_padding_scheme("PSSR");
1✔
755
   const Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
756
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
757
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
758
                       "RSA/PSS");
759

760
         #if defined(BOTAN_HAS_EMSA_X931)
761
   // Try to set a padding scheme that is not supported for signing with the given key type
762
   opt.set_padding_scheme("X9.31");
1✔
763
   try {
1✔
764
      const Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
765
      test_result.test_failure("Could build CA cert with invalid encoding scheme X9.31 for key type " +
×
766
                               sk->algo_name());
×
767
   } catch(const Botan::Invalid_Argument& e) {
1✔
768
      test_result.test_eq("Build CA certificate with invalid encoding scheme X9.31 for key type " + sk->algo_name(),
3✔
769
                          e.what(),
1✔
770
                          "Signatures using RSA/X9.31(SHA-512) are not supported");
771
   }
1✔
772
         #endif
773

774
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
775
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
776
                       "RSA/PSS");
777

778
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
779
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
780

781
   // Prepare a signing request for the end certificate
782
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
783
   req_opt.set_padding_scheme("PSS(SHA-512,MGF1,64)");
1✔
784
   const Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng);
1✔
785
   test_result.test_eq(
3✔
786
      "Certificate request signature algorithm", end_req.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
787

788
   // Create X509 CA object: will fail as the chosen hash functions differ
789
   try {
1✔
790
      const Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "PSS(SHA-256)", *rng);
1✔
791
      test_result.test_failure("Configured conflicting hash functions for CA");
×
792
   } catch(const Botan::Invalid_Argument& e) {
1✔
793
      test_result.test_eq(
1✔
794
         "Configured conflicting hash functions for CA",
795
         e.what(),
1✔
796
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding PSS(SHA-256)");
797
   }
1✔
798

799
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. PKCS1v15
800
   const Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
801
   const Botan::X509_Certificate end_cert_pkcs1 = ca_def.sign_request(end_req, *rng, not_before, not_after);
1✔
802
   test_result.test_eq("End certificate signature algorithm",
3✔
803
                       end_cert_pkcs1.signature_algorithm().oid().to_formatted_string(),
3✔
804
                       "RSA/PKCS1v15(SHA-512)");
805

806
   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
807
   const Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "PSS", *rng);
1✔
808
   const Botan::X509_Certificate end_cert_diff_pss = ca_diff.sign_request(end_req, *rng, not_before, not_after);
1✔
809
   test_result.test_eq("End certificate signature algorithm",
3✔
810
                       end_cert_diff_pss.signature_algorithm().oid().to_formatted_string(),
2✔
811
                       "RSA/PSS");
812

813
   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
814
   const Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "PSS(SHA-512,MGF1,64)", *rng);
1✔
815
   const Botan::X509_Certificate end_cert_pss = ca_exp.sign_request(end_req, *rng, not_before, not_after);
1✔
816
   test_result.test_eq(
3✔
817
      "End certificate signature algorithm", end_cert_pss.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
818

819
   // Check CRL signature algorithm
820
   const Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
821
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
822

823
   // sanity check for verification, the heavy lifting is done in the other unit tests
824
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
825
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
826
   const Botan::Path_Validation_Result validation_result =
1✔
827
      Botan::x509_path_validate(end_cert_pss, restrictions, trusted);
1✔
828
   test_result.confirm("PSS signed certificate validates", validation_result.successful_validation());
2✔
829

830
   return test_result;
2✔
831
}
3✔
832
      #endif
833

834
   #endif
835

836
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
11✔
837
                             const std::string& sig_padding,
838
                             const std::string& hash_fn,
839
                             Botan::RandomNumberGenerator& rng) {
840
   Test::Result result("PKCS10 extensions");
11✔
841

842
   Botan::X509_Cert_Options opts;
11✔
843

844
   opts.dns = "main.example.org";
11✔
845
   opts.more_dns.push_back("more1.example.org");
22✔
846
   opts.more_dns.push_back("more2.example.org");
22✔
847

848
   opts.padding_scheme = sig_padding;
11✔
849

850
   Botan::AlternativeName alt_name;
11✔
851
   alt_name.add_attribute("DNS", "bonus.example.org");
11✔
852

853
   Botan::X509_DN alt_dn;
11✔
854
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
11✔
855
   alt_dn.add_attribute("X520.Organization", "testing");
11✔
856
   alt_name.add_dn(alt_dn);
11✔
857

858
   opts.extensions.add(std::make_unique<Botan::Cert_Extension::Subject_Alternative_Name>(alt_name));
22✔
859

860
   const auto req = Botan::X509::create_cert_req(opts, key, hash_fn, rng);
11✔
861

862
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
11✔
863

864
   result.test_eq("Expected number of DNS names", alt_dns_names.size(), 4);
11✔
865

866
   if(alt_dns_names.size() == 4) {
11✔
867
      result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
33✔
868
      result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
33✔
869
      result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
33✔
870
      result.test_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
33✔
871
   }
872

873
   result.test_eq("Expected number of alt DNs", req.subject_alt_name().directory_names().size(), 1);
11✔
874
   result.confirm("Alt DN is correct", *req.subject_alt_name().directory_names().begin() == alt_dn);
22✔
875

876
   return result;
11✔
877
}
11✔
878

879
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
11✔
880
                            const std::string& sig_algo,
881
                            const std::string& sig_padding,
882
                            const std::string& hash_fn,
883
                            Botan::RandomNumberGenerator& rng) {
884
   Test::Result result("X509 Unit");
11✔
885

886
   /* Create the self-signed cert */
887
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
888

889
   {
11✔
890
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
22✔
891
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
22✔
892
   }
893

894
   /* Create user #1's key and cert request */
895
   auto user1_key = make_a_private_key(sig_algo, rng);
11✔
896

897
   const Botan::PKCS10_Request user1_req =
11✔
898
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
11✔
899

900
   result.test_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom");
22✔
901

902
   /* Create user #2's key and cert request */
903
   auto user2_key = make_a_private_key(sig_algo, rng);
11✔
904

905
   const Botan::PKCS10_Request user2_req =
11✔
906
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
11✔
907

908
   // /* Create user #3's key and cert request */
909
   auto user3_key = make_a_private_key(sig_algo, rng);
11✔
910

911
   const Botan::PKCS10_Request user3_req =
11✔
912
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
11✔
913

914
   /* Create the CA object */
915
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
916

917
   const BigInt user1_serial(99);
11✔
918

919
   /* Sign the requests to create the certs */
920
   const Botan::X509_Certificate user1_cert =
11✔
921
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
922

923
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
11✔
924
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
11✔
925

926
   const Botan::X509_Certificate user2_cert =
11✔
927
      ca.sign_request(user2_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
928
   result.test_eq("extended key usage is set", user2_cert.has_ex_constraint("PKIX.EmailProtection"), true);
11✔
929

930
   const Botan::X509_Certificate user3_cert =
11✔
931
      ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
932

933
   // user#1 creates a self-signed cert on the side
934
   const auto user1_ss_cert =
11✔
935
      Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
11✔
936

937
   {
11✔
938
      auto constraints = req_opts1(sig_algo).constraints;
11✔
939
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
22✔
940
   }
941

942
   /* Copy, assign and compare */
943
   Botan::X509_Certificate user1_cert_copy(user1_cert);
11✔
944
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
11✔
945

946
   user1_cert_copy = user2_cert;
11✔
947
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
11✔
948

949
   const Botan::X509_Certificate user1_cert_differ =
11✔
950
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
951

952
   result.test_eq("certificate differs", user1_cert == user1_cert_differ, false);
11✔
953

954
   /* Get cert data */
955
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
11✔
956

957
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
11✔
958
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
22✔
959
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
22✔
960
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
22✔
961
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
22✔
962

963
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
11✔
964
   result.test_eq("subject OrgaUnit count",
11✔
965
                  user3_subject_dn.get_attribute("OU").size(),
22✔
966
                  req_opts3(sig_algo).more_org_units.size() + 1);
22✔
967
   result.test_eq(
11✔
968
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
44✔
969

970
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
11✔
971
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
22✔
972
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
22✔
973
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
22✔
974

975
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
11✔
976
   result.test_eq(
11✔
977
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
22✔
978
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
44✔
979

980
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
11✔
981

982
   /* Verify the certs */
983
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
984
   Botan::Certificate_Store_In_Memory store;
11✔
985

986
   // First try with an empty store
987
   const Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
988
   result.test_eq("user 1 issuer not found",
33✔
989
                  result_no_issuer.result_string(),
22✔
990
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
991

992
   store.add_certificate(ca.ca_certificate());
11✔
993

994
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
995
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
22✔
996
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
997
   }
998

999
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1000
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
22✔
1001
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
1002
   }
1003

1004
   const Botan::Path_Validation_Result result_self_signed =
11✔
1005
      Botan::x509_path_validate(user1_ss_cert, restrictions, store);
11✔
1006
   result.test_eq("user 1 issuer not found",
33✔
1007
                  result_no_issuer.result_string(),
22✔
1008
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1009
   store.add_crl(crl1);
11✔
1010

1011
   std::vector<Botan::CRL_Entry> revoked;
11✔
1012
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1013
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1014

1015
   const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, rng);
11✔
1016

1017
   store.add_crl(crl2);
11✔
1018

1019
   const std::string revoked_str =
11✔
1020
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1021

1022
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1023
   result.test_eq("user 1 revoked", result_u1.result_string(), revoked_str);
22✔
1024

1025
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1026
   result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
22✔
1027

1028
   revoked.clear();
11✔
1029
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
22✔
1030
   const Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
11✔
1031

1032
   store.add_crl(crl3);
11✔
1033

1034
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1035
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
22✔
1036
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1037
   }
1038

1039
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1040
   result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
22✔
1041

1042
   return result;
11✔
1043
}
44✔
1044

1045
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1046
                        const std::string& sig_algo,
1047
                        const std::string& hash_fn,
1048
                        Botan::RandomNumberGenerator& rng) {
1049
   using Botan::Key_Constraints;
12✔
1050
   using Botan::Usage_Type;
12✔
1051

1052
   Test::Result result("X509 Usage");
12✔
1053

1054
   /* Create the self-signed cert */
1055
   const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), ca_key, hash_fn, rng);
12✔
1056

1057
   /* Create the CA object */
1058
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1059

1060
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1061

1062
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1063
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1064

1065
   const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng);
12✔
1066

1067
   const Botan::X509_Certificate user1_cert =
12✔
1068
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1069

1070
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1071
   result.test_eq(
12✔
1072
      "key usage cRLSign not allowed",
1073
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1074
      false);
1075
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1076

1077
   // cert only allows digitalSignature, so checking for only that should be ok
1078
   result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1079

1080
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1081

1082
   const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng);
12✔
1083

1084
   const Botan::X509_Certificate mult_usage_cert =
12✔
1085
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1086

1087
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1088
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1089
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1090
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1091
   result.confirm(
24✔
1092
      "key usage multiple digitalSignature and cRLSign allowed",
1093
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1094
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1095

1096
   opts.constraints = Key_Constraints();
12✔
1097

1098
   const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng);
12✔
1099

1100
   const Botan::X509_Certificate no_usage_cert =
12✔
1101
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1102

1103
   // cert allows every usage
1104
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1105
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1106
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1107

1108
   if(sig_algo == "RSA") {
12✔
1109
      // cert allows data encryption
1110
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1111

1112
      const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng);
1✔
1113

1114
      const Botan::X509_Certificate enc_cert =
1✔
1115
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1116

1117
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1118
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1119
   }
1✔
1120

1121
   return result;
12✔
1122
}
24✔
1123

1124
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
11✔
1125
                              const std::string& sig_algo,
1126
                              const std::string& sig_padding,
1127
                              const std::string& hash_fn,
1128
                              Botan::RandomNumberGenerator& rng) {
1129
   using Botan::Key_Constraints;
11✔
1130

1131
   Test::Result result("X509 Self Issued");
11✔
1132

1133
   // create the self-signed cert
1134
   const Botan::X509_Certificate ca_cert =
11✔
1135
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1136

1137
   /* Create the CA object */
1138
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1139

1140
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1141

1142
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1143
   // but signed by a CA, not signed by it's own private key
1144
   Botan::X509_Cert_Options opts = ca_opts();
11✔
1145
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1146
   opts.set_padding_scheme(sig_padding);
11✔
1147

1148
   const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng);
11✔
1149

1150
   const Botan::X509_Certificate self_issued_cert =
11✔
1151
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1152

1153
   // check that this chain can can be verified successfully
1154
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
11✔
1155

1156
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1157

1158
   const Botan::Path_Validation_Result validation_result =
11✔
1159
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
11✔
1160

1161
   result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
22✔
1162

1163
   return result;
11✔
1164
}
22✔
1165

1166
Test::Result test_x509_uninit() {
1✔
1167
   Test::Result result("X509 object uninitialized access");
1✔
1168

1169
   Botan::X509_Certificate cert;
1✔
1170
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1171
      cert.x509_version();
1✔
1172
   });
1173

1174
   Botan::X509_CRL crl;
1✔
1175
   result.test_throws(
2✔
1176
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1177

1178
   return result;
1✔
1179
}
1✔
1180

1181
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1182
   using Botan::Key_Constraints;
19✔
1183

1184
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1185

1186
   result.confirm("empty constraints always acceptable", Key_Constraints().compatible_with(key));
38✔
1187

1188
   // Now check some typical usage scenarios for the given key type
1189
   // Taken from RFC 5280, sec. 4.2.1.3
1190
   // ALL constraints are not typical at all, but we use them for a negative test
1191
   const auto all = Key_Constraints(
19✔
1192
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1193
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1194
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1195

1196
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1197
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1198
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1199
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1200
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1201
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1202
   const auto key_agreement_encipher_only =
19✔
1203
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1204
   const auto key_agreement_decipher_only =
19✔
1205
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1206
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1207
   const auto sign_everything =
19✔
1208
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1209

1210
   if(pk_algo == "DH" || pk_algo == "ECDH") {
19✔
1211
      // DH and ECDH only for key agreement
1212
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1213
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1214
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1215
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1216
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1217
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1218
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1219
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1220
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1221
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1222
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1223
   } else if(pk_algo == "Kyber" || pk_algo == "FrodoKEM" || pk_algo == "ML-KEM" || pk_algo == "ClassicMcEliece") {
17✔
1224
      // KEMs can encrypt and agree
1225
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
4✔
1226
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
4✔
1227
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
4✔
1228
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
4✔
1229
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
4✔
1230
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1231
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
4✔
1232
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), false);
4✔
1233
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
8✔
1234
   } else if(pk_algo == "RSA") {
13✔
1235
      // RSA can do everything except key agreement
1236
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1237

1238
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1239
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1240
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1241
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1242
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1243
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1244
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1245
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1246
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1247
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1248
   } else if(pk_algo == "ElGamal") {
12✔
1249
      // only ElGamal encryption is currently implemented
1250
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1251
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1252
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1253
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1254
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1255
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1256
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1257
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1258
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1259
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
10✔
1260
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" ||
18✔
1261
             pk_algo == "HSS-LMS") {
3✔
1262
      // these are signature algorithms only
1263
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
9✔
1264

1265
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1266
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1267
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1268
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1269
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1270
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1271
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1272
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1273
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1274
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1275
   }
1276

1277
   return result;
19✔
1278
}
×
1279

1280
/**
1281
 * @brief X.509v3 extension that encodes a given string
1282
 */
1283
class String_Extension final : public Botan::Certificate_Extension {
22✔
1284
   public:
1285
      String_Extension() = default;
22✔
1286

1287
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1288

1289
      std::string value() const { return m_contents; }
44✔
1290

1291
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1292
         return std::make_unique<String_Extension>(m_contents);
×
1293
      }
1294

1295
      Botan::OID oid_of() const override { return Botan::OID("1.2.3.4.5.6.7.8.9.1"); }
22✔
1296

1297
      bool should_encode() const override { return true; }
22✔
1298

1299
      std::string oid_name() const override { return "String Extension"; }
×
1300

1301
      std::vector<uint8_t> encode_inner() const override {
11✔
1302
         std::vector<uint8_t> bits;
11✔
1303
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
11✔
1304
         return bits;
11✔
1305
      }
×
1306

1307
      void decode_inner(const std::vector<uint8_t>& in) override {
22✔
1308
         Botan::ASN1_String str;
22✔
1309
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
22✔
1310
         m_contents = str.value();
22✔
1311
      }
22✔
1312

1313
   private:
1314
      std::string m_contents;
1315
};
1316

1317
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
11✔
1318
                                 const std::string& sig_algo,
1319
                                 const std::string& sig_padding,
1320
                                 const std::string& hash_fn,
1321
                                 Botan::RandomNumberGenerator& rng) {
1322
   Test::Result result("X509 Custom DN");
11✔
1323

1324
   /* Create the self-signed cert */
1325
   const Botan::X509_Certificate ca_cert =
11✔
1326
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1327

1328
   /* Create the CA object */
1329
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1330

1331
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1332

1333
   Botan::X509_DN subject_dn;
11✔
1334

1335
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
11✔
1336
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
11✔
1337
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
11✔
1338
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
11✔
1339

1340
   subject_dn.add_attribute(attr1, val1);
11✔
1341
   subject_dn.add_attribute(attr2, val2);
11✔
1342

1343
   const Botan::Extensions extensions;
11✔
1344

1345
   const Botan::PKCS10_Request req =
11✔
1346
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
11✔
1347

1348
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1349

1350
   result.test_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
11✔
1351

1352
   const Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
11✔
1353
   const Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
11✔
1354
   result.confirm("Attr1 matches encoded", req_val1 == val1);
22✔
1355
   result.confirm("Attr2 matches encoded", req_val2 == val2);
22✔
1356
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
22✔
1357
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
22✔
1358

1359
   const Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1360
   const Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1361

1362
   auto cert = ca.sign_request(req, rng, not_before, not_after);
11✔
1363

1364
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1365

1366
   result.test_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
11✔
1367

1368
   const Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
11✔
1369
   const Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
11✔
1370
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
22✔
1371
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
22✔
1372
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
22✔
1373
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
22✔
1374

1375
   return result;
11✔
1376
}
33✔
1377

1378
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
11✔
1379
                                  const std::string& sig_algo,
1380
                                  const std::string& sig_padding,
1381
                                  const std::string& hash_fn,
1382
                                  Botan::RandomNumberGenerator& rng) {
1383
   using Botan::Key_Constraints;
11✔
1384

1385
   Test::Result result("X509 Extensions");
11✔
1386

1387
   /* Create the self-signed cert */
1388
   const Botan::X509_Certificate ca_cert =
11✔
1389
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1390

1391
   /* Create the CA object */
1392
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1393

1394
   /* Prepare CDP extension */
1395
   std::vector<std::string> cdp_urls = {
11✔
1396
      "http://example.com/crl1.pem",
1397
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
11✔
1398

1399
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1400

1401
   for(const auto& uri : cdp_urls) {
33✔
1402
      Botan::AlternativeName cdp_alt_name;
22✔
1403
      cdp_alt_name.add_uri(uri);
22✔
1404
      const Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
22✔
1405

1406
      dps.emplace_back(dp);
22✔
1407
   }
22✔
1408

1409
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1410

1411
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
11✔
1412
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1413

1414
   // include a custom extension in the request
1415
   Botan::Extensions req_extensions;
11✔
1416
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
11✔
1417
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
11✔
1418
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
22✔
1419
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
22✔
1420
   opts.extensions = req_extensions;
11✔
1421
   opts.set_padding_scheme(sig_padding);
11✔
1422

1423
   /* Create a self-signed certificate */
1424
   const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, rng);
11✔
1425

1426
   result.confirm("Extensions::extension_set true for Key_Usage",
22✔
1427
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1428

1429
   // check if known Key_Usage extension is present in self-signed cert
1430
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
11✔
1431
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
22✔
1432
      result.confirm(
22✔
1433
         "Key_Usage extension value matches in self-signed certificate",
1434
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
11✔
1435
   }
1436

1437
   // check if custom extension is present in self-signed cert
1438
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
11✔
1439
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
22✔
1440
      result.test_eq(
44✔
1441
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1442
   }
1443

1444
   // check if CDPs are present in the self-signed cert
1445
   const auto* cert_cdps =
11✔
1446
      self_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
22✔
1447

1448
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
22✔
1449
                     !cert_cdps->crl_distribution_urls().empty())) {
11✔
1450
      for(const auto& cdp : cert_cdps->distribution_points()) {
33✔
1451
         result.confirm("CDP URI present in self-signed certificate",
44✔
1452
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
66✔
1453
      }
1454
   }
1455

1456
   const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng);
11✔
1457

1458
   /* Create a CA-signed certificate */
1459
   const Botan::X509_Certificate ca_signed_cert =
11✔
1460
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1461

1462
   // check if known Key_Usage extension is present in CA-signed cert
1463
   result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
22✔
1464

1465
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
22✔
1466
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
22✔
1467
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
11✔
1468
      result.confirm("Key_Usage extension value matches in user certificate",
22✔
1469
                     constraints == Botan::Key_Constraints::DigitalSignature);
11✔
1470
   }
1471

1472
   // check if custom extension is present in CA-signed cert
1473
   result.confirm("Extensions::extension_set true for String_Extension",
22✔
1474
                  ca_signed_cert.v3_extensions().extension_set(oid));
11✔
1475
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
22✔
1476
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
22✔
1477
      result.test_eq(
44✔
1478
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1479
   }
1480

1481
   // check if CDPs are present in the CA-signed cert
1482
   cert_cdps = ca_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
22✔
1483

1484
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
22✔
1485
                     !cert_cdps->crl_distribution_urls().empty())) {
11✔
1486
      for(const auto& cdp : cert_cdps->distribution_points()) {
33✔
1487
         result.confirm("CDP URI present in self-signed certificate",
44✔
1488
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
66✔
1489
      }
1490
   }
1491

1492
   return result;
11✔
1493
}
55✔
1494

1495
Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn, Botan::RandomNumberGenerator& rng) {
12✔
1496
   Test::Result result("X509 Hashes");
12✔
1497

1498
   struct TestData {
12✔
1499
         const std::string issuer, subject, issuer_hash, subject_hash;
1500
   } const cases[]{{"",
12✔
1501
                    "",
1502
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1503
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1504
                   {"a",
1505
                    "b",
1506
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1507
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1508
                   {"A",
1509
                    "B",
1510
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1511
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1512
                   {
1513
                      "Test Issuer/US/Botan Project/Testing",
1514
                      "Test Subject/US/Botan Project/Testing",
1515
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1516
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1517
                   },
1518
                   {
1519
                      "Test Subject/US/Botan Project/Testing",
1520
                      "Test Issuer/US/Botan Project/Testing",
1521
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1522
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1523
                   }};
72✔
1524

1525
   for(const auto& a : cases) {
72✔
1526
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1527
      opts.CA_key();
60✔
1528

1529
      const Botan::X509_Certificate issuer_cert = Botan::X509::create_self_signed_cert(opts, key, hash_fn, rng);
60✔
1530

1531
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1532
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1533

1534
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1535
      const Botan::PKCS10_Request req =
60✔
1536
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1537
      const Botan::X509_Certificate subject_cert =
60✔
1538
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1539

1540
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1541
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1542
   }
60✔
1543
   return result;
12✔
1544
}
72✔
1545

1546
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1547

1548
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1549
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1550

1551
      asn1=SEQUENCE:tn_auth_list
1552

1553
      [tn_auth_list]
1554
      spc=EXP:0,IA5:1001
1555
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1556
      one=EXP:2,IA5:333
1557

1558
      [TelephoneNumberRange]
1559
      start1=IA5:111
1560
      count1=INT:128
1561
      start2=IA5:222
1562
      count2=INT:256
1563
    */
1564
   const std::string filename("TNAuthList.pem");
1✔
1565
   Test::Result result("X509 TNAuthList decode");
1✔
1566
   result.start_timer();
1✔
1567

1568
   const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1569

1570
   using Botan::Cert_Extension::TNAuthList;
1✔
1571

1572
   const auto* tn_auth_list = cert.v3_extensions().get_extension_object_as<TNAuthList>();
1✔
1573

1574
   const auto& tn_entries = tn_auth_list->entries();
1✔
1575

1576
   result.confirm("cert has TNAuthList extension", tn_auth_list != nullptr, true);
2✔
1577

1578
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1579
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1580
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1581
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1582
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1583
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1584

1585
   result.test_eq("spc entry type", tn_entries[0].type() == TNAuthList::Entry::ServiceProviderCode, true);
1✔
1586
   result.test_eq("spc entry data", tn_entries[0].service_provider_code(), "1001");
2✔
1587

1588
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1589
   const auto& range = tn_entries[1].telephone_number_range();
1✔
1590
   result.test_eq("range entries count", range.size(), 2);
1✔
1591
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1592
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1593
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1594
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1595

1596
   result.test_eq("one entry type", tn_entries[2].type() == TNAuthList::Entry::TelephoneNumber, true);
1✔
1597
   result.test_eq("one entry data", tn_entries[2].telephone_number(), "333");
2✔
1598

1599
   result.end_timer();
1✔
1600
   return result;
2✔
1601
}
1✔
1602

1603
   #endif
1604

1605
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
1606
   if(sig_algo == "RSA") {
12✔
1607
      return {
1✔
1608
   #if defined(BOTAN_HAS_EMSA_PKCS1)
1609
         "PKCS1v15(" + hash + ")",
1✔
1610
   #endif
1611
   #if defined(BOTAN_HAS_EMSA_PSS)
1612
            "PSS(" + hash + ")",
1613
   #endif
1614
      };
3✔
1615
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
1616
             sig_algo == "GOST-34.10") {
7✔
1617
      return {hash};
10✔
1618
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
1619
      return {"Pure"};
2✔
1620
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
1621
      return {"Randomized"};
2✔
1622
   } else if(sig_algo == "HSS-LMS") {
2✔
1623
      return {""};
1✔
1624
   } else {
1625
      return {};
1✔
1626
   }
1627
}
6✔
1628

1629
class X509_Cert_Unit_Tests final : public Test {
×
1630
   public:
1631
      std::vector<Test::Result> run() override {
1✔
1632
         std::vector<Test::Result> results;
1✔
1633

1634
         auto& rng = this->rng();
1✔
1635

1636
         const std::string sig_algos[]{"RSA",
1✔
1637
                                       "DSA",
1638
                                       "ECDSA",
1639
                                       "ECGDSA",
1640
                                       "ECKCDSA",
1641
                                       "GOST-34.10",
1642
                                       "Ed25519",
1643
                                       "Ed448",
1644
                                       "Dilithium",
1645
                                       "ML-DSA",
1646
                                       "SLH-DSA",
1647
                                       "HSS-LMS"};
13✔
1648

1649
         for(const std::string& algo : sig_algos) {
13✔
1650
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1651
            if(algo == "RSA")
1652
               continue;
1653
   #endif
1654

1655
            std::string hash = "SHA-256";
12✔
1656

1657
            if(algo == "Ed25519") {
12✔
1658
               hash = "SHA-512";
1✔
1659
            }
1660
            if(algo == "Ed448") {
12✔
1661
               hash = "SHAKE-256(912)";
1✔
1662
            }
1663
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1664
               hash = "SHAKE-256(512)";
2✔
1665
            }
1666

1667
            auto key = make_a_private_key(algo, rng);
12✔
1668

1669
            if(key == nullptr) {
12✔
1670
               continue;
×
1671
            }
1672

1673
            results.push_back(test_hashes(*key, hash, rng));
24✔
1674
            results.push_back(test_valid_constraints(*key, algo));
24✔
1675

1676
            Test::Result usage_result("X509 Usage");
12✔
1677
            try {
12✔
1678
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1679
            } catch(std::exception& e) {
×
1680
               usage_result.test_failure("test_usage " + algo, e.what());
×
1681
            }
×
1682
            results.push_back(usage_result);
12✔
1683

1684
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
23✔
1685
               Test::Result cert_result("X509 Unit");
11✔
1686

1687
               try {
11✔
1688
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
11✔
1689
               } catch(std::exception& e) {
×
1690
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1691
               }
×
1692
               results.push_back(cert_result);
11✔
1693

1694
               Test::Result pkcs10_result("PKCS10 extensions");
11✔
1695
               try {
11✔
1696
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
11✔
1697
               } catch(std::exception& e) {
×
1698
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
1699
               }
×
1700
               results.push_back(pkcs10_result);
11✔
1701

1702
               Test::Result self_issued_result("X509 Self Issued");
11✔
1703
               try {
11✔
1704
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
11✔
1705
               } catch(std::exception& e) {
×
1706
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
1707
               }
×
1708
               results.push_back(self_issued_result);
11✔
1709

1710
               Test::Result extensions_result("X509 Extensions");
11✔
1711
               try {
11✔
1712
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
11✔
1713
               } catch(std::exception& e) {
×
1714
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
1715
               }
×
1716
               results.push_back(extensions_result);
11✔
1717

1718
               Test::Result custom_dn_result("X509 Custom DN");
11✔
1719
               try {
11✔
1720
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
11✔
1721
               } catch(std::exception& e) {
×
1722
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1723
               }
×
1724
               results.push_back(custom_dn_result);
11✔
1725
            }
23✔
1726
         }
24✔
1727

1728
         /*
1729
         These are algos which cannot sign but can be included in certs
1730
         */
1731
         const std::vector<std::string> enc_algos = {
1✔
1732
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
1733

1734
         for(const std::string& algo : enc_algos) {
8✔
1735
            auto key = make_a_private_key(algo, rng);
7✔
1736

1737
            if(key) {
7✔
1738
               results.push_back(test_valid_constraints(*key, algo));
14✔
1739
            }
1740
         }
7✔
1741

1742
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1743
      defined(BOTAN_HAS_RSA)
1744
         Test::Result pad_config_result("X509 Padding Config");
1✔
1745
         try {
1✔
1746
            pad_config_result.merge(test_padding_config());
1✔
1747
         } catch(const std::exception& e) {
×
1748
            pad_config_result.test_failure("test_padding_config", e.what());
×
1749
         }
×
1750
         results.push_back(pad_config_result);
1✔
1751
   #endif
1752

1753
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1754
         results.push_back(test_x509_utf8());
2✔
1755
         results.push_back(test_x509_bmpstring());
2✔
1756
         results.push_back(test_x509_teletex());
2✔
1757
         results.push_back(test_crl_dn_name());
2✔
1758
         results.push_back(test_rdn_multielement_set_name());
2✔
1759
         results.push_back(test_x509_decode_list());
2✔
1760
         results.push_back(test_rsa_oaep());
2✔
1761
         results.push_back(test_x509_authority_info_access_extension());
2✔
1762
         results.push_back(test_verify_gost2012_cert());
2✔
1763
         results.push_back(test_parse_rsa_pss_cert());
2✔
1764
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1765
   #endif
1766

1767
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1768
         results.push_back(test_x509_extension());
2✔
1769
         results.push_back(test_x509_dates());
2✔
1770
         results.push_back(test_cert_status_strings());
2✔
1771
         results.push_back(test_x509_uninit());
2✔
1772

1773
         return results;
1✔
1774
      }
13✔
1775
};
1776

1777
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1778

1779
#endif
1780

1781
}  // namespace
1782

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

© 2026 Coveralls, Inc