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

randombit / botan / 22020273955

14 Feb 2026 04:02PM UTC coverage: 90.059% (-0.005%) from 90.064%
22020273955

push

github

web-flow
Merge pull request #5328 from randombit/jack/test-predicates

Change Test::Result integer and bool predicates to be specifically named

102238 of 113523 relevant lines covered (90.06%)

11567860.88 hits per line

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

93.99
/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/hex.h>
14
   #include <botan/pk_algs.h>
15
   #include <botan/pkcs10.h>
16
   #include <botan/pkcs8.h>
17
   #include <botan/rng.h>
18
   #include <botan/x509_ca.h>
19
   #include <botan/x509_ext.h>
20
   #include <botan/x509path.h>
21
   #include <botan/x509self.h>
22
   #include <botan/internal/calendar.h>
23
   #include <algorithm>
24

25
   #if defined(BOTAN_HAS_ECC_GROUP)
26
      #include <botan/ec_group.h>
27
   #endif
28
#endif
29

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_X509_CERTIFICATES)
35

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

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

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

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

52
   opts.CA_key(1);
112✔
53

54
   return opts;
112✔
55
}
×
56

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

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

65
   opts.not_before("160101200000Z");
36✔
66
   opts.not_after("300101200000Z");
36✔
67

68
   opts.challenge = "zoom";
36✔
69

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

76
   return opts;
36✔
77
}
×
78

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

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

87
   opts.add_ex_constraint("PKIX.EmailProtection");
11✔
88

89
   return opts;
11✔
90
}
×
91

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

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

100
   opts.more_org_units.push_back("IT");
110✔
101
   opts.more_org_units.push_back("Security");
110✔
102
   opts.more_dns.push_back("www.botan.randombit.net");
110✔
103

104
   return opts;
55✔
105
}
×
106

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

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

140
Test::Result test_cert_status_strings() {
1✔
141
   Test::Result result("Certificate_Status_Code to_string");
1✔
142

143
   std::set<std::string> seen;
1✔
144

145
   result.test_eq("Same string",
1✔
146
                  Botan::to_string(Botan::Certificate_Status_Code::OK),
147
                  Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
148

149
   const Botan::Certificate_Status_Code codes[]{
1✔
150
      Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
151
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
152
      Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
153
      Botan::Certificate_Status_Code::OCSP_NO_HTTP,
154

155
      Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
156
      Botan::Certificate_Status_Code::DN_TOO_LONG,
157

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

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

207
   return result;
1✔
208
}
1✔
209

210
Test::Result test_x509_extension() {
1✔
211
   Test::Result result("X509 Extensions API");
1✔
212

213
   Botan::Extensions extn;
1✔
214

215
   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
1✔
216
   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
1✔
217

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

220
   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
1✔
221
   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
1✔
222
   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
1✔
223
   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
1✔
224

225
   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "30060101FF020100");
1✔
226

227
   result.test_throws("Extension::get_extension_bits throws if not set", [&]() { extn.get_extension_bits(oid_skid); });
2✔
228

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

232
   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "30060101FF020100");
1✔
233

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

237
   result.confirm("Basic constraints is still critical", extn.critical_extension_set(oid_bc));
1✔
238

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

243
   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
1✔
244
   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
1✔
245
   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
1✔
246
   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
1✔
247

248
   return result;
2✔
249
}
2✔
250

251
Test::Result test_x509_dates() {
1✔
252
   Test::Result result("X509 Time");
1✔
253

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

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

263
   time = Botan::X509_Time("200305100350Z");
1✔
264
   result.test_eq(
1✔
265
      "UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
1✔
266

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

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

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

284
      "080201000000-0000",
285
      "080201172412+0000",
286
      "040614233433-0500",
287
      "990614233444+0500",
288
      "000614233455-0530",
289
      "000614233455+0530",
290
   };
13✔
291

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

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

309
      // No seconds
310
      "0802010000Z",
311
      "0802011724Z",
312
      "0406142334Z",
313
      "9906142334Z",
314
      "0006142334Z",
315

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

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

349
      // wrong time zone
350
      "080201000000",
351
      "080201000000z",
352

353
      // Fractional seconds
354
      "170217180154.001Z",
355

356
      // Timezone offset
357
      "170217180154+0100",
358

359
      // Extra digits
360
      "17021718015400Z",
361

362
      // Non-digits
363
      "17021718015aZ",
364

365
      // Trailing garbage
366
      "170217180154Zlongtrailinggarbage",
367

368
      // Swapped type
369
      "20170217180154Z",
370
   };
49✔
371

372
   // valid length 15
373
   const std::string valid_generalized_time[]{
1✔
374
      "20000305100350Z",
375
   };
2✔
376

377
   const std::string invalid_generalized[]{
1✔
378
      // No trailing Z
379
      "20000305100350",
380

381
      // No seconds
382
      "200003051003Z",
383

384
      // Fractional seconds
385
      "20000305100350.001Z",
386

387
      // Timezone offset
388
      "20170217180154+0100",
389

390
      // Extra digits
391
      "2017021718015400Z",
392

393
      // Non-digits
394
      "2017021718015aZ",
395

396
      // Trailing garbage
397
      "20170217180154Zlongtrailinggarbage",
398

399
      // Swapped type
400
      "170217180154Z",
401
   };
9✔
402

403
   for(const auto& v : valid_but_unsup) {
13✔
404
      result.test_throws("valid but unsupported", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
60✔
405
   }
406

407
   for(const auto& v : valid_utc) {
6✔
408
      const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
409
   }
5✔
410

411
   for(const auto& v : valid_generalized_time) {
2✔
412
      const Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
413
   }
1✔
414

415
   for(const auto& v : invalid_utc) {
49✔
416
      result.test_throws("invalid", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
240✔
417
   }
418

419
   for(const auto& v : invalid_generalized) {
9✔
420
      result.test_throws("invalid", [v]() { const Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime); });
40✔
421
   }
422

423
   return result;
1✔
424
}
80✔
425

426
Test::Result test_x509_encode_authority_info_access_extension() {
1✔
427
   Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension");
1✔
428

429
   #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
430
   auto rng = Test::new_rng(__func__);
1✔
431

432
   const std::string sig_algo{"RSA"};
1✔
433
   const std::string hash_fn{"SHA-256"};
1✔
434
   const std::string padding_method{"PKCS1v15(SHA-256)"};
1✔
435

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

441
   // OCSP
442
   const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"};
1✔
443

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

450
   // create a certificate with only caIssuer information
451
   auto key = make_a_private_key(sig_algo, *rng);
1✔
452

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

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

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

460
   if(!result.test_sz_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
461
      return result;
462
   }
463

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

469
   result.confirm("no OCSP url available", cert.ocsp_responder().empty());
1✔
470

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

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

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

479
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
1✔
480
   result.confirm("no CA Issuer URI available", cert.ca_issuers().empty());
1✔
481
   result.test_eq("OCSP responder URI matches", cert.ocsp_responder(), std::string(ocsp_uri));
2✔
482

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

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

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

491
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
1✔
492
   result.confirm("CA Issuer URI available", !cert.ca_issuers().empty());
1✔
493
   #endif
494

495
   return result;
1✔
496
}
4✔
497

498
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
499

500
Test::Result test_crl_dn_name() {
1✔
501
   Test::Result result("CRL DN name");
1✔
502

503
      // See GH #1252
504

505
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
506
   auto rng = Test::new_rng(__func__);
1✔
507

508
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
509

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

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

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

518
   result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
1✔
519

520
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
2✔
521
      #endif
522

523
   return result;
2✔
524
}
3✔
525

526
Test::Result test_rdn_multielement_set_name() {
1✔
527
   Test::Result result("DN with multiple elements in RDN");
1✔
528

529
   // GH #2611
530

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

533
   result.confirm("issuer DN contains expected name components", cert.issuer_dn().get_attributes().size() == 4);
1✔
534
   result.confirm("subject DN contains expected name components", cert.subject_dn().get_attributes().size() == 4);
1✔
535

536
   return result;
1✔
537
}
1✔
538

539
Test::Result test_rsa_oaep() {
1✔
540
   Test::Result result("RSA OAEP decoding");
1✔
541

542
      #if defined(BOTAN_HAS_RSA)
543
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
544

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

549
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
1✔
550
      #endif
551

552
   return result;
2✔
553
}
1✔
554

555
Test::Result test_x509_decode_list() {
1✔
556
   Test::Result result("X509_Certificate list decode");
1✔
557

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

560
   Botan::BER_Decoder dec(input);
1✔
561
   std::vector<Botan::X509_Certificate> certs;
1✔
562
   dec.decode_list(certs);
1✔
563

564
   result.test_sz_eq("Expected number of certs in list", certs.size(), 2);
1✔
565

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

569
   return result;
1✔
570
}
1✔
571

572
Test::Result test_x509_utf8() {
1✔
573
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
574

575
   try {
1✔
576
      const Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
577

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

590
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
591

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

600
   return result;
1✔
601
}
×
602

603
Test::Result test_x509_bmpstring() {
1✔
604
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
605

606
   try {
1✔
607
      const Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
608

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

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

617
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
618

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

626
   return result;
1✔
627
}
×
628

629
Test::Result test_x509_teletex() {
1✔
630
   Test::Result result("X509 with TeletexString encoded fields");
1✔
631

632
   try {
1✔
633
      const Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
634

635
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
636

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

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

645
   return result;
1✔
646
}
×
647

648
Test::Result test_x509_authority_info_access_extension() {
1✔
649
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
650

651
   // contains no AIA extension
652
   const Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
653

654
   result.test_sz_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
1✔
655
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
1✔
656

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

660
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
661

662
   result.test_sz_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
663
   if(result.tests_failed() > 0) {
1✔
664
      return result;
665
   }
666

667
   result.test_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
1✔
668
   result.test_eq("OCSP responder URL matches", aia_cert.ocsp_responder(), "http://gp.symcd.com");
1✔
669

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

674
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
675

676
   result.test_sz_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
677
   if(result.tests_failed() > 0) {
1✔
678
      return result;
679
   }
680

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

689
   return result;
1✔
690
}
1✔
691

692
Test::Result test_parse_rsa_pss_cert() {
1✔
693
   Test::Result result("X509 RSA-PSS certificate");
1✔
694

695
   // See https://github.com/randombit/botan/issues/3019 for background
696

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

704
   return result;
1✔
705
}
×
706

707
Test::Result test_verify_gost2012_cert() {
1✔
708
   Test::Result result("X509 GOST-2012 certificates");
1✔
709

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

716
         Botan::Certificate_Store_In_Memory trusted;
1✔
717
         trusted.add_certificate(root_cert);
1✔
718

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

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

730
   return result;
1✔
731
}
×
732

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

742
   auto rng = Test::new_rng(__func__);
1✔
743

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

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

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

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

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

777
   test_result.test_eq("CA certificate signature algorithm (explicit)",
1✔
778
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
1✔
779
                       "RSA/PSS");
780

781
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
782
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
783

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

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

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

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

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

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

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

833
   return test_result;
2✔
834
}
3✔
835
      #endif
836

837
   #endif
838

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

845
   Botan::X509_Cert_Options opts;
11✔
846

847
   opts.dns = "main.example.org";
11✔
848
   opts.more_dns.push_back("more1.example.org");
22✔
849
   opts.more_dns.push_back("more2.example.org");
22✔
850

851
   opts.padding_scheme = sig_padding;
11✔
852

853
   Botan::AlternativeName alt_name;
11✔
854
   alt_name.add_attribute("DNS", "bonus.example.org");
11✔
855

856
   Botan::X509_DN alt_dn;
11✔
857
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
11✔
858
   alt_dn.add_attribute("X520.Organization", "testing");
11✔
859
   alt_name.add_dn(alt_dn);
11✔
860

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

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

865
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
11✔
866

867
   result.test_sz_eq("Expected number of DNS names", alt_dns_names.size(), 4);
11✔
868

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

876
   result.test_sz_eq("Expected number of alt DNs", req.subject_alt_name().directory_names().size(), 1);
11✔
877
   result.confirm("Alt DN is correct", *req.subject_alt_name().directory_names().begin() == alt_dn);
11✔
878

879
   return result;
11✔
880
}
22✔
881

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

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

892
   {
11✔
893
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
11✔
894
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
11✔
895
   }
896

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

900
   const Botan::PKCS10_Request user1_req =
11✔
901
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
11✔
902

903
   result.test_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom");
11✔
904

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

908
   const Botan::PKCS10_Request user2_req =
11✔
909
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
11✔
910

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

914
   const Botan::PKCS10_Request user3_req =
11✔
915
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
11✔
916

917
   /* Create the CA object */
918
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
919

920
   const BigInt user1_serial(99);
11✔
921

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

926
   result.test_sz_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
11✔
927
   result.test_sz_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
11✔
928

929
   const Botan::X509_Certificate user2_cert =
11✔
930
      ca.sign_request(user2_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
931
   result.test_is_true("extended key usage is set", user2_cert.has_ex_constraint("PKIX.EmailProtection"));
11✔
932

933
   const Botan::X509_Certificate user3_cert =
11✔
934
      ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
935

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

940
   {
11✔
941
      auto constraints = req_opts1(sig_algo).constraints;
11✔
942
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
11✔
943
   }
944

945
   /* Copy, assign and compare */
946
   Botan::X509_Certificate user1_cert_copy(user1_cert);
11✔
947
   result.test_is_true("certificate copy", user1_cert == user1_cert_copy);
11✔
948

949
   user1_cert_copy = user2_cert;
11✔
950
   result.test_is_true("certificate assignment", user2_cert == user1_cert_copy);
11✔
951

952
   const Botan::X509_Certificate user1_cert_differ =
11✔
953
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
954

955
   result.test_is_false("certificate differs", user1_cert == user1_cert_differ);
11✔
956

957
   /* Get cert data */
958
   result.test_sz_eq("x509 version", user1_cert.x509_version(), size_t(3));
11✔
959

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

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

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

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

983
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
11✔
984

985
   /* Verify the certs */
986
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
987
   Botan::Certificate_Store_In_Memory store;
11✔
988

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

995
   store.add_certificate(ca.ca_certificate());
11✔
996

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

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

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

1014
   std::vector<Botan::CRL_Entry> revoked;
11✔
1015
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1016
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1017

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

1020
   store.add_crl(crl2);
11✔
1021

1022
   const std::string revoked_str =
11✔
1023
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1024

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

1028
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1029
   result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
11✔
1030

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

1035
   store.add_crl(crl3);
11✔
1036

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

1042
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1043
   result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
11✔
1044

1045
   return result;
11✔
1046
}
44✔
1047

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

1055
   Test::Result result("X509 Usage");
12✔
1056

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

1060
   /* Create the CA object */
1061
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1062

1063
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1064

1065
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1066
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1067

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

1070
   const Botan::X509_Certificate user1_cert =
12✔
1071
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1072

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

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

1082
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1083

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

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

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

1098
   opts.constraints = Key_Constraints();
12✔
1099

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

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

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

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

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

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

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

1123
   return result;
12✔
1124
}
24✔
1125

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

1133
   Test::Result result("X509 Self Issued");
11✔
1134

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

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

1142
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1143

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

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

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

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

1158
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1159

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

1163
   result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
11✔
1164

1165
   return result;
11✔
1166
}
22✔
1167

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

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

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

1180
   return result;
1✔
1181
}
1✔
1182

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

1186
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1187

1188
   result.confirm("empty constraints always acceptable", Key_Constraints().compatible_with(key));
19✔
1189

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

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

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

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

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

1279
   return result;
19✔
1280
}
×
1281

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

1289
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1290

1291
      std::string value() const { return m_contents; }
44✔
1292

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

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

1299
      bool should_encode() const override { return true; }
22✔
1300

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

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

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

1315
   private:
1316
      std::string m_contents;
1317
};
1318

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

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

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

1333
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1334

1335
   Botan::X509_DN subject_dn;
11✔
1336

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

1342
   subject_dn.add_attribute(attr1, val1);
11✔
1343
   subject_dn.add_attribute(attr2, val2);
11✔
1344

1345
   const Botan::Extensions extensions;
11✔
1346

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

1350
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1351

1352
   result.test_sz_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
11✔
1353

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

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

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

1366
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1367

1368
   result.test_sz_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
11✔
1369

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

1377
   return result;
22✔
1378
}
99✔
1379

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

1387
   Test::Result result("X509 Extensions");
11✔
1388

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

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

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

1401
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1402

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

1408
      dps.emplace_back(dp);
22✔
1409
   }
22✔
1410

1411
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1412

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1494
   return result;
11✔
1495
}
55✔
1496

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

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

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

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

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

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

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

1548
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1549

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

1553
      asn1=SEQUENCE:tn_auth_list
1554

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

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

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

1572
   using Botan::Cert_Extension::TNAuthList;
1✔
1573

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

1576
   const auto& tn_entries = tn_auth_list->entries();
1✔
1577

1578
   result.confirm("cert has TNAuthList extension", tn_auth_list != nullptr, true);
1✔
1579

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

1587
   result.test_is_true("spc entry type", tn_entries[0].type() == TNAuthList::Entry::ServiceProviderCode);
1✔
1588
   result.test_eq("spc entry data", tn_entries[0].service_provider_code(), "1001");
1✔
1589

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

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

1601
   result.end_timer();
1✔
1602
   return result;
2✔
1603
}
1✔
1604

1605
   #endif
1606

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

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

1636
         auto& rng = this->rng();
1✔
1637

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

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

1657
            std::string hash = "SHA-256";
12✔
1658

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

1669
            auto key = make_a_private_key(algo, rng);
12✔
1670

1671
            if(key == nullptr) {
12✔
1672
               continue;
×
1673
            }
1674

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1775
         return results;
1✔
1776
      }
13✔
1777
};
1778

1779
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1780

1781
#endif
1782

1783
}  // namespace
1784

1785
}  // 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