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

randombit / botan / 21825138935

09 Feb 2026 12:29PM UTC coverage: 90.072% (+0.002%) from 90.07%
21825138935

push

github

web-flow
Merge pull request #5294 from randombit/jack/range-concepts-h

Split part of concepts.h into range_concepts.h

102225 of 113493 relevant lines covered (90.07%)

11473123.55 hits per line

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

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

8
#include "tests.h"
9

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

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

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_X509_CERTIFICATES)
35

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

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

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

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

52
   opts.CA_key(1);
112✔
53

54
   return opts;
112✔
55
}
×
56

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

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

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

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

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

76
   return opts;
36✔
77
}
×
78

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

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

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

89
   return opts;
11✔
90
}
×
91

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

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

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

104
   return opts;
55✔
105
}
×
106

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

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

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

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

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

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

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

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

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

207
   return result;
1✔
208
}
1✔
209

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

213
   Botan::Extensions extn;
1✔
214

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

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

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

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

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

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

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

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

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

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

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

248
   return result;
2✔
249
}
2✔
250

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

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

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

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

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

272
   time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Type::GeneralizedTime);
1✔
273
   result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
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); });
108✔
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); });
432✔
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); });
72✔
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_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
461
      return result;
462
   }
463

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

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

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

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

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

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

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

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

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

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

495
   return result;
1✔
496
}
4✔
497

498
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
499

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

503
      // See GH #1252
504

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

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

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

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

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

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

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

523
   return result;
2✔
524
}
3✔
525

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

529
   // GH #2611
530

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

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

536
   return result;
1✔
537
}
1✔
538

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

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

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

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

552
   return result;
2✔
553
}
1✔
554

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

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

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

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

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

569
   return result;
1✔
570
}
1✔
571

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

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

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

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

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

600
   return result;
1✔
601
}
×
602

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

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

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

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

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

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

626
   return result;
1✔
627
}
×
628

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

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

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

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

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

645
   return result;
1✔
646
}
×
647

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

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

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

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

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

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

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

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

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

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

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

689
   return result;
1✔
690
}
1✔
691

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

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

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

704
   return result;
1✔
705
}
×
706

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

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

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

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

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

730
   return result;
1✔
731
}
×
732

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

837
   #endif
838

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

845
   Botan::X509_Cert_Options opts;
11✔
846

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

851
   opts.padding_scheme = sig_padding;
11✔
852

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

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

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

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

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

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

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

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

879
   return result;
11✔
880
}
22✔
881

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

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

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

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

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

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

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

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

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

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

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

920
   const BigInt user1_serial(99);
11✔
921

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1020
   store.add_crl(crl2);
11✔
1021

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

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

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

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

1035
   store.add_crl(crl3);
11✔
1036

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

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

1045
   return result;
11✔
1046
}
44✔
1047

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1124
   return result;
12✔
1125
}
24✔
1126

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

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

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

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

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

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

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

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

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

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

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

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

1166
   return result;
11✔
1167
}
22✔
1168

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

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

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

1181
   return result;
1✔
1182
}
1✔
1183

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

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

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

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

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

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

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

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

1280
   return result;
19✔
1281
}
×
1282

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1336
   Botan::X509_DN subject_dn;
11✔
1337

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

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

1346
   const Botan::Extensions extensions;
11✔
1347

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

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

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

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

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

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

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

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

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

1378
   return result;
22✔
1379
}
99✔
1380

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1495
   return result;
11✔
1496
}
55✔
1497

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

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

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

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

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

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

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

1549
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1550

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

1554
      asn1=SEQUENCE:tn_auth_list
1555

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

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

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

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

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

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

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

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

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

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

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

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

1606
   #endif
1607

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1782
#endif
1783

1784
}  // namespace
1785

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