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

randombit / botan / 26141725099

19 May 2026 08:32PM UTC coverage: 89.343% (+0.009%) from 89.334%
26141725099

push

github

web-flow
Merge pull request #5609 from randombit/jack/improve-http

Improve the HTTP 1.0 client used for OCSP/CRL

109341 of 122383 relevant lines covered (89.34%)

11264402.07 hits per line

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

88.42
/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
   using DB = SQL_Database;
4✔
27
   const auto blob = DB::Column_Type::Blob;
4✔
28
   const auto integer = DB::Column_Type::Integer;
4✔
29

30
   m_database->create_table(DB::Table_Schema(m_prefix + "certificates",
36✔
31
                                             {
32
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
33
                                                DB::Column("subject_dn", blob),
34
                                                DB::Column("key_id", blob),
35
                                                DB::Column("priv_fingerprint", blob),
36
                                                DB::Column("certificate", blob).not_null(),
8✔
37
                                             })
20✔
38
                               .if_not_exists());
4✔
39

40
   m_database->create_table(DB::Table_Schema(m_prefix + "keys",
24✔
41
                                             {
42
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
43
                                                DB::Column("key", blob).not_null(),
12✔
44
                                             })
8✔
45
                               .if_not_exists());
4✔
46

47
   m_database->create_table(DB::Table_Schema(m_prefix + "revoked",
28✔
48
                                             {
49
                                                DB::Column("fingerprint", blob).primary_key(),
12✔
50
                                                DB::Column("reason", integer).not_null(),
12✔
51
                                                DB::Column("time", integer),
52
                                             })
12✔
53
                               .if_not_exists());
4✔
54
}
64✔
55

56
// Certificate handling
57
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn,
24✔
58
                                                                    const std::vector<uint8_t>& key_id) const {
59
   std::shared_ptr<SQL_Database::Statement> stmt;
24✔
60

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

63
   if(key_id.empty()) {
24✔
64
      stmt = m_database->select("certificate", m_prefix + "certificates", "subject_dn = ?1", 1);
6✔
65
      stmt->bind(1, dn_encoding);
6✔
66
   } else {
67
      stmt = m_database->select(
18✔
68
         "certificate", m_prefix + "certificates", "subject_dn = ?1 AND (key_id IS NULL OR key_id = ?2)", 1);
36✔
69
      stmt->bind(1, dn_encoding);
18✔
70
      stmt->bind(2, key_id);
18✔
71
   }
72

73
   while(stmt->step()) {
24✔
74
      return X509_Certificate(stmt->get_blob(0));
18✔
75
   }
76

77
   return std::optional<X509_Certificate>();
6✔
78
}
48✔
79

80
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn,
7✔
81
                                                                       const std::vector<uint8_t>& key_id) const {
82
   std::vector<X509_Certificate> certs;
7✔
83

84
   std::shared_ptr<SQL_Database::Statement> stmt;
7✔
85

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

88
   if(key_id.empty()) {
7✔
89
      stmt = m_database->select("certificate", m_prefix + "certificates", "subject_dn = ?1");
1✔
90
      stmt->bind(1, dn_encoding);
1✔
91
   } else {
92
      stmt = m_database->select(
6✔
93
         "certificate", m_prefix + "certificates", "subject_dn = ?1 AND (key_id IS NULL OR key_id = ?2)");
12✔
94
      stmt->bind(1, dn_encoding);
6✔
95
      stmt->bind(2, key_id);
6✔
96
   }
97

98
   while(stmt->step()) {
15✔
99
      certs.push_back(X509_Certificate(stmt->get_blob(0)));
16✔
100
   }
101

102
   return certs;
14✔
103
}
14✔
104

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

110
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(
×
111
   const std::vector<uint8_t>& /*subject_hash*/) const {
112
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
×
113
}
114

115
std::optional<X509_Certificate> Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number(
×
116
   const X509_DN& /*issuer_dn*/, std::span<const uint8_t> /*serial_number*/) const {
117
   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number");
×
118
}
119

120
std::optional<X509_CRL> Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const {
2✔
121
   const auto all_crls = generate_crls();
2✔
122

123
   for(const auto& crl : all_crls) {
3✔
124
      if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) {
2✔
125
         return crl;
1✔
126
      }
127
   }
128

129
   return std::optional<X509_CRL>();
1✔
130
}
2✔
131

132
std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const {
1✔
133
   std::vector<X509_DN> ret;
1✔
134
   auto stmt = m_database->select("subject_dn", m_prefix + "certificates");
1✔
135

136
   while(stmt->step()) {
7✔
137
      BER_Decoder dec(stmt->get_blob(0), BER_Decoder::Limits::DER());
6✔
138
      X509_DN dn;
6✔
139

140
      dn.decode_from(dec);
6✔
141

142
      ret.push_back(dn);
6✔
143
   }
6✔
144

145
   return ret;
1✔
146
}
1✔
147

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

152
   auto stmt = m_database->upsert(m_prefix + "certificates",
29✔
153
                                  {"fingerprint", "subject_dn", "key_id", "priv_fingerprint", "certificate"});
29✔
154

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

162
   return true;
58✔
163
}
29✔
164

165
bool Certificate_Store_In_SQL::contains(const X509_Certificate& cert) const {
×
166
   auto stmt = m_database->select("1", m_prefix + "certificates", "fingerprint = ?1");
×
167
   stmt->bind(1, cert.fingerprint("SHA-256"));
×
168
   return stmt->step();
×
169
}
×
170

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

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

178
   stmt->bind(1, cert.fingerprint("SHA-256"));
6✔
179
   stmt->spin();
6✔
180

181
   return true;
6✔
182
}
6✔
183

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

195
   std::shared_ptr<const Private_Key> key;
18✔
196
   while(stmt->step()) {
23✔
197
      DataSource_Memory src(stmt->get_blob(0));
5✔
198
      key = PKCS8::load_key(src, m_password);
10✔
199
   }
5✔
200

201
   return key;
18✔
202
}
18✔
203

204
std::vector<X509_Certificate> Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const {
5✔
205
   auto fprint = key.fingerprint_private("SHA-256");
5✔
206
   auto stmt = m_database->select("certificate", m_prefix + "certificates", "priv_fingerprint = ?1");
5✔
207

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

210
   std::vector<X509_Certificate> certs;
5✔
211
   while(stmt->step()) {
11✔
212
      certs.push_back(X509_Certificate(stmt->get_blob(0)));
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 = m_database->upsert(m_prefix + "keys", {"fingerprint", "key"});
6✔
229

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

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

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

241
   return true;
6✔
242
}
12✔
243

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

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

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

257
   auto stmt1 = m_database->upsert(m_prefix + "revoked", {"fingerprint", "reason", "time"});
×
258

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

262
   if(time.time_is_set()) {
×
263
      stmt1->bind(3, time.to_std_timepoint());
×
264
   } else {
265
      stmt1->bind_null(3);
×
266
   }
267

268
   stmt1->spin();
×
269
}
×
270

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

275
   auto stmt1 = m_database->upsert(m_prefix + "revoked", {"fingerprint", "reason", "time"});
3✔
276

277
   stmt1->bind(1, cert.fingerprint("SHA-256"));
3✔
278
   stmt1->bind(2, static_cast<uint32_t>(code));
3✔
279
   stmt1->bind_null(3);
3✔
280

281
   stmt1->spin();
3✔
282
}
3✔
283

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

287
   stmt->bind(1, cert.fingerprint("SHA-256"));
1✔
288
   stmt->spin();
1✔
289
}
1✔
290

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

298
   std::map<X509_DN, std::vector<CRL_Entry>> crls;
4✔
299
   while(stmt->step()) {
9✔
300
      auto cert = X509_Certificate(stmt->get_blob(0));
5✔
301
      auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
5✔
302
      auto ent = CRL_Entry(cert, code);
5✔
303

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

312
   const X509_Time t(std::chrono::system_clock::now());
4✔
313

314
   std::vector<X509_CRL> ret;
4✔
315
   ret.reserve(crls.size());
4✔
316

317
   for(const auto& p : crls) {
9✔
318
      ret.push_back(X509_CRL(p.first, t, t, p.second));
10✔
319
   }
320

321
   return ret;
4✔
322
}
13✔
323

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