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

randombit / botan / 22017538080

14 Feb 2026 12:43PM UTC coverage: 90.069% (+0.004%) from 90.065%
22017538080

Pull #5328

github

web-flow
Merge 164f4ff86 into 5f60b6bbe
Pull Request #5328: Change Test::Result integer and bool predicates to be specifically named

102249 of 113523 relevant lines covered (90.07%)

11529820.5 hits per line

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

82.05
/src/tests/test_certstor.cpp
1
/*
2
* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8

9
#if defined(BOTAN_HAS_X509_CERTIFICATES)
10
   #include <botan/certstor.h>
11
   #include <botan/pk_keys.h>
12
   #include <botan/pkcs8.h>
13
   #include <botan/pkix_types.h>
14
   #include <botan/x509cert.h>
15

16
   #if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
17
      #include <botan/certstor_sqlite.h>
18
      #include <botan/rng.h>
19
      #include <botan/sqlite3.h>
20
   #endif
21
#endif
22

23
namespace Botan_Tests {
24

25
namespace {
26

27
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
28

29
class CertificateAndKey {
13✔
30
   public:
31
      CertificateAndKey(const Botan::X509_Certificate& cert, std::shared_ptr<Botan::Private_Key> key) :
6✔
32
            m_certificate(cert), m_private_key(std::move(key)) {}
6✔
33

34
      bool operator!=(const CertificateAndKey& rhs) const {
9✔
35
         if(m_certificate != rhs.m_certificate) {
9✔
36
            return false;
37
         }
38
         // XXX: this is comparing the pointers, is that really correct?
39
         if(m_private_key != rhs.m_private_key) {
2✔
40
            return false;
41
         }
42
         return true;
43
      }
44

45
      const Botan::X509_DN& subject_dn() const { return certificate().subject_dn(); }
32✔
46

47
      const Botan::X509_Certificate& certificate() const { return m_certificate; }
13✔
48

49
      const Botan::Private_Key& private_key() const { return *m_private_key; }
12✔
50

51
   private:
52
      const Botan::X509_Certificate m_certificate;
53
      const std::shared_ptr<Botan::Private_Key> m_private_key;
54
};
55

56
   #if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
57
Test::Result test_certstor_sqlite3_insert_find_remove_test(const std::vector<CertificateAndKey>& certsandkeys) {
1✔
58
   Test::Result result("Certificate Store SQLITE3 - Insert, Find, Remove");
1✔
59

60
   try {
1✔
61
      auto rng = Test::new_rng(__func__);
1✔
62
      const std::string passwd(reinterpret_cast<const char*>(rng->random_vec(8).data()), 8);
2✔
63
      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
64
      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng);
1✔
65

66
      for(const auto& a : certsandkeys) {
7✔
67
         store.insert_key(a.certificate(), a.private_key());
6✔
68
      }
69

70
      for(const auto& certandkey : certsandkeys) {
7✔
71
         const auto& cert = certandkey.certificate();
6✔
72
         const auto& key = certandkey.private_key();
6✔
73
         const auto wo_keyid = store.find_cert(cert.subject_dn(), {});
6✔
74
         const auto w_keyid = store.find_cert(cert.subject_dn(), cert.subject_key_id());
6✔
75

76
         if(!wo_keyid || !w_keyid) {
6✔
77
            result.test_failure("Can't retrieve certificate");
×
78
            return result;
×
79
         }
80

81
         const auto priv = store.find_key(cert);
6✔
82
         if(!priv && (certsandkeys[1] != certandkey && certsandkeys[0] != certandkey)) {
7✔
83
            result.test_failure("Can't retrieve private key for " + cert.fingerprint("SHA-1"));
×
84
            return result;
×
85
         }
86

87
         result.test_eq("Got wrong certificate", cert.fingerprint(), w_keyid->fingerprint());
6✔
88

89
         if(priv) {
6✔
90
            result.test_eq("Got wrong private key", key.private_key_bits(), priv->private_key_bits());
15✔
91

92
            const auto rev_certs = store.find_certs_for_key(*priv);
5✔
93

94
            if(rev_certs.empty()) {
5✔
95
               result.test_failure("No certificate");
×
96
            } else {
97
               const bool found =
5✔
98
                  std::any_of(rev_certs.begin(), rev_certs.end(), [&](const Botan::X509_Certificate& c) {
5✔
99
                     return c.fingerprint() == cert.fingerprint();
5✔
100
                  });
101

102
               result.test_is_true("Got wrong/no certificate", found);
5✔
103
            }
104
         }
5✔
105

106
         if(certsandkeys[4] != certandkey && certsandkeys[5] != certandkey) {
7✔
107
            result.test_eq("Got wrong certificate", cert.fingerprint(), wo_keyid->fingerprint());
×
108
         }
109

110
         result.test_is_true("Can't remove certificate", store.remove_cert(cert));
6✔
111
         result.test_is_true("Can't remove certificate", !store.find_cert(cert.subject_dn(), cert.subject_key_id()));
6✔
112

113
         if(priv) {
6✔
114
            store.remove_key(key);
5✔
115
         }
116

117
         result.test_is_true("Can't remove key", !store.find_key(cert));
6✔
118
      }
18✔
119

120
      return result;
121
   } catch(std::exception& e) {
2✔
122
      result.test_failure(e.what());
×
123
      return result;
×
124
   }
×
125
}
×
126

127
Test::Result test_certstor_sqlite3_crl_test(const std::vector<CertificateAndKey>& certsandkeys) {
1✔
128
   Test::Result result("Certificate Store SQLITE3 - CRL");
1✔
129
   try {
1✔
130
      auto rng = Test::new_rng(__func__);
1✔
131
      const std::string passwd(reinterpret_cast<const char*>(rng->random_vec(8).data()), 8);
2✔
132
      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
133
      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng);
1✔
134

135
      for(const auto& a : certsandkeys) {
7✔
136
         store.insert_cert(a.certificate());
6✔
137
      }
138

139
      store.revoke_cert(certsandkeys[0].certificate(), Botan::CRL_Code::CaCompromise);
1✔
140
      store.revoke_cert(certsandkeys[3].certificate(), Botan::CRL_Code::CaCompromise);
1✔
141
      store.revoke_cert(certsandkeys[3].certificate(), Botan::CRL_Code::CaCompromise);
1✔
142

143
      {
1✔
144
         const auto crls = store.generate_crls();
1✔
145

146
         result.test_sz_eq("Can't revoke certificate", crls.size(), 2);
1✔
147
         result.test_is_true(
2✔
148
            "Can't revoke certificate",
149
            crls[0].is_revoked(certsandkeys[0].certificate()) ^ crls[1].is_revoked(certsandkeys[0].certificate()));
1✔
150
         result.test_is_true(
2✔
151
            "Can't revoke certificate",
152
            crls[0].is_revoked(certsandkeys[3].certificate()) ^ crls[1].is_revoked(certsandkeys[3].certificate()));
1✔
153
      }
1✔
154

155
      store.affirm_cert(certsandkeys[3].certificate());
1✔
156

157
      {
1✔
158
         const auto crls = store.generate_crls();
1✔
159

160
         result.test_sz_eq("Can't revoke certificate, wrong crl size", crls.size(), 1);
1✔
161
         result.test_is_true("Can't revoke certificate, cert 0 not revoked",
1✔
162
                             crls[0].is_revoked(certsandkeys[0].certificate()));
1✔
163
      }
1✔
164

165
      const auto cert0_crl = store.find_crl_for(certsandkeys[0].certificate());
1✔
166

167
      result.test_is_false("Can't revoke certificate, crl for cert 0", !cert0_crl);
1✔
168
      result.test_sz_eq("Can't revoke certificate, crl for cert 0 size check", cert0_crl->get_revoked().size(), 1);
1✔
169
      result.test_is_true("Can't revoke certificate, no crl for cert 0",
1✔
170
                          cert0_crl->is_revoked(certsandkeys[0].certificate()));
1✔
171

172
      const auto cert3_crl = store.find_crl_for(certsandkeys[3].certificate());
1✔
173

174
      result.test_is_true("Can't revoke certificate, crl for cert 3", !cert3_crl);
1✔
175

176
      return result;
1✔
177
   } catch(std::exception& e) {
3✔
178
      result.test_failure(e.what());
×
179
      return result;
×
180
   }
×
181
}
×
182

183
Test::Result test_certstor_sqlite3_all_subjects_test(const std::vector<CertificateAndKey>& certsandkeys) {
1✔
184
   Test::Result result("Certificate Store SQLITE3 - All subjects");
1✔
185
   try {
1✔
186
      auto rng = Test::new_rng(__func__);
1✔
187
      const std::string passwd(reinterpret_cast<const char*>(rng->random_vec(8).data()), 8);
2✔
188
      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
189
      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng);
1✔
190

191
      for(const auto& a : certsandkeys) {
7✔
192
         store.insert_cert(a.certificate());
6✔
193
      }
194

195
      const auto subjects = store.all_subjects();
1✔
196

197
      result.test_sz_eq("Check subject list length", subjects.size(), 6);
1✔
198

199
      for(const auto& sub : subjects) {
7✔
200
         const std::string ss = sub.to_string();
6✔
201

202
         result.test_is_true("Check subject " + ss,
18✔
203
                             certsandkeys[0].subject_dn() == sub || certsandkeys[1].subject_dn() == sub ||
15✔
204
                                certsandkeys[2].subject_dn() == sub || certsandkeys[3].subject_dn() == sub ||
9✔
205
                                certsandkeys[4].subject_dn() == sub || certsandkeys[5].subject_dn() == sub);
8✔
206
      }
6✔
207
      return result;
1✔
208
   } catch(std::exception& e) {
2✔
209
      result.test_failure(e.what());
×
210
      return result;
×
211
   }
×
212
}
6✔
213

214
Test::Result test_certstor_sqlite3_find_all_certs_test(const std::vector<CertificateAndKey>& certsandkeys) {
1✔
215
   Test::Result result("Certificate Store SQLITE3 - Find all certs");
1✔
216
   try {
1✔
217
      auto rng = Test::new_rng(__func__);
1✔
218
      const std::string passwd(reinterpret_cast<const char*>(rng->random_vec(8).data()), 8);
2✔
219
      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
220
      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng);
1✔
221

222
      for(const auto& a : certsandkeys) {
7✔
223
         store.insert_cert(a.certificate());
6✔
224
      }
225

226
      for(const auto& a : certsandkeys) {
7✔
227
         auto res_vec = store.find_all_certs(a.subject_dn(), a.certificate().subject_key_id());
6✔
228
         if(res_vec.size() != 1) {
6✔
229
            result.test_failure("SQLITE all lookup error");
×
230
            return result;
×
231
         } else {
232
            const std::string a_str = a.subject_dn().to_string();
6✔
233
            const std::string res_str = res_vec.at(0).subject_dn().to_string();
6✔
234
            result.test_eq("Check subject " + a_str, a_str, res_str);
12✔
235
         }
6✔
236
      }
6✔
237

238
      const Botan::X509_Certificate same_dn_1 =
1✔
239
         Botan::X509_Certificate(Test::data_file("x509/bsi/common_14/common_14_sub_ca.ca.pem.crt"));
2✔
240
      const Botan::X509_Certificate same_dn_2 =
1✔
241
         Botan::X509_Certificate(Test::data_file("x509/bsi/common_14/common_14_wrong_sub_ca.ca.pem.crt"));
2✔
242

243
      store.insert_cert(same_dn_1);
1✔
244
      store.insert_cert(same_dn_2);
1✔
245
      auto res_vec = store.find_all_certs(same_dn_1.subject_dn(), {});
1✔
246

247
      if(res_vec.size() != 2) {
1✔
248
         result.test_failure("SQLITE all lookup error (duplicate) " + std::to_string(res_vec.size()));
×
249
         return result;
×
250
      } else {
251
         const std::string cert_dn = same_dn_1.subject_dn().to_string();
1✔
252
         const std::string res0_dn = res_vec.at(0).subject_dn().to_string();
1✔
253

254
         result.test_eq("Check subject " + cert_dn, cert_dn, res0_dn);
1✔
255

256
         const std::string res1_dn = res_vec.at(1).subject_dn().to_string();
1✔
257
         result.test_eq("Check subject " + cert_dn, cert_dn, res1_dn);
2✔
258
      }
1✔
259
   } catch(const std::exception& e) {
1✔
260
      result.test_failure(e.what());
×
261
      return result;
×
262
   }
×
263
   return result;
1✔
264
}
×
265

266
   #endif
267

268
Test::Result test_certstor_all_finders(const std::vector<CertificateAndKey>& certsandkeys) {
1✔
269
   Test::Result result("Certificate Store - Test all finders");
1✔
270

271
   try {
1✔
272
      Botan::Certificate_Store_In_Memory store;
1✔
273

274
      for(const auto& a : certsandkeys) {
7✔
275
         store.add_certificate(a.certificate());
6✔
276
      }
277

278
      for(const auto& certandkey : certsandkeys) {
7✔
279
         const auto& cert = certandkey.certificate();
6✔
280

281
         // find by subject hash
282
         {
6✔
283
            const auto hash = cert.raw_subject_dn_sha256();
6✔
284

285
            const auto found = store.find_cert_by_raw_subject_dn_sha256(hash);
6✔
286
            if(!found) {
6✔
287
               result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA-1"));
×
288
               return result;
×
289
            }
290

291
            result.test_eq("Got wrong certificate", hash, found->raw_subject_dn_sha256());
12✔
292
         }
6✔
293

294
         // find by issuer dn and serial number
295
         {
6✔
296
            const auto& issuer_dn = cert.issuer_dn();
6✔
297
            const auto& serial_number = cert.serial_number();
6✔
298

299
            const auto found = store.find_cert_by_issuer_dn_and_serial_number(issuer_dn, serial_number);
6✔
300
            if(!found) {
6✔
301
               result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA-1"));
×
302
               return result;
×
303
            }
304

305
            result.test_eq("Got wrong certificate", serial_number, found->serial_number());
6✔
306
         }
6✔
307
      }
308

309
      const auto found = store.find_cert_by_raw_subject_dn_sha256(std::vector<uint8_t>(32, 0));
1✔
310
      if(found) {
1✔
311
         result.test_failure("Certificate found for dummy hash");
×
312
         return result;
×
313
      }
314

315
      return result;
316
   } catch(std::exception& e) {
1✔
317
      result.test_failure(e.what());
×
318
      return result;
×
319
   }
×
320
}
×
321

322
Test::Result test_certstor_load_allcert() {
1✔
323
   Test::Result result("Certificate Store - Load every cert of every files");
1✔
324
   // test_dir_bundled dir should contain only one file with 2 certificates
325
   // concatenated (ValidCert and root)
326
   const std::string test_dir_bundled = Test::data_dir("x509/misc/bundledcertdir");
1✔
327

328
   try {
1✔
329
      result.test_note("load certs from dir: " + test_dir_bundled);
1✔
330
      // Certificate_Store_In_Memory constructor loads every cert of every files of the dir.
331
      const Botan::Certificate_Store_In_Memory store(test_dir_bundled);
1✔
332

333
      // X509_Certificate constructor loads only the first certificate found in the file.
334
      const Botan::X509_Certificate root_cert(Test::data_file("x509/x509test/root.pem"));
2✔
335
      const Botan::X509_Certificate valid_cert(Test::data_file("x509/x509test/ValidCert.pem"));
2✔
336
      const std::vector<uint8_t> key_id;
1✔
337
      result.confirm("Root cert found", store.find_cert(root_cert.subject_dn(), key_id) != std::nullopt);
1✔
338
      result.confirm("ValidCert found", store.find_cert(valid_cert.subject_dn(), key_id) != std::nullopt);
1✔
339
      return result;
1✔
340
   } catch(std::exception& e) {
1✔
341
      result.test_failure(e.what());
×
342
      return result;
×
343
   }
×
344
}
1✔
345

346
class Certstor_Tests final : public Test {
1✔
347
   public:
348
      std::vector<Test::Result> run() override {
1✔
349
         struct CertificateAndKeyFilenames {
6✔
350
               const std::string certificate;
351
               const std::string private_key;
352
         } const certsandkeys_filenames[]{
353
            {"cert1.crt", "key01.pem"},
1✔
354
            {"cert2.crt", "key01.pem"},
355
            {"cert3.crt", "key03.pem"},
356
            {"cert4.crt", "key04.pem"},
357
            {"cert5a.crt", "key05.pem"},
358
            {"cert5b.crt", "key06.pem"},
359
         };
13✔
360

361
         std::vector<CertificateAndKey> certsandkeys;
1✔
362

363
         for(const auto& [certpath, keypath] : certsandkeys_filenames) {
7✔
364
            const auto test_cert = Test::data_file("x509/certstor/" + certpath);
6✔
365
            const Botan::X509_Certificate certificate(test_cert);
6✔
366

367
            const auto test_key = Test::data_file("x509/certstor/" + keypath);
6✔
368
            Botan::DataSource_Stream key_stream(test_key);
6✔
369
            const std::shared_ptr<Botan::Private_Key> private_key = Botan::PKCS8::load_key(key_stream);
12✔
370

371
            if(!private_key) {
6✔
372
               Test::Result result("Certificate Store");
×
373
               result.test_failure("Failed to load key from disk at path: " + test_key);
×
374
               return {result};
×
375
            }
×
376

377
            certsandkeys.push_back(CertificateAndKey(certificate, private_key));
6✔
378
         }
6✔
379

380
         std::vector<Test::Result> results;
1✔
381

382
         results.push_back(test_certstor_all_finders(certsandkeys));
2✔
383
         results.push_back(test_certstor_load_allcert());
2✔
384
   #if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
385
         results.push_back(test_certstor_sqlite3_insert_find_remove_test(certsandkeys));
2✔
386
         results.push_back(test_certstor_sqlite3_crl_test(certsandkeys));
2✔
387
         results.push_back(test_certstor_sqlite3_all_subjects_test(certsandkeys));
2✔
388
         results.push_back(test_certstor_sqlite3_find_all_certs_test(certsandkeys));
2✔
389
   #endif
390
         return results;
1✔
391
      }
7✔
392
};
393

394
BOTAN_REGISTER_TEST("x509", "certstor", Certstor_Tests);
395
#endif
396
}  // namespace
397
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc