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

randombit / botan / 24101703016

06 Apr 2026 10:36PM UTC coverage: 89.45% (-0.005%) from 89.455%
24101703016

push

github

web-flow
Merge pull request #5521 from randombit/jack/fix-rollup

Rollup of small fixes

105880 of 118368 relevant lines covered (89.45%)

11539723.42 hits per line

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

85.71
/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/hash.h>
15
#include <botan/pubkey.h>
16
#include <botan/x509_ext.h>
17

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

22
namespace Botan::OCSP {
23

24
CertID::CertID(const X509_Certificate& issuer, const BigInt& subject_serial) : m_subject_serial(subject_serial) {
4✔
25
   /*
26
   In practice it seems some responders, including, notably,
27
   ocsp.verisign.com, will reject anything but SHA-1 here
28
   */
29
   auto hash = HashFunction::create_or_throw("SHA-1");
4✔
30

31
   m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM);
4✔
32
   m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring()));
8✔
33
   m_issuer_dn_hash = unlock(hash->process(issuer.raw_subject_dn()));
12✔
34
}
4✔
35

36
bool CertID::is_id_for(const X509_Certificate& issuer, const X509_Certificate& subject) const {
38✔
37
   try {
38✔
38
      if(BigInt::from_bytes(subject.serial_number()) != m_subject_serial) {
76✔
39
         return false;
×
40
      }
41

42
      const std::string hash_algo = m_hash_id.oid().to_formatted_string();
38✔
43

44
      if(hash_algo != "SHA-1" && hash_algo != "SHA-256") {
38✔
45
         return false;
46
      }
47

48
      auto hash = HashFunction::create_or_throw(hash_algo);
38✔
49

50
      if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn()))) {
152✔
51
         return false;
52
      }
53

54
      if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring()))) {
152✔
55
         return false;
56
      }
57
   } catch(...) {
38✔
58
      return false;
×
59
   }
×
60

61
   return true;
38✔
62
}
63

64
void CertID::encode_into(DER_Encoder& to) const {
3✔
65
   to.start_sequence()
3✔
66
      .encode(m_hash_id)
3✔
67
      .encode(m_issuer_dn_hash, ASN1_Type::OctetString)
3✔
68
      .encode(m_issuer_key_hash, ASN1_Type::OctetString)
3✔
69
      .encode(m_subject_serial)
3✔
70
      .end_cons();
3✔
71
}
3✔
72

73
void CertID::decode_from(BER_Decoder& from) {
48✔
74
   /*
75
   * RFC 6960 Section 4.1.1
76
   *
77
   * CertID ::= SEQUENCE {
78
   *    hashAlgorithm       AlgorithmIdentifier,
79
   *    issuerNameHash      OCTET STRING,
80
   *    issuerKeyHash       OCTET STRING,
81
   *    serialNumber        CertificateSerialNumber }
82
   */
83
   from.start_sequence()
96✔
84
      .decode(m_hash_id)
48✔
85
      .decode(m_issuer_dn_hash, ASN1_Type::OctetString)
48✔
86
      .decode(m_issuer_key_hash, ASN1_Type::OctetString)
48✔
87
      .decode(m_subject_serial)
48✔
88
      .end_cons();
46✔
89
}
46✔
90

91
void SingleResponse::encode_into(DER_Encoder& /*to*/) const {
×
92
   throw Not_Implemented("SingleResponse::encode_into");
×
93
}
94

95
void SingleResponse::decode_from(BER_Decoder& from) {
48✔
96
   /*
97
   * RFC 6960 Section 4.2.1
98
   *
99
   * SingleResponse ::= SEQUENCE {
100
   *    certID                       CertID,
101
   *    certStatus                   CertStatus,
102
   *    thisUpdate                   GeneralizedTime,
103
   *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
104
   *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
105
   *
106
   * CertStatus ::= CHOICE {
107
   *    good        [0]     IMPLICIT NULL,
108
   *    revoked     [1]     IMPLICIT RevokedInfo,
109
   *    unknown     [2]     IMPLICIT UnknownInfo }
110
   *
111
   * RevokedInfo ::= SEQUENCE {
112
   *    revocationTime              GeneralizedTime,
113
   *    revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
114
   */
115
   BER_Object cert_status;
48✔
116
   Extensions extensions;
48✔
117

118
   from.start_sequence()
56✔
119
      .decode(m_certid)
48✔
120
      .get_next(cert_status)
46✔
121
      .decode(m_thisupdate)
46✔
122
      .decode_optional(m_nextupdate, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
83✔
123
      .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
90✔
124
      .end_cons();
40✔
125

126
   // TODO: should verify the cert_status body and decode RevokedInfo
127
   m_cert_status = static_cast<uint32_t>(cert_status.type());
40✔
128
}
48✔
129

130
namespace {
131

132
// TODO: should this be in a header somewhere?
133
void decode_optional_list(BER_Decoder& ber, ASN1_Type tag, std::vector<X509_Certificate>& output) {
990✔
134
   const BER_Object obj = ber.get_next_object();
990✔
135

136
   if(!obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
990✔
137
      ber.push_back(obj);
24✔
138
      return;
24✔
139
   }
140

141
   BER_Decoder list(obj, BER_Decoder::Limits::DER());
966✔
142
   auto seq = list.start_sequence();
966✔
143
   while(seq.more_items()) {
981✔
144
      output.push_back([&] {
2,871✔
145
         X509_Certificate cert;
941✔
146
         cert.decode_from(seq);
941✔
147
         return cert;
48✔
148
      }());
1,882✔
149
   }
150
   seq.end_cons();
40✔
151
}
2,809✔
152

153
}  // namespace
154

155
Request::Request(const X509_Certificate& issuer_cert, const X509_Certificate& subject_cert) :
2✔
156
      m_issuer(issuer_cert), m_certid(m_issuer, BigInt::from_bytes(subject_cert.serial_number())) {
2✔
157
   if(subject_cert.issuer_dn() != issuer_cert.subject_dn()) {
2✔
158
      throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
1✔
159
   }
160
}
3✔
161

162
Request::Request(const X509_Certificate& issuer_cert, const BigInt& subject_serial) :
2✔
163
      m_issuer(issuer_cert), m_certid(m_issuer, subject_serial) {}
2✔
164

165
std::vector<uint8_t> Request::BER_encode() const {
3✔
166
   /*
167
   * RFC 6960 Section 4.1.1
168
   *
169
   * OCSPRequest ::= SEQUENCE {
170
   *    tbsRequest                  TBSRequest,
171
   *    optionalSignature   [0]    EXPLICIT Signature OPTIONAL }
172
   *
173
   * TBSRequest ::= SEQUENCE {
174
   *    version             [0]    EXPLICIT Version DEFAULT v1,
175
   *    requestList                SEQUENCE OF Request }
176
   *
177
   * Request ::= SEQUENCE {
178
   *    reqCert                    CertID }
179
   */
180
   std::vector<uint8_t> output;
3✔
181
   DER_Encoder(output)
6✔
182
      .start_sequence()
3✔
183
      .start_sequence()
3✔
184
      .start_explicit(0)
3✔
185
      .encode(static_cast<size_t>(0))  // version #
3✔
186
      .end_explicit()
3✔
187
      .start_sequence()
3✔
188
      .start_sequence()
3✔
189
      .encode(m_certid)
3✔
190
      .end_cons()
3✔
191
      .end_cons()
3✔
192
      .end_cons()
3✔
193
      .end_cons();
3✔
194

195
   return output;
3✔
196
}
×
197

198
std::string Request::base64_encode() const {
2✔
199
   return Botan::base64_encode(BER_encode());
4✔
200
}
201

202
Response::Response(Certificate_Status_Code status) :
11✔
203
      m_status(Response_Status_Code::Successful), m_dummy_response_status(status) {}
11✔
204

205
Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
2,905✔
206
      m_response_bits(response_bits, response_bits + response_bits_len) {
2,905✔
207
   /*
208
   * RFC 6960 Section 4.2.1
209
   *
210
   * OCSPResponse ::= SEQUENCE {
211
   *    responseStatus         OCSPResponseStatus,
212
   *    responseBytes      [0] EXPLICIT ResponseBytes OPTIONAL }
213
   *
214
   * OCSPResponseStatus ::= ENUMERATED { ... }
215
   *
216
   * ResponseBytes ::= SEQUENCE {
217
   *    responseType   OBJECT IDENTIFIER,
218
   *    response       OCTET STRING }
219
   */
220
   BER_Decoder outer_decoder(m_response_bits, BER_Decoder::Limits::DER());
2,905✔
221
   BER_Decoder response_outer = outer_decoder.start_sequence();
2,905✔
222

223
   size_t resp_status = 0;
1,004✔
224

225
   response_outer.decode(resp_status, ASN1_Type::Enumerated, ASN1_Class::Universal);
1,004✔
226

227
   m_status = static_cast<Response_Status_Code>(resp_status);
993✔
228

229
   if(m_status != Response_Status_Code::Successful) {
993✔
230
      return;
1✔
231
   }
232

233
   if(response_outer.more_items()) {
992✔
234
      BER_Decoder response_bytes_ctx = response_outer.start_context_specific(0);
992✔
235
      BER_Decoder response_bytes = response_bytes_ctx.start_sequence();
992✔
236

237
      response_bytes.decode_and_check(OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 1}), "Unknown response type in OCSP response");
992✔
238

239
      /*
240
      * RFC 6960 Section 4.2.1
241
      *
242
      * BasicOCSPResponse ::= SEQUENCE {
243
      *    tbsResponseData      ResponseData,
244
      *    signatureAlgorithm   AlgorithmIdentifier,
245
      *    signature            BIT STRING,
246
      *    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
247
      */
248
      BER_Decoder basic_response_decoder(response_bytes.get_next_octet_string(), BER_Decoder::Limits::DER());
992✔
249
      BER_Decoder basicresponse = basic_response_decoder.start_sequence();
992✔
250

251
      basicresponse.start_sequence()
992✔
252
         .raw_bytes(m_tbs_bits)
990✔
253
         .end_cons()
990✔
254
         .decode(m_sig_algo)
990✔
255
         .decode(m_signature, ASN1_Type::BitString);
990✔
256
      decode_optional_list(basicresponse, ASN1_Type(0), m_certs);
990✔
257

258
      basicresponse.verify_end();
64✔
259
      basic_response_decoder.verify_end();
63✔
260

261
      /*
262
      * RFC 6960 Section 4.2.1
263
      *
264
      * ResponseData ::= SEQUENCE {
265
      *    version              [0] EXPLICIT Version DEFAULT v1,
266
      *    responderID              ResponderID,
267
      *    producedAt               GeneralizedTime,
268
      *    responses                SEQUENCE OF SingleResponse,
269
      *    responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
270
      *
271
      * ResponderID ::= CHOICE {
272
      *    byName   [1] Name,
273
      *    byKey    [2] KeyHash }
274
      */
275
      size_t responsedata_version = 0;
63✔
276
      Extensions extensions;
63✔
277

278
      BER_Decoder(m_tbs_bits, BER_Decoder::Limits::DER())
86✔
279
         .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
63✔
280

281
         .decode_optional(m_signer_name, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
122✔
282

283
         .decode_optional_string(
59✔
284
            m_key_hash, ASN1_Type::OctetString, 2, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
59✔
285

286
         .decode(m_produced_at)
59✔
287

288
         .decode_list(m_responses)
48✔
289

290
         .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
99✔
291

292
         .verify_end();
40✔
293

294
      const bool has_signer = !m_signer_name.empty();
40✔
295
      const bool has_key_hash = !m_key_hash.empty();
40✔
296

297
      if(has_signer && has_key_hash) {
40✔
298
         throw Decoding_Error("OCSP response includes both byName and byKey in responderID field");
×
299
      }
300
      if(!has_signer && !has_key_hash) {
40✔
301
         throw Decoding_Error("OCSP response contains neither byName nor byKey in responderID field");
×
302
      }
303

304
      response_bytes.verify_end();
40✔
305
      response_bytes_ctx.verify_end();
40✔
306
   }
3,848✔
307

308
   response_outer.verify_end();
40✔
309
   outer_decoder.verify_end();
40✔
310
}
20,098✔
311

312
bool Response::is_issued_by(const X509_Certificate& candidate) const {
117✔
313
   if(!m_signer_name.empty()) {
117✔
314
      return (candidate.subject_dn() == m_signer_name);
114✔
315
   }
316

317
   if(!m_key_hash.empty()) {
3✔
318
      return (candidate.subject_public_key_bitstring_sha1() == m_key_hash);
2✔
319
   }
320

321
   return false;
322
}
323

324
Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const {
40✔
325
   if(m_dummy_response_status) {
40✔
326
      return m_dummy_response_status.value();
×
327
   }
328

329
   if(m_signer_name.empty() && m_key_hash.empty()) {
40✔
330
      return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
331
   }
332

333
   if(!is_issued_by(issuer)) {
40✔
334
      return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
335
   }
336

337
   try {
40✔
338
      auto pub_key = issuer.subject_public_key();
40✔
339

340
      PK_Verifier verifier(*pub_key, m_sig_algo);
40✔
341

342
      if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) {
80✔
343
         return Certificate_Status_Code::OCSP_SIGNATURE_OK;
344
      } else {
345
         return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
1✔
346
      }
347
   } catch(Exception&) {
80✔
348
      return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
×
349
   }
×
350
}
351

352
std::optional<X509_Certificate> Response::find_signing_certificate(
53✔
353
   const X509_Certificate& issuer_certificate, const Certificate_Store* trusted_ocsp_responders) const {
354
   using namespace std::placeholders;
53✔
355

356
   // Check whether the CA issuing the certificate in question also signed this
357
   if(is_issued_by(issuer_certificate)) {
53✔
358
      return issuer_certificate;
21✔
359
   }
360

361
   // Then try to find a delegated responder certificate in the stapled certs
362
   for(const auto& cert : m_certs) {
32✔
363
      if(this->is_issued_by(cert)) {
24✔
364
         return cert;
24✔
365
      }
366
   }
367

368
   // Last resort: check the additionally provides trusted OCSP responders
369
   if(trusted_ocsp_responders != nullptr) {
8✔
370
      if(!m_key_hash.empty()) {
4✔
371
         auto signing_cert = trusted_ocsp_responders->find_cert_by_pubkey_sha1(m_key_hash);
×
372
         if(signing_cert) {
×
373
            return signing_cert;
×
374
         }
375
      }
×
376

377
      if(!m_signer_name.empty()) {
4✔
378
         auto signing_cert = trusted_ocsp_responders->find_cert(m_signer_name, {});
4✔
379
         if(signing_cert) {
4✔
380
            return signing_cert;
2✔
381
         }
382
      }
4✔
383
   }
384

385
   return std::nullopt;
6✔
386
}
387

388
Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
38✔
389
                                             const X509_Certificate& subject,
390
                                             std::chrono::system_clock::time_point ref_time,
391
                                             std::chrono::seconds max_age) const {
392
   if(m_dummy_response_status) {
38✔
393
      return m_dummy_response_status.value();
×
394
   }
395

396
   for(const auto& response : m_responses) {
38✔
397
      if(response.certid().is_id_for(issuer, subject)) {
38✔
398
         const X509_Time x509_ref_time(ref_time);
38✔
399

400
         if(response.cert_status() == 1) {
38✔
401
            return Certificate_Status_Code::CERT_IS_REVOKED;
402
         }
403

404
         if(response.this_update() > x509_ref_time) {
38✔
405
            return Certificate_Status_Code::OCSP_NOT_YET_VALID;
406
         }
407

408
         if(response.next_update().time_is_set()) {
28✔
409
            if(x509_ref_time > response.next_update()) {
20✔
410
               return Certificate_Status_Code::OCSP_HAS_EXPIRED;
411
            }
412
         } else if(max_age > std::chrono::seconds::zero() &&
12✔
413
                   ref_time - response.this_update().to_std_timepoint() > max_age) {
8✔
414
            return Certificate_Status_Code::OCSP_IS_TOO_OLD;
2✔
415
         }
416

417
         if(response.cert_status() == 0) {
20✔
418
            return Certificate_Status_Code::OCSP_RESPONSE_GOOD;
419
         } else {
420
            return Certificate_Status_Code::OCSP_BAD_STATUS;
×
421
         }
422
      }
38✔
423
   }
424

425
   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
426
}
427

428
#if defined(BOTAN_HAS_HTTP_UTIL)
429

430
Response online_check(const X509_Certificate& issuer,
×
431
                      const BigInt& subject_serial,
432
                      std::string_view ocsp_responder,
433
                      std::chrono::milliseconds timeout) {
434
   if(ocsp_responder.empty()) {
×
435
      throw Invalid_Argument("No OCSP responder specified");
×
436
   }
437

438
   const OCSP::Request req(issuer, subject_serial);
×
439

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

442
   http.throw_unless_ok();
×
443

444
   // Check the MIME type?
445

446
   return OCSP::Response(http.body());
×
447
}
×
448

449
Response online_check(const X509_Certificate& issuer,
×
450
                      const X509_Certificate& subject,
451
                      std::chrono::milliseconds timeout) {
452
   if(subject.issuer_dn() != issuer.subject_dn()) {
×
453
      throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
×
454
   }
455

456
   return online_check(issuer, BigInt::from_bytes(subject.serial_number()), subject.ocsp_responder(), timeout);
×
457
}
458

459
#endif
460

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