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