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

randombit / botan / 22570762269

02 Mar 2026 07:15AM UTC coverage: 90.317% (+0.009%) from 90.308%
22570762269

push

github

web-flow
Merge pull request #5231 from Rohde-Schwarz/feature/multiple_ocsp_responders

X509: Multiple OCSP Responders

103503 of 114600 relevant lines covered (90.32%)

11810303.81 hits per line

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

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

8
#include "tests.h"
9

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

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

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_X509_CERTIFICATES)
35

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

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

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

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

52
   opts.CA_key(1);
112✔
53

54
   return opts;
112✔
55
}
×
56

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

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

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

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

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

76
   return opts;
37✔
77
}
×
78

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

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

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

89
   return opts;
11✔
90
}
×
91

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

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

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

104
   return opts;
55✔
105
}
×
106

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

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

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

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

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

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

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

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

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

207
   return result;
1✔
208
}
1✔
209

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

213
   Botan::Extensions extn;
1✔
214

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

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

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

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

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

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

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

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

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

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

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

248
   return result;
2✔
249
}
2✔
250

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423
   return result;
1✔
424
}
80✔
425

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

520
   return result;
1✔
521
}
4✔
522

523
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
524

525
Test::Result test_crl_dn_name() {
1✔
526
   Test::Result result("CRL DN name");
1✔
527

528
      // See GH #1252
529

530
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
531
   auto rng = Test::new_rng(__func__);
1✔
532

533
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
534

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

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

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

543
   result.test_is_true("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
1✔
544

545
   result.test_is_true("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
2✔
546
      #endif
547

548
   return result;
2✔
549
}
3✔
550

551
Test::Result test_rdn_multielement_set_name() {
1✔
552
   Test::Result result("DN with multiple elements in RDN");
1✔
553

554
   // GH #2611
555

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

558
   result.test_is_true("issuer DN contains expected name components", cert.issuer_dn().get_attributes().size() == 4);
1✔
559
   result.test_is_true("subject DN contains expected name components", cert.subject_dn().get_attributes().size() == 4);
1✔
560

561
   return result;
1✔
562
}
1✔
563

564
Test::Result test_rsa_oaep() {
1✔
565
   Test::Result result("RSA OAEP decoding");
1✔
566

567
      #if defined(BOTAN_HAS_RSA)
568
   const Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
569

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

574
   result.test_str_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
1✔
575
      #endif
576

577
   return result;
2✔
578
}
1✔
579

580
Test::Result test_x509_decode_list() {
1✔
581
   Test::Result result("X509_Certificate list decode");
1✔
582

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

585
   Botan::BER_Decoder dec(input);
1✔
586
   std::vector<Botan::X509_Certificate> certs;
1✔
587
   dec.decode_list(certs);
1✔
588

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

591
   result.test_str_eq("Expected cert 1 CN", certs[0].subject_dn().get_first_attribute("CN"), "CA1-PP.01.02");
1✔
592
   result.test_str_eq("Expected cert 2 CN", certs[1].subject_dn().get_first_attribute("CN"), "User1-PP.01.02");
1✔
593

594
   return result;
1✔
595
}
1✔
596

597
Test::Result test_x509_utf8() {
1✔
598
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
599

600
   try {
1✔
601
      const Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
602

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

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

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

625
   return result;
1✔
626
}
×
627

628
Test::Result test_x509_bmpstring() {
1✔
629
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
630

631
   try {
1✔
632
      const Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
633

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

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

642
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
643

644
      result.test_str_eq("O", issuer_dn.get_first_attribute("O"), organization);
1✔
645
      result.test_str_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
1✔
646
      result.test_str_eq("L", issuer_dn.get_first_attribute("L"), location);
1✔
647
   } catch(const Botan::Decoding_Error& ex) {
1✔
648
      result.test_failure(ex.what());
×
649
   }
×
650

651
   return result;
1✔
652
}
×
653

654
Test::Result test_x509_teletex() {
1✔
655
   Test::Result result("X509 with TeletexString encoded fields");
1✔
656

657
   try {
1✔
658
      const Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
659

660
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
661

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

664
      result.test_str_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
1✔
665
      result.test_str_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
1✔
666
   } catch(const Botan::Decoding_Error& ex) {
1✔
667
      result.test_failure(ex.what());
×
668
   }
×
669

670
   return result;
1✔
671
}
×
672

673
Test::Result test_x509_authority_info_access_extension() {
1✔
674
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
675

676
   // contains no AIA extension
677
   const Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
678

679
   result.test_sz_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
1✔
680
   result.test_str_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
1✔
681

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

685
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
686

687
   result.test_sz_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
688
   if(result.tests_failed() > 0) {
1✔
689
      return result;
690
   }
691

692
   result.test_str_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
1✔
693
   result.test_str_eq("OCSP responder URL matches", aia_cert.ocsp_responder(), "http://gp.symcd.com");
1✔
694

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

699
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
700

701
   result.test_sz_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
702
   if(result.tests_failed() > 0) {
1✔
703
      return result;
704
   }
705

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

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

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

725
   return result;
1✔
726
}
1✔
727

728
Test::Result test_parse_rsa_pss_cert() {
1✔
729
   Test::Result result("X509 RSA-PSS certificate");
1✔
730

731
   // See https://github.com/randombit/botan/issues/3019 for background
732

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

740
   return result;
1✔
741
}
×
742

743
Test::Result test_verify_gost2012_cert() {
1✔
744
   Test::Result result("X509 GOST-2012 certificates");
1✔
745

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

752
         Botan::Certificate_Store_In_Memory trusted;
1✔
753
         trusted.add_certificate(root_cert);
1✔
754

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

759
         result.test_is_true("GOST certificate validates", validation_result.successful_validation());
1✔
760
      }
1✔
761
   } catch(const Botan::Decoding_Error& e) {
×
762
      result.test_failure(e.what());
×
763
   }
×
764
      #endif
765

766
   return result;
1✔
767
}
×
768

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

778
   auto rng = Test::new_rng(__func__);
1✔
779

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

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

787
   const Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
788
   test_result.test_str_eq("CA certificate signature algorithm (default)",
1✔
789
                           ca_cert_def.signature_algorithm().oid().to_formatted_string(),
1✔
790
                           "RSA/PKCS1v15(SHA-512)");
791

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

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

813
   test_result.test_str_eq("CA certificate signature algorithm (explicit)",
1✔
814
                           ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
1✔
815
                           "RSA/PSS");
816

817
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
818
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
819

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

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

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

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

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

858
   // Check CRL signature algorithm
859
   const Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
860
   test_result.test_str_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
1✔
861

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

869
   return test_result;
2✔
870
}
3✔
871
      #endif
872

873
   #endif
874

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

881
   Botan::X509_Cert_Options opts;
11✔
882

883
   opts.dns = "main.example.org";
11✔
884
   opts.more_dns.push_back("more1.example.org");
22✔
885
   opts.more_dns.push_back("more2.example.org");
22✔
886

887
   opts.padding_scheme = sig_padding;
11✔
888

889
   Botan::AlternativeName alt_name;
11✔
890
   alt_name.add_attribute("DNS", "bonus.example.org");
11✔
891

892
   Botan::X509_DN alt_dn;
11✔
893
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
11✔
894
   alt_dn.add_attribute("X520.Organization", "testing");
11✔
895
   alt_name.add_dn(alt_dn);
11✔
896

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

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

901
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
11✔
902

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

905
   if(alt_dns_names.size() == 4) {
11✔
906
      result.test_str_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
11✔
907
      result.test_str_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
11✔
908
      result.test_str_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
11✔
909
      result.test_str_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
11✔
910
   }
911

912
   result.test_sz_eq("Expected number of alt DNs", req.subject_alt_name().directory_names().size(), 1);
11✔
913
   result.test_is_true("Alt DN is correct", *req.subject_alt_name().directory_names().begin() == alt_dn);
11✔
914

915
   return result;
11✔
916
}
22✔
917

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

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

928
   {
11✔
929
      result.test_is_true("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
11✔
930
      result.test_is_true("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
11✔
931
   }
932

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

936
   const Botan::PKCS10_Request user1_req =
11✔
937
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
11✔
938

939
   result.test_str_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom");
11✔
940

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

944
   const Botan::PKCS10_Request user2_req =
11✔
945
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
11✔
946

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

950
   const Botan::PKCS10_Request user3_req =
11✔
951
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
11✔
952

953
   /* Create the CA object */
954
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
955

956
   const BigInt user1_serial(99);
11✔
957

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

962
   result.test_sz_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
11✔
963
   result.test_sz_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
11✔
964

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

969
   const Botan::X509_Certificate user3_cert =
11✔
970
      ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
971

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

976
   {
11✔
977
      auto constraints = req_opts1(sig_algo).constraints;
11✔
978
      result.test_is_true("user1 key usage", user1_cert.constraints().includes(constraints));
11✔
979
   }
980

981
   /* Copy, assign and compare */
982
   Botan::X509_Certificate user1_cert_copy(user1_cert);
11✔
983
   result.test_is_true("certificate copy", user1_cert == user1_cert_copy);
11✔
984

985
   user1_cert_copy = user2_cert;
11✔
986
   result.test_is_true("certificate assignment", user2_cert == user1_cert_copy);
11✔
987

988
   const Botan::X509_Certificate user1_cert_differ =
11✔
989
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
990

991
   result.test_is_false("certificate differs", user1_cert == user1_cert_differ);
11✔
992

993
   /* Get cert data */
994
   result.test_sz_eq("x509 version", user1_cert.x509_version(), size_t(3));
11✔
995

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

1002
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
11✔
1003
   result.test_sz_eq("subject OrgaUnit count",
11✔
1004
                     user3_subject_dn.get_attribute("OU").size(),
22✔
1005
                     req_opts3(sig_algo).more_org_units.size() + 1);
22✔
1006
   result.test_str_eq(
22✔
1007
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
33✔
1008

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

1014
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
11✔
1015
   result.test_sz_eq(
11✔
1016
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
22✔
1017
   result.test_str_eq(
22✔
1018
      "subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
33✔
1019

1020
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
11✔
1021

1022
   /* Verify the certs */
1023
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1024
   Botan::Certificate_Store_In_Memory store;
11✔
1025

1026
   // First try with an empty store
1027
   const Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1028
   result.test_str_eq(
11✔
1029
      "user 1 issuer not found",
1030
      result_no_issuer.result_string(),
11✔
1031
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1032

1033
   store.add_certificate(ca.ca_certificate());
11✔
1034

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

1040
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1041
   if(!result.test_is_true("user 2 validates", result_u2.successful_validation())) {
11✔
1042
      result.test_note("user 2 validation result", result_u2.result_string());
×
1043
   }
1044

1045
   const Botan::Path_Validation_Result result_self_signed =
11✔
1046
      Botan::x509_path_validate(user1_ss_cert, restrictions, store);
11✔
1047
   result.test_str_eq(
11✔
1048
      "user 1 issuer not found",
1049
      result_no_issuer.result_string(),
11✔
1050
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1051
   store.add_crl(crl1);
11✔
1052

1053
   std::vector<Botan::CRL_Entry> revoked;
11✔
1054
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1055
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1056

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

1059
   store.add_crl(crl2);
11✔
1060

1061
   const std::string revoked_str =
11✔
1062
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1063

1064
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1065
   result.test_str_eq("user 1 revoked", result_u1.result_string(), revoked_str);
11✔
1066

1067
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1068
   result.test_str_eq("user 1 revoked", result_u2.result_string(), revoked_str);
11✔
1069

1070
   revoked.clear();
11✔
1071
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
22✔
1072
   const Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
11✔
1073

1074
   store.add_crl(crl3);
11✔
1075

1076
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1077
   if(!result.test_is_true("user 1 validates", result_u1.successful_validation())) {
11✔
1078
      result.test_note("user 1 validation result", result_u1.result_string());
×
1079
   }
1080

1081
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1082
   result.test_str_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
11✔
1083

1084
   return result;
11✔
1085
}
44✔
1086

1087
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1088
                        const std::string& sig_algo,
1089
                        const std::string& hash_fn,
1090
                        Botan::RandomNumberGenerator& rng) {
1091
   using Botan::Key_Constraints;
12✔
1092
   using Botan::Usage_Type;
12✔
1093

1094
   Test::Result result("X509 Usage");
12✔
1095

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

1099
   /* Create the CA object */
1100
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1101

1102
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1103

1104
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1105
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1106

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

1109
   const Botan::X509_Certificate user1_cert =
12✔
1110
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1111

1112
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1113
   result.test_is_false(
12✔
1114
      "key usage cRLSign not allowed",
1115
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1116
   result.test_is_false("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION));
12✔
1117

1118
   // cert only allows digitalSignature, so checking for only that should be ok
1119
   result.test_is_true("key usage digitalSignature allowed",
12✔
1120
                       user1_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1121

1122
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1123

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

1126
   const Botan::X509_Certificate mult_usage_cert =
12✔
1127
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1128

1129
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1130
   result.test_is_true("key usage multiple digitalSignature allowed",
12✔
1131
                       mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1132
   result.test_is_true("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
12✔
1133
   result.test_is_true(
12✔
1134
      "key usage multiple digitalSignature and cRLSign allowed",
1135
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1136
   result.test_is_false("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
12✔
1137

1138
   opts.constraints = Key_Constraints();
12✔
1139

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

1142
   const Botan::X509_Certificate no_usage_cert =
12✔
1143
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1144

1145
   // cert allows every usage
1146
   result.test_is_true("key usage digitalSignature allowed",
12✔
1147
                       no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1148
   result.test_is_true("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
12✔
1149
   result.test_is_true("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
12✔
1150

1151
   if(sig_algo == "RSA") {
12✔
1152
      // cert allows data encryption
1153
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1154

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

1157
      const Botan::X509_Certificate enc_cert =
1✔
1158
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1159

1160
      result.test_is_true("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
1✔
1161
      result.test_is_true("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
1✔
1162
   }
1✔
1163

1164
   return result;
12✔
1165
}
24✔
1166

1167
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
11✔
1168
                              const std::string& sig_algo,
1169
                              const std::string& sig_padding,
1170
                              const std::string& hash_fn,
1171
                              Botan::RandomNumberGenerator& rng) {
1172
   using Botan::Key_Constraints;
11✔
1173

1174
   Test::Result result("X509 Self Issued");
11✔
1175

1176
   // create the self-signed cert
1177
   const Botan::X509_Certificate ca_cert =
11✔
1178
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1179

1180
   /* Create the CA object */
1181
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1182

1183
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1184

1185
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1186
   // but signed by a CA, not signed by it's own private key
1187
   Botan::X509_Cert_Options opts = ca_opts();
11✔
1188
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1189
   opts.set_padding_scheme(sig_padding);
11✔
1190

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

1193
   const Botan::X509_Certificate self_issued_cert =
11✔
1194
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1195

1196
   // check that this chain can can be verified successfully
1197
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
11✔
1198

1199
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1200

1201
   const Botan::Path_Validation_Result validation_result =
11✔
1202
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
11✔
1203

1204
   result.test_is_true("chain with self-issued cert validates", validation_result.successful_validation());
11✔
1205

1206
   return result;
11✔
1207
}
22✔
1208

1209
Test::Result test_x509_uninit() {
1✔
1210
   Test::Result result("X509 object uninitialized access");
1✔
1211

1212
   Botan::X509_Certificate cert;
1✔
1213
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
1✔
1214
      cert.x509_version();
1✔
1215
   });
1216

1217
   Botan::X509_CRL crl;
1✔
1218
   result.test_throws(
1✔
1219
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1220

1221
   return result;
1✔
1222
}
1✔
1223

1224
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1225
   using Botan::Key_Constraints;
19✔
1226

1227
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1228

1229
   result.test_is_true("empty constraints always acceptable", Key_Constraints().compatible_with(key));
19✔
1230

1231
   // Now check some typical usage scenarios for the given key type
1232
   // Taken from RFC 5280, sec. 4.2.1.3
1233
   // ALL constraints are not typical at all, but we use them for a negative test
1234
   const auto all = Key_Constraints(
19✔
1235
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1236
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1237
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1238

1239
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1240
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1241
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1242
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1243
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1244
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1245
   const auto key_agreement_encipher_only =
19✔
1246
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1247
   const auto key_agreement_decipher_only =
19✔
1248
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1249
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1250
   const auto sign_everything =
19✔
1251
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1252

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

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

1308
      result.test_is_true("ca allowed", ca.compatible_with(key));
9✔
1309
      result.test_is_true("sign allowed", sign_data.compatible_with(key));
9✔
1310
      result.test_is_true("non-repudiation allowed", non_repudiation.compatible_with(key));
9✔
1311
      result.test_is_false("key encipherment not permitted", key_encipherment.compatible_with(key));
9✔
1312
      result.test_is_false("data encipherment not permitted", data_encipherment.compatible_with(key));
9✔
1313
      result.test_is_false("key agreement not permitted", key_agreement.compatible_with(key));
9✔
1314
      result.test_is_false("key agreement", key_agreement_encipher_only.compatible_with(key));
9✔
1315
      result.test_is_false("key agreement", key_agreement_decipher_only.compatible_with(key));
9✔
1316
      result.test_is_true("crl sign allowed", crl_sign.compatible_with(key));
9✔
1317
      result.test_is_true("sign allowed", sign_everything.compatible_with(key));
9✔
1318
   }
1319

1320
   return result;
19✔
1321
}
×
1322

1323
/**
1324
 * @brief X.509v3 extension that encodes a given string
1325
 */
1326
class String_Extension final : public Botan::Certificate_Extension {
22✔
1327
   public:
1328
      String_Extension() = default;
22✔
1329

1330
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1331

1332
      std::string value() const { return m_contents; }
44✔
1333

1334
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1335
         return std::make_unique<String_Extension>(m_contents);
×
1336
      }
1337

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

1340
      bool should_encode() const override { return true; }
22✔
1341

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

1344
      std::vector<uint8_t> encode_inner() const override {
11✔
1345
         std::vector<uint8_t> bits;
11✔
1346
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
22✔
1347
         return bits;
11✔
1348
      }
×
1349

1350
      void decode_inner(const std::vector<uint8_t>& in) override {
22✔
1351
         Botan::ASN1_String str;
22✔
1352
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
22✔
1353
         m_contents = str.value();
44✔
1354
      }
22✔
1355

1356
   private:
1357
      std::string m_contents;
1358
};
1359

1360
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
11✔
1361
                                 const std::string& sig_algo,
1362
                                 const std::string& sig_padding,
1363
                                 const std::string& hash_fn,
1364
                                 Botan::RandomNumberGenerator& rng) {
1365
   Test::Result result("X509 Custom DN");
11✔
1366

1367
   /* Create the self-signed cert */
1368
   const Botan::X509_Certificate ca_cert =
11✔
1369
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1370

1371
   /* Create the CA object */
1372
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1373

1374
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1375

1376
   Botan::X509_DN subject_dn;
11✔
1377

1378
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
11✔
1379
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
11✔
1380
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
11✔
1381
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
11✔
1382

1383
   subject_dn.add_attribute(attr1, val1);
11✔
1384
   subject_dn.add_attribute(attr2, val2);
11✔
1385

1386
   const Botan::Extensions extensions;
11✔
1387

1388
   const Botan::PKCS10_Request req =
11✔
1389
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
11✔
1390

1391
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1392

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

1395
   const Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
11✔
1396
   const Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
11✔
1397
   result.test_is_true("Attr1 matches encoded", req_val1 == val1);
11✔
1398
   result.test_is_true("Attr2 matches encoded", req_val2 == val2);
11✔
1399
   result.test_is_true("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
11✔
1400
   result.test_is_true("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
11✔
1401

1402
   const Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1403
   const Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1404

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

1407
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1408

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

1411
   const Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
11✔
1412
   const Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
11✔
1413
   result.test_is_true("Attr1 matches encoded", cert_val1 == val1);
11✔
1414
   result.test_is_true("Attr2 matches encoded", cert_val2 == val2);
11✔
1415
   result.test_is_true("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
11✔
1416
   result.test_is_true("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
11✔
1417

1418
   return result;
22✔
1419
}
99✔
1420

1421
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
11✔
1422
                                  const std::string& sig_algo,
1423
                                  const std::string& sig_padding,
1424
                                  const std::string& hash_fn,
1425
                                  Botan::RandomNumberGenerator& rng) {
1426
   using Botan::Key_Constraints;
11✔
1427

1428
   Test::Result result("X509 Extensions");
11✔
1429

1430
   /* Create the self-signed cert */
1431
   const Botan::X509_Certificate ca_cert =
11✔
1432
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
11✔
1433

1434
   /* Create the CA object */
1435
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1436

1437
   /* Prepare CDP extension */
1438
   std::vector<std::string> cdp_urls = {
11✔
1439
      "http://example.com/crl1.pem",
1440
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
11✔
1441

1442
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1443

1444
   for(const auto& uri : cdp_urls) {
33✔
1445
      Botan::AlternativeName cdp_alt_name;
22✔
1446
      cdp_alt_name.add_uri(uri);
22✔
1447
      const Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
22✔
1448

1449
      dps.emplace_back(dp);
22✔
1450
   }
22✔
1451

1452
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1453

1454
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
11✔
1455
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1456

1457
   // include a custom extension in the request
1458
   Botan::Extensions req_extensions;
11✔
1459
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
11✔
1460
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
11✔
1461
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
22✔
1462
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
22✔
1463
   opts.extensions = req_extensions;
11✔
1464
   opts.set_padding_scheme(sig_padding);
11✔
1465

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

1469
   result.test_is_true("Extensions::extension_set true for Key_Usage",
11✔
1470
                       self_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1471

1472
   // check if known Key_Usage extension is present in self-signed cert
1473
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
11✔
1474
   if(result.test_is_true("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
11✔
1475
      result.test_is_true(
22✔
1476
         "Key_Usage extension value matches in self-signed certificate",
1477
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
11✔
1478
   }
1479

1480
   // check if custom extension is present in self-signed cert
1481
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
11✔
1482
   if(result.test_is_true("Custom extension present in self-signed certificate", string_ext != nullptr)) {
11✔
1483
      result.test_str_eq(
22✔
1484
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1485
   }
1486

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

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

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

1501
   /* Create a CA-signed certificate */
1502
   const Botan::X509_Certificate ca_signed_cert =
11✔
1503
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
11✔
1504

1505
   // check if known Key_Usage extension is present in CA-signed cert
1506
   result.test_is_true("Extensions::extension_set true for Key_Usage",
11✔
1507
                       ca_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1508

1509
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
22✔
1510
   if(result.test_is_true("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
11✔
1511
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
11✔
1512
      result.test_is_true("Key_Usage extension value matches in user certificate",
22✔
1513
                          constraints == Botan::Key_Constraints::DigitalSignature);
11✔
1514
   }
1515

1516
   // check if custom extension is present in CA-signed cert
1517
   result.test_is_true("Extensions::extension_set true for String_Extension",
11✔
1518
                       ca_signed_cert.v3_extensions().extension_set(oid));
11✔
1519
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
22✔
1520
   if(result.test_is_true("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
11✔
1521
      result.test_str_eq(
22✔
1522
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
33✔
1523
   }
1524

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

1528
   if(result.test_is_true("CRL Distribution Points extension present in self-signed certificate",
11✔
1529
                          !cert_cdps->crl_distribution_urls().empty())) {
11✔
1530
      for(const auto& cdp : cert_cdps->distribution_points()) {
33✔
1531
         result.test_is_true("CDP URI present in self-signed certificate",
44✔
1532
                             std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
66✔
1533
      }
1534
   }
1535

1536
   return result;
11✔
1537
}
55✔
1538

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

1542
   struct TestData {
12✔
1543
         const std::string issuer, subject, issuer_hash, subject_hash;
1544
   } const cases[]{{"",
12✔
1545
                    "",
1546
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1547
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1548
                   {"a",
1549
                    "b",
1550
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1551
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1552
                   {"A",
1553
                    "B",
1554
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1555
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1556
                   {
1557
                      "Test Issuer/US/Botan Project/Testing",
1558
                      "Test Subject/US/Botan Project/Testing",
1559
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1560
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1561
                   },
1562
                   {
1563
                      "Test Subject/US/Botan Project/Testing",
1564
                      "Test Issuer/US/Botan Project/Testing",
1565
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1566
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1567
                   }};
72✔
1568

1569
   for(const auto& a : cases) {
72✔
1570
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1571
      opts.CA_key();
60✔
1572

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

1575
      result.test_str_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1576
      result.test_str_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1577

1578
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1579
      const Botan::PKCS10_Request req =
60✔
1580
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1581
      const Botan::X509_Certificate subject_cert =
60✔
1582
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1583

1584
      result.test_str_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1585
      result.test_str_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1586
   }
60✔
1587
   return result;
12✔
1588
}
72✔
1589

1590
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1591

1592
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1593
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1594

1595
      asn1=SEQUENCE:tn_auth_list
1596

1597
      [tn_auth_list]
1598
      spc=EXP:0,IA5:1001
1599
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1600
      one=EXP:2,IA5:333
1601

1602
      [TelephoneNumberRange]
1603
      start1=IA5:111
1604
      count1=INT:128
1605
      start2=IA5:222
1606
      count2=INT:256
1607
    */
1608
   const std::string filename("TNAuthList.pem");
1✔
1609
   Test::Result result("X509 TNAuthList decode");
1✔
1610
   result.start_timer();
1✔
1611

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

1614
   using Botan::Cert_Extension::TNAuthList;
1✔
1615

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

1618
   const auto& tn_entries = tn_auth_list->entries();
1✔
1619

1620
   result.test_not_null("cert has TNAuthList extension", tn_auth_list);
1✔
1621

1622
   result.test_throws("wrong telephone_number_range() accessor for spc",
1✔
1623
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1624
   result.test_throws("wrong telephone_number() accessor for range",
1✔
1625
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1626
   result.test_throws("wrong service_provider_code() accessor for one",
1✔
1627
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1628

1629
   result.test_is_true("spc entry type", tn_entries[0].type() == TNAuthList::Entry::ServiceProviderCode);
1✔
1630
   result.test_str_eq("spc entry data", tn_entries[0].service_provider_code(), "1001");
1✔
1631

1632
   result.test_is_true("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange);
1✔
1633
   const auto& range = tn_entries[1].telephone_number_range();
1✔
1634
   result.test_sz_eq("range entries count", range.size(), 2);
1✔
1635
   result.test_str_eq("range entry 0 start data", range[0].start.value(), "111");
1✔
1636
   result.test_sz_eq("range entry 0 count data", range[0].count, 128);
1✔
1637
   result.test_str_eq("range entry 1 start data", range[1].start.value(), "222");
1✔
1638
   result.test_sz_eq("range entry 1 count data", range[1].count, 256);
1✔
1639

1640
   result.test_is_true("one entry type", tn_entries[2].type() == TNAuthList::Entry::TelephoneNumber);
1✔
1641
   result.test_str_eq("one entry data", tn_entries[2].telephone_number(), "333");
1✔
1642

1643
   result.end_timer();
1✔
1644
   return result;
2✔
1645
}
1✔
1646

1647
   #endif
1648

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

1673
class X509_Cert_Unit_Tests final : public Test {
1✔
1674
   public:
1675
      std::vector<Test::Result> run() override {
1✔
1676
         std::vector<Test::Result> results;
1✔
1677

1678
         auto& rng = this->rng();
1✔
1679

1680
         const std::string sig_algos[]{"RSA",
1✔
1681
                                       "DSA",
1682
                                       "ECDSA",
1683
                                       "ECGDSA",
1684
                                       "ECKCDSA",
1685
                                       "GOST-34.10",
1686
                                       "Ed25519",
1687
                                       "Ed448",
1688
                                       "Dilithium",
1689
                                       "ML-DSA",
1690
                                       "SLH-DSA",
1691
                                       "HSS-LMS"};
13✔
1692

1693
         for(const std::string& algo : sig_algos) {
13✔
1694
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1695
            if(algo == "RSA")
1696
               continue;
1697
   #endif
1698

1699
            std::string hash = "SHA-256";
12✔
1700

1701
            if(algo == "Ed25519") {
12✔
1702
               hash = "SHA-512";
1✔
1703
            }
1704
            if(algo == "Ed448") {
12✔
1705
               hash = "SHAKE-256(912)";
1✔
1706
            }
1707
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1708
               hash = "SHAKE-256(512)";
2✔
1709
            }
1710

1711
            auto key = make_a_private_key(algo, rng);
12✔
1712

1713
            if(key == nullptr) {
12✔
1714
               continue;
×
1715
            }
1716

1717
            results.push_back(test_hashes(*key, hash, rng));
24✔
1718
            results.push_back(test_valid_constraints(*key, algo));
24✔
1719

1720
            Test::Result usage_result("X509 Usage");
12✔
1721
            try {
12✔
1722
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1723
            } catch(std::exception& e) {
×
1724
               usage_result.test_failure("test_usage " + algo, e.what());
×
1725
            }
×
1726
            results.push_back(usage_result);
12✔
1727

1728
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
23✔
1729
               Test::Result cert_result("X509 Unit");
11✔
1730

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

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

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

1754
               Test::Result extensions_result("X509 Extensions");
11✔
1755
               try {
11✔
1756
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
11✔
1757
               } catch(std::exception& e) {
×
1758
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
1759
               }
×
1760
               results.push_back(extensions_result);
11✔
1761

1762
               Test::Result custom_dn_result("X509 Custom DN");
11✔
1763
               try {
11✔
1764
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
11✔
1765
               } catch(std::exception& e) {
×
1766
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1767
               }
×
1768
               results.push_back(custom_dn_result);
11✔
1769
            }
23✔
1770
         }
24✔
1771

1772
         /*
1773
         These are algos which cannot sign but can be included in certs
1774
         */
1775
         const std::vector<std::string> enc_algos = {
1✔
1776
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
1777

1778
         for(const std::string& algo : enc_algos) {
8✔
1779
            auto key = make_a_private_key(algo, rng);
7✔
1780

1781
            if(key) {
7✔
1782
               results.push_back(test_valid_constraints(*key, algo));
14✔
1783
            }
1784
         }
7✔
1785

1786
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1787
      defined(BOTAN_HAS_RSA)
1788
         Test::Result pad_config_result("X509 Padding Config");
1✔
1789
         try {
1✔
1790
            pad_config_result.merge(test_padding_config());
1✔
1791
         } catch(const std::exception& e) {
×
1792
            pad_config_result.test_failure("test_padding_config", e.what());
×
1793
         }
×
1794
         results.push_back(pad_config_result);
1✔
1795
   #endif
1796

1797
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1798
         results.push_back(test_x509_utf8());
2✔
1799
         results.push_back(test_x509_bmpstring());
2✔
1800
         results.push_back(test_x509_teletex());
2✔
1801
         results.push_back(test_crl_dn_name());
2✔
1802
         results.push_back(test_rdn_multielement_set_name());
2✔
1803
         results.push_back(test_x509_decode_list());
2✔
1804
         results.push_back(test_rsa_oaep());
2✔
1805
         results.push_back(test_x509_authority_info_access_extension());
2✔
1806
         results.push_back(test_verify_gost2012_cert());
2✔
1807
         results.push_back(test_parse_rsa_pss_cert());
2✔
1808
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1809
   #endif
1810

1811
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1812
         results.push_back(test_x509_extension());
2✔
1813
         results.push_back(test_x509_dates());
2✔
1814
         results.push_back(test_cert_status_strings());
2✔
1815
         results.push_back(test_x509_uninit());
2✔
1816

1817
         return results;
1✔
1818
      }
13✔
1819
};
1820

1821
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1822

1823
#endif
1824

1825
}  // namespace
1826

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