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

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

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

96.58
/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) {
240✔
30
   const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
240✔
31

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

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

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

45
   opts.CA_key(1);
89✔
46

47
   return opts;
89✔
48
}
×
49

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

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

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

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

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

69
   return opts;
27✔
70
}
×
71

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

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

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

82
   return opts;
9✔
83
}
×
84

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

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

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

97
   return opts;
45✔
98
}
×
99

100
std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo) {
74✔
101
   const std::string params = [&] {
74✔
102
      // Here we override defaults as needed
103
      if(algo == "RSA") {
118✔
104
         return "1024";
105
      }
106
      if(algo == "GOST-34.10") {
60✔
107
         return "gost_256A";
108
      }
109
      if(algo == "ECKCDSA" || algo == "ECGDSA") {
52✔
110
         return "brainpool256r1";
16✔
111
      }
112
      return "";  // default "" means choose acceptable algo-specific params
113
   }();
74✔
114

115
   return Botan::create_private_key(algo, Test::rng(), params);
74✔
116
}
74✔
117

118
Test::Result test_cert_status_strings() {
1✔
119
   Test::Result result("Certificate_Status_Code to_string");
1✔
120

121
   std::set<std::string> seen;
1✔
122

123
   result.test_eq("Same string",
1✔
124
                  Botan::to_string(Botan::Certificate_Status_Code::OK),
125
                  Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
126

127
   const Botan::Certificate_Status_Code codes[]{
1✔
128
      Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
129
      Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
130
      Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
131
      Botan::Certificate_Status_Code::OCSP_NO_HTTP,
132

133
      Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
134
      Botan::Certificate_Status_Code::DN_TOO_LONG,
135

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

176
   for(const auto code : codes) {
45✔
177
      const std::string s = Botan::to_string(code);
44✔
178
      result.confirm("String is long enough to be informative", s.size() > 12);
88✔
179
      result.test_eq("No duplicates", seen.count(s), 0);
44✔
180
      seen.insert(s);
44✔
181
   }
44✔
182

183
   return result;
1✔
184
}
1✔
185

186
Test::Result test_x509_extension() {
1✔
187
   Test::Result result("X509 Extensions API");
1✔
188

189
   Botan::Extensions extn;
1✔
190

191
   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
1✔
192
   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
1✔
193

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

196
   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
2✔
197
   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
2✔
198
   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
2✔
199
   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
2✔
200

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

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

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

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

210
   result.confirm("Returns false since extension already existed",
2✔
211
                  !extn.add_new(std::make_unique<Botan::Cert_Extension::Basic_Constraints>(false), false));
2✔
212

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

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

219
   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
2✔
220
   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
2✔
221
   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
2✔
222
   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
2✔
223

224
   return result;
2✔
225
}
2✔
226

227
Test::Result test_x509_dates() {
1✔
228
   Test::Result result("X509 Time");
1✔
229

230
   Botan::X509_Time time;
1✔
231
   result.confirm("unset time not set", !time.time_is_set());
2✔
232
   time = Botan::X509_Time("080201182200Z", Botan::ASN1_Type::UtcTime);
1✔
233
   result.confirm("time set after construction", time.time_is_set());
2✔
234
   result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
3✔
235

236
   time = Botan::X509_Time("200305100350Z", Botan::ASN1_Type::UtcTime);
1✔
237
   result.test_eq("UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
3✔
238

239
   time = Botan::X509_Time("200305100350Z");
1✔
240
   result.test_eq(
3✔
241
      "UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
2✔
242

243
   time = Botan::X509_Time("20200305100350Z");
1✔
244
   result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string",
3✔
245
                  time.readable_string(),
2✔
246
                  "2020/03/05 10:03:50 UTC");
247

248
   time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Type::GeneralizedTime);
1✔
249
   result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
3✔
250

251
   // Dates that are valid per X.500 but rejected as unsupported
252
   const std::string valid_but_unsup[]{
1✔
253
      "0802010000-0000",
254
      "0802011724+0000",
255
      "0406142334-0500",
256
      "9906142334+0500",
257
      "0006142334-0530",
258
      "0006142334+0530",
259

260
      "080201000000-0000",
261
      "080201172412+0000",
262
      "040614233433-0500",
263
      "990614233444+0500",
264
      "000614233455-0530",
265
      "000614233455+0530",
266
   };
13✔
267

268
   // valid length 13
269
   const std::string valid_utc[]{
1✔
270
      "080201000000Z",
271
      "080201172412Z",
272
      "040614233433Z",
273
      "990614233444Z",
274
      "000614233455Z",
275
   };
6✔
276

277
   const std::string invalid_utc[]{
1✔
278
      "",
279
      " ",
280
      "2008`02-01",
281
      "9999-02-01",
282
      "2000-02-01 17",
283
      "999921",
284

285
      // No seconds
286
      "0802010000Z",
287
      "0802011724Z",
288
      "0406142334Z",
289
      "9906142334Z",
290
      "0006142334Z",
291

292
      // valid length 13 -> range check
293
      "080201000061Z",  // seconds too big (61)
294
      "080201000060Z",  // seconds too big (60, leap seconds not covered by the standard)
295
      "0802010000-1Z",  // seconds too small (-1)
296
      "080201006000Z",  // minutes too big (60)
297
      "080201240000Z",  // hours too big (24:00)
298

299
      // valid length 13 -> invalid numbers
300
      "08020123112 Z",
301
      "08020123112!Z",
302
      "08020123112,Z",
303
      "08020123112\nZ",
304
      "080201232 33Z",
305
      "080201232!33Z",
306
      "080201232,33Z",
307
      "080201232\n33Z",
308
      "0802012 3344Z",
309
      "0802012!3344Z",
310
      "0802012,3344Z",
311
      "08022\n334455Z",
312
      "08022 334455Z",
313
      "08022!334455Z",
314
      "08022,334455Z",
315
      "08022\n334455Z",
316
      "082 33445511Z",
317
      "082!33445511Z",
318
      "082,33445511Z",
319
      "082\n33445511Z",
320
      "2 2211221122Z",
321
      "2!2211221122Z",
322
      "2,2211221122Z",
323
      "2\n2211221122Z",
324

325
      // wrong time zone
326
      "080201000000",
327
      "080201000000z",
328

329
      // Fractional seconds
330
      "170217180154.001Z",
331

332
      // Timezone offset
333
      "170217180154+0100",
334

335
      // Extra digits
336
      "17021718015400Z",
337

338
      // Non-digits
339
      "17021718015aZ",
340

341
      // Trailing garbage
342
      "170217180154Zlongtrailinggarbage",
343

344
      // Swapped type
345
      "20170217180154Z",
346
   };
49✔
347

348
   // valid length 15
349
   const std::string valid_generalized_time[]{
1✔
350
      "20000305100350Z",
351
   };
2✔
352

353
   const std::string invalid_generalized[]{
1✔
354
      // No trailing Z
355
      "20000305100350",
356

357
      // No seconds
358
      "200003051003Z",
359

360
      // Fractional seconds
361
      "20000305100350.001Z",
362

363
      // Timezone offset
364
      "20170217180154+0100",
365

366
      // Extra digits
367
      "2017021718015400Z",
368

369
      // Non-digits
370
      "2017021718015aZ",
371

372
      // Trailing garbage
373
      "20170217180154Zlongtrailinggarbage",
374

375
      // Swapped type
376
      "170217180154Z",
377
   };
9✔
378

379
   for(const auto& v : valid_but_unsup) {
13✔
380
      result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
126✔
381
   }
382

383
   for(const auto& v : valid_utc) {
6✔
384
      Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime);
5✔
385
   }
5✔
386

387
   for(const auto& v : valid_generalized_time) {
2✔
388
      Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime);
1✔
389
   }
1✔
390

391
   for(const auto& v : invalid_utc) {
49✔
392
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::UtcTime); });
441✔
393
   }
394

395
   for(const auto& v : invalid_generalized) {
9✔
396
      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Type::GeneralizedTime); });
84✔
397
   }
398

399
   return result;
1✔
400
}
80✔
401

402
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
403

404
Test::Result test_crl_dn_name() {
1✔
405
   Test::Result result("CRL DN name");
1✔
406

407
      // See GH #1252
408

409
      #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
410
   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
1✔
411

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

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

418
   Botan::X509_CRL crl = ca.new_crl(Test::rng());
1✔
419

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

422
   result.confirm("contains DC component", crl.issuer_dn().get_attributes().count(dc_oid) == 1);
4✔
423
      #endif
424

425
   return result;
1✔
426
}
2✔
427

428
Test::Result test_rdn_multielement_set_name() {
1✔
429
   Test::Result result("DN with multiple elements in RDN");
1✔
430

431
   // GH #2611
432

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

435
   result.confirm("issuer DN contains expected name components", cert.issuer_dn().get_attributes().size() == 4);
3✔
436
   result.confirm("subject DN contains expected name components", cert.subject_dn().get_attributes().size() == 4);
3✔
437

438
   return result;
1✔
439
}
1✔
440

441
Test::Result test_rsa_oaep() {
1✔
442
   Test::Result result("RSA OAEP decoding");
1✔
443

444
      #if defined(BOTAN_HAS_RSA)
445
   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
2✔
446

447
   auto public_key = cert.subject_public_key();
1✔
448
   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
1✔
449
   const auto& pk_info = cert.subject_public_key_algo();
1✔
450

451
   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
4✔
452
      #endif
453

454
   return result;
2✔
455
}
1✔
456

457
Test::Result test_x509_decode_list() {
1✔
458
   Test::Result result("X509_Certificate list decode");
1✔
459

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

462
   Botan::BER_Decoder dec(input);
1✔
463
   std::vector<Botan::X509_Certificate> certs;
1✔
464
   dec.decode_list(certs);
1✔
465

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

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

471
   return result;
1✔
472
}
1✔
473

474
Test::Result test_x509_utf8() {
1✔
475
   Test::Result result("X509 with UTF-8 encoded fields");
1✔
476

477
   try {
1✔
478
      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
479

480
      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
481
      const std::string organization =
1✔
482
         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
483
         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
1✔
484
      const std::string organization_unit =
1✔
485
         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
486
         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
1✔
487
      const std::string common_name =
1✔
488
         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
489
         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
1✔
490
      const std::string location = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
491

492
      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
1✔
493

494
      result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
2✔
495
      result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
2✔
496
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
2✔
497
      result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
2✔
498
   } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); }
4✔
499

500
   return result;
1✔
501
}
×
502

503
Test::Result test_x509_bmpstring() {
1✔
504
   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
1✔
505

506
   try {
1✔
507
      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
2✔
508

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

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

517
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
518

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

524
   return result;
1✔
525
}
×
526

527
Test::Result test_x509_teletex() {
1✔
528
   Test::Result result("X509 with TeletexString encoded fields");
1✔
529

530
   try {
1✔
531
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
532

533
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
534

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

537
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
538
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
3✔
539
   } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); }
1✔
540

541
   return result;
1✔
542
}
×
543

544
Test::Result test_x509_authority_info_access_extension() {
1✔
545
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
546

547
   // contains no AIA extension
548
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
549

550
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
551
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
552

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

556
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
557

558
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
559
   if(result.tests_failed())
1✔
560
      return result;
561

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

565
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
566
   Botan::X509_Certificate aia_cert_2ca(
1✔
567
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
568

569
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
570

571
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
572
   if(result.tests_failed())
1✔
573
      return result;
574

575
   result.test_eq(
2✔
576
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
577
   result.test_eq(
2✔
578
      "CA issuer URL matches",
579
      ca_issuers2[1],
1✔
580
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
581
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
3✔
582

583
   return result;
1✔
584
}
1✔
585

586
Test::Result test_parse_rsa_pss_cert() {
1✔
587
   Test::Result result("X509 RSA-PSS certificate");
1✔
588

589
   // See https://github.com/randombit/botan/issues/3019 for background
590

591
   try {
1✔
592
      Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
593
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
594
   } catch(Botan::Exception& e) { result.test_failure("Parsing failed", e.what()); }
1✔
595

596
   return result;
1✔
597
}
×
598

599
Test::Result test_verify_gost2012_cert() {
1✔
600
   Test::Result result("X509 GOST-2012 certificates");
1✔
601

602
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
603
   try {
1✔
604
      Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
605
      Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
606

607
      Botan::Certificate_Store_In_Memory trusted;
1✔
608
      trusted.add_certificate(root_cert);
1✔
609

610
      const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
3✔
611
      const Botan::Path_Validation_Result validation_result =
1✔
612
         Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
613

614
      result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
615
   } catch(const Botan::Decoding_Error& e) { result.test_failure(e.what()); }
1✔
616
      #endif
617

618
   return result;
1✔
619
}
×
620

621
      /*
622
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
623
 *
624
 * For the other algorithms than RSA, only one padding is supported right now.
625
 */
626
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
627
Test::Result test_padding_config() {
1✔
628
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
629
   Test::Result test_result("X509 Padding Config");
1✔
630

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

634
   // Create X509 CA certificate; EMSA3 is used for signing by default
635
   Botan::X509_Cert_Options opt("TESTCA");
1✔
636
   opt.CA_key();
1✔
637

638
   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
1✔
639
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
640
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
641
                       "RSA/EMSA3(SHA-512)");
642

643
   // Create X509 CA certificate; RSA-PSS is explicitly set
644
   opt.set_padding_scheme("PSSR");
1✔
645
   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
1✔
646
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
647
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
648
                       "RSA/EMSA4");
649

650
         #if defined(BOTAN_HAS_EMSA2)
651
   // Try to set a padding scheme that is not supported for signing with the given key type
652
   opt.set_padding_scheme("EMSA2");
653
   try {
654
      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
655
      test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " +
656
                               sk->algo_name());
657
   } catch(const Botan::Invalid_Argument& e) {
658
      test_result.test_eq("Build CA certificate with invalid encoding scheme EMSA1 for key type " + sk->algo_name(),
659
                          e.what(),
660
                          "Signatures using RSA/EMSA2(SHA-512) are not supported");
661
   }
662
         #endif
663

664
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
665
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
666
                       "RSA/EMSA4");
667

668
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
669
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
670

671
   // Prepare a signing request for the end certificate
672
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
673
   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
1✔
674
   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
1✔
675
   test_result.test_eq("Certificate request signature algorithm",
3✔
676
                       end_req.signature_algorithm().oid().to_formatted_string(),
2✔
677
                       "RSA/EMSA4");
678

679
   // Create X509 CA object: will fail as the chosen hash functions differ
680
   try {
1✔
681
      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", Test::rng());
1✔
682
      test_result.test_failure("Configured conflicting hash functions for CA");
×
683
   } catch(const Botan::Invalid_Argument& e) {
1✔
684
      test_result.test_eq(
1✔
685
         "Configured conflicting hash functions for CA",
686
         e.what(),
1✔
687
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding EMSA4(SHA-256)");
688
   }
1✔
689

690
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
691
   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
1✔
692
   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after);
1✔
693
   test_result.test_eq("End certificate signature algorithm",
3✔
694
                       end_cert_emsa3.signature_algorithm().oid().to_formatted_string(),
2✔
695
                       "RSA/EMSA3(SHA-512)");
696

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

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

711
   // Check CRL signature algorithm
712
   Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
1✔
713
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4");
2✔
714

715
   // sanity check for verification, the heavy lifting is done in the other unit tests
716
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
717
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
718
   const Botan::Path_Validation_Result validation_result =
1✔
719
      Botan::x509_path_validate(end_cert_emsa4, restrictions, trusted);
1✔
720
   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
2✔
721

722
   return test_result;
1✔
723
}
2✔
724
      #endif
725

726
   #endif
727

728
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
9✔
729
                             const std::string& sig_padding,
730
                             const std::string& hash_fn) {
731
   Test::Result result("PKCS10 extensions");
9✔
732

733
   Botan::X509_Cert_Options opts;
9✔
734

735
   opts.padding_scheme = sig_padding;
9✔
736

737
   Botan::AlternativeName alt_name;
9✔
738
   alt_name.add_attribute("DNS", "example.org");
9✔
739
   alt_name.add_attribute("DNS", "example.com");
9✔
740
   alt_name.add_attribute("DNS", "example.net");
9✔
741

742
   opts.extensions.add(std::make_unique<Botan::Cert_Extension::Subject_Alternative_Name>(alt_name));
18✔
743

744
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, key, hash_fn, Test::rng());
9✔
745

746
   std::vector<std::string> alt_dns_names = req.subject_alt_name().get_attribute("DNS");
9✔
747

748
   result.test_eq("Expected number of DNS names", alt_dns_names.size(), 3);
9✔
749

750
   // The order is not guaranteed so sort before comparing
751
   std::sort(alt_dns_names.begin(), alt_dns_names.end());
9✔
752

753
   result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "example.com");
27✔
754
   result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "example.net");
27✔
755
   result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "example.org");
27✔
756

757
   return result;
9✔
758
}
9✔
759

760
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
9✔
761
                            const std::string& sig_algo,
762
                            const std::string& sig_padding,
763
                            const std::string& hash_fn) {
764
   Test::Result result("X509 Unit");
9✔
765

766
   /* Create the self-signed cert */
767
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
768

769
   {
9✔
770
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
18✔
771
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
18✔
772
   }
773

774
   /* Create user #1's key and cert request */
775
   auto user1_key = make_a_private_key(sig_algo);
9✔
776

777
   Botan::PKCS10_Request user1_req =
9✔
778
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
9✔
779

780
   result.test_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom");
18✔
781

782
   /* Create user #2's key and cert request */
783
   auto user2_key = make_a_private_key(sig_algo);
9✔
784

785
   Botan::PKCS10_Request user2_req =
9✔
786
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, Test::rng());
9✔
787

788
   // /* Create user #3's key and cert request */
789
   auto user3_key = make_a_private_key(sig_algo);
9✔
790

791
   Botan::PKCS10_Request user3_req =
9✔
792
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, Test::rng());
9✔
793

794
   /* Create the CA object */
795
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
796

797
   const BigInt user1_serial = 99;
9✔
798

799
   /* Sign the requests to create the certs */
800
   Botan::X509_Certificate user1_cert =
9✔
801
      ca.sign_request(user1_req, Test::rng(), user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
802

803
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
9✔
804
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
9✔
805

806
   Botan::X509_Certificate user2_cert =
9✔
807
      ca.sign_request(user2_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
808

809
   Botan::X509_Certificate user3_cert =
9✔
810
      ca.sign_request(user3_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
811

812
   // user#1 creates a self-signed cert on the side
813
   const auto user1_ss_cert =
9✔
814
      Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
9✔
815

816
   {
9✔
817
      auto constraints = req_opts1(sig_algo).constraints;
9✔
818
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
18✔
819
   }
820

821
   /* Copy, assign and compare */
822
   Botan::X509_Certificate user1_cert_copy(user1_cert);
9✔
823
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
9✔
824

825
   user1_cert_copy = user2_cert;
9✔
826
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
9✔
827

828
   Botan::X509_Certificate user1_cert_differ =
9✔
829
      ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
830

831
   result.test_eq("certificate differs", user1_cert == user1_cert_differ, false);
9✔
832

833
   /* Get cert data */
834
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
9✔
835

836
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
9✔
837
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
18✔
838
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
18✔
839
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
18✔
840
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
18✔
841

842
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
9✔
843
   result.test_eq("subject OrgaUnit count",
9✔
844
                  user3_subject_dn.get_attribute("OU").size(),
18✔
845
                  req_opts3(sig_algo).more_org_units.size() + 1);
18✔
846
   result.test_eq(
9✔
847
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
36✔
848

849
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
9✔
850
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
27✔
851
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
27✔
852
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
27✔
853

854
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
9✔
855
   result.test_eq(
9✔
856
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
18✔
857
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
36✔
858

859
   const Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
9✔
860

861
   /* Verify the certs */
862
   Botan::Path_Validation_Restrictions restrictions(false, 80);
18✔
863
   Botan::Certificate_Store_In_Memory store;
9✔
864

865
   // First try with an empty store
866
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
867
   result.test_eq("user 1 issuer not found",
27✔
868
                  result_no_issuer.result_string(),
18✔
869
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
870

871
   store.add_certificate(ca.ca_certificate());
9✔
872

873
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
874
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
27✔
875
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
876
   }
877

878
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
9✔
879
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
27✔
880
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
881
   }
882

883
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
9✔
884
   result.test_eq("user 1 issuer not found",
27✔
885
                  result_no_issuer.result_string(),
18✔
886
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
887
   store.add_crl(crl1);
9✔
888

889
   std::vector<Botan::CRL_Entry> revoked;
9✔
890
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
18✔
891
   revoked.push_back(user2_cert);
18✔
892

893
   const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng());
9✔
894

895
   store.add_crl(crl2);
9✔
896

897
   const std::string revoked_str =
9✔
898
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
9✔
899

900
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
901
   result.test_eq("user 1 revoked", result_u1.result_string(), revoked_str);
18✔
902

903
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
9✔
904
   result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
18✔
905

906
   revoked.clear();
9✔
907
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
18✔
908
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, Test::rng());
9✔
909

910
   store.add_crl(crl3);
9✔
911

912
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
913
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
27✔
914
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
915
   }
916

917
   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
9✔
918
   result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
18✔
919

920
   return result;
9✔
921
}
54✔
922

923
Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& hash_fn) {
8✔
924
   using Botan::Key_Constraints;
8✔
925
   using Botan::Usage_Type;
8✔
926

927
   Test::Result result("X509 Usage");
8✔
928

929
   /* Create the self-signed cert */
930
   const Botan::X509_Certificate ca_cert =
8✔
931
      Botan::X509::create_self_signed_cert(ca_opts(), ca_key, hash_fn, Test::rng());
16✔
932

933
   /* Create the CA object */
934
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, Test::rng());
8✔
935

936
   auto user1_key = make_a_private_key(sig_algo);
8✔
937

938
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
8✔
939
   opts.constraints = Key_Constraints::DigitalSignature;
8✔
940

941
   const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
8✔
942

943
   const Botan::X509_Certificate user1_cert =
8✔
944
      ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
945

946
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
947
   result.test_eq(
8✔
948
      "key usage cRLSign not allowed",
949
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
8✔
950
      false);
951
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
8✔
952

953
   // cert only allows digitalSignature, so checking for only that should be ok
954
   result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DigitalSignature));
16✔
955

956
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
8✔
957

958
   const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
8✔
959

960
   const Botan::X509_Certificate mult_usage_cert =
8✔
961
      ca.sign_request(mult_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
962

963
   // cert allows multiple usages, so each one of them as well as both together should be allowed
964
   result.confirm("key usage multiple digitalSignature allowed",
16✔
965
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
8✔
966
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
16✔
967
   result.confirm(
16✔
968
      "key usage multiple digitalSignature and cRLSign allowed",
969
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
8✔
970
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
8✔
971

972
   opts.constraints = Key_Constraints();
8✔
973

974
   const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
8✔
975

976
   const Botan::X509_Certificate no_usage_cert =
8✔
977
      ca.sign_request(no_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
978

979
   // cert allows every usage
980
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
16✔
981
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
16✔
982
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
16✔
983

984
   if(sig_algo == "RSA") {
8✔
985
      // cert allows data encryption
986
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
987

988
      const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
1✔
989

990
      const Botan::X509_Certificate enc_cert =
1✔
991
         ca.sign_request(enc_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
992

993
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
994
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
995
   }
1✔
996

997
   return result;
8✔
998
}
16✔
999

1000
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
9✔
1001
                              const std::string& sig_algo,
1002
                              const std::string& sig_padding,
1003
                              const std::string& hash_fn) {
1004
   using Botan::Key_Constraints;
9✔
1005

1006
   Test::Result result("X509 Self Issued");
9✔
1007

1008
   // create the self-signed cert
1009
   const Botan::X509_Certificate ca_cert =
9✔
1010
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1011

1012
   /* Create the CA object */
1013
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1014

1015
   auto user_key = make_a_private_key(sig_algo);
9✔
1016

1017
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1018
   // but signed by a CA, not signed by it's own private key
1019
   Botan::X509_Cert_Options opts = ca_opts();
9✔
1020
   opts.constraints = Key_Constraints::DigitalSignature;
9✔
1021
   opts.set_padding_scheme(sig_padding);
9✔
1022

1023
   const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
9✔
1024

1025
   const Botan::X509_Certificate self_issued_cert =
9✔
1026
      ca.sign_request(self_issued_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
1027

1028
   // check that this chain can can be verified successfully
1029
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
9✔
1030

1031
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
18✔
1032

1033
   const Botan::Path_Validation_Result validation_result =
9✔
1034
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
9✔
1035

1036
   result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
18✔
1037

1038
   return result;
9✔
1039
}
18✔
1040

1041
Test::Result test_x509_uninit() {
1✔
1042
   Test::Result result("X509 object uninitialized access");
1✔
1043

1044
   Botan::X509_Certificate cert;
1✔
1045
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
3✔
1046
      cert.x509_version();
1✔
1047
   });
1048

1049
   Botan::X509_CRL crl;
1✔
1050
   result.test_throws(
3✔
1051
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
1✔
1052

1053
   return result;
1✔
1054
}
1✔
1055

1056
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
12✔
1057
   using Botan::Key_Constraints;
12✔
1058

1059
   Test::Result result("X509 Valid Constraints " + pk_algo);
12✔
1060

1061
   result.confirm("empty constraints always acceptable", Key_Constraints().compatible_with(key));
24✔
1062

1063
   // Now check some typical usage scenarios for the given key type
1064
   // Taken from RFC 5280, sec. 4.2.1.3
1065
   // ALL constraints are not typical at all, but we use them for a negative test
1066
   const auto all = Key_Constraints(
12✔
1067
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1068
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1069
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
12✔
1070

1071
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
12✔
1072
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
12✔
1073
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
12✔
1074
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
12✔
1075
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
12✔
1076
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
12✔
1077
   const auto key_agreement_encipher_only =
12✔
1078
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
12✔
1079
   const auto key_agreement_decipher_only =
12✔
1080
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
12✔
1081
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
12✔
1082
   const auto sign_everything =
12✔
1083
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
12✔
1084

1085
   if(pk_algo == "DH" || pk_algo == "ECDH") {
12✔
1086
      // DH and ECDH only for key agreement
1087
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1088
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1089
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1090
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1091
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1092
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1093
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1094
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1095
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1096
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1097
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1098
   } else if(pk_algo == "Kyber") {
10✔
1099
      // Kyber can encrypt and agree
1100
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1101
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1102
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
1✔
1103
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
1✔
1104
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1105
      result.test_eq("sign", sign_everything.compatible_with(key), false);
1✔
1106
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1107
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1108
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
2✔
1109
   } else if(pk_algo == "RSA") {
9✔
1110
      // RSA can do everything except key agreement
1111
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1112

1113
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1114
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1115
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1116
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1117
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1118
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1119
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1120
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1121
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1122
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1123
   } else if(pk_algo == "ElGamal") {
8✔
1124
      // only ElGamal encryption is currently implemented
1125
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1126
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1127
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1128
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1129
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1130
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1131
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1132
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1133
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1134
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
7✔
1135
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium") {
10✔
1136
      // these are signature algorithms only
1137
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
6✔
1138

1139
      result.test_eq("ca allowed", ca.compatible_with(key), true);
6✔
1140
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
6✔
1141
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
6✔
1142
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
6✔
1143
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
6✔
1144
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
6✔
1145
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
6✔
1146
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
6✔
1147
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
6✔
1148
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
12✔
1149
   }
1150

1151
   return result;
12✔
1152
}
×
1153

1154
/**
1155
 * @brief X.509v3 extension that encodes a given string
1156
 */
1157
class String_Extension final : public Botan::Certificate_Extension {
1158
   public:
1159
      String_Extension() = default;
18✔
1160

1161
      explicit String_Extension(const std::string& val) : m_contents(val) {}
9✔
1162

1163
      std::string value() const { return m_contents; }
18✔
1164

1165
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1166
         return std::make_unique<String_Extension>(m_contents);
×
1167
      }
1168

1169
      Botan::OID oid_of() const override { return Botan::OID("1.2.3.4.5.6.7.8.9.1"); }
18✔
1170

1171
      bool should_encode() const override { return true; }
18✔
1172

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

1175
      std::vector<uint8_t> encode_inner() const override {
9✔
1176
         std::vector<uint8_t> bits;
9✔
1177
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
9✔
1178
         return bits;
9✔
1179
      }
×
1180

1181
      void decode_inner(const std::vector<uint8_t>& in) override {
18✔
1182
         Botan::ASN1_String str;
18✔
1183
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
18✔
1184
         m_contents = str.value();
18✔
1185
      }
18✔
1186

1187
   private:
1188
      std::string m_contents;
1189
};
1190

1191
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
9✔
1192
                                 const std::string& sig_algo,
1193
                                 const std::string& sig_padding,
1194
                                 const std::string& hash_fn) {
1195
   Test::Result result("X509 Custom DN");
9✔
1196

1197
   /* Create the self-signed cert */
1198
   Botan::X509_Certificate ca_cert =
9✔
1199
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1200

1201
   /* Create the CA object */
1202
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1203

1204
   auto user_key = make_a_private_key(sig_algo);
9✔
1205

1206
   Botan::X509_DN subject_dn;
9✔
1207

1208
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
9✔
1209
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
9✔
1210
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
9✔
1211
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
9✔
1212

1213
   subject_dn.add_attribute(attr1, val1);
9✔
1214
   subject_dn.add_attribute(attr2, val2);
9✔
1215

1216
   Botan::Extensions extensions;
9✔
1217

1218
   Botan::PKCS10_Request req =
9✔
1219
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, Test::rng(), sig_padding);
9✔
1220

1221
   const Botan::X509_DN& req_dn = req.subject_dn();
9✔
1222

1223
   result.test_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
9✔
1224

1225
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
9✔
1226
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
9✔
1227
   result.confirm("Attr1 matches encoded", req_val1 == val1);
18✔
1228
   result.confirm("Attr2 matches encoded", req_val2 == val2);
18✔
1229
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
18✔
1230
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
18✔
1231

1232
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
9✔
1233
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
9✔
1234

1235
   auto cert = ca.sign_request(req, Test::rng(), not_before, not_after);
9✔
1236

1237
   const Botan::X509_DN& cert_dn = cert.subject_dn();
9✔
1238

1239
   result.test_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
9✔
1240

1241
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
9✔
1242
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
9✔
1243
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
18✔
1244
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
18✔
1245
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
18✔
1246
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
18✔
1247

1248
   return result;
9✔
1249
}
27✔
1250

1251
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
9✔
1252
                                  const std::string& sig_algo,
1253
                                  const std::string& sig_padding,
1254
                                  const std::string& hash_fn) {
1255
   using Botan::Key_Constraints;
9✔
1256

1257
   Test::Result result("X509 Extensions");
9✔
1258

1259
   /* Create the self-signed cert */
1260
   Botan::X509_Certificate ca_cert =
9✔
1261
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1262

1263
   /* Create the CA object */
1264
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1265

1266
   auto user_key = make_a_private_key(sig_algo);
9✔
1267

1268
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
9✔
1269
   opts.constraints = Key_Constraints::DigitalSignature;
9✔
1270

1271
   // include a custom extension in the request
1272
   Botan::Extensions req_extensions;
9✔
1273
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
9✔
1274
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
9✔
1275
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
18✔
1276
   opts.extensions = req_extensions;
9✔
1277
   opts.set_padding_scheme(sig_padding);
9✔
1278

1279
   /* Create a self-signed certificate */
1280
   const Botan::X509_Certificate self_signed_cert =
9✔
1281
      Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, Test::rng());
9✔
1282

1283
   result.confirm("Extensions::extension_set true for Key_Usage",
18✔
1284
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
9✔
1285

1286
   // check if known Key_Usage extension is present in self-signed cert
1287
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
9✔
1288
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
27✔
1289
      result.confirm(
27✔
1290
         "Key_Usage extension value matches in self-signed certificate",
1291
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
9✔
1292
   }
1293

1294
   // check if custom extension is present in self-signed cert
1295
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
9✔
1296
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
27✔
1297
      result.test_eq(
36✔
1298
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
27✔
1299
   }
1300

1301
   const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
9✔
1302

1303
   /* Create a CA-signed certificate */
1304
   const Botan::X509_Certificate ca_signed_cert =
9✔
1305
      ca.sign_request(user_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
1306

1307
   // check if known Key_Usage extension is present in CA-signed cert
1308
   result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
18✔
1309

1310
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
18✔
1311
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
27✔
1312
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
9✔
1313
      result.confirm("Key_Usage extension value matches in user certificate",
27✔
1314
                     constraints == Botan::Key_Constraints::DigitalSignature);
9✔
1315
   }
1316

1317
   // check if custom extension is present in CA-signed cert
1318
   result.confirm("Extensions::extension_set true for String_Extension",
18✔
1319
                  ca_signed_cert.v3_extensions().extension_set(oid));
9✔
1320
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
18✔
1321
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
27✔
1322
      result.test_eq(
36✔
1323
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
27✔
1324
   }
1325

1326
   return result;
9✔
1327
}
45✔
1328

1329
Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn) {
8✔
1330
   Test::Result result("X509 Hashes");
8✔
1331

1332
   struct TestData {
8✔
1333
         const std::string issuer, subject, issuer_hash, subject_hash;
1334
   } const cases[]{{"",
1335
                    "",
1336
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1337
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1338
                   {"a",
1339
                    "b",
1340
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1341
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1342
                   {"A",
1343
                    "B",
1344
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1345
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1346
                   {
1347
                      "Test Issuer/US/Botan Project/Testing",
1348
                      "Test Subject/US/Botan Project/Testing",
1349
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1350
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1351
                   },
1352
                   {
1353
                      "Test Subject/US/Botan Project/Testing",
1354
                      "Test Issuer/US/Botan Project/Testing",
1355
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1356
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1357
                   }};
48✔
1358

1359
   for(const auto& a : cases) {
48✔
1360
      Botan::X509_Cert_Options opts{a.issuer};
40✔
1361
      opts.CA_key();
40✔
1362

1363
      const Botan::X509_Certificate issuer_cert = Botan::X509::create_self_signed_cert(opts, key, hash_fn, Test::rng());
40✔
1364

1365
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
80✔
1366
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
80✔
1367

1368
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, Test::rng());
40✔
1369
      const Botan::PKCS10_Request req =
40✔
1370
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, Test::rng());
40✔
1371
      const Botan::X509_Certificate subject_cert =
40✔
1372
         ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
40✔
1373

1374
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
80✔
1375
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
80✔
1376
   }
40✔
1377
   return result;
8✔
1378
}
48✔
1379

1380
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
8✔
1381
   if(sig_algo == "RSA")
8✔
1382
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
3✔
1383
   else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
7✔
1384
           sig_algo == "GOST-34.10")
3✔
1385
      return {hash};
10✔
1386
   else if(sig_algo == "Ed25519")
2✔
1387
      return {"Pure"};
2✔
1388
   else if(sig_algo == "Dilithium")
1✔
1389
      return {"Randomized"};
2✔
1390
   else
1391
      return {};
8✔
1392
}
1393

1394
class X509_Cert_Unit_Tests final : public Test {
×
1395
   public:
1396
      std::vector<Test::Result> run() override {
1✔
1397
         std::vector<Test::Result> results;
1✔
1398

1399
         const std::string sig_algos[]{
1✔
1400
            "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10", "Ed25519", "Dilithium"};
9✔
1401

1402
         for(const std::string& algo : sig_algos) {
9✔
1403
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1404
            if(algo == "RSA")
1405
               continue;
1406
   #endif
1407

1408
            std::string hash = "SHA-256";
8✔
1409

1410
            if(algo == "Ed25519")
8✔
1411
               hash = "SHA-512";
1✔
1412
            if(algo == "Dilithium")
8✔
1413
               hash = "SHAKE-256(512)";
1✔
1414

1415
            auto key = make_a_private_key(algo);
8✔
1416

1417
            if(key == nullptr)
8✔
1418
               continue;
×
1419

1420
            results.push_back(test_hashes(*key, hash));
16✔
1421
            results.push_back(test_valid_constraints(*key, algo));
16✔
1422

1423
            Test::Result usage_result("X509 Usage");
8✔
1424
            try {
8✔
1425
               usage_result.merge(test_usage(*key, algo, hash));
8✔
1426
            } catch(std::exception& e) { usage_result.test_failure("test_usage " + algo, e.what()); }
×
1427
            results.push_back(usage_result);
8✔
1428

1429
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
17✔
1430
               Test::Result cert_result("X509 Unit");
9✔
1431

1432
               try {
9✔
1433
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash));
9✔
1434
               } catch(std::exception& e) { cert_result.test_failure("test_x509_cert " + algo, e.what()); }
×
1435
               results.push_back(cert_result);
9✔
1436

1437
               Test::Result pkcs10_result("PKCS10 extensions");
9✔
1438
               try {
9✔
1439
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash));
9✔
1440
               } catch(std::exception& e) { pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what()); }
×
1441
               results.push_back(pkcs10_result);
9✔
1442

1443
               Test::Result self_issued_result("X509 Self Issued");
9✔
1444
               try {
9✔
1445
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash));
9✔
1446
               } catch(std::exception& e) { self_issued_result.test_failure("test_self_issued " + algo, e.what()); }
×
1447
               results.push_back(self_issued_result);
9✔
1448

1449
               Test::Result extensions_result("X509 Extensions");
9✔
1450
               try {
9✔
1451
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash));
9✔
1452
               } catch(std::exception& e) { extensions_result.test_failure("test_extensions " + algo, e.what()); }
×
1453
               results.push_back(extensions_result);
9✔
1454

1455
               Test::Result custom_dn_result("X509 Custom DN");
9✔
1456
               try {
9✔
1457
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash));
9✔
1458
               } catch(std::exception& e) { custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what()); }
×
1459
               results.push_back(custom_dn_result);
9✔
1460
            }
17✔
1461
         }
16✔
1462

1463
         /*
1464
         These are algos which cannot sign but can be included in certs
1465
         */
1466
         const std::vector<std::string> enc_algos = {"DH", "ECDH", "ElGamal", "Kyber"};
5✔
1467

1468
         for(const std::string& algo : enc_algos) {
5✔
1469
            auto key = make_a_private_key(algo);
4✔
1470

1471
            if(key) {
4✔
1472
               results.push_back(test_valid_constraints(*key, algo));
8✔
1473
            }
1474
         }
4✔
1475

1476
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1477
      defined(BOTAN_HAS_RSA)
1478
         Test::Result pad_config_result("X509 Padding Config");
1✔
1479
         try {
1✔
1480
            pad_config_result.merge(test_padding_config());
1✔
1481
         } catch(const std::exception& e) { pad_config_result.test_failure("test_padding_config", e.what()); }
×
1482
         results.push_back(pad_config_result);
1✔
1483
   #endif
1484

1485
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1486
         results.push_back(test_x509_utf8());
2✔
1487
         results.push_back(test_x509_bmpstring());
2✔
1488
         results.push_back(test_x509_teletex());
2✔
1489
         results.push_back(test_crl_dn_name());
2✔
1490
         results.push_back(test_rdn_multielement_set_name());
2✔
1491
         results.push_back(test_x509_decode_list());
2✔
1492
         results.push_back(test_rsa_oaep());
2✔
1493
         results.push_back(test_x509_authority_info_access_extension());
2✔
1494
         results.push_back(test_verify_gost2012_cert());
2✔
1495
         results.push_back(test_parse_rsa_pss_cert());
2✔
1496
   #endif
1497

1498
         results.push_back(test_x509_extension());
2✔
1499
         results.push_back(test_x509_dates());
2✔
1500
         results.push_back(test_cert_status_strings());
2✔
1501
         results.push_back(test_x509_uninit());
2✔
1502

1503
         return results;
1✔
1504
      }
9✔
1505
};
1506

1507
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1508

1509
#endif
1510

1511
}
1512

1513
}
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