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

randombit / botan / 5134090420

31 May 2023 03:12PM UTC coverage: 91.721% (-0.3%) from 91.995%
5134090420

push

github

randombit
Merge GH #3565 Disable noisy/pointless pylint warnings

76048 of 82912 relevant lines covered (91.72%)

11755290.1 hits per line

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

93.73
/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) {
4✔
499
      result.test_failure(ex.what());
×
500
   }
×
501

502
   return result;
1✔
503
}
×
504

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

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

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

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

519
      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
1✔
520

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

528
   return result;
1✔
529
}
×
530

531
Test::Result test_x509_teletex() {
1✔
532
   Test::Result result("X509 with TeletexString encoded fields");
1✔
533

534
   try {
1✔
535
      Botan::X509_Certificate teletex_cert(Test::data_file("x509/misc/teletex_dn.der"));
2✔
536

537
      const Botan::X509_DN& issuer_dn = teletex_cert.issuer_dn();
1✔
538

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

541
      result.test_eq("O", issuer_dn.get_first_attribute("O"), "neam CA");
2✔
542
      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
3✔
543
   } catch(const Botan::Decoding_Error& ex) {
1✔
544
      result.test_failure(ex.what());
×
545
   }
×
546

547
   return result;
1✔
548
}
×
549

550
Test::Result test_x509_authority_info_access_extension() {
1✔
551
   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
1✔
552

553
   // contains no AIA extension
554
   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
2✔
555

556
   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
2✔
557
   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
2✔
558

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

562
   const auto ca_issuers = aia_cert.ca_issuers();
1✔
563

564
   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
1✔
565
   if(result.tests_failed()) {
1✔
566
      return result;
567
   }
568

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

572
   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
573
   Botan::X509_Certificate aia_cert_2ca(
1✔
574
      Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
2✔
575

576
   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
1✔
577

578
   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
1✔
579
   if(result.tests_failed()) {
1✔
580
      return result;
581
   }
582

583
   result.test_eq(
2✔
584
      "CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
1✔
585
   result.test_eq(
2✔
586
      "CA issuer URL matches",
587
      ca_issuers2[1],
1✔
588
      "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
589
   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
3✔
590

591
   return result;
1✔
592
}
1✔
593

594
Test::Result test_parse_rsa_pss_cert() {
1✔
595
   Test::Result result("X509 RSA-PSS certificate");
1✔
596

597
   // See https://github.com/randombit/botan/issues/3019 for background
598

599
   try {
1✔
600
      Botan::X509_Certificate rsa_pss(Test::data_file("x509/misc/rsa_pss.pem"));
2✔
601
      result.test_success("Was able to parse RSA-PSS certificate signed with ECDSA");
1✔
602
   } catch(Botan::Exception& e) {
1✔
603
      result.test_failure("Parsing failed", e.what());
×
604
   }
×
605

606
   return result;
1✔
607
}
×
608

609
Test::Result test_verify_gost2012_cert() {
1✔
610
   Test::Result result("X509 GOST-2012 certificates");
1✔
611

612
      #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG)
613
   try {
1✔
614
      Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
2✔
615
      Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
2✔
616

617
      Botan::Certificate_Store_In_Memory trusted;
1✔
618
      trusted.add_certificate(root_cert);
1✔
619

620
      const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
3✔
621
      const Botan::Path_Validation_Result validation_result =
1✔
622
         Botan::x509_path_validate(root_int, restrictions, trusted);
1✔
623

624
      result.confirm("GOST certificate validates", validation_result.successful_validation());
2✔
625
   } catch(const Botan::Decoding_Error& e) {
1✔
626
      result.test_failure(e.what());
×
627
   }
×
628
      #endif
629

630
   return result;
1✔
631
}
×
632

633
      /*
634
 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
635
 *
636
 * For the other algorithms than RSA, only one padding is supported right now.
637
 */
638
      #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
639
Test::Result test_padding_config() {
1✔
640
   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
641
   Test::Result test_result("X509 Padding Config");
1✔
642

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

646
   // Create X509 CA certificate; EMSA3 is used for signing by default
647
   Botan::X509_Cert_Options opt("TESTCA");
1✔
648
   opt.CA_key();
1✔
649

650
   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
1✔
651
   test_result.test_eq("CA certificate signature algorithm (default)",
3✔
652
                       ca_cert_def.signature_algorithm().oid().to_formatted_string(),
2✔
653
                       "RSA/EMSA3(SHA-512)");
654

655
   // Create X509 CA certificate; RSA-PSS is explicitly set
656
   opt.set_padding_scheme("PSSR");
1✔
657
   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
1✔
658
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
659
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
660
                       "RSA/EMSA4");
661

662
         #if defined(BOTAN_HAS_EMSA2)
663
   // Try to set a padding scheme that is not supported for signing with the given key type
664
   opt.set_padding_scheme("EMSA2");
665
   try {
666
      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
667
      test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " +
668
                               sk->algo_name());
669
   } catch(const Botan::Invalid_Argument& e) {
670
      test_result.test_eq("Build CA certificate with invalid encoding scheme EMSA1 for key type " + sk->algo_name(),
671
                          e.what(),
672
                          "Signatures using RSA/EMSA2(SHA-512) are not supported");
673
   }
674
         #endif
675

676
   test_result.test_eq("CA certificate signature algorithm (explicit)",
3✔
677
                       ca_cert_exp.signature_algorithm().oid().to_formatted_string(),
2✔
678
                       "RSA/EMSA4");
679

680
   const Botan::X509_Time not_before = from_date(-1, 1, 1);
1✔
681
   const Botan::X509_Time not_after = from_date(2, 1, 2);
1✔
682

683
   // Prepare a signing request for the end certificate
684
   Botan::X509_Cert_Options req_opt("endpoint");
1✔
685
   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
1✔
686
   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
1✔
687
   test_result.test_eq("Certificate request signature algorithm",
3✔
688
                       end_req.signature_algorithm().oid().to_formatted_string(),
2✔
689
                       "RSA/EMSA4");
690

691
   // Create X509 CA object: will fail as the chosen hash functions differ
692
   try {
1✔
693
      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", Test::rng());
1✔
694
      test_result.test_failure("Configured conflicting hash functions for CA");
×
695
   } catch(const Botan::Invalid_Argument& e) {
1✔
696
      test_result.test_eq(
1✔
697
         "Configured conflicting hash functions for CA",
698
         e.what(),
1✔
699
         "Specified hash function SHA-512 is incompatible with RSA chose hash function SHA-256 with user specified padding EMSA4(SHA-256)");
700
   }
1✔
701

702
   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
703
   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
1✔
704
   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after);
1✔
705
   test_result.test_eq("End certificate signature algorithm",
3✔
706
                       end_cert_emsa3.signature_algorithm().oid().to_formatted_string(),
2✔
707
                       "RSA/EMSA3(SHA-512)");
708

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

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

723
   // Check CRL signature algorithm
724
   Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
1✔
725
   test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4");
2✔
726

727
   // sanity check for verification, the heavy lifting is done in the other unit tests
728
   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
1✔
729
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
730
   const Botan::Path_Validation_Result validation_result =
1✔
731
      Botan::x509_path_validate(end_cert_emsa4, restrictions, trusted);
1✔
732
   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
2✔
733

734
   return test_result;
1✔
735
}
2✔
736
      #endif
737

738
   #endif
739

740
Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
9✔
741
                             const std::string& sig_padding,
742
                             const std::string& hash_fn) {
743
   Test::Result result("PKCS10 extensions");
9✔
744

745
   Botan::X509_Cert_Options opts;
9✔
746

747
   opts.padding_scheme = sig_padding;
9✔
748

749
   Botan::AlternativeName alt_name;
9✔
750
   alt_name.add_attribute("DNS", "example.org");
9✔
751
   alt_name.add_attribute("DNS", "example.com");
9✔
752
   alt_name.add_attribute("DNS", "example.net");
9✔
753

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

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

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

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

762
   // The order is not guaranteed so sort before comparing
763
   std::sort(alt_dns_names.begin(), alt_dns_names.end());
9✔
764

765
   result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "example.com");
27✔
766
   result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "example.net");
27✔
767
   result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "example.org");
27✔
768

769
   return result;
9✔
770
}
9✔
771

772
Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
9✔
773
                            const std::string& sig_algo,
774
                            const std::string& sig_padding,
775
                            const std::string& hash_fn) {
776
   Test::Result result("X509 Unit");
9✔
777

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

781
   {
9✔
782
      result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
18✔
783
      result.confirm("ca key usage crl", ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
18✔
784
   }
785

786
   /* Create user #1's key and cert request */
787
   auto user1_key = make_a_private_key(sig_algo);
9✔
788

789
   Botan::PKCS10_Request user1_req =
9✔
790
      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
9✔
791

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

794
   /* Create user #2's key and cert request */
795
   auto user2_key = make_a_private_key(sig_algo);
9✔
796

797
   Botan::PKCS10_Request user2_req =
9✔
798
      Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, Test::rng());
9✔
799

800
   // /* Create user #3's key and cert request */
801
   auto user3_key = make_a_private_key(sig_algo);
9✔
802

803
   Botan::PKCS10_Request user3_req =
9✔
804
      Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, Test::rng());
9✔
805

806
   /* Create the CA object */
807
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
808

809
   const BigInt user1_serial = 99;
9✔
810

811
   /* Sign the requests to create the certs */
812
   Botan::X509_Certificate user1_cert =
9✔
813
      ca.sign_request(user1_req, Test::rng(), user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
814

815
   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
9✔
816
   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
9✔
817

818
   Botan::X509_Certificate user2_cert =
9✔
819
      ca.sign_request(user2_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
820

821
   Botan::X509_Certificate user3_cert =
9✔
822
      ca.sign_request(user3_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
823

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

828
   {
9✔
829
      auto constraints = req_opts1(sig_algo).constraints;
9✔
830
      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
18✔
831
   }
832

833
   /* Copy, assign and compare */
834
   Botan::X509_Certificate user1_cert_copy(user1_cert);
9✔
835
   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
9✔
836

837
   user1_cert_copy = user2_cert;
9✔
838
   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
9✔
839

840
   Botan::X509_Certificate user1_cert_differ =
9✔
841
      ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
842

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

845
   /* Get cert data */
846
   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
9✔
847

848
   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
9✔
849
   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
18✔
850
   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
18✔
851
   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
18✔
852
   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
18✔
853

854
   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
9✔
855
   result.test_eq("subject OrgaUnit count",
9✔
856
                  user3_subject_dn.get_attribute("OU").size(),
18✔
857
                  req_opts3(sig_algo).more_org_units.size() + 1);
18✔
858
   result.test_eq(
9✔
859
      "subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
36✔
860

861
   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
9✔
862
   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
27✔
863
   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
27✔
864
   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
27✔
865

866
   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
9✔
867
   result.test_eq(
9✔
868
      "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
18✔
869
   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
36✔
870

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

873
   /* Verify the certs */
874
   Botan::Path_Validation_Restrictions restrictions(false, 80);
18✔
875
   Botan::Certificate_Store_In_Memory store;
9✔
876

877
   // First try with an empty store
878
   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
879
   result.test_eq("user 1 issuer not found",
27✔
880
                  result_no_issuer.result_string(),
18✔
881
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
882

883
   store.add_certificate(ca.ca_certificate());
9✔
884

885
   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
886
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
27✔
887
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
888
   }
889

890
   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
9✔
891
   if(!result.confirm("user 2 validates", result_u2.successful_validation())) {
27✔
892
      result.test_note("user 2 validation result was " + result_u2.result_string());
×
893
   }
894

895
   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
9✔
896
   result.test_eq("user 1 issuer not found",
27✔
897
                  result_no_issuer.result_string(),
18✔
898
                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
899
   store.add_crl(crl1);
9✔
900

901
   std::vector<Botan::CRL_Entry> revoked;
9✔
902
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation));
18✔
903
   revoked.push_back(user2_cert);
18✔
904

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

907
   store.add_crl(crl2);
9✔
908

909
   const std::string revoked_str =
9✔
910
      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
9✔
911

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

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

918
   revoked.clear();
9✔
919
   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl));
18✔
920
   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, Test::rng());
9✔
921

922
   store.add_crl(crl3);
9✔
923

924
   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
9✔
925
   if(!result.confirm("user 1 validates", result_u1.successful_validation())) {
27✔
926
      result.test_note("user 1 validation result was " + result_u1.result_string());
×
927
   }
928

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

932
   return result;
9✔
933
}
54✔
934

935
Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& hash_fn) {
8✔
936
   using Botan::Key_Constraints;
8✔
937
   using Botan::Usage_Type;
8✔
938

939
   Test::Result result("X509 Usage");
8✔
940

941
   /* Create the self-signed cert */
942
   const Botan::X509_Certificate ca_cert =
8✔
943
      Botan::X509::create_self_signed_cert(ca_opts(), ca_key, hash_fn, Test::rng());
16✔
944

945
   /* Create the CA object */
946
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, Test::rng());
8✔
947

948
   auto user1_key = make_a_private_key(sig_algo);
8✔
949

950
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
8✔
951
   opts.constraints = Key_Constraints::DigitalSignature;
8✔
952

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

955
   const Botan::X509_Certificate user1_cert =
8✔
956
      ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
957

958
   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
959
   result.test_eq(
8✔
960
      "key usage cRLSign not allowed",
961
      user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)),
8✔
962
      false);
963
   result.test_eq("encryption is not allowed", user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
8✔
964

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

968
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
8✔
969

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

972
   const Botan::X509_Certificate mult_usage_cert =
8✔
973
      ca.sign_request(mult_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
974

975
   // cert allows multiple usages, so each one of them as well as both together should be allowed
976
   result.confirm("key usage multiple digitalSignature allowed",
16✔
977
                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
8✔
978
   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
16✔
979
   result.confirm(
16✔
980
      "key usage multiple digitalSignature and cRLSign allowed",
981
      mult_usage_cert.allowed_usage(Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
8✔
982
   result.test_eq("encryption is not allowed", mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
8✔
983

984
   opts.constraints = Key_Constraints();
8✔
985

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

988
   const Botan::X509_Certificate no_usage_cert =
8✔
989
      ca.sign_request(no_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
8✔
990

991
   // cert allows every usage
992
   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
16✔
993
   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
16✔
994
   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
16✔
995

996
   if(sig_algo == "RSA") {
8✔
997
      // cert allows data encryption
998
      opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
1✔
999

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

1002
      const Botan::X509_Certificate enc_cert =
1✔
1003
         ca.sign_request(enc_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1004

1005
      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
2✔
1006
      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
2✔
1007
   }
1✔
1008

1009
   return result;
8✔
1010
}
16✔
1011

1012
Test::Result test_self_issued(const Botan::Private_Key& ca_key,
9✔
1013
                              const std::string& sig_algo,
1014
                              const std::string& sig_padding,
1015
                              const std::string& hash_fn) {
1016
   using Botan::Key_Constraints;
9✔
1017

1018
   Test::Result result("X509 Self Issued");
9✔
1019

1020
   // create the self-signed cert
1021
   const Botan::X509_Certificate ca_cert =
9✔
1022
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1023

1024
   /* Create the CA object */
1025
   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1026

1027
   auto user_key = make_a_private_key(sig_algo);
9✔
1028

1029
   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1030
   // but signed by a CA, not signed by it's own private key
1031
   Botan::X509_Cert_Options opts = ca_opts();
9✔
1032
   opts.constraints = Key_Constraints::DigitalSignature;
9✔
1033
   opts.set_padding_scheme(sig_padding);
9✔
1034

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

1037
   const Botan::X509_Certificate self_issued_cert =
9✔
1038
      ca.sign_request(self_issued_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
1039

1040
   // check that this chain can can be verified successfully
1041
   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
9✔
1042

1043
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
18✔
1044

1045
   const Botan::Path_Validation_Result validation_result =
9✔
1046
      Botan::x509_path_validate(self_issued_cert, restrictions, trusted);
9✔
1047

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

1050
   return result;
9✔
1051
}
18✔
1052

1053
Test::Result test_x509_uninit() {
1✔
1054
   Test::Result result("X509 object uninitialized access");
1✔
1055

1056
   Botan::X509_Certificate cert;
1✔
1057
   result.test_throws("uninitialized cert access causes exception", "X509_Certificate uninitialized", [&cert]() {
3✔
1058
      cert.x509_version();
1✔
1059
   });
1060

1061
   Botan::X509_CRL crl;
1✔
1062
   result.test_throws(
3✔
1063
      "uninitialized crl access causes exception", "X509_CRL uninitialized", [&crl]() { crl.crl_number(); });
1✔
1064

1065
   return result;
1✔
1066
}
1✔
1067

1068
Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::string& pk_algo) {
12✔
1069
   using Botan::Key_Constraints;
12✔
1070

1071
   Test::Result result("X509 Valid Constraints " + pk_algo);
12✔
1072

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

1075
   // Now check some typical usage scenarios for the given key type
1076
   // Taken from RFC 5280, sec. 4.2.1.3
1077
   // ALL constraints are not typical at all, but we use them for a negative test
1078
   const auto all = Key_Constraints(
12✔
1079
      Key_Constraints::DigitalSignature | Key_Constraints::NonRepudiation | Key_Constraints::KeyEncipherment |
1080
      Key_Constraints::DataEncipherment | Key_Constraints::KeyAgreement | Key_Constraints::KeyCertSign |
1081
      Key_Constraints::CrlSign | Key_Constraints::EncipherOnly | Key_Constraints::DecipherOnly);
12✔
1082

1083
   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
12✔
1084
   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
12✔
1085
   const auto non_repudiation = Key_Constraints(Key_Constraints::NonRepudiation | Key_Constraints::DigitalSignature);
12✔
1086
   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
12✔
1087
   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
12✔
1088
   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
12✔
1089
   const auto key_agreement_encipher_only =
12✔
1090
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::EncipherOnly);
12✔
1091
   const auto key_agreement_decipher_only =
12✔
1092
      Key_Constraints(Key_Constraints::KeyAgreement | Key_Constraints::DecipherOnly);
12✔
1093
   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
12✔
1094
   const auto sign_everything =
12✔
1095
      Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
12✔
1096

1097
   if(pk_algo == "DH" || pk_algo == "ECDH") {
12✔
1098
      // DH and ECDH only for key agreement
1099
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
2✔
1100
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
2✔
1101
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
2✔
1102
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
2✔
1103
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
2✔
1104
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
2✔
1105
      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
2✔
1106
      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
2✔
1107
      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
2✔
1108
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
2✔
1109
      result.test_eq("sign", sign_everything.compatible_with(key), false);
4✔
1110
   } else if(pk_algo == "Kyber") {
10✔
1111
      // Kyber can encrypt and agree
1112
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1113
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1114
      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
1✔
1115
      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
1✔
1116
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1117
      result.test_eq("sign", sign_everything.compatible_with(key), false);
1✔
1118
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1119
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1120
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
2✔
1121
   } else if(pk_algo == "RSA") {
9✔
1122
      // RSA can do everything except key agreement
1123
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1124

1125
      result.test_eq("usage acceptable", ca.compatible_with(key), true);
1✔
1126
      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
1✔
1127
      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
1✔
1128
      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
1✔
1129
      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
1✔
1130
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1131
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1132
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1133
      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
1✔
1134
      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
2✔
1135
   } else if(pk_algo == "ElGamal") {
8✔
1136
      // only ElGamal encryption is currently implemented
1137
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
1✔
1138
      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
1✔
1139
      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
1✔
1140
      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
1✔
1141
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
1✔
1142
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
1✔
1143
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
1✔
1144
      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
1✔
1145
      result.test_eq("sign", sign_everything.compatible_with(key), false);
2✔
1146
   } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
7✔
1147
             pk_algo == "GOST-34.10" || pk_algo == "Dilithium") {
10✔
1148
      // these are signature algorithms only
1149
      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
6✔
1150

1151
      result.test_eq("ca allowed", ca.compatible_with(key), true);
6✔
1152
      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
6✔
1153
      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
6✔
1154
      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
6✔
1155
      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
6✔
1156
      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
6✔
1157
      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
6✔
1158
      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
6✔
1159
      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
6✔
1160
      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
12✔
1161
   }
1162

1163
   return result;
12✔
1164
}
×
1165

1166
/**
1167
 * @brief X.509v3 extension that encodes a given string
1168
 */
1169
class String_Extension final : public Botan::Certificate_Extension {
1170
   public:
1171
      String_Extension() = default;
18✔
1172

1173
      explicit String_Extension(const std::string& val) : m_contents(val) {}
9✔
1174

1175
      std::string value() const { return m_contents; }
18✔
1176

1177
      std::unique_ptr<Certificate_Extension> copy() const override {
×
1178
         return std::make_unique<String_Extension>(m_contents);
×
1179
      }
1180

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

1183
      bool should_encode() const override { return true; }
18✔
1184

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

1187
      std::vector<uint8_t> encode_inner() const override {
9✔
1188
         std::vector<uint8_t> bits;
9✔
1189
         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Type::Utf8String));
9✔
1190
         return bits;
9✔
1191
      }
×
1192

1193
      void decode_inner(const std::vector<uint8_t>& in) override {
18✔
1194
         Botan::ASN1_String str;
18✔
1195
         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Type::Utf8String).verify_end();
18✔
1196
         m_contents = str.value();
18✔
1197
      }
18✔
1198

1199
   private:
1200
      std::string m_contents;
1201
};
1202

1203
Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
9✔
1204
                                 const std::string& sig_algo,
1205
                                 const std::string& sig_padding,
1206
                                 const std::string& hash_fn) {
1207
   Test::Result result("X509 Custom DN");
9✔
1208

1209
   /* Create the self-signed cert */
1210
   Botan::X509_Certificate ca_cert =
9✔
1211
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1212

1213
   /* Create the CA object */
1214
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1215

1216
   auto user_key = make_a_private_key(sig_algo);
9✔
1217

1218
   Botan::X509_DN subject_dn;
9✔
1219

1220
   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
9✔
1221
   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
9✔
1222
   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Type::PrintableString);
9✔
1223
   const Botan::ASN1_String val2("12345", Botan::ASN1_Type::Utf8String);
9✔
1224

1225
   subject_dn.add_attribute(attr1, val1);
9✔
1226
   subject_dn.add_attribute(attr2, val2);
9✔
1227

1228
   Botan::Extensions extensions;
9✔
1229

1230
   Botan::PKCS10_Request req =
9✔
1231
      Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, Test::rng(), sig_padding);
9✔
1232

1233
   const Botan::X509_DN& req_dn = req.subject_dn();
9✔
1234

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

1237
   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
9✔
1238
   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
9✔
1239
   result.confirm("Attr1 matches encoded", req_val1 == val1);
18✔
1240
   result.confirm("Attr2 matches encoded", req_val2 == val2);
18✔
1241
   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
18✔
1242
   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
18✔
1243

1244
   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime);
9✔
1245
   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime);
9✔
1246

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

1249
   const Botan::X509_DN& cert_dn = cert.subject_dn();
9✔
1250

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

1253
   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
9✔
1254
   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
9✔
1255
   result.confirm("Attr1 matches encoded", cert_val1 == val1);
18✔
1256
   result.confirm("Attr2 matches encoded", cert_val2 == val2);
18✔
1257
   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
18✔
1258
   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
18✔
1259

1260
   return result;
9✔
1261
}
27✔
1262

1263
Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
9✔
1264
                                  const std::string& sig_algo,
1265
                                  const std::string& sig_padding,
1266
                                  const std::string& hash_fn) {
1267
   using Botan::Key_Constraints;
9✔
1268

1269
   Test::Result result("X509 Extensions");
9✔
1270

1271
   /* Create the self-signed cert */
1272
   Botan::X509_Certificate ca_cert =
9✔
1273
      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
9✔
1274

1275
   /* Create the CA object */
1276
   Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng());
9✔
1277

1278
   auto user_key = make_a_private_key(sig_algo);
9✔
1279

1280
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
9✔
1281
   opts.constraints = Key_Constraints::DigitalSignature;
9✔
1282

1283
   // include a custom extension in the request
1284
   Botan::Extensions req_extensions;
9✔
1285
   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
9✔
1286
   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
9✔
1287
   req_extensions.add(std::make_unique<String_Extension>("AAAAAAAAAAAAAABCDEF"), false);
18✔
1288
   opts.extensions = req_extensions;
9✔
1289
   opts.set_padding_scheme(sig_padding);
9✔
1290

1291
   /* Create a self-signed certificate */
1292
   const Botan::X509_Certificate self_signed_cert =
9✔
1293
      Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, Test::rng());
9✔
1294

1295
   result.confirm("Extensions::extension_set true for Key_Usage",
18✔
1296
                  self_signed_cert.v3_extensions().extension_set(ku_oid));
9✔
1297

1298
   // check if known Key_Usage extension is present in self-signed cert
1299
   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
9✔
1300
   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr)) {
27✔
1301
      result.confirm(
27✔
1302
         "Key_Usage extension value matches in self-signed certificate",
1303
         dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
9✔
1304
   }
1305

1306
   // check if custom extension is present in self-signed cert
1307
   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
9✔
1308
   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr)) {
27✔
1309
      result.test_eq(
36✔
1310
         "Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
27✔
1311
   }
1312

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

1315
   /* Create a CA-signed certificate */
1316
   const Botan::X509_Certificate ca_signed_cert =
9✔
1317
      ca.sign_request(user_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
9✔
1318

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

1322
   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
18✔
1323
   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr)) {
27✔
1324
      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
9✔
1325
      result.confirm("Key_Usage extension value matches in user certificate",
27✔
1326
                     constraints == Botan::Key_Constraints::DigitalSignature);
9✔
1327
   }
1328

1329
   // check if custom extension is present in CA-signed cert
1330
   result.confirm("Extensions::extension_set true for String_Extension",
18✔
1331
                  ca_signed_cert.v3_extensions().extension_set(oid));
9✔
1332
   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
18✔
1333
   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr)) {
27✔
1334
      result.test_eq(
36✔
1335
         "Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
27✔
1336
   }
1337

1338
   return result;
9✔
1339
}
45✔
1340

1341
Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn) {
8✔
1342
   Test::Result result("X509 Hashes");
8✔
1343

1344
   struct TestData {
8✔
1345
         const std::string issuer, subject, issuer_hash, subject_hash;
1346
   } const cases[]{{"",
1347
                    "",
1348
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1349
                    "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
1350
                   {"a",
1351
                    "b",
1352
                    "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1353
                    "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
1354
                   {"A",
1355
                    "B",
1356
                    "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1357
                    "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
1358
                   {
1359
                      "Test Issuer/US/Botan Project/Testing",
1360
                      "Test Subject/US/Botan Project/Testing",
1361
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1362
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1363
                   },
1364
                   {
1365
                      "Test Subject/US/Botan Project/Testing",
1366
                      "Test Issuer/US/Botan Project/Testing",
1367
                      "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1368
                      "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1369
                   }};
48✔
1370

1371
   for(const auto& a : cases) {
48✔
1372
      Botan::X509_Cert_Options opts{a.issuer};
40✔
1373
      opts.CA_key();
40✔
1374

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

1377
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
80✔
1378
      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
80✔
1379

1380
      const Botan::X509_CA ca(issuer_cert, key, hash_fn, Test::rng());
40✔
1381
      const Botan::PKCS10_Request req =
40✔
1382
         Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, Test::rng());
40✔
1383
      const Botan::X509_Certificate subject_cert =
40✔
1384
         ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
40✔
1385

1386
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
80✔
1387
      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
80✔
1388
   }
40✔
1389
   return result;
8✔
1390
}
48✔
1391

1392
std::vector<std::string> get_sig_paddings(const std::string& sig_algo, const std::string& hash) {
8✔
1393
   if(sig_algo == "RSA") {
8✔
1394
      return {"EMSA3(" + hash + ")", "EMSA4(" + hash + ")"};
3✔
1395
   } else if(sig_algo == "DSA" || sig_algo == "ECDSA" || sig_algo == "ECGDSA" || sig_algo == "ECKCDSA" ||
7✔
1396
             sig_algo == "GOST-34.10") {
3✔
1397
      return {hash};
10✔
1398
   } else if(sig_algo == "Ed25519") {
2✔
1399
      return {"Pure"};
2✔
1400
   } else if(sig_algo == "Dilithium") {
1✔
1401
      return {"Randomized"};
2✔
1402
   } else {
1403
      return {};
8✔
1404
   }
1405
}
1406

1407
class X509_Cert_Unit_Tests final : public Test {
×
1408
   public:
1409
      std::vector<Test::Result> run() override {
1✔
1410
         std::vector<Test::Result> results;
1✔
1411

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

1415
         for(const std::string& algo : sig_algos) {
9✔
1416
   #if !defined(BOTAN_HAS_EMSA_PKCS1)
1417
            if(algo == "RSA")
1418
               continue;
1419
   #endif
1420

1421
            std::string hash = "SHA-256";
8✔
1422

1423
            if(algo == "Ed25519") {
8✔
1424
               hash = "SHA-512";
1✔
1425
            }
1426
            if(algo == "Dilithium") {
8✔
1427
               hash = "SHAKE-256(512)";
1✔
1428
            }
1429

1430
            auto key = make_a_private_key(algo);
8✔
1431

1432
            if(key == nullptr) {
8✔
1433
               continue;
×
1434
            }
1435

1436
            results.push_back(test_hashes(*key, hash));
16✔
1437
            results.push_back(test_valid_constraints(*key, algo));
16✔
1438

1439
            Test::Result usage_result("X509 Usage");
8✔
1440
            try {
8✔
1441
               usage_result.merge(test_usage(*key, algo, hash));
8✔
1442
            } catch(std::exception& e) {
×
1443
               usage_result.test_failure("test_usage " + algo, e.what());
×
1444
            }
×
1445
            results.push_back(usage_result);
8✔
1446

1447
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
17✔
1448
               Test::Result cert_result("X509 Unit");
9✔
1449

1450
               try {
9✔
1451
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash));
9✔
1452
               } catch(std::exception& e) {
×
1453
                  cert_result.test_failure("test_x509_cert " + algo, e.what());
×
1454
               }
×
1455
               results.push_back(cert_result);
9✔
1456

1457
               Test::Result pkcs10_result("PKCS10 extensions");
9✔
1458
               try {
9✔
1459
                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash));
9✔
1460
               } catch(std::exception& e) {
×
1461
                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
×
1462
               }
×
1463
               results.push_back(pkcs10_result);
9✔
1464

1465
               Test::Result self_issued_result("X509 Self Issued");
9✔
1466
               try {
9✔
1467
                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash));
9✔
1468
               } catch(std::exception& e) {
×
1469
                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
×
1470
               }
×
1471
               results.push_back(self_issued_result);
9✔
1472

1473
               Test::Result extensions_result("X509 Extensions");
9✔
1474
               try {
9✔
1475
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash));
9✔
1476
               } catch(std::exception& e) {
×
1477
                  extensions_result.test_failure("test_extensions " + algo, e.what());
×
1478
               }
×
1479
               results.push_back(extensions_result);
9✔
1480

1481
               Test::Result custom_dn_result("X509 Custom DN");
9✔
1482
               try {
9✔
1483
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash));
9✔
1484
               } catch(std::exception& e) {
×
1485
                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
×
1486
               }
×
1487
               results.push_back(custom_dn_result);
9✔
1488
            }
17✔
1489
         }
16✔
1490

1491
         /*
1492
         These are algos which cannot sign but can be included in certs
1493
         */
1494
         const std::vector<std::string> enc_algos = {"DH", "ECDH", "ElGamal", "Kyber"};
5✔
1495

1496
         for(const std::string& algo : enc_algos) {
5✔
1497
            auto key = make_a_private_key(algo);
4✔
1498

1499
            if(key) {
4✔
1500
               results.push_back(test_valid_constraints(*key, algo));
8✔
1501
            }
1502
         }
4✔
1503

1504
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1505
      defined(BOTAN_HAS_RSA)
1506
         Test::Result pad_config_result("X509 Padding Config");
1✔
1507
         try {
1✔
1508
            pad_config_result.merge(test_padding_config());
1✔
1509
         } catch(const std::exception& e) {
×
1510
            pad_config_result.test_failure("test_padding_config", e.what());
×
1511
         }
×
1512
         results.push_back(pad_config_result);
1✔
1513
   #endif
1514

1515
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1516
         results.push_back(test_x509_utf8());
2✔
1517
         results.push_back(test_x509_bmpstring());
2✔
1518
         results.push_back(test_x509_teletex());
2✔
1519
         results.push_back(test_crl_dn_name());
2✔
1520
         results.push_back(test_rdn_multielement_set_name());
2✔
1521
         results.push_back(test_x509_decode_list());
2✔
1522
         results.push_back(test_rsa_oaep());
2✔
1523
         results.push_back(test_x509_authority_info_access_extension());
2✔
1524
         results.push_back(test_verify_gost2012_cert());
2✔
1525
         results.push_back(test_parse_rsa_pss_cert());
2✔
1526
   #endif
1527

1528
         results.push_back(test_x509_extension());
2✔
1529
         results.push_back(test_x509_dates());
2✔
1530
         results.push_back(test_cert_status_strings());
2✔
1531
         results.push_back(test_x509_uninit());
2✔
1532

1533
         return results;
1✔
1534
      }
9✔
1535
};
1536

1537
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1538

1539
#endif
1540

1541
}  // namespace
1542

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