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

randombit / botan / 19012754211

02 Nov 2025 01:10PM UTC coverage: 90.677% (+0.006%) from 90.671%
19012754211

push

github

web-flow
Merge pull request #5137 from randombit/jack/clang-tidy-includes

Remove various unused includes flagged by clang-tidy misc-include-cleaner

100457 of 110786 relevant lines covered (90.68%)

12189873.8 hits per line

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

83.33
/src/lib/x509/ocsp.cpp
1
/*
2
* OCSP
3
* (C) 2012,2013 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/ocsp.h>
9

10
#include <botan/base64.h>
11
#include <botan/ber_dec.h>
12
#include <botan/certstor.h>
13
#include <botan/der_enc.h>
14
#include <botan/pubkey.h>
15
#include <botan/x509_ext.h>
16

17
#if defined(BOTAN_HAS_HTTP_UTIL)
18
   #include <botan/internal/http_util.h>
19
#endif
20

21
namespace Botan::OCSP {
22

23
namespace {
24

25
// TODO: should this be in a header somewhere?
26
void decode_optional_list(BER_Decoder& ber, ASN1_Type tag, std::vector<X509_Certificate>& output) {
1,351✔
27
   BER_Object obj = ber.get_next_object();
1,351✔
28

29
   if(!obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
1,350✔
30
      ber.push_back(obj);
324✔
31
      return;
324✔
32
   }
33

34
   BER_Decoder list(obj);
1,026✔
35
   auto seq = list.start_sequence();
1,026✔
36
   while(seq.more_items()) {
1,526✔
37
      output.push_back([&] {
3,456✔
38
         X509_Certificate cert;
959✔
39
         cert.decode_from(seq);
959✔
40
         return cert;
579✔
41
      }());
1,918✔
42
   }
43
   seq.end_cons();
567✔
44
}
2,297✔
45

46
}  // namespace
47

48
Request::Request(const X509_Certificate& issuer_cert, const X509_Certificate& subject_cert) :
2✔
49
      m_issuer(issuer_cert), m_certid(m_issuer, BigInt::from_bytes(subject_cert.serial_number())) {
2✔
50
   if(subject_cert.issuer_dn() != issuer_cert.subject_dn()) {
2✔
51
      throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
1✔
52
   }
53
}
3✔
54

55
Request::Request(const X509_Certificate& issuer_cert, const BigInt& subject_serial) :
2✔
56
      m_issuer(issuer_cert), m_certid(m_issuer, subject_serial) {}
2✔
57

58
std::vector<uint8_t> Request::BER_encode() const {
3✔
59
   std::vector<uint8_t> output;
3✔
60
   DER_Encoder(output)
6✔
61
      .start_sequence()
3✔
62
      .start_sequence()
3✔
63
      .start_explicit(0)
3✔
64
      .encode(static_cast<size_t>(0))  // version #
3✔
65
      .end_explicit()
3✔
66
      .start_sequence()
3✔
67
      .start_sequence()
3✔
68
      .encode(m_certid)
3✔
69
      .end_cons()
3✔
70
      .end_cons()
3✔
71
      .end_cons()
3✔
72
      .end_cons();
3✔
73

74
   return output;
3✔
75
}
×
76

77
std::string Request::base64_encode() const {
2✔
78
   return Botan::base64_encode(BER_encode());
4✔
79
}
80

81
Response::Response(Certificate_Status_Code status) :
10✔
82
      m_status(Response_Status_Code::Successful), m_dummy_response_status(status) {}
10✔
83

84
Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
2,903✔
85
      m_response_bits(response_bits, response_bits + response_bits_len) {
2,903✔
86
   BER_Decoder response_outer = BER_Decoder(m_response_bits).start_sequence();
5,806✔
87

88
   size_t resp_status = 0;
2,798✔
89

90
   response_outer.decode(resp_status, ASN1_Type::Enumerated, ASN1_Class::Universal);
2,798✔
91

92
   m_status = static_cast<Response_Status_Code>(resp_status);
2,745✔
93

94
   if(m_status != Response_Status_Code::Successful) {
2,745✔
95
      return;
3✔
96
   }
97

98
   if(response_outer.more_items()) {
2,742✔
99
      BER_Decoder response_bytes = response_outer.start_context_specific(0).start_sequence();
2,735✔
100

101
      response_bytes.decode_and_check(OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 1}), "Unknown response type in OCSP response");
2,722✔
102

103
      BER_Decoder basicresponse = BER_Decoder(response_bytes.get_next_octet_string()).start_sequence();
5,205✔
104

105
      basicresponse.start_sequence()
3,841✔
106
         .raw_bytes(m_tbs_bits)
2,595✔
107
         .end_cons()
2,595✔
108
         .decode(m_sig_algo)
2,595✔
109
         .decode(m_signature, ASN1_Type::BitString);
1,357✔
110
      decode_optional_list(basicresponse, ASN1_Type(0), m_certs);
1,351✔
111

112
      size_t responsedata_version = 0;
891✔
113
      Extensions extensions;
891✔
114

115
      BER_Decoder(m_tbs_bits)
1,731✔
116
         .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
891✔
117

118
         .decode_optional(m_signer_name, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
1,767✔
119

120
         .decode_optional_string(
836✔
121
            m_key_hash, ASN1_Type::OctetString, 2, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
836✔
122

123
         .decode(m_produced_at)
833✔
124

125
         .decode_list(m_responses)
261✔
126

127
         .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed);
1,112✔
128

129
      const bool has_signer = !m_signer_name.empty();
52✔
130
      const bool has_key_hash = !m_key_hash.empty();
52✔
131

132
      if(has_signer && has_key_hash) {
52✔
133
         throw Decoding_Error("OCSP response includes both byName and byKey in responderID field");
×
134
      }
135
      if(!has_signer && !has_key_hash) {
52✔
136
         throw Decoding_Error("OCSP response contains neither byName nor byKey in responderID field");
1✔
137
      }
138
   }
5,268✔
139

140
   response_outer.end_cons();
58✔
141
}
21,164✔
142

143
bool Response::is_issued_by(const X509_Certificate& candidate) const {
76✔
144
   if(!m_signer_name.empty()) {
76✔
145
      return (candidate.subject_dn() == m_signer_name);
74✔
146
   }
147

148
   if(!m_key_hash.empty()) {
2✔
149
      return (candidate.subject_public_key_bitstring_sha1() == m_key_hash);
1✔
150
   }
151

152
   return false;
153
}
154

155
Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const {
1✔
156
   if(m_dummy_response_status) {
1✔
157
      return m_dummy_response_status.value();
×
158
   }
159

160
   if(m_signer_name.empty() && m_key_hash.empty()) {
1✔
161
      return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
162
   }
163

164
   if(!is_issued_by(issuer)) {
1✔
165
      return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
166
   }
167

168
   try {
1✔
169
      auto pub_key = issuer.subject_public_key();
1✔
170

171
      PK_Verifier verifier(*pub_key, m_sig_algo);
1✔
172

173
      if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) {
2✔
174
         return Certificate_Status_Code::OCSP_SIGNATURE_OK;
175
      } else {
176
         return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
×
177
      }
178
   } catch(Exception&) {
2✔
179
      return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
×
180
   }
×
181
}
182

183
std::optional<X509_Certificate> Response::find_signing_certificate(
51✔
184
   const X509_Certificate& issuer_certificate, const Certificate_Store* trusted_ocsp_responders) const {
185
   using namespace std::placeholders;
51✔
186

187
   // Check whether the CA issuing the certificate in question also signed this
188
   if(is_issued_by(issuer_certificate)) {
51✔
189
      return issuer_certificate;
19✔
190
   }
191

192
   // Then try to find a delegated responder certificate in the stapled certs
193
   for(const auto& cert : m_certs) {
32✔
194
      if(this->is_issued_by(cert)) {
24✔
195
         return cert;
24✔
196
      }
197
   }
198

199
   // Last resort: check the additionally provides trusted OCSP responders
200
   if(trusted_ocsp_responders != nullptr) {
8✔
201
      if(!m_key_hash.empty()) {
4✔
202
         auto signing_cert = trusted_ocsp_responders->find_cert_by_pubkey_sha1(m_key_hash);
×
203
         if(signing_cert) {
×
204
            return signing_cert;
×
205
         }
206
      }
×
207

208
      if(!m_signer_name.empty()) {
4✔
209
         auto signing_cert = trusted_ocsp_responders->find_cert(m_signer_name, {});
4✔
210
         if(signing_cert) {
4✔
211
            return signing_cert;
2✔
212
         }
213
      }
4✔
214
   }
215

216
   return std::nullopt;
6✔
217
}
218

219
Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
37✔
220
                                             const X509_Certificate& subject,
221
                                             std::chrono::system_clock::time_point ref_time,
222
                                             std::chrono::seconds max_age) const {
223
   if(m_dummy_response_status) {
37✔
224
      return m_dummy_response_status.value();
×
225
   }
226

227
   for(const auto& response : m_responses) {
37✔
228
      if(response.certid().is_id_for(issuer, subject)) {
37✔
229
         X509_Time x509_ref_time(ref_time);
37✔
230

231
         if(response.cert_status() == 1) {
37✔
232
            return Certificate_Status_Code::CERT_IS_REVOKED;
233
         }
234

235
         if(response.this_update() > x509_ref_time) {
37✔
236
            return Certificate_Status_Code::OCSP_NOT_YET_VALID;
237
         }
238

239
         if(response.next_update().time_is_set()) {
27✔
240
            if(x509_ref_time > response.next_update()) {
19✔
241
               return Certificate_Status_Code::OCSP_HAS_EXPIRED;
242
            }
243
         } else if(max_age > std::chrono::seconds::zero() &&
12✔
244
                   ref_time - response.this_update().to_std_timepoint() > max_age) {
8✔
245
            return Certificate_Status_Code::OCSP_IS_TOO_OLD;
246
         }
247

248
         if(response.cert_status() == 0) {
19✔
249
            return Certificate_Status_Code::OCSP_RESPONSE_GOOD;
250
         } else {
251
            return Certificate_Status_Code::OCSP_BAD_STATUS;
×
252
         }
253
      }
37✔
254
   }
255

256
   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
257
}
258

259
#if defined(BOTAN_HAS_HTTP_UTIL)
260

261
Response online_check(const X509_Certificate& issuer,
×
262
                      const BigInt& subject_serial,
263
                      std::string_view ocsp_responder,
264
                      std::chrono::milliseconds timeout) {
265
   if(ocsp_responder.empty()) {
×
266
      throw Invalid_Argument("No OCSP responder specified");
×
267
   }
268

269
   OCSP::Request req(issuer, subject_serial);
×
270

271
   auto http = HTTP::POST_sync(ocsp_responder, "application/ocsp-request", req.BER_encode(), 1, timeout);
×
272

273
   http.throw_unless_ok();
×
274

275
   // Check the MIME type?
276

277
   return OCSP::Response(http.body());
×
278
}
×
279

280
Response online_check(const X509_Certificate& issuer,
×
281
                      const X509_Certificate& subject,
282
                      std::chrono::milliseconds timeout) {
283
   if(subject.issuer_dn() != issuer.subject_dn()) {
×
284
      throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
×
285
   }
286

287
   return online_check(issuer, BigInt::from_bytes(subject.serial_number()), subject.ocsp_responder(), timeout);
×
288
}
289

290
#endif
291

292
}  // namespace Botan::OCSP
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