LCOV - code coverage report
Current view: top level - source/extensions/transport_sockets/tls/ocsp - ocsp.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 13 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 7 0.0 %

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

Generated by: LCOV version 1.15