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

randombit / botan / 22022566023

14 Feb 2026 06:55PM UTC coverage: 90.067%. Remained the same
22022566023

push

github

web-flow
Merge pull request #5334 from randombit/jack/test-h-remove-confirm

Remove confirm test helper, use test_is_true or test_is_false instead

102260 of 113538 relevant lines covered (90.07%)

11510296.11 hits per line

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

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

8
#include "tests.h"
9

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

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

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_X509_CERTIFICATES)
35

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

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

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

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

52
   opts.CA_key(1);
112✔
53

54
   return opts;
112✔
55
}
×
56

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

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

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

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

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

76
   return opts;
36✔
77
}
×
78

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

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

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

89
   return opts;
11✔
90
}
×
91

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

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

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

104
   return opts;
55✔
105
}
×
106

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

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

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

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

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

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

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

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

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

207
   return result;
1✔
208
}
1✔
209

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

213
   Botan::Extensions extn;
1✔
214

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

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

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

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

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

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

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

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

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

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

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

248
   return result;
2✔
249
}
2✔
250

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423
   return result;
1✔
424
}
80✔
425

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

495
   return result;
1✔
496
}
4✔
497

498
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
499

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

503
      // See GH #1252
504

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

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

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

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

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

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

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

523
   return result;
2✔
524
}
3✔
525

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

529
   // GH #2611
530

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

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

536
   return result;
1✔
537
}
1✔
538

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

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

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

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

552
   return result;
2✔
553
}
1✔
554

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

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

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

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

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

569
   return result;
1✔
570
}
1✔
571

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

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

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

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

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

600
   return result;
1✔
601
}
×
602

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

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

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

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

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

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

626
   return result;
1✔
627
}
×
628

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

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

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

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

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

645
   return result;
1✔
646
}
×
647

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

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

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

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

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

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

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

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

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

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

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

689
   return result;
1✔
690
}
1✔
691

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

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

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

704
   return result;
1✔
705
}
×
706

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

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

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

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

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

730
   return result;
1✔
731
}
×
732

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

837
   #endif
838

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

845
   Botan::X509_Cert_Options opts;
11✔
846

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

851
   opts.padding_scheme = sig_padding;
11✔
852

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

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

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

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

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

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

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

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

879
   return result;
11✔
880
}
22✔
881

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

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

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

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

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

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

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

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

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

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

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

920
   const BigInt user1_serial(99);
11✔
921

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1020
   store.add_crl(crl2);
11✔
1021

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

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

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

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

1035
   store.add_crl(crl3);
11✔
1036

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

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

1045
   return result;
11✔
1046
}
44✔
1047

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1125
   return result;
12✔
1126
}
24✔
1127

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

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

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

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

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

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

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

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

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

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

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

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

1167
   return result;
11✔
1168
}
22✔
1169

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

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

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

1182
   return result;
1✔
1183
}
1✔
1184

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

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

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

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

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

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

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

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

1281
   return result;
19✔
1282
}
×
1283

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1337
   Botan::X509_DN subject_dn;
11✔
1338

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

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

1347
   const Botan::Extensions extensions;
11✔
1348

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

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

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

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

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

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

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

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

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

1379
   return result;
22✔
1380
}
99✔
1381

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1497
   return result;
11✔
1498
}
55✔
1499

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

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

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

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

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

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

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

1551
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1552

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

1556
      asn1=SEQUENCE:tn_auth_list
1557

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

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

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

1575
   using Botan::Cert_Extension::TNAuthList;
1✔
1576

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

1579
   const auto& tn_entries = tn_auth_list->entries();
1✔
1580

1581
   result.test_not_null("cert has TNAuthList extension", tn_auth_list);
1✔
1582

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

1590
   result.test_is_true("spc entry type", tn_entries[0].type() == TNAuthList::Entry::ServiceProviderCode);
1✔
1591
   result.test_eq("spc entry data", tn_entries[0].service_provider_code(), "1001");
1✔
1592

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

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

1604
   result.end_timer();
1✔
1605
   return result;
2✔
1606
}
1✔
1607

1608
   #endif
1609

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

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

1639
         auto& rng = this->rng();
1✔
1640

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

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

1660
            std::string hash = "SHA-256";
12✔
1661

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

1672
            auto key = make_a_private_key(algo, rng);
12✔
1673

1674
            if(key == nullptr) {
12✔
1675
               continue;
×
1676
            }
1677

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1778
         return results;
1✔
1779
      }
13✔
1780
};
1781

1782
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1783

1784
#endif
1785

1786
}  // namespace
1787

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