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

randombit / botan / 19586471228

21 Nov 2025 11:36PM UTC coverage: 90.802% (+0.2%) from 90.627%
19586471228

Pull #5167

github

web-flow
Merge dc170f795 into f8eb34002
Pull Request #5167: Changes to reduce unnecessary inclusions

100849 of 111065 relevant lines covered (90.8%)

12790156.99 hits per line

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

93.94
/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/rng.h>
17
   #include <botan/x509_ca.h>
18
   #include <botan/x509_ext.h>
19
   #include <botan/x509path.h>
20
   #include <botan/x509self.h>
21
   #include <botan/internal/calendar.h>
22

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

28
namespace Botan_Tests {
29

30
namespace {
31

32
#if defined(BOTAN_HAS_X509_CERTIFICATES)
33

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

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

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

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

50
   opts.CA_key(1);
112✔
51

52
   return opts;
112✔
53
}
×
54

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

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

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

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

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

74
   return opts;
36✔
75
}
×
76

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

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

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

87
   return opts;
11✔
88
}
×
89

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

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

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

102
   return opts;
55✔
103
}
×
104

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

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

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

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

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

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

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

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

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

205
   return result;
1✔
206
}
1✔
207

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

211
   Botan::Extensions extn;
1✔
212

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

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

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

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

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

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

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

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

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

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

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

246
   return result;
2✔
247
}
2✔
248

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

421
   return result;
1✔
422
}
80✔
423

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

493
   return result;
1✔
494
}
4✔
495

496
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
497

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

501
      // See GH #1252
502

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

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

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

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

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

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

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

521
   return result;
2✔
522
}
3✔
523

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

527
   // GH #2611
528

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

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

534
   return result;
1✔
535
}
1✔
536

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

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

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

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

550
   return result;
2✔
551
}
1✔
552

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

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

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

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

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

567
   return result;
1✔
568
}
1✔
569

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

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

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

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

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

598
   return result;
1✔
599
}
×
600

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

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

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

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

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

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

624
   return result;
1✔
625
}
×
626

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

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

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

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

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

643
   return result;
1✔
644
}
×
645

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

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

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

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

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

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

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

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

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

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

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

687
   return result;
1✔
688
}
1✔
689

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

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

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

702
   return result;
1✔
703
}
×
704

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

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

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

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

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

728
   return result;
1✔
729
}
×
730

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

835
   #endif
836

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

843
   Botan::X509_Cert_Options opts;
11✔
844

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

849
   opts.padding_scheme = sig_padding;
11✔
850

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

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

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

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

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

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

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

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

877
   return result;
11✔
878
}
11✔
879

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

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

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

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

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

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

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

906
   Botan::PKCS10_Request user2_req = 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
   Botan::PKCS10_Request user3_req = Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
11✔
912

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

916
   const BigInt user1_serial(99);
11✔
917

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

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

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

928
   Botan::X509_Certificate user3_cert = ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
929

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

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

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

943
   user1_cert_copy = user2_cert;
11✔
944
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
11✔
945

946
   Botan::X509_Certificate user1_cert_differ =
11✔
947
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
948

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

951
   /* Get cert data */
952
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
11✔
953

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

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

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

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

977
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
11✔
978

979
   /* Verify the certs */
980
   Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
981
   Botan::Certificate_Store_In_Memory store;
11✔
982

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

989
   store.add_certificate(ca.ca_certificate());
11✔
990

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

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

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

1007
   std::vector<Botan::CRL_Entry> revoked;
11✔
1008
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1009
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1010

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

1013
   store.add_crl(crl2);
11✔
1014

1015
   const std::string revoked_str =
11✔
1016
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1017

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

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

1024
   revoked.clear();
11✔
1025
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
22✔
1026
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
11✔
1027

1028
   store.add_crl(crl3);
11✔
1029

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

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

1038
   return result;
11✔
1039
}
44✔
1040

1041
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1042
                        const std::string& sig_algo,
1043
                        const std::string& hash_fn,
1044
                        Botan::RandomNumberGenerator& rng) {
1045
   using Botan::Key_Constraints;
12✔
1046
   using Botan::Usage_Type;
12✔
1047

1048
   Test::Result result("X509 Usage");
12✔
1049

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

1053
   /* Create the CA object */
1054
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1055

1056
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1057

1058
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1059
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1060

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

1063
   const Botan::X509_Certificate user1_cert =
12✔
1064
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1065

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

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

1076
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1077

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

1080
   const Botan::X509_Certificate mult_usage_cert =
12✔
1081
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1082

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

1092
   opts.constraints = Key_Constraints();
12✔
1093

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

1096
   const Botan::X509_Certificate no_usage_cert =
12✔
1097
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1098

1099
   // cert allows every usage
1100
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1101
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1102
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1103

1104
   if(sig_algo == "RSA") {
12✔
1105
      // cert allows data encryption
1106
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1107

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

1110
      const Botan::X509_Certificate enc_cert =
1✔
1111
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1112

1113
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1114
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1115
   }
1✔
1116

1117
   return result;
12✔
1118
}
24✔
1119

1120
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
11✔
1121
                              const std::string& sig_algo,
1122
                              const std::string& sig_padding,
1123
                              const std::string& hash_fn,
1124
                              Botan::RandomNumberGenerator& rng) {
1125
   using Botan::Key_Constraints;
11✔
1126

1127
   Test::Result result("X509 Self Issued");
11✔
1128

1129
   // create the self-signed cert
1130
   const Botan::X509_Certificate ca_cert =
11✔
1131
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1132

1133
   /* Create the CA object */
1134
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1135

1136
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1137

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

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

1146
   const Botan::X509_Certificate self_issued_cert =
11✔
1147
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1148

1149
   // check that this chain can can be verified successfully
1150
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
11✔
1151

1152
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1153

1154
   const Botan::Path_Validation_Result validation_result =
11✔
1155
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
11✔
1156

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

1159
   return result;
11✔
1160
}
22✔
1161

1162
Test::Result test_x509_uninit() {
1✔
1163
   Test::Result result("X509 object uninitialized access");
1✔
1164

1165
   Botan::X509_Certificate cert;
1✔
1166
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1167
      cert.x509_version();
1✔
1168
   });
1169

1170
   Botan::X509_CRL crl;
1✔
1171
   result.test_throws(
2✔
1172
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1173

1174
   return result;
1✔
1175
}
1✔
1176

1177
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1178
   using Botan::Key_Constraints;
19✔
1179

1180
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1181

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

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

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

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

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

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

1273
   return result;
19✔
1274
}
×
1275

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

1283
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1284

1285
      std::string value() const { return m_contents; }
44✔
1286

1287
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1288
         return std::make_unique<String_Extension>(m_contents);
×
1289
      }
1290

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

1293
      bool should_encode() const override { return true; }
22✔
1294

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

1297
      std::vector<uint8_t> encode_inner() const override {
11✔
1298
         std::vector<uint8_t> bits;
11✔
1299
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
11✔
1300
         return bits;
11✔
1301
      }
×
1302

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

1309
   private:
1310
      std::string m_contents;
1311
};
1312

1313
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
11✔
1314
                                 const std::string& sig_algo,
1315
                                 const std::string& sig_padding,
1316
                                 const std::string& hash_fn,
1317
                                 Botan::RandomNumberGenerator& rng) {
1318
   Test::Result result("X509 Custom DN");
11✔
1319

1320
   /* Create the self-signed cert */
1321
   Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1322

1323
   /* Create the CA object */
1324
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1325

1326
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1327

1328
   Botan::X509_DN subject_dn;
11✔
1329

1330
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
11✔
1331
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
11✔
1332
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
11✔
1333
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
11✔
1334

1335
   subject_dn.add_attribute(attr1, val1);
11✔
1336
   subject_dn.add_attribute(attr2, val2);
11✔
1337

1338
   Botan::Extensions extensions;
11✔
1339

1340
   Botan::PKCS10_Request req =
11✔
1341
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
11✔
1342

1343
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1344

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

1347
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
11✔
1348
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
11✔
1349
   result.confirm("Attr1 matches encoded", req_val1 == val1);
22✔
1350
   result.confirm("Attr2 matches encoded", req_val2 == val2);
22✔
1351
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
22✔
1352
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
22✔
1353

1354
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1355
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1356

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

1359
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1360

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

1363
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
11✔
1364
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
11✔
1365
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
22✔
1366
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
22✔
1367
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
22✔
1368
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
22✔
1369

1370
   return result;
11✔
1371
}
33✔
1372

1373
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
11✔
1374
                                  const std::string& sig_algo,
1375
                                  const std::string& sig_padding,
1376
                                  const std::string& hash_fn,
1377
                                  Botan::RandomNumberGenerator& rng) {
1378
   using Botan::Key_Constraints;
11✔
1379

1380
   Test::Result result("X509 Extensions");
11✔
1381

1382
   /* Create the self-signed cert */
1383
   Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1384

1385
   /* Create the CA object */
1386
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1387

1388
   /* Prepare CDP extension */
1389
   std::vector<std::string> cdp_urls = {
11✔
1390
      "http://example.com/crl1.pem",
1391
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
11✔
1392

1393
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1394

1395
   for(const auto& uri : cdp_urls) {
33✔
1396
      Botan::AlternativeName cdp_alt_name;
22✔
1397
      cdp_alt_name.add_uri(uri);
22✔
1398
      Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
22✔
1399

1400
      dps.emplace_back(dp);
22✔
1401
   }
22✔
1402

1403
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1404

1405
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
11✔
1406
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1407

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

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

1420
   result.confirm("Extensions::extension_set true for Key_Usage",
22✔
1421
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1422

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

1431
   // check if custom extension is present in self-signed cert
1432
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
11✔
1433
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
22✔
1434
      result.test_eq(
44✔
1435
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1436
   }
1437

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

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

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

1452
   /* Create a CA-signed certificate */
1453
   const Botan::X509_Certificate ca_signed_cert =
11✔
1454
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1455

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

1459
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
22✔
1460
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
22✔
1461
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
11✔
1462
      result.confirm("Key_Usage extension value matches in user certificate",
22✔
1463
                     constraints == Botan::Key_Constraints::DigitalSignature);
11✔
1464
   }
1465

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

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

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

1486
   return result;
11✔
1487
}
55✔
1488

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

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

1519
   for(const auto& a : cases) {
72✔
1520
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1521
      opts.CA_key();
60✔
1522

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

1525
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1526
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1527

1528
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1529
      const Botan::PKCS10_Request req =
60✔
1530
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1531
      const Botan::X509_Certificate subject_cert =
60✔
1532
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1533

1534
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1535
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1536
   }
60✔
1537
   return result;
12✔
1538
}
72✔
1539

1540
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1541

1542
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1543
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1544

1545
      asn1=SEQUENCE:tn_auth_list
1546

1547
      [tn_auth_list]
1548
      spc=EXP:0,IA5:1001
1549
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1550
      one=EXP:2,IA5:333
1551

1552
      [TelephoneNumberRange]
1553
      start1=IA5:111
1554
      count1=INT:128
1555
      start2=IA5:222
1556
      count2=INT:256
1557
    */
1558
   const std::string filename("TNAuthList.pem");
1✔
1559
   Test::Result result("X509 TNAuthList decode");
1✔
1560
   result.start_timer();
1✔
1561

1562
   Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1563

1564
   using Botan::Cert_Extension::TNAuthList;
1✔
1565

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

1568
   const auto& tn_entries = tn_auth_list->entries();
1✔
1569

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

1572
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1573
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1574
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1575
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1576
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1577
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1578

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

1582
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1583
   const auto& range = tn_entries[1].telephone_number_range();
1✔
1584
   result.test_eq("range entries count", range.size(), 2);
1✔
1585
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1586
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1587
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1588
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1589

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

1593
   result.end_timer();
1✔
1594
   return result;
2✔
1595
}
1✔
1596

1597
   #endif
1598

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

1623
class X509_Cert_Unit_Tests final : public Test {
1✔
1624
   public:
1625
      std::vector<Test::Result> run() override {
1✔
1626
         std::vector<Test::Result> results;
1✔
1627

1628
         auto& rng = this->rng();
1✔
1629

1630
         const std::string sig_algos[]{"RSA",
1✔
1631
                                       "DSA",
1632
                                       "ECDSA",
1633
                                       "ECGDSA",
1634
                                       "ECKCDSA",
1635
                                       "GOST-34.10",
1636
                                       "Ed25519",
1637
                                       "Ed448",
1638
                                       "Dilithium",
1639
                                       "ML-DSA",
1640
                                       "SLH-DSA",
1641
                                       "HSS-LMS"};
13✔
1642

1643
         for(const std::string& algo : sig_algos) {
13✔
1644
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1645
            if(algo == "RSA")
1646
               continue;
1647
   #endif
1648

1649
            std::string hash = "SHA-256";
12✔
1650

1651
            if(algo == "Ed25519") {
12✔
1652
               hash = "SHA-512";
1✔
1653
            }
1654
            if(algo == "Ed448") {
12✔
1655
               hash = "SHAKE-256(912)";
1✔
1656
            }
1657
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1658
               hash = "SHAKE-256(512)";
2✔
1659
            }
1660

1661
            auto key = make_a_private_key(algo, rng);
12✔
1662

1663
            if(key == nullptr) {
12✔
1664
               continue;
×
1665
            }
1666

1667
            results.push_back(test_hashes(*key, hash, rng));
24✔
1668
            results.push_back(test_valid_constraints(*key, algo));
24✔
1669

1670
            Test::Result usage_result("X509 Usage");
12✔
1671
            try {
12✔
1672
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1673
            } catch(std::exception& e) {
×
1674
               usage_result.test_failure("test_usage " + algo, e.what());
×
1675
            }
×
1676
            results.push_back(usage_result);
12✔
1677

1678
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
23✔
1679
               Test::Result cert_result("X509 Unit");
11✔
1680

1681
               try {
11✔
1682
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
11✔
1683
               } catch(std::exception& e) {
×
1684
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1685
               }
×
1686
               results.push_back(cert_result);
11✔
1687

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

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

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

1712
               Test::Result custom_dn_result("X509 Custom DN");
11✔
1713
               try {
11✔
1714
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
11✔
1715
               } catch(std::exception& e) {
×
1716
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1717
               }
×
1718
               results.push_back(custom_dn_result);
11✔
1719
            }
23✔
1720
         }
24✔
1721

1722
         /*
1723
         These are algos which cannot sign but can be included in certs
1724
         */
1725
         const std::vector<std::string> enc_algos = {
1✔
1726
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
1727

1728
         for(const std::string& algo : enc_algos) {
8✔
1729
            auto key = make_a_private_key(algo, rng);
7✔
1730

1731
            if(key) {
7✔
1732
               results.push_back(test_valid_constraints(*key, algo));
14✔
1733
            }
1734
         }
7✔
1735

1736
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1737
      defined(BOTAN_HAS_RSA)
1738
         Test::Result pad_config_result("X509 Padding Config");
1✔
1739
         try {
1✔
1740
            pad_config_result.merge(test_padding_config());
1✔
1741
         } catch(const std::exception& e) {
×
1742
            pad_config_result.test_failure("test_padding_config", e.what());
×
1743
         }
×
1744
         results.push_back(pad_config_result);
1✔
1745
   #endif
1746

1747
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1748
         results.push_back(test_x509_utf8());
2✔
1749
         results.push_back(test_x509_bmpstring());
2✔
1750
         results.push_back(test_x509_teletex());
2✔
1751
         results.push_back(test_crl_dn_name());
2✔
1752
         results.push_back(test_rdn_multielement_set_name());
2✔
1753
         results.push_back(test_x509_decode_list());
2✔
1754
         results.push_back(test_rsa_oaep());
2✔
1755
         results.push_back(test_x509_authority_info_access_extension());
2✔
1756
         results.push_back(test_verify_gost2012_cert());
2✔
1757
         results.push_back(test_parse_rsa_pss_cert());
2✔
1758
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1759
   #endif
1760

1761
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1762
         results.push_back(test_x509_extension());
2✔
1763
         results.push_back(test_x509_dates());
2✔
1764
         results.push_back(test_cert_status_strings());
2✔
1765
         results.push_back(test_x509_uninit());
2✔
1766

1767
         return results;
1✔
1768
      }
13✔
1769
};
1770

1771
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1772

1773
#endif
1774

1775
}  // namespace
1776

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