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

randombit / botan / 23877866835

02 Apr 2026 12:38AM UTC coverage: 86.172% (-3.4%) from 89.526%
23877866835

push

github

web-flow
Merge pull request #5510 from randombit/jack/certstore-search

Improve search and indexing in Certificate_Store implementations

41828 of 48540 relevant lines covered (86.17%)

28184914.87 hits per line

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

87.63
/src/lib/x509/certstor_sql/certstor_sql.cpp
1
/*
2
* Certificate Store in SQL
3
* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
4
* (C) 2018 Jack Lloyd
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/certstor_sql.h>
10

11
#include <botan/asn1_obj.h>
12
#include <botan/asn1_time.h>
13
#include <botan/ber_dec.h>
14
#include <botan/data_src.h>
15
#include <botan/pk_keys.h>
16
#include <botan/pkcs8.h>
17
#include <botan/pkix_types.h>
18

19
namespace Botan {
20

21
Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
4✔
22
                                                   std::string_view passwd,
23
                                                   RandomNumberGenerator& rng,
24
                                                   std::string_view table_prefix) :
4✔
25
      m_rng(rng), m_database(std::move(db)), m_prefix(table_prefix), m_password(passwd) {
12✔
26
   m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix +
12✔
27
                            "certificates (                \
28
                                 fingerprint       BLOB PRIMARY KEY,   \
29
                                 subject_dn        BLOB,               \
30
                                 key_id            BLOB,               \
31
                                 priv_fingerprint  BLOB,               \
32
                                 certificate       BLOB UNIQUE NOT NULL\
33
                             )");
34
   m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix +
12✔
35
                            "keys (\
36
                                 fingerprint BLOB PRIMARY KEY,                \
37
                                 key         BLOB UNIQUE NOT NULL             \
38
                             )");
39
   m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix +
12✔
40
                            "revoked (\
41
                                 fingerprint BLOB PRIMARY KEY,                   \
42
                                 reason      BLOB NOT NULL,                      \
43
                                 time        BLOB NOT NULL                       \
44
                            )");
45
}
4✔
46

47
// Certificate handling
48
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn,
24✔
49
                                                                    const std::vector<uint8_t>& key_id) const {
50
   std::shared_ptr<SQL_Database::Statement> stmt;
24✔
51

52
   const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
24✔
53

54
   if(key_id.empty()) {
24✔
55
      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix +
12✔
56
                                       "certificates WHERE subject_dn == ?1 LIMIT 1");
6✔
57
      stmt->bind(1, dn_encoding);
6✔
58
   } else {
59
      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix +
36✔
60
                                       "certificates WHERE\
61
                                        subject_dn == ?1 AND (key_id == NULL OR key_id == ?2) LIMIT 1");
18✔
62
      stmt->bind(1, dn_encoding);
18✔
63
      stmt->bind(2, key_id);
18✔
64
   }
65

66
   while(stmt->step()) {
24✔
67
      auto blob = stmt->get_blob(0);
18✔
68
      return X509_Certificate(blob.first, blob.second);
18✔
69
   }
70

71
   return std::optional<X509_Certificate>();
6✔
72
}
48✔
73

74
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn,
7✔
75
                                                                       const std::vector<uint8_t>& key_id) const {
76
   std::vector<X509_Certificate> certs;
7✔
77

78
   std::shared_ptr<SQL_Database::Statement> stmt;
7✔
79

80
   const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
7✔
81

82
   if(key_id.empty()) {
7✔
83
      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1");
2✔
84
      stmt->bind(1, dn_encoding);
1✔
85
   } else {
86
      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix +
12✔
87
                                       "certificates WHERE\
88
                                        subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
6✔
89
      stmt->bind(1, dn_encoding);
6✔
90
      stmt->bind(2, key_id);
6✔
91
   }
92

93
   while(stmt->step()) {
15✔
94
      auto blob = stmt->get_blob(0);
8✔
95
      certs.push_back(X509_Certificate(blob.first, blob.second));
16✔
96
   }
97

98
   return certs;
14✔
99
}
14✔
100

101
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(
×
102
   const std::vector<uint8_t>& /*key_hash*/) const {
103
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_pubkey_sha1");
×
104
}
105

106
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(
×
107
   const std::vector<uint8_t>& /*subject_hash*/) const {
108
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
×
109
}
110

111
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number(
×
112
   const X509_DN& /*issuer_dn*/, std::span<const uint8_t> /*serial_number*/) const {
113
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number");
×
114
}
115

116
std::optional<X509_CRL> Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const {
2✔
117
   const auto all_crls = generate_crls();
2✔
118

119
   for(const auto& crl : all_crls) {
3✔
120
      if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) {
2✔
121
         return crl;
1✔
122
      }
123
   }
124

125
   return std::optional<X509_CRL>();
1✔
126
}
2✔
127

128
std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const {
1✔
129
   std::vector<X509_DN> ret;
1✔
130
   auto stmt = m_database->new_statement("SELECT subject_dn FROM " + m_prefix + "certificates");
3✔
131

132
   while(stmt->step()) {
7✔
133
      auto blob = stmt->get_blob(0);
6✔
134
      BER_Decoder dec(std::span<const uint8_t>{blob.first, blob.second});
6✔
135
      X509_DN dn;
6✔
136

137
      dn.decode_from(dec);
6✔
138

139
      ret.push_back(dn);
6✔
140
   }
6✔
141

142
   return ret;
1✔
143
}
1✔
144

145
bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert) {
29✔
146
   const std::vector<uint8_t> dn_encoding = cert.subject_dn().BER_encode();
29✔
147
   const std::vector<uint8_t> cert_encoding = cert.BER_encode();
29✔
148

149
   auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix +
87✔
150
                                         "certificates (\
151
                                         fingerprint,          \
152
                                         subject_dn,           \
153
                                         key_id,               \
154
                                         priv_fingerprint,     \
155
                                         certificate           \
156
                                     ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
29✔
157

158
   stmt->bind(1, cert.fingerprint("SHA-256"));
29✔
159
   stmt->bind(2, dn_encoding);
29✔
160
   stmt->bind(3, cert.subject_key_id());
29✔
161
   stmt->bind(4, std::vector<uint8_t>());
29✔
162
   stmt->bind(5, cert_encoding);
29✔
163
   stmt->spin();
29✔
164

165
   return true;
58✔
166
}
29✔
167

168
bool Certificate_Store_In_SQL::contains(const X509_Certificate& cert) const {
×
169
   auto stmt = m_database->new_statement("SELECT 1 FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
×
170
   stmt->bind(1, cert.fingerprint("SHA-256"));
×
171
   return stmt->step();
×
172
}
×
173

174
bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert) {
6✔
175
   if(!find_cert(cert.subject_dn(), cert.subject_key_id())) {
12✔
176
      return false;
177
   }
178

179
   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
18✔
180

181
   stmt->bind(1, cert.fingerprint("SHA-256"));
6✔
182
   stmt->spin();
6✔
183

184
   return true;
6✔
185
}
6✔
186

187
// Private key handling
188
std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const {
18✔
189
   auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix +
54✔
190
                                         "keys "
191
                                         "JOIN " +
36✔
192
                                         m_prefix + "certificates ON " + m_prefix + "keys.fingerprint == " + m_prefix +
108✔
193
                                         "certificates.priv_fingerprint "
194
                                         "WHERE " +
36✔
195
                                         m_prefix + "certificates.fingerprint == ?1");
54✔
196
   stmt->bind(1, cert.fingerprint("SHA-256"));
18✔
197

198
   std::shared_ptr<const Private_Key> key;
18✔
199
   while(stmt->step()) {
23✔
200
      auto blob = stmt->get_blob(0);
5✔
201
      DataSource_Memory src(blob.first, blob.second);
5✔
202
      key = PKCS8::load_key(src, m_password);
10✔
203
   }
5✔
204

205
   return key;
18✔
206
}
18✔
207

208
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const {
5✔
209
   auto fprint = key.fingerprint_private("SHA-256");
5✔
210
   auto stmt =
5✔
211
      m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE priv_fingerprint == ?1");
15✔
212

213
   stmt->bind(1, fprint);
5✔
214

215
   std::vector<X509_Certificate> certs;
5✔
216
   while(stmt->step()) {
11✔
217
      auto blob = stmt->get_blob(0);
6✔
218
      certs.push_back(X509_Certificate(blob.first, blob.second));
12✔
219
   }
220

221
   return certs;
5✔
222
}
5✔
223

224
bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
6✔
225
   insert_cert(cert);
6✔
226

227
   if(find_key(cert)) {
6✔
228
      return false;
229
   }
230

231
   auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
6✔
232
   auto fprint = key.fingerprint_private("SHA-256");
6✔
233

234
   auto stmt1 =
6✔
235
      m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )");
18✔
236

237
   stmt1->bind(1, fprint);
6✔
238
   stmt1->bind(2, pkcs8.data(), pkcs8.size());
6✔
239
   stmt1->spin();
6✔
240

241
   auto stmt2 = m_database->new_statement("UPDATE " + m_prefix +
18✔
242
                                          "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2");
6✔
243

244
   stmt2->bind(1, fprint);
6✔
245
   stmt2->bind(2, cert.fingerprint("SHA-256"));
6✔
246
   stmt2->spin();
6✔
247

248
   return true;
6✔
249
}
12✔
250

251
void Certificate_Store_In_SQL::remove_key(const Private_Key& key) {
5✔
252
   auto fprint = key.fingerprint_private("SHA-256");
5✔
253
   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1");
15✔
254

255
   stmt->bind(1, fprint);
5✔
256
   stmt->spin();
5✔
257
}
5✔
258

259
// Revocation
260
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) {
×
261
   // TODO(Botan4) require that time be valid
262
   insert_cert(cert);
×
263

264
   auto stmt1 = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix +
×
265
                                          "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
×
266

267
   stmt1->bind(1, cert.fingerprint("SHA-256"));
×
268
   stmt1->bind(2, static_cast<uint32_t>(code));
×
269

270
   if(time.time_is_set()) {
×
271
      stmt1->bind(3, time.BER_encode());
×
272
   } else {
273
      stmt1->bind(3, static_cast<size_t>(-1));
×
274
   }
275

276
   stmt1->spin();
×
277
}
×
278

279
// Revocation
280
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code) {
3✔
281
   insert_cert(cert);
3✔
282

283
   auto stmt1 = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix +
9✔
284
                                          "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
3✔
285

286
   stmt1->bind(1, cert.fingerprint("SHA-256"));
3✔
287
   stmt1->bind(2, static_cast<uint32_t>(code));
3✔
288
   stmt1->bind(3, static_cast<size_t>(-1));
3✔
289

290
   stmt1->spin();
3✔
291
}
3✔
292

293
void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert) {
1✔
294
   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1");
3✔
295

296
   stmt->bind(1, cert.fingerprint("SHA-256"));
1✔
297
   stmt->spin();
1✔
298
}
1✔
299

300
std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const {
4✔
301
   auto stmt = m_database->new_statement("SELECT certificate,reason,time FROM " + m_prefix +
12✔
302
                                         "revoked "
303
                                         "JOIN " +
8✔
304
                                         m_prefix + "certificates ON " + m_prefix +
16✔
305
                                         "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
16✔
306

307
   std::map<X509_DN, std::vector<CRL_Entry>> crls;
4✔
308
   while(stmt->step()) {
9✔
309
      auto blob = stmt->get_blob(0);
5✔
310
      auto cert = X509_Certificate(std::vector<uint8_t>(blob.first, blob.first + blob.second));
5✔
311
      auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
5✔
312
      auto ent = CRL_Entry(cert, code);
5✔
313

314
      auto i = crls.find(cert.issuer_dn());
5✔
315
      if(i == crls.end()) {
5✔
316
         crls.insert(std::make_pair(cert.issuer_dn(), std::vector<CRL_Entry>({ent})));
15✔
317
      } else {
318
         i->second.push_back(ent);
×
319
      }
320
   }
5✔
321

322
   const X509_Time t(std::chrono::system_clock::now());
4✔
323

324
   std::vector<X509_CRL> ret;
4✔
325
   ret.reserve(crls.size());
4✔
326

327
   for(const auto& p : crls) {
9✔
328
      ret.push_back(X509_CRL(p.first, t, t, p.second));
10✔
329
   }
330

331
   return ret;
4✔
332
}
13✔
333

334
}  // namespace Botan
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