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

randombit / botan / 21768358452

06 Feb 2026 10:35PM UTC coverage: 90.064% (-0.003%) from 90.067%
21768358452

Pull #5289

github

web-flow
Merge f589db195 into 8ea0ca252
Pull Request #5289: Further misc header reductions, forward declarations, etc

102238 of 113517 relevant lines covered (90.06%)

11357432.36 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

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

29
namespace Botan_Tests {
30

31
namespace {
32

33
#if defined(BOTAN_HAS_X509_CERTIFICATES)
34

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

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

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

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

51
   opts.CA_key(1);
112✔
52

53
   return opts;
112✔
54
}
×
55

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

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

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

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

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

75
   return opts;
36✔
76
}
×
77

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

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

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

88
   return opts;
11✔
89
}
×
90

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

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

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

103
   return opts;
55✔
104
}
×
105

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

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

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

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

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

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

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

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

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

206
   return result;
1✔
207
}
1✔
208

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

212
   Botan::Extensions extn;
1✔
213

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

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

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

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

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

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

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

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

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

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

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

247
   return result;
2✔
248
}
2✔
249

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

422
   return result;
1✔
423
}
80✔
424

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

494
   return result;
1✔
495
}
4✔
496

497
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
498

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

502
      // See GH #1252
503

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

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

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

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

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

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

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

522
   return result;
2✔
523
}
3✔
524

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

528
   // GH #2611
529

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

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

535
   return result;
1✔
536
}
1✔
537

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

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

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

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

551
   return result;
2✔
552
}
1✔
553

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

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

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

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

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

568
   return result;
1✔
569
}
1✔
570

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

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

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

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

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

599
   return result;
1✔
600
}
×
601

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

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

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

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

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

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

625
   return result;
1✔
626
}
×
627

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

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

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

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

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

644
   return result;
1✔
645
}
×
646

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

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

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

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

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

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

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

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

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

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

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

688
   return result;
1✔
689
}
1✔
690

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

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

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

703
   return result;
1✔
704
}
×
705

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

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

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

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

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

729
   return result;
1✔
730
}
×
731

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

836
   #endif
837

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

844
   Botan::X509_Cert_Options opts;
11✔
845

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

850
   opts.padding_scheme = sig_padding;
11✔
851

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

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

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

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

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

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

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

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

878
   return result;
11✔
879
}
22✔
880

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

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

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

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

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

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

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

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

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

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

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

919
   const BigInt user1_serial(99);
11✔
920

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1019
   store.add_crl(crl2);
11✔
1020

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

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

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

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

1034
   store.add_crl(crl3);
11✔
1035

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

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

1044
   return result;
11✔
1045
}
44✔
1046

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1123
   return result;
12✔
1124
}
24✔
1125

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

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

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

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

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

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

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

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

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

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

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

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

1165
   return result;
11✔
1166
}
22✔
1167

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

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

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

1180
   return result;
1✔
1181
}
1✔
1182

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

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

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

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

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

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

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

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

1279
   return result;
19✔
1280
}
×
1281

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1335
   Botan::X509_DN subject_dn;
11✔
1336

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

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

1345
   const Botan::Extensions extensions;
11✔
1346

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

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

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

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

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

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

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

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

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

1377
   return result;
22✔
1378
}
99✔
1379

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1494
   return result;
11✔
1495
}
55✔
1496

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

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

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

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

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

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

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

1548
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1549

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

1553
      asn1=SEQUENCE:tn_auth_list
1554

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

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

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

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

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

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

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

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

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

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

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

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

1605
   #endif
1606

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1781
#endif
1782

1783
}  // namespace
1784

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