Botan 2.19.5 Crypto and TLS for C&

Public Member Functions | List of all members
Botan::OCSP::Response Class Referencefinal

`#include <ocsp.h>`

## Public Member Functions
const std::vector< X509_Certificate > & | certificates () const
Certificate_Status_Code | check_signature (const std::vector< Certificate_Store * > &trust_roots, const std::vector< std::shared_ptr< const X509_Certificate > > &cert_path={}) const
const X509_Time & | produced_at () const
const std::vector< uint8_t > & | raw_bits () const
| Response ()=default
| Response (Certificate_Status_Code status)
| Response (const std::vector< uint8_t > &response_bits)
| Response (const uint8_t response_bits[], size_t response_bits_len)
const std::vector< uint8_t > & | signer_key_hash () const
const X509_DN & | signer_name () const
Response_Status_Code | status () const
Certificate_Status_Code | status_for (const X509_Certificate &issuer, const X509_Certificate &subject, std::chrono::system_clock::time_point ref_time=std::chrono::system_clock::now(), std::chrono::seconds max_age=std::chrono::seconds::zero()) const
Certificate_Status_Code | verify_signature (const X509_Certificate &issuer) const

## Detailed Description

OCSP response.

Note this class is only usable as an OCSP client

Definition at line 131 of file ocsp.h.

## Constructor & Destructor Documentation

## ◆ Response() [1/4]

Botan::OCSP::Response::Response | ( |  | ) |
| default

Creates an empty OCSP response.

## ◆ Response() [2/4]

Botan::OCSP::Response::Response | ( | Certificate_Status_Code | status | ) |

Create a fake OCSP response from a given status code.

Parameters

  status | the status code the check functions will return

Definition at line 93 of file ocsp.cpp.

94 {
95 m_status = Response_Status_Code::Successful;
96 m_dummy_response_status = status;
97 }
Botan::OCSP::Response::status
Response_Status_Code status() const
Definition: ocsp.h:183
Botan::OCSP::Response_Status_Code::Successful
@ Successful

References status(), and Botan::OCSP::Successful.

## ◆ Response() [3/4]

Botan::OCSP::Response::Response | ( | const std::vector< uint8_t > & | response_bits | ) |
| inline

Parses an OCSP response.

Parameters

  response_bits | response bits received

Definition at line 149 of file ocsp.h.

149 :
150 Response(response_bits.data(), response_bits.size())
151 {}
Botan::OCSP::Response::Response
Response()=default

## ◆ Response() [4/4]

Botan::OCSP::Response::Response | ( | const uint8_t | response_bits[],
|  | size_t | response_bits_len
| ) |  |

Parses an OCSP response.

Parameters

  response_bits | response bits received
  response_bits_len | length of response in bytes

Definition at line 99 of file ocsp.cpp.

99 :
100 m_response_bits(response_bits, response_bits + response_bits_len)
101 {
102 m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID;
103
104 BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE);
105
106 size_t resp_status = 0;
107
108 response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
109
110 m_status = static_cast<Response_Status_Code>(resp_status);
111
112 if(m_status != Response_Status_Code::Successful)
113 { return; }
114
115 if(response_outer.more_items())
116 {
117 BER_Decoder response_bytes =
118 response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
119
120 response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"),
121 "Unknown response type in OCSP response");
122
123 BER_Decoder basicresponse =
124 BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
125
126 basicresponse.start_cons(SEQUENCE)
127 .raw_bytes(m_tbs_bits)
128 .end_cons()
129 .decode(m_sig_algo)
130 .decode(m_signature, BIT_STRING);
131 decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
132
133 size_t responsedata_version = 0;
134 Extensions extensions;
135
136 BER_Decoder(m_tbs_bits)
137 .decode_optional(responsedata_version, ASN1_Tag(0),
138 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
139
140 .decode_optional(m_signer_name, ASN1_Tag(1),
141 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
142
143 .decode_optional_string(m_key_hash, OCTET_STRING, 2,
144 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
145
146 .decode(m_produced_at)
147
148 .decode_list(m_responses)
149
150 .decode_optional(extensions, ASN1_Tag(1),
151 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
152 }
153
154 response_outer.end_cons();
155 }
Botan::OCSP::Response_Status_Code
Response_Status_Code
Definition: ocsp.h:117
Botan::ASN1_Tag
ASN1_Tag
Definition: asn1_obj.h:25
Botan::CONSTRUCTED
@ CONSTRUCTED
Definition: asn1_obj.h:30
Botan::BIT_STRING
@ BIT_STRING
Definition: asn1_obj.h:37
Botan::SEQUENCE
@ SEQUENCE
Definition: asn1_obj.h:42
Botan::CONTEXT_SPECIFIC
@ CONTEXT_SPECIFIC
Definition: asn1_obj.h:28
Botan::ENUMERATED
@ ENUMERATED
Definition: asn1_obj.h:41
Botan::OCTET_STRING
@ OCTET_STRING
Definition: asn1_obj.h:38
Botan::UNIVERSAL
@ UNIVERSAL
Definition: asn1_obj.h:26
Botan::Certificate_Status_Code::OCSP_RESPONSE_INVALID
@ OCSP_RESPONSE_INVALID

References Botan::BIT_STRING, Botan::CONSTRUCTED, Botan::CONTEXT_SPECIFIC, Botan::BER_Decoder::decode(), Botan::BER_Decoder::decode_and_check(), Botan::BER_Decoder::decode_list(), Botan::BER_Decoder::decode_optional(), Botan::BER_Decoder::decode_optional_string(), Botan::BER_Decoder::end_cons(), Botan::ENUMERATED, Botan::BER_Decoder::get_next_octet_string(), Botan::BER_Decoder::more_items(), Botan::OCSP_RESPONSE_INVALID, Botan::OCTET_STRING, Botan::BER_Decoder::raw_bytes(), Botan::SEQUENCE, Botan::BER_Decoder::start_cons(), Botan::OCSP::Successful, and Botan::UNIVERSAL.

## Member Function Documentation

## ◆ certificates()

const std::vector< X509_Certificate > & Botan::OCSP::Response::certificates | ( |  | ) | const
| inline

Returns
  the certificate chain, if provided in response

Definition at line 226 of file ocsp.h.

226{ return m_certs; }

## ◆ check_signature()

Certificate_Status_Code Botan::OCSP::Response::check_signature | ( | const std::vector< Certificate_Store * > & | trust_roots,
|  | const std::vector< std::shared_ptr< const X509_Certificate > > & | cert_path = `{}`
| ) |  | const

Check signature and return status The optional cert_path is the (already validated!) certificate path of the end entity which is being inquired about

Parameters

  trust_roots | list of certstores containing trusted roots
  cert_path | optionally, the (already verified!) certificate path for the certificate this is an OCSP response for. This is necessary to find the correct intermediate CA in some cases.

Definition at line 188 of file ocsp.cpp.

190 {
191 if (m_responses.empty())
192 return m_dummy_response_status;
193
194 std::shared_ptr<const X509_Certificate> signing_cert;
195
196 for(size_t i = 0; i != trusted_roots.size(); ++i)
197 {
198 if(m_signer_name.empty() && m_key_hash.empty())
199 return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
200
201 if(!m_signer_name.empty())
202 {
203 signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector<uint8_t>());
204 if(signing_cert)
205 {
206 break;
207 }
208 }
209
210 if(m_key_hash.size() > 0)
211 {
212 signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash);
213 if(signing_cert)
214 {
215 break;
216 }
217 }
218 }
219
220 if(!signing_cert && ee_cert_path.size() > 1)
221 {
222 // End entity cert is not allowed to sign their own OCSP request :)
223 for(size_t i = 1; i < ee_cert_path.size(); ++i)
224 {
225 // Check all CA certificates in the (assumed validated) EE cert path
226 if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name)
227 {
228 signing_cert = ee_cert_path[i];
229 break;
230 }
231
232 if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash)
233 {
234 signing_cert = ee_cert_path[i];
235 break;
236 }
237 }
238 }
239
240 if(!signing_cert && m_certs.size() > 0)
241 {
242 for(size_t i = 0; i < m_certs.size(); ++i)
243 {
244 if(!m_signer_name.empty() && m_certs[i].subject_dn() == m_signer_name)
245 {
246 signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
247 break;
248 }
249
250 if(m_key_hash.size() > 0 && m_certs[i].subject_public_key_bitstring_sha1() == m_key_hash)
251 {
252 signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
253 break;
254 }
255 }
256
257 // RFC 6960 4.2.2.2
258 // OCSP signing delegation SHALL be designated by the inclusion of
259 // id-kp-OCSPSigning in an extended key usage certificate extension
260 // included in the OCSP response signer's certificate. This certificate
261 // MUST be issued directly by the CA that is identified in the request.
262 //
263 // The CA SHOULD use the same issuing key to issue a delegation
264 // certificate as that used to sign the certificate being checked for
265 // revocation. Systems relying on OCSP responses MUST recognize a
266 // delegation certificate as being issued by the CA that issued the
267 // certificate in question only if the delegation certificate and the
268 // certificate being checked for revocation were signed by the same key.
269 //
270 // I.e. it is safe to assume that the certificate's issuer also signed the
271 // responder's certificate.
272 //
273 // Note: The 'SHOULD' in the second paragraph above allows for backward
274 // compatibility to RFC 2560 that is "strongly discouraged". This
275 // implementation explicitly _does not_ implement this backward
276 // compatibility.
277 if(signing_cert)
278 {
279 const auto issuer =
280 Certificate_Store_In_Memory(ee_cert_path)
281 .find_cert(signing_cert->issuer_dn(), signing_cert->authority_key_id());
282
283 // User did not provide the certificate path to verify the delegation
284 if(!issuer)
285 {
286 return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
287 }
288
289 if(!issuer->is_CA_cert())
290 {
291 return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
292 }
293
294 // Sub-optimal fix for CVE-2022-43705 found in Botan 2.19.2 and older.
295 //
296 // This certificate validation is incomplete. Missing checks:
297 // * validity check against the reference time
298 // * revocation status check of the responder certificate
299 // * certificate extension validations
300 // * ... potentially more
301 //
302 // A more comprehensive validation will be introduced with Botan 3.0
303 try
304 {
305 const auto issuer_pubkey = issuer->load_subject_public_key();
306 const auto sig = signing_cert->verify_signature(*issuer_pubkey);
307
308 if(sig != Certificate_Status_Code::VERIFIED)
309 {
310 return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
311 }
312
313 if(!signing_cert->has_ex_constraint(OID::from_string("PKIX.OCSPSigning")))
314 {
315 return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
316 }
317 }
318 catch(const Exception& ex)
319 {
320 return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
321 }
322 }
323 }
324
325 if(!signing_cert)
326 return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
327
328 if(!signing_cert->allowed_usage(CRL_SIGN) &&
329 !signing_cert->allowed_extended_usage("PKIX.OCSPSigning"))
330 {
331 return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
332 }
333
334 return this->verify_signature(*signing_cert);
335 }
Botan::OCSP::Response::verify_signature
Certificate_Status_Code verify_signature(const X509_Certificate &issuer) const
Definition: ocsp.cpp:157
Botan::OID::from_string
static OID from_string(const std::string &str)
Definition: asn1_oid.cpp:62
Botan::X509_DN::empty
bool empty() const
Definition: pkix_types.h:70
Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE
@ OCSP_RESPONSE_MISSING_KEYUSAGE
Botan::Certificate_Status_Code::VERIFIED
@ VERIFIED
Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR
@ OCSP_SIGNATURE_ERROR
Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND
@ OCSP_ISSUER_NOT_FOUND
Botan::CRL_SIGN
@ CRL_SIGN
Definition: pkix_enums.h:114

References Botan::CRL_SIGN, Botan::X509_DN::empty(), Botan::Certificate_Store_In_Memory::find_cert(), Botan::OID::from_string(), Botan::OCSP_ISSUER_NOT_FOUND, Botan::OCSP_RESPONSE_INVALID, Botan::OCSP_RESPONSE_MISSING_KEYUSAGE, Botan::OCSP_SIGNATURE_ERROR, Botan::VERIFIED, and verify_signature().

## ◆ produced_at()

const X509_Time & Botan::OCSP::Response::produced_at | ( |  | ) | const
| inline

Returns
  the time this OCSP response was supposedly produced at

Definition at line 188 of file ocsp.h.

188{ return m_produced_at; }

## ◆ raw_bits()

const std::vector< uint8_t > & Botan::OCSP::Response::raw_bits | ( |  | ) | const
| inline

Definition at line 200 of file ocsp.h.

200{ return m_response_bits; }

## ◆ signer_key_hash()

const std::vector< uint8_t > & Botan::OCSP::Response::signer_key_hash | ( |  | ) | const
| inline

Returns
  key hash, if provided in response (may be empty)

Definition at line 198 of file ocsp.h.

198{ return m_key_hash; }

## ◆ signer_name()

const X509_DN & Botan::OCSP::Response::signer_name | ( |  | ) | const
| inline

Returns
  DN of signer, if provided in response (may be empty)

Definition at line 193 of file ocsp.h.

193{ return m_signer_name; }

## ◆ status()

Response_Status_Code Botan::OCSP::Response::status | ( |  | ) | const
| inline

Returns
  the status of the response

Definition at line 183 of file ocsp.h.

183{ return m_status; }

Referenced by Response().

## ◆ status_for()

Certificate_Status_Code Botan::OCSP::Response::status_for | ( | const X509_Certificate & | issuer,
|  | const X509_Certificate & | subject,
|  | std::chrono::system_clock::time_point | ref_time = `std::chrono::system_clock::now()`,
|  | std::chrono::seconds | max_age = `std::chrono::seconds::zero()`
| ) |  | const

Searches the OCSP response for issuer and subject certificate.

Parameters

  issuer | issuer certificate
  subject | subject certificate
  ref_time | the reference time
  max_age | the maximum age the response should be considered valid if next_update is not set

Returns
  OCSP status code, possible values: CERT_IS_REVOKED, OCSP_NOT_YET_VALID, OCSP_HAS_EXPIRED, OCSP_IS_TOO_OLD, OCSP_RESPONSE_GOOD, OCSP_BAD_STATUS, OCSP_CERT_NOT_LISTED

Definition at line 337 of file ocsp.cpp.

341 {
342 if(m_responses.empty())
343 { return m_dummy_response_status; }
344
345 for(const auto& response : m_responses)
346 {
347 if(response.certid().is_id_for(issuer, subject))
348 {
349 X509_Time x509_ref_time(ref_time);
350
351 if(response.cert_status() == 1)
352 { return Certificate_Status_Code::CERT_IS_REVOKED; }
353
354 if(response.this_update() > x509_ref_time)
355 { return Certificate_Status_Code::OCSP_NOT_YET_VALID; }
356
357 if(response.next_update().time_is_set())
358 {
359 if(x509_ref_time > response.next_update())
360 { return Certificate_Status_Code::OCSP_HAS_EXPIRED; }
361 }
362 else if(max_age > std::chrono::seconds::zero() && ref_time - response.this_update().to_std_timepoint() > max_age)
363 { return Certificate_Status_Code::OCSP_IS_TOO_OLD; }
364
365 if(response.cert_status() == 0)
366 { return Certificate_Status_Code::OCSP_RESPONSE_GOOD; }
367 else
368 { return Certificate_Status_Code::OCSP_BAD_STATUS; }
369 }
370 }
371
372 return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
373 }
Botan::X509_Time
ASN1_Time X509_Time
Definition: asn1_obj.h:386
Botan::Certificate_Status_Code::OCSP_BAD_STATUS
@ OCSP_BAD_STATUS
Botan::Certificate_Status_Code::OCSP_CERT_NOT_LISTED
@ OCSP_CERT_NOT_LISTED
Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED
@ OCSP_HAS_EXPIRED
Botan::Certificate_Status_Code::CERT_IS_REVOKED
@ CERT_IS_REVOKED
Botan::Certificate_Status_Code::OCSP_IS_TOO_OLD
@ OCSP_IS_TOO_OLD
Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD
@ OCSP_RESPONSE_GOOD
Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID
@ OCSP_NOT_YET_VALID

References Botan::CERT_IS_REVOKED, Botan::OCSP_BAD_STATUS, Botan::OCSP_CERT_NOT_LISTED, Botan::OCSP_HAS_EXPIRED, Botan::OCSP_IS_TOO_OLD, Botan::OCSP_NOT_YET_VALID, and Botan::OCSP_RESPONSE_GOOD.

## ◆ verify_signature()

Certificate_Status_Code Botan::OCSP::Response::verify_signature | ( | const X509_Certificate & | issuer | ) | const

Verify that issuer's key signed this response

Parameters

  issuer | certificate of issuer

Returns
  if signature valid OCSP_SIGNATURE_OK else an error code

Definition at line 157 of file ocsp.cpp.

158 {
159 if (m_responses.empty())
160 return m_dummy_response_status;
161
162 try
163 {
164 std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
165
166 const std::vector<std::string> sig_info =
167 split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
168
169 if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
170 return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
171
172 std::string padding = sig_info[1];
173 const Signature_Format format = pub_key->default_x509_signature_format();
174
175 PK_Verifier verifier(*pub_key, padding, format);
176
177 if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature))
178 return Certificate_Status_Code::OCSP_SIGNATURE_OK;
179 else
180 return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
181 }
182 catch(Exception&)
183 {
184 return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
185 }
186 }
Botan::AlgorithmIdentifier::get_oid
const OID & get_oid() const
Definition: asn1_obj.h:445
Botan::OID::to_formatted_string
std::string to_formatted_string() const
Definition: asn1_oid.cpp:111
Botan::ASN1::put_in_sequence
std::vector< uint8_t > put_in_sequence(const std::vector< uint8_t > &contents)
Definition: asn1_obj.cpp:195
Botan::split_on
std::vector< std::string > split_on(const std::string &str, char delim)
Definition: parsing.cpp:148
Botan::Signature_Format
Signature_Format
Definition: pk_keys.h:23
Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK
@ OCSP_SIGNATURE_OK

References Botan::AlgorithmIdentifier::get_oid(), Botan::OCSP_RESPONSE_INVALID, Botan::OCSP_SIGNATURE_ERROR, Botan::OCSP_SIGNATURE_OK, Botan::ASN1::put_in_sequence(), Botan::split_on(), Botan::X509_Certificate::subject_public_key(), Botan::OID::to_formatted_string(), and Botan::PK_Verifier::verify_message().

Referenced by check_signature().

The documentation for this class was generated from the following files:

- src/lib/x509/ocsp.h
- src/lib/x509/ocsp.cpp