/src/botan/src/lib/x509/ocsp.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include <botan/certstor.h> |
10 | | #include <botan/der_enc.h> |
11 | | #include <botan/ber_dec.h> |
12 | | #include <botan/x509_ext.h> |
13 | | #include <botan/oids.h> |
14 | | #include <botan/base64.h> |
15 | | #include <botan/pubkey.h> |
16 | | #include <botan/internal/parsing.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 | | namespace { |
25 | | |
26 | | // TODO: should this be in a header somewhere? |
27 | | void decode_optional_list(BER_Decoder& ber, |
28 | | ASN1_Type tag, |
29 | | std::vector<X509_Certificate>& output) |
30 | 4.24k | { |
31 | 4.24k | BER_Object obj = ber.get_next_object(); |
32 | | |
33 | 4.24k | if(obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed) == false) |
34 | 1.69k | { |
35 | 1.69k | ber.push_back(obj); |
36 | 1.69k | return; |
37 | 1.69k | } |
38 | | |
39 | 2.54k | BER_Decoder list(obj); |
40 | | |
41 | 7.58k | while(list.more_items()) |
42 | 5.04k | { |
43 | 5.04k | BER_Object certbits = list.get_next_object(); |
44 | 5.04k | X509_Certificate cert(certbits.bits(), certbits.length()); |
45 | 5.04k | output.push_back(std::move(cert)); |
46 | 5.04k | } |
47 | 2.54k | } |
48 | | |
49 | | } |
50 | | |
51 | | Request::Request(const X509_Certificate& issuer_cert, |
52 | | const X509_Certificate& subject_cert) : |
53 | | m_issuer(issuer_cert), |
54 | | m_certid(m_issuer, BigInt::decode(subject_cert.serial_number())) |
55 | 0 | { |
56 | 0 | if(subject_cert.issuer_dn() != issuer_cert.subject_dn()) |
57 | 0 | throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)"); |
58 | 0 | } |
59 | | |
60 | | Request::Request(const X509_Certificate& issuer_cert, |
61 | | const BigInt& subject_serial) : |
62 | | m_issuer(issuer_cert), |
63 | | m_certid(m_issuer, subject_serial) |
64 | 0 | { |
65 | 0 | } |
66 | | |
67 | | std::vector<uint8_t> Request::BER_encode() const |
68 | 0 | { |
69 | 0 | std::vector<uint8_t> output; |
70 | 0 | DER_Encoder(output).start_sequence() |
71 | 0 | .start_sequence() |
72 | 0 | .start_explicit(0) |
73 | 0 | .encode(static_cast<size_t>(0)) // version # |
74 | 0 | .end_explicit() |
75 | 0 | .start_sequence() |
76 | 0 | .start_sequence() |
77 | 0 | .encode(m_certid) |
78 | 0 | .end_cons() |
79 | 0 | .end_cons() |
80 | 0 | .end_cons() |
81 | 0 | .end_cons(); |
82 | |
|
83 | 0 | return output; |
84 | 0 | } |
85 | | |
86 | | std::string Request::base64_encode() const |
87 | 0 | { |
88 | 0 | return Botan::base64_encode(BER_encode()); |
89 | 0 | } |
90 | | |
91 | | Response::Response(Certificate_Status_Code status) |
92 | 0 | { |
93 | 0 | m_status = Response_Status_Code::Successful; |
94 | 0 | m_dummy_response_status = status; |
95 | 0 | } |
96 | | |
97 | | Response::Response(const uint8_t response_bits[], size_t response_bits_len) : |
98 | | m_response_bits(response_bits, response_bits + response_bits_len) |
99 | 5.66k | { |
100 | 5.66k | m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID; |
101 | | |
102 | 5.66k | BER_Decoder response_outer = BER_Decoder(m_response_bits).start_sequence(); |
103 | | |
104 | 5.66k | size_t resp_status = 0; |
105 | | |
106 | 5.66k | response_outer.decode(resp_status, ASN1_Type::Enumerated, ASN1_Class::Universal); |
107 | | |
108 | 5.66k | m_status = static_cast<Response_Status_Code>(resp_status); |
109 | | |
110 | 5.66k | if(m_status != Response_Status_Code::Successful) |
111 | 72 | { return; } |
112 | | |
113 | 5.59k | if(response_outer.more_items()) |
114 | 4.54k | { |
115 | 4.54k | BER_Decoder response_bytes = |
116 | 4.54k | response_outer.start_context_specific(0).start_sequence(); |
117 | | |
118 | 4.54k | response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"), |
119 | 4.54k | "Unknown response type in OCSP response"); |
120 | | |
121 | 4.54k | BER_Decoder basicresponse = |
122 | 4.54k | BER_Decoder(response_bytes.get_next_octet_string()).start_sequence(); |
123 | | |
124 | 4.54k | basicresponse.start_sequence() |
125 | 4.54k | .raw_bytes(m_tbs_bits) |
126 | 4.54k | .end_cons() |
127 | 4.54k | .decode(m_sig_algo) |
128 | 4.54k | .decode(m_signature, ASN1_Type::BitString); |
129 | 4.54k | decode_optional_list(basicresponse, ASN1_Type(0), m_certs); |
130 | | |
131 | 4.54k | size_t responsedata_version = 0; |
132 | 4.54k | Extensions extensions; |
133 | | |
134 | 4.54k | BER_Decoder(m_tbs_bits) |
135 | 4.54k | .decode_optional(responsedata_version, ASN1_Type(0), |
136 | 4.54k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed) |
137 | | |
138 | 4.54k | .decode_optional(m_signer_name, ASN1_Type(1), |
139 | 4.54k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed) |
140 | | |
141 | 4.54k | .decode_optional_string(m_key_hash, ASN1_Type::OctetString, 2, |
142 | 4.54k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed) |
143 | | |
144 | 4.54k | .decode(m_produced_at) |
145 | | |
146 | 4.54k | .decode_list(m_responses) |
147 | | |
148 | 4.54k | .decode_optional(extensions, ASN1_Type(1), |
149 | 4.54k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed); |
150 | 4.54k | } |
151 | | |
152 | 5.59k | response_outer.end_cons(); |
153 | 5.59k | } |
154 | | |
155 | | Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const |
156 | 0 | { |
157 | 0 | if (m_responses.empty()) |
158 | 0 | return m_dummy_response_status; |
159 | | |
160 | 0 | try |
161 | 0 | { |
162 | 0 | std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key()); |
163 | |
|
164 | 0 | const std::vector<std::string> sig_info = |
165 | 0 | split_on(m_sig_algo.get_oid().to_formatted_string(), '/'); |
166 | |
|
167 | 0 | if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name()) |
168 | 0 | return Certificate_Status_Code::OCSP_RESPONSE_INVALID; |
169 | | |
170 | 0 | std::string padding = sig_info[1]; |
171 | 0 | const Signature_Format format = pub_key->default_x509_signature_format(); |
172 | |
|
173 | 0 | PK_Verifier verifier(*pub_key, padding, format); |
174 | |
|
175 | 0 | if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) |
176 | 0 | return Certificate_Status_Code::OCSP_SIGNATURE_OK; |
177 | 0 | else |
178 | 0 | return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; |
179 | 0 | } |
180 | 0 | catch(Exception&) |
181 | 0 | { |
182 | 0 | return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | Certificate_Status_Code Response::check_signature(const std::vector<Certificate_Store*>& trusted_roots, |
187 | | const std::vector<X509_Certificate>& ee_cert_path) const |
188 | 0 | { |
189 | 0 | if (m_responses.empty()) |
190 | 0 | return m_dummy_response_status; |
191 | | |
192 | 0 | std::optional<X509_Certificate> signing_cert; |
193 | |
|
194 | 0 | for(const auto& trusted_root : trusted_roots) |
195 | 0 | { |
196 | 0 | if(m_signer_name.empty() && m_key_hash.empty()) |
197 | 0 | return Certificate_Status_Code::OCSP_RESPONSE_INVALID; |
198 | | |
199 | 0 | if(!m_signer_name.empty()) |
200 | 0 | { |
201 | 0 | signing_cert = trusted_root->find_cert(m_signer_name, std::vector<uint8_t>()); |
202 | 0 | if(signing_cert) |
203 | 0 | { |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | 0 | if(!m_key_hash.empty()) |
209 | 0 | { |
210 | 0 | signing_cert = trusted_root->find_cert_by_pubkey_sha1(m_key_hash); |
211 | 0 | if(signing_cert) |
212 | 0 | { |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | } |
216 | 0 | } |
217 | | |
218 | 0 | if(!signing_cert && ee_cert_path.size() > 1) |
219 | 0 | { |
220 | | // End entity cert is not allowed to sign their own OCSP request :) |
221 | 0 | for(size_t i = 1; i < ee_cert_path.size(); ++i) |
222 | 0 | { |
223 | | // Check all CA certificates in the (assumed validated) EE cert path |
224 | 0 | if(!m_signer_name.empty() && ee_cert_path[i].subject_dn() == m_signer_name) |
225 | 0 | { |
226 | 0 | signing_cert = ee_cert_path[i]; |
227 | 0 | break; |
228 | 0 | } |
229 | | |
230 | 0 | if(!m_key_hash.empty() && ee_cert_path[i].subject_public_key_bitstring_sha1() == m_key_hash) |
231 | 0 | { |
232 | 0 | signing_cert = ee_cert_path[i]; |
233 | 0 | break; |
234 | 0 | } |
235 | 0 | } |
236 | 0 | } |
237 | |
|
238 | 0 | if(!signing_cert && !m_certs.empty()) |
239 | 0 | { |
240 | 0 | for(const auto& cert : m_certs) |
241 | 0 | { |
242 | | // Check all CA certificates in the (assumed validated) EE cert path |
243 | 0 | if(!m_signer_name.empty() && cert.subject_dn() == m_signer_name) |
244 | 0 | { |
245 | 0 | signing_cert = cert; |
246 | 0 | break; |
247 | 0 | } |
248 | | |
249 | 0 | if(!m_key_hash.empty() && cert.subject_public_key_bitstring_sha1() == m_key_hash) |
250 | 0 | { |
251 | 0 | signing_cert = cert; |
252 | 0 | break; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | } |
256 | |
|
257 | 0 | if(!signing_cert) |
258 | 0 | return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND; |
259 | | |
260 | 0 | if(!signing_cert->allowed_usage(CRL_SIGN) && |
261 | 0 | !signing_cert->allowed_extended_usage("PKIX.OCSPSigning")) |
262 | 0 | { |
263 | 0 | return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE; |
264 | 0 | } |
265 | | |
266 | 0 | return this->verify_signature(*signing_cert); |
267 | 0 | } |
268 | | |
269 | | Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, |
270 | | const X509_Certificate& subject, |
271 | | std::chrono::system_clock::time_point ref_time, |
272 | | std::chrono::seconds max_age) const |
273 | 0 | { |
274 | 0 | if(m_responses.empty()) |
275 | 0 | { return m_dummy_response_status; } |
276 | | |
277 | 0 | for(const auto& response : m_responses) |
278 | 0 | { |
279 | 0 | if(response.certid().is_id_for(issuer, subject)) |
280 | 0 | { |
281 | 0 | X509_Time x509_ref_time(ref_time); |
282 | |
|
283 | 0 | if(response.cert_status() == 1) |
284 | 0 | { return Certificate_Status_Code::CERT_IS_REVOKED; } |
285 | | |
286 | 0 | if(response.this_update() > x509_ref_time) |
287 | 0 | { return Certificate_Status_Code::OCSP_NOT_YET_VALID; } |
288 | | |
289 | 0 | if(response.next_update().time_is_set()) |
290 | 0 | { |
291 | 0 | if(x509_ref_time > response.next_update()) |
292 | 0 | { return Certificate_Status_Code::OCSP_HAS_EXPIRED; } |
293 | 0 | } |
294 | 0 | else if(max_age > std::chrono::seconds::zero() && ref_time - response.this_update().to_std_timepoint() > max_age) |
295 | 0 | { return Certificate_Status_Code::OCSP_IS_TOO_OLD; } |
296 | | |
297 | 0 | if(response.cert_status() == 0) |
298 | 0 | { return Certificate_Status_Code::OCSP_RESPONSE_GOOD; } |
299 | 0 | else |
300 | 0 | { return Certificate_Status_Code::OCSP_BAD_STATUS; } |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | 0 | return Certificate_Status_Code::OCSP_CERT_NOT_LISTED; |
305 | 0 | } |
306 | | |
307 | | #if defined(BOTAN_HAS_HTTP_UTIL) |
308 | | |
309 | | Response online_check(const X509_Certificate& issuer, |
310 | | const BigInt& subject_serial, |
311 | | const std::string& ocsp_responder, |
312 | | Certificate_Store* trusted_roots, |
313 | | std::chrono::milliseconds timeout) |
314 | 0 | { |
315 | 0 | if(ocsp_responder.empty()) |
316 | 0 | throw Invalid_Argument("No OCSP responder specified"); |
317 | | |
318 | 0 | OCSP::Request req(issuer, subject_serial); |
319 | |
|
320 | 0 | auto http = HTTP::POST_sync(ocsp_responder, |
321 | 0 | "application/ocsp-request", |
322 | 0 | req.BER_encode(), |
323 | 0 | 1, |
324 | 0 | timeout); |
325 | |
|
326 | 0 | http.throw_unless_ok(); |
327 | | |
328 | | // Check the MIME type? |
329 | |
|
330 | 0 | OCSP::Response response(http.body()); |
331 | |
|
332 | 0 | std::vector<Certificate_Store*> trusted_roots_vec; |
333 | 0 | trusted_roots_vec.push_back(trusted_roots); |
334 | |
|
335 | 0 | if(trusted_roots) |
336 | 0 | response.check_signature(trusted_roots_vec); |
337 | |
|
338 | 0 | return response; |
339 | 0 | } |
340 | | |
341 | | |
342 | | Response online_check(const X509_Certificate& issuer, |
343 | | const X509_Certificate& subject, |
344 | | Certificate_Store* trusted_roots, |
345 | | std::chrono::milliseconds timeout) |
346 | 0 | { |
347 | 0 | if(subject.issuer_dn() != issuer.subject_dn()) |
348 | 0 | throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)"); |
349 | | |
350 | 0 | return online_check(issuer, |
351 | 0 | BigInt::decode(subject.serial_number()), |
352 | 0 | subject.ocsp_responder(), |
353 | 0 | trusted_roots, |
354 | 0 | timeout); |
355 | 0 | } |
356 | | |
357 | | #endif |
358 | | |
359 | | } |