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

randombit / botan / 13286863269

12 Feb 2025 01:47PM UTC coverage: 91.699% (+0.05%) from 91.649%
13286863269

push

github

arckoor
Add IPAddrBlock and ASIdentifiers extensions (RFC 3779)

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

95840 of 104516 relevant lines covered (91.7%)

11276139.78 hits per line

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

95.78
/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

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

28
namespace Botan_Tests {
29

30
namespace {
31

32
#if defined(BOTAN_HAS_X509_CERTIFICATES)
33

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

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

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

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

50
   opts.CA_key(1);
564✔
51

52
   return opts;
564✔
53
}
×
54

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

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

63
   opts.not_before("160101200000Z");
482✔
64
   opts.not_after("300101200000Z");
482✔
65

66
   opts.challenge = "zoom";
482✔
67

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

74
   return opts;
482✔
75
}
×
76

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

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

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

87
   return opts;
12✔
88
}
×
89

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

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

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

102
   return opts;
60✔
103
}
×
104

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

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

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

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

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

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

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

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

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

203
   return result;
1✔
204
}
1✔
205

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

209
   Botan::Extensions extn;
1✔
210

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

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

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

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

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

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

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

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

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

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

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

244
   return result;
2✔
245
}
2✔
246

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

419
   return result;
1✔
420
}
80✔
421

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

491
   return result;
1✔
492
}
4✔
493

494
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
495

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

499
      // See GH #1252
500

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

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

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

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

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

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

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

519
   return result;
2✔
520
}
3✔
521

522
Test::Result test_rdn_multielement_set_name() {
1✔
523
   Test::Result result("DN with multiple elements in RDN");
1✔
524

525
   // GH #2611
526

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

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

532
   return result;
1✔
533
}
1✔
534

535
Test::Result test_rsa_oaep() {
1✔
536
   Test::Result result("RSA OAEP decoding");
1✔
537

538
      #if defined(BOTAN_HAS_RSA)
539
   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
540

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

545
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
546
      #endif
547

548
   return result;
2✔
549
}
1✔
550

551
Test::Result test_x509_decode_list() {
1✔
552
   Test::Result result("X509_Certificate list decode");
1✔
553

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

556
   Botan::BER_Decoder dec(input);
1✔
557
   std::vector<Botan::X509_Certificate> certs;
1✔
558
   dec.decode_list(certs);
1✔
559

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

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

565
   return result;
1✔
566
}
1✔
567

568
Test::Result test_x509_utf8() {
1✔
569
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
570

571
   try {
1✔
572
      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
573

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

586
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
587

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

596
   return result;
1✔
597
}
×
598

599
Test::Result test_x509_bmpstring() {
1✔
600
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
601

602
   try {
1✔
603
      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
604

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

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

613
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
614

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

622
   return result;
1✔
623
}
×
624

625
Test::Result test_x509_teletex() {
1✔
626
   Test::Result result("X509 with TeletexString encoded fields");
1✔
627

628
   try {
1✔
629
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
630

631
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
632

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

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

641
   return result;
1✔
642
}
×
643

644
Test::Result test_x509_authority_info_access_extension() {
1✔
645
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
646

647
   // contains no AIA extension
648
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
649

650
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
651
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
652

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

656
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
657

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

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

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

670
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
671

672
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
673
   if(result.tests_failed()) {
1✔
674
      return result;
675
   }
676

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

685
   return result;
1✔
686
}
1✔
687

688
Test::Result test_parse_rsa_pss_cert() {
1✔
689
   Test::Result result("X509 RSA-PSS certificate");
1✔
690

691
   // See https://github.com/randombit/botan/issues/3019 for background
692

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

700
   return result;
1✔
701
}
×
702

703
Test::Result test_verify_gost2012_cert() {
1✔
704
   Test::Result result("X509 GOST-2012 certificates");
1✔
705

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

712
         Botan::Certificate_Store_In_Memory trusted;
1✔
713
         trusted.add_certificate(root_cert);
1✔
714

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

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

726
   return result;
1✔
727
}
×
728

729
      /*
730
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
731
 *
732
 * For the other algorithms than RSA, only one padding is supported right now.
733
 */
734
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
735
Test::Result test_padding_config() {
1✔
736
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
737
   Test::Result test_result("X509 Padding Config");
1✔
738

739
   auto rng = Test::new_rng(__func__);
1✔
740

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

744
   // Create X509 CA certificate; EMSA3 is used for signing by default
745
   Botan::X509_Cert_Options opt("TESTCA");
1✔
746
   opt.CA_key();
1✔
747

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

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

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

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

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

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

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

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

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

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

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

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

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

835
   #endif
836

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

843
   Botan::X509_Cert_Options opts;
12✔
844

845
   opts.dns = "main.example.org";
12✔
846
   opts.more_dns.push_back("more1.example.org");
24✔
847
   opts.more_dns.push_back("more2.example.org");
24✔
848

849
   opts.padding_scheme = sig_padding;
12✔
850

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

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

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

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

863
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
12✔
864

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

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

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

877
   return result;
12✔
878
}
12✔
879

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

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

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

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

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

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

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

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

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

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

913
   /* Create the CA object */
914
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
915

916
   const BigInt user1_serial = 99;
12✔
917

918
   /* Sign the requests to create the certs */
919
   Botan::X509_Certificate user1_cert =
12✔
920
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
921

922
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
12✔
923
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
12✔
924

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

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

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

933
   {
12✔
934
      auto constraints = req_opts1(sig_algo).constraints;
12✔
935
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
24✔
936
   }
937

938
   /* Copy, assign and compare */
939
   Botan::X509_Certificate user1_cert_copy(user1_cert);
12✔
940
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
12✔
941

942
   user1_cert_copy = user2_cert;
12✔
943
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
12✔
944

945
   Botan::X509_Certificate user1_cert_differ =
12✔
946
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
947

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

950
   /* Get cert data */
951
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
12✔
952

953
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
12✔
954
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
24✔
955
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
24✔
956
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
24✔
957
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
24✔
958

959
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
12✔
960
   result.test_eq("subject OrgaUnit count",
12✔
961
                  user3_subject_dn.get_attribute("OU").size(),
24✔
962
                  req_opts3(sig_algo).more_org_units.size() + 1);
24✔
963
   result.test_eq(
12✔
964
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
48✔
965

966
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
12✔
967
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
24✔
968
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
24✔
969
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
24✔
970

971
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
12✔
972
   result.test_eq(
12✔
973
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
24✔
974
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
48✔
975

976
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
12✔
977

978
   /* Verify the certs */
979
   Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
980
   Botan::Certificate_Store_In_Memory store;
12✔
981

982
   // First try with an empty store
983
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
984
   result.test_eq("user 1 issuer not found",
36✔
985
                  result_no_issuer.result_string(),
24✔
986
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
987

988
   store.add_certificate(ca.ca_certificate());
12✔
989

990
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
991
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
992
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
993
   }
994

995
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
996
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
24✔
997
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
998
   }
999

1000
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
12✔
1001
   result.test_eq("user 1 issuer not found",
36✔
1002
                  result_no_issuer.result_string(),
24✔
1003
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1004
   store.add_crl(crl1);
12✔
1005

1006
   std::vector<Botan::CRL_Entry> revoked;
12✔
1007
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
24✔
1008
   revoked.push_back(user2_cert);
24✔
1009

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

1012
   store.add_crl(crl2);
12✔
1013

1014
   const std::string revoked_str =
12✔
1015
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
12✔
1016

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

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

1023
   revoked.clear();
12✔
1024
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
24✔
1025
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
12✔
1026

1027
   store.add_crl(crl3);
12✔
1028

1029
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1030
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
1031
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1032
   }
1033

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

1037
   return result;
12✔
1038
}
48✔
1039

1040
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1041
                        const std::string& sig_algo,
1042
                        const std::string& hash_fn,
1043
                        Botan::RandomNumberGenerator& rng) {
1044
   using Botan::Key_Constraints;
12✔
1045
   using Botan::Usage_Type;
12✔
1046

1047
   Test::Result result("X509 Usage");
12✔
1048

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

1052
   /* Create the CA object */
1053
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1054

1055
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1056

1057
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1058
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1059

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

1062
   const Botan::X509_Certificate user1_cert =
12✔
1063
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1064

1065
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1066
   result.test_eq(
12✔
1067
      "key usage cRLSign not allowed",
1068
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1069
      false);
1070
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1071

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

1075
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1076

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

1079
   const Botan::X509_Certificate mult_usage_cert =
12✔
1080
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1081

1082
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1083
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1084
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1085
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1086
   result.confirm(
24✔
1087
      "key usage multiple digitalSignature and cRLSign allowed",
1088
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1089
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1090

1091
   opts.constraints = Key_Constraints();
12✔
1092

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

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

1098
   // cert allows every usage
1099
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1100
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1101
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1102

1103
   if(sig_algo == "RSA") {
12✔
1104
      // cert allows data encryption
1105
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1106

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

1109
      const Botan::X509_Certificate enc_cert =
1✔
1110
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1111

1112
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1113
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1114
   }
1✔
1115

1116
   return result;
12✔
1117
}
24✔
1118

1119
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
12✔
1120
                              const std::string& sig_algo,
1121
                              const std::string& sig_padding,
1122
                              const std::string& hash_fn,
1123
                              Botan::RandomNumberGenerator& rng) {
1124
   using Botan::Key_Constraints;
12✔
1125

1126
   Test::Result result("X509 Self Issued");
12✔
1127

1128
   // create the self-signed cert
1129
   const Botan::X509_Certificate ca_cert =
12✔
1130
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1131

1132
   /* Create the CA object */
1133
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1134

1135
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1136

1137
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1138
   // but signed by a CA, not signed by it's own private key
1139
   Botan::X509_Cert_Options opts = ca_opts();
12✔
1140
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1141
   opts.set_padding_scheme(sig_padding);
12✔
1142

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

1145
   const Botan::X509_Certificate self_issued_cert =
12✔
1146
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1147

1148
   // check that this chain can can be verified successfully
1149
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
12✔
1150

1151
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1152

1153
   const Botan::Path_Validation_Result validation_result =
12✔
1154
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
12✔
1155

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

1158
   return result;
12✔
1159
}
24✔
1160

1161
Test::Result test_x509_uninit() {
1✔
1162
   Test::Result result("X509 object uninitialized access");
1✔
1163

1164
   Botan::X509_Certificate cert;
1✔
1165
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1166
      cert.x509_version();
1✔
1167
   });
1168

1169
   Botan::X509_CRL crl;
1✔
1170
   result.test_throws(
2✔
1171
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1172

1173
   return result;
1✔
1174
}
1✔
1175

1176
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
19✔
1177
   using Botan::Key_Constraints;
19✔
1178

1179
   Test::Result result("X509 Valid Constraints " + pk_algo);
19✔
1180

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

1183
   // Now check some typical usage scenarios for the given key type
1184
   // Taken from RFC 5280, sec. 4.2.1.3
1185
   // ALL constraints are not typical at all, but we use them for a negative test
1186
   const auto all = Key_Constraints(
19✔
1187
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1188
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1189
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
19✔
1190

1191
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
19✔
1192
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
19✔
1193
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
19✔
1194
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
19✔
1195
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
19✔
1196
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
19✔
1197
   const auto key_agreement_encipher_only =
19✔
1198
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
19✔
1199
   const auto key_agreement_decipher_only =
19✔
1200
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
19✔
1201
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
19✔
1202
   const auto sign_everything =
19✔
1203
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
19✔
1204

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

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

1260
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1261
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1262
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1263
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1264
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1265
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1266
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1267
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1268
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1269
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1270
   }
1271

1272
   return result;
19✔
1273
}
×
1274

1275
/**
1276
 * @brief X.509v3 extension that encodes a given string
1277
 */
1278
class String_Extension final : public Botan::Certificate_Extension {
24✔
1279
   public:
1280
      String_Extension() = default;
24✔
1281

1282
      explicit String_Extension(const std::string& val) : m_contents(val) {}
12✔
1283

1284
      std::string value() const { return m_contents; }
48✔
1285

1286
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1287
         return std::make_unique<String_Extension>(m_contents);
×
1288
      }
1289

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

1292
      bool should_encode() const override { return true; }
24✔
1293

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

1296
      std::vector<uint8_t> encode_inner() const override {
12✔
1297
         std::vector<uint8_t> bits;
12✔
1298
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
12✔
1299
         return bits;
12✔
1300
      }
×
1301

1302
      void decode_inner(const std::vector<uint8_t>& in) override {
24✔
1303
         Botan::ASN1_String str;
24✔
1304
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
24✔
1305
         m_contents = str.value();
24✔
1306
      }
24✔
1307

1308
   private:
1309
      std::string m_contents;
1310
};
1311

1312
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
12✔
1313
                                 const std::string& sig_algo,
1314
                                 const std::string& sig_padding,
1315
                                 const std::string& hash_fn,
1316
                                 Botan::RandomNumberGenerator& rng) {
1317
   Test::Result result("X509 Custom DN");
12✔
1318

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

1322
   /* Create the CA object */
1323
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1324

1325
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1326

1327
   Botan::X509_DN subject_dn;
12✔
1328

1329
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
12✔
1330
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
12✔
1331
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
12✔
1332
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
12✔
1333

1334
   subject_dn.add_attribute(attr1, val1);
12✔
1335
   subject_dn.add_attribute(attr2, val2);
12✔
1336

1337
   Botan::Extensions extensions;
12✔
1338

1339
   Botan::PKCS10_Request req =
12✔
1340
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
12✔
1341

1342
   const Botan::X509_DN& req_dn = req.subject_dn();
12✔
1343

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

1346
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
12✔
1347
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
12✔
1348
   result.confirm("Attr1 matches encoded", req_val1 == val1);
24✔
1349
   result.confirm("Attr2 matches encoded", req_val2 == val2);
24✔
1350
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
24✔
1351
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
24✔
1352

1353
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1354
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1355

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

1358
   const Botan::X509_DN& cert_dn = cert.subject_dn();
12✔
1359

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

1362
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
12✔
1363
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
12✔
1364
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
24✔
1365
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
24✔
1366
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
24✔
1367
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
24✔
1368

1369
   return result;
12✔
1370
}
36✔
1371

1372
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
12✔
1373
                                  const std::string& sig_algo,
1374
                                  const std::string& sig_padding,
1375
                                  const std::string& hash_fn,
1376
                                  Botan::RandomNumberGenerator& rng) {
1377
   using Botan::Key_Constraints;
12✔
1378

1379
   Test::Result result("X509 Extensions");
12✔
1380

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

1384
   /* Create the CA object */
1385
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1386

1387
   /* Prepare CDP extension */
1388
   std::vector<std::string> cdp_urls = {
12✔
1389
      "http://example.com/crl1.pem",
1390
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
12✔
1391

1392
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
12✔
1393

1394
   for(const auto& uri : cdp_urls) {
36✔
1395
      Botan::AlternativeName cdp_alt_name;
24✔
1396
      cdp_alt_name.add_uri(uri);
24✔
1397
      Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
24✔
1398

1399
      dps.emplace_back(dp);
24✔
1400
   }
24✔
1401

1402
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1403

1404
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1405
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1406

1407
   // include a custom extension in the request
1408
   Botan::Extensions req_extensions;
12✔
1409
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
12✔
1410
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
12✔
1411
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
24✔
1412
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
24✔
1413
   opts.extensions = req_extensions;
12✔
1414
   opts.set_padding_scheme(sig_padding);
12✔
1415

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

1419
   result.confirm("Extensions::extension_set true for Key_Usage",
24✔
1420
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
12✔
1421

1422
   // check if known Key_Usage extension is present in self-signed cert
1423
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
12✔
1424
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
24✔
1425
      result.confirm(
24✔
1426
         "Key_Usage extension value matches in self-signed certificate",
1427
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
12✔
1428
   }
1429

1430
   // check if custom extension is present in self-signed cert
1431
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
12✔
1432
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
24✔
1433
      result.test_eq(
48✔
1434
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1435
   }
1436

1437
   // check if CDPs are present in the self-signed cert
1438
   auto cert_cdps =
12✔
1439
      self_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
24✔
1440

1441
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1442
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1443
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1444
         result.confirm("CDP URI present in self-signed certificate",
48✔
1445
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1446
      }
1447
   }
1448

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

1451
   /* Create a CA-signed certificate */
1452
   const Botan::X509_Certificate ca_signed_cert =
12✔
1453
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1454

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

1458
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
24✔
1459
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
24✔
1460
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
12✔
1461
      result.confirm("Key_Usage extension value matches in user certificate",
24✔
1462
                     constraints == Botan::Key_Constraints::DigitalSignature);
12✔
1463
   }
1464

1465
   // check if custom extension is present in CA-signed cert
1466
   result.confirm("Extensions::extension_set true for String_Extension",
24✔
1467
                  ca_signed_cert.v3_extensions().extension_set(oid));
12✔
1468
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
24✔
1469
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
24✔
1470
      result.test_eq(
48✔
1471
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1472
   }
1473

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

1477
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1478
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1479
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1480
         result.confirm("CDP URI present in self-signed certificate",
48✔
1481
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1482
      }
1483
   }
1484

1485
   return result;
12✔
1486
}
60✔
1487

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

1491
   struct TestData {
12✔
1492
         const std::string issuer, subject, issuer_hash, subject_hash;
1493
   } const cases[]{{"",
12✔
1494
                    "",
1495
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1496
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1497
                   {"a",
1498
                    "b",
1499
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1500
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1501
                   {"A",
1502
                    "B",
1503
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1504
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1505
                   {
1506
                      "Test Issuer/US/Botan Project/Testing",
1507
                      "Test Subject/US/Botan Project/Testing",
1508
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1509
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1510
                   },
1511
                   {
1512
                      "Test Subject/US/Botan Project/Testing",
1513
                      "Test Issuer/US/Botan Project/Testing",
1514
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1515
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1516
                   }};
72✔
1517

1518
   for(const auto& a : cases) {
72✔
1519
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1520
      opts.CA_key();
60✔
1521

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

1524
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1525
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1526

1527
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1528
      const Botan::PKCS10_Request req =
60✔
1529
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1530
      const Botan::X509_Certificate subject_cert =
60✔
1531
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1532

1533
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1534
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1535
   }
60✔
1536
   return result;
12✔
1537
}
72✔
1538

1539
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1540

1541
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1542
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1543

1544
      asn1=SEQUENCE:tn_auth_list
1545

1546
      [tn_auth_list]
1547
      spc=EXP:0,IA5:1001
1548
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1549
      one=EXP:2,IA5:333
1550

1551
      [TelephoneNumberRange]
1552
      start1=IA5:111
1553
      count1=INT:128
1554
      start2=IA5:222
1555
      count2=INT:256
1556
    */
1557
   const std::string filename("TNAuthList.pem");
1✔
1558
   Test::Result result("X509 TNAuthList decode");
1✔
1559
   result.start_timer();
1✔
1560

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

1563
   using Botan::Cert_Extension::TNAuthList;
1✔
1564

1565
   auto tn_auth_list = cert.v3_extensions().get_extension_object_as<TNAuthList>();
2✔
1566

1567
   auto& tn_entries = tn_auth_list->entries();
1✔
1568

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

1571
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1572
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1573
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1574
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1575
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1576
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1577

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

1581
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1582
   auto& range = tn_entries[1].telephone_number_range();
1✔
1583
   result.test_eq("range entries count", range.size(), 2);
1✔
1584
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1585
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1586
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1587
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1588

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

1592
   result.end_timer();
1✔
1593
   return result;
2✔
1594
}
1✔
1595

1596
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
1597
   Test::Result result("X509 IP Address Block decode");
1✔
1598
   result.start_timer();
1✔
1599
   const std::string filename("IPAddrBlocksAll.pem");
1✔
1600

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

1603
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1604

1605
   auto ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
1606

1607
   const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
1608
   result.confirm("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr, true);
2✔
1609
   result.confirm("cert has exactly one IpAddrBlock", addr_blocks.size() == 2, true);
2✔
1610

1611
   const auto& ipv4block =
1✔
1612
      std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(addr_blocks[0].addr_choice());
1✔
1613
   const auto& ipv6block =
1✔
1614
      std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(addr_blocks[1].addr_choice());
1✔
1615

1616
   auto& v4_blocks = ipv4block.ranges().value();
1✔
1617

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

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

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

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

1630
   auto& v6_blocks = ipv6block.ranges().value();
1✔
1631

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

1638
   result.confirm("ipv6 block 0 max",
1✔
1639
                  v6_blocks[0].max().value() ==
1✔
1640
                     std::array<uint8_t, 16>{
1✔
1641
                        0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1642
                  true);
1643

1644
   result.confirm("ipv6 block 1 min",
1✔
1645
                  v6_blocks[1].min().value() ==
1✔
1646
                     std::array<uint8_t, 16>{
1✔
1647
                        0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1648
                  true);
1649

1650
   result.confirm("ipv6 block 1 max",
1✔
1651
                  v6_blocks[1].max().value() ==
1✔
1652
                     std::array<uint8_t, 16>{
1✔
1653
                        0xfe, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1654
                  true);
1655

1656
   result.confirm("ipv6 block 2 min",
1✔
1657
                  v6_blocks[2].min().value() ==
1✔
1658
                     std::array<uint8_t, 16>{
1✔
1659
                        0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4},
1660
                  true);
1661

1662
   result.confirm("ipv6 block 2 max",
1✔
1663
                  v6_blocks[2].max().value() ==
1✔
1664
                     std::array<uint8_t, 16>{
1✔
1665
                        0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4},
1666
                  true);
1667

1668
   result.confirm("ipv6 block 3 min",
1✔
1669
                  v6_blocks[3].min().value() ==
1✔
1670
                     std::array<uint8_t, 16>{
1✔
1671
                        0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
1672
                  true);
1673

1674
   result.confirm("ipv6 block 3 max",
1✔
1675
                  v6_blocks[3].max().value() ==
1✔
1676
                     std::array<uint8_t, 16>{
1✔
1677
                        0xcd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
1678
                  true);
1679

1680
   result.end_timer();
1✔
1681
   return result;
2✔
1682
}
1✔
1683

1684
Test::Result test_x509_as_blocks_extension_decode() {
1✔
1685
   Test::Result result("X509 AS Block decode");
1✔
1686
   result.start_timer();
1✔
1687
   using Botan::Cert_Extension::ASBlocks;
1✔
1688

1689
   {
1✔
1690
      const std::string filename("ASNumberCert.pem");
1✔
1691
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1692

1693
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1694

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

1698
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
1699
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
1700

1701
      // cert contains asnum 0-999, 5042, 0-4294967295
1702
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
1703
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
1704

1705
      // and rdi 1234-5678, 32768, 0-4294967295
1706
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1707
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1708
   }
1✔
1709
   {
1✔
1710
      const std::string filename("ASNumberOnly.pem");
1✔
1711
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1712

1713
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1714

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

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

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

1729
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1730

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

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

1737
      // contains 1234-5678, 0-4294967295
1738
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1739
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1740
   }
1✔
1741
   {
1✔
1742
      const std::string filename("ASNumberInherit.pem");
1✔
1743
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1744

1745
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1746

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

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

1753
      // contains 1234-5678, 0-4294967295
1754
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
1755
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
1756
   }
1✔
1757

1758
   result.end_timer();
1✔
1759
   return result;
1✔
1760
}
×
1761

1762
   #endif
1763

1764
Test::Result test_x509_ip_addr_blocks_extension_encode() {
1✔
1765
   Test::Result result("X509 IP Address Block encode");
1✔
1766
   #if defined(BOTAN_HAS_RSA)
1767
   result.start_timer();
1✔
1768

1769
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1770

1771
   auto rng = Test::new_rng(__func__);
1✔
1772

1773
   const std::string sig_algo{"RSA"};
1✔
1774
   const std::string hash_fn{"SHA-256"};
1✔
1775
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
1776

1777
   for(size_t i = 0; i < 64; i++) {
65✔
1778
      bool push_ipv4_ranges = i & 1;
64✔
1779
      bool push_ipv6_ranges = i >> 1 & 1;
64✔
1780
      bool inherit_ipv4 = i >> 2 & 1;
64✔
1781
      bool inherit_ipv6 = i >> 3 & 1;
64✔
1782
      bool push_ipv4_family = i >> 4 & 1;
64✔
1783
      bool push_ipv6_family = i >> 5 & 1;
64✔
1784

1785
      auto ca_key = make_a_private_key(sig_algo, *rng);
64✔
1786
      result.require("CA key", ca_key != nullptr);
64✔
1787
      const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
64✔
1788
      Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
64✔
1789

1790
      auto key = make_a_private_key(sig_algo, *rng);
64✔
1791

1792
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
64✔
1793

1794
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
1795
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1796
      a = {255, 255, 255, 255};
64✔
1797
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1798

1799
      // encoded as min, max
1800
      a = {127, 0, 0, 1};
64✔
1801
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1802
      a = {189, 5, 7, 255};
64✔
1803
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1804

1805
      // encoded as prefix
1806
      a = {190, 5, 0, 0};
64✔
1807
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1808
      a = {190, 5, 127, 255};
64✔
1809
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(a);
64✔
1810

1811
      a = {0xab, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
1812
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1813
      a = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
64✔
1814
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1815

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

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

1823
      // encoded as prefix
1824
      a = {0xbf, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
1825
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1826
      a = {0xbf, 0xcd, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x07, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
64✔
1827
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(a);
64✔
1828

1829
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_1);
64✔
1830
      auto ipv4_range_2 =
64✔
1831
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
1832
      auto ipv4_range_3 =
64✔
1833
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
1834
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(ipv4_2);
64✔
1835

1836
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_1);
64✔
1837
      auto ipv6_range_2 =
64✔
1838
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
1839
      auto ipv6_range_3 =
64✔
1840
         IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
1841
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>(ipv6_2);
64✔
1842

1843
      std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>> ipv4_ranges;
64✔
1844
      if(push_ipv4_ranges) {
64✔
1845
         ipv4_ranges.push_back(ipv4_range_1);
32✔
1846
         ipv4_ranges.push_back(ipv4_range_2);
32✔
1847
         ipv4_ranges.push_back(ipv4_range_3);
32✔
1848
         ipv4_ranges.push_back(ipv4_range_4);
32✔
1849
      }
1850

1851
      std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>> ipv6_ranges;
64✔
1852
      if(push_ipv6_ranges) {
64✔
1853
         ipv6_ranges.push_back(ipv6_range_1);
32✔
1854
         ipv6_ranges.push_back(ipv6_range_2);
32✔
1855
         ipv6_ranges.push_back(ipv6_range_3);
32✔
1856
         ipv6_ranges.push_back(ipv6_range_4);
32✔
1857
      }
1858

1859
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>();
64✔
1860
      if(!inherit_ipv4) {
64✔
1861
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>(ipv4_ranges);
96✔
1862
      }
1863

1864
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>();
64✔
1865
      if(!inherit_ipv6) {
64✔
1866
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>(ipv6_ranges);
96✔
1867
      }
1868

1869
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
1870
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
1871

1872
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
1873
      if(push_ipv4_family) {
64✔
1874
         addr_blocks.push_back(ipv4_addr_family);
32✔
1875
      }
1876
      if(push_ipv6_family) {
64✔
1877
         addr_blocks.push_back(ipv6_addr_family);
32✔
1878
      }
1879

1880
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
1881

1882
      opts1.extensions.add(std::move(blocks));
64✔
1883

1884
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
64✔
1885
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
1886
      {
64✔
1887
         auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
128✔
1888
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
1889

1890
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
1891
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
1892
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
1893
            continue;
16✔
1894
         }
1895

1896
         if(push_ipv4_family) {
48✔
1897
            auto family = dec_addr_blocks[0];
32✔
1898
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
1899
            result.confirm("ipv4 family safi", ipv4_addr_family.safi() == family.safi(), true);
96✔
1900
            auto choice =
32✔
1901
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(family.addr_choice());
32✔
1902

1903
            if(!inherit_ipv4) {
32✔
1904
               auto ranges = choice.ranges().value();
16✔
1905
               if(push_ipv4_ranges) {
16✔
1906
                  result.confirm("ipv4 entry 0 min", ranges[0].min().value() == ipv4_range_1.min().value(), true);
16✔
1907
                  result.confirm("ipv4 entry 0 max", ranges[0].max().value() == ipv4_range_1.max().value(), true);
16✔
1908
                  result.confirm("ipv4 entry 1 min", ranges[1].min().value() == ipv4_range_2.min().value(), true);
16✔
1909
                  result.confirm("ipv4 entry 1 max", ranges[1].max().value() == ipv4_range_2.max().value(), true);
16✔
1910
                  result.confirm("ipv4 entry 2 min", ranges[2].min().value() == ipv4_range_3.min().value(), true);
16✔
1911
                  result.confirm("ipv4 entry 2 max", ranges[2].max().value() == ipv4_range_3.max().value(), true);
16✔
1912
                  result.confirm("ipv4 entry 3 min", ranges[3].min().value() == ipv4_range_4.min().value(), true);
16✔
1913
                  result.confirm("ipv4 entry 3 max", ranges[3].max().value() == ipv4_range_4.max().value(), true);
16✔
1914
               } else {
1915
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
1916
               }
1917
            } else {
16✔
1918
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
1919
            }
1920
         }
64✔
1921

1922
         if(push_ipv6_family) {
48✔
1923
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
1924
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
1925
            result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
96✔
1926
            auto choice =
32✔
1927
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(family.addr_choice());
32✔
1928
            if(!inherit_ipv6) {
32✔
1929
               auto ranges = choice.ranges().value();
16✔
1930
               if(push_ipv6_ranges) {
16✔
1931
                  result.confirm("ipv6 entry 0 min", ranges[0].min().value() == ipv6_range_1.min().value(), true);
16✔
1932
                  result.confirm("ipv6 entry 0 max", ranges[0].max().value() == ipv6_range_1.max().value(), true);
16✔
1933
                  result.confirm("ipv6 entry 1 min", ranges[1].min().value() == ipv6_range_2.min().value(), true);
16✔
1934
                  result.confirm("ipv6 entry 1 max", ranges[1].max().value() == ipv6_range_2.max().value(), true);
16✔
1935
                  result.confirm("ipv6 entry 2 min", ranges[2].min().value() == ipv6_range_3.min().value(), true);
16✔
1936
                  result.confirm("ipv6 entry 2 max", ranges[2].max().value() == ipv6_range_3.max().value(), true);
16✔
1937
                  result.confirm("ipv6 entry 3 min", ranges[3].min().value() == ipv6_range_4.min().value(), true);
16✔
1938
                  result.confirm("ipv6 entry 3 max", ranges[3].max().value() == ipv6_range_4.max().value(), true);
16✔
1939
               } else {
1940
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
1941
               }
1942
            } else {
16✔
1943
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
1944
            }
1945
         }
64✔
1946
      }
1947
   }
448✔
1948

1949
   result.end_timer();
1✔
1950
   #endif
1951
   return result;
1✔
1952
}
2✔
1953

1954
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases() {
1✔
1955
   Test::Result result("X509 IP Address Block encode edge cases");
1✔
1956
   #if defined(BOTAN_HAS_RSA)
1957
   result.start_timer();
1✔
1958

1959
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1960

1961
   auto rng = Test::new_rng(__func__);
1✔
1962

1963
   const std::string sig_algo{"RSA"};
1✔
1964
   const std::string hash_fn{"SHA-256"};
1✔
1965
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
1966

1967
   // trailing 0s, trailing 1s, and some arbitrary values
1968
   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✔
1969

1970
   for(size_t i = 0; i < edge_values.size(); i++) {
21✔
1971
      for(size_t j = 0; j < 18; j++) {
380✔
1972
         auto ca_key = make_a_private_key(sig_algo, *rng);
360✔
1973
         result.require("CA key", ca_key != nullptr);
360✔
1974
         const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
360✔
1975
         Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
360✔
1976

1977
         auto key = make_a_private_key(sig_algo, *rng);
360✔
1978

1979
         Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
360✔
1980

1981
         std::vector<uint8_t> min_bytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
360✔
1982
         std::vector<uint8_t> max_bytes = {
360✔
1983
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
360✔
1984

1985
         min_bytes[15 - (j < 2 ? 0 : j - 2)] = edge_values[i];
360✔
1986
         max_bytes[15 - (j > 15 ? 15 : j)] = edge_values[i];
360✔
1987

1988
         auto address_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(min_bytes);
360✔
1989
         auto address_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(max_bytes);
360✔
1990

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

1993
         std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>> ipv6_ranges;
360✔
1994
         ipv6_ranges.push_back(ipv6_range);
360✔
1995

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

1998
         auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
1,080✔
1999

2000
         std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
360✔
2001
         addr_blocks.push_back(ipv6_addr_family);
360✔
2002

2003
         std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
360✔
2004

2005
         opts1.extensions.add(std::move(blocks));
360✔
2006

2007
         Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
360✔
2008
         Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
360✔
2009
         {
360✔
2010
            auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
720✔
2011
            result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
720✔
2012
            const auto& dec_addr_blocks = ip_blocks->addr_blocks();
360✔
2013
            auto family = dec_addr_blocks[0];
360✔
2014
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
720✔
2015
            result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
1,080✔
2016
            auto choice =
360✔
2017
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(family.addr_choice());
360✔
2018
            auto ranges = choice.ranges().value();
360✔
2019

2020
            result.confirm("ipv6 edge case min", ranges[0].min().value() == ipv6_range.min().value(), true);
720✔
2021
            result.confirm("ipv6 edge case max", ranges[0].max().value() == ipv6_range.max().value(), true);
720✔
2022
         }
720✔
2023
      }
2,520✔
2024
   }
2025
   result.end_timer();
1✔
2026
   #endif
2027
   return result;
1✔
2028
}
2✔
2029

2030
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
2031
   Test::Result result("X509 IP Address Block range merge");
1✔
2032
   #if defined(BOTAN_HAS_RSA)
2033
   result.start_timer();
1✔
2034

2035
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
2036

2037
   auto rng = Test::new_rng(__func__);
1✔
2038

2039
   const std::string sig_algo{"RSA"};
1✔
2040
   const std::string hash_fn{"SHA-256"};
1✔
2041
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2042

2043
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2044
   result.require("CA key", ca_key != nullptr);
1✔
2045
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2046
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2047

2048
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2049

2050
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2051

2052
   std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
2053
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
2054
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
2055
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
2056
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
2057
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
2058
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
2059
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
2060
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
2061
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
2062
   };
46✔
2063

2064
   std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>> ipv6_ranges;
1✔
2065
   for(auto pair : addresses) {
10✔
2066
      auto address_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(pair[0]);
9✔
2067
      auto address_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>(pair[1]);
9✔
2068
      auto range = IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>(address_min, address_max);
9✔
2069
      ipv6_ranges.push_back(range);
9✔
2070
   }
9✔
2071

2072
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>(ipv6_ranges);
1✔
2073
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
2074

2075
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
2076
   addr_blocks.push_back(ipv6_addr_family);
1✔
2077

2078
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
2079

2080
   opts1.extensions.add(std::move(blocks));
1✔
2081

2082
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
2083
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2084
   {
1✔
2085
      auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
2086
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
2087
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
2088
      auto family = dec_addr_blocks[0];
1✔
2089
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(family.addr_choice());
1✔
2090
      auto ranges = choice.ranges().value();
1✔
2091

2092
      std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
2093
      std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
2094

2095
      result.confirm("range expected min", ranges[0].min().value() == expected_min, true);
2✔
2096
      result.confirm("range expected max", ranges[0].max().value() == expected_max, true);
2✔
2097
      result.confirm("range length", ranges.size() == 1, true);
2✔
2098
   }
2✔
2099

2100
   result.end_timer();
1✔
2101
   #endif
2102
   return result;
2✔
2103
}
26✔
2104

2105
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
2106
   Test::Result result("X509 IP Address Block family merge");
1✔
2107
   #if defined(BOTAN_HAS_RSA)
2108
   result.start_timer();
1✔
2109

2110
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
2111

2112
   auto rng = Test::new_rng(__func__);
1✔
2113

2114
   const std::string sig_algo{"RSA"};
1✔
2115
   const std::string hash_fn{"SHA-256"};
1✔
2116
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2117

2118
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2119
   result.require("CA key", ca_key != nullptr);
1✔
2120
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2121
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2122

2123
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2124

2125
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2126

2127
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
2128

2129
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4> v4_empty_choice;
1✔
2130
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6> v6_empty_choice;
1✔
2131

2132
   uint8_t v4_bytes_1[4] = {123, 123, 123, 123};
1✔
2133
   IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4> v4_addr_1(v4_bytes_1);
1✔
2134
   // create 2 prefixes from the v4 addresses -> they should be merged
2135
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4> v4_choice_dupl({{v4_addr_1}, {v4_addr_1}});
1✔
2136
   result.confirm(
2✔
2137
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
2138
   IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
2139

2140
   uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
2141
   IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6> v6_addr_1(v6_bytes_1);
1✔
2142
   IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6> v6_choice_dupl({{v6_addr_1}, {v6_addr_1}});
1✔
2143
   result.confirm(
2✔
2144
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
2145
   IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
2146

2147
   IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
2148
   IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
2149

2150
   IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
2151
   IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
2152

2153
   /*
2154
   considering the push order, the resulting order should be
2155
   [0] v4 no safi
2156
   [1] v6 no safi
2157
   [2] v4 safi
2158
   [3] v6 safi
2159
   */
2160
   for(size_t i = 0; i < 3; i++) {
4✔
2161
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
2162
      addr_blocks.push_back(v6_empty_fam);
3✔
2163
      addr_blocks.push_back(v4_fam_dupl);
3✔
2164
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
2165
      addr_blocks.push_back(v6_fam_dupl);
3✔
2166
      addr_blocks.push_back(v4_empty_fam);
3✔
2167
   }
2168

2169
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
2170
      v4_empty_fam, v6_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_fam_dupl, v6_empty_fam_safi};
7✔
2171

2172
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
2173

2174
   opts1.extensions.add(std::move(blocks));
1✔
2175

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

2179
   auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
2✔
2180
   result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
2181
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
2182

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

2185
   bool sorted = true;
1✔
2186
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
2187
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
2188
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
2189

2190
      uint32_t afam_a = a.afi();
5✔
2191
      if(a.safi().has_value()) {
5✔
2192
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
2193
      }
2194

2195
      uint32_t afam_b = b.afi();
5✔
2196
      if(b.safi().has_value()) {
5✔
2197
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
2198
      }
2199

2200
      if(afam_a > afam_b) {
5✔
2201
         sorted = false;
2202
         break;
2203
      }
2204
   }
2205

2206
   result.confirm("blocks got sorted", sorted, true);
2✔
2207

2208
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
2209
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
2210
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
2211

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

2215
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
2216
         auto dec_choice =
3✔
2217
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(dec.addr_choice());
3✔
2218
         auto exp_choice =
3✔
2219
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(exp.addr_choice());
3✔
2220

2221
         if(!exp_choice.ranges().has_value()) {
3✔
2222
            result.confirm(
2✔
2223
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
2224
         } else {
2225
            result.confirm(
1✔
2226
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
2227

2228
            if(dec_choice.ranges().has_value() == false) {
1✔
2229
               continue;
×
2230
            }
2231

2232
            auto dec_ranges = dec_choice.ranges().value();
1✔
2233
            auto exp_ranges = exp_choice.ranges().value();
1✔
2234
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
2235
                           dec_ranges.size() == exp_ranges.size(),
1✔
2236
                           true);
2237

2238
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
2239
               continue;
×
2240
            }
2241

2242
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
2243
               result.confirm(
2✔
2244
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2245
                  exp_ranges[j].min() == dec_ranges[j].min(),
1✔
2246
                  true);
2247
               result.confirm(
2✔
2248
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2249
                  exp_ranges[j].max() == dec_ranges[j].max(),
2✔
2250
                  true);
2251
            }
2252
         }
1✔
2253
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
2254
         auto dec_choice =
3✔
2255
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(dec.addr_choice());
3✔
2256
         auto exp_choice =
3✔
2257
            std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>>(exp.addr_choice());
3✔
2258

2259
         if(!exp_choice.ranges().has_value()) {
3✔
2260
            result.confirm(
2✔
2261
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
2262
         } else {
2263
            result.confirm(
1✔
2264
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
2265

2266
            if(dec_choice.ranges().has_value() == false) {
1✔
2267
               continue;
×
2268
            }
2269

2270
            auto dec_ranges = dec_choice.ranges().value();
1✔
2271
            auto exp_ranges = exp_choice.ranges().value();
1✔
2272
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
2273
                           dec_ranges.size() == exp_ranges.size(),
1✔
2274
                           true);
2275

2276
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
2277
               continue;
×
2278
            }
2279

2280
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
2281
               result.confirm(
2✔
2282
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2283
                  exp_ranges[j].min() == dec_ranges[j].min(),
1✔
2284
                  true);
2285
               result.confirm(
2✔
2286
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
2287
                  exp_ranges[j].max() == dec_ranges[j].max(),
2✔
2288
                  true);
2289
            }
2290
         }
1✔
2291
      }
4✔
2292
   }
2293

2294
   result.end_timer();
1✔
2295
   #endif
2296
   return result;
2✔
2297
}
21✔
2298

2299
Test::Result test_x509_as_blocks_extension_encode() {
1✔
2300
   Test::Result result("X509 AS Number encode");
1✔
2301
   #if defined(BOTAN_HAS_RSA)
2302
   result.start_timer();
1✔
2303

2304
   using Botan::Cert_Extension::ASBlocks;
1✔
2305

2306
   auto rng = Test::new_rng(__func__);
1✔
2307

2308
   const std::string sig_algo{"RSA"};
1✔
2309
   const std::string hash_fn{"SHA-256"};
1✔
2310
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2311

2312
   for(size_t i = 0; i < 16; i++) {
17✔
2313
      bool push_asnum = i & 1;
16✔
2314
      bool push_rdi = i >> 1 & 1;
16✔
2315
      bool include_asnum = i >> 2 & 1;
16✔
2316
      bool include_rdi = i >> 3 & 1;
16✔
2317

2318
      ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
16✔
2319

2320
      ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
16✔
2321

2322
      ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
16✔
2323

2324
      ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
16✔
2325

2326
      ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
16✔
2327

2328
      ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
16✔
2329

2330
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
16✔
2331
      if(push_asnum) {
16✔
2332
         as_ranges.push_back(asnum_id_or_range0);
8✔
2333
         as_ranges.push_back(asnum_id_or_range1);
8✔
2334
         as_ranges.push_back(asnum_id_or_range2);
8✔
2335
      }
2336

2337
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
16✔
2338
      if(push_rdi) {
16✔
2339
         rdi_ranges.push_back(rdi_id_or_range0);
8✔
2340
         rdi_ranges.push_back(rdi_id_or_range1);
8✔
2341
         rdi_ranges.push_back(rdi_id_or_range2);
8✔
2342
      }
2343

2344
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
16✔
2345
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
16✔
2346

2347
      ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
40✔
2348
                                                              include_rdi ? std::optional(rdi) : std::nullopt);
40✔
2349

2350
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
16✔
2351

2352
      auto ca_key = make_a_private_key(sig_algo, *rng);
16✔
2353
      result.require("CA key", ca_key != nullptr);
16✔
2354
      const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
16✔
2355
      Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
16✔
2356
      auto key = make_a_private_key(sig_algo, *rng);
16✔
2357

2358
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
16✔
2359
      opts1.extensions.add(std::move(blocks));
16✔
2360

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

2364
      {
16✔
2365
         auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
32✔
2366
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
32✔
2367

2368
         const auto& identifier = as_blocks->as_identifiers();
16✔
2369

2370
         if(include_asnum) {
16✔
2371
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
2372

2373
            if(push_asnum) {
8✔
2374
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
2375
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
2376

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

2386
         if(include_rdi) {
16✔
2387
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
2388

2389
            if(push_rdi) {
8✔
2390
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
2391
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
2392

2393
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
2394
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
2395
            } else {
2396
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
2397
            }
2398
         } else {
2399
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
16✔
2400
         }
2401
      }
2402
   }
80✔
2403

2404
   result.end_timer();
1✔
2405
   #endif
2406
   return result;
1✔
2407
}
2✔
2408

2409
Test::Result test_x509_ip_as_blocks_range_merge() {
1✔
2410
   Test::Result result("X509 IP Address Block range merge");
1✔
2411
   #if defined(BOTAN_HAS_RSA)
2412
   result.start_timer();
1✔
2413

2414
   using Botan::Cert_Extension::ASBlocks;
1✔
2415

2416
   auto rng = Test::new_rng(__func__);
1✔
2417

2418
   const std::string sig_algo{"RSA"};
1✔
2419
   const std::string hash_fn{"SHA-256"};
1✔
2420
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2421

2422
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
2423
   result.require("CA key", ca_key != nullptr);
1✔
2424
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
2425
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
2426

2427
   auto key = make_a_private_key(sig_algo, *rng);
1✔
2428

2429
   Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
1✔
2430

2431
   std::vector<std::vector<uint16_t>> ranges = {
1✔
2432
      {2005, 37005},
2433
      {60, 70},
2434
      {22, 50},
2435
      {35, 2000},
2436
      {2001, 2004},
2437
      {21, 21},
2438
      {0, 20},
2439
   };
9✔
2440

2441
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
2442
   for(auto pair : ranges) {
8✔
2443
      auto range = ASBlocks::ASIdOrRange(pair[0], pair[1]);
7✔
2444
      as_ranges.push_back(range);
7✔
2445
   }
7✔
2446

2447
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
2448

2449
   ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(std::optional(asnum), std::nullopt);
3✔
2450

2451
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
2452

2453
   opts1.extensions.add(std::move(blocks));
1✔
2454

2455
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
1✔
2456
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2457
   {
1✔
2458
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
2459
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
2460

2461
      const auto& identifier = as_blocks->as_identifiers();
1✔
2462

2463
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
2464

2465
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
2466
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
2467
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
2468
   }
2469

2470
   result.end_timer();
1✔
2471
   #endif
2472
   return result;
2✔
2473
}
7✔
2474

2475
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
2476
   if(sig_algo == "RSA") {
12✔
2477
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
5✔
2478
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
2479
             sig_algo == "GOST-34.10") {
7✔
2480
      return {hash};
10✔
2481
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
2482
      return {"Pure"};
2✔
2483
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
2484
      return {"Randomized"};
2✔
2485
   } else if(sig_algo == "HSS-LMS") {
2✔
2486
      return {""};
1✔
2487
   } else {
2488
      return {};
1✔
2489
   }
2490
}
7✔
2491

2492
class X509_Cert_Unit_Tests final : public Test {
×
2493
   public:
2494
      std::vector<Test::Result> run() override {
1✔
2495
         std::vector<Test::Result> results;
1✔
2496

2497
         auto& rng = this->rng();
1✔
2498

2499
         const std::string sig_algos[]{"RSA",
1✔
2500
                                       "DSA",
2501
                                       "ECDSA",
2502
                                       "ECGDSA",
2503
                                       "ECKCDSA",
2504
                                       "GOST-34.10",
2505
                                       "Ed25519",
2506
                                       "Ed448",
2507
                                       "Dilithium",
2508
                                       "ML-DSA",
2509
                                       "SLH-DSA",
2510
                                       "HSS-LMS"};
13✔
2511

2512
         for(const std::string& algo : sig_algos) {
13✔
2513
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
2514
            if(algo == "RSA")
2515
               continue;
2516
   #endif
2517

2518
            std::string hash = "SHA-256";
12✔
2519

2520
            if(algo == "Ed25519") {
12✔
2521
               hash = "SHA-512";
1✔
2522
            }
2523
            if(algo == "Ed448") {
12✔
2524
               hash = "SHAKE-256(912)";
1✔
2525
            }
2526
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
2527
               hash = "SHAKE-256(512)";
2✔
2528
            }
2529

2530
            auto key = make_a_private_key(algo, rng);
12✔
2531

2532
            if(key == nullptr) {
12✔
2533
               continue;
×
2534
            }
2535

2536
            results.push_back(test_hashes(*key, hash, rng));
24✔
2537
            results.push_back(test_valid_constraints(*key, algo));
24✔
2538

2539
            Test::Result usage_result("X509 Usage");
12✔
2540
            try {
12✔
2541
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
2542
            } catch(std::exception& e) {
×
2543
               usage_result.test_failure("test_usage " + algo, e.what());
×
2544
            }
×
2545
            results.push_back(usage_result);
12✔
2546

2547
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
24✔
2548
               Test::Result cert_result("X509 Unit");
12✔
2549

2550
               try {
12✔
2551
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
12✔
2552
               } catch(std::exception& e) {
×
2553
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
2554
               }
×
2555
               results.push_back(cert_result);
12✔
2556

2557
               Test::Result pkcs10_result("PKCS10 extensions");
12✔
2558
               try {
12✔
2559
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
12✔
2560
               } catch(std::exception& e) {
×
2561
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
2562
               }
×
2563
               results.push_back(pkcs10_result);
12✔
2564

2565
               Test::Result self_issued_result("X509 Self Issued");
12✔
2566
               try {
12✔
2567
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
12✔
2568
               } catch(std::exception& e) {
×
2569
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
2570
               }
×
2571
               results.push_back(self_issued_result);
12✔
2572

2573
               Test::Result extensions_result("X509 Extensions");
12✔
2574
               try {
12✔
2575
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
12✔
2576
               } catch(std::exception& e) {
×
2577
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
2578
               }
×
2579
               results.push_back(extensions_result);
12✔
2580

2581
               Test::Result custom_dn_result("X509 Custom DN");
12✔
2582
               try {
12✔
2583
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
12✔
2584
               } catch(std::exception& e) {
×
2585
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
2586
               }
×
2587
               results.push_back(custom_dn_result);
12✔
2588
            }
24✔
2589
         }
24✔
2590

2591
         /*
2592
         These are algos which cannot sign but can be included in certs
2593
         */
2594
         const std::vector<std::string> enc_algos = {
1✔
2595
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
2596

2597
         for(const std::string& algo : enc_algos) {
8✔
2598
            auto key = make_a_private_key(algo, rng);
7✔
2599

2600
            if(key) {
7✔
2601
               results.push_back(test_valid_constraints(*key, algo));
14✔
2602
            }
2603
         }
7✔
2604

2605
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
2606
      defined(BOTAN_HAS_RSA)
2607
         Test::Result pad_config_result("X509 Padding Config");
1✔
2608
         try {
1✔
2609
            pad_config_result.merge(test_padding_config());
1✔
2610
         } catch(const std::exception& e) {
×
2611
            pad_config_result.test_failure("test_padding_config", e.what());
×
2612
         }
×
2613
         results.push_back(pad_config_result);
1✔
2614
   #endif
2615

2616
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2617
         results.push_back(test_x509_utf8());
2✔
2618
         results.push_back(test_x509_bmpstring());
2✔
2619
         results.push_back(test_x509_teletex());
2✔
2620
         results.push_back(test_crl_dn_name());
2✔
2621
         results.push_back(test_rdn_multielement_set_name());
2✔
2622
         results.push_back(test_x509_decode_list());
2✔
2623
         results.push_back(test_rsa_oaep());
2✔
2624
         results.push_back(test_x509_authority_info_access_extension());
2✔
2625
         results.push_back(test_verify_gost2012_cert());
2✔
2626
         results.push_back(test_parse_rsa_pss_cert());
2✔
2627
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
2628
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2629
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2630
   #endif
2631

2632
         results.push_back(test_x509_ip_addr_blocks_extension_encode());
2✔
2633
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases());
2✔
2634
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
2635
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
2636
         results.push_back(test_x509_as_blocks_extension_encode());
2✔
2637
         results.push_back(test_x509_ip_as_blocks_range_merge());
2✔
2638
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
2639
         results.push_back(test_x509_extension());
2✔
2640
         results.push_back(test_x509_dates());
2✔
2641
         results.push_back(test_cert_status_strings());
2✔
2642
         results.push_back(test_x509_uninit());
2✔
2643

2644
         return results;
1✔
2645
      }
13✔
2646
};
2647

2648
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
2649

2650
#endif
2651

2652
}  // namespace
2653

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