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

randombit / botan / 20579846577

29 Dec 2025 06:24PM UTC coverage: 90.415% (+0.2%) from 90.243%
20579846577

push

github

web-flow
Merge pull request #5167 from randombit/jack/src-size-reductions

Changes to reduce unnecessary inclusions

101523 of 112285 relevant lines covered (90.42%)

12817276.56 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/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
   const 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]() { const Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
108✔
403
   }
404

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

409
   for(const auto& v : valid_generalized_time) {
2✔
410
      const 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]() { const 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]() { const 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
   const 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
   const 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
   const Botan::X509_CA ca(cert, *key, "SHA-256", *rng);
1✔
513

514
   const 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
   const 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
   const 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
      const 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
      const 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
      const 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
   const 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
   const 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
   const 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
      const 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
         const Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
712
         const 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
   const 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
   const 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
      const 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
   const 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
      const 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
   const Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
802
   const 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
   const Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "PSS", *rng);
1✔
809
   const 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
   const Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "PSS(SHA-512,MGF1,64)", *rng);
1✔
816
   const 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
   const 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
   const 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
   const Botan::PKCS10_Request user2_req =
11✔
907
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
11✔
908

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

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

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

918
   const BigInt user1_serial(99);
11✔
919

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1018
   store.add_crl(crl2);
11✔
1019

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

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

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

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

1033
   store.add_crl(crl3);
11✔
1034

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

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

1043
   return result;
11✔
1044
}
44✔
1045

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1122
   return result;
12✔
1123
}
24✔
1124

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

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

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

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

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

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

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

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

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

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

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

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

1164
   return result;
11✔
1165
}
22✔
1166

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

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

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

1179
   return result;
1✔
1180
}
1✔
1181

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

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

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

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

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

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

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

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

1278
   return result;
19✔
1279
}
×
1280

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1334
   Botan::X509_DN subject_dn;
11✔
1335

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

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

1344
   const Botan::Extensions extensions;
11✔
1345

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

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

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

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

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

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

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

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

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

1376
   return result;
11✔
1377
}
33✔
1378

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1493
   return result;
11✔
1494
}
55✔
1495

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

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

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

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

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

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

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

1547
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1548

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

1552
      asn1=SEQUENCE:tn_auth_list
1553

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

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

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

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

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

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

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

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

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

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

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

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

1604
   #endif
1605

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1780
#endif
1781

1782
}  // namespace
1783

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