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

randombit / botan / 11844561993

14 Nov 2024 07:58PM UTC coverage: 91.178% (+0.1%) from 91.072%
11844561993

Pull #4435

github

web-flow
Merge 81dcb29da into e430f157a
Pull Request #4435: Test duration values ​​are now presented in seconds with six digits of precision. Tests without time measurements have been edited.

91856 of 100744 relevant lines covered (91.18%)

9311006.71 hits per line

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

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

8
#include "tests.h"
9

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

23
namespace Botan_Tests {
24

25
namespace {
26

27
#if defined(BOTAN_HAS_X509_CERTIFICATES)
28

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

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

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

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

45
   opts.CA_key(1);
121✔
46

47
   return opts;
121✔
48
}
×
49

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

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

58
   opts.not_before("160101200000Z");
39✔
59
   opts.not_after("300101200000Z");
39✔
60

61
   opts.challenge = "zoom";
39✔
62

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

69
   return opts;
39✔
70
}
×
71

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

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

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

82
   return opts;
12✔
83
}
×
84

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

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

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

97
   return opts;
60✔
98
}
×
99

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

121
   return Botan::create_private_key(algo, rng, params);
104✔
122
}
104✔
123

124
Test::Result test_cert_status_strings() {
1✔
125
   Test::Result result("Certificate_Status_Code to_string");
1✔
126
   result.start_timer();
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
   result.end_timer();
1✔
191
   return result;
1✔
192
}
1✔
193

194
Test::Result test_x509_extension() {
1✔
195
   Test::Result result("X509 Extensions API");
1✔
196
   result.start_timer();
1✔
197

198
   Botan::Extensions extn;
1✔
199

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

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

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

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

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

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

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

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

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

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

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

233
   result.end_timer();
1✔
234
   return result;
2✔
235
}
2✔
236

237
Test::Result test_x509_dates() {
1✔
238
   Test::Result result("X509 Time");
1✔
239
   result.start_timer();
1✔
240

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

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

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

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

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

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

271
      "080201000000-0000",
272
      "080201172412+0000",
273
      "040614233433-0500",
274
      "990614233444+0500",
275
      "000614233455-0530",
276
      "000614233455+0530",
277
   };
13✔
278

279
   // valid length 13
280
   const std::string valid_utc[]{
1✔
281
      "080201000000Z",
282
      "080201172412Z",
283
      "040614233433Z",
284
      "990614233444Z",
285
      "000614233455Z",
286
   };
6✔
287

288
   const std::string invalid_utc[]{
1✔
289
      "",
290
      " ",
291
      "2008`02-01",
292
      "9999-02-01",
293
      "2000-02-01 17",
294
      "999921",
295

296
      // No seconds
297
      "0802010000Z",
298
      "0802011724Z",
299
      "0406142334Z",
300
      "9906142334Z",
301
      "0006142334Z",
302

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

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

336
      // wrong time zone
337
      "080201000000",
338
      "080201000000z",
339

340
      // Fractional seconds
341
      "170217180154.001Z",
342

343
      // Timezone offset
344
      "170217180154+0100",
345

346
      // Extra digits
347
      "17021718015400Z",
348

349
      // Non-digits
350
      "17021718015aZ",
351

352
      // Trailing garbage
353
      "170217180154Zlongtrailinggarbage",
354

355
      // Swapped type
356
      "20170217180154Z",
357
   };
49✔
358

359
   // valid length 15
360
   const std::string valid_generalized_time[]{
1✔
361
      "20000305100350Z",
362
   };
2✔
363

364
   const std::string invalid_generalized[]{
1✔
365
      // No trailing Z
366
      "20000305100350",
367

368
      // No seconds
369
      "200003051003Z",
370

371
      // Fractional seconds
372
      "20000305100350.001Z",
373

374
      // Timezone offset
375
      "20170217180154+0100",
376

377
      // Extra digits
378
      "2017021718015400Z",
379

380
      // Non-digits
381
      "2017021718015aZ",
382

383
      // Trailing garbage
384
      "20170217180154Zlongtrailinggarbage",
385

386
      // Swapped type
387
      "170217180154Z",
388
   };
9✔
389

390
   for(const auto& v : valid_but_unsup) {
13✔
391
      result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
108✔
392
   }
393

394
   for(const auto& v : valid_utc) {
6✔
395
      Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
396
   }
5✔
397

398
   for(const auto& v : valid_generalized_time) {
2✔
399
      Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
400
   }
1✔
401

402
   for(const auto& v : invalid_utc) {
49✔
403
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
432✔
404
   }
405

406
   for(const auto& v : invalid_generalized) {
9✔
407
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime); });
72✔
408
   }
409

410
   result.end_timer();
1✔
411
   return result;
1✔
412
}
80✔
413

414
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
415

416
Test::Result test_crl_dn_name() {
1✔
417
   Test::Result result("CRL DN name");
1✔
418
   result.start_timer();
1✔
419

420
      // See GH #1252
421

422
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
423
   auto rng = Test::new_rng(__func__);
1✔
424

425
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
426

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

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

433
   Botan::X509_CRL crl = ca.new_crl(*rng);
1✔
434

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

437
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
3✔
438
      #endif
439

440
   result.end_timer();
1✔
441
   return result;
2✔
442
}
3✔
443

444
Test::Result test_rdn_multielement_set_name() {
1✔
445
   Test::Result result("DN with multiple elements in RDN");
1✔
446
   result.start_timer();
1✔
447

448
   // GH #2611
449

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

452
   result.confirm("issuer DN contains expected name components", cert.issuer_dn().get_attributes().size() == 4);
2✔
453
   result.confirm("subject DN contains expected name components", cert.subject_dn().get_attributes().size() == 4);
2✔
454
   
455
   result.end_timer();
1✔
456
   return result;
1✔
457
}
1✔
458

459
Test::Result test_rsa_oaep() {
1✔
460
   Test::Result result("RSA OAEP decoding");
1✔
461
   result.start_timer();
1✔
462

463
      #if defined(BOTAN_HAS_RSA)
464
   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
465

466
   auto public_key = cert.subject_public_key();
1✔
467
   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
1✔
468
   const auto& pk_info = cert.subject_public_key_algo();
1✔
469

470
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
2✔
471
      #endif
472

473
   result.end_timer();
1✔
474
   return result;
2✔
475
}
1✔
476

477
Test::Result test_x509_decode_list() {
1✔
478
   Test::Result result("X509_Certificate list decode");
1✔
479
   result.start_timer();
1✔
480

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

483
   Botan::BER_Decoder dec(input);
1✔
484
   std::vector<Botan::X509_Certificate> certs;
1✔
485
   dec.decode_list(certs);
1✔
486

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

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

492
   result.end_timer();
1✔
493
   return result;
1✔
494
}
1✔
495

496
Test::Result test_x509_utf8() {
1✔
497
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
498
   result.start_timer();
1✔
499

500
   try {
1✔
501
      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
502

503
      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
504
      const std::string organization =
1✔
505
         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
506
         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
1✔
507
      const std::string organization_unit =
1✔
508
         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
509
         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
1✔
510
      const std::string common_name =
1✔
511
         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
512
         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
1✔
513
      const std::string location = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
514

515
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
516

517
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
518
      result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
2✔
519
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
520
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
521
   } catch(const Botan::Decoding_Error& ex) {
1✔
522
      result.test_failure(ex.what());
×
523
   }
×
524

525
   result.end_timer();
1✔
526
   return result;
1✔
527
}
×
528

529
Test::Result test_x509_bmpstring() {
1✔
530
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
531
   result.start_timer();
1✔
532

533
   try {
1✔
534
      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
535

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

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

544
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
545

546
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
547
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
548
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
549
   } catch(const Botan::Decoding_Error& ex) {
1✔
550
      result.test_failure(ex.what());
×
551
   }
×
552

553
   result.end_timer();
1✔
554
   return result;
1✔
555
}
×
556

557
Test::Result test_x509_teletex() {
1✔
558
   Test::Result result("X509 with TeletexString encoded fields");
1✔
559
   result.start_timer();
1✔
560

561
   try {
1✔
562
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
563

564
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
565

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

568
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
569
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
570
   } catch(const Botan::Decoding_Error& ex) {
1✔
571
      result.test_failure(ex.what());
×
572
   }
×
573

574
   result.end_timer();
1✔
575
   return result;
1✔
576
}
×
577

578
Test::Result test_x509_authority_info_access_extension() {
1✔
579
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
580
   result.start_timer();
1✔
581

582
   // contains no AIA extension
583
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
584

585
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
586
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
587

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

591
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
592

593
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
594
   if(result.tests_failed()) {
1✔
595
      result.end_timer();
×
596
      return result;
597
   }
598

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

602
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
603
   Botan::X509_Certificate aia_cert_2ca(
1✔
604
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
605

606
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
607

608
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
609
   if(result.tests_failed()) {
1✔
610
      result.end_timer();
×
611
      return result;
612
   }
613

614
   result.test_eq(
2✔
615
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
616
   result.test_eq(
2✔
617
      "CA issuer URL matches",
618
      ca_issuers2[1],
1✔
619
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
620
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
2✔
621

622
   result.end_timer();
1✔
623
   return result;
624
}
1✔
625

626
Test::Result test_x509_encode_authority_info_access_extension() {
1✔
627
   Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension");
1✔
628
   result.start_timer();
1✔
629

630
      #if defined(BOTAN_HAS_RSA)
631
   auto rng = Test::new_rng(__func__);
1✔
632

633
   const std::string sig_algo{"RSA"};
1✔
634
   const std::string hash_fn{"SHA-256"};
1✔
635
   const std::string padding_method{"EMSA3(SHA-256)"};
1✔
636

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

642
   // OCSP
643
   const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"};
1✔
644

645
   // create a CA
646
   auto ca_key = make_a_private_key(sig_algo, *rng);
1✔
647
   result.require("CA key", ca_key != nullptr);
1✔
648
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng);
1✔
649
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
1✔
650

651
   // create a certificate with only caIssuer information
652
   auto key = make_a_private_key(sig_algo, *rng);
1✔
653

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

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

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

661
   if(!result.test_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) {
1✔
662
      result.end_timer();
×
663
      return result;
664
   }
665

666
   for(const auto& ca_issuer : cert.ca_issuers()) {
3✔
667
      result.confirm("CA issuer URI present in certificate",
4✔
668
                     std::ranges::find(ca_issuers, ca_issuer) != ca_issuers.end());
4✔
669
   }
1✔
670

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

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

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

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

681
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
682
   result.confirm("no CA Issuer URI available", cert.ca_issuers().empty());
2✔
683
   result.test_eq("OCSP responder URI matches", cert.ocsp_responder(), std::string(ocsp_uri));
3✔
684

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

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

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

693
   result.confirm("OCSP URI available", !cert.ocsp_responder().empty());
2✔
694
   result.confirm("CA Issuer URI available", !cert.ca_issuers().empty());
2✔
695
      #endif
696

697
   result.end_timer();
1✔
698
   return result;
1✔
699
}
4✔
700

701
Test::Result test_parse_rsa_pss_cert() {
1✔
702
   Test::Result result("X509 RSA-PSS certificate");
1✔
703
   result.start_timer();
1✔
704

705
   // See https://github.com/randombit/botan/issues/3019 for background
706

707
   try {
1✔
708
      Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
709
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
710
   } catch(Botan::Exception& e) {
1✔
711
      result.test_failure("Parsing failed", e.what());
×
712
   }
×
713

714
   result.end_timer();
1✔
715
   return result;
1✔
716
}
×
717

718
Test::Result test_verify_gost2012_cert() {
1✔
719
   Test::Result result("X509 GOST-2012 certificates");
1✔
720
   result.start_timer();
1✔
721

722
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
723
   try {
1✔
724
      Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
725
      Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
726

727
      Botan::Certificate_Store_In_Memory trusted;
1✔
728
      trusted.add_certificate(root_cert);
1✔
729

730
      const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
2✔
731
      const Botan::Path_Validation_Result validation_result =
1✔
732
         Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
733

734
      result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
735
   } catch(const Botan::Decoding_Error& e) {
1✔
736
      result.test_failure(e.what());
×
737
   }
×
738
      #endif
739

740
   result.end_timer();
1✔
741
   return result;
1✔
742
}
×
743

744
      /*
745
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
746
 *
747
 * For the other algorithms than RSA, only one padding is supported right now.
748
 */
749
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
750
Test::Result test_padding_config() {
1✔
751
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
752
   Test::Result test_result("X509 Padding Config");
1✔
753
   test_result.start_timer();
1✔
754

755
   auto rng = Test::new_rng(__func__);
1✔
756

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

760
   // Create X509 CA certificate; EMSA3 is used for signing by default
761
   Botan::X509_Cert_Options opt("TESTCA");
1✔
762
   opt.CA_key();
1✔
763

764
   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
765
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
766
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
767
                       "RSA/EMSA3(SHA-512)");
768

769
   // Create X509 CA certificate; RSA-PSS is explicitly set
770
   opt.set_padding_scheme("PSSR");
1✔
771
   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
1✔
772
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
773
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
774
                       "RSA/EMSA4");
775

776
         #if defined(BOTAN_HAS_EMSA2)
777
   // Try to set a padding scheme that is not supported for signing with the given key type
778
   opt.set_padding_scheme("EMSA2");
779
   try {
780
      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng);
781
      test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " +
782
                               sk->algo_name());
783
   } catch(const Botan::Invalid_Argument& e) {
784
      test_result.test_eq("Build CA certificate with invalid encoding scheme EMSA1 for key type " + sk->algo_name(),
785
                          e.what(),
786
                          "Signatures using RSA/EMSA2(SHA-512) are not supported");
787
   }
788
         #endif
789

790
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
791
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
792
                       "RSA/EMSA4");
793

794
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
795
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
796

797
   // Prepare a signing request for the end certificate
798
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
799
   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
1✔
800
   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng);
1✔
801
   test_result.test_eq("Certificate request signature algorithm",
3✔
802
                       end_req.signature_algorithm().oid().to_formatted_string(),
2✔
803
                       "RSA/EMSA4");
804

805
   // Create X509 CA object: will fail as the chosen hash functions differ
806
   try {
1✔
807
      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", *rng);
1✔
808
      test_result.test_failure("Configured conflicting hash functions for CA");
×
809
   } catch(const Botan::Invalid_Argument& e) {
1✔
810
      test_result.test_eq(
1✔
811
         "Configured conflicting hash functions for CA",
812
         e.what(),
1✔
813
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding EMSA4(SHA-256)");
814
   }
1✔
815

816
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
817
   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng);
1✔
818
   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, *rng, not_before, not_after);
1✔
819
   test_result.test_eq("End certificate signature algorithm",
3✔
820
                       end_cert_emsa3.signature_algorithm().oid().to_formatted_string(),
2✔
821
                       "RSA/EMSA3(SHA-512)");
822

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

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

837
   // Check CRL signature algorithm
838
   Botan::X509_CRL crl = ca_exp.new_crl(*rng);
1✔
839
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4");
2✔
840

841
   // sanity check for verification, the heavy lifting is done in the other unit tests
842
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
843
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
844
   const Botan::Path_Validation_Result validation_result =
1✔
845
      Botan::x509_path_validate(end_cert_emsa4, restrictions, trusted);
1✔
846
   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
2✔
847

848
   test_result.end_timer();
1✔
849
   return test_result;
2✔
850
}
3✔
851
      #endif
852

853
   #endif
854

855
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
12✔
856
                             const std::string& sig_padding,
857
                             const std::string& hash_fn,
858
                             Botan::RandomNumberGenerator& rng) {
859
   Test::Result result("PKCS10 extensions");
12✔
860
   result.start_timer();
12✔
861

862
   Botan::X509_Cert_Options opts;
12✔
863

864
   opts.dns = "main.example.org";
12✔
865
   opts.more_dns.push_back("more1.example.org");
24✔
866
   opts.more_dns.push_back("more2.example.org");
24✔
867

868
   opts.padding_scheme = sig_padding;
12✔
869

870
   Botan::AlternativeName alt_name;
12✔
871
   alt_name.add_attribute("DNS", "bonus.example.org");
12✔
872

873
   Botan::X509_DN alt_dn;
12✔
874
   alt_dn.add_attribute("X520.CommonName", "alt_cn");
12✔
875
   alt_dn.add_attribute("X520.Organization", "testing");
12✔
876
   alt_name.add_dn(alt_dn);
12✔
877

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

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

882
   const auto alt_dns_names = req.subject_alt_name().get_attribute("DNS");
12✔
883

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

886
   if(alt_dns_names.size() == 4) {
12✔
887
      result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "bonus.example.org");
36✔
888
      result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "main.example.org");
36✔
889
      result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "more1.example.org");
36✔
890
      result.test_eq("Expected DNS name 3", alt_dns_names.at(3), "more2.example.org");
36✔
891
   }
892

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

896
   result.end_timer();
12✔
897
   return result;
12✔
898
}
12✔
899

900
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
12✔
901
                            const std::string& sig_algo,
902
                            const std::string& sig_padding,
903
                            const std::string& hash_fn,
904
                            Botan::RandomNumberGenerator& rng) {
905
   Test::Result result("X509 Unit");
12✔
906
   result.start_timer();
12✔
907

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

911
   {
12✔
912
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
24✔
913
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
24✔
914
   }
915

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

919
   Botan::PKCS10_Request user1_req =
12✔
920
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng);
12✔
921

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

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

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

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

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

934
   /* Create the CA object */
935
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
936

937
   const BigInt user1_serial = 99;
12✔
938

939
   /* Sign the requests to create the certs */
940
   Botan::X509_Certificate user1_cert =
12✔
941
      ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
942

943
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
12✔
944
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
12✔
945

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

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

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

954
   {
12✔
955
      auto constraints = req_opts1(sig_algo).constraints;
12✔
956
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
24✔
957
   }
958

959
   /* Copy, assign and compare */
960
   Botan::X509_Certificate user1_cert_copy(user1_cert);
12✔
961
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
12✔
962

963
   user1_cert_copy = user2_cert;
12✔
964
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
12✔
965

966
   Botan::X509_Certificate user1_cert_differ =
12✔
967
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
968

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

971
   /* Get cert data */
972
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
12✔
973

974
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
12✔
975
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
24✔
976
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
24✔
977
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
24✔
978
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
24✔
979

980
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
12✔
981
   result.test_eq("subject OrgaUnit count",
12✔
982
                  user3_subject_dn.get_attribute("OU").size(),
24✔
983
                  req_opts3(sig_algo).more_org_units.size() + 1);
24✔
984
   result.test_eq(
12✔
985
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
48✔
986

987
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
12✔
988
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
24✔
989
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
24✔
990
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
24✔
991

992
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
12✔
993
   result.test_eq(
12✔
994
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
24✔
995
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
48✔
996

997
   const Botan::X509_CRL crl1 = ca.new_crl(rng);
12✔
998

999
   /* Verify the certs */
1000
   Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1001
   Botan::Certificate_Store_In_Memory store;
12✔
1002

1003
   // First try with an empty store
1004
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1005
   result.test_eq("user 1 issuer not found",
36✔
1006
                  result_no_issuer.result_string(),
24✔
1007
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1008

1009
   store.add_certificate(ca.ca_certificate());
12✔
1010

1011
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1012
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
1013
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1014
   }
1015

1016
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
12✔
1017
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
24✔
1018
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
1019
   }
1020

1021
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
12✔
1022
   result.test_eq("user 1 issuer not found",
36✔
1023
                  result_no_issuer.result_string(),
24✔
1024
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
1025
   store.add_crl(crl1);
12✔
1026

1027
   std::vector<Botan::CRL_Entry> revoked;
12✔
1028
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
24✔
1029
   revoked.push_back(user2_cert);
24✔
1030

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

1033
   store.add_crl(crl2);
12✔
1034

1035
   const std::string revoked_str =
12✔
1036
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
12✔
1037

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

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

1044
   revoked.clear();
12✔
1045
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
24✔
1046
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng);
12✔
1047

1048
   store.add_crl(crl3);
12✔
1049

1050
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
12✔
1051
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
24✔
1052
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
1053
   }
1054

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

1058
   result.end_timer();
12✔
1059
   return result;
12✔
1060
}
60✔
1061

1062
Test::Result test_usage(const Botan::Private_Key& ca_key,
12✔
1063
                        const std::string& sig_algo,
1064
                        const std::string& hash_fn,
1065
                        Botan::RandomNumberGenerator& rng) {
1066
   using Botan::Key_Constraints;
12✔
1067
   using Botan::Usage_Type;
12✔
1068

1069
   Test::Result result("X509 Usage");
12✔
1070
   result.start_timer();
12✔
1071

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

1075
   /* Create the CA object */
1076
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng);
12✔
1077

1078
   auto user1_key = make_a_private_key(sig_algo, rng);
12✔
1079

1080
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1081
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1082

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

1085
   const Botan::X509_Certificate user1_cert =
12✔
1086
      ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1087

1088
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1089
   result.test_eq(
12✔
1090
      "key usage cRLSign not allowed",
1091
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
12✔
1092
      false);
1093
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1094

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

1098
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
12✔
1099

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

1102
   const Botan::X509_Certificate mult_usage_cert =
12✔
1103
      ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1104

1105
   // cert allows multiple usages, so each one of them as well as both together should be allowed
1106
   result.confirm("key usage multiple digitalSignature allowed",
24✔
1107
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
12✔
1108
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1109
   result.confirm(
24✔
1110
      "key usage multiple digitalSignature and cRLSign allowed",
1111
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
12✔
1112
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
12✔
1113

1114
   opts.constraints = Key_Constraints();
12✔
1115

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

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

1121
   // cert allows every usage
1122
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
24✔
1123
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
24✔
1124
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
24✔
1125

1126
   if(sig_algo == "RSA") {
12✔
1127
      // cert allows data encryption
1128
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
1129

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

1132
      const Botan::X509_Certificate enc_cert =
1✔
1133
         ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1134

1135
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1136
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1137
   }
1✔
1138

1139
   result.end_timer();
12✔
1140
   return result;
12✔
1141
}
24✔
1142

1143
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
12✔
1144
                              const std::string& sig_algo,
1145
                              const std::string& sig_padding,
1146
                              const std::string& hash_fn,
1147
                              Botan::RandomNumberGenerator& rng) {
1148
   using Botan::Key_Constraints;
12✔
1149

1150
   Test::Result result("X509 Self Issued");
12✔
1151
   result.start_timer();
12✔
1152

1153
   // create the self-signed cert
1154
   const Botan::X509_Certificate ca_cert =
12✔
1155
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1156

1157
   /* Create the CA object */
1158
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1159

1160
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1161

1162
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1163
   // but signed by a CA, not signed by it's own private key
1164
   Botan::X509_Cert_Options opts = ca_opts();
12✔
1165
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1166
   opts.set_padding_scheme(sig_padding);
12✔
1167

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

1170
   const Botan::X509_Certificate self_issued_cert =
12✔
1171
      ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1172

1173
   // check that this chain can can be verified successfully
1174
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
12✔
1175

1176
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
24✔
1177

1178
   const Botan::Path_Validation_Result validation_result =
12✔
1179
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
12✔
1180

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

1183
   result.end_timer();
12✔
1184
   return result;
12✔
1185
}
24✔
1186

1187
Test::Result test_x509_uninit() {
1✔
1188
   Test::Result result("X509 object uninitialized access");
1✔
1189
   result.start_timer();
1✔
1190

1191
   Botan::X509_Certificate cert;
1✔
1192
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
2✔
1193
      cert.x509_version();
1✔
1194
   });
1195

1196
   Botan::X509_CRL crl;
1✔
1197
   result.test_throws(
2✔
1198
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
2✔
1199

1200
   result.end_timer();
1✔
1201
   return result;
1✔
1202
}
1✔
1203

1204
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
18✔
1205
   using Botan::Key_Constraints;
18✔
1206

1207
   Test::Result result("X509 Valid Constraints " + pk_algo);
18✔
1208
   result.start_timer();
18✔
1209

1210
   result.confirm("empty constraints always acceptable", Key_Constraints().compatible_with(key));
36✔
1211

1212
   // Now check some typical usage scenarios for the given key type
1213
   // Taken from RFC 5280, sec. 4.2.1.3
1214
   // ALL constraints are not typical at all, but we use them for a negative test
1215
   const auto all = Key_Constraints(
18✔
1216
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1217
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1218
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
18✔
1219

1220
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
18✔
1221
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
18✔
1222
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
18✔
1223
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
18✔
1224
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
18✔
1225
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
18✔
1226
   const auto key_agreement_encipher_only =
18✔
1227
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
18✔
1228
   const auto key_agreement_decipher_only =
18✔
1229
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
18✔
1230
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
18✔
1231
   const auto sign_everything =
18✔
1232
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
18✔
1233

1234
   if(pk_algo == "DH" || pk_algo == "ECDH") {
18✔
1235
      // DH and ECDH only for key agreement
1236
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1237
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1238
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1239
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1240
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1241
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1242
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1243
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1244
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1245
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1246
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1247
   } else if(pk_algo == "Kyber" || pk_algo == "FrodoKEM" || pk_algo == "ML-KEM") {
16✔
1248
      // KEMs can encrypt and agree
1249
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
3✔
1250
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
3✔
1251
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
3✔
1252
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
3✔
1253
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
3✔
1254
      result.test_eq("sign", sign_everything.compatible_with(key), false);
3✔
1255
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
3✔
1256
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), false);
3✔
1257
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
6✔
1258
   } else if(pk_algo == "RSA") {
13✔
1259
      // RSA can do everything except key agreement
1260
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1261

1262
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1263
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1264
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1265
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1266
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1267
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1268
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1269
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1270
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1271
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1272
   } else if(pk_algo == "ElGamal") {
12✔
1273
      // only ElGamal encryption is currently implemented
1274
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1275
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1276
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1277
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1278
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1279
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1280
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1281
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1282
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1283
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
10✔
1284
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" ||
18✔
1285
             pk_algo == "HSS-LMS") {
3✔
1286
      // these are signature algorithms only
1287
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
9✔
1288

1289
      result.test_eq("ca allowed", ca.compatible_with(key), true);
9✔
1290
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
9✔
1291
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
9✔
1292
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
9✔
1293
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
9✔
1294
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
9✔
1295
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
9✔
1296
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
9✔
1297
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
9✔
1298
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
18✔
1299
   }
1300

1301
   result.end_timer();
18✔
1302
   return result;
18✔
1303
}
×
1304

1305
/**
1306
 * @brief X.509v3 extension that encodes a given string
1307
 */
1308
class String_Extension final : public Botan::Certificate_Extension {
24✔
1309
   public:
1310
      String_Extension() = default;
24✔
1311

1312
      explicit String_Extension(const std::string& val) : m_contents(val) {}
12✔
1313

1314
      std::string value() const { return m_contents; }
48✔
1315

1316
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1317
         return std::make_unique<String_Extension>(m_contents);
×
1318
      }
1319

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

1322
      bool should_encode() const override { return true; }
24✔
1323

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

1326
      std::vector<uint8_t> encode_inner() const override {
12✔
1327
         std::vector<uint8_t> bits;
12✔
1328
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
12✔
1329
         return bits;
12✔
1330
      }
×
1331

1332
      void decode_inner(const std::vector<uint8_t>& in) override {
24✔
1333
         Botan::ASN1_String str;
24✔
1334
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
24✔
1335
         m_contents = str.value();
24✔
1336
      }
24✔
1337

1338
   private:
1339
      std::string m_contents;
1340
};
1341

1342
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
12✔
1343
                                 const std::string& sig_algo,
1344
                                 const std::string& sig_padding,
1345
                                 const std::string& hash_fn,
1346
                                 Botan::RandomNumberGenerator& rng) {
1347
   Test::Result result("X509 Custom DN");
12✔
1348
   result.start_timer();
12✔
1349
   
1350
   /* Create the self-signed cert */
1351
   Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng);
12✔
1352

1353
   /* Create the CA object */
1354
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1355

1356
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1357

1358
   Botan::X509_DN subject_dn;
12✔
1359

1360
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
12✔
1361
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
12✔
1362
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
12✔
1363
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
12✔
1364

1365
   subject_dn.add_attribute(attr1, val1);
12✔
1366
   subject_dn.add_attribute(attr2, val2);
12✔
1367

1368
   Botan::Extensions extensions;
12✔
1369

1370
   Botan::PKCS10_Request req =
12✔
1371
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding);
12✔
1372

1373
   const Botan::X509_DN& req_dn = req.subject_dn();
12✔
1374

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

1377
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
12✔
1378
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
12✔
1379
   result.confirm("Attr1 matches encoded", req_val1 == val1);
24✔
1380
   result.confirm("Attr2 matches encoded", req_val2 == val2);
24✔
1381
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
24✔
1382
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
24✔
1383

1384
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1385
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
12✔
1386

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

1389
   const Botan::X509_DN& cert_dn = cert.subject_dn();
12✔
1390

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

1393
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
12✔
1394
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
12✔
1395
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
24✔
1396
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
24✔
1397
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
24✔
1398
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
24✔
1399

1400
   result.end_timer();
12✔
1401
   return result;
12✔
1402
}
36✔
1403

1404
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
12✔
1405
                                  const std::string& sig_algo,
1406
                                  const std::string& sig_padding,
1407
                                  const std::string& hash_fn,
1408
                                  Botan::RandomNumberGenerator& rng) {
1409
   using Botan::Key_Constraints;
12✔
1410

1411
   Test::Result result("X509 Extensions");
12✔
1412
   result.start_timer();
12✔
1413

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

1417
   /* Create the CA object */
1418
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng);
12✔
1419

1420
   /* Prepare CDP extension */
1421
   std::vector<std::string> cdp_urls = {
12✔
1422
      "http://example.com/crl1.pem",
1423
      "ldap://ldap.example.com/cn=crl1,dc=example,dc=com?certificateRevocationList;binary"};
12✔
1424

1425
   std::vector<Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point> dps;
12✔
1426

1427
   for(const auto& uri : cdp_urls) {
36✔
1428
      Botan::AlternativeName cdp_alt_name;
24✔
1429
      cdp_alt_name.add_uri(uri);
24✔
1430
      Botan::Cert_Extension::CRL_Distribution_Points::Distribution_Point dp(cdp_alt_name);
24✔
1431

1432
      dps.emplace_back(dp);
24✔
1433
   }
24✔
1434

1435
   auto user_key = make_a_private_key(sig_algo, rng);
12✔
1436

1437
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
12✔
1438
   opts.constraints = Key_Constraints::DigitalSignature;
12✔
1439

1440
   // include a custom extension in the request
1441
   Botan::Extensions req_extensions;
12✔
1442
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
12✔
1443
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
12✔
1444
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
24✔
1445
   req_extensions.add(std::make_unique<Botan::Cert_Extension::CRL_Distribution_Points>(dps));
24✔
1446
   opts.extensions = req_extensions;
12✔
1447
   opts.set_padding_scheme(sig_padding);
12✔
1448

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

1452
   result.confirm("Extensions::extension_set true for Key_Usage",
24✔
1453
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
12✔
1454

1455
   // check if known Key_Usage extension is present in self-signed cert
1456
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
12✔
1457
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
24✔
1458
      result.confirm(
24✔
1459
         "Key_Usage extension value matches in self-signed certificate",
1460
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
12✔
1461
   }
1462

1463
   // check if custom extension is present in self-signed cert
1464
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
12✔
1465
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
24✔
1466
      result.test_eq(
48✔
1467
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1468
   }
1469

1470
   // check if CDPs are present in the self-signed cert
1471
   auto cert_cdps =
12✔
1472
      self_signed_cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
24✔
1473

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

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

1484
   /* Create a CA-signed certificate */
1485
   const Botan::X509_Certificate ca_signed_cert =
12✔
1486
      ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1487

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

1491
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
24✔
1492
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
24✔
1493
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
12✔
1494
      result.confirm("Key_Usage extension value matches in user certificate",
24✔
1495
                     constraints == Botan::Key_Constraints::DigitalSignature);
12✔
1496
   }
1497

1498
   // check if custom extension is present in CA-signed cert
1499
   result.confirm("Extensions::extension_set true for String_Extension",
24✔
1500
                  ca_signed_cert.v3_extensions().extension_set(oid));
12✔
1501
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
24✔
1502
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
24✔
1503
      result.test_eq(
48✔
1504
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
36✔
1505
   }
1506

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

1510
   if(result.confirm("CRL Distribution Points extension present in self-signed certificate",
24✔
1511
                     !cert_cdps->crl_distribution_urls().empty())) {
12✔
1512
      for(const auto& cdp : cert_cdps->distribution_points()) {
36✔
1513
         result.confirm("CDP URI present in self-signed certificate",
48✔
1514
                        std::ranges::find(cdp_urls, cdp.point().get_first_attribute("URI")) != cdp_urls.end());
72✔
1515
      }
1516
   }
1517

1518
   result.end_timer();
12✔
1519
   return result;
12✔
1520
}
60✔
1521

1522
Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn, Botan::RandomNumberGenerator& rng) {
12✔
1523
   Test::Result result("X509 Hashes");
12✔
1524
   result.start_timer();
12✔
1525

1526
   struct TestData {
12✔
1527
         const std::string issuer, subject, issuer_hash, subject_hash;
1528
   } const cases[]{{"",
12✔
1529
                    "",
1530
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1531
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1532
                   {"a",
1533
                    "b",
1534
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1535
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1536
                   {"A",
1537
                    "B",
1538
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1539
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1540
                   {
1541
                      "Test Issuer/US/Botan Project/Testing",
1542
                      "Test Subject/US/Botan Project/Testing",
1543
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1544
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1545
                   },
1546
                   {
1547
                      "Test Subject/US/Botan Project/Testing",
1548
                      "Test Issuer/US/Botan Project/Testing",
1549
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1550
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1551
                   }};
72✔
1552

1553
   for(const auto& a : cases) {
72✔
1554
      Botan::X509_Cert_Options opts{a.issuer};
60✔
1555
      opts.CA_key();
60✔
1556

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

1559
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1560
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
120✔
1561

1562
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng);
60✔
1563
      const Botan::PKCS10_Request req =
60✔
1564
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng);
60✔
1565
      const Botan::X509_Certificate subject_cert =
60✔
1566
         ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01));
60✔
1567

1568
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
120✔
1569
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
120✔
1570
   }
60✔
1571
   result.end_timer();
12✔
1572
   return result;
12✔
1573
}
72✔
1574

1575
Test::Result test_x509_tn_auth_list_extension_decode() {
1✔
1576
   /* cert with TNAuthList extension data was generated by asn1parse cfg:
1577

1578
      asn1=SEQUENCE:tn_auth_list
1579

1580
      [tn_auth_list]
1581
      spc=EXP:0,IA5:1001
1582
      range=EXP:1,SEQUENCE:TelephoneNumberRange
1583
      one=EXP:2,IA5:333
1584

1585
      [TelephoneNumberRange]
1586
      start1=IA5:111
1587
      count1=INT:128
1588
      start2=IA5:222
1589
      count2=INT:256
1590
    */
1591
   const std::string filename("TNAuthList.pem");
1✔
1592
   Test::Result result("X509 TNAuthList decode");
1✔
1593
   result.start_timer();
1✔
1594

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

1597
   using Botan::Cert_Extension::TNAuthList;
1✔
1598

1599
   auto tn_auth_list = cert.v3_extensions().get_extension_object_as<TNAuthList>();
2✔
1600

1601
   auto& tn_entries = tn_auth_list->entries();
1✔
1602

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

1605
   result.test_throws("wrong telephone_number_range() accessor for spc",
2✔
1606
                      [&tn_entries] { tn_entries[0].telephone_number_range(); });
2✔
1607
   result.test_throws("wrong telephone_number() accessor for range",
2✔
1608
                      [&tn_entries] { tn_entries[1].telephone_number(); });
2✔
1609
   result.test_throws("wrong service_provider_code() accessor for one",
2✔
1610
                      [&tn_entries] { tn_entries[2].service_provider_code(); });
2✔
1611

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

1615
   result.test_eq("range entry type", tn_entries[1].type() == TNAuthList::Entry::TelephoneNumberRange, true);
1✔
1616
   auto& range = tn_entries[1].telephone_number_range();
1✔
1617
   result.test_eq("range entries count", range.size(), 2);
1✔
1618
   result.test_eq("range entry 0 start data", range[0].start.value(), "111");
2✔
1619
   result.test_eq("range entry 0 count data", range[0].count, 128);
1✔
1620
   result.test_eq("range entry 1 start data", range[1].start.value(), "222");
2✔
1621
   result.test_eq("range entry 1 count data", range[1].count, 256);
1✔
1622

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

1626
   result.end_timer();
1✔
1627
   return result;
2✔
1628
}
1✔
1629

1630
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
12✔
1631
   if(sig_algo == "RSA") {
12✔
1632
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
5✔
1633
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
11✔
1634
             sig_algo == "GOST-34.10") {
7✔
1635
      return {hash};
10✔
1636
   } else if(sig_algo == "Ed25519" || sig_algo == "Ed448") {
6✔
1637
      return {"Pure"};
2✔
1638
   } else if(sig_algo == "Dilithium" || sig_algo == "ML-DSA") {
4✔
1639
      return {"Randomized"};
2✔
1640
   } else if(sig_algo == "HSS-LMS") {
2✔
1641
      return {""};
1✔
1642
   } else {
1643
      return {};
1✔
1644
   }
1645
}
7✔
1646

1647
class X509_Cert_Unit_Tests final : public Test {
×
1648
   public:
1649
      std::vector<Test::Result> run() override {
1✔
1650
         std::vector<Test::Result> results;
1✔
1651

1652
         auto& rng = this->rng();
1✔
1653

1654
         const std::string sig_algos[]{"RSA",
1✔
1655
                                       "DSA",
1656
                                       "ECDSA",
1657
                                       "ECGDSA",
1658
                                       "ECKCDSA",
1659
                                       "GOST-34.10",
1660
                                       "Ed25519",
1661
                                       "Ed448",
1662
                                       "Dilithium",
1663
                                       "ML-DSA",
1664
                                       "SLH-DSA",
1665
                                       "HSS-LMS"};
13✔
1666

1667
         for(const std::string& algo : sig_algos) {
13✔
1668
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1669
            if(algo == "RSA")
1670
               continue;
1671
   #endif
1672

1673
            std::string hash = "SHA-256";
12✔
1674

1675
            if(algo == "Ed25519") {
12✔
1676
               hash = "SHA-512";
1✔
1677
            }
1678
            if(algo == "Ed448") {
12✔
1679
               hash = "SHAKE-256(912)";
1✔
1680
            }
1681
            if(algo == "Dilithium" || algo == "ML-DSA") {
12✔
1682
               hash = "SHAKE-256(512)";
2✔
1683
            }
1684

1685
            auto key = make_a_private_key(algo, rng);
12✔
1686

1687
            if(key == nullptr) {
12✔
1688
               continue;
×
1689
            }
1690

1691
            results.push_back(test_hashes(*key, hash, rng));
24✔
1692
            results.push_back(test_valid_constraints(*key, algo));
24✔
1693

1694
            Test::Result usage_result("X509 Usage");
12✔
1695
            try {
12✔
1696
               usage_result.merge(test_usage(*key, algo, hash, rng));
12✔
1697
            } catch(std::exception& e) {
×
1698
               usage_result.test_failure("test_usage " + algo, e.what());
×
1699
            }
×
1700
            results.push_back(usage_result);
12✔
1701

1702
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
24✔
1703
               Test::Result cert_result("X509 Unit");
12✔
1704

1705
               try {
12✔
1706
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng));
12✔
1707
               } catch(std::exception& e) {
×
1708
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1709
               }
×
1710
               results.push_back(cert_result);
12✔
1711

1712
               Test::Result pkcs10_result("PKCS10 extensions");
12✔
1713
               try {
12✔
1714
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng));
12✔
1715
               } catch(std::exception& e) {
×
1716
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
1717
               }
×
1718
               results.push_back(pkcs10_result);
12✔
1719

1720
               Test::Result self_issued_result("X509 Self Issued");
12✔
1721
               try {
12✔
1722
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng));
12✔
1723
               } catch(std::exception& e) {
×
1724
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
1725
               }
×
1726
               results.push_back(self_issued_result);
12✔
1727

1728
               Test::Result extensions_result("X509 Extensions");
12✔
1729
               try {
12✔
1730
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng));
12✔
1731
               } catch(std::exception& e) {
×
1732
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
1733
               }
×
1734
               results.push_back(extensions_result);
12✔
1735

1736
               Test::Result custom_dn_result("X509 Custom DN");
12✔
1737
               try {
12✔
1738
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng));
12✔
1739
               } catch(std::exception& e) {
×
1740
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1741
               }
×
1742
               results.push_back(custom_dn_result);
12✔
1743
            }
24✔
1744
         }
24✔
1745

1746
         /*
1747
         These are algos which cannot sign but can be included in certs
1748
         */
1749
         const std::vector<std::string> enc_algos = {"DH", "ECDH", "ElGamal", "Kyber", "ML-KEM", "FrodoKEM"};
1✔
1750

1751
         for(const std::string& algo : enc_algos) {
7✔
1752
            auto key = make_a_private_key(algo, rng);
6✔
1753

1754
            if(key) {
6✔
1755
               results.push_back(test_valid_constraints(*key, algo));
12✔
1756
            }
1757
         }
6✔
1758

1759
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1760
      defined(BOTAN_HAS_RSA)
1761
         Test::Result pad_config_result("X509 Padding Config");
1✔
1762
         try {
1✔
1763
            pad_config_result.merge(test_padding_config());
1✔
1764
         } catch(const std::exception& e) {
×
1765
            pad_config_result.test_failure("test_padding_config", e.what());
×
1766
         }
×
1767
         results.push_back(pad_config_result);
1✔
1768
   #endif
1769

1770
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1771
         results.push_back(test_x509_utf8());
2✔
1772
         results.push_back(test_x509_bmpstring());
2✔
1773
         results.push_back(test_x509_teletex());
2✔
1774
         results.push_back(test_crl_dn_name());
2✔
1775
         results.push_back(test_rdn_multielement_set_name());
2✔
1776
         results.push_back(test_x509_decode_list());
2✔
1777
         results.push_back(test_rsa_oaep());
2✔
1778
         results.push_back(test_x509_authority_info_access_extension());
2✔
1779
         results.push_back(test_verify_gost2012_cert());
2✔
1780
         results.push_back(test_parse_rsa_pss_cert());
2✔
1781
         results.push_back(test_x509_tn_auth_list_extension_decode());
2✔
1782
   #endif
1783

1784
         results.push_back(test_x509_encode_authority_info_access_extension());
2✔
1785
         results.push_back(test_x509_extension());
2✔
1786
         results.push_back(test_x509_dates());
2✔
1787
         results.push_back(test_cert_status_strings());
2✔
1788
         results.push_back(test_x509_uninit());
2✔
1789

1790
         return results;
1✔
1791
      }
13✔
1792
};
1793

1794
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1795

1796
#endif
1797

1798
}  // namespace
1799

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

© 2025 Coveralls, Inc