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

randombit / botan / 25147314492

30 Apr 2026 04:17AM UTC coverage: 89.359% (-0.01%) from 89.37%
25147314492

push

github

randombit
Avoid using BOTAN_ASSERT for input validation

107055 of 119803 relevant lines covered (89.36%)

11298138.36 hits per line

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

85.51
/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) {
45✔
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()
90✔
84
      .decode(m_hash_id)
45✔
85
      .decode(m_issuer_dn_hash, ASN1_Type::OctetString)
45✔
86
      .decode(m_issuer_key_hash, ASN1_Type::OctetString)
45✔
87
      .decode(m_subject_serial)
45✔
88
      .end_cons();
43✔
89
}
43✔
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) {
45✔
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;
45✔
116
   Extensions extensions;
45✔
117

118
   from.start_sequence()
53✔
119
      .decode(m_certid)
45✔
120
      .get_next(cert_status)
43✔
121
      .decode(m_thisupdate)
43✔
122
      .decode_optional(m_nextupdate, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
77✔
123
      .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
84✔
124
      .end_cons();
37✔
125

126
   const auto cert_status_class = cert_status.get_class();
37✔
127
   if(cert_status_class != ASN1_Class::ContextSpecific &&
37✔
128
      cert_status_class != (ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
1✔
129
      throw Decoding_Error("OCSP::SingleResponse: certStatus has unexpected class tag");
×
130
   }
131

132
   // TODO: should verify the cert_status body and decode RevokedInfo
133
   m_cert_status = static_cast<uint32_t>(cert_status.type());
37✔
134
}
45✔
135

136
namespace {
137

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

142
   if(!obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
990✔
143
      ber.push_back(obj);
24✔
144
      return;
24✔
145
   }
146

147
   BER_Decoder list(obj, BER_Decoder::Limits::DER());
966✔
148
   auto seq = list.start_sequence();
966✔
149
   while(seq.more_items()) {
978✔
150
      output.push_back([&] {
2,868✔
151
         X509_Certificate cert;
941✔
152
         cert.decode_from(seq);
941✔
153
         return cert;
45✔
154
      }());
1,882✔
155
   }
156
   seq.end_cons();
37✔
157
}
2,815✔
158

159
}  // namespace
160

161
Request::Request(const X509_Certificate& issuer_cert, const X509_Certificate& subject_cert) :
2✔
162
      m_issuer(issuer_cert), m_certid(m_issuer, BigInt::from_bytes(subject_cert.serial_number())) {
2✔
163
   if(subject_cert.issuer_dn() != issuer_cert.subject_dn()) {
2✔
164
      throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
1✔
165
   }
166
}
3✔
167

168
Request::Request(const X509_Certificate& issuer_cert, const BigInt& subject_serial) :
2✔
169
      m_issuer(issuer_cert), m_certid(m_issuer, subject_serial) {}
2✔
170

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

201
   return output;
3✔
202
}
×
203

204
std::string Request::base64_encode() const {
2✔
205
   return Botan::base64_encode(BER_encode());
4✔
206
}
207

208
Response::Response(Certificate_Status_Code status) :
11✔
209
      m_status(Response_Status_Code::Successful), m_dummy_response_status(status) {}
11✔
210

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

229
   size_t resp_status = 0;
1,004✔
230

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

233
   m_status = static_cast<Response_Status_Code>(resp_status);
993✔
234

235
   if(m_status != Response_Status_Code::Successful) {
993✔
236
      return;
1✔
237
   }
238

239
   if(response_outer.more_items()) {
992✔
240
      BER_Decoder response_bytes_ctx = response_outer.start_context_specific(0);
992✔
241
      BER_Decoder response_bytes = response_bytes_ctx.start_sequence();
992✔
242

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

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

257
      basicresponse.start_sequence()
992✔
258
         .raw_bytes(m_tbs_bits)
990✔
259
         .end_cons()
990✔
260
         .decode(m_sig_algo)
990✔
261
         .decode(m_signature, ASN1_Type::BitString);
990✔
262
      decode_optional_list(basicresponse, ASN1_Type(0), m_certs);
990✔
263

264
      basicresponse.verify_end();
61✔
265
      basic_response_decoder.verify_end();
60✔
266

267
      /*
268
      * RFC 6960 Section 4.2.1
269
      *
270
      * ResponseData ::= SEQUENCE {
271
      *    version              [0] EXPLICIT Version DEFAULT v1,
272
      *    responderID              ResponderID,
273
      *    producedAt               GeneralizedTime,
274
      *    responses                SEQUENCE OF SingleResponse,
275
      *    responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
276
      *
277
      * ResponderID ::= CHOICE {
278
      *    byName   [1] Name,
279
      *    byKey    [2] KeyHash }
280
      */
281
      size_t responsedata_version = 0;
60✔
282
      Extensions extensions;
60✔
283

284
      BER_Decoder(m_tbs_bits, BER_Decoder::Limits::DER())
83✔
285
         .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
60✔
286

287
         .decode_optional(m_signer_name, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
116✔
288

289
         .decode_optional_string(
56✔
290
            m_key_hash, ASN1_Type::OctetString, 2, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
56✔
291

292
         .decode(m_produced_at)
56✔
293

294
         .decode_list(m_responses)
45✔
295

296
         .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
93✔
297

298
         .verify_end();
37✔
299

300
      const bool has_signer = !m_signer_name.empty();
37✔
301
      const bool has_key_hash = !m_key_hash.empty();
37✔
302

303
      if(has_signer && has_key_hash) {
37✔
304
         throw Decoding_Error("OCSP response includes both byName and byKey in responderID field");
×
305
      }
306
      if(!has_signer && !has_key_hash) {
37✔
307
         throw Decoding_Error("OCSP response contains neither byName nor byKey in responderID field");
×
308
      }
309

310
      response_bytes.verify_end();
37✔
311
      response_bytes_ctx.verify_end();
37✔
312
   }
3,857✔
313

314
   response_outer.verify_end();
37✔
315
   outer_decoder.verify_end();
37✔
316
}
20,122✔
317

318
bool Response::is_issued_by(const X509_Certificate& candidate) const {
117✔
319
   if(!m_signer_name.empty()) {
117✔
320
      return (candidate.subject_dn() == m_signer_name);
114✔
321
   }
322

323
   if(!m_key_hash.empty()) {
3✔
324
      return (candidate.subject_public_key_bitstring_sha1() == m_key_hash);
2✔
325
   }
326

327
   return false;
328
}
329

330
Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const {
40✔
331
   if(m_dummy_response_status) {
40✔
332
      return m_dummy_response_status.value();
×
333
   }
334

335
   if(m_signer_name.empty() && m_key_hash.empty()) {
40✔
336
      return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
337
   }
338

339
   if(!is_issued_by(issuer)) {
40✔
340
      return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
341
   }
342

343
   try {
40✔
344
      auto pub_key = issuer.subject_public_key();
40✔
345

346
      PK_Verifier verifier(*pub_key, m_sig_algo);
40✔
347

348
      if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) {
80✔
349
         return Certificate_Status_Code::OCSP_SIGNATURE_OK;
350
      } else {
351
         return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
1✔
352
      }
353
   } catch(Exception&) {
80✔
354
      return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
×
355
   }
×
356
}
357

358
std::optional<X509_Certificate> Response::find_signing_certificate(
53✔
359
   const X509_Certificate& issuer_certificate, const Certificate_Store* trusted_ocsp_responders) const {
360
   using namespace std::placeholders;
53✔
361

362
   // Check whether the CA issuing the certificate in question also signed this
363
   if(is_issued_by(issuer_certificate)) {
53✔
364
      return issuer_certificate;
21✔
365
   }
366

367
   // Then try to find a delegated responder certificate in the stapled certs
368
   for(const auto& cert : m_certs) {
32✔
369
      if(this->is_issued_by(cert)) {
24✔
370
         return cert;
24✔
371
      }
372
   }
373

374
   // Last resort: check the additionally provides trusted OCSP responders
375
   if(trusted_ocsp_responders != nullptr) {
8✔
376
      if(!m_key_hash.empty()) {
4✔
377
         auto signing_cert = trusted_ocsp_responders->find_cert_by_pubkey_sha1(m_key_hash);
×
378
         if(signing_cert) {
×
379
            return signing_cert;
×
380
         }
381
      }
×
382

383
      if(!m_signer_name.empty()) {
4✔
384
         auto signing_cert = trusted_ocsp_responders->find_cert(m_signer_name, {});
4✔
385
         if(signing_cert) {
4✔
386
            return signing_cert;
2✔
387
         }
388
      }
4✔
389
   }
390

391
   return std::nullopt;
6✔
392
}
393

394
Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
38✔
395
                                             const X509_Certificate& subject,
396
                                             std::chrono::system_clock::time_point ref_time,
397
                                             std::chrono::seconds max_age) const {
398
   if(m_dummy_response_status) {
38✔
399
      return m_dummy_response_status.value();
×
400
   }
401

402
   for(const auto& response : m_responses) {
38✔
403
      if(response.certid().is_id_for(issuer, subject)) {
38✔
404
         const X509_Time x509_ref_time(ref_time);
38✔
405

406
         if(response.cert_status() == 1) {
38✔
407
            return Certificate_Status_Code::CERT_IS_REVOKED;
408
         }
409

410
         if(response.this_update() > x509_ref_time) {
38✔
411
            return Certificate_Status_Code::OCSP_NOT_YET_VALID;
412
         }
413

414
         if(response.next_update().time_is_set()) {
28✔
415
            if(x509_ref_time > response.next_update()) {
20✔
416
               return Certificate_Status_Code::OCSP_HAS_EXPIRED;
417
            }
418
         } else if(max_age > std::chrono::seconds::zero() &&
12✔
419
                   ref_time - response.this_update().to_std_timepoint() > max_age) {
8✔
420
            return Certificate_Status_Code::OCSP_IS_TOO_OLD;
2✔
421
         }
422

423
         if(response.cert_status() == 0) {
20✔
424
            return Certificate_Status_Code::OCSP_RESPONSE_GOOD;
425
         } else {
426
            return Certificate_Status_Code::OCSP_BAD_STATUS;
×
427
         }
428
      }
38✔
429
   }
430

431
   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
432
}
433

434
#if defined(BOTAN_HAS_HTTP_UTIL)
435

436
Response online_check(const X509_Certificate& issuer,
×
437
                      const BigInt& subject_serial,
438
                      std::string_view ocsp_responder,
439
                      std::chrono::milliseconds timeout) {
440
   if(ocsp_responder.empty()) {
×
441
      throw Invalid_Argument("No OCSP responder specified");
×
442
   }
443

444
   const OCSP::Request req(issuer, subject_serial);
×
445

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

448
   http.throw_unless_ok();
×
449

450
   // Check the MIME type?
451

452
   return OCSP::Response(http.body());
×
453
}
×
454

455
Response online_check(const X509_Certificate& issuer,
×
456
                      const X509_Certificate& subject,
457
                      std::chrono::milliseconds timeout) {
458
   if(subject.issuer_dn() != issuer.subject_dn()) {
×
459
      throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
×
460
   }
461

462
   return online_check(issuer, BigInt::from_bytes(subject.serial_number()), subject.ocsp_responder(), timeout);
×
463
}
464

465
#endif
466

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