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

randombit / botan / 13678541372

05 Mar 2025 02:41PM UTC coverage: 91.682% (-0.005%) from 91.687%
13678541372

Pull #4732

github

web-flow
Merge 060ed46d4 into e2a66f99f
Pull Request #4732: Allow using CRLs without a nextUpdate field

95873 of 104571 relevant lines covered (91.68%)

11440074.5 hits per line

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

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

8
#include "tests.h"
9

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

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

27
namespace Botan_Tests {
28

29
namespace {
30

31
#if defined(BOTAN_HAS_X509_CERTIFICATES)
32

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

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

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

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

49
   opts.CA_key(1);
121✔
50

51
   return opts;
121✔
52
}
×
53

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

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

62
   opts.not_before("160101200000Z");
39✔
63
   opts.not_after("300101200000Z");
39✔
64

65
   opts.challenge = "zoom";
39✔
66

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

73
   return opts;
39✔
74
}
×
75

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

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

84
   opts.add_ex_constraint("PKIX.EmailProtection");
12✔
85

86
   return opts;
12✔
87
}
×
88

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

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

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

101
   return opts;
60✔
102
}
×
103

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

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

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

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

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

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

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

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

195
   for(const auto code : codes) {
45✔
196
      const std::string s = Botan::to_string(code);
44✔
197
      result.confirm("String is long enough to be informative", s.size() > 12);
88✔
198
      result.test_eq("No duplicates", seen.count(s), 0);
44✔
199
      seen.insert(s);
44✔
200
   }
44✔
201

202
   return result;
1✔
203
}
1✔
204

205
Test::Result test_x509_extension() {
1✔
206
   Test::Result result("X509 Extensions API");
1✔
207

208
   Botan::Extensions extn;
1✔
209

210
   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
1✔
211
   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
1✔
212

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

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

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

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

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

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

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

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

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

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

243
   return result;
2✔
244
}
2✔
245

246
Test::Result test_x509_dates() {
1✔
247
   Test::Result result("X509 Time");
1✔
248

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

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

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

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

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

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

279
      "080201000000-0000",
280
      "080201172412+0000",
281
      "040614233433-0500",
282
      "990614233444+0500",
283
      "000614233455-0530",
284
      "000614233455+0530",
285
   };
13✔
286

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

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

304
      // No seconds
305
      "0802010000Z",
306
      "0802011724Z",
307
      "0406142334Z",
308
      "9906142334Z",
309
      "0006142334Z",
310

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

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

344
      // wrong time zone
345
      "080201000000",
346
      "080201000000z",
347

348
      // Fractional seconds
349
      "170217180154.001Z",
350

351
      // Timezone offset
352
      "170217180154+0100",
353

354
      // Extra digits
355
      "17021718015400Z",
356

357
      // Non-digits
358
      "17021718015aZ",
359

360
      // Trailing garbage
361
      "170217180154Zlongtrailinggarbage",
362

363
      // Swapped type
364
      "20170217180154Z",
365
   };
49✔
366

367
   // valid length 15
368
   const std::string valid_generalized_time[]{
1✔
369
      "20000305100350Z",
370
   };
2✔
371

372
   const std::string invalid_generalized[]{
1✔
373
      // No trailing Z
374
      "20000305100350",
375

376
      // No seconds
377
      "200003051003Z",
378

379
      // Fractional seconds
380
      "20000305100350.001Z",
381

382
      // Timezone offset
383
      "20170217180154+0100",
384

385
      // Extra digits
386
      "2017021718015400Z",
387

388
      // Non-digits
389
      "2017021718015aZ",
390

391
      // Trailing garbage
392
      "20170217180154Zlongtrailinggarbage",
393

394
      // Swapped type
395
      "170217180154Z",
396
   };
9✔
397

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

402
   for(const auto& v : valid_utc) {
6✔
403
      Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
404
   }
5✔
405

406
   for(const auto& v : valid_generalized_time) {
2✔
407
      Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
408
   }
1✔
409

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

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

418
   return result;
1✔
419
}
80✔
420

421
Test::Result test_x509_encode_authority_info_access_extension() {
1✔
422
   Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension");
1✔
423

424
   #if defined(BOTAN_HAS_RSA)
425
   auto rng = Test::new_rng(__func__);
1✔
426

427
   const std::string sig_algo{"RSA"};
1✔
428
   const std::string hash_fn{"SHA-256"};
1✔
429
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
430

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

436
   // OCSP
437
   const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"};
1✔
438

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

445
   // create a certificate with only caIssuer information
446
   auto key = make_a_private_key(sig_algo, *rng);
1✔
447

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

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

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

455
   if(!result.test_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
456
      return result;
457
   }
458

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

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

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

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

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

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

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

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

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

486
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
487
   result.confirm("CA Issuer URI available", !cert.ca_issuers().empty());
2✔
488
   #endif
489

490
   return result;
1✔
491
}
4✔
492

493
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
494

495
Test::Result test_crl_dn_name() {
1✔
496
   Test::Result result("CRL DN name");
1✔
497

498
      // See GH #1252
499

500
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
501
   auto rng = Test::new_rng(__func__);
1✔
502

503
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
504

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

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

511
   Botan::X509_CRL crl = ca.new_crl(*rng);
1✔
512

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

515
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
3✔
516
      #endif
517

518
   return result;
2✔
519
}
3✔
520

521
Test::Result test_crl_without_next_update() {
1✔
522
   Test::Result result("CRL without nextUpdate");
1✔
523

524
   const auto crl = [&]() -> std::optional<Botan::X509_CRL> {
3✔
525
      try {
1✔
526
         return Botan::X509_CRL(Test::data_file("x509/misc/crl_without_nextupdate/valid_forever.crl"));
2✔
527
      } catch(const Botan::Exception& e) {
×
528
         result.test_failure(e.what());
×
529
      }
×
530

531
      return {};
×
532
   }();
1✔
533

534
   if(!crl) {
1✔
535
      return result;
536
   }
537

538
   result.confirm("this update is set", crl->this_update().time_is_set());
2✔
539
   result.confirm("next update is not set", !crl->next_update().time_is_set());
2✔
540
   result.confirm("CRL is not empty", !crl->get_revoked().empty());
2✔
541

542
   return result;
1✔
543
}
1✔
544

545
Test::Result test_rdn_multielement_set_name() {
1✔
546
   Test::Result result("DN with multiple elements in RDN");
1✔
547

548
   // GH #2611
549

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

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

555
   return result;
1✔
556
}
1✔
557

558
Test::Result test_rsa_oaep() {
1✔
559
   Test::Result result("RSA OAEP decoding");
1✔
560

561
      #if defined(BOTAN_HAS_RSA)
562
   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
563

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

568
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
569
      #endif
570

571
   return result;
2✔
572
}
1✔
573

574
Test::Result test_x509_decode_list() {
1✔
575
   Test::Result result("X509_Certificate list decode");
1✔
576

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

579
   Botan::BER_Decoder dec(input);
1✔
580
   std::vector<Botan::X509_Certificate> certs;
1✔
581
   dec.decode_list(certs);
1✔
582

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

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

588
   return result;
1✔
589
}
1✔
590

591
Test::Result test_x509_utf8() {
1✔
592
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
593

594
   try {
1✔
595
      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
596

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

609
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
610

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

619
   return result;
1✔
620
}
×
621

622
Test::Result test_x509_bmpstring() {
1✔
623
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
624

625
   try {
1✔
626
      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
627

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

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

636
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
637

638
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
639
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
640
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
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_teletex() {
1✔
649
   Test::Result result("X509 with TeletexString encoded fields");
1✔
650

651
   try {
1✔
652
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
653

654
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
655

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

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

664
   return result;
1✔
665
}
×
666

667
Test::Result test_x509_authority_info_access_extension() {
1✔
668
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
669

670
   // contains no AIA extension
671
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
672

673
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
674
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
675

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

679
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
680

681
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
682
   if(result.tests_failed()) {
1✔
683
      return result;
684
   }
685

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

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

693
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
694

695
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
696
   if(result.tests_failed()) {
1✔
697
      return result;
698
   }
699

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

708
   return result;
1✔
709
}
1✔
710

711
Test::Result test_parse_rsa_pss_cert() {
1✔
712
   Test::Result result("X509 RSA-PSS certificate");
1✔
713

714
   // See https://github.com/randombit/botan/issues/3019 for background
715

716
   try {
1✔
717
      Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
718
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
719
   } catch(Botan::Exception& e) {
1✔
720
      result.test_failure("Parsing failed", e.what());
×
721
   }
×
722

723
   return result;
1✔
724
}
×
725

726
Test::Result test_verify_gost2012_cert() {
1✔
727
   Test::Result result("X509 GOST-2012 certificates");
1✔
728

729
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
730
   try {
1✔
731
      if(Botan::EC_Group::supports_named_group("gost_256A")) {
1✔
732
         Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
733
         Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
734

735
         Botan::Certificate_Store_In_Memory trusted;
1✔
736
         trusted.add_certificate(root_cert);
1✔
737

738
         const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
2✔
739
         const Botan::Path_Validation_Result validation_result =
1✔
740
            Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
741

742
         result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
743
      }
1✔
744
   } catch(const Botan::Decoding_Error& e) {
×
745
      result.test_failure(e.what());
×
746
   }
×
747
      #endif
748

749
   return result;
1✔
750
}
×
751

752
      /*
753
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
754
 *
755
 * For the other algorithms than RSA, only one padding is supported right now.
756
 */
757
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
758
Test::Result test_padding_config() {
1✔
759
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
760
   Test::Result test_result("X509 Padding Config");
1✔
761

762
   auto rng = Test::new_rng(__func__);
1✔
763

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

767
   // Create X509 CA certificate; EMSA3 is used for signing by default
768
   Botan::X509_Cert_Options opt("TESTCA");
1✔
769
   opt.CA_key();
1✔
770

771
   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
772
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
773
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
774
                       "RSA/PKCS1v15(SHA-512)");
775

776
   // Create X509 CA certificate; RSA-PSS is explicitly set
777
   opt.set_padding_scheme("PSSR");
1✔
778
   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
779
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
780
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
781
                       "RSA/PSS");
782

783
         #if defined(BOTAN_HAS_EMSA_X931)
784
   // Try to set a padding scheme that is not supported for signing with the given key type
785
   opt.set_padding_scheme("EMSA2");
1✔
786
   try {
1✔
787
      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
788
      test_result.test_failure("Could build CA cert with invalid encoding scheme X9.31 for key type " +
×
789
                               sk->algo_name());
×
790
   } catch(const Botan::Invalid_Argument& e) {
1✔
791
      test_result.test_eq("Build CA certificate with invalid encoding scheme X9.31 for key type " + sk->algo_name(),
3✔
792
                          e.what(),
1✔
793
                          "Signatures using RSA/X9.31(SHA-512) are not supported");
794
   }
1✔
795
         #endif
796

797
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
798
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
799
                       "RSA/PSS");
800

801
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
802
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
803

804
   // Prepare a signing request for the end certificate
805
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
806
   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
1✔
807
   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng);
1✔
808
   test_result.test_eq(
3✔
809
      "Certificate request signature algorithm", end_req.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
810

811
   // Create X509 CA object: will fail as the chosen hash functions differ
812
   try {
1✔
813
      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", *rng);
1✔
814
      test_result.test_failure("Configured conflicting hash functions for CA");
×
815
   } catch(const Botan::Invalid_Argument& e) {
1✔
816
      test_result.test_eq(
1✔
817
         "Configured conflicting hash functions for CA",
818
         e.what(),
1✔
819
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding EMSA4(SHA-256)");
820
   }
1✔
821

822
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
823
   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
824
   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, *rng, not_before, not_after);
1✔
825
   test_result.test_eq("End certificate signature algorithm",
3✔
826
                       end_cert_emsa3.signature_algorithm().oid().to_formatted_string(),
2✔
827
                       "RSA/PKCS1v15(SHA-512)");
828

829
   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
830
   Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "EMSA-PSS", *rng);
1✔
831
   Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, *rng, not_before, not_after);
1✔
832
   test_result.test_eq("End certificate signature algorithm",
3✔
833
                       end_cert_diff_emsa4.signature_algorithm().oid().to_formatted_string(),
2✔
834
                       "RSA/PSS");
835

836
   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
837
   Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-512,MGF1,64)", *rng);
1✔
838
   Botan::X509_Certificate end_cert_emsa4 = ca_exp.sign_request(end_req, *rng, not_before, not_after);
1✔
839
   test_result.test_eq("End certificate signature algorithm",
3✔
840
                       end_cert_emsa4.signature_algorithm().oid().to_formatted_string(),
2✔
841
                       "RSA/PSS");
842

843
   // Check CRL signature algorithm
844
   Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
845
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/PSS");
2✔
846

847
   // sanity check for verification, the heavy lifting is done in the other unit tests
848
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
849
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
850
   const Botan::Path_Validation_Result validation_result =
1✔
851
      Botan::x509_path_validate(end_cert_emsa4, restrictions, trusted);
1✔
852
   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
2✔
853

854
   return test_result;
2✔
855
}
3✔
856
      #endif
857

858
   #endif
859

860
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
12✔
861
                             const std::string& sig_padding,
862
                             const std::string& hash_fn,
863
                             Botan::RandomNumberGenerator& rng) {
864
   Test::Result result("PKCS10 extensions");
12✔
865

866
   Botan::X509_Cert_Options opts;
12✔
867

868
   opts.dns = "main.example.org";
12✔
869
   opts.more_dns.push_back("more1.example.org");
24✔
870
   opts.more_dns.push_back("more2.example.org");
24✔
871

872
   opts.padding_scheme = sig_padding;
12✔
873

874
   Botan::AlternativeName alt_name;
12✔
875
   alt_name.add_attribute("DNS", "bonus.example.org");
12✔
876

877
   Botan::X509_DN alt_dn;
12✔
878
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
12✔
879
   alt_dn.add_attribute("X520.Organization", "testing");
12✔
880
   alt_name.add_dn(alt_dn);
12✔
881

882
   opts.extensions.add(std::make_unique<Botan::Cert_Extension::Subject_Alternative_Name>(alt_name));
24✔
883

884
   const auto req = Botan::X509::create_cert_req(opts, key, hash_fn, rng);
12✔
885

886
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
12✔
887

888
   result.test_eq("Expected number of DNS names", alt_dns_names.size(), 4);
12✔
889

890
   if(alt_dns_names.size() == 4) {
12✔
891
      result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
36✔
892
      result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
36✔
893
      result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
36✔
894
      result.test_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
36✔
895
   }
896

897
   result.test_eq("Expected number of alt DNs", req.subject_alt_name().directory_names().size(), 1);
12✔
898
   result.confirm("Alt DN is correct", *req.subject_alt_name().directory_names().begin() == alt_dn);
24✔
899

900
   return result;
12✔
901
}
12✔
902

903
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
12✔
904
                            const std::string& sig_algo,
905
                            const std::string& sig_padding,
906
                            const std::string& hash_fn,
907
                            Botan::RandomNumberGenerator& rng) {
908
   Test::Result result("X509 Unit");
12✔
909

910
   /* Create the self-signed cert */
911
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
912

913
   {
12✔
914
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
24✔
915
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
24✔
916
   }
917

918
   /* Create user #1's key and cert request */
919
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
920

921
   Botan::PKCS10_Request user1_req =
12✔
922
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
12✔
923

924
   result.test_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom");
24✔
925

926
   /* Create user #2's key and cert request */
927
   auto user2_key = make_a_private_key(sig_algo, rng);
12✔
928

929
   Botan::PKCS10_Request user2_req = Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng);
12✔
930

931
   // /* Create user #3's key and cert request */
932
   auto user3_key = make_a_private_key(sig_algo, rng);
12✔
933

934
   Botan::PKCS10_Request user3_req = Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng);
12✔
935

936
   /* Create the CA object */
937
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
938

939
   const BigInt user1_serial = 99;
12✔
940

941
   /* Sign the requests to create the certs */
942
   Botan::X509_Certificate user1_cert =
12✔
943
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
944

945
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
12✔
946
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
12✔
947

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

951
   Botan::X509_Certificate user3_cert = ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
952

953
   // user#1 creates a self-signed cert on the side
954
   const auto user1_ss_cert =
12✔
955
      Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
12✔
956

957
   {
12✔
958
      auto constraints = req_opts1(sig_algo).constraints;
12✔
959
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
24✔
960
   }
961

962
   /* Copy, assign and compare */
963
   Botan::X509_Certificate user1_cert_copy(user1_cert);
12✔
964
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
12✔
965

966
   user1_cert_copy = user2_cert;
12✔
967
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
12✔
968

969
   Botan::X509_Certificate user1_cert_differ =
12✔
970
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
971

972
   result.test_eq("certificate differs", user1_cert == user1_cert_differ, false);
12✔
973

974
   /* Get cert data */
975
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
12✔
976

977
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
12✔
978
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
24✔
979
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
24✔
980
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
24✔
981
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
24✔
982

983
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
12✔
984
   result.test_eq("subject OrgaUnit count",
12✔
985
                  user3_subject_dn.get_attribute("OU").size(),
24✔
986
                  req_opts3(sig_algo).more_org_units.size() + 1);
24✔
987
   result.test_eq(
12✔
988
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
48✔
989

990
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
12✔
991
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
24✔
992
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
24✔
993
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
24✔
994

995
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
12✔
996
   result.test_eq(
12✔
997
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
24✔
998
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
48✔
999

1000
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
12✔
1001

1002
   /* Verify the certs */
1003
   Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1004
   Botan::Certificate_Store_In_Memory store;
12✔
1005

1006
   // First try with an empty store
1007
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1008
   result.test_eq("user 1 issuer not found",
36✔
1009
                  result_no_issuer.result_string(),
24✔
1010
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1011

1012
   store.add_certificate(ca.ca_certificate());
12✔
1013

1014
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1015
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
1016
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1017
   }
1018

1019
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
1020
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
24✔
1021
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
1022
   }
1023

1024
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
12✔
1025
   result.test_eq("user 1 issuer not found",
36✔
1026
                  result_no_issuer.result_string(),
24✔
1027
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1028
   store.add_crl(crl1);
12✔
1029

1030
   std::vector<Botan::CRL_Entry> revoked;
12✔
1031
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
24✔
1032
   revoked.push_back(user2_cert);
24✔
1033

1034
   const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, rng);
12✔
1035

1036
   store.add_crl(crl2);
12✔
1037

1038
   const std::string revoked_str =
12✔
1039
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
12✔
1040

1041
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1042
   result.test_eq("user 1 revoked", result_u1.result_string(), revoked_str);
24✔
1043

1044
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
1045
   result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
24✔
1046

1047
   revoked.clear();
12✔
1048
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
24✔
1049
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
12✔
1050

1051
   store.add_crl(crl3);
12✔
1052

1053
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1054
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
1055
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1056
   }
1057

1058
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
1059
   result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
24✔
1060

1061
   return result;
12✔
1062
}
48✔
1063

1064
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1065
                        const std::string& sig_algo,
1066
                        const std::string& hash_fn,
1067
                        Botan::RandomNumberGenerator& rng) {
1068
   using Botan::Key_Constraints;
12✔
1069
   using Botan::Usage_Type;
12✔
1070

1071
   Test::Result result("X509 Usage");
12✔
1072

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

1076
   /* Create the CA object */
1077
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1078

1079
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1080

1081
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1082
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1083

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

1086
   const Botan::X509_Certificate user1_cert =
12✔
1087
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1088

1089
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1090
   result.test_eq(
12✔
1091
      "key usage cRLSign not allowed",
1092
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1093
      false);
1094
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1095

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

1099
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1100

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

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

1106
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1107
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1108
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1109
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1110
   result.confirm(
24✔
1111
      "key usage multiple digitalSignature and cRLSign allowed",
1112
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1113
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1114

1115
   opts.constraints = Key_Constraints();
12✔
1116

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

1119
   const Botan::X509_Certificate no_usage_cert =
12✔
1120
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1121

1122
   // cert allows every usage
1123
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1124
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1125
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1126

1127
   if(sig_algo == "RSA") {
12✔
1128
      // cert allows data encryption
1129
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1130

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

1133
      const Botan::X509_Certificate enc_cert =
1✔
1134
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1135

1136
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1137
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1138
   }
1✔
1139

1140
   return result;
12✔
1141
}
24✔
1142

1143
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
12✔
1144
                              const std::string& sig_algo,
1145
                              const std::string& sig_padding,
1146
                              const std::string& hash_fn,
1147
                              Botan::RandomNumberGenerator& rng) {
1148
   using Botan::Key_Constraints;
12✔
1149

1150
   Test::Result result("X509 Self Issued");
12✔
1151

1152
   // create the self-signed cert
1153
   const Botan::X509_Certificate ca_cert =
12✔
1154
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1155

1156
   /* Create the CA object */
1157
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1158

1159
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1160

1161
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1162
   // but signed by a CA, not signed by it's own private key
1163
   Botan::X509_Cert_Options opts = ca_opts();
12✔
1164
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1165
   opts.set_padding_scheme(sig_padding);
12✔
1166

1167
   const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng);
12✔
1168

1169
   const Botan::X509_Certificate self_issued_cert =
12✔
1170
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1171

1172
   // check that this chain can can be verified successfully
1173
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
12✔
1174

1175
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1176

1177
   const Botan::Path_Validation_Result validation_result =
12✔
1178
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
12✔
1179

1180
   result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
24✔
1181

1182
   return result;
12✔
1183
}
24✔
1184

1185
Test::Result test_x509_uninit() {
1✔
1186
   Test::Result result("X509 object uninitialized access");
1✔
1187

1188
   Botan::X509_Certificate cert;
1✔
1189
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1190
      cert.x509_version();
1✔
1191
   });
1192

1193
   Botan::X509_CRL crl;
1✔
1194
   result.test_throws(
2✔
1195
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1196

1197
   return result;
1✔
1198
}
1✔
1199

1200
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1201
   using Botan::Key_Constraints;
19✔
1202

1203
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1204

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

1207
   // Now check some typical usage scenarios for the given key type
1208
   // Taken from RFC 5280, sec. 4.2.1.3
1209
   // ALL constraints are not typical at all, but we use them for a negative test
1210
   const auto all = Key_Constraints(
19✔
1211
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1212
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1213
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1214

1215
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1216
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1217
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1218
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1219
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1220
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1221
   const auto key_agreement_encipher_only =
19✔
1222
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1223
   const auto key_agreement_decipher_only =
19✔
1224
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1225
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1226
   const auto sign_everything =
19✔
1227
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1228

1229
   if(pk_algo == "DH" || pk_algo == "ECDH") {
19✔
1230
      // DH and ECDH only for key agreement
1231
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1232
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1233
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1234
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1235
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1236
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1237
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1238
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1239
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1240
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1241
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1242
   } else if(pk_algo == "Kyber" || pk_algo == "FrodoKEM" || pk_algo == "ML-KEM" || pk_algo == "ClassicMcEliece") {
17✔
1243
      // KEMs can encrypt and agree
1244
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
4✔
1245
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
4✔
1246
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
4✔
1247
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
4✔
1248
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
4✔
1249
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1250
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
4✔
1251
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), false);
4✔
1252
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
8✔
1253
   } else if(pk_algo == "RSA") {
13✔
1254
      // RSA can do everything except key agreement
1255
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1256

1257
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1258
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1259
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1260
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1261
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1262
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1263
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1264
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1265
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1266
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1267
   } else if(pk_algo == "ElGamal") {
12✔
1268
      // only ElGamal encryption is currently implemented
1269
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1270
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1271
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1272
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1273
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1274
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1275
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1276
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1277
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1278
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
10✔
1279
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" ||
18✔
1280
             pk_algo == "HSS-LMS") {
3✔
1281
      // these are signature algorithms only
1282
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
9✔
1283

1284
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1285
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1286
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1287
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1288
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1289
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1290
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1291
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1292
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1293
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1294
   }
1295

1296
   return result;
19✔
1297
}
×
1298

1299
/**
1300
 * @brief X.509v3 extension that encodes a given string
1301
 */
1302
class String_Extension final : public Botan::Certificate_Extension {
24✔
1303
   public:
1304
      String_Extension() = default;
24✔
1305

1306
      explicit String_Extension(const std::string& val) : m_contents(val) {}
12✔
1307

1308
      std::string value() const { return m_contents; }
48✔
1309

1310
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1311
         return std::make_unique<String_Extension>(m_contents);
×
1312
      }
1313

1314
      Botan::OID oid_of() const override { return Botan::OID("1.2.3.4.5.6.7.8.9.1"); }
24✔
1315

1316
      bool should_encode() const override { return true; }
24✔
1317

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

1320
      std::vector<uint8_t> encode_inner() const override {
12✔
1321
         std::vector<uint8_t> bits;
12✔
1322
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
12✔
1323
         return bits;
12✔
1324
      }
×
1325

1326
      void decode_inner(const std::vector<uint8_t>& in) override {
24✔
1327
         Botan::ASN1_String str;
24✔
1328
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
24✔
1329
         m_contents = str.value();
24✔
1330
      }
24✔
1331

1332
   private:
1333
      std::string m_contents;
1334
};
1335

1336
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
12✔
1337
                                 const std::string& sig_algo,
1338
                                 const std::string& sig_padding,
1339
                                 const std::string& hash_fn,
1340
                                 Botan::RandomNumberGenerator& rng) {
1341
   Test::Result result("X509 Custom DN");
12✔
1342

1343
   /* Create the self-signed cert */
1344
   Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1345

1346
   /* Create the CA object */
1347
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1348

1349
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1350

1351
   Botan::X509_DN subject_dn;
12✔
1352

1353
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
12✔
1354
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
12✔
1355
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
12✔
1356
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
12✔
1357

1358
   subject_dn.add_attribute(attr1, val1);
12✔
1359
   subject_dn.add_attribute(attr2, val2);
12✔
1360

1361
   Botan::Extensions extensions;
12✔
1362

1363
   Botan::PKCS10_Request req =
12✔
1364
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
12✔
1365

1366
   const Botan::X509_DN& req_dn = req.subject_dn();
12✔
1367

1368
   result.test_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
12✔
1369

1370
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
12✔
1371
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
12✔
1372
   result.confirm("Attr1 matches encoded", req_val1 == val1);
24✔
1373
   result.confirm("Attr2 matches encoded", req_val2 == val2);
24✔
1374
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
24✔
1375
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
24✔
1376

1377
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1378
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1379

1380
   auto cert = ca.sign_request(req, rng, not_before, not_after);
12✔
1381

1382
   const Botan::X509_DN& cert_dn = cert.subject_dn();
12✔
1383

1384
   result.test_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
12✔
1385

1386
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
12✔
1387
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
12✔
1388
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
24✔
1389
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
24✔
1390
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
24✔
1391
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
24✔
1392

1393
   return result;
12✔
1394
}
36✔
1395

1396
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
12✔
1397
                                  const std::string& sig_algo,
1398
                                  const std::string& sig_padding,
1399
                                  const std::string& hash_fn,
1400
                                  Botan::RandomNumberGenerator& rng) {
1401
   using Botan::Key_Constraints;
12✔
1402

1403
   Test::Result result("X509 Extensions");
12✔
1404

1405
   /* Create the self-signed cert */
1406
   Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1407

1408
   /* Create the CA object */
1409
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1410

1411
   /* Prepare CDP extension */
1412
   std::vector<std::string> cdp_urls = {
12✔
1413
      "http://example.com/crl1.pem",
1414
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
12✔
1415

1416
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
12✔
1417

1418
   for(const auto& uri : cdp_urls) {
36✔
1419
      Botan::AlternativeName cdp_alt_name;
24✔
1420
      cdp_alt_name.add_uri(uri);
24✔
1421
      Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
24✔
1422

1423
      dps.emplace_back(dp);
24✔
1424
   }
24✔
1425

1426
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1427

1428
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1429
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1430

1431
   // include a custom extension in the request
1432
   Botan::Extensions req_extensions;
12✔
1433
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
12✔
1434
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
12✔
1435
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
24✔
1436
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
24✔
1437
   opts.extensions = req_extensions;
12✔
1438
   opts.set_padding_scheme(sig_padding);
12✔
1439

1440
   /* Create a self-signed certificate */
1441
   const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, rng);
12✔
1442

1443
   result.confirm("Extensions::extension_set true for Key_Usage",
24✔
1444
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
12✔
1445

1446
   // check if known Key_Usage extension is present in self-signed cert
1447
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
12✔
1448
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
24✔
1449
      result.confirm(
24✔
1450
         "Key_Usage extension value matches in self-signed certificate",
1451
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
12✔
1452
   }
1453

1454
   // check if custom extension is present in self-signed cert
1455
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
12✔
1456
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
24✔
1457
      result.test_eq(
48✔
1458
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1459
   }
1460

1461
   // check if CDPs are present in the self-signed cert
1462
   auto cert_cdps =
12✔
1463
      self_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
24✔
1464

1465
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1466
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1467
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1468
         result.confirm("CDP URI present in self-signed certificate",
48✔
1469
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1470
      }
1471
   }
1472

1473
   const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng);
12✔
1474

1475
   /* Create a CA-signed certificate */
1476
   const Botan::X509_Certificate ca_signed_cert =
12✔
1477
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1478

1479
   // check if known Key_Usage extension is present in CA-signed cert
1480
   result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
24✔
1481

1482
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
24✔
1483
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
24✔
1484
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
12✔
1485
      result.confirm("Key_Usage extension value matches in user certificate",
24✔
1486
                     constraints == Botan::Key_Constraints::DigitalSignature);
12✔
1487
   }
1488

1489
   // check if custom extension is present in CA-signed cert
1490
   result.confirm("Extensions::extension_set true for String_Extension",
24✔
1491
                  ca_signed_cert.v3_extensions().extension_set(oid));
12✔
1492
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
24✔
1493
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
24✔
1494
      result.test_eq(
48✔
1495
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1496
   }
1497

1498
   // check if CDPs are present in the CA-signed cert
1499
   cert_cdps = ca_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
24✔
1500

1501
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1502
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1503
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1504
         result.confirm("CDP URI present in self-signed certificate",
48✔
1505
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1506
      }
1507
   }
1508

1509
   return result;
12✔
1510
}
60✔
1511

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

1515
   struct TestData {
12✔
1516
         const std::string issuer, subject, issuer_hash, subject_hash;
1517
   } const cases[]{{"",
12✔
1518
                    "",
1519
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1520
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1521
                   {"a",
1522
                    "b",
1523
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1524
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1525
                   {"A",
1526
                    "B",
1527
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1528
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1529
                   {
1530
                      "Test Issuer/US/Botan Project/Testing",
1531
                      "Test Subject/US/Botan Project/Testing",
1532
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1533
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1534
                   },
1535
                   {
1536
                      "Test Subject/US/Botan Project/Testing",
1537
                      "Test Issuer/US/Botan Project/Testing",
1538
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1539
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1540
                   }};
72✔
1541

1542
   for(const auto& a : cases) {
72✔
1543
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1544
      opts.CA_key();
60✔
1545

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

1548
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1549
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1550

1551
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1552
      const Botan::PKCS10_Request req =
60✔
1553
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1554
      const Botan::X509_Certificate subject_cert =
60✔
1555
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1556

1557
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1558
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1559
   }
60✔
1560
   return result;
12✔
1561
}
72✔
1562

1563
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1564

1565
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1566
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1567

1568
      asn1=SEQUENCE:tn_auth_list
1569

1570
      [tn_auth_list]
1571
      spc=EXP:0,IA5:1001
1572
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1573
      one=EXP:2,IA5:333
1574

1575
      [TelephoneNumberRange]
1576
      start1=IA5:111
1577
      count1=INT:128
1578
      start2=IA5:222
1579
      count2=INT:256
1580
    */
1581
   const std::string filename("TNAuthList.pem");
1✔
1582
   Test::Result result("X509 TNAuthList decode");
1✔
1583
   result.start_timer();
1✔
1584

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

1587
   using Botan::Cert_Extension::TNAuthList;
1✔
1588

1589
   auto tn_auth_list = cert.v3_extensions().get_extension_object_as<TNAuthList>();
2✔
1590

1591
   auto& tn_entries = tn_auth_list->entries();
1✔
1592

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

1595
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1596
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1597
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1598
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1599
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1600
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1601

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

1605
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1606
   auto& range = tn_entries[1].telephone_number_range();
1✔
1607
   result.test_eq("range entries count", range.size(), 2);
1✔
1608
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1609
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1610
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1611
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1612

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

1616
   result.end_timer();
1✔
1617
   return result;
2✔
1618
}
1✔
1619

1620
   #endif
1621

1622
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
1623
   if(sig_algo == "RSA") {
12✔
1624
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
5✔
1625
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
1626
             sig_algo == "GOST-34.10") {
7✔
1627
      return {hash};
10✔
1628
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
1629
      return {"Pure"};
2✔
1630
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
1631
      return {"Randomized"};
2✔
1632
   } else if(sig_algo == "HSS-LMS") {
2✔
1633
      return {""};
1✔
1634
   } else {
1635
      return {};
1✔
1636
   }
1637
}
7✔
1638

1639
class X509_Cert_Unit_Tests final : public Test {
×
1640
   public:
1641
      std::vector<Test::Result> run() override {
1✔
1642
         std::vector<Test::Result> results;
1✔
1643

1644
         auto& rng = this->rng();
1✔
1645

1646
         const std::string sig_algos[]{"RSA",
1✔
1647
                                       "DSA",
1648
                                       "ECDSA",
1649
                                       "ECGDSA",
1650
                                       "ECKCDSA",
1651
                                       "GOST-34.10",
1652
                                       "Ed25519",
1653
                                       "Ed448",
1654
                                       "Dilithium",
1655
                                       "ML-DSA",
1656
                                       "SLH-DSA",
1657
                                       "HSS-LMS"};
13✔
1658

1659
         for(const std::string& algo : sig_algos) {
13✔
1660
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1661
            if(algo == "RSA")
1662
               continue;
1663
   #endif
1664

1665
            std::string hash = "SHA-256";
12✔
1666

1667
            if(algo == "Ed25519") {
12✔
1668
               hash = "SHA-512";
1✔
1669
            }
1670
            if(algo == "Ed448") {
12✔
1671
               hash = "SHAKE-256(912)";
1✔
1672
            }
1673
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1674
               hash = "SHAKE-256(512)";
2✔
1675
            }
1676

1677
            auto key = make_a_private_key(algo, rng);
12✔
1678

1679
            if(key == nullptr) {
12✔
1680
               continue;
×
1681
            }
1682

1683
            results.push_back(test_hashes(*key, hash, rng));
24✔
1684
            results.push_back(test_valid_constraints(*key, algo));
24✔
1685

1686
            Test::Result usage_result("X509 Usage");
12✔
1687
            try {
12✔
1688
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1689
            } catch(std::exception& e) {
×
1690
               usage_result.test_failure("test_usage " + algo, e.what());
×
1691
            }
×
1692
            results.push_back(usage_result);
12✔
1693

1694
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
24✔
1695
               Test::Result cert_result("X509 Unit");
12✔
1696

1697
               try {
12✔
1698
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
12✔
1699
               } catch(std::exception& e) {
×
1700
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1701
               }
×
1702
               results.push_back(cert_result);
12✔
1703

1704
               Test::Result pkcs10_result("PKCS10 extensions");
12✔
1705
               try {
12✔
1706
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
12✔
1707
               } catch(std::exception& e) {
×
1708
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
1709
               }
×
1710
               results.push_back(pkcs10_result);
12✔
1711

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

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

1728
               Test::Result custom_dn_result("X509 Custom DN");
12✔
1729
               try {
12✔
1730
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
12✔
1731
               } catch(std::exception& e) {
×
1732
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1733
               }
×
1734
               results.push_back(custom_dn_result);
12✔
1735
            }
24✔
1736
         }
24✔
1737

1738
         /*
1739
         These are algos which cannot sign but can be included in certs
1740
         */
1741
         const std::vector<std::string> enc_algos = {
1✔
1742
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
1743

1744
         for(const std::string& algo : enc_algos) {
8✔
1745
            auto key = make_a_private_key(algo, rng);
7✔
1746

1747
            if(key) {
7✔
1748
               results.push_back(test_valid_constraints(*key, algo));
14✔
1749
            }
1750
         }
7✔
1751

1752
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1753
      defined(BOTAN_HAS_RSA)
1754
         Test::Result pad_config_result("X509 Padding Config");
1✔
1755
         try {
1✔
1756
            pad_config_result.merge(test_padding_config());
1✔
1757
         } catch(const std::exception& e) {
×
1758
            pad_config_result.test_failure("test_padding_config", e.what());
×
1759
         }
×
1760
         results.push_back(pad_config_result);
1✔
1761
   #endif
1762

1763
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1764
         results.push_back(test_x509_utf8());
2✔
1765
         results.push_back(test_x509_bmpstring());
2✔
1766
         results.push_back(test_x509_teletex());
2✔
1767
         results.push_back(test_crl_dn_name());
2✔
1768
         results.push_back(test_crl_without_next_update());
2✔
1769
         results.push_back(test_rdn_multielement_set_name());
2✔
1770
         results.push_back(test_x509_decode_list());
2✔
1771
         results.push_back(test_rsa_oaep());
2✔
1772
         results.push_back(test_x509_authority_info_access_extension());
2✔
1773
         results.push_back(test_verify_gost2012_cert());
2✔
1774
         results.push_back(test_parse_rsa_pss_cert());
2✔
1775
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1776
   #endif
1777

1778
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1779
         results.push_back(test_x509_extension());
2✔
1780
         results.push_back(test_x509_dates());
2✔
1781
         results.push_back(test_cert_status_strings());
2✔
1782
         results.push_back(test_x509_uninit());
2✔
1783

1784
         return results;
1✔
1785
      }
13✔
1786
};
1787

1788
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1789

1790
#endif
1791

1792
}  // namespace
1793

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