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

randombit / botan / 5134090420

31 May 2023 03:12PM UTC coverage: 91.721% (-0.3%) from 91.995%
5134090420

push

github

randombit
Merge GH #3565 Disable noisy/pointless pylint warnings

76048 of 82912 relevant lines covered (91.72%)

11755290.1 hits per line

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

82.01
/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
#include <botan/internal/parsing.h>
17

18
#include <functional>
19

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

24
namespace Botan::OCSP {
25

26
namespace {
27

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

32
   if(obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed) == false) {
997✔
33
      ber.push_back(obj);
22✔
34
      return;
22✔
35
   }
36

37
   BER_Decoder list(obj);
975✔
38

39
   while(list.more_items()) {
1,690✔
40
      BER_Object certbits = list.get_next_object();
979✔
41
      X509_Certificate cert(certbits.bits(), certbits.length());
979✔
42
      output.push_back(std::move(cert));
715✔
43
   }
1,694✔
44
}
1,708✔
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::decode(subject_cert.serial_number())) {
4✔
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 { return Botan::base64_encode(BER_encode()); }
4✔
78

79
Response::Response(Certificate_Status_Code status) :
6✔
80
      m_status(Response_Status_Code::Successful), m_dummy_response_status(status) {}
6✔
81

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

86
   size_t resp_status = 0;
2,798✔
87

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

90
   m_status = static_cast<Response_Status_Code>(resp_status);
1,513✔
91

92
   if(m_status != Response_Status_Code::Successful) {
1,513✔
93
      return;
5✔
94
   }
95

96
   if(response_outer.more_items()) {
1,508✔
97
      BER_Decoder response_bytes = response_outer.start_context_specific(0).start_sequence();
1,500✔
98

99
      response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response");
1,265✔
100

101
      BER_Decoder basicresponse = BER_Decoder(response_bytes.get_next_octet_string()).start_sequence();
2,246✔
102

103
      basicresponse.start_sequence()
1,009✔
104
         .raw_bytes(m_tbs_bits)
1,003✔
105
         .end_cons()
1,003✔
106
         .decode(m_sig_algo)
1,003✔
107
         .decode(m_signature, ASN1_Type::BitString);
997✔
108
      decode_optional_list(basicresponse, ASN1_Type(0), m_certs);
997✔
109

110
      size_t responsedata_version = 0;
733✔
111
      Extensions extensions;
733✔
112

113
      BER_Decoder(m_tbs_bits)
1,392✔
114
         .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
733✔
115

116
         .decode_optional(m_signer_name, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
1,455✔
117

118
         .decode_optional_string(
720✔
119
            m_key_hash, ASN1_Type::OctetString, 2, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
120

121
         .decode(m_produced_at)
719✔
122

123
         .decode_list(m_responses)
138✔
124

125
         .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed);
798✔
126

127
      const bool has_signer = !m_signer_name.empty();
74✔
128
      const bool has_key_hash = !m_key_hash.empty();
74✔
129

130
      if(has_signer && has_key_hash) {
74✔
131
         throw Decoding_Error("OCSP response includes both byName and byKey in responderID field");
×
132
      }
133
      if(!has_signer && !has_key_hash) {
74✔
134
         throw Decoding_Error("OCSP response contains neither byName nor byKey in responderID field");
×
135
      }
136
   }
2,194✔
137

138
   response_outer.end_cons();
82✔
139
}
19,320✔
140

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

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

150
   return false;
151
}
152

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

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

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

166
   try {
1✔
167
      auto pub_key = issuer.subject_public_key();
1✔
168

169
      PK_Verifier verifier(*pub_key, m_sig_algo);
1✔
170

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

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

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

190
   // Then try to find a delegated responder certificate in the stapled certs
191
   auto match = std::find_if(m_certs.begin(), m_certs.end(), std::bind(&Response::is_issued_by, this, _1));
32✔
192
   if(match != m_certs.end()) {
32✔
193
      return *match;
75✔
194
   }
195

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

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

213
   return std::nullopt;
6✔
214
}
215

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

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

228
         if(response.cert_status() == 1) {
37✔
229
            return Certificate_Status_Code::CERT_IS_REVOKED;
230
         }
231

232
         if(response.this_update() > x509_ref_time) {
37✔
233
            return Certificate_Status_Code::OCSP_NOT_YET_VALID;
234
         }
235

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

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

253
   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
254
}
255

256
#if defined(BOTAN_HAS_HTTP_UTIL)
257

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

266
   OCSP::Request req(issuer, subject_serial);
×
267

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

270
   http.throw_unless_ok();
×
271

272
   // Check the MIME type?
273

274
   return OCSP::Response(http.body());
×
275
}
×
276

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

284
   return online_check(issuer, BigInt::decode(subject.serial_number()), subject.ocsp_responder(), timeout);
×
285
}
286

287
#endif
288

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