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

randombit / botan / 24872040145

24 Apr 2026 01:10AM UTC coverage: 89.401% (-0.07%) from 89.474%
24872040145

push

github

web-flow
Merge pull request #5539 from randombit/jack/cert-cache

Refactor and optimize Windows certificate store

106671 of 119318 relevant lines covered (89.4%)

11479699.69 hits per line

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

75.21
/src/lib/x509/certstor.cpp
1
/*
2
* Certificate Store
3
* (C) 1999-2010,2013 Jack Lloyd
4
* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/certstor.h>
10

11
#include <botan/asn1_time.h>
12
#include <botan/assert.h>
13
#include <botan/data_src.h>
14
#include <botan/pkix_types.h>
15
#include <botan/internal/filesystem.h>
16
#include <algorithm>
17

18
namespace Botan {
19

20
Certificate_Store::~Certificate_Store() = default;
9,509✔
21

22
bool Certificate_Store::certificate_known(const X509_Certificate& cert) const {
×
23
   return contains(cert);
×
24
}
25

26
bool Certificate_Store::contains(const X509_Certificate& searching) const {
×
27
   for(const auto& cert : find_all_certs(searching.subject_dn(), searching.subject_key_id())) {
×
28
      if(cert == searching) {
×
29
         return true;
×
30
      }
31
   }
×
32

33
   return false;
×
34
}
35

36
std::optional<X509_Certificate> Certificate_Store::find_cert(const X509_DN& subject_dn,
13✔
37
                                                             const std::vector<uint8_t>& key_id) const {
38
   const auto certs = find_all_certs(subject_dn, key_id);
13✔
39

40
   if(certs.empty()) {
13✔
41
      return std::nullopt;
2✔
42
   }
43

44
   // `count` might be greater than 1, but we'll just select the first match
45
   return certs.front();
11✔
46
}
13✔
47

48
std::optional<X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate& /*unused*/) const {
×
49
   return std::nullopt;
×
50
}
51

52
void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert) {
5,407✔
53
   const auto tag = cert.tag();
5,407✔
54
   if(!m_cert_tags.contains(tag)) {
5,407✔
55
      m_cert_tags.insert(tag);
5,294✔
56
      const size_t idx = m_certs.size();
5,294✔
57
      m_certs.push_back(cert);
5,294✔
58
      m_dn_to_indices[cert.subject_dn()].push_back(idx);
5,294✔
59
   }
60
}
5,407✔
61

62
std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const {
976✔
63
   std::vector<X509_DN> subjects;
976✔
64
   subjects.reserve(m_certs.size());
976✔
65
   for(const auto& cert : m_certs) {
1,504✔
66
      subjects.push_back(cert.subject_dn());
528✔
67
   }
68
   return subjects;
976✔
69
}
×
70

71
std::optional<X509_Certificate> Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
6✔
72
                                                                       const std::vector<uint8_t>& key_id) const {
73
   const auto it = m_dn_to_indices.find(subject_dn);
6✔
74
   if(it == m_dn_to_indices.end()) {
6✔
75
      return std::nullopt;
2✔
76
   }
77

78
   for(const size_t idx : it->second) {
4✔
79
      const auto& cert = m_certs[idx];
4✔
80
      BOTAN_ASSERT_NOMSG(cert.subject_dn() == subject_dn);
4✔
81

82
      if(!key_id.empty()) {
4✔
83
         const std::vector<uint8_t>& skid = cert.subject_key_id();
×
84
         if(!skid.empty() && skid != key_id) {  // no match
×
85
            continue;
×
86
         }
87
      }
88

89
      return cert;
4✔
90
   }
91

92
   return std::nullopt;
×
93
}
94

95
std::vector<X509_Certificate> Certificate_Store_In_Memory::find_all_certs(const X509_DN& subject_dn,
20,359✔
96
                                                                          const std::vector<uint8_t>& key_id) const {
97
   std::vector<X509_Certificate> matches;
20,359✔
98

99
   const auto it = m_dn_to_indices.find(subject_dn);
20,359✔
100
   if(it == m_dn_to_indices.end()) {
20,359✔
101
      return matches;
102
   }
103

104
   for(const size_t idx : it->second) {
20,974✔
105
      const auto& cert = m_certs[idx];
10,641✔
106
      BOTAN_ASSERT_NOMSG(cert.subject_dn() == subject_dn);
10,641✔
107

108
      if(!key_id.empty()) {
10,641✔
109
         const std::vector<uint8_t>& skid = cert.subject_key_id();
9,495✔
110
         if(!skid.empty() && skid != key_id) {  // no match
9,495✔
111
            continue;
415✔
112
         }
113
      }
114

115
      matches.push_back(cert);
10,226✔
116
   }
117

118
   return matches;
119
}
×
120

121
std::optional<X509_Certificate> Certificate_Store_In_Memory::find_cert_by_pubkey_sha1(
×
122
   const std::vector<uint8_t>& key_hash) const {
123
   if(key_hash.size() != 20) {
×
124
      throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash");
×
125
   }
126

127
   for(const auto& cert : m_certs) {
×
128
      if(key_hash == cert.subject_public_key_bitstring_sha1()) {
×
129
         return cert;
×
130
      }
131
   }
132

133
   return std::nullopt;
×
134
}
135

136
std::optional<X509_Certificate> Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256(
7✔
137
   const std::vector<uint8_t>& subject_hash) const {
138
   if(subject_hash.size() != 32) {
7✔
139
      throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash");
×
140
   }
141

142
   for(const auto& cert : m_certs) {
27✔
143
      if(subject_hash == cert.raw_subject_dn_sha256()) {
26✔
144
         return cert;
6✔
145
      }
146
   }
147

148
   return std::nullopt;
1✔
149
}
150

151
std::optional<X509_Certificate> Certificate_Store_In_Memory::find_cert_by_issuer_dn_and_serial_number(
6✔
152
   const X509_DN& issuer_dn, std::span<const uint8_t> serial_number) const {
153
   for(const auto& cert : m_certs) {
20✔
154
      if(cert.issuer_dn() == issuer_dn && std::ranges::equal(cert.serial_number(), serial_number)) {
26✔
155
         return cert;
6✔
156
      }
157
   }
158

159
   return std::nullopt;
×
160
}
161

162
void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) {
506✔
163
   const X509_DN& crl_issuer = crl.issuer_dn();
506✔
164

165
   for(auto& c : m_crls) {
951✔
166
      // Found an update of a previously existing one; replace it
167
      if(c.issuer_dn() == crl_issuer) {
467✔
168
         if(c.this_update() <= crl.this_update()) {
22✔
169
            c = crl;
22✔
170
         }
171
         return;
22✔
172
      }
173
   }
174

175
   // Totally new CRL, add to the list
176
   m_crls.push_back(crl);
484✔
177
}
178

179
std::optional<X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const {
6,962✔
180
   const std::vector<uint8_t>& key_id = subject.authority_key_id();
6,962✔
181

182
   for(const auto& c : m_crls) {
8,373✔
183
      // Only compare key ids if set in both call and in the CRL
184
      if(!key_id.empty()) {
3,426✔
185
         const std::vector<uint8_t>& akid = c.authority_key_id();
3,424✔
186

187
         if(!akid.empty() && akid != key_id) {  // no match
3,424✔
188
            continue;
1,343✔
189
         }
190
      }
191

192
      if(c.issuer_dn() == subject.issuer_dn()) {
2,083✔
193
         return c;
2,015✔
194
      }
195
   }
196

197
   return {};
4,947✔
198
}
199

200
bool Certificate_Store_In_Memory::contains(const X509_Certificate& cert) const {
6,840✔
201
   return m_cert_tags.contains(cert.tag());
6,840✔
202
}
203

204
Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert) {
301✔
205
   add_certificate(cert);
301✔
206
}
301✔
207

208
Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert, const X509_CRL& crl) {
×
209
   add_certificate(cert);
×
210
   add_crl(crl);
×
211
}
×
212

213
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
214
Certificate_Store_In_Memory::Certificate_Store_In_Memory(std::string_view dir) {
13✔
215
   if(dir.empty()) {
13✔
216
      return;
×
217
   }
218

219
   std::vector<std::string> maybe_certs = get_files_recursive(dir);
13✔
220

221
   if(maybe_certs.empty()) {
13✔
222
      maybe_certs.push_back(std::string(dir));
22✔
223
   }
224

225
   for(auto&& cert_file : maybe_certs) {
351✔
226
      try {
338✔
227
         DataSource_Stream src(cert_file, true);
338✔
228
         while(!src.end_of_data()) {
550✔
229
            try {
349✔
230
               add_certificate(X509_Certificate(src));
349✔
231
            } catch(std::exception&) {
136✔
232
               // stop searching for other certificate at first exception
233
               break;
136✔
234
            }
136✔
235
         }
236
      } catch(std::exception&) {}
338✔
237
   }
238
}
13✔
239
#endif
240

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