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

randombit / botan / 5123321399

30 May 2023 04:06PM UTC coverage: 92.213% (+0.004%) from 92.209%
5123321399

Pull #3558

github

web-flow
Merge dd72f7389 into 057bcbc35
Pull Request #3558: Add braces around all if/else statements

75602 of 81986 relevant lines covered (92.21%)

11859779.3 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

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

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

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

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

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

585
   return result;
1✔
586
}
1✔
587

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

591
   // See https://github.com/randombit/botan/issues/3019 for background
592

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

598
   return result;
1✔
599
}
×
600

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

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

609
      Botan::Certificate_Store_In_Memory trusted;
1✔
610
      trusted.add_certificate(root_cert);
1✔
611

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

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

620
   return result;
1✔
621
}
×
622

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

724
   return test_result;
1✔
725
}
2✔
726
      #endif
727

728
   #endif
729

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

735
   Botan::X509_Cert_Options opts;
9✔
736

737
   opts.padding_scheme = sig_padding;
9✔
738

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

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

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

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

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

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

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

759
   return result;
9✔
760
}
9✔
761

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

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

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

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

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

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

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

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

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

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

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

799
   const BigInt user1_serial = 99;
9✔
800

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

873
   store.add_certificate(ca.ca_certificate());
9✔
874

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

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

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

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

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

897
   store.add_crl(crl2);
9✔
898

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

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

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

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

912
   store.add_crl(crl3);
9✔
913

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

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

922
   return result;
9✔
923
}
54✔
924

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

929
   Test::Result result("X509 Usage");
8✔
930

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

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

938
   auto user1_key = make_a_private_key(sig_algo);
8✔
939

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

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

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

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

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

958
   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
8✔
959

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

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

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

974
   opts.constraints = Key_Constraints();
8✔
975

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

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

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

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

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

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

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

999
   return result;
8✔
1000
}
16✔
1001

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

1008
   Test::Result result("X509 Self Issued");
9✔
1009

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

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

1017
   auto user_key = make_a_private_key(sig_algo);
9✔
1018

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

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

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

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

1033
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
18✔
1034

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

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

1040
   return result;
9✔
1041
}
18✔
1042

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

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

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

1055
   return result;
1✔
1056
}
1✔
1057

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

1061
   Test::Result result("X509 Valid Constraints " + pk_algo);
12✔
1062

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

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

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

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

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

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

1153
   return result;
12✔
1154
}
×
1155

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

1163
      explicit String_Extension(const std::string& val) : m_contents(val) {}
9✔
1164

1165
      std::string value() const { return m_contents; }
18✔
1166

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

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

1173
      bool should_encode() const override { return true; }
18✔
1174

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

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

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

1189
   private:
1190
      std::string m_contents;
1191
};
1192

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

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

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

1206
   auto user_key = make_a_private_key(sig_algo);
9✔
1207

1208
   Botan::X509_DN subject_dn;
9✔
1209

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

1215
   subject_dn.add_attribute(attr1, val1);
9✔
1216
   subject_dn.add_attribute(attr2, val2);
9✔
1217

1218
   Botan::Extensions extensions;
9✔
1219

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

1223
   const Botan::X509_DN& req_dn = req.subject_dn();
9✔
1224

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

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

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

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

1239
   const Botan::X509_DN& cert_dn = cert.subject_dn();
9✔
1240

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

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

1250
   return result;
9✔
1251
}
27✔
1252

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

1259
   Test::Result result("X509 Extensions");
9✔
1260

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

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

1268
   auto user_key = make_a_private_key(sig_algo);
9✔
1269

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

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

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

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

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

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

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

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

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

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

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

1328
   return result;
9✔
1329
}
45✔
1330

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

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

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

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

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

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

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

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

1397
class X509_Cert_Unit_Tests final : public Test {
×
1398
   public:
1399
      std::vector<Test::Result> run() override {
1✔
1400
         std::vector<Test::Result> results;
1✔
1401

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

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

1411
            std::string hash = "SHA-256";
8✔
1412

1413
            if(algo == "Ed25519") {
8✔
1414
               hash = "SHA-512";
1✔
1415
            }
1416
            if(algo == "Dilithium") {
8✔
1417
               hash = "SHAKE-256(512)";
1✔
1418
            }
1419

1420
            auto key = make_a_private_key(algo);
8✔
1421

1422
            if(key == nullptr) {
8✔
1423
               continue;
×
1424
            }
1425

1426
            results.push_back(test_hashes(*key, hash));
16✔
1427
            results.push_back(test_valid_constraints(*key, algo));
16✔
1428

1429
            Test::Result usage_result("X509 Usage");
8✔
1430
            try {
8✔
1431
               usage_result.merge(test_usage(*key, algo, hash));
8✔
1432
            } catch(std::exception& e) { usage_result.test_failure("test_usage " + algo, e.what()); }
×
1433
            results.push_back(usage_result);
8✔
1434

1435
            for(const auto& padding_scheme : get_sig_paddings(algo, hash)) {
17✔
1436
               Test::Result cert_result("X509 Unit");
9✔
1437

1438
               try {
9✔
1439
                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash));
9✔
1440
               } catch(std::exception& e) { cert_result.test_failure("test_x509_cert " + algo, e.what()); }
×
1441
               results.push_back(cert_result);
9✔
1442

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

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

1455
               Test::Result extensions_result("X509 Extensions");
9✔
1456
               try {
9✔
1457
                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash));
9✔
1458
               } catch(std::exception& e) { extensions_result.test_failure("test_extensions " + algo, e.what()); }
×
1459
               results.push_back(extensions_result);
9✔
1460

1461
               Test::Result custom_dn_result("X509 Custom DN");
9✔
1462
               try {
9✔
1463
                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash));
9✔
1464
               } catch(std::exception& e) { custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what()); }
×
1465
               results.push_back(custom_dn_result);
9✔
1466
            }
17✔
1467
         }
16✔
1468

1469
         /*
1470
         These are algos which cannot sign but can be included in certs
1471
         */
1472
         const std::vector<std::string> enc_algos = {"DH", "ECDH", "ElGamal", "Kyber"};
5✔
1473

1474
         for(const std::string& algo : enc_algos) {
5✔
1475
            auto key = make_a_private_key(algo);
4✔
1476

1477
            if(key) {
4✔
1478
               results.push_back(test_valid_constraints(*key, algo));
8✔
1479
            }
1480
         }
4✔
1481

1482
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && \
1483
      defined(BOTAN_HAS_RSA)
1484
         Test::Result pad_config_result("X509 Padding Config");
1✔
1485
         try {
1✔
1486
            pad_config_result.merge(test_padding_config());
1✔
1487
         } catch(const std::exception& e) { pad_config_result.test_failure("test_padding_config", e.what()); }
×
1488
         results.push_back(pad_config_result);
1✔
1489
   #endif
1490

1491
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1492
         results.push_back(test_x509_utf8());
2✔
1493
         results.push_back(test_x509_bmpstring());
2✔
1494
         results.push_back(test_x509_teletex());
2✔
1495
         results.push_back(test_crl_dn_name());
2✔
1496
         results.push_back(test_rdn_multielement_set_name());
2✔
1497
         results.push_back(test_x509_decode_list());
2✔
1498
         results.push_back(test_rsa_oaep());
2✔
1499
         results.push_back(test_x509_authority_info_access_extension());
2✔
1500
         results.push_back(test_verify_gost2012_cert());
2✔
1501
         results.push_back(test_parse_rsa_pss_cert());
2✔
1502
   #endif
1503

1504
         results.push_back(test_x509_extension());
2✔
1505
         results.push_back(test_x509_dates());
2✔
1506
         results.push_back(test_cert_status_strings());
2✔
1507
         results.push_back(test_x509_uninit());
2✔
1508

1509
         return results;
1✔
1510
      }
9✔
1511
};
1512

1513
BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1514

1515
#endif
1516

1517
}  // namespace
1518

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