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

randombit / botan / 12931924536

23 Jan 2025 03:00PM UTC coverage: 91.249% (+0.05%) from 91.202%
12931924536

push

github

arckoor
Add IPAddrBlock and ASIdentifiers extensions (RFC 3779)

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

94260 of 103300 relevant lines covered (91.25%)

11700393.77 hits per line

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

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

9
#include "tests.h"
10

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

24
namespace Botan_Tests {
25

26
namespace {
27

28
#if defined(BOTAN_HAS_X509_CERTIFICATES)
29

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

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

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

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

46
   opts.CA_key(1);
561✔
47

48
   return opts;
561✔
49
}
×
50

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

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

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

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

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

70
   return opts;
479✔
71
}
×
72

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

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

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

83
   return opts;
12✔
84
}
×
85

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

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

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

98
   return opts;
60✔
99
}
×
100

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

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

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

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

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

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

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

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

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

190
   return result;
1✔
191
}
1✔
192

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

196
   Botan::Extensions extn;
1✔
197

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

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

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

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

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

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

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

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

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

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

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

231
   return result;
2✔
232
}
2✔
233

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

406
   return result;
1✔
407
}
80✔
408

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

478
   return result;
1✔
479
}
4✔
480

481
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
482

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

486
      // See GH #1252
487

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

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

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

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

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

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

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

506
   return result;
2✔
507
}
3✔
508

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

512
   // GH #2611
513

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

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

519
   return result;
1✔
520
}
1✔
521

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

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

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

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

535
   return result;
2✔
536
}
1✔
537

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

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

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

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

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

552
   return result;
1✔
553
}
1✔
554

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

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

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

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

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

583
   return result;
1✔
584
}
×
585

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

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

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

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

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

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

609
   return result;
1✔
610
}
×
611

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

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

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

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

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

628
   return result;
1✔
629
}
×
630

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

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

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

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

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

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

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

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

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

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

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

672
   return result;
1✔
673
}
1✔
674

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

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

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

687
   return result;
1✔
688
}
×
689

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

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

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

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

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

711
   return result;
1✔
712
}
×
713

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

821
   #endif
822

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

829
   Botan::X509_Cert_Options opts;
12✔
830

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

835
   opts.padding_scheme = sig_padding;
12✔
836

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

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

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

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

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

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

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

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

863
   return result;
12✔
864
}
12✔
865

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

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

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

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

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

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

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

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

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

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

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

902
   const BigInt user1_serial = 99;
12✔
903

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

998
   store.add_crl(crl2);
12✔
999

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

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

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

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

1013
   store.add_crl(crl3);
12✔
1014

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

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

1023
   return result;
12✔
1024
}
60✔
1025

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1102
   return result;
12✔
1103
}
24✔
1104

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

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

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

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

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

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

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

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

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

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

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

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

1144
   return result;
12✔
1145
}
24✔
1146

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

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

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

1159
   return result;
1✔
1160
}
1✔
1161

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

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

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

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

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

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

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

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

1258
   return result;
19✔
1259
}
×
1260

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1313
   Botan::X509_DN subject_dn;
12✔
1314

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

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

1323
   Botan::Extensions extensions;
12✔
1324

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

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

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

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

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

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

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

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

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

1355
   return result;
12✔
1356
}
36✔
1357

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1471
   return result;
12✔
1472
}
60✔
1473

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

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

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

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

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

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

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

1525
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1526

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

1530
      asn1=SEQUENCE:tn_auth_list
1531

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1687
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
1688
      result.confirm("asnum entry 0 max", asnum[0].max() == 999, true);
2✔
1689

1690
      result.confirm("asnum entry 1 min", asnum[1].min() == 5042, true);
2✔
1691
      result.confirm("asnum entry 1 max", asnum[1].max() == 5042, true);
2✔
1692

1693
      result.confirm("asnum entry 2 min", asnum[2].min() == 0, true);
2✔
1694
      result.confirm("asnum entry 2 max", asnum[2].max() == 4294967295, true);
2✔
1695

1696
      result.confirm("rdi entry 0 min", rdi[0].min() == 1234, true);
2✔
1697
      result.confirm("rdi entry 0 max", rdi[0].max() == 5678, true);
2✔
1698

1699
      result.confirm("rdi entry 1 min", rdi[1].min() == 32768, true);
2✔
1700
      result.confirm("rdi entry 1 max", rdi[1].max() == 32768, true);
2✔
1701

1702
      result.confirm("rdi entry 2 min", rdi[2].min() == 0, true);
2✔
1703
      result.confirm("rdi entry 2 max", rdi[2].max() == 4294967295, true);
2✔
1704
   }
1✔
1705
   {
1✔
1706
      const std::string filename("ASNumberOnly.pem");
1✔
1707
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
1708

1709
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1710

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

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

1717
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
1718
      result.confirm("asnum entry 0 max", asnum[0].max() == 999, true);
2✔
1719

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

1727
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
2✔
1728

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

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

1735
      result.confirm("rdi entry 0 min", rdi[0].min() == 1234, true);
2✔
1736
      result.confirm("rdi entry 0 max", rdi[0].max() == 5678, true);
2✔
1737

1738
      result.confirm("rdi entry 1 min", rdi[1].min() == 0, true);
2✔
1739
      result.confirm("rdi entry 1 max", rdi[1].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
      result.confirm("rdi entry 0 min", rdi[0].min() == 1234, true);
2✔
1754
      result.confirm("rdi entry 0 max", rdi[0].max() == 5678, true);
2✔
1755

1756
      result.confirm("rdi entry 1 min", rdi[1].min() == 0, true);
2✔
1757
      result.confirm("rdi entry 1 max", rdi[1].max() == 4294967295, true);
2✔
1758
   }
1✔
1759

1760
   result.end_timer();
1✔
1761
   return result;
1✔
1762
}
×
1763

1764
   #endif
1765

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

1771
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1772

1773
   auto rng = Test::new_rng(__func__);
1✔
1774

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

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

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

1792
      auto key = make_a_private_key(sig_algo, *rng);
64✔
1793

1794
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
64✔
1795

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

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

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

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

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

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

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

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

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

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

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

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

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

1871
      std::vector<uint8_t> afi = {0, 1};
64✔
1872
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(std::span(afi), ipv4_addr_choice);
96✔
1873

1874
      afi = {0, 2};
64✔
1875
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(std::span(afi), ipv6_addr_choice);
96✔
1876

1877
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
1878
      if(push_ipv4_family) {
64✔
1879
         addr_blocks.push_back(ipv4_addr_family);
32✔
1880
      }
1881
      if(push_ipv6_family) {
64✔
1882
         addr_blocks.push_back(ipv6_addr_family);
32✔
1883
      }
1884

1885
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
64✔
1886
      blocks->set_addr_blocks(addr_blocks);
64✔
1887

1888
      opts1.extensions.add(std::move(blocks));
64✔
1889

1890
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng);
64✔
1891
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
1892
      {
64✔
1893
         auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
128✔
1894
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
1895

1896
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
1897
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
1898
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
1899
            continue;
16✔
1900
         }
1901

1902
         if(push_ipv4_family) {
48✔
1903
            auto family = dec_addr_blocks[0];
32✔
1904
            result.confirm("ipv4 family afi", ipv4_addr_family.addr_family() == family.addr_family(), true);
64✔
1905
            auto choice =
32✔
1906
               std::get<IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>>(family.addr_choice());
32✔
1907

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

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

1953
   result.end_timer();
1✔
1954
   #endif
1955
   return result;
1✔
1956
}
2✔
1957

1958
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases() {
1✔
1959
   Test::Result result("X509 IP Address Block encode edge cases");
1✔
1960
   #if defined(BOTAN_HAS_RSA)
1961
   result.start_timer();
1✔
1962

1963
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1964

1965
   auto rng = Test::new_rng(__func__);
1✔
1966

1967
   const std::string sig_algo{"RSA"};
1✔
1968
   const std::string hash_fn{"SHA-256"};
1✔
1969
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
1970

1971
   // trailing 0s, trailing 1s, and some arbitrary values
1972
   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✔
1973

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

1981
         auto key = make_a_private_key(sig_algo, *rng);
360✔
1982

1983
         Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
360✔
1984

1985
         std::vector<uint8_t> min_bytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
360✔
1986
         std::vector<uint8_t> max_bytes = {
360✔
1987
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
360✔
1988

1989
         min_bytes[15 - (j < 2 ? 0 : j - 2)] = edge_values[i];
360✔
1990
         max_bytes[15 - (j > 15 ? 15 : j)] = edge_values[i];
360✔
1991

1992
         auto address_min = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(min_bytes);
360✔
1993
         auto address_max = IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>(max_bytes);
360✔
1994

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

1997
         std::vector<IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>> ipv6_ranges;
360✔
1998
         ipv6_ranges.push_back(ipv6_range);
360✔
1999

2000
         auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>();
360✔
2001
         ipv6_addr_choice.set_range(ipv6_ranges);
720✔
2002

2003
         std::vector<uint8_t> afi = {0, 2};
360✔
2004
         auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(afi, ipv6_addr_choice);
720✔
2005

2006
         std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
360✔
2007
         addr_blocks.push_back(ipv6_addr_family);
360✔
2008

2009
         std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
360✔
2010
         blocks->set_addr_blocks(addr_blocks);
360✔
2011

2012
         opts1.extensions.add(std::move(blocks));
360✔
2013

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

2026
            result.confirm("ipv6 edge case min", ranges[0].min().value() == ipv6_range.min().value(), true);
720✔
2027
            result.confirm("ipv6 edge case max", ranges[0].max().value() == ipv6_range.max().value(), true);
720✔
2028
         }
720✔
2029
      }
2,520✔
2030
   }
2031
   result.end_timer();
1✔
2032
   #endif
2033
   return result;
1✔
2034
}
2✔
2035

2036
Test::Result test_x509_as_blocks_extension_encode() {
1✔
2037
   Test::Result result("X509 AS Number encode");
1✔
2038
   #if defined(BOTAN_HAS_RSA)
2039
   result.start_timer();
1✔
2040

2041
   using Botan::Cert_Extension::ASBlocks;
1✔
2042

2043
   auto rng = Test::new_rng(__func__);
1✔
2044

2045
   const std::string sig_algo{"RSA"};
1✔
2046
   const std::string hash_fn{"SHA-256"};
1✔
2047
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
2048

2049
   for(size_t i = 0; i < 16; i++) {
17✔
2050
      bool push_asnum = i & 1;
16✔
2051
      bool push_rdi = i >> 1 & 1;
16✔
2052
      bool include_asnum = i >> 2 & 1;
16✔
2053
      bool include_rdi = i >> 3 & 1;
16✔
2054

2055
      ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
16✔
2056

2057
      ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
16✔
2058

2059
      ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
16✔
2060

2061
      ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
16✔
2062

2063
      ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
16✔
2064

2065
      ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
16✔
2066

2067
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
16✔
2068
      if(push_asnum) {
16✔
2069
         as_ranges.push_back(asnum_id_or_range0);
8✔
2070
         as_ranges.push_back(asnum_id_or_range1);
8✔
2071
         as_ranges.push_back(asnum_id_or_range2);
8✔
2072
      }
2073

2074
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
16✔
2075
      if(push_rdi) {
16✔
2076
         rdi_ranges.push_back(rdi_id_or_range0);
8✔
2077
         rdi_ranges.push_back(rdi_id_or_range1);
8✔
2078
         rdi_ranges.push_back(rdi_id_or_range2);
8✔
2079
      }
2080

2081
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(std::optional(as_ranges));
16✔
2082
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(std::optional(rdi_ranges));
16✔
2083

2084
      ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
40✔
2085
                                                              include_rdi ? std::optional(rdi) : std::nullopt);
40✔
2086

2087
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
16✔
2088

2089
      auto ca_key = make_a_private_key(sig_algo, *rng);
16✔
2090
      result.require("CA key", ca_key != nullptr);
16✔
2091
      const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
16✔
2092
      Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
16✔
2093
      auto key = make_a_private_key(sig_algo, *rng);
16✔
2094

2095
      Botan::X509_Cert_Options opts1 = req_opts1(sig_algo);
16✔
2096
      opts1.extensions.add(std::move(blocks));
16✔
2097

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

2101
      {
16✔
2102
         auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
32✔
2103
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
32✔
2104

2105
         const auto& identifier = as_blocks->as_identifiers();
16✔
2106

2107
         if(include_asnum) {
16✔
2108
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
2109

2110
            if(push_asnum) {
8✔
2111
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
2112
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
2113

2114
               result.confirm("asnum entry 1 min", asnum_entries[1].min() == 5042, true);
8✔
2115
               result.confirm("asnum entry 1 max", asnum_entries[1].max() == 5042, true);
8✔
2116

2117
               result.confirm("asnum entry 2 min", asnum_entries[2].min() == 5043, true);
8✔
2118
               result.confirm("asnum entry 2 max", asnum_entries[2].max() == 4294967295, true);
8✔
2119
            } else {
2120
               result.confirm("asnum has no entries", asnum_entries.empty(), true);
8✔
2121
            }
2122
         } else {
2123
            result.confirm("no asnum entry", identifier.asnum().has_value(), false);
16✔
2124
         }
2125

2126
         if(include_rdi) {
16✔
2127
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
2128

2129
            if(push_rdi) {
8✔
2130
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
2131
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
2132

2133
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
2134
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 32768, true);
8✔
2135

2136
               result.confirm("rdi entry 2 min", rdi_entries[2].min() == 32769, true);
8✔
2137
               result.confirm("rdi entry 2 max", rdi_entries[2].max() == 4294967295, true);
8✔
2138
            } else {
2139
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
2140
            }
2141
         } else {
2142
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
16✔
2143
         }
2144
      }
2145
   }
80✔
2146

2147
   result.end_timer();
1✔
2148
   #endif
2149
   return result;
1✔
2150
}
2✔
2151

2152
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
2153
   if(sig_algo == "RSA") {
12✔
2154
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
5✔
2155
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
2156
             sig_algo == "GOST-34.10") {
7✔
2157
      return {hash};
10✔
2158
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
2159
      return {"Pure"};
2✔
2160
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
2161
      return {"Randomized"};
2✔
2162
   } else if(sig_algo == "HSS-LMS") {
2✔
2163
      return {""};
1✔
2164
   } else {
2165
      return {};
1✔
2166
   }
2167
}
7✔
2168

2169
class X509_Cert_Unit_Tests final : public Test {
×
2170
   public:
2171
      std::vector<Test::Result> run() override {
1✔
2172
         std::vector<Test::Result> results;
1✔
2173

2174
         auto& rng = this->rng();
1✔
2175

2176
         const std::string sig_algos[]{"RSA",
1✔
2177
                                       "DSA",
2178
                                       "ECDSA",
2179
                                       "ECGDSA",
2180
                                       "ECKCDSA",
2181
                                       "GOST-34.10",
2182
                                       "Ed25519",
2183
                                       "Ed448",
2184
                                       "Dilithium",
2185
                                       "ML-DSA",
2186
                                       "SLH-DSA",
2187
                                       "HSS-LMS"};
13✔
2188

2189
         for(const std::string& algo : sig_algos) {
13✔
2190
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
2191
            if(algo == "RSA")
2192
               continue;
2193
   #endif
2194

2195
            std::string hash = "SHA-256";
12✔
2196

2197
            if(algo == "Ed25519") {
12✔
2198
               hash = "SHA-512";
1✔
2199
            }
2200
            if(algo == "Ed448") {
12✔
2201
               hash = "SHAKE-256(912)";
1✔
2202
            }
2203
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
2204
               hash = "SHAKE-256(512)";
2✔
2205
            }
2206

2207
            auto key = make_a_private_key(algo, rng);
12✔
2208

2209
            if(key == nullptr) {
12✔
2210
               continue;
×
2211
            }
2212

2213
            results.push_back(test_hashes(*key, hash, rng));
24✔
2214
            results.push_back(test_valid_constraints(*key, algo));
24✔
2215

2216
            Test::Result usage_result("X509 Usage");
12✔
2217
            try {
12✔
2218
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
2219
            } catch(std::exception& e) {
×
2220
               usage_result.test_failure("test_usage " + algo, e.what());
×
2221
            }
×
2222
            results.push_back(usage_result);
12✔
2223

2224
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
24✔
2225
               Test::Result cert_result("X509 Unit");
12✔
2226

2227
               try {
12✔
2228
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
12✔
2229
               } catch(std::exception& e) {
×
2230
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
2231
               }
×
2232
               results.push_back(cert_result);
12✔
2233

2234
               Test::Result pkcs10_result("PKCS10 extensions");
12✔
2235
               try {
12✔
2236
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
12✔
2237
               } catch(std::exception& e) {
×
2238
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
2239
               }
×
2240
               results.push_back(pkcs10_result);
12✔
2241

2242
               Test::Result self_issued_result("X509 Self Issued");
12✔
2243
               try {
12✔
2244
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
12✔
2245
               } catch(std::exception& e) {
×
2246
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
2247
               }
×
2248
               results.push_back(self_issued_result);
12✔
2249

2250
               Test::Result extensions_result("X509 Extensions");
12✔
2251
               try {
12✔
2252
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
12✔
2253
               } catch(std::exception& e) {
×
2254
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
2255
               }
×
2256
               results.push_back(extensions_result);
12✔
2257

2258
               Test::Result custom_dn_result("X509 Custom DN");
12✔
2259
               try {
12✔
2260
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
12✔
2261
               } catch(std::exception& e) {
×
2262
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
2263
               }
×
2264
               results.push_back(custom_dn_result);
12✔
2265
            }
24✔
2266
         }
24✔
2267

2268
         /*
2269
         These are algos which cannot sign but can be included in certs
2270
         */
2271
         const std::vector<std::string> enc_algos = {
1✔
2272
            "DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM", "ClassicMcEliece"};
1✔
2273

2274
         for(const std::string& algo : enc_algos) {
8✔
2275
            auto key = make_a_private_key(algo, rng);
7✔
2276

2277
            if(key) {
7✔
2278
               results.push_back(test_valid_constraints(*key, algo));
14✔
2279
            }
2280
         }
7✔
2281

2282
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
2283
      defined(BOTAN_HAS_RSA)
2284
         Test::Result pad_config_result("X509 Padding Config");
1✔
2285
         try {
1✔
2286
            pad_config_result.merge(test_padding_config());
1✔
2287
         } catch(const std::exception& e) {
×
2288
            pad_config_result.test_failure("test_padding_config", e.what());
×
2289
         }
×
2290
         results.push_back(pad_config_result);
1✔
2291
   #endif
2292

2293
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2294
         results.push_back(test_x509_utf8());
2✔
2295
         results.push_back(test_x509_bmpstring());
2✔
2296
         results.push_back(test_x509_teletex());
2✔
2297
         results.push_back(test_crl_dn_name());
2✔
2298
         results.push_back(test_rdn_multielement_set_name());
2✔
2299
         results.push_back(test_x509_decode_list());
2✔
2300
         results.push_back(test_rsa_oaep());
2✔
2301
         results.push_back(test_x509_authority_info_access_extension());
2✔
2302
         results.push_back(test_verify_gost2012_cert());
2✔
2303
         results.push_back(test_parse_rsa_pss_cert());
2✔
2304
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
2305
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2306
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2307
   #endif
2308

2309
         results.push_back(test_x509_ip_addr_blocks_extension_encode());
2✔
2310
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases());
2✔
2311
         results.push_back(test_x509_as_blocks_extension_encode());
2✔
2312
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
2313
         results.push_back(test_x509_extension());
2✔
2314
         results.push_back(test_x509_dates());
2✔
2315
         results.push_back(test_cert_status_strings());
2✔
2316
         results.push_back(test_x509_uninit());
2✔
2317

2318
         return results;
1✔
2319
      }
13✔
2320
};
2321

2322
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
2323

2324
#endif
2325

2326
}  // namespace
2327

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