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

          Line data    Source code
       1             : #include "source/extensions/transport_sockets/tls/ocsp/ocsp.h"
       2             : 
       3             : #include "source/common/common/utility.h"
       4             : #include "source/extensions/transport_sockets/tls/ocsp/asn1_utility.h"
       5             : #include "source/extensions/transport_sockets/tls/utility.h"
       6             : 
       7             : namespace Envoy {
       8             : namespace Extensions {
       9             : namespace TransportSockets {
      10             : namespace Tls {
      11             : namespace Ocsp {
      12             : 
      13             : namespace CertUtility = Envoy::Extensions::TransportSockets::Tls::Utility;
      14             : 
      15             : namespace {
      16             : 
      17           0 : template <typename T> T unwrap(ParsingResult<T> res) {
      18           0 :   if (absl::holds_alternative<T>(res)) {
      19           0 :     return absl::get<0>(res);
      20           0 :   }
      21             : 
      22           0 :   throwEnvoyExceptionOrPanic(std::string(absl::get<1>(res)));
      23           0 : }
      24             : 
      25           0 : unsigned parseTag(CBS& cbs) {
      26           0 :   unsigned tag;
      27           0 :   if (!CBS_get_any_asn1_element(&cbs, nullptr, &tag, nullptr)) {
      28           0 :     throwEnvoyExceptionOrPanic("Failed to parse ASN.1 element tag");
      29           0 :   }
      30           0 :   return tag;
      31           0 : }
      32             : 
      33           0 : std::unique_ptr<OcspResponse> readDerEncodedOcspResponse(const std::vector<uint8_t>& der) {
      34           0 :   CBS cbs;
      35           0 :   CBS_init(&cbs, der.data(), der.size());
      36             : 
      37           0 :   auto resp = Asn1OcspUtility::parseOcspResponse(cbs);
      38           0 :   if (CBS_len(&cbs) != 0) {
      39           0 :     throwEnvoyExceptionOrPanic("Data contained more than a single OCSP response");
      40           0 :   }
      41             : 
      42           0 :   return resp;
      43           0 : }
      44             : 
      45           0 : void skipResponderId(CBS& cbs) {
      46             :   // ResponderID ::= CHOICE {
      47             :   //    byName               [1] Name,
      48             :   //    byKey                [2] KeyHash
      49             :   // }
      50             :   //
      51             :   // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
      52             :   //    (excluding the tag and length fields)
      53             : 
      54           0 :   if (unwrap(Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1)) ||
      55           0 :       unwrap(Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2))) {
      56           0 :     return;
      57           0 :   }
      58             : 
      59           0 :   throwEnvoyExceptionOrPanic(absl::StrCat("Unknown choice for Responder ID: ", parseTag(cbs)));
      60           0 : }
      61             : 
      62           0 : void skipCertStatus(CBS& cbs) {
      63             :   // CertStatus ::= CHOICE {
      64             :   //  good                [0] IMPLICIT NULL,
      65             :   //  revoked             [1] IMPLICIT RevokedInfo,
      66             :   //  unknown             [2] IMPLICIT UnknownInfo
      67             :   // }
      68           0 :   if (!(unwrap(Asn1Utility::getOptional(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 0)) ||
      69           0 :         unwrap(
      70           0 :             Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1)) ||
      71           0 :         unwrap(Asn1Utility::getOptional(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 2)))) {
      72           0 :     throwEnvoyExceptionOrPanic(absl::StrCat("Unknown OcspCertStatus tag: ", parseTag(cbs)));
      73           0 :   }
      74           0 : }
      75             : 
      76             : } // namespace
      77             : 
      78             : OcspResponse::OcspResponse(OcspResponseStatus status, ResponsePtr response)
      79           0 :     : status_(status), response_(std::move(response)) {}
      80             : 
      81           0 : BasicOcspResponse::BasicOcspResponse(ResponseData data) : data_(data) {}
      82             : 
      83             : ResponseData::ResponseData(std::vector<SingleResponse> single_responses)
      84           0 :     : single_responses_(std::move(single_responses)) {}
      85             : 
      86             : SingleResponse::SingleResponse(CertId cert_id, Envoy::SystemTime this_update,
      87             :                                absl::optional<Envoy::SystemTime> next_update)
      88           0 :     : cert_id_(cert_id), this_update_(this_update), next_update_(next_update) {}
      89             : 
      90           0 : CertId::CertId(std::string serial_number) : serial_number_(serial_number) {}
      91             : 
      92             : OcspResponseWrapper::OcspResponseWrapper(std::vector<uint8_t> der_response, TimeSource& time_source)
      93             :     : raw_bytes_(std::move(der_response)), response_(readDerEncodedOcspResponse(raw_bytes_)),
      94           0 :       time_source_(time_source) {
      95             : 
      96           0 :   if (response_->status_ != OcspResponseStatus::Successful) {
      97           0 :     throwEnvoyExceptionOrPanic("OCSP response was unsuccessful");
      98           0 :   }
      99             : 
     100           0 :   if (response_->response_ == nullptr) {
     101           0 :     throwEnvoyExceptionOrPanic("OCSP response has no body");
     102           0 :   }
     103             : 
     104             :   // We only permit a 1:1 of certificate to response.
     105           0 :   if (response_->response_->getNumCerts() != 1) {
     106           0 :     throwEnvoyExceptionOrPanic("OCSP Response must be for one certificate only");
     107           0 :   }
     108             : 
     109           0 :   auto& this_update = response_->response_->getThisUpdate();
     110           0 :   if (time_source_.systemTime() < this_update) {
     111           0 :     std::string time_format(GENERALIZED_TIME_FORMAT);
     112           0 :     DateFormatter formatter(time_format);
     113           0 :     ENVOY_LOG_MISC(warn, "OCSP Response thisUpdate field is set in the future: {}",
     114           0 :                    formatter.fromTime(this_update));
     115           0 :   }
     116           0 : }
     117             : 
     118             : // We use just the serial number to uniquely identify a certificate.
     119             : // Though different issuers could produce certificates with the same serial
     120             : // number, this is check is to prevent operator error and a collision in this
     121             : // case is unlikely.
     122           0 : bool OcspResponseWrapper::matchesCertificate(X509& cert) const {
     123           0 :   std::string cert_serial_number = CertUtility::getSerialNumberFromCertificate(cert);
     124           0 :   std::string resp_cert_serial_number = response_->response_->getCertSerialNumber();
     125           0 :   return resp_cert_serial_number == cert_serial_number;
     126           0 : }
     127             : 
     128           0 : bool OcspResponseWrapper::isExpired() {
     129           0 :   auto& next_update = response_->response_->getNextUpdate();
     130           0 :   return next_update == absl::nullopt || next_update < time_source_.systemTime();
     131           0 : }
     132             : 
     133           0 : uint64_t OcspResponseWrapper::secondsUntilExpiration() const {
     134           0 :   auto& next_update = response_->response_->getNextUpdate();
     135           0 :   auto now = time_source_.systemTime();
     136           0 :   if (!next_update || next_update.value() <= now) {
     137           0 :     return 0;
     138           0 :   }
     139           0 :   return std::chrono::duration_cast<std::chrono::seconds>(next_update.value() - now).count();
     140           0 : }
     141             : 
     142           0 : Envoy::SystemTime OcspResponseWrapper::getThisUpdate() const {
     143           0 :   return response_->response_->getThisUpdate();
     144           0 : }
     145             : 
     146           0 : Envoy::SystemTime OcspResponseWrapper::getNextUpdate() const {
     147           0 :   auto& next_update = response_->response_->getNextUpdate();
     148           0 :   if (next_update) {
     149           0 :     return *next_update;
     150           0 :   }
     151             : 
     152           0 :   return time_source_.systemTime();
     153           0 : }
     154             : 
     155           0 : std::unique_ptr<OcspResponse> Asn1OcspUtility::parseOcspResponse(CBS& cbs) {
     156             :   // OCSPResponse ::= SEQUENCE {
     157             :   //    responseStatus         OCSPResponseStatus,
     158             :   //    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL
     159             :   // }
     160             : 
     161           0 :   CBS elem;
     162           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     163           0 :     throwEnvoyExceptionOrPanic("OCSP Response is not a well-formed ASN.1 SEQUENCE");
     164           0 :   }
     165             : 
     166           0 :   OcspResponseStatus status = Asn1OcspUtility::parseResponseStatus(elem);
     167           0 :   auto maybe_bytes =
     168           0 :       unwrap(Asn1Utility::getOptional(elem, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0));
     169           0 :   ResponsePtr resp = nullptr;
     170           0 :   if (maybe_bytes) {
     171           0 :     resp = Asn1OcspUtility::parseResponseBytes(maybe_bytes.value());
     172           0 :   }
     173             : 
     174           0 :   return std::make_unique<OcspResponse>(status, std::move(resp));
     175           0 : }
     176             : 
     177           0 : OcspResponseStatus Asn1OcspUtility::parseResponseStatus(CBS& cbs) {
     178             :   // OCSPResponseStatus ::= ENUMERATED {
     179             :   //    successful            (0),  -- Response has valid confirmations
     180             :   //    malformedRequest      (1),  -- Illegal confirmation request
     181             :   //    internalError         (2),  -- Internal error in issuer
     182             :   //    tryLater              (3),  -- Try again later
     183             :   //                                -- (4) is not used
     184             :   //    sigRequired           (5),  -- Must sign the request
     185             :   //    unauthorized          (6)   -- Request unauthorized
     186             :   // }
     187           0 :   CBS status;
     188           0 :   if (!CBS_get_asn1(&cbs, &status, CBS_ASN1_ENUMERATED)) {
     189           0 :     throwEnvoyExceptionOrPanic("OCSP ResponseStatus is not a well-formed ASN.1 ENUMERATED");
     190           0 :   }
     191             : 
     192           0 :   auto status_ordinal = *CBS_data(&status);
     193           0 :   switch (status_ordinal) {
     194           0 :   case 0:
     195           0 :     return OcspResponseStatus::Successful;
     196           0 :   case 1:
     197           0 :     return OcspResponseStatus::MalformedRequest;
     198           0 :   case 2:
     199           0 :     return OcspResponseStatus::InternalError;
     200           0 :   case 3:
     201           0 :     return OcspResponseStatus::TryLater;
     202           0 :   case 5:
     203           0 :     return OcspResponseStatus::SigRequired;
     204           0 :   case 6:
     205           0 :     return OcspResponseStatus::Unauthorized;
     206           0 :   default:
     207           0 :     throwEnvoyExceptionOrPanic(
     208           0 :         absl::StrCat("Unknown OCSP Response Status variant: ", status_ordinal));
     209           0 :   }
     210           0 : }
     211             : 
     212           0 : ResponsePtr Asn1OcspUtility::parseResponseBytes(CBS& cbs) {
     213             :   // ResponseBytes ::=  SEQUENCE {
     214             :   //     responseType        RESPONSE.
     215             :   //                             &id ({ResponseSet}),
     216             :   //     response            OCTET STRING (CONTAINING RESPONSE.
     217             :   //                             &Type({ResponseSet}{@responseType}))
     218             :   // }
     219           0 :   CBS elem, response;
     220           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     221           0 :     throwEnvoyExceptionOrPanic("OCSP ResponseBytes is not a well-formed SEQUENCE");
     222           0 :   }
     223             : 
     224           0 :   auto oid_str = unwrap(Asn1Utility::parseOid(elem));
     225           0 :   if (!CBS_get_asn1(&elem, &response, CBS_ASN1_OCTETSTRING)) {
     226           0 :     throwEnvoyExceptionOrPanic("Expected ASN.1 OCTETSTRING for response");
     227           0 :   }
     228             : 
     229           0 :   if (oid_str == BasicOcspResponse::OID) {
     230           0 :     return Asn1OcspUtility::parseBasicOcspResponse(response);
     231           0 :   }
     232           0 :   throwEnvoyExceptionOrPanic(absl::StrCat("Unknown OCSP Response type with OID: ", oid_str));
     233           0 : }
     234             : 
     235           0 : std::unique_ptr<BasicOcspResponse> Asn1OcspUtility::parseBasicOcspResponse(CBS& cbs) {
     236             :   // BasicOCSPResponse       ::= SEQUENCE {
     237             :   //    tbsResponseData      ResponseData,
     238             :   //    signatureAlgorithm   AlgorithmIdentifier{SIGNATURE-ALGORITHM,
     239             :   //                             {`sa-dsaWithSHA1` | `sa-rsaWithSHA1` |
     240             :   //                                  `sa-rsaWithMD5` | `sa-rsaWithMD2`, ...}},
     241             :   //    signature            BIT STRING,
     242             :   //    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
     243             :   // }
     244           0 :   CBS elem;
     245           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     246           0 :     throwEnvoyExceptionOrPanic("OCSP BasicOCSPResponse is not a wellf-formed ASN.1 SEQUENCE");
     247           0 :   }
     248           0 :   auto response_data = Asn1OcspUtility::parseResponseData(elem);
     249             :   // The `signatureAlgorithm` and `signature` are ignored because OCSP
     250             :   // responses are expected to be delivered from a reliable source.
     251             :   // Optional additional certs are ignored.
     252             : 
     253           0 :   return std::make_unique<BasicOcspResponse>(response_data);
     254           0 : }
     255             : 
     256           0 : ResponseData Asn1OcspUtility::parseResponseData(CBS& cbs) {
     257             :   // ResponseData ::= SEQUENCE {
     258             :   //    version              [0] EXPLICIT Version DEFAULT v1,
     259             :   //    responderID              ResponderID,
     260             :   //    producedAt               GeneralizedTime,
     261             :   //    responses                SEQUENCE OF SingleResponse,
     262             :   //    responseExtensions   [1] EXPLICIT Extensions OPTIONAL
     263             :   // }
     264           0 :   CBS elem;
     265           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     266           0 :     throwEnvoyExceptionOrPanic("OCSP ResponseData is not a well-formed ASN.1 SEQUENCE");
     267           0 :   }
     268             : 
     269             :   // only support v1, the value of v1 is 0x00
     270           0 :   auto version_cbs =
     271           0 :       unwrap(Asn1Utility::getOptional(elem, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
     272           0 :   if (version_cbs.has_value()) {
     273           0 :     auto version = unwrap(Asn1Utility::parseInteger(*version_cbs));
     274           0 :     if (version != "00") {
     275           0 :       throwEnvoyExceptionOrPanic(
     276           0 :           fmt::format("OCSP ResponseData version 0x{} is not supported", version));
     277           0 :     }
     278           0 :   }
     279             : 
     280           0 :   skipResponderId(elem);
     281           0 :   unwrap(Asn1Utility::skip(elem, CBS_ASN1_GENERALIZEDTIME));
     282           0 :   auto responses = unwrap(Asn1Utility::parseSequenceOf<SingleResponse>(
     283           0 :       elem, [](CBS& cbs) -> ParsingResult<SingleResponse> { return {parseSingleResponse(cbs)}; }));
     284             :   // Extensions currently ignored.
     285             : 
     286           0 :   return {std::move(responses)};
     287           0 : }
     288             : 
     289           0 : SingleResponse Asn1OcspUtility::parseSingleResponse(CBS& cbs) {
     290             :   // SingleResponse ::= SEQUENCE {
     291             :   //    certID                  CertID,
     292             :   //    certStatus              CertStatus,
     293             :   //    thisUpdate              GeneralizedTime,
     294             :   //    nextUpdate          [0] EXPLICIT GeneralizedTime OPTIONAL,
     295             :   //    singleExtensions    [1] EXPLICIT Extensions OPTIONAL
     296             :   // }
     297           0 :   CBS elem;
     298           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     299           0 :     throwEnvoyExceptionOrPanic("OCSP SingleResponse is not a well-formed ASN.1 SEQUENCE");
     300           0 :   }
     301             : 
     302           0 :   auto cert_id = Asn1OcspUtility::parseCertId(elem);
     303           0 :   skipCertStatus(elem);
     304           0 :   auto this_update = unwrap(Asn1Utility::parseGeneralizedTime(elem));
     305           0 :   auto next_update = unwrap(Asn1Utility::parseOptional<Envoy::SystemTime>(
     306           0 :       elem, Asn1Utility::parseGeneralizedTime,
     307           0 :       CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0));
     308             :   // Extensions currently ignored.
     309             : 
     310           0 :   return {cert_id, this_update, next_update};
     311           0 : }
     312             : 
     313           0 : CertId Asn1OcspUtility::parseCertId(CBS& cbs) {
     314             :   // CertID ::= SEQUENCE {
     315             :   //    hashAlgorithm       AlgorithmIdentifier,
     316             :   //    issuerNameHash      OCTET STRING, -- Hash of issuer's `DN`
     317             :   //    issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
     318             :   //    serialNumber        CertificateSerialNumber
     319             :   // }
     320           0 :   CBS elem;
     321           0 :   if (!CBS_get_asn1(&cbs, &elem, CBS_ASN1_SEQUENCE)) {
     322           0 :     throwEnvoyExceptionOrPanic("OCSP CertID is not a well-formed ASN.1 SEQUENCE");
     323           0 :   }
     324             : 
     325           0 :   unwrap(Asn1Utility::skip(elem, CBS_ASN1_SEQUENCE));
     326           0 :   unwrap(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING));
     327           0 :   unwrap(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING));
     328           0 :   auto serial_number = unwrap(Asn1Utility::parseInteger(elem));
     329             : 
     330           0 :   return {serial_number};
     331           0 : }
     332             : 
     333             : } // namespace Ocsp
     334             : } // namespace Tls
     335             : } // namespace TransportSockets
     336             : } // namespace Extensions
     337             : } // namespace Envoy

Generated by: LCOV version 1.15