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

randombit / botan / 5878884781

16 Aug 2023 12:40PM UTC coverage: 91.628% (-0.07%) from 91.698%
5878884781

Pull #3673

github

web-flow
Merge 1b089e175 into fc4b4271f
Pull Request #3673: Refactoring SHA3: based on new permutation keccak-fips

78515 of 85689 relevant lines covered (91.63%)

12478353.03 hits per line

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

82.14
/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) {
2,630✔
30
   BER_Object obj = ber.get_next_object();
2,630✔
31

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

37
   BER_Decoder list(obj);
1,058✔
38

39
   while(list.more_items()) {
1,800✔
40
      BER_Object certbits = list.get_next_object();
1,033✔
41
      X509_Certificate cert(certbits.bits(), certbits.length());
1,032✔
42
      output.push_back(std::move(cert));
742✔
43
   }
1,774✔
44
}
3,367✔
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 {
2✔
78
   return Botan::base64_encode(BER_encode());
4✔
79
}
80

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

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

88
   size_t resp_status = 0;
2,797✔
89

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

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

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

98
   if(response_outer.more_items()) {
2,741✔
99
      BER_Decoder response_bytes = response_outer.start_context_specific(0).start_sequence();
2,734✔
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,721✔
102

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

105
      basicresponse.start_sequence()
2,678✔
106
         .raw_bytes(m_tbs_bits)
2,653✔
107
         .end_cons()
2,653✔
108
         .decode(m_sig_algo)
2,653✔
109
         .decode(m_signature, ASN1_Type::BitString);
2,638✔
110
      decode_optional_list(basicresponse, ASN1_Type(0), m_certs);
2,630✔
111

112
      size_t responsedata_version = 0;
2,338✔
113
      Extensions extensions;
2,338✔
114

115
      BER_Decoder(m_tbs_bits)
4,594✔
116
         .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed)
2,338✔
117

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

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

123
         .decode(m_produced_at)
1,984✔
124

125
         .decode_list(m_responses)
1,271✔
126

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

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

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

140
   response_outer.end_cons();
89✔
141
}
22,360✔
142

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

148
   if(!m_key_hash.empty()) {
1✔
149
      return (candidate.subject_public_key_bitstring_sha1() == m_key_hash);
×
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(
50✔
184
   const X509_Certificate& issuer_certificate, const Certificate_Store* trusted_ocsp_responders) const {
185
   using namespace std::placeholders;
50✔
186

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

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

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

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

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

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

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

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

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

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

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

255
   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
256
}
257

258
#if defined(BOTAN_HAS_HTTP_UTIL)
259

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

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

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

272
   http.throw_unless_ok();
×
273

274
   // Check the MIME type?
275

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

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

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

289
#endif
290

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

© 2025 Coveralls, Inc