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

randombit / botan / 20954202357

13 Jan 2026 10:58AM UTC coverage: 90.47% (+0.02%) from 90.455%
20954202357

Pull #5231

github

web-flow
Merge d4f21e016 into 8c072d7ea
Pull Request #5231: X509: Multiple OCSP Responders

102357 of 113139 relevant lines covered (90.47%)

12883585.71 hits per line

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

94.14
/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) {
336✔
35
   const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
336✔
36

37
   const Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
336✔
38
   return Botan::X509_Time(t.to_std_timepoint());
336✔
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 = "") {
37✔
56
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
37✔
57

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

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

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

68
   if(algo == "RSA") {
37✔
69
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
7✔
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;
37✔
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

492
   // create a certificate with multiple OCSP URIs
493
   Botan::X509_Cert_Options opts_multi_ocsp = req_opts1(sig_algo);
1✔
494
   const std::vector<std::string> ocsp_uris = {"http://ocsp.example.com", "http://backup-ocsp.example.com"};
1✔
495
   opts_multi_ocsp.extensions.add(std::make_unique<Botan::Cert_Extension::Authority_Information_Access>(ocsp_uris));
2✔
496

497
   req = Botan::X509::create_cert_req(opts_multi_ocsp, *key, hash_fn, *rng);
1✔
498

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

501
   const auto* aia_ext =
1✔
502
      cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::Authority_Information_Access>();
1✔
503
   result.confirm("AIA extension present", aia_ext != nullptr);
2✔
504

505
   const auto ocsp_responders = aia_ext->ocsp_responders();
1✔
506
   result.test_eq("number of OCSP responder URIs", ocsp_responders.size(), 2);
1✔
507
   result.test_eq("First OCSP responder URI matches", ocsp_responders[0], "http://ocsp.example.com");
2✔
508
   result.test_eq("Second OCSP responder URI matches", ocsp_responders[1], "http://backup-ocsp.example.com");
2✔
509

510
   const auto cert_ocsp_responders = cert.ocsp_responders();
1✔
511
   result.test_eq("Certificate: number of OCSP responder URIs", cert_ocsp_responders.size(), 2);
1✔
512
   result.test_eq("Certificate: First OCSP responder URI matches", cert_ocsp_responders[0], "http://ocsp.example.com");
2✔
513
   result.test_eq(
2✔
514
      "Certificate: Second OCSP responder URI matches", cert_ocsp_responders[1], "http://backup-ocsp.example.com");
1✔
515
   #endif
516

517
   return result;
1✔
518
}
4✔
519

520
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
521

522
Test::Result test_crl_dn_name() {
1✔
523
   Test::Result result("CRL DN name");
1✔
524

525
      // See GH #1252
526

527
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
528
   auto rng = Test::new_rng(__func__);
1✔
529

530
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
531

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

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

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

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

542
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
3✔
543
      #endif
544

545
   return result;
2✔
546
}
3✔
547

548
Test::Result test_rdn_multielement_set_name() {
1✔
549
   Test::Result result("DN with multiple elements in RDN");
1✔
550

551
   // GH #2611
552

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

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

558
   return result;
1✔
559
}
1✔
560

561
Test::Result test_rsa_oaep() {
1✔
562
   Test::Result result("RSA OAEP decoding");
1✔
563

564
      #if defined(BOTAN_HAS_RSA)
565
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
566

567
   auto public_key = cert.subject_public_key();
1✔
568
   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
1✔
569
   const auto& pk_info = cert.subject_public_key_algo();
1✔
570

571
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
572
      #endif
573

574
   return result;
2✔
575
}
1✔
576

577
Test::Result test_x509_decode_list() {
1✔
578
   Test::Result result("X509_Certificate list decode");
1✔
579

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

582
   Botan::BER_Decoder dec(input);
1✔
583
   std::vector<Botan::X509_Certificate> certs;
1✔
584
   dec.decode_list(certs);
1✔
585

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

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

591
   return result;
1✔
592
}
1✔
593

594
Test::Result test_x509_utf8() {
1✔
595
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
596

597
   try {
1✔
598
      const Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
599

600
      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
601
      const std::string organization =
1✔
602
         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
603
         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
1✔
604
      const std::string organization_unit =
1✔
605
         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
606
         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
1✔
607
      const std::string common_name =
1✔
608
         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
609
         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
1✔
610
      const std::string location = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
611

612
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
613

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

622
   return result;
1✔
623
}
×
624

625
Test::Result test_x509_bmpstring() {
1✔
626
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
627

628
   try {
1✔
629
      const Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
630

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

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

639
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
640

641
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
642
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
643
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
644
   } catch(const Botan::Decoding_Error& ex) {
1✔
645
      result.test_failure(ex.what());
×
646
   }
×
647

648
   return result;
1✔
649
}
×
650

651
Test::Result test_x509_teletex() {
1✔
652
   Test::Result result("X509 with TeletexString encoded fields");
1✔
653

654
   try {
1✔
655
      const Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
656

657
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
658

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

661
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
662
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
663
   } catch(const Botan::Decoding_Error& ex) {
1✔
664
      result.test_failure(ex.what());
×
665
   }
×
666

667
   return result;
1✔
668
}
×
669

670
Test::Result test_x509_authority_info_access_extension() {
1✔
671
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
672

673
   // contains no AIA extension
674
   const Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
675

676
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
677
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
678

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

682
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
683

684
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
685
   if(result.tests_failed() > 0) {
1✔
686
      return result;
687
   }
688

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

692
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
693
   const Botan::X509_Certificate aia_cert_2ca(
1✔
694
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
695

696
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
697

698
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
699
   if(result.tests_failed() > 0) {
1✔
700
      return result;
701
   }
702

703
   result.test_eq(
2✔
704
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
705
   result.test_eq(
2✔
706
      "CA issuer URL matches",
707
      ca_issuers2[1],
1✔
708
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
709
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
2✔
710

711
   // contains AIA extension with multiple OCSP responders
712
   const Botan::X509_Certificate aia_cert_multi_ocsp(
1✔
713
      Test::data_file("x509/misc/contains_multiple_ocsp_responders.pem"));
2✔
714

715
   const auto ocsp_responders_multi = aia_cert_multi_ocsp.ocsp_responders();
1✔
716
   result.test_eq("number of OCSP responders", ocsp_responders_multi.size(), 3);
1✔
717
   result.test_eq("First OCSP responder URL matches", ocsp_responders_multi[0], "http://ocsp1.example.com");
2✔
718
   result.test_eq("Second OCSP responder URL matches", ocsp_responders_multi[1], "http://ocsp2.example.com");
2✔
719
   result.test_eq("Third OCSP responder URL matches", ocsp_responders_multi[2], "http://ocsp3.example.com");
2✔
720
   result.confirm("no CA Issuer URI available", aia_cert_multi_ocsp.ca_issuers().empty());
2✔
721

722
   return result;
1✔
723
}
1✔
724

725
Test::Result test_parse_rsa_pss_cert() {
1✔
726
   Test::Result result("X509 RSA-PSS certificate");
1✔
727

728
   // See https://github.com/randombit/botan/issues/3019 for background
729

730
   try {
1✔
731
      const Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
732
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
733
   } catch(Botan::Exception& e) {
1✔
734
      result.test_failure("Parsing failed", e.what());
×
735
   }
×
736

737
   return result;
1✔
738
}
×
739

740
Test::Result test_verify_gost2012_cert() {
1✔
741
   Test::Result result("X509 GOST-2012 certificates");
1✔
742

743
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
744
   try {
1✔
745
      if(Botan::EC_Group::supports_named_group("gost_256A")) {
1✔
746
         const Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
747
         const Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
748

749
         Botan::Certificate_Store_In_Memory trusted;
1✔
750
         trusted.add_certificate(root_cert);
1✔
751

752
         const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
2✔
753
         const Botan::Path_Validation_Result validation_result =
1✔
754
            Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
755

756
         result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
757
      }
1✔
758
   } catch(const Botan::Decoding_Error& e) {
×
759
      result.test_failure(e.what());
×
760
   }
×
761
      #endif
762

763
   return result;
1✔
764
}
×
765

766
   /*
767
 * @brief checks the configurability of the RSA-PSS signature scheme
768
 *
769
 * For the other algorithms than RSA, only one padding is supported right now.
770
 */
771
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
772
Test::Result test_padding_config() {
1✔
773
   Test::Result test_result("X509 Padding Config");
1✔
774

775
   auto rng = Test::new_rng(__func__);
1✔
776

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

780
   // Create X509 CA certificate; PKCS1v15 is used for signing by default
781
   Botan::X509_Cert_Options opt("TEST CA");
1✔
782
   opt.CA_key();
1✔
783

784
   const Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
785
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
786
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
787
                       "RSA/PKCS1v15(SHA-512)");
788

789
   // Create X509 CA certificate; RSA-PSS is explicitly set
790
   opt.set_padding_scheme("PSSR");
1✔
791
   const Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
792
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
793
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
794
                       "RSA/PSS");
795

796
         #if defined(BOTAN_HAS_EMSA_X931)
797
   // Try to set a padding scheme that is not supported for signing with the given key type
798
   opt.set_padding_scheme("X9.31");
1✔
799
   try {
1✔
800
      const Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
801
      test_result.test_failure("Could build CA cert with invalid encoding scheme X9.31 for key type " +
×
802
                               sk->algo_name());
×
803
   } catch(const Botan::Invalid_Argument& e) {
1✔
804
      test_result.test_eq("Build CA certificate with invalid encoding scheme X9.31 for key type " + sk->algo_name(),
3✔
805
                          e.what(),
1✔
806
                          "Signatures using RSA/X9.31(SHA-512) are not supported");
807
   }
1✔
808
         #endif
809

810
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
811
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
812
                       "RSA/PSS");
813

814
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
815
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
816

817
   // Prepare a signing request for the end certificate
818
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
819
   req_opt.set_padding_scheme("PSS(SHA-512,MGF1,64)");
1✔
820
   const Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng);
1✔
821
   test_result.test_eq(
3✔
822
      "Certificate request signature algorithm", end_req.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
823

824
   // Create X509 CA object: will fail as the chosen hash functions differ
825
   try {
1✔
826
      const Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "PSS(SHA-256)", *rng);
1✔
827
      test_result.test_failure("Configured conflicting hash functions for CA");
×
828
   } catch(const Botan::Invalid_Argument& e) {
1✔
829
      test_result.test_eq(
1✔
830
         "Configured conflicting hash functions for CA",
831
         e.what(),
1✔
832
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding PSS(SHA-256)");
833
   }
1✔
834

835
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. PKCS1v15
836
   const Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
837
   const Botan::X509_Certificate end_cert_pkcs1 = ca_def.sign_request(end_req, *rng, not_before, not_after);
1✔
838
   test_result.test_eq("End certificate signature algorithm",
3✔
839
                       end_cert_pkcs1.signature_algorithm().oid().to_formatted_string(),
2✔
840
                       "RSA/PKCS1v15(SHA-512)");
841

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

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

855
   // Check CRL signature algorithm
856
   const Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
857
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
858

859
   // sanity check for verification, the heavy lifting is done in the other unit tests
860
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
861
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
862
   const Botan::Path_Validation_Result validation_result =
1✔
863
      Botan::x509_path_validate(end_cert_pss, restrictions, trusted);
1✔
864
   test_result.confirm("PSS signed certificate validates", validation_result.successful_validation());
2✔
865

866
   return test_result;
2✔
867
}
3✔
868
      #endif
869

870
   #endif
871

872
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
11✔
873
                             const std::string& sig_padding,
874
                             const std::string& hash_fn,
875
                             Botan::RandomNumberGenerator& rng) {
876
   Test::Result result("PKCS10 extensions");
11✔
877

878
   Botan::X509_Cert_Options opts;
11✔
879

880
   opts.dns = "main.example.org";
11✔
881
   opts.more_dns.push_back("more1.example.org");
22✔
882
   opts.more_dns.push_back("more2.example.org");
22✔
883

884
   opts.padding_scheme = sig_padding;
11✔
885

886
   Botan::AlternativeName alt_name;
11✔
887
   alt_name.add_attribute("DNS", "bonus.example.org");
11✔
888

889
   Botan::X509_DN alt_dn;
11✔
890
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
11✔
891
   alt_dn.add_attribute("X520.Organization", "testing");
11✔
892
   alt_name.add_dn(alt_dn);
11✔
893

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

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

898
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
11✔
899

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

902
   if(alt_dns_names.size() == 4) {
11✔
903
      result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
33✔
904
      result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
33✔
905
      result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
33✔
906
      result.test_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
33✔
907
   }
908

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

912
   return result;
11✔
913
}
11✔
914

915
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
11✔
916
                            const std::string& sig_algo,
917
                            const std::string& sig_padding,
918
                            const std::string& hash_fn,
919
                            Botan::RandomNumberGenerator& rng) {
920
   Test::Result result("X509 Unit");
11✔
921

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

925
   {
11✔
926
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
22✔
927
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
22✔
928
   }
929

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

933
   const Botan::PKCS10_Request user1_req =
11✔
934
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
11✔
935

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

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

941
   const Botan::PKCS10_Request user2_req =
11✔
942
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
11✔
943

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

947
   const Botan::PKCS10_Request user3_req =
11✔
948
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
11✔
949

950
   /* Create the CA object */
951
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
952

953
   const BigInt user1_serial(99);
11✔
954

955
   /* Sign the requests to create the certs */
956
   const Botan::X509_Certificate user1_cert =
11✔
957
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
958

959
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
11✔
960
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
11✔
961

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

966
   const Botan::X509_Certificate user3_cert =
11✔
967
      ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
968

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

973
   {
11✔
974
      auto constraints = req_opts1(sig_algo).constraints;
11✔
975
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
22✔
976
   }
977

978
   /* Copy, assign and compare */
979
   Botan::X509_Certificate user1_cert_copy(user1_cert);
11✔
980
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
11✔
981

982
   user1_cert_copy = user2_cert;
11✔
983
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
11✔
984

985
   const Botan::X509_Certificate user1_cert_differ =
11✔
986
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
987

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

990
   /* Get cert data */
991
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
11✔
992

993
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
11✔
994
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
22✔
995
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
22✔
996
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
22✔
997
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
22✔
998

999
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
11✔
1000
   result.test_eq("subject OrgaUnit count",
11✔
1001
                  user3_subject_dn.get_attribute("OU").size(),
22✔
1002
                  req_opts3(sig_algo).more_org_units.size() + 1);
22✔
1003
   result.test_eq(
11✔
1004
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
44✔
1005

1006
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
11✔
1007
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
22✔
1008
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
22✔
1009
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
22✔
1010

1011
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
11✔
1012
   result.test_eq(
11✔
1013
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
22✔
1014
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
44✔
1015

1016
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
11✔
1017

1018
   /* Verify the certs */
1019
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1020
   Botan::Certificate_Store_In_Memory store;
11✔
1021

1022
   // First try with an empty store
1023
   const Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1024
   result.test_eq("user 1 issuer not found",
33✔
1025
                  result_no_issuer.result_string(),
22✔
1026
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1027

1028
   store.add_certificate(ca.ca_certificate());
11✔
1029

1030
   Botan::Path_Validation_Result 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
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1036
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
22✔
1037
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
1038
   }
1039

1040
   const Botan::Path_Validation_Result result_self_signed =
11✔
1041
      Botan::x509_path_validate(user1_ss_cert, restrictions, store);
11✔
1042
   result.test_eq("user 1 issuer not found",
33✔
1043
                  result_no_issuer.result_string(),
22✔
1044
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1045
   store.add_crl(crl1);
11✔
1046

1047
   std::vector<Botan::CRL_Entry> revoked;
11✔
1048
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1049
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1050

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

1053
   store.add_crl(crl2);
11✔
1054

1055
   const std::string revoked_str =
11✔
1056
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1057

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

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

1064
   revoked.clear();
11✔
1065
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
22✔
1066
   const Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
11✔
1067

1068
   store.add_crl(crl3);
11✔
1069

1070
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1071
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
22✔
1072
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1073
   }
1074

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

1078
   return result;
11✔
1079
}
44✔
1080

1081
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1082
                        const std::string& sig_algo,
1083
                        const std::string& hash_fn,
1084
                        Botan::RandomNumberGenerator& rng) {
1085
   using Botan::Key_Constraints;
12✔
1086
   using Botan::Usage_Type;
12✔
1087

1088
   Test::Result result("X509 Usage");
12✔
1089

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

1093
   /* Create the CA object */
1094
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1095

1096
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1097

1098
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1099
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1100

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

1103
   const Botan::X509_Certificate user1_cert =
12✔
1104
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1105

1106
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1107
   result.test_eq(
12✔
1108
      "key usage cRLSign not allowed",
1109
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1110
      false);
1111
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1112

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

1116
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1117

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

1120
   const Botan::X509_Certificate mult_usage_cert =
12✔
1121
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1122

1123
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1124
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1125
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1126
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1127
   result.confirm(
24✔
1128
      "key usage multiple digitalSignature and cRLSign allowed",
1129
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1130
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1131

1132
   opts.constraints = Key_Constraints();
12✔
1133

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

1136
   const Botan::X509_Certificate no_usage_cert =
12✔
1137
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1138

1139
   // cert allows every usage
1140
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1141
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1142
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1143

1144
   if(sig_algo == "RSA") {
12✔
1145
      // cert allows data encryption
1146
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1147

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

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

1153
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1154
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1155
   }
1✔
1156

1157
   return result;
12✔
1158
}
24✔
1159

1160
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
11✔
1161
                              const std::string& sig_algo,
1162
                              const std::string& sig_padding,
1163
                              const std::string& hash_fn,
1164
                              Botan::RandomNumberGenerator& rng) {
1165
   using Botan::Key_Constraints;
11✔
1166

1167
   Test::Result result("X509 Self Issued");
11✔
1168

1169
   // create the self-signed cert
1170
   const Botan::X509_Certificate ca_cert =
11✔
1171
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1172

1173
   /* Create the CA object */
1174
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1175

1176
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1177

1178
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1179
   // but signed by a CA, not signed by it's own private key
1180
   Botan::X509_Cert_Options opts = ca_opts();
11✔
1181
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1182
   opts.set_padding_scheme(sig_padding);
11✔
1183

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

1186
   const Botan::X509_Certificate self_issued_cert =
11✔
1187
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1188

1189
   // check that this chain can can be verified successfully
1190
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
11✔
1191

1192
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1193

1194
   const Botan::Path_Validation_Result validation_result =
11✔
1195
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
11✔
1196

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

1199
   return result;
11✔
1200
}
22✔
1201

1202
Test::Result test_x509_uninit() {
1✔
1203
   Test::Result result("X509 object uninitialized access");
1✔
1204

1205
   Botan::X509_Certificate cert;
1✔
1206
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1207
      cert.x509_version();
1✔
1208
   });
1209

1210
   Botan::X509_CRL crl;
1✔
1211
   result.test_throws(
2✔
1212
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1213

1214
   return result;
1✔
1215
}
1✔
1216

1217
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1218
   using Botan::Key_Constraints;
19✔
1219

1220
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1221

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

1224
   // Now check some typical usage scenarios for the given key type
1225
   // Taken from RFC 5280, sec. 4.2.1.3
1226
   // ALL constraints are not typical at all, but we use them for a negative test
1227
   const auto all = Key_Constraints(
19✔
1228
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1229
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1230
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1231

1232
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1233
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1234
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1235
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1236
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1237
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1238
   const auto key_agreement_encipher_only =
19✔
1239
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1240
   const auto key_agreement_decipher_only =
19✔
1241
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1242
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1243
   const auto sign_everything =
19✔
1244
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1245

1246
   if(pk_algo == "DH" || pk_algo == "ECDH") {
19✔
1247
      // DH and ECDH only for key agreement
1248
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1249
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1250
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1251
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1252
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1253
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1254
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1255
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1256
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1257
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1258
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1259
   } else if(pk_algo == "Kyber" || pk_algo == "FrodoKEM" || pk_algo == "ML-KEM" || pk_algo == "ClassicMcEliece") {
17✔
1260
      // KEMs can encrypt and agree
1261
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
4✔
1262
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
4✔
1263
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
4✔
1264
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
4✔
1265
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
4✔
1266
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1267
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
4✔
1268
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), false);
4✔
1269
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
8✔
1270
   } else if(pk_algo == "RSA") {
13✔
1271
      // RSA can do everything except key agreement
1272
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1273

1274
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1275
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1276
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1277
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1278
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1279
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1280
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1281
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1282
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1283
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1284
   } else if(pk_algo == "ElGamal") {
12✔
1285
      // only ElGamal encryption is currently implemented
1286
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1287
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1288
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1289
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1290
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1291
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1292
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1293
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1294
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1295
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
10✔
1296
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" ||
18✔
1297
             pk_algo == "HSS-LMS") {
3✔
1298
      // these are signature algorithms only
1299
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
9✔
1300

1301
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1302
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1303
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1304
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1305
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1306
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1307
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1308
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1309
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1310
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1311
   }
1312

1313
   return result;
19✔
1314
}
×
1315

1316
/**
1317
 * @brief X.509v3 extension that encodes a given string
1318
 */
1319
class String_Extension final : public Botan::Certificate_Extension {
22✔
1320
   public:
1321
      String_Extension() = default;
22✔
1322

1323
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1324

1325
      std::string value() const { return m_contents; }
44✔
1326

1327
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1328
         return std::make_unique<String_Extension>(m_contents);
×
1329
      }
1330

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

1333
      bool should_encode() const override { return true; }
22✔
1334

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

1337
      std::vector<uint8_t> encode_inner() const override {
11✔
1338
         std::vector<uint8_t> bits;
11✔
1339
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
11✔
1340
         return bits;
11✔
1341
      }
×
1342

1343
      void decode_inner(const std::vector<uint8_t>& in) override {
22✔
1344
         Botan::ASN1_String str;
22✔
1345
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
22✔
1346
         m_contents = str.value();
22✔
1347
      }
22✔
1348

1349
   private:
1350
      std::string m_contents;
1351
};
1352

1353
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
11✔
1354
                                 const std::string& sig_algo,
1355
                                 const std::string& sig_padding,
1356
                                 const std::string& hash_fn,
1357
                                 Botan::RandomNumberGenerator& rng) {
1358
   Test::Result result("X509 Custom DN");
11✔
1359

1360
   /* Create the self-signed cert */
1361
   const Botan::X509_Certificate ca_cert =
11✔
1362
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1363

1364
   /* Create the CA object */
1365
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1366

1367
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1368

1369
   Botan::X509_DN subject_dn;
11✔
1370

1371
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
11✔
1372
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
11✔
1373
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
11✔
1374
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
11✔
1375

1376
   subject_dn.add_attribute(attr1, val1);
11✔
1377
   subject_dn.add_attribute(attr2, val2);
11✔
1378

1379
   const Botan::Extensions extensions;
11✔
1380

1381
   const Botan::PKCS10_Request req =
11✔
1382
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
11✔
1383

1384
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1385

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

1388
   const Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
11✔
1389
   const Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
11✔
1390
   result.confirm("Attr1 matches encoded", req_val1 == val1);
22✔
1391
   result.confirm("Attr2 matches encoded", req_val2 == val2);
22✔
1392
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
22✔
1393
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
22✔
1394

1395
   const Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1396
   const Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1397

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

1400
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1401

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

1404
   const Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
11✔
1405
   const Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
11✔
1406
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
22✔
1407
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
22✔
1408
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
22✔
1409
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
22✔
1410

1411
   return result;
11✔
1412
}
33✔
1413

1414
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
11✔
1415
                                  const std::string& sig_algo,
1416
                                  const std::string& sig_padding,
1417
                                  const std::string& hash_fn,
1418
                                  Botan::RandomNumberGenerator& rng) {
1419
   using Botan::Key_Constraints;
11✔
1420

1421
   Test::Result result("X509 Extensions");
11✔
1422

1423
   /* Create the self-signed cert */
1424
   const Botan::X509_Certificate ca_cert =
11✔
1425
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1426

1427
   /* Create the CA object */
1428
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1429

1430
   /* Prepare CDP extension */
1431
   std::vector<std::string> cdp_urls = {
11✔
1432
      "http://example.com/crl1.pem",
1433
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
11✔
1434

1435
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1436

1437
   for(const auto& uri : cdp_urls) {
33✔
1438
      Botan::AlternativeName cdp_alt_name;
22✔
1439
      cdp_alt_name.add_uri(uri);
22✔
1440
      const Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
22✔
1441

1442
      dps.emplace_back(dp);
22✔
1443
   }
22✔
1444

1445
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1446

1447
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
11✔
1448
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1449

1450
   // include a custom extension in the request
1451
   Botan::Extensions req_extensions;
11✔
1452
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
11✔
1453
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
11✔
1454
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
22✔
1455
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
22✔
1456
   opts.extensions = req_extensions;
11✔
1457
   opts.set_padding_scheme(sig_padding);
11✔
1458

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

1462
   result.confirm("Extensions::extension_set true for Key_Usage",
22✔
1463
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1464

1465
   // check if known Key_Usage extension is present in self-signed cert
1466
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
11✔
1467
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
22✔
1468
      result.confirm(
22✔
1469
         "Key_Usage extension value matches in self-signed certificate",
1470
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
11✔
1471
   }
1472

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

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

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

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

1494
   /* Create a CA-signed certificate */
1495
   const Botan::X509_Certificate ca_signed_cert =
11✔
1496
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1497

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

1501
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
22✔
1502
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
22✔
1503
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
11✔
1504
      result.confirm("Key_Usage extension value matches in user certificate",
22✔
1505
                     constraints == Botan::Key_Constraints::DigitalSignature);
11✔
1506
   }
1507

1508
   // check if custom extension is present in CA-signed cert
1509
   result.confirm("Extensions::extension_set true for String_Extension",
22✔
1510
                  ca_signed_cert.v3_extensions().extension_set(oid));
11✔
1511
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
22✔
1512
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
22✔
1513
      result.test_eq(
44✔
1514
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1515
   }
1516

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

1520
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
22✔
1521
                     !cert_cdps->crl_distribution_urls().empty())) {
11✔
1522
      for(const auto& cdp : cert_cdps->distribution_points()) {
33✔
1523
         result.confirm("CDP URI present in self-signed certificate",
44✔
1524
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
66✔
1525
      }
1526
   }
1527

1528
   return result;
11✔
1529
}
55✔
1530

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

1534
   struct TestData {
12✔
1535
         const std::string issuer, subject, issuer_hash, subject_hash;
1536
   } const cases[]{{"",
12✔
1537
                    "",
1538
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1539
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1540
                   {"a",
1541
                    "b",
1542
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1543
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1544
                   {"A",
1545
                    "B",
1546
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1547
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1548
                   {
1549
                      "Test Issuer/US/Botan Project/Testing",
1550
                      "Test Subject/US/Botan Project/Testing",
1551
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1552
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1553
                   },
1554
                   {
1555
                      "Test Subject/US/Botan Project/Testing",
1556
                      "Test Issuer/US/Botan Project/Testing",
1557
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1558
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1559
                   }};
72✔
1560

1561
   for(const auto& a : cases) {
72✔
1562
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1563
      opts.CA_key();
60✔
1564

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

1567
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1568
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1569

1570
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1571
      const Botan::PKCS10_Request req =
60✔
1572
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1573
      const Botan::X509_Certificate subject_cert =
60✔
1574
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1575

1576
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1577
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1578
   }
60✔
1579
   return result;
12✔
1580
}
72✔
1581

1582
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1583

1584
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1585
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1586

1587
      asn1=SEQUENCE:tn_auth_list
1588

1589
      [tn_auth_list]
1590
      spc=EXP:0,IA5:1001
1591
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1592
      one=EXP:2,IA5:333
1593

1594
      [TelephoneNumberRange]
1595
      start1=IA5:111
1596
      count1=INT:128
1597
      start2=IA5:222
1598
      count2=INT:256
1599
    */
1600
   const std::string filename("TNAuthList.pem");
1✔
1601
   Test::Result result("X509 TNAuthList decode");
1✔
1602
   result.start_timer();
1✔
1603

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

1606
   using Botan::Cert_Extension::TNAuthList;
1✔
1607

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

1610
   const auto& tn_entries = tn_auth_list->entries();
1✔
1611

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

1614
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1615
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1616
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1617
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1618
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1619
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1620

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

1624
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1625
   const auto& range = tn_entries[1].telephone_number_range();
1✔
1626
   result.test_eq("range entries count", range.size(), 2);
1✔
1627
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1628
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1629
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1630
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1631

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

1635
   result.end_timer();
1✔
1636
   return result;
2✔
1637
}
1✔
1638

1639
   #endif
1640

1641
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
1642
   if(sig_algo == "RSA") {
12✔
1643
      return {
1✔
1644
   #if defined(BOTAN_HAS_EMSA_PKCS1)
1645
         "PKCS1v15(" + hash + ")",
1✔
1646
   #endif
1647
   #if defined(BOTAN_HAS_EMSA_PSS)
1648
            "PSS(" + hash + ")",
1649
   #endif
1650
      };
3✔
1651
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
1652
             sig_algo == "GOST-34.10") {
7✔
1653
      return {hash};
10✔
1654
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
1655
      return {"Pure"};
2✔
1656
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
1657
      return {"Randomized"};
2✔
1658
   } else if(sig_algo == "HSS-LMS") {
2✔
1659
      return {""};
1✔
1660
   } else {
1661
      return {};
1✔
1662
   }
1663
}
6✔
1664

1665
class X509_Cert_Unit_Tests final : public Test {
1✔
1666
   public:
1667
      std::vector<Test::Result> run() override {
1✔
1668
         std::vector<Test::Result> results;
1✔
1669

1670
         auto& rng = this->rng();
1✔
1671

1672
         const std::string sig_algos[]{"RSA",
1✔
1673
                                       "DSA",
1674
                                       "ECDSA",
1675
                                       "ECGDSA",
1676
                                       "ECKCDSA",
1677
                                       "GOST-34.10",
1678
                                       "Ed25519",
1679
                                       "Ed448",
1680
                                       "Dilithium",
1681
                                       "ML-DSA",
1682
                                       "SLH-DSA",
1683
                                       "HSS-LMS"};
13✔
1684

1685
         for(const std::string& algo : sig_algos) {
13✔
1686
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1687
            if(algo == "RSA")
1688
               continue;
1689
   #endif
1690

1691
            std::string hash = "SHA-256";
12✔
1692

1693
            if(algo == "Ed25519") {
12✔
1694
               hash = "SHA-512";
1✔
1695
            }
1696
            if(algo == "Ed448") {
12✔
1697
               hash = "SHAKE-256(912)";
1✔
1698
            }
1699
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1700
               hash = "SHAKE-256(512)";
2✔
1701
            }
1702

1703
            auto key = make_a_private_key(algo, rng);
12✔
1704

1705
            if(key == nullptr) {
12✔
1706
               continue;
×
1707
            }
1708

1709
            results.push_back(test_hashes(*key, hash, rng));
24✔
1710
            results.push_back(test_valid_constraints(*key, algo));
24✔
1711

1712
            Test::Result usage_result("X509 Usage");
12✔
1713
            try {
12✔
1714
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1715
            } catch(std::exception& e) {
×
1716
               usage_result.test_failure("test_usage " + algo, e.what());
×
1717
            }
×
1718
            results.push_back(usage_result);
12✔
1719

1720
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
23✔
1721
               Test::Result cert_result("X509 Unit");
11✔
1722

1723
               try {
11✔
1724
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
11✔
1725
               } catch(std::exception& e) {
×
1726
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1727
               }
×
1728
               results.push_back(cert_result);
11✔
1729

1730
               Test::Result pkcs10_result("PKCS10 extensions");
11✔
1731
               try {
11✔
1732
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
11✔
1733
               } catch(std::exception& e) {
×
1734
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
1735
               }
×
1736
               results.push_back(pkcs10_result);
11✔
1737

1738
               Test::Result self_issued_result("X509 Self Issued");
11✔
1739
               try {
11✔
1740
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
11✔
1741
               } catch(std::exception& e) {
×
1742
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
1743
               }
×
1744
               results.push_back(self_issued_result);
11✔
1745

1746
               Test::Result extensions_result("X509 Extensions");
11✔
1747
               try {
11✔
1748
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
11✔
1749
               } catch(std::exception& e) {
×
1750
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
1751
               }
×
1752
               results.push_back(extensions_result);
11✔
1753

1754
               Test::Result custom_dn_result("X509 Custom DN");
11✔
1755
               try {
11✔
1756
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
11✔
1757
               } catch(std::exception& e) {
×
1758
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1759
               }
×
1760
               results.push_back(custom_dn_result);
11✔
1761
            }
23✔
1762
         }
24✔
1763

1764
         /*
1765
         These are algos which cannot sign but can be included in certs
1766
         */
1767
         const std::vector<std::string> enc_algos = {
1✔
1768
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
1769

1770
         for(const std::string& algo : enc_algos) {
8✔
1771
            auto key = make_a_private_key(algo, rng);
7✔
1772

1773
            if(key) {
7✔
1774
               results.push_back(test_valid_constraints(*key, algo));
14✔
1775
            }
1776
         }
7✔
1777

1778
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1779
      defined(BOTAN_HAS_RSA)
1780
         Test::Result pad_config_result("X509 Padding Config");
1✔
1781
         try {
1✔
1782
            pad_config_result.merge(test_padding_config());
1✔
1783
         } catch(const std::exception& e) {
×
1784
            pad_config_result.test_failure("test_padding_config", e.what());
×
1785
         }
×
1786
         results.push_back(pad_config_result);
1✔
1787
   #endif
1788

1789
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1790
         results.push_back(test_x509_utf8());
2✔
1791
         results.push_back(test_x509_bmpstring());
2✔
1792
         results.push_back(test_x509_teletex());
2✔
1793
         results.push_back(test_crl_dn_name());
2✔
1794
         results.push_back(test_rdn_multielement_set_name());
2✔
1795
         results.push_back(test_x509_decode_list());
2✔
1796
         results.push_back(test_rsa_oaep());
2✔
1797
         results.push_back(test_x509_authority_info_access_extension());
2✔
1798
         results.push_back(test_verify_gost2012_cert());
2✔
1799
         results.push_back(test_parse_rsa_pss_cert());
2✔
1800
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1801
   #endif
1802

1803
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1804
         results.push_back(test_x509_extension());
2✔
1805
         results.push_back(test_x509_dates());
2✔
1806
         results.push_back(test_cert_status_strings());
2✔
1807
         results.push_back(test_x509_uninit());
2✔
1808

1809
         return results;
1✔
1810
      }
13✔
1811
};
1812

1813
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1814

1815
#endif
1816

1817
}  // namespace
1818

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