Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/tls/ocsp/ocsp.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include <iomanip>
4
#include <sstream>
5
#include <vector>
6
7
#include "envoy/common/exception.h"
8
#include "envoy/common/time.h"
9
#include "envoy/ssl/context.h"
10
11
#include "absl/status/statusor.h"
12
#include "absl/strings/string_view.h"
13
#include "absl/types/optional.h"
14
#include "openssl/bytestring.h"
15
#include "openssl/ssl.h"
16
17
/**
18
 * Data structures and functions for unmarshaling OCSP responses
19
 * according to the RFC6960 B.2 spec. See: https://tools.ietf.org/html/rfc6960#appendix-B
20
 *
21
 * WARNING: This module is meant to validate that OCSP responses are well-formed
22
 * and extract useful fields for OCSP stapling. This assumes that responses are
23
 * provided from configs or another trusted source and does not perform
24
 * checks necessary to verify responses coming from an upstream server.
25
 */
26
27
namespace Envoy {
28
namespace Extensions {
29
namespace TransportSockets {
30
namespace Tls {
31
namespace Ocsp {
32
33
/**
34
 * Reflection of the `ASN.1` OcspResponseStatus enumeration.
35
 * The possible statuses that can accompany an OCSP response.
36
 */
37
enum class OcspResponseStatus {
38
  // OCSPResponseStatus ::= ENUMERATED {
39
  //    successful            (0),  -- Response has valid confirmations
40
  //    malformedRequest      (1),  -- Illegal confirmation request
41
  //    internalError         (2),  -- Internal error in issuer
42
  //    tryLater              (3),  -- Try again later
43
  //                                -- (4) is not used
44
  //    sigRequired           (5),  -- Must sign the request
45
  //    unauthorized          (6)   -- Request unauthorized
46
  // }
47
  Successful = 0,
48
  MalformedRequest = 1,
49
  InternalError = 2,
50
  TryLater = 3,
51
  SigRequired = 5,
52
  Unauthorized = 6
53
};
54
55
/**
56
 * Partial reflection of the `ASN.1` CertId structure.
57
 * Contains the information to identify an SSL Certificate.
58
 * Serial numbers are guaranteed to be
59
 * unique per issuer but not necessarily universally.
60
 */
61
struct CertId {
62
  CertId(std::string serial_number);
63
64
  std::string serial_number_;
65
};
66
67
/**
68
 * Partial reflection of the `ASN.1` SingleResponse structure.
69
 * Contains information about the OCSP status of a single certificate.
70
 * An OCSP request may request the status of multiple certificates and
71
 * therefore responses may contain multiple SingleResponses.
72
 *
73
 * this_update_ and next_update_ reflect the validity period for this response.
74
 * If next_update_ is not present, the OCSP responder always has new information
75
 * available. In this case the response would be considered immediately expired
76
 * and invalid for stapling.
77
 */
78
struct SingleResponse {
79
  SingleResponse(CertId cert_id, Envoy::SystemTime this_update,
80
                 absl::optional<Envoy::SystemTime> next_update);
81
82
  const CertId cert_id_;
83
  const Envoy::SystemTime this_update_;
84
  const absl::optional<Envoy::SystemTime> next_update_;
85
};
86
87
/**
88
 * Partial reflection of the `ASN.1` ResponseData structure.
89
 * Contains an OCSP response for each certificate in a given request
90
 * as well as the time at which the response was produced.
91
 */
92
struct ResponseData {
93
  ResponseData(std::vector<SingleResponse> single_responses);
94
95
  const std::vector<SingleResponse> single_responses_;
96
};
97
98
/**
99
 * An abstract type for OCSP response formats. Which variant of `Response` is
100
 * used in an `OcspResponse` is indicated by the structure's `OID`.
101
 *
102
 * Envoy enforces that OCSP responses must be for a single certificate
103
 * only. The methods on this class extract the relevant information for the
104
 * single certificate contained in the response.
105
 */
106
class Response {
107
public:
108
0
  virtual ~Response() = default;
109
110
  /**
111
   * @return The number of certs reported on by this response.
112
   */
113
  virtual size_t getNumCerts() PURE;
114
115
  /**
116
   * @return The serial number of the certificate.
117
   */
118
  virtual const std::string& getCertSerialNumber() PURE;
119
120
  /**
121
   * @return The beginning of the validity window for this response.
122
   */
123
  virtual const Envoy::SystemTime& getThisUpdate() PURE;
124
125
  /**
126
   * The time at which this response is considered to expire. If
127
   * `nullopt`, then there is assumed to always be more up-to-date
128
   * information available and the response is always considered expired.
129
   *
130
   * @return The end of the validity window for this response.
131
   */
132
  virtual const absl::optional<Envoy::SystemTime>& getNextUpdate() PURE;
133
};
134
135
using ResponsePtr = std::unique_ptr<Response>;
136
137
/**
138
 * Reflection of the `ASN.1` BasicOcspResponse structure.
139
 * Contains the full data of an OCSP response.
140
 * Envoy enforces that OCSP responses contain a response for only
141
 * a single certificate.
142
 *
143
 * BasicOcspResponse is the only supported Response type in RFC 6960.
144
 */
145
class BasicOcspResponse : public Response {
146
public:
147
  BasicOcspResponse(ResponseData data);
148
149
  // Response
150
0
  size_t getNumCerts() override { return data_.single_responses_.size(); }
151
0
  const std::string& getCertSerialNumber() override {
152
0
    return data_.single_responses_[0].cert_id_.serial_number_;
153
0
  }
154
0
  const Envoy::SystemTime& getThisUpdate() override {
155
0
    return data_.single_responses_[0].this_update_;
156
0
  }
157
0
  const absl::optional<Envoy::SystemTime>& getNextUpdate() override {
158
0
    return data_.single_responses_[0].next_update_;
159
0
  }
160
161
  // Identified as `id-pkix-ocsp-basic` in
162
  // https://tools.ietf.org/html/rfc6960#appendix-B.2
163
  constexpr static absl::string_view OID = "1.3.6.1.5.5.7.48.1.1";
164
165
private:
166
  const ResponseData data_;
167
};
168
169
/**
170
 * Reflection of the `ASN.1` OcspResponse structure.
171
 * This is the top-level data structure for OCSP responses.
172
 */
173
struct OcspResponse {
174
  OcspResponse(OcspResponseStatus status, ResponsePtr response);
175
176
  OcspResponseStatus status_;
177
  ResponsePtr response_;
178
};
179
180
/**
181
 * A wrapper used to own and query an OCSP response in DER-encoded format.
182
 */
183
class OcspResponseWrapperImpl : public Ssl::OcspResponseWrapper {
184
public:
185
  OcspResponseWrapperImpl(std::vector<uint8_t> der_response, TimeSource& time_source,
186
                          std::unique_ptr<OcspResponse>&& response);
187
  static absl::StatusOr<std::unique_ptr<OcspResponseWrapperImpl>>
188
  create(std::vector<uint8_t> der_response, TimeSource& time_source);
189
190
  /**
191
   * @return OcspResponseStatus whether the OCSP response was successfully created
192
   * or a status indicating an error in the OCSP process
193
   */
194
0
  OcspResponseStatus getResponseStatus() const { return response_->status_; }
195
196
  /**
197
   * @param cert a X509& SSL certificate
198
   * @returns bool whether this OCSP response contains the revocation status of `cert`
199
   */
200
  bool matchesCertificate(X509& cert) const;
201
202
  // OcspResponseWrapper
203
  uint64_t secondsUntilExpiration() const override;
204
  Envoy::SystemTime getThisUpdate() const override;
205
  Envoy::SystemTime getNextUpdate() const override;
206
  bool isExpired() override;
207
0
  const std::vector<uint8_t>& rawBytes() const override { return raw_bytes_; }
208
209
private:
210
  const std::vector<uint8_t> raw_bytes_;
211
  const std::unique_ptr<OcspResponse> response_;
212
  TimeSource& time_source_;
213
};
214
215
/**
216
 * `ASN.1` DER-encoded parsing functions similar to `Asn1Utility` but specifically
217
 * for structures related to OCSP.
218
 *
219
 * Each function must advance `cbs` across the element it refers to.
220
 */
221
class Asn1OcspUtility {
222
public:
223
  /**
224
   * @param `cbs` a CBS& that refers to an `ASN.1` OcspResponse element
225
   * @returns std::unique_ptr<OcspResponse> the OCSP response encoded in `cbs`
226
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed OcspResponse
227
   * element.
228
   */
229
  static absl::StatusOr<std::unique_ptr<OcspResponse>> parseOcspResponse(CBS& cbs);
230
231
  /**
232
   * @param cbs a CBS& that refers to an `ASN.1` OcspResponseStatus element
233
   * @returns OcspResponseStatus the OCSP response encoded in `cbs`
234
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed
235
   * OcspResponseStatus element.
236
   */
237
  static absl::StatusOr<OcspResponseStatus> parseResponseStatus(CBS& cbs);
238
239
  /**
240
   * @param cbs a CBS& that refers to an `ASN.1` Response element
241
   * @returns Response containing the content of an OCSP response
242
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed
243
   * structure that is a valid Response type.
244
   */
245
  static absl::StatusOr<ResponsePtr> parseResponseBytes(CBS& cbs);
246
247
  /**
248
   * @param cbs a CBS& that refers to an `ASN.1` BasicOcspResponse element
249
   * @returns BasicOcspResponse containing the content of an OCSP response
250
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed
251
   * BasicOcspResponse element.
252
   */
253
  static absl::StatusOr<std::unique_ptr<BasicOcspResponse>> parseBasicOcspResponse(CBS& cbs);
254
255
  /**
256
   * @param cbs a CBS& that refers to an `ASN.1` ResponseData element
257
   * @returns ResponseData containing the content of an OCSP response relating
258
   * to certificate statuses.
259
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed
260
   * ResponseData element.
261
   */
262
  static absl::StatusOr<ResponseData> parseResponseData(CBS& cbs);
263
264
  /**
265
   * @param cbs a CBS& that refers to an `ASN.1` SingleResponse element
266
   * @returns SingleResponse containing the id and revocation status of
267
   * a single certificate.
268
   * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed
269
   * SingleResponse element.
270
   */
271
  static absl::StatusOr<SingleResponse> parseSingleResponse(CBS& cbs);
272
273
  /**
274
   * @param cbs a CBS& that refers to an `ASN.1` CertId element
275
   * @returns CertId containing the information necessary to uniquely identify
276
   * an SSL certificate or an error status if
277
   * `cbs` does not contain a well-formed CertId element.
278
   */
279
  static absl::StatusOr<CertId> parseCertId(CBS& cbs);
280
};
281
282
} // namespace Ocsp
283
} // namespace Tls
284
} // namespace TransportSockets
285
} // namespace Extensions
286
} // namespace Envoy