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

randombit / botan / 22034777194

15 Feb 2026 11:21AM UTC coverage: 89.999% (-0.001%) from 90.0%
22034777194

Pull #5338

github

web-flow
Merge 8493f6ee7 into 2993205af
Pull Request #5338: Rename and cleanup tests.h comparators for strings

102267 of 113631 relevant lines covered (90.0%)

11490211.35 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_str_eq("Same string",
1✔
146
                      Botan::to_string(Botan::Certificate_Status_Code::OK),
147
                      Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
148

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

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

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

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

207
   return result;
1✔
208
}
1✔
209

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

213
   Botan::Extensions extn;
1✔
214

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

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

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

225
   result.test_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_str_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
1✔
259

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423
   return result;
1✔
424
}
80✔
425

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

491
   result.test_is_true("OCSP URI available", !cert.ocsp_responder().empty());
1✔
492
   result.test_is_true("CA Issuer URI available", !cert.ca_issuers().empty());
1✔
493
   #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_str_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_str_eq("Expected cert 1 CN", certs[0].subject_dn().get_first_attribute("CN"), "CA1-PP.01.02");
1✔
567
   result.test_str_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_str_eq("O", issuer_dn.get_first_attribute("O"), organization);
1✔
593
      result.test_str_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
1✔
594
      result.test_str_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
1✔
595
      result.test_str_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_str_eq("O", issuer_dn.get_first_attribute("O"), organization);
1✔
620
      result.test_str_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
1✔
621
      result.test_str_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_str_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
1✔
640
      result.test_str_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_str_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_str_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
1✔
668
   result.test_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_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_str_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
11✔
871
      result.test_str_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
11✔
872
      result.test_str_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
11✔
873
      result.test_str_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_str_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_str_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
11✔
962
   result.test_str_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
11✔
963
   result.test_str_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
11✔
964
   result.test_str_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_str_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_str_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
11✔
975
   result.test_str_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
11✔
976
   result.test_str_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_str_eq(
22✔
982
      "subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
33✔
983

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

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

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

997
   store.add_certificate(ca.ca_certificate());
11✔
998

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

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

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

1017
   std::vector<Botan::CRL_Entry> revoked;
11✔
1018
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
22✔
1019
   revoked.push_back(Botan::CRL_Entry(user2_cert));
22✔
1020

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

1023
   store.add_crl(crl2);
11✔
1024

1025
   const std::string revoked_str =
11✔
1026
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
11✔
1027

1028
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
11✔
1029
   result.test_str_eq("user 1 revoked", result_u1.result_string(), revoked_str);
11✔
1030

1031
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
11✔
1032
   result.test_str_eq("user 1 revoked", result_u2.result_string(), revoked_str);
11✔
1033

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

1038
   store.add_crl(crl3);
11✔
1039

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

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

1048
   return result;
11✔
1049
}
44✔
1050

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

1058
   Test::Result result("X509 Usage");
12✔
1059

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

1063
   /* Create the CA object */
1064
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1065

1066
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1067

1068
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1069
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1070

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

1073
   const Botan::X509_Certificate user1_cert =
12✔
1074
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1075

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

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

1086
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1087

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

1090
   const Botan::X509_Certificate mult_usage_cert =
12✔
1091
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1092

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

1102
   opts.constraints = Key_Constraints();
12✔
1103

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

1106
   const Botan::X509_Certificate no_usage_cert =
12✔
1107
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1108

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

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

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

1121
      const Botan::X509_Certificate enc_cert =
1✔
1122
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1123

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

1128
   return result;
12✔
1129
}
24✔
1130

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

1138
   Test::Result result("X509 Self Issued");
11✔
1139

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

1144
   /* Create the CA object */
1145
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1146

1147
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1148

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

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

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

1160
   // check that this chain can can be verified successfully
1161
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
11✔
1162

1163
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
22✔
1164

1165
   const Botan::Path_Validation_Result validation_result =
11✔
1166
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
11✔
1167

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

1170
   return result;
11✔
1171
}
22✔
1172

1173
Test::Result test_x509_uninit() {
1✔
1174
   Test::Result result("X509 object uninitialized access");
1✔
1175

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

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

1185
   return result;
1✔
1186
}
1✔
1187

1188
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1189
   using Botan::Key_Constraints;
19✔
1190

1191
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1192

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

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

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

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

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

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

1284
   return result;
19✔
1285
}
×
1286

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

1294
      explicit String_Extension(const std::string& val) : m_contents(val) {}
11✔
1295

1296
      std::string value() const { return m_contents; }
44✔
1297

1298
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1299
         return std::make_unique<String_Extension>(m_contents);
×
1300
      }
1301

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

1304
      bool should_encode() const override { return true; }
22✔
1305

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

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

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

1320
   private:
1321
      std::string m_contents;
1322
};
1323

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

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

1335
   /* Create the CA object */
1336
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1337

1338
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1339

1340
   Botan::X509_DN subject_dn;
11✔
1341

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

1347
   subject_dn.add_attribute(attr1, val1);
11✔
1348
   subject_dn.add_attribute(attr2, val2);
11✔
1349

1350
   const Botan::Extensions extensions;
11✔
1351

1352
   const Botan::PKCS10_Request req =
11✔
1353
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
11✔
1354

1355
   const Botan::X509_DN& req_dn = req.subject_dn();
11✔
1356

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

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

1366
   const Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1367
   const Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
11✔
1368

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

1371
   const Botan::X509_DN& cert_dn = cert.subject_dn();
11✔
1372

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

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

1382
   return result;
22✔
1383
}
99✔
1384

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

1392
   Test::Result result("X509 Extensions");
11✔
1393

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

1398
   /* Create the CA object */
1399
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
11✔
1400

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

1406
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
11✔
1407

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

1413
      dps.emplace_back(dp);
22✔
1414
   }
22✔
1415

1416
   auto user_key = make_a_private_key(sig_algo, rng);
11✔
1417

1418
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
11✔
1419
   opts.constraints = Key_Constraints::DigitalSignature;
11✔
1420

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

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

1433
   result.test_is_true("Extensions::extension_set true for Key_Usage",
11✔
1434
                       self_signed_cert.v3_extensions().extension_set(ku_oid));
11✔
1435

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

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

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

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

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

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

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

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

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

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

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

1500
   return result;
11✔
1501
}
55✔
1502

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

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

1533
   for(const auto& a : cases) {
72✔
1534
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1535
      opts.CA_key();
60✔
1536

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

1539
      result.test_str_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1540
      result.test_str_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1541

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

1548
      result.test_str_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1549
      result.test_str_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1550
   }
60✔
1551
   return result;
12✔
1552
}
72✔
1553

1554
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1555

1556
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1557
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1558

1559
      asn1=SEQUENCE:tn_auth_list
1560

1561
      [tn_auth_list]
1562
      spc=EXP:0,IA5:1001
1563
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1564
      one=EXP:2,IA5:333
1565

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

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

1578
   using Botan::Cert_Extension::TNAuthList;
1✔
1579

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

1582
   const auto& tn_entries = tn_auth_list->entries();
1✔
1583

1584
   result.test_not_null("cert has TNAuthList extension", tn_auth_list);
1✔
1585

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

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

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

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

1607
   result.end_timer();
1✔
1608
   return result;
2✔
1609
}
1✔
1610

1611
   #endif
1612

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

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

1642
         auto& rng = this->rng();
1✔
1643

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

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

1663
            std::string hash = "SHA-256";
12✔
1664

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

1675
            auto key = make_a_private_key(algo, rng);
12✔
1676

1677
            if(key == nullptr) {
12✔
1678
               continue;
×
1679
            }
1680

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

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

1692
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
23✔
1693
               Test::Result cert_result("X509 Unit");
11✔
1694

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

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

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

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

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

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

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

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

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

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

1775
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1776
         results.push_back(test_x509_extension());
2✔
1777
         results.push_back(test_x509_dates());
2✔
1778
         results.push_back(test_cert_status_strings());
2✔
1779
         results.push_back(test_x509_uninit());
2✔
1780

1781
         return results;
1✔
1782
      }
13✔
1783
};
1784

1785
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1786

1787
#endif
1788

1789
}  // namespace
1790

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