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

randombit / botan / 20579846577

29 Dec 2025 06:24PM UTC coverage: 90.415% (+0.2%) from 90.243%
20579846577

push

github

web-flow
Merge pull request #5167 from randombit/jack/src-size-reductions

Changes to reduce unnecessary inclusions

101523 of 112285 relevant lines covered (90.42%)

12817276.56 hits per line

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

90.16
/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/ber_dec.h>
13
#include <botan/data_src.h>
14
#include <botan/pk_keys.h>
15
#include <botan/pkcs8.h>
16
#include <botan/pkix_types.h>
17

18
namespace Botan {
19

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

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

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

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

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

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

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

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

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

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

92
   const std::optional<X509_Certificate> cert;
7✔
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;
7✔
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
   auto all_crls = generate_crls();
2✔
118

119
   for(auto crl : all_crls) {
3✔
120
      if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) {
2✔
121
         return crl;
1✔
122
      }
123
   }
2✔
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(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::remove_cert(const X509_Certificate& cert) {
6✔
169
   if(!find_cert(cert.subject_dn(), cert.subject_key_id())) {
12✔
170
      return false;
171
   }
172

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

175
   stmt->bind(1, cert.fingerprint("SHA-256"));
6✔
176
   stmt->spin();
6✔
177

178
   return true;
6✔
179
}
6✔
180

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

192
   std::shared_ptr<const Private_Key> key;
18✔
193
   while(stmt->step()) {
23✔
194
      auto blob = stmt->get_blob(0);
5✔
195
      DataSource_Memory src(blob.first, blob.second);
5✔
196
      key = PKCS8::load_key(src, m_password);
10✔
197
   }
5✔
198

199
   return key;
18✔
200
}
18✔
201

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

207
   stmt->bind(1, fprint);
5✔
208

209
   std::vector<X509_Certificate> certs;
5✔
210
   while(stmt->step()) {
11✔
211
      auto blob = stmt->get_blob(0);
6✔
212
      certs.push_back(X509_Certificate(blob.first, blob.second));
12✔
213
   }
214

215
   return certs;
5✔
216
}
5✔
217

218
bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
6✔
219
   insert_cert(cert);
6✔
220

221
   if(find_key(cert)) {
6✔
222
      return false;
223
   }
224

225
   auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
6✔
226
   auto fprint = key.fingerprint_private("SHA-256");
6✔
227

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

231
   stmt1->bind(1, fprint);
6✔
232
   stmt1->bind(2, pkcs8.data(), pkcs8.size());
6✔
233
   stmt1->spin();
6✔
234

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

238
   stmt2->bind(1, fprint);
6✔
239
   stmt2->bind(2, cert.fingerprint("SHA-256"));
6✔
240
   stmt2->spin();
6✔
241

242
   return true;
6✔
243
}
12✔
244

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

249
   stmt->bind(1, fprint);
5✔
250
   stmt->spin();
5✔
251
}
5✔
252

253
// Revocation
254
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) {
×
255
   // TODO(Botan4) require that time be valid
256
   insert_cert(cert);
×
257

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

261
   stmt1->bind(1, cert.fingerprint("SHA-256"));
×
262
   stmt1->bind(2, static_cast<uint32_t>(code));
×
263

264
   if(time.time_is_set()) {
×
265
      stmt1->bind(3, time.BER_encode());
×
266
   } else {
267
      stmt1->bind(3, static_cast<size_t>(-1));
×
268
   }
269

270
   stmt1->spin();
×
271
}
×
272

273
// Revocation
274
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code) {
3✔
275
   insert_cert(cert);
3✔
276

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

280
   stmt1->bind(1, cert.fingerprint("SHA-256"));
3✔
281
   stmt1->bind(2, static_cast<uint32_t>(code));
3✔
282
   stmt1->bind(3, static_cast<size_t>(-1));
3✔
283

284
   stmt1->spin();
3✔
285
}
3✔
286

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

290
   stmt->bind(1, cert.fingerprint("SHA-256"));
1✔
291
   stmt->spin();
1✔
292
}
1✔
293

294
std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const {
4✔
295
   auto stmt = m_database->new_statement("SELECT certificate,reason,time FROM " + m_prefix +
12✔
296
                                         "revoked "
297
                                         "JOIN " +
8✔
298
                                         m_prefix + "certificates ON " + m_prefix +
16✔
299
                                         "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
16✔
300

301
   std::map<X509_DN, std::vector<CRL_Entry>> crls;
4✔
302
   while(stmt->step()) {
9✔
303
      auto blob = stmt->get_blob(0);
5✔
304
      auto cert = X509_Certificate(std::vector<uint8_t>(blob.first, blob.first + blob.second));
5✔
305
      auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
5✔
306
      auto ent = CRL_Entry(cert, code);
5✔
307

308
      auto i = crls.find(cert.issuer_dn());
5✔
309
      if(i == crls.end()) {
5✔
310
         crls.insert(std::make_pair(cert.issuer_dn(), std::vector<CRL_Entry>({ent})));
15✔
311
      } else {
312
         i->second.push_back(ent);
×
313
      }
314
   }
5✔
315

316
   const X509_Time t(std::chrono::system_clock::now());
4✔
317

318
   std::vector<X509_CRL> ret;
4✔
319
   ret.reserve(crls.size());
4✔
320

321
   for(const auto& p : crls) {
9✔
322
      ret.push_back(X509_CRL(p.first, t, t, p.second));
10✔
323
   }
324

325
   return ret;
4✔
326
}
13✔
327

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