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

randombit / botan / 13245259474

10 Feb 2025 04:09PM UTC coverage: 91.269% (+0.06%) from 91.205%
13245259474

push

github

arckoor
Add IPAddrBlock and ASIdentifiers extensions (RFC 3779)

Co-authored-by: butteronarchbtw <152636161+butteronarchbtw@users.noreply.github.com>

94541 of 103585 relevant lines covered (91.27%)

11635654.44 hits per line

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

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

9
#include "tests.h"
10

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

24
namespace Botan_Tests {
25

26
namespace {
27

28
#if defined(BOTAN_HAS_X509_CERTIFICATES)
29

30
Botan::X509_Time from_date(const int y, const int m, const int d) {
1,232✔
31
   const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
1,232✔
32

33
   Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
1,232✔
34
   return Botan::X509_Time(t.to_std_timepoint());
1,232✔
35
}
36

37
/* Return some option sets */
38
Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "") {
564✔
39
   Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
564✔
40

41
   opts.uri = "https://botan.randombit.net";
564✔
42
   opts.dns = "botan.randombit.net";
564✔
43
   opts.email = "testing@randombit.net";
564✔
44
   opts.set_padding_scheme(sig_padding);
564✔
45

46
   opts.CA_key(1);
564✔
47

48
   return opts;
564✔
49
}
×
50

51
Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& sig_padding = "") {
482✔
52
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
482✔
53

54
   opts.uri = "https://botan.randombit.net";
482✔
55
   opts.dns = "botan.randombit.net";
482✔
56
   opts.email = "testing@randombit.net";
482✔
57
   opts.set_padding_scheme(sig_padding);
482✔
58

59
   opts.not_before("160101200000Z");
482✔
60
   opts.not_after("300101200000Z");
482✔
61

62
   opts.challenge = "zoom";
482✔
63

64
   if(algo == "RSA") {
482✔
65
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
452✔
66
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
30✔
67
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
12✔
68
   }
69

70
   return opts;
482✔
71
}
×
72

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

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

81
   opts.add_ex_constraint("PKIX.EmailProtection");
12✔
82

83
   return opts;
12✔
84
}
×
85

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

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

94
   opts.more_org_units.push_back("IT");
120✔
95
   opts.more_org_units.push_back("Security");
120✔
96
   opts.more_dns.push_back("www.botan.randombit.net");
120✔
97

98
   return opts;
60✔
99
}
×
100

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

122
   return Botan::create_private_key(algo, rng, params);
991✔
123
}
991✔
124

125
Test::Result test_cert_status_strings() {
1✔
126
   Test::Result result("Certificate_Status_Code to_string");
1✔
127

128
   std::set<std::string> seen;
1✔
129

130
   result.test_eq("Same string",
1✔
131
                  Botan::to_string(Botan::Certificate_Status_Code::OK),
132
                  Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
133

134
   const Botan::Certificate_Status_Code codes[]{
1✔
135
      Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
136
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
137
      Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
138
      Botan::Certificate_Status_Code::OCSP_NO_HTTP,
139

140
      Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
141
      Botan::Certificate_Status_Code::DN_TOO_LONG,
142

143
      Botan::Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK,
144
      Botan::Certificate_Status_Code::NO_MATCHING_CRLDP,
145
      Botan::Certificate_Status_Code::UNTRUSTED_HASH,
146
      Botan::Certificate_Status_Code::NO_REVOCATION_DATA,
147
      Botan::Certificate_Status_Code::CERT_NOT_YET_VALID,
148
      Botan::Certificate_Status_Code::CERT_HAS_EXPIRED,
149
      Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID,
150
      Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED,
151
      Botan::Certificate_Status_Code::CRL_NOT_YET_VALID,
152
      Botan::Certificate_Status_Code::CRL_HAS_EXPIRED,
153
      Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
154
      Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST,
155
      Botan::Certificate_Status_Code::CERT_CHAIN_LOOP,
156
      Botan::Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT,
157
      Botan::Certificate_Status_Code::CHAIN_NAME_MISMATCH,
158
      Botan::Certificate_Status_Code::POLICY_ERROR,
159
      Botan::Certificate_Status_Code::DUPLICATE_CERT_POLICY,
160
      Botan::Certificate_Status_Code::INVALID_USAGE,
161
      Botan::Certificate_Status_Code::CERT_CHAIN_TOO_LONG,
162
      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER,
163
      Botan::Certificate_Status_Code::NAME_CONSTRAINT_ERROR,
164
      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER,
165
      Botan::Certificate_Status_Code::OCSP_CERT_NOT_LISTED,
166
      Botan::Certificate_Status_Code::OCSP_BAD_STATUS,
167
      Botan::Certificate_Status_Code::CERT_NAME_NOMATCH,
168
      Botan::Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION,
169
      Botan::Certificate_Status_Code::DUPLICATE_CERT_EXTENSION,
170
      Botan::Certificate_Status_Code::EXT_IN_V1_V2_CERT,
171
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR,
172
      Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND,
173
      Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE,
174
      Botan::Certificate_Status_Code::OCSP_RESPONSE_INVALID,
175
      Botan::Certificate_Status_Code::CERT_IS_REVOKED,
176
      Botan::Certificate_Status_Code::CRL_BAD_SIGNATURE,
177
      Botan::Certificate_Status_Code::SIGNATURE_ERROR,
178
      Botan::Certificate_Status_Code::CERT_PUBKEY_INVALID,
179
      Botan::Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN,
180
      Botan::Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS,
181
   };
182

183
   for(const auto code : codes) {
45✔
184
      const std::string s = Botan::to_string(code);
44✔
185
      result.confirm("String is long enough to be informative", s.size() > 12);
88✔
186
      result.test_eq("No duplicates", seen.count(s), 0);
44✔
187
      seen.insert(s);
44✔
188
   }
44✔
189

190
   return result;
1✔
191
}
1✔
192

193
Test::Result test_x509_extension() {
1✔
194
   Test::Result result("X509 Extensions API");
1✔
195

196
   Botan::Extensions extn;
1✔
197

198
   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
1✔
199
   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
1✔
200

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

203
   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
2✔
204
   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
2✔
205
   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
2✔
206
   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
2✔
207

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

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

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

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

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

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

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

226
   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
2✔
227
   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
2✔
228
   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
2✔
229
   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
2✔
230

231
   return result;
2✔
232
}
2✔
233

234
Test::Result test_x509_dates() {
1✔
235
   Test::Result result("X509 Time");
1✔
236

237
   Botan::X509_Time time;
1✔
238
   result.confirm("unset time not set", !time.time_is_set());
2✔
239
   time = Botan::X509_Time("080201182200Z", Botan::ASN1_Type::UtcTime);
1✔
240
   result.confirm("time set after construction", time.time_is_set());
2✔
241
   result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
2✔
242

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

246
   time = Botan::X509_Time("200305100350Z");
1✔
247
   result.test_eq(
3✔
248
      "UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
249

250
   time = Botan::X509_Time("20200305100350Z");
1✔
251
   result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string",
3✔
252
                  time.readable_string(),
2✔
253
                  "2020/03/05 10:03:50 UTC");
254

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

258
   // Dates that are valid per X.500 but rejected as unsupported
259
   const std::string valid_but_unsup[]{
1✔
260
      "0802010000-0000",
261
      "0802011724+0000",
262
      "0406142334-0500",
263
      "9906142334+0500",
264
      "0006142334-0530",
265
      "0006142334+0530",
266

267
      "080201000000-0000",
268
      "080201172412+0000",
269
      "040614233433-0500",
270
      "990614233444+0500",
271
      "000614233455-0530",
272
      "000614233455+0530",
273
   };
13✔
274

275
   // valid length 13
276
   const std::string valid_utc[]{
1✔
277
      "080201000000Z",
278
      "080201172412Z",
279
      "040614233433Z",
280
      "990614233444Z",
281
      "000614233455Z",
282
   };
6✔
283

284
   const std::string invalid_utc[]{
1✔
285
      "",
286
      " ",
287
      "2008`02-01",
288
      "9999-02-01",
289
      "2000-02-01 17",
290
      "999921",
291

292
      // No seconds
293
      "0802010000Z",
294
      "0802011724Z",
295
      "0406142334Z",
296
      "9906142334Z",
297
      "0006142334Z",
298

299
      // valid length 13 -> range check
300
      "080201000061Z",  // seconds too big (61)
301
      "080201000060Z",  // seconds too big (60, leap seconds not covered by the standard)
302
      "0802010000-1Z",  // seconds too small (-1)
303
      "080201006000Z",  // minutes too big (60)
304
      "080201240000Z",  // hours too big (24:00)
305

306
      // valid length 13 -> invalid numbers
307
      "08020123112 Z",
308
      "08020123112!Z",
309
      "08020123112,Z",
310
      "08020123112\nZ",
311
      "080201232 33Z",
312
      "080201232!33Z",
313
      "080201232,33Z",
314
      "080201232\n33Z",
315
      "0802012 3344Z",
316
      "0802012!3344Z",
317
      "0802012,3344Z",
318
      "08022\n334455Z",
319
      "08022 334455Z",
320
      "08022!334455Z",
321
      "08022,334455Z",
322
      "08022\n334455Z",
323
      "082 33445511Z",
324
      "082!33445511Z",
325
      "082,33445511Z",
326
      "082\n33445511Z",
327
      "2 2211221122Z",
328
      "2!2211221122Z",
329
      "2,2211221122Z",
330
      "2\n2211221122Z",
331

332
      // wrong time zone
333
      "080201000000",
334
      "080201000000z",
335

336
      // Fractional seconds
337
      "170217180154.001Z",
338

339
      // Timezone offset
340
      "170217180154+0100",
341

342
      // Extra digits
343
      "17021718015400Z",
344

345
      // Non-digits
346
      "17021718015aZ",
347

348
      // Trailing garbage
349
      "170217180154Zlongtrailinggarbage",
350

351
      // Swapped type
352
      "20170217180154Z",
353
   };
49✔
354

355
   // valid length 15
356
   const std::string valid_generalized_time[]{
1✔
357
      "20000305100350Z",
358
   };
2✔
359

360
   const std::string invalid_generalized[]{
1✔
361
      // No trailing Z
362
      "20000305100350",
363

364
      // No seconds
365
      "200003051003Z",
366

367
      // Fractional seconds
368
      "20000305100350.001Z",
369

370
      // Timezone offset
371
      "20170217180154+0100",
372

373
      // Extra digits
374
      "2017021718015400Z",
375

376
      // Non-digits
377
      "2017021718015aZ",
378

379
      // Trailing garbage
380
      "20170217180154Zlongtrailinggarbage",
381

382
      // Swapped type
383
      "170217180154Z",
384
   };
9✔
385

386
   for(const auto& v : valid_but_unsup) {
13✔
387
      result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
108✔
388
   }
389

390
   for(const auto& v : valid_utc) {
6✔
391
      Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
392
   }
5✔
393

394
   for(const auto& v : valid_generalized_time) {
2✔
395
      Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
396
   }
1✔
397

398
   for(const auto& v : invalid_utc) {
49✔
399
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
432✔
400
   }
401

402
   for(const auto& v : invalid_generalized) {
9✔
403
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime); });
72✔
404
   }
405

406
   return result;
1✔
407
}
80✔
408

409
Test::Result test_x509_encode_authority_info_access_extension() {
1✔
410
   Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension");
1✔
411

412
   #if defined(BOTAN_HAS_RSA)
413
   auto rng = Test::new_rng(__func__);
1✔
414

415
   const std::string sig_algo{"RSA"};
1✔
416
   const std::string hash_fn{"SHA-256"};
1✔
417
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
418

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

424
   // OCSP
425
   const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"};
1✔
426

427
   // create a CA
428
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
429
   result.require("CA key", ca_key != nullptr);
1✔
430
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
431
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
432

433
   // create a certificate with only caIssuer information
434
   auto key = make_a_private_key(sig_algo, *rng);
1✔
435

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

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

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

443
   if(!result.test_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
444
      return result;
445
   }
446

447
   for(const auto& ca_issuer : cert.ca_issuers()) {
3✔
448
      result.confirm("CA issuer URI present in certificate",
4✔
449
                     std::ranges::find(ca_issuers, ca_issuer) != ca_issuers.end());
4✔
450
   }
1✔
451

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

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

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

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

462
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
463
   result.confirm("no CA Issuer URI available", cert.ca_issuers().empty());
2✔
464
   result.test_eq("OCSP responder URI matches", cert.ocsp_responder(), std::string(ocsp_uri));
3✔
465

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

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

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

474
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
475
   result.confirm("CA Issuer URI available", !cert.ca_issuers().empty());
2✔
476
   #endif
477

478
   return result;
1✔
479
}
4✔
480

481
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
482

483
Test::Result test_crl_dn_name() {
1✔
484
   Test::Result result("CRL DN name");
1✔
485

486
      // See GH #1252
487

488
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
489
   auto rng = Test::new_rng(__func__);
1✔
490

491
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
492

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

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

499
   Botan::X509_CRL crl = ca.new_crl(*rng);
1✔
500

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

503
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
3✔
504
      #endif
505

506
   return result;
2✔
507
}
3✔
508

509
Test::Result test_rdn_multielement_set_name() {
1✔
510
   Test::Result result("DN with multiple elements in RDN");
1✔
511

512
   // GH #2611
513

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

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

519
   return result;
1✔
520
}
1✔
521

522
Test::Result test_rsa_oaep() {
1✔
523
   Test::Result result("RSA OAEP decoding");
1✔
524

525
      #if defined(BOTAN_HAS_RSA)
526
   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
527

528
   auto public_key = cert.subject_public_key();
1✔
529
   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
1✔
530
   const auto& pk_info = cert.subject_public_key_algo();
1✔
531

532
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
533
      #endif
534

535
   return result;
2✔
536
}
1✔
537

538
Test::Result test_x509_decode_list() {
1✔
539
   Test::Result result("X509_Certificate list decode");
1✔
540

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

543
   Botan::BER_Decoder dec(input);
1✔
544
   std::vector<Botan::X509_Certificate> certs;
1✔
545
   dec.decode_list(certs);
1✔
546

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

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

552
   return result;
1✔
553
}
1✔
554

555
Test::Result test_x509_utf8() {
1✔
556
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
557

558
   try {
1✔
559
      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
560

561
      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
562
      const std::string organization =
1✔
563
         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
564
         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
1✔
565
      const std::string organization_unit =
1✔
566
         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
567
         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
1✔
568
      const std::string common_name =
1✔
569
         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
570
         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
1✔
571
      const std::string location = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
572

573
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
574

575
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
576
      result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
2✔
577
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
578
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
579
   } catch(const Botan::Decoding_Error& ex) {
1✔
580
      result.test_failure(ex.what());
×
581
   }
×
582

583
   return result;
1✔
584
}
×
585

586
Test::Result test_x509_bmpstring() {
1✔
587
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
588

589
   try {
1✔
590
      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
591

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

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

600
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
601

602
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
603
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
604
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
605
   } catch(const Botan::Decoding_Error& ex) {
1✔
606
      result.test_failure(ex.what());
×
607
   }
×
608

609
   return result;
1✔
610
}
×
611

612
Test::Result test_x509_teletex() {
1✔
613
   Test::Result result("X509 with TeletexString encoded fields");
1✔
614

615
   try {
1✔
616
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
617

618
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
619

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

622
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
623
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
624
   } catch(const Botan::Decoding_Error& ex) {
1✔
625
      result.test_failure(ex.what());
×
626
   }
×
627

628
   return result;
1✔
629
}
×
630

631
Test::Result test_x509_authority_info_access_extension() {
1✔
632
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
633

634
   // contains no AIA extension
635
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
636

637
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
638
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
639

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

643
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
644

645
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
646
   if(result.tests_failed()) {
1✔
647
      return result;
648
   }
649

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

653
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
654
   Botan::X509_Certificate aia_cert_2ca(
1✔
655
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
656

657
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
658

659
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
660
   if(result.tests_failed()) {
1✔
661
      return result;
662
   }
663

664
   result.test_eq(
2✔
665
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
666
   result.test_eq(
2✔
667
      "CA issuer URL matches",
668
      ca_issuers2[1],
1✔
669
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
670
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
2✔
671

672
   return result;
1✔
673
}
1✔
674

675
Test::Result test_parse_rsa_pss_cert() {
1✔
676
   Test::Result result("X509 RSA-PSS certificate");
1✔
677

678
   // See https://github.com/randombit/botan/issues/3019 for background
679

680
   try {
1✔
681
      Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
682
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
683
   } catch(Botan::Exception& e) {
1✔
684
      result.test_failure("Parsing failed", e.what());
×
685
   }
×
686

687
   return result;
1✔
688
}
×
689

690
Test::Result test_verify_gost2012_cert() {
1✔
691
   Test::Result result("X509 GOST-2012 certificates");
1✔
692

693
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
694
   try {
1✔
695
      Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
696
      Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
697

698
      Botan::Certificate_Store_In_Memory trusted;
1✔
699
      trusted.add_certificate(root_cert);
1✔
700

701
      const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
2✔
702
      const Botan::Path_Validation_Result validation_result =
1✔
703
         Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
704

705
      result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
706
   } catch(const Botan::Decoding_Error& e) {
1✔
707
      result.test_failure(e.what());
×
708
   }
×
709
      #endif
710

711
   return result;
1✔
712
}
×
713

714
      /*
715
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
716
 *
717
 * For the other algorithms than RSA, only one padding is supported right now.
718
 */
719
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
720
Test::Result test_padding_config() {
1✔
721
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
722
   Test::Result test_result("X509 Padding Config");
1✔
723

724
   auto rng = Test::new_rng(__func__);
1✔
725

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

729
   // Create X509 CA certificate; EMSA3 is used for signing by default
730
   Botan::X509_Cert_Options opt("TESTCA");
1✔
731
   opt.CA_key();
1✔
732

733
   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
734
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
735
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
736
                       "RSA/EMSA3(SHA-512)");
737

738
   // Create X509 CA certificate; RSA-PSS is explicitly set
739
   opt.set_padding_scheme("PSSR");
1✔
740
   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
741
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
742
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
743
                       "RSA/EMSA4");
744

745
         #if defined(BOTAN_HAS_EMSA2)
746
   // Try to set a padding scheme that is not supported for signing with the given key type
747
   opt.set_padding_scheme("EMSA2");
748
   try {
749
      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
750
      test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " +
751
                               sk->algo_name());
752
   } catch(const Botan::Invalid_Argument& e) {
753
      test_result.test_eq("Build CA certificate with invalid encoding scheme EMSA1 for key type " + sk->algo_name(),
754
                          e.what(),
755
                          "Signatures using RSA/EMSA2(SHA-512) are not supported");
756
   }
757
         #endif
758

759
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
760
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
761
                       "RSA/EMSA4");
762

763
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
764
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
765

766
   // Prepare a signing request for the end certificate
767
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
768
   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
1✔
769
   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng);
1✔
770
   test_result.test_eq("Certificate request signature algorithm",
3✔
771
                       end_req.signature_algorithm().oid().to_formatted_string(),
2✔
772
                       "RSA/EMSA4");
773

774
   // Create X509 CA object: will fail as the chosen hash functions differ
775
   try {
1✔
776
      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", *rng);
1✔
777
      test_result.test_failure("Configured conflicting hash functions for CA");
×
778
   } catch(const Botan::Invalid_Argument& e) {
1✔
779
      test_result.test_eq(
1✔
780
         "Configured conflicting hash functions for CA",
781
         e.what(),
1✔
782
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding EMSA4(SHA-256)");
783
   }
1✔
784

785
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
786
   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
787
   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, *rng, not_before, not_after);
1✔
788
   test_result.test_eq("End certificate signature algorithm",
3✔
789
                       end_cert_emsa3.signature_algorithm().oid().to_formatted_string(),
2✔
790
                       "RSA/EMSA3(SHA-512)");
791

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

799
   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
800
   Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-512,MGF1,64)", *rng);
1✔
801
   Botan::X509_Certificate end_cert_emsa4 = ca_exp.sign_request(end_req, *rng, not_before, not_after);
1✔
802
   test_result.test_eq("End certificate signature algorithm",
3✔
803
                       end_cert_emsa4.signature_algorithm().oid().to_formatted_string(),
3✔
804
                       "RSA/EMSA4");
805

806
   // Check CRL signature algorithm
807
   Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
808
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4");
2✔
809

810
   // sanity check for verification, the heavy lifting is done in the other unit tests
811
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
812
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
813
   const Botan::Path_Validation_Result validation_result =
1✔
814
      Botan::x509_path_validate(end_cert_emsa4, restrictions, trusted);
1✔
815
   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
2✔
816

817
   return test_result;
2✔
818
}
3✔
819
      #endif
820

821
   #endif
822

823
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
12✔
824
                             const std::string& sig_padding,
825
                             const std::string& hash_fn,
826
                             Botan::RandomNumberGenerator& rng) {
827
   Test::Result result("PKCS10 extensions");
12✔
828

829
   Botan::X509_Cert_Options opts;
12✔
830

831
   opts.dns = "main.example.org";
12✔
832
   opts.more_dns.push_back("more1.example.org");
24✔
833
   opts.more_dns.push_back("more2.example.org");
24✔
834

835
   opts.padding_scheme = sig_padding;
12✔
836

837
   Botan::AlternativeName alt_name;
12✔
838
   alt_name.add_attribute("DNS", "bonus.example.org");
12✔
839

840
   Botan::X509_DN alt_dn;
12✔
841
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
12✔
842
   alt_dn.add_attribute("X520.Organization", "testing");
12✔
843
   alt_name.add_dn(alt_dn);
12✔
844

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

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

849
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
12✔
850

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

853
   if(alt_dns_names.size() == 4) {
12✔
854
      result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
36✔
855
      result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
36✔
856
      result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
36✔
857
      result.test_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
36✔
858
   }
859

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

863
   return result;
12✔
864
}
12✔
865

866
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
12✔
867
                            const std::string& sig_algo,
868
                            const std::string& sig_padding,
869
                            const std::string& hash_fn,
870
                            Botan::RandomNumberGenerator& rng) {
871
   Test::Result result("X509 Unit");
12✔
872

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

876
   {
12✔
877
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
24✔
878
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
24✔
879
   }
880

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

884
   Botan::PKCS10_Request user1_req =
12✔
885
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
12✔
886

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

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

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

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

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

899
   /* Create the CA object */
900
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
901

902
   const BigInt user1_serial = 99;
12✔
903

904
   /* Sign the requests to create the certs */
905
   Botan::X509_Certificate user1_cert =
12✔
906
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
907

908
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
12✔
909
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
12✔
910

911
   Botan::X509_Certificate user2_cert = ca.sign_request(user2_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
912

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

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

919
   {
12✔
920
      auto constraints = req_opts1(sig_algo).constraints;
12✔
921
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
24✔
922
   }
923

924
   /* Copy, assign and compare */
925
   Botan::X509_Certificate user1_cert_copy(user1_cert);
12✔
926
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
12✔
927

928
   user1_cert_copy = user2_cert;
12✔
929
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
12✔
930

931
   Botan::X509_Certificate user1_cert_differ =
12✔
932
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
933

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

936
   /* Get cert data */
937
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
12✔
938

939
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
12✔
940
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
24✔
941
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
24✔
942
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
24✔
943
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
24✔
944

945
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
12✔
946
   result.test_eq("subject OrgaUnit count",
12✔
947
                  user3_subject_dn.get_attribute("OU").size(),
24✔
948
                  req_opts3(sig_algo).more_org_units.size() + 1);
24✔
949
   result.test_eq(
12✔
950
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
48✔
951

952
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
12✔
953
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
24✔
954
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
24✔
955
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
24✔
956

957
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
12✔
958
   result.test_eq(
12✔
959
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
24✔
960
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
48✔
961

962
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
12✔
963

964
   /* Verify the certs */
965
   Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
966
   Botan::Certificate_Store_In_Memory store;
12✔
967

968
   // First try with an empty store
969
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
970
   result.test_eq("user 1 issuer not found",
36✔
971
                  result_no_issuer.result_string(),
24✔
972
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
973

974
   store.add_certificate(ca.ca_certificate());
12✔
975

976
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
977
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
978
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
979
   }
980

981
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
982
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
24✔
983
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
984
   }
985

986
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
12✔
987
   result.test_eq("user 1 issuer not found",
36✔
988
                  result_no_issuer.result_string(),
24✔
989
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
990
   store.add_crl(crl1);
12✔
991

992
   std::vector<Botan::CRL_Entry> revoked;
12✔
993
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
24✔
994
   revoked.push_back(user2_cert);
24✔
995

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

998
   store.add_crl(crl2);
12✔
999

1000
   const std::string revoked_str =
12✔
1001
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
12✔
1002

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

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

1009
   revoked.clear();
12✔
1010
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
24✔
1011
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
12✔
1012

1013
   store.add_crl(crl3);
12✔
1014

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

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

1023
   return result;
12✔
1024
}
60✔
1025

1026
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1027
                        const std::string& sig_algo,
1028
                        const std::string& hash_fn,
1029
                        Botan::RandomNumberGenerator& rng) {
1030
   using Botan::Key_Constraints;
12✔
1031
   using Botan::Usage_Type;
12✔
1032

1033
   Test::Result result("X509 Usage");
12✔
1034

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

1038
   /* Create the CA object */
1039
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1040

1041
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1042

1043
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1044
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1045

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

1048
   const Botan::X509_Certificate user1_cert =
12✔
1049
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1050

1051
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1052
   result.test_eq(
12✔
1053
      "key usage cRLSign not allowed",
1054
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1055
      false);
1056
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1057

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

1061
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1062

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

1065
   const Botan::X509_Certificate mult_usage_cert =
12✔
1066
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1067

1068
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1069
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1070
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1071
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1072
   result.confirm(
24✔
1073
      "key usage multiple digitalSignature and cRLSign allowed",
1074
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1075
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1076

1077
   opts.constraints = Key_Constraints();
12✔
1078

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

1081
   const Botan::X509_Certificate no_usage_cert =
12✔
1082
      ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1083

1084
   // cert allows every usage
1085
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1086
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1087
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1088

1089
   if(sig_algo == "RSA") {
12✔
1090
      // cert allows data encryption
1091
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1092

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

1095
      const Botan::X509_Certificate enc_cert =
1✔
1096
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1097

1098
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1099
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1100
   }
1✔
1101

1102
   return result;
12✔
1103
}
24✔
1104

1105
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
12✔
1106
                              const std::string& sig_algo,
1107
                              const std::string& sig_padding,
1108
                              const std::string& hash_fn,
1109
                              Botan::RandomNumberGenerator& rng) {
1110
   using Botan::Key_Constraints;
12✔
1111

1112
   Test::Result result("X509 Self Issued");
12✔
1113

1114
   // create the self-signed cert
1115
   const Botan::X509_Certificate ca_cert =
12✔
1116
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1117

1118
   /* Create the CA object */
1119
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1120

1121
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1122

1123
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1124
   // but signed by a CA, not signed by it's own private key
1125
   Botan::X509_Cert_Options opts = ca_opts();
12✔
1126
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1127
   opts.set_padding_scheme(sig_padding);
12✔
1128

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

1131
   const Botan::X509_Certificate self_issued_cert =
12✔
1132
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1133

1134
   // check that this chain can can be verified successfully
1135
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
12✔
1136

1137
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1138

1139
   const Botan::Path_Validation_Result validation_result =
12✔
1140
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
12✔
1141

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

1144
   return result;
12✔
1145
}
24✔
1146

1147
Test::Result test_x509_uninit() {
1✔
1148
   Test::Result result("X509 object uninitialized access");
1✔
1149

1150
   Botan::X509_Certificate cert;
1✔
1151
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1152
      cert.x509_version();
1✔
1153
   });
1154

1155
   Botan::X509_CRL crl;
1✔
1156
   result.test_throws(
2✔
1157
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1158

1159
   return result;
1✔
1160
}
1✔
1161

1162
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1163
   using Botan::Key_Constraints;
19✔
1164

1165
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1166

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

1169
   // Now check some typical usage scenarios for the given key type
1170
   // Taken from RFC 5280, sec. 4.2.1.3
1171
   // ALL constraints are not typical at all, but we use them for a negative test
1172
   const auto all = Key_Constraints(
19✔
1173
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1174
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1175
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1176

1177
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1178
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1179
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1180
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1181
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1182
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1183
   const auto key_agreement_encipher_only =
19✔
1184
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1185
   const auto key_agreement_decipher_only =
19✔
1186
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1187
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1188
   const auto sign_everything =
19✔
1189
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1190

1191
   if(pk_algo == "DH" || pk_algo == "ECDH") {
19✔
1192
      // DH and ECDH only for key agreement
1193
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1194
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1195
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1196
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1197
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1198
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1199
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1200
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1201
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1202
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1203
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1204
   } else if(pk_algo == "Kyber" || pk_algo == "FrodoKEM" || pk_algo == "ML-KEM" || pk_algo == "ClassicMcEliece") {
17✔
1205
      // KEMs can encrypt and agree
1206
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
4✔
1207
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
4✔
1208
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
4✔
1209
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
4✔
1210
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
4✔
1211
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1212
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
4✔
1213
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), false);
4✔
1214
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
8✔
1215
   } else if(pk_algo == "RSA") {
13✔
1216
      // RSA can do everything except key agreement
1217
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1218

1219
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1220
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1221
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1222
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1223
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1224
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1225
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1226
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1227
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1228
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1229
   } else if(pk_algo == "ElGamal") {
12✔
1230
      // only ElGamal encryption is currently implemented
1231
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1232
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1233
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1234
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1235
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1236
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1237
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1238
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1239
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1240
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
10✔
1241
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" ||
18✔
1242
             pk_algo == "HSS-LMS") {
3✔
1243
      // these are signature algorithms only
1244
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
9✔
1245

1246
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1247
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1248
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1249
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1250
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1251
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1252
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1253
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1254
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1255
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1256
   }
1257

1258
   return result;
19✔
1259
}
×
1260

1261
/**
1262
 * @brief X.509v3 extension that encodes a given string
1263
 */
1264
class String_Extension final : public Botan::Certificate_Extension {
24✔
1265
   public:
1266
      String_Extension() = default;
24✔
1267

1268
      explicit String_Extension(const std::string& val) : m_contents(val) {}
12✔
1269

1270
      std::string value() const { return m_contents; }
48✔
1271

1272
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1273
         return std::make_unique<String_Extension>(m_contents);
×
1274
      }
1275

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

1278
      bool should_encode() const override { return true; }
24✔
1279

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

1282
      std::vector<uint8_t> encode_inner() const override {
12✔
1283
         std::vector<uint8_t> bits;
12✔
1284
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
12✔
1285
         return bits;
12✔
1286
      }
×
1287

1288
      void decode_inner(const std::vector<uint8_t>& in) override {
24✔
1289
         Botan::ASN1_String str;
24✔
1290
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
24✔
1291
         m_contents = str.value();
24✔
1292
      }
24✔
1293

1294
   private:
1295
      std::string m_contents;
1296
};
1297

1298
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
12✔
1299
                                 const std::string& sig_algo,
1300
                                 const std::string& sig_padding,
1301
                                 const std::string& hash_fn,
1302
                                 Botan::RandomNumberGenerator& rng) {
1303
   Test::Result result("X509 Custom DN");
12✔
1304

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

1308
   /* Create the CA object */
1309
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1310

1311
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1312

1313
   Botan::X509_DN subject_dn;
12✔
1314

1315
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
12✔
1316
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
12✔
1317
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
12✔
1318
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
12✔
1319

1320
   subject_dn.add_attribute(attr1, val1);
12✔
1321
   subject_dn.add_attribute(attr2, val2);
12✔
1322

1323
   Botan::Extensions extensions;
12✔
1324

1325
   Botan::PKCS10_Request req =
12✔
1326
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
12✔
1327

1328
   const Botan::X509_DN& req_dn = req.subject_dn();
12✔
1329

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

1332
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
12✔
1333
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
12✔
1334
   result.confirm("Attr1 matches encoded", req_val1 == val1);
24✔
1335
   result.confirm("Attr2 matches encoded", req_val2 == val2);
24✔
1336
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
24✔
1337
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
24✔
1338

1339
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1340
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1341

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

1344
   const Botan::X509_DN& cert_dn = cert.subject_dn();
12✔
1345

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

1348
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
12✔
1349
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
12✔
1350
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
24✔
1351
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
24✔
1352
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
24✔
1353
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
24✔
1354

1355
   return result;
12✔
1356
}
36✔
1357

1358
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
12✔
1359
                                  const std::string& sig_algo,
1360
                                  const std::string& sig_padding,
1361
                                  const std::string& hash_fn,
1362
                                  Botan::RandomNumberGenerator& rng) {
1363
   using Botan::Key_Constraints;
12✔
1364

1365
   Test::Result result("X509 Extensions");
12✔
1366

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

1370
   /* Create the CA object */
1371
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1372

1373
   /* Prepare CDP extension */
1374
   std::vector<std::string> cdp_urls = {
12✔
1375
      "http://example.com/crl1.pem",
1376
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
12✔
1377

1378
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
12✔
1379

1380
   for(const auto& uri : cdp_urls) {
36✔
1381
      Botan::AlternativeName cdp_alt_name;
24✔
1382
      cdp_alt_name.add_uri(uri);
24✔
1383
      Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
24✔
1384

1385
      dps.emplace_back(dp);
24✔
1386
   }
24✔
1387

1388
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1389

1390
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1391
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1392

1393
   // include a custom extension in the request
1394
   Botan::Extensions req_extensions;
12✔
1395
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
12✔
1396
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
12✔
1397
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
24✔
1398
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
24✔
1399
   opts.extensions = req_extensions;
12✔
1400
   opts.set_padding_scheme(sig_padding);
12✔
1401

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

1405
   result.confirm("Extensions::extension_set true for Key_Usage",
24✔
1406
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
12✔
1407

1408
   // check if known Key_Usage extension is present in self-signed cert
1409
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
12✔
1410
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
24✔
1411
      result.confirm(
24✔
1412
         "Key_Usage extension value matches in self-signed certificate",
1413
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
12✔
1414
   }
1415

1416
   // check if custom extension is present in self-signed cert
1417
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
12✔
1418
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
24✔
1419
      result.test_eq(
48✔
1420
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1421
   }
1422

1423
   // check if CDPs are present in the self-signed cert
1424
   auto cert_cdps =
12✔
1425
      self_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
24✔
1426

1427
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1428
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1429
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1430
         result.confirm("CDP URI present in self-signed certificate",
48✔
1431
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1432
      }
1433
   }
1434

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

1437
   /* Create a CA-signed certificate */
1438
   const Botan::X509_Certificate ca_signed_cert =
12✔
1439
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1440

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

1444
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
24✔
1445
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
24✔
1446
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
12✔
1447
      result.confirm("Key_Usage extension value matches in user certificate",
24✔
1448
                     constraints == Botan::Key_Constraints::DigitalSignature);
12✔
1449
   }
1450

1451
   // check if custom extension is present in CA-signed cert
1452
   result.confirm("Extensions::extension_set true for String_Extension",
24✔
1453
                  ca_signed_cert.v3_extensions().extension_set(oid));
12✔
1454
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
24✔
1455
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
24✔
1456
      result.test_eq(
48✔
1457
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1458
   }
1459

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

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

1471
   return result;
12✔
1472
}
60✔
1473

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

1477
   struct TestData {
12✔
1478
         const std::string issuer, subject, issuer_hash, subject_hash;
1479
   } const cases[]{{"",
12✔
1480
                    "",
1481
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1482
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1483
                   {"a",
1484
                    "b",
1485
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1486
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1487
                   {"A",
1488
                    "B",
1489
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1490
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1491
                   {
1492
                      "Test Issuer/US/Botan Project/Testing",
1493
                      "Test Subject/US/Botan Project/Testing",
1494
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1495
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1496
                   },
1497
                   {
1498
                      "Test Subject/US/Botan Project/Testing",
1499
                      "Test Issuer/US/Botan Project/Testing",
1500
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1501
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1502
                   }};
72✔
1503

1504
   for(const auto& a : cases) {
72✔
1505
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1506
      opts.CA_key();
60✔
1507

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

1510
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1511
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1512

1513
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1514
      const Botan::PKCS10_Request req =
60✔
1515
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1516
      const Botan::X509_Certificate subject_cert =
60✔
1517
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1518

1519
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1520
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1521
   }
60✔
1522
   return result;
12✔
1523
}
72✔
1524

1525
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1526

1527
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1528
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1529

1530
      asn1=SEQUENCE:tn_auth_list
1531

1532
      [tn_auth_list]
1533
      spc=EXP:0,IA5:1001
1534
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1535
      one=EXP:2,IA5:333
1536

1537
      [TelephoneNumberRange]
1538
      start1=IA5:111
1539
      count1=INT:128
1540
      start2=IA5:222
1541
      count2=INT:256
1542
    */
1543
   const std::string filename("TNAuthList.pem");
1✔
1544
   Test::Result result("X509 TNAuthList decode");
1✔
1545
   result.start_timer();
1✔
1546

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

1549
   using Botan::Cert_Extension::TNAuthList;
1✔
1550

1551
   auto tn_auth_list = cert.v3_extensions().get_extension_object_as<TNAuthList>();
2✔
1552

1553
   auto& tn_entries = tn_auth_list->entries();
1✔
1554

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

1557
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1558
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1559
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1560
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1561
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1562
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1563

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

1567
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1568
   auto& range = tn_entries[1].telephone_number_range();
1✔
1569
   result.test_eq("range entries count", range.size(), 2);
1✔
1570
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1571
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1572
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1573
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1574

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

1578
   result.end_timer();
1✔
1579
   return result;
2✔
1580
}
1✔
1581

1582
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
1583
   Test::Result result("X509 IP Address Block decode");
1✔
1584
   result.start_timer();
1✔
1585
   const std::string filename("IPAddrBlocksAll.pem");
1✔
1586

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

1589
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1590

1591
   auto ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
1592

1593
   const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
1594
   result.confirm("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr, true);
2✔
1595
   result.confirm("cert has exactly one IpAddrBlock", addr_blocks.size() == 2, true);
2✔
1596

1597
   const auto& ipv4block =
1✔
1598
      std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(addr_blocks[0].addr_choice());
1✔
1599
   const auto& ipv6block =
1✔
1600
      std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(addr_blocks[1].addr_choice());
1✔
1601

1602
   auto& v4_blocks = ipv4block.ranges().value();
1✔
1603

1604
   result.confirm("ipv4 block 0 min", v4_blocks[0].min().value() == std::array<uint8_t, 4>{192, 168, 0, 0}, true);
2✔
1605
   result.confirm("ipv4 block 0 max", v4_blocks[0].max().value() == std::array<uint8_t, 4>{192, 168, 127, 255}, true);
2✔
1606

1607
   result.confirm("ipv4 block 1 min", v4_blocks[1].min().value() == std::array<uint8_t, 4>{193, 168, 0, 0}, true);
2✔
1608
   result.confirm("ipv4 block 1 max", v4_blocks[1].max().value() == std::array<uint8_t, 4>{193, 169, 255, 255}, true);
2✔
1609

1610
   result.confirm("ipv4 block 2 min", v4_blocks[2].min().value() == std::array<uint8_t, 4>{194, 168, 0, 0}, true);
2✔
1611
   result.confirm("ipv4 block 2 max", v4_blocks[2].max().value() == std::array<uint8_t, 4>{195, 175, 1, 2}, true);
2✔
1612

1613
   result.confirm("ipv4 block 3 min", v4_blocks[3].min().value() == std::array<uint8_t, 4>{196, 168, 0, 1}, true);
2✔
1614
   result.confirm("ipv4 block 3 max", v4_blocks[3].max().value() == std::array<uint8_t, 4>{196, 168, 0, 1}, true);
2✔
1615

1616
   auto& v6_blocks = ipv6block.ranges().value();
1✔
1617

1618
   result.confirm("ipv6 block 0 min",
1✔
1619
                  v6_blocks[0].min().value() ==
1✔
1620
                     std::array<uint8_t, 16>{
1✔
1621
                        0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1622
                  true);
1623

1624
   result.confirm("ipv6 block 0 max",
1✔
1625
                  v6_blocks[0].max().value() ==
1✔
1626
                     std::array<uint8_t, 16>{
1✔
1627
                        0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1628
                  true);
1629

1630
   result.confirm("ipv6 block 1 min",
1✔
1631
                  v6_blocks[1].min().value() ==
1✔
1632
                     std::array<uint8_t, 16>{
1✔
1633
                        0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1634
                  true);
1635

1636
   result.confirm("ipv6 block 1 max",
1✔
1637
                  v6_blocks[1].max().value() ==
1✔
1638
                     std::array<uint8_t, 16>{
1✔
1639
                        0xfe, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1640
                  true);
1641

1642
   result.confirm("ipv6 block 2 min",
1✔
1643
                  v6_blocks[2].min().value() ==
1✔
1644
                     std::array<uint8_t, 16>{
1✔
1645
                        0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4},
1646
                  true);
1647

1648
   result.confirm("ipv6 block 2 max",
1✔
1649
                  v6_blocks[2].max().value() ==
1✔
1650
                     std::array<uint8_t, 16>{
1✔
1651
                        0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4},
1652
                  true);
1653

1654
   result.confirm("ipv6 block 3 min",
1✔
1655
                  v6_blocks[3].min().value() ==
1✔
1656
                     std::array<uint8_t, 16>{
1✔
1657
                        0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
1658
                  true);
1659

1660
   result.confirm("ipv6 block 3 max",
1✔
1661
                  v6_blocks[3].max().value() ==
1✔
1662
                     std::array<uint8_t, 16>{
1✔
1663
                        0xcd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
1664
                  true);
1665

1666
   result.end_timer();
1✔
1667
   return result;
2✔
1668
}
1✔
1669

1670
Test::Result test_x509_as_blocks_extension_decode() {
1✔
1671
   Test::Result result("X509 AS Block decode");
1✔
1672
   result.start_timer();
1✔
1673
   using Botan::Cert_Extension::ASBlocks;
1✔
1674

1675
   {
1✔
1676
      const std::string filename("ASNumberCert.pem");
1✔
1677
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1678

1679
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1680

1681
      const auto& identifier = as_blocks->as_identifiers();
1✔
1682
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1683

1684
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
1685
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
1686

1687
      // cert contains asnum 0-999, 5042, 0-4294967295
1688
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
1689
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
1690

1691
      // and rdi 1234-5678, 32768, 0-4294967295
1692
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1693
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1694
   }
1✔
1695
   {
1✔
1696
      const std::string filename("ASNumberOnly.pem");
1✔
1697
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1698

1699
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1700

1701
      const auto& identifier = as_blocks->as_identifiers();
1✔
1702
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1703

1704
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
1705
      result.confirm("cert has no RDI entries", identifier.rdi().has_value(), false);
2✔
1706

1707
      // contains 0-999, 0-4294967295
1708
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
1709
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
1710
   }
1✔
1711
   {
1✔
1712
      const std::string filename("ASRdiOnly.pem");
1✔
1713
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1714

1715
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1716

1717
      const auto& identifier = as_blocks->as_identifiers();
1✔
1718
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1719

1720
      result.confirm("cert has no ASNUM entries", identifier.asnum().has_value(), false);
2✔
1721
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
1722

1723
      // contains 1234-5678, 0-4294967295
1724
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1725
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1726
   }
1✔
1727
   {
1✔
1728
      const std::string filename("ASNumberInherit.pem");
1✔
1729
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1730

1731
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1732

1733
      const auto& identifier = as_blocks->as_identifiers();
1✔
1734
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1735

1736
      result.confirm("asnum has no entries", identifier.asnum().value().ranges().has_value(), false);
2✔
1737
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
1738

1739
      // contains 1234-5678, 0-4294967295
1740
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1741
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1742
   }
1✔
1743

1744
   result.end_timer();
1✔
1745
   return result;
1✔
1746
}
×
1747

1748
   #endif
1749

1750
Test::Result test_x509_ip_addr_blocks_extension_encode() {
1✔
1751
   Test::Result result("X509 IP Address Block encode");
1✔
1752
   #if defined(BOTAN_HAS_RSA)
1753
   result.start_timer();
1✔
1754

1755
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1756

1757
   auto rng = Test::new_rng(__func__);
1✔
1758

1759
   const std::string sig_algo{"RSA"};
1✔
1760
   const std::string hash_fn{"SHA-256"};
1✔
1761
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
1762

1763
   for(size_t i = 0; i < 64; i++) {
65✔
1764
      bool push_ipv4_ranges = i & 1;
64✔
1765
      bool push_ipv6_ranges = i >> 1 & 1;
64✔
1766
      bool inherit_ipv4 = i >> 2 & 1;
64✔
1767
      bool inherit_ipv6 = i >> 3 & 1;
64✔
1768
      bool push_ipv4_family = i >> 4 & 1;
64✔
1769
      bool push_ipv6_family = i >> 5 & 1;
64✔
1770

1771
      auto ca_key = make_a_private_key(sig_algo, *rng);
64✔
1772
      result.require("CA key", ca_key != nullptr);
64✔
1773
      const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
64✔
1774
      Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
64✔
1775

1776
      auto key = make_a_private_key(sig_algo, *rng);
64✔
1777

1778
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
64✔
1779

1780
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
1781
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1782
      a = {255, 255, 255, 255};
64✔
1783
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1784

1785
      // encoded as min, max
1786
      a = {127, 0, 0, 1};
64✔
1787
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1788
      a = {189, 5, 7, 255};
64✔
1789
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1790

1791
      // encoded as prefix
1792
      a = {190, 5, 0, 0};
64✔
1793
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1794
      a = {190, 5, 127, 255};
64✔
1795
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1796

1797
      a = {0xab, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
1798
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1799
      a = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
64✔
1800
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1801

1802
      // encoded as min, max
1803
      a = {0xaf, 0x23, 0x34, 0x45, 0x67, 0x2a, 0x7d, 0xef, 0x8c, 0x00, 0x00, 0x00, 0x66, 0x00, 0x52, 0x00};
64✔
1804
      auto ipv6_range_1_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1805

1806
      a = {0xaf, 0xcd, 0xde, 0xf0, 0x00, 0x0f, 0xee, 0x00, 0xbb, 0x4a, 0x9b, 0x00, 0x00, 0x4c, 0x00, 0xcc};
64✔
1807
      auto ipv6_range_1_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1808

1809
      // encoded as prefix
1810
      a = {0xbf, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
1811
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1812
      a = {0xbf, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x07, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
64✔
1813
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1814

1815
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_1);
64✔
1816
      auto ipv4_range_2 =
64✔
1817
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
1818
      auto ipv4_range_3 =
64✔
1819
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
1820
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_2);
64✔
1821

1822
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_1);
64✔
1823
      auto ipv6_range_2 =
64✔
1824
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
1825
      auto ipv6_range_3 =
64✔
1826
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
1827
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_2);
64✔
1828

1829
      std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>> ipv4_ranges;
64✔
1830
      if(push_ipv4_ranges) {
64✔
1831
         ipv4_ranges.push_back(ipv4_range_1);
32✔
1832
         ipv4_ranges.push_back(ipv4_range_2);
32✔
1833
         ipv4_ranges.push_back(ipv4_range_3);
32✔
1834
         ipv4_ranges.push_back(ipv4_range_4);
32✔
1835
      }
1836

1837
      std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>> ipv6_ranges;
64✔
1838
      if(push_ipv6_ranges) {
64✔
1839
         ipv6_ranges.push_back(ipv6_range_1);
32✔
1840
         ipv6_ranges.push_back(ipv6_range_2);
32✔
1841
         ipv6_ranges.push_back(ipv6_range_3);
32✔
1842
         ipv6_ranges.push_back(ipv6_range_4);
32✔
1843
      }
1844

1845
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>();
64✔
1846
      if(!inherit_ipv4) {
64✔
1847
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>(ipv4_ranges);
96✔
1848
      }
1849

1850
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>();
64✔
1851
      if(!inherit_ipv6) {
64✔
1852
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>(ipv6_ranges);
96✔
1853
      }
1854

1855
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
1856
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
1857

1858
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
1859
      if(push_ipv4_family) {
64✔
1860
         addr_blocks.push_back(ipv4_addr_family);
32✔
1861
      }
1862
      if(push_ipv6_family) {
64✔
1863
         addr_blocks.push_back(ipv6_addr_family);
32✔
1864
      }
1865

1866
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
1867

1868
      opts1.extensions.add(std::move(blocks));
64✔
1869

1870
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
64✔
1871
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
1872
      {
64✔
1873
         auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
128✔
1874
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
1875

1876
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
1877
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
1878
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
1879
            continue;
16✔
1880
         }
1881

1882
         if(push_ipv4_family) {
48✔
1883
            auto family = dec_addr_blocks[0];
32✔
1884
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
1885
            result.confirm("ipv4 family safi", ipv4_addr_family.safi() == family.safi(), true);
96✔
1886
            auto choice =
32✔
1887
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(family.addr_choice());
32✔
1888

1889
            if(!inherit_ipv4) {
32✔
1890
               auto ranges = choice.ranges().value();
16✔
1891
               if(push_ipv4_ranges) {
16✔
1892
                  result.confirm("ipv4 entry 0 min", ranges[0].min().value() == ipv4_range_1.min().value(), true);
16✔
1893
                  result.confirm("ipv4 entry 0 max", ranges[0].max().value() == ipv4_range_1.max().value(), true);
16✔
1894
                  result.confirm("ipv4 entry 1 min", ranges[1].min().value() == ipv4_range_2.min().value(), true);
16✔
1895
                  result.confirm("ipv4 entry 1 max", ranges[1].max().value() == ipv4_range_2.max().value(), true);
16✔
1896
                  result.confirm("ipv4 entry 2 min", ranges[2].min().value() == ipv4_range_3.min().value(), true);
16✔
1897
                  result.confirm("ipv4 entry 2 max", ranges[2].max().value() == ipv4_range_3.max().value(), true);
16✔
1898
                  result.confirm("ipv4 entry 3 min", ranges[3].min().value() == ipv4_range_4.min().value(), true);
16✔
1899
                  result.confirm("ipv4 entry 3 max", ranges[3].max().value() == ipv4_range_4.max().value(), true);
16✔
1900
               } else {
1901
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
1902
               }
1903
            } else {
16✔
1904
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
1905
            }
1906
         }
64✔
1907

1908
         if(push_ipv6_family) {
48✔
1909
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
1910
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
1911
            result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
96✔
1912
            auto choice =
32✔
1913
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(family.addr_choice());
32✔
1914
            if(!inherit_ipv6) {
32✔
1915
               auto ranges = choice.ranges().value();
16✔
1916
               if(push_ipv6_ranges) {
16✔
1917
                  result.confirm("ipv6 entry 0 min", ranges[0].min().value() == ipv6_range_1.min().value(), true);
16✔
1918
                  result.confirm("ipv6 entry 0 max", ranges[0].max().value() == ipv6_range_1.max().value(), true);
16✔
1919
                  result.confirm("ipv6 entry 1 min", ranges[1].min().value() == ipv6_range_2.min().value(), true);
16✔
1920
                  result.confirm("ipv6 entry 1 max", ranges[1].max().value() == ipv6_range_2.max().value(), true);
16✔
1921
                  result.confirm("ipv6 entry 2 min", ranges[2].min().value() == ipv6_range_3.min().value(), true);
16✔
1922
                  result.confirm("ipv6 entry 2 max", ranges[2].max().value() == ipv6_range_3.max().value(), true);
16✔
1923
                  result.confirm("ipv6 entry 3 min", ranges[3].min().value() == ipv6_range_4.min().value(), true);
16✔
1924
                  result.confirm("ipv6 entry 3 max", ranges[3].max().value() == ipv6_range_4.max().value(), true);
16✔
1925
               } else {
1926
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
1927
               }
1928
            } else {
16✔
1929
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
1930
            }
1931
         }
64✔
1932
      }
1933
   }
448✔
1934

1935
   result.end_timer();
1✔
1936
   #endif
1937
   return result;
1✔
1938
}
2✔
1939

1940
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases() {
1✔
1941
   Test::Result result("X509 IP Address Block encode edge cases");
1✔
1942
   #if defined(BOTAN_HAS_RSA)
1943
   result.start_timer();
1✔
1944

1945
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1946

1947
   auto rng = Test::new_rng(__func__);
1✔
1948

1949
   const std::string sig_algo{"RSA"};
1✔
1950
   const std::string hash_fn{"SHA-256"};
1✔
1951
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
1952

1953
   // trailing 0s, trailing 1s, and some arbitrary values
1954
   std::vector<uint8_t> edge_values = {2, 4, 8, 16, 32, 64, 128, 0, 1, 3, 7, 15, 31, 63, 127, 255, 12, 46, 123, 234};
1✔
1955

1956
   for(size_t i = 0; i < edge_values.size(); i++) {
21✔
1957
      for(size_t j = 0; j < 18; j++) {
380✔
1958
         auto ca_key = make_a_private_key(sig_algo, *rng);
360✔
1959
         result.require("CA key", ca_key != nullptr);
360✔
1960
         const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
360✔
1961
         Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
360✔
1962

1963
         auto key = make_a_private_key(sig_algo, *rng);
360✔
1964

1965
         Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
360✔
1966

1967
         std::vector<uint8_t> min_bytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
360✔
1968
         std::vector<uint8_t> max_bytes = {
360✔
1969
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
360✔
1970

1971
         min_bytes[15 - (j < 2 ? 0 : j - 2)] = edge_values[i];
360✔
1972
         max_bytes[15 - (j > 15 ? 15 : j)] = edge_values[i];
360✔
1973

1974
         auto address_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(min_bytes);
360✔
1975
         auto address_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(max_bytes);
360✔
1976

1977
         auto ipv6_range = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(address_min, address_max);
360✔
1978

1979
         std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>> ipv6_ranges;
360✔
1980
         ipv6_ranges.push_back(ipv6_range);
360✔
1981

1982
         auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>(ipv6_ranges);
360✔
1983

1984
         auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
1,080✔
1985

1986
         std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
360✔
1987
         addr_blocks.push_back(ipv6_addr_family);
360✔
1988

1989
         std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
360✔
1990

1991
         opts1.extensions.add(std::move(blocks));
360✔
1992

1993
         Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
360✔
1994
         Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
360✔
1995
         {
360✔
1996
            auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
720✔
1997
            result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
720✔
1998
            const auto& dec_addr_blocks = ip_blocks->addr_blocks();
360✔
1999
            auto family = dec_addr_blocks[0];
360✔
2000
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
720✔
2001
            result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
1,080✔
2002
            auto choice =
360✔
2003
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(family.addr_choice());
360✔
2004
            auto ranges = choice.ranges().value();
360✔
2005

2006
            result.confirm("ipv6 edge case min", ranges[0].min().value() == ipv6_range.min().value(), true);
720✔
2007
            result.confirm("ipv6 edge case max", ranges[0].max().value() == ipv6_range.max().value(), true);
720✔
2008
         }
720✔
2009
      }
2,520✔
2010
   }
2011
   result.end_timer();
1✔
2012
   #endif
2013
   return result;
1✔
2014
}
2✔
2015

2016
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
2017
   Test::Result result("X509 IP Address Block range merge");
1✔
2018
   #if defined(BOTAN_HAS_RSA)
2019
   result.start_timer();
1✔
2020

2021
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
2022

2023
   auto rng = Test::new_rng(__func__);
1✔
2024

2025
   const std::string sig_algo{"RSA"};
1✔
2026
   const std::string hash_fn{"SHA-256"};
1✔
2027
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2028

2029
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2030
   result.require("CA key", ca_key != nullptr);
1✔
2031
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2032
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2033

2034
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2035

2036
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2037

2038
   std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
2039
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
2040
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
2041
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
2042
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
2043
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
2044
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
2045
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
2046
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
2047
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
2048
   };
46✔
2049

2050
   std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>> ipv6_ranges;
1✔
2051
   for(auto pair : addresses) {
10✔
2052
      auto address_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(pair[0]);
9✔
2053
      auto address_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(pair[1]);
9✔
2054
      auto range = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(address_min, address_max);
9✔
2055
      ipv6_ranges.push_back(range);
9✔
2056
   }
9✔
2057

2058
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>(ipv6_ranges);
1✔
2059
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
2060

2061
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
2062
   addr_blocks.push_back(ipv6_addr_family);
1✔
2063

2064
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
2065

2066
   opts1.extensions.add(std::move(blocks));
1✔
2067

2068
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
2069
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2070
   {
1✔
2071
      auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
2072
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
2073
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
2074
      auto family = dec_addr_blocks[0];
1✔
2075
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(family.addr_choice());
1✔
2076
      auto ranges = choice.ranges().value();
1✔
2077

2078
      std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
2079
      std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
2080

2081
      result.confirm("range expected min", ranges[0].min().value() == expected_min, true);
2✔
2082
      result.confirm("range expected max", ranges[0].max().value() == expected_max, true);
2✔
2083
      result.confirm("range length", ranges.size() == 1, true);
2✔
2084
   }
2✔
2085

2086
   result.end_timer();
1✔
2087
   #endif
2088
   return result;
2✔
2089
}
26✔
2090

2091
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
2092
   Test::Result result("X509 IP Address Block family merge");
1✔
2093
   #if defined(BOTAN_HAS_RSA)
2094
   result.start_timer();
1✔
2095

2096
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
2097

2098
   auto rng = Test::new_rng(__func__);
1✔
2099

2100
   const std::string sig_algo{"RSA"};
1✔
2101
   const std::string hash_fn{"SHA-256"};
1✔
2102
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2103

2104
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2105
   result.require("CA key", ca_key != nullptr);
1✔
2106
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2107
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2108

2109
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2110

2111
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2112

2113
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
2114

2115
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4> v4_empty_choice;
1✔
2116
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6> v6_empty_choice;
1✔
2117

2118
   uint8_t v4_addr_1[4] = {123, 123, 123, 123};
1✔
2119
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4> v4_choice_dupl({{{v4_addr_1}, {v4_addr_1}}});
1✔
2120
   result.confirm(
2✔
2121
      "IPAddressChoice v4 merges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
2122
   IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
2123

2124
   uint8_t v6_addr_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
2125
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6> v6_choice_dupl({{{v6_addr_1}, {v6_addr_1}}});
1✔
2126
   result.confirm(
2✔
2127
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
2128
   IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
2129

2130
   IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
2131
   IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
2132

2133
   IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
2134
   IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
2135

2136
   /*
2137
   considering the push order, the resulting order should be
2138
   [0] v4 no safi
2139
   [1] v6 no safi
2140
   [2] v4 safi
2141
   [3] v6 safi
2142
   */
2143
   for(size_t i = 0; i < 3; i++) {
4✔
2144
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
2145
      addr_blocks.push_back(v6_empty_fam);
3✔
2146
      addr_blocks.push_back(v4_fam_dupl);
3✔
2147
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
2148
      addr_blocks.push_back(v6_fam_dupl);
3✔
2149
      addr_blocks.push_back(v4_empty_fam);
3✔
2150
   }
2151

2152
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
2153
      v4_empty_fam, v6_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_fam_dupl, v6_empty_fam_safi};
7✔
2154

2155
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
2156

2157
   opts1.extensions.add(std::move(blocks));
1✔
2158

2159
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
2160
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2161

2162
   auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
2163
   result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
2164
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
2165

2166
   result.confirm("blocks got merged lengthwise", dec_blocks.size() == expected_blocks.size(), true);
2✔
2167

2168
   bool sorted = true;
1✔
2169
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
2170
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
2171
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
2172

2173
      uint32_t afam_a = a.afi();
5✔
2174
      if(a.safi().has_value()) {
5✔
2175
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
2176
      }
2177

2178
      uint32_t afam_b = b.afi();
5✔
2179
      if(b.safi().has_value()) {
5✔
2180
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
2181
      }
2182

2183
      if(afam_a > afam_b) {
5✔
2184
         sorted = false;
2185
         break;
2186
      }
2187
   }
2188

2189
   result.confirm("blocks got sorted", sorted, true);
2✔
2190

2191
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
2192
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
2193
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
2194

2195
      result.confirm("blocks match push order by afi at index " + std::to_string(i), dec.afi() == exp.afi(), true);
12✔
2196
      result.confirm("blocks match push order by safi at index " + std::to_string(i), dec.safi() == exp.safi(), true);
18✔
2197

2198
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
2199
         auto dec_choice =
3✔
2200
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(dec.addr_choice());
3✔
2201
         auto exp_choice =
3✔
2202
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(exp.addr_choice());
3✔
2203

2204
         if(!exp_choice.ranges().has_value()) {
3✔
2205
            result.confirm(
2✔
2206
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
2207
         } else {
2208
            result.confirm(
1✔
2209
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
2210

2211
            if(dec_choice.ranges().has_value() == false) {
1✔
2212
               continue;
×
2213
            }
2214

2215
            auto dec_ranges = dec_choice.ranges().value();
1✔
2216
            auto exp_ranges = exp_choice.ranges().value();
1✔
2217
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
2218
                           dec_ranges.size() == exp_ranges.size(),
1✔
2219
                           true);
2220

2221
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
2222
               continue;
×
2223
            }
2224

2225
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
2226
               result.confirm(
2✔
2227
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2228
                  exp_ranges[j].min() == dec_ranges[j].min(),
1✔
2229
                  true);
2230
               result.confirm(
2✔
2231
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2232
                  exp_ranges[j].max() == dec_ranges[j].max(),
2✔
2233
                  true);
2234
            }
2235
         }
1✔
2236
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
2237
         auto dec_choice =
3✔
2238
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(dec.addr_choice());
3✔
2239
         auto exp_choice =
3✔
2240
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(exp.addr_choice());
3✔
2241

2242
         if(!exp_choice.ranges().has_value()) {
3✔
2243
            result.confirm(
2✔
2244
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
2245
         } else {
2246
            result.confirm(
1✔
2247
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
2248

2249
            if(dec_choice.ranges().has_value() == false) {
1✔
2250
               continue;
×
2251
            }
2252

2253
            auto dec_ranges = dec_choice.ranges().value();
1✔
2254
            auto exp_ranges = exp_choice.ranges().value();
1✔
2255
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
2256
                           dec_ranges.size() == exp_ranges.size(),
1✔
2257
                           true);
2258

2259
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
2260
               continue;
×
2261
            }
2262

2263
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
2264
               result.confirm(
2✔
2265
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2266
                  exp_ranges[j].min() == dec_ranges[j].min(),
1✔
2267
                  true);
2268
               result.confirm(
2✔
2269
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2270
                  exp_ranges[j].max() == dec_ranges[j].max(),
2✔
2271
                  true);
2272
            }
2273
         }
1✔
2274
      }
4✔
2275
   }
2276

2277
   result.end_timer();
1✔
2278
   #endif
2279
   return result;
2✔
2280
}
23✔
2281

2282
Test::Result test_x509_as_blocks_extension_encode() {
1✔
2283
   Test::Result result("X509 AS Number encode");
1✔
2284
   #if defined(BOTAN_HAS_RSA)
2285
   result.start_timer();
1✔
2286

2287
   using Botan::Cert_Extension::ASBlocks;
1✔
2288

2289
   auto rng = Test::new_rng(__func__);
1✔
2290

2291
   const std::string sig_algo{"RSA"};
1✔
2292
   const std::string hash_fn{"SHA-256"};
1✔
2293
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2294

2295
   for(size_t i = 0; i < 16; i++) {
17✔
2296
      bool push_asnum = i & 1;
16✔
2297
      bool push_rdi = i >> 1 & 1;
16✔
2298
      bool include_asnum = i >> 2 & 1;
16✔
2299
      bool include_rdi = i >> 3 & 1;
16✔
2300

2301
      ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
16✔
2302

2303
      ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
16✔
2304

2305
      ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
16✔
2306

2307
      ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
16✔
2308

2309
      ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
16✔
2310

2311
      ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
16✔
2312

2313
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
16✔
2314
      if(push_asnum) {
16✔
2315
         as_ranges.push_back(asnum_id_or_range0);
8✔
2316
         as_ranges.push_back(asnum_id_or_range1);
8✔
2317
         as_ranges.push_back(asnum_id_or_range2);
8✔
2318
      }
2319

2320
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
16✔
2321
      if(push_rdi) {
16✔
2322
         rdi_ranges.push_back(rdi_id_or_range0);
8✔
2323
         rdi_ranges.push_back(rdi_id_or_range1);
8✔
2324
         rdi_ranges.push_back(rdi_id_or_range2);
8✔
2325
      }
2326

2327
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
16✔
2328
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
16✔
2329

2330
      ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
40✔
2331
                                                              include_rdi ? std::optional(rdi) : std::nullopt);
40✔
2332

2333
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
16✔
2334

2335
      auto ca_key = make_a_private_key(sig_algo, *rng);
16✔
2336
      result.require("CA key", ca_key != nullptr);
16✔
2337
      const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
16✔
2338
      Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
16✔
2339
      auto key = make_a_private_key(sig_algo, *rng);
16✔
2340

2341
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
16✔
2342
      opts1.extensions.add(std::move(blocks));
16✔
2343

2344
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
16✔
2345
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
16✔
2346

2347
      {
16✔
2348
         auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
32✔
2349
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
32✔
2350

2351
         const auto& identifier = as_blocks->as_identifiers();
16✔
2352

2353
         if(include_asnum) {
16✔
2354
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
2355

2356
            if(push_asnum) {
8✔
2357
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
2358
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
2359

2360
               result.confirm("asnum entry 1 min", asnum_entries[1].min() == 5042, true);
8✔
2361
               result.confirm("asnum entry 1 max", asnum_entries[1].max() == 4294967295, true);
8✔
2362
            } else {
2363
               result.confirm("asnum has no entries", asnum_entries.empty(), true);
8✔
2364
            }
2365
         } else {
2366
            result.confirm("no asnum entry", identifier.asnum().has_value(), false);
16✔
2367
         }
2368

2369
         if(include_rdi) {
16✔
2370
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
2371

2372
            if(push_rdi) {
8✔
2373
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
2374
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
2375

2376
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
2377
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
2378
            } else {
2379
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
2380
            }
2381
         } else {
2382
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
16✔
2383
         }
2384
      }
2385
   }
80✔
2386

2387
   result.end_timer();
1✔
2388
   #endif
2389
   return result;
1✔
2390
}
2✔
2391

2392
Test::Result test_x509_ip_as_blocks_range_merge() {
1✔
2393
   Test::Result result("X509 IP Address Block range merge");
1✔
2394
   #if defined(BOTAN_HAS_RSA)
2395
   result.start_timer();
1✔
2396

2397
   using Botan::Cert_Extension::ASBlocks;
1✔
2398

2399
   auto rng = Test::new_rng(__func__);
1✔
2400

2401
   const std::string sig_algo{"RSA"};
1✔
2402
   const std::string hash_fn{"SHA-256"};
1✔
2403
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2404

2405
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2406
   result.require("CA key", ca_key != nullptr);
1✔
2407
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2408
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2409

2410
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2411

2412
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2413

2414
   std::vector<std::vector<uint16_t>> ranges = {
1✔
2415
      {2005, 37005},
2416
      {60, 70},
2417
      {22, 50},
2418
      {35, 2000},
2419
      {2001, 2004},
2420
      {21, 21},
2421
      {0, 20},
2422
   };
9✔
2423

2424
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
2425
   for(auto pair : ranges) {
8✔
2426
      auto range = ASBlocks::ASIdOrRange(pair[0], pair[1]);
7✔
2427
      as_ranges.push_back(range);
7✔
2428
   }
7✔
2429

2430
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
2431

2432
   ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(std::optional(asnum), std::nullopt);
3✔
2433

2434
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
2435

2436
   opts1.extensions.add(std::move(blocks));
1✔
2437

2438
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
2439
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2440
   {
1✔
2441
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
2442
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
2443

2444
      const auto& identifier = as_blocks->as_identifiers();
1✔
2445

2446
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
2447

2448
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
2449
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
2450
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
2451
   }
2452

2453
   result.end_timer();
1✔
2454
   #endif
2455
   return result;
2✔
2456
}
7✔
2457

2458
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
2459
   if(sig_algo == "RSA") {
12✔
2460
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
5✔
2461
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
2462
             sig_algo == "GOST-34.10") {
7✔
2463
      return {hash};
10✔
2464
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
2465
      return {"Pure"};
2✔
2466
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
2467
      return {"Randomized"};
2✔
2468
   } else if(sig_algo == "HSS-LMS") {
2✔
2469
      return {""};
1✔
2470
   } else {
2471
      return {};
1✔
2472
   }
2473
}
7✔
2474

2475
class X509_Cert_Unit_Tests final : public Test {
×
2476
   public:
2477
      std::vector<Test::Result> run() override {
1✔
2478
         std::vector<Test::Result> results;
1✔
2479

2480
         auto& rng = this->rng();
1✔
2481

2482
         const std::string sig_algos[]{"RSA",
1✔
2483
                                       "DSA",
2484
                                       "ECDSA",
2485
                                       "ECGDSA",
2486
                                       "ECKCDSA",
2487
                                       "GOST-34.10",
2488
                                       "Ed25519",
2489
                                       "Ed448",
2490
                                       "Dilithium",
2491
                                       "ML-DSA",
2492
                                       "SLH-DSA",
2493
                                       "HSS-LMS"};
13✔
2494

2495
         for(const std::string& algo : sig_algos) {
13✔
2496
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
2497
            if(algo == "RSA")
2498
               continue;
2499
   #endif
2500

2501
            std::string hash = "SHA-256";
12✔
2502

2503
            if(algo == "Ed25519") {
12✔
2504
               hash = "SHA-512";
1✔
2505
            }
2506
            if(algo == "Ed448") {
12✔
2507
               hash = "SHAKE-256(912)";
1✔
2508
            }
2509
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
2510
               hash = "SHAKE-256(512)";
2✔
2511
            }
2512

2513
            auto key = make_a_private_key(algo, rng);
12✔
2514

2515
            if(key == nullptr) {
12✔
2516
               continue;
×
2517
            }
2518

2519
            results.push_back(test_hashes(*key, hash, rng));
24✔
2520
            results.push_back(test_valid_constraints(*key, algo));
24✔
2521

2522
            Test::Result usage_result("X509 Usage");
12✔
2523
            try {
12✔
2524
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
2525
            } catch(std::exception& e) {
×
2526
               usage_result.test_failure("test_usage " + algo, e.what());
×
2527
            }
×
2528
            results.push_back(usage_result);
12✔
2529

2530
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
24✔
2531
               Test::Result cert_result("X509 Unit");
12✔
2532

2533
               try {
12✔
2534
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
12✔
2535
               } catch(std::exception& e) {
×
2536
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
2537
               }
×
2538
               results.push_back(cert_result);
12✔
2539

2540
               Test::Result pkcs10_result("PKCS10 extensions");
12✔
2541
               try {
12✔
2542
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
12✔
2543
               } catch(std::exception& e) {
×
2544
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
2545
               }
×
2546
               results.push_back(pkcs10_result);
12✔
2547

2548
               Test::Result self_issued_result("X509 Self Issued");
12✔
2549
               try {
12✔
2550
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
12✔
2551
               } catch(std::exception& e) {
×
2552
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
2553
               }
×
2554
               results.push_back(self_issued_result);
12✔
2555

2556
               Test::Result extensions_result("X509 Extensions");
12✔
2557
               try {
12✔
2558
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
12✔
2559
               } catch(std::exception& e) {
×
2560
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
2561
               }
×
2562
               results.push_back(extensions_result);
12✔
2563

2564
               Test::Result custom_dn_result("X509 Custom DN");
12✔
2565
               try {
12✔
2566
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
12✔
2567
               } catch(std::exception& e) {
×
2568
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
2569
               }
×
2570
               results.push_back(custom_dn_result);
12✔
2571
            }
24✔
2572
         }
24✔
2573

2574
         /*
2575
         These are algos which cannot sign but can be included in certs
2576
         */
2577
         const std::vector<std::string> enc_algos = {
1✔
2578
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
2579

2580
         for(const std::string& algo : enc_algos) {
8✔
2581
            auto key = make_a_private_key(algo, rng);
7✔
2582

2583
            if(key) {
7✔
2584
               results.push_back(test_valid_constraints(*key, algo));
14✔
2585
            }
2586
         }
7✔
2587

2588
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
2589
      defined(BOTAN_HAS_RSA)
2590
         Test::Result pad_config_result("X509 Padding Config");
1✔
2591
         try {
1✔
2592
            pad_config_result.merge(test_padding_config());
1✔
2593
         } catch(const std::exception& e) {
×
2594
            pad_config_result.test_failure("test_padding_config", e.what());
×
2595
         }
×
2596
         results.push_back(pad_config_result);
1✔
2597
   #endif
2598

2599
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2600
         results.push_back(test_x509_utf8());
2✔
2601
         results.push_back(test_x509_bmpstring());
2✔
2602
         results.push_back(test_x509_teletex());
2✔
2603
         results.push_back(test_crl_dn_name());
2✔
2604
         results.push_back(test_rdn_multielement_set_name());
2✔
2605
         results.push_back(test_x509_decode_list());
2✔
2606
         results.push_back(test_rsa_oaep());
2✔
2607
         results.push_back(test_x509_authority_info_access_extension());
2✔
2608
         results.push_back(test_verify_gost2012_cert());
2✔
2609
         results.push_back(test_parse_rsa_pss_cert());
2✔
2610
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
2611
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2612
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2613
   #endif
2614

2615
         results.push_back(test_x509_ip_addr_blocks_extension_encode());
2✔
2616
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases());
2✔
2617
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
2618
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
2619
         results.push_back(test_x509_as_blocks_extension_encode());
2✔
2620
         results.push_back(test_x509_ip_as_blocks_range_merge());
2✔
2621
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
2622
         results.push_back(test_x509_extension());
2✔
2623
         results.push_back(test_x509_dates());
2✔
2624
         results.push_back(test_cert_status_strings());
2✔
2625
         results.push_back(test_x509_uninit());
2✔
2626

2627
         return results;
1✔
2628
      }
13✔
2629
};
2630

2631
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
2632

2633
#endif
2634

2635
}  // namespace
2636

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