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

randombit / botan / 27806188297

18 Jun 2026 04:12PM UTC coverage: 89.37% (-0.03%) from 89.397%
27806188297

push

github

web-flow
Merge pull request #5677 from randombit/jack/oid-names

Add OID::registered_name

111637 of 124915 relevant lines covered (89.37%)

10895907.86 hits per line

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

88.89
/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/assert.h>
14
#include <botan/ber_dec.h>
15
#include <botan/data_src.h>
16
#include <botan/pk_keys.h>
17
#include <botan/pkcs8.h>
18
#include <botan/pkix_types.h>
19
#include <botan/internal/fmt.h>
20

21
namespace Botan {
22

23
Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
4✔
24
                                                   std::string_view passwd,
25
                                                   RandomNumberGenerator& rng,
26
                                                   std::string_view table_prefix) :
4✔
27
      m_rng(rng),
4✔
28
      m_database(std::move(db)),
4✔
29
      m_db_cert_table(fmt("{}certificates", table_prefix)),
4✔
30
      m_db_keys_table(fmt("{}keys", table_prefix)),
4✔
31
      m_db_crls_table(fmt("{}revoked", table_prefix)),
4✔
32
      m_password(passwd) {
12✔
33
   using DB = SQL_Database;
4✔
34
   const auto blob = DB::Column_Type::Blob;
4✔
35
   const auto integer = DB::Column_Type::Integer;
4✔
36

37
   BOTAN_ARG_CHECK(m_database->is_valid_table_name(m_db_cert_table), "Invalid table name");
4✔
38
   BOTAN_ARG_CHECK(m_database->is_valid_table_name(m_db_keys_table), "Invalid table name");
4✔
39
   BOTAN_ARG_CHECK(m_database->is_valid_table_name(m_db_crls_table), "Invalid table name");
4✔
40

41
   m_database->create_table(DB::Table_Schema(m_db_cert_table,
36✔
42
                                             {
43
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
44
                                                DB::Column("subject_dn", blob),
45
                                                DB::Column("key_id", blob),
46
                                                DB::Column("priv_fingerprint", blob),
47
                                                DB::Column("certificate", blob).not_null(),
8✔
48
                                             })
20✔
49
                               .if_not_exists());
4✔
50

51
   m_database->create_table(DB::Table_Schema(m_db_keys_table,
24✔
52
                                             {
53
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
54
                                                DB::Column("key", blob).not_null(),
12✔
55
                                             })
8✔
56
                               .if_not_exists());
4✔
57

58
   m_database->create_table(DB::Table_Schema(m_db_crls_table,
28✔
59
                                             {
60
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
61
                                                DB::Column("reason", integer).not_null(),
12✔
62
                                                DB::Column("time", integer),
63
                                             })
12✔
64
                               .if_not_exists());
4✔
65
}
64✔
66

67
// Certificate handling
68
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn,
24✔
69
                                                                    const std::vector<uint8_t>& key_id) const {
70
   std::shared_ptr<SQL_Database::Statement> stmt;
24✔
71

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

74
   if(key_id.empty()) {
24✔
75
      stmt = m_database->select("certificate", m_db_cert_table, "subject_dn = ?1", 1);
6✔
76
      stmt->bind(1, dn_encoding);
6✔
77
   } else {
78
      stmt =
18✔
79
         m_database->select("certificate", m_db_cert_table, "subject_dn = ?1 AND (key_id IS NULL OR key_id = ?2)", 1);
18✔
80
      stmt->bind(1, dn_encoding);
18✔
81
      stmt->bind(2, key_id);
18✔
82
   }
83

84
   while(stmt->step()) {
24✔
85
      return X509_Certificate(stmt->get_blob(0));
18✔
86
   }
87

88
   return std::optional<X509_Certificate>();
6✔
89
}
48✔
90

91
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn,
7✔
92
                                                                       const std::vector<uint8_t>& key_id) const {
93
   std::vector<X509_Certificate> certs;
7✔
94

95
   std::shared_ptr<SQL_Database::Statement> stmt;
7✔
96

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

99
   if(key_id.empty()) {
7✔
100
      stmt = m_database->select("certificate", m_db_cert_table, "subject_dn = ?1");
1✔
101
      stmt->bind(1, dn_encoding);
1✔
102
   } else {
103
      stmt = m_database->select("certificate", m_db_cert_table, "subject_dn = ?1 AND (key_id IS NULL OR key_id = ?2)");
6✔
104
      stmt->bind(1, dn_encoding);
6✔
105
      stmt->bind(2, key_id);
6✔
106
   }
107

108
   while(stmt->step()) {
15✔
109
      certs.push_back(X509_Certificate(stmt->get_blob(0)));
16✔
110
   }
111

112
   return certs;
14✔
113
}
14✔
114

115
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(
×
116
   const std::vector<uint8_t>& /*key_hash*/) const {
117
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_pubkey_sha1");
×
118
}
119

120
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(
×
121
   const std::vector<uint8_t>& /*subject_hash*/) const {
122
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
×
123
}
124

125
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number(
×
126
   const X509_DN& /*issuer_dn*/, std::span<const uint8_t> /*serial_number*/) const {
127
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number");
×
128
}
129

130
std::optional<X509_CRL> Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const {
2✔
131
   const auto all_crls = generate_crls();
2✔
132

133
   for(const auto& crl : all_crls) {
3✔
134
      if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) {
2✔
135
         return crl;
1✔
136
      }
137
   }
138

139
   return std::optional<X509_CRL>();
1✔
140
}
2✔
141

142
std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const {
1✔
143
   std::vector<X509_DN> ret;
1✔
144
   auto stmt = m_database->select("subject_dn", m_db_cert_table);
1✔
145

146
   while(stmt->step()) {
7✔
147
      BER_Decoder dec(stmt->get_blob(0), BER_Decoder::Limits::DER());
6✔
148
      X509_DN dn;
6✔
149

150
      dn.decode_from(dec);
6✔
151

152
      ret.push_back(dn);
6✔
153
   }
6✔
154

155
   return ret;
1✔
156
}
1✔
157

158
bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert) {
29✔
159
   const std::vector<uint8_t> dn_encoding = cert.subject_dn().BER_encode();
29✔
160
   const std::vector<uint8_t> cert_encoding = cert.BER_encode();
29✔
161

162
   auto stmt =
29✔
163
      m_database->upsert(m_db_cert_table, {"fingerprint", "subject_dn", "key_id", "priv_fingerprint", "certificate"});
29✔
164

165
   stmt->bind(1, cert.fingerprint("SHA-256"));
29✔
166
   stmt->bind(2, dn_encoding);
29✔
167
   stmt->bind(3, cert.subject_key_id());
29✔
168
   stmt->bind(4, std::vector<uint8_t>());
29✔
169
   stmt->bind(5, cert_encoding);
29✔
170
   stmt->spin();
29✔
171

172
   return true;
58✔
173
}
29✔
174

175
bool Certificate_Store_In_SQL::contains(const X509_Certificate& cert) const {
×
176
   auto stmt = m_database->select("1", m_db_cert_table, "fingerprint = ?1");
×
177
   stmt->bind(1, cert.fingerprint("SHA-256"));
×
178
   return stmt->step();
×
179
}
×
180

181
bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert) {
6✔
182
   if(!find_cert(cert.subject_dn(), cert.subject_key_id())) {
12✔
183
      return false;
184
   }
185

186
   auto stmt = m_database->new_statement(fmt("DELETE FROM {} WHERE fingerprint = ?1", m_db_cert_table));
6✔
187

188
   stmt->bind(1, cert.fingerprint("SHA-256"));
6✔
189
   stmt->spin();
6✔
190

191
   return true;
6✔
192
}
6✔
193

194
// Private key handling
195
std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const {
18✔
196
   auto stmt =
18✔
197
      m_database->new_statement(fmt("SELECT key FROM {} JOIN {} ON {}.fingerprint = {}.priv_fingerprint "
18✔
198
                                    "WHERE {}.fingerprint = ?1",
199
                                    m_db_keys_table,
18✔
200
                                    m_db_cert_table,
201
                                    m_db_keys_table,
18✔
202
                                    m_db_cert_table,
203
                                    m_db_cert_table));
18✔
204
   stmt->bind(1, cert.fingerprint("SHA-256"));
18✔
205

206
   std::shared_ptr<const Private_Key> key;
18✔
207
   while(stmt->step()) {
23✔
208
      DataSource_Memory src(stmt->get_blob(0));
5✔
209
      key = PKCS8::load_key(src, m_password);
10✔
210
   }
5✔
211

212
   return key;
18✔
213
}
18✔
214

215
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const {
5✔
216
   auto fprint = key.fingerprint_private("SHA-256");
5✔
217
   auto stmt = m_database->select("certificate", m_db_cert_table, "priv_fingerprint = ?1");
5✔
218

219
   stmt->bind(1, fprint);
5✔
220

221
   std::vector<X509_Certificate> certs;
5✔
222
   while(stmt->step()) {
11✔
223
      certs.push_back(X509_Certificate(stmt->get_blob(0)));
12✔
224
   }
225

226
   return certs;
5✔
227
}
5✔
228

229
bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
6✔
230
   insert_cert(cert);
6✔
231

232
   if(find_key(cert)) {
6✔
233
      return false;
234
   }
235

236
   auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
6✔
237
   auto fprint = key.fingerprint_private("SHA-256");
6✔
238

239
   auto stmt1 = m_database->upsert(m_db_keys_table, {"fingerprint", "key"});
6✔
240

241
   stmt1->bind(1, fprint);
6✔
242
   stmt1->bind(2, pkcs8.data(), pkcs8.size());
6✔
243
   stmt1->spin();
6✔
244

245
   auto stmt2 =
6✔
246
      m_database->new_statement(fmt("UPDATE {} SET priv_fingerprint = ?1 WHERE fingerprint = ?2", m_db_cert_table));
6✔
247

248
   stmt2->bind(1, fprint);
6✔
249
   stmt2->bind(2, cert.fingerprint("SHA-256"));
6✔
250
   stmt2->spin();
6✔
251

252
   return true;
6✔
253
}
12✔
254

255
void Certificate_Store_In_SQL::remove_key(const Private_Key& key) {
5✔
256
   auto fprint = key.fingerprint_private("SHA-256");
5✔
257
   auto stmt = m_database->new_statement(fmt("DELETE FROM {} WHERE fingerprint = ?1", m_db_keys_table));
5✔
258

259
   stmt->bind(1, fprint);
5✔
260
   stmt->spin();
5✔
261
}
5✔
262

263
// Revocation
264
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) {
×
265
   // TODO(Botan4) require that time be valid
266
   insert_cert(cert);
×
267

268
   auto stmt1 = m_database->upsert(m_db_crls_table, {"fingerprint", "reason", "time"});
×
269

270
   stmt1->bind(1, cert.fingerprint("SHA-256"));
×
271
   stmt1->bind(2, static_cast<uint32_t>(code));
×
272

273
   if(time.time_is_set()) {
×
274
      stmt1->bind(3, time.to_std_timepoint());
×
275
   } else {
276
      stmt1->bind_null(3);
×
277
   }
278

279
   stmt1->spin();
×
280
}
×
281

282
// Revocation
283
void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code) {
3✔
284
   insert_cert(cert);
3✔
285

286
   auto stmt1 = m_database->upsert(m_db_crls_table, {"fingerprint", "reason", "time"});
3✔
287

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

292
   stmt1->spin();
3✔
293
}
3✔
294

295
void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert) {
1✔
296
   auto stmt = m_database->new_statement(fmt("DELETE FROM {} WHERE fingerprint = ?1", m_db_crls_table));
1✔
297

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

302
std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const {
4✔
303
   auto stmt =
4✔
304
      m_database->new_statement(fmt("SELECT certificate,reason,time FROM {} JOIN {} ON {}.fingerprint = "
4✔
305
                                    "{}.fingerprint",
306
                                    m_db_crls_table,
4✔
307
                                    m_db_cert_table,
308
                                    m_db_cert_table,
4✔
309
                                    m_db_crls_table));
4✔
310

311
   std::map<X509_DN, std::vector<CRL_Entry>> crls;
4✔
312
   while(stmt->step()) {
9✔
313
      auto cert = X509_Certificate(stmt->get_blob(0));
5✔
314
      auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
5✔
315
      auto ent = CRL_Entry(cert, code);
5✔
316

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

325
   const X509_Time t(std::chrono::system_clock::now());
4✔
326

327
   std::vector<X509_CRL> ret;
4✔
328
   ret.reserve(crls.size());
4✔
329

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

334
   return ret;
4✔
335
}
13✔
336

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