LCOV - code coverage report
Current view: top level - source/extensions/transport_sockets/tls/ocsp - asn1_utility.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 33 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 2 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 "source/common/common/assert.h"
      11             : 
      12             : #include "absl/types/optional.h"
      13             : #include "absl/types/variant.h"
      14             : #include "openssl/bn.h"
      15             : #include "openssl/bytestring.h"
      16             : #include "openssl/ssl.h"
      17             : 
      18             : namespace Envoy {
      19             : namespace Extensions {
      20             : namespace TransportSockets {
      21             : namespace Tls {
      22             : namespace Ocsp {
      23             : 
      24             : constexpr absl::string_view GENERALIZED_TIME_FORMAT = "%E4Y%m%d%H%M%S";
      25             : 
      26             : /**
      27             :  * The result of parsing an `ASN.1` structure or an `absl::string_view` error
      28             :  * description.
      29             :  */
      30             : template <typename T> using ParsingResult = absl::variant<T, absl::string_view>;
      31             : 
      32             : /**
      33             :  * Construct a `T` from the data contained in the CBS&. Functions
      34             :  * of this type must advance the input CBS& over the element.
      35             :  */
      36             : template <typename T> using Asn1ParsingFunc = std::function<ParsingResult<T>(CBS&)>;
      37             : 
      38             : /**
      39             :  * Utility functions for parsing DER-encoded `ASN.1` objects.
      40             :  * This relies heavily on the 'openssl/bytestring' API which
      41             :  * is BoringSSL's recommended interface for parsing DER-encoded
      42             :  * `ASN.1` data when there is not an existing wrapper.
      43             :  * This is not a complete library for `ASN.1` parsing and primarily
      44             :  * serves as abstractions for the OCSP module, but can be
      45             :  * extended and moved into a general utility to support parsing of
      46             :  * additional `ASN.1` objects.
      47             :  *
      48             :  * Each function adheres to the invariant that given a reference
      49             :  * to a crypto `bytestring` (CBS&), it will parse the specified
      50             :  * `ASN.1` element and advance `cbs` over it.
      51             :  *
      52             :  * An exception is thrown if the `bytestring` is malformed or does
      53             :  * not match the specified `ASN.1` object. The position
      54             :  * of `cbs` is not reliable after an exception is thrown.
      55             :  */
      56             : class Asn1Utility {
      57             : public:
      58             :   ~Asn1Utility() = default;
      59             : 
      60             :   /**
      61             :    * Extracts the full contents of `cbs` as a string.
      62             :    *
      63             :    * @param `cbs` a CBS& that refers to the current document position
      64             :    * @returns absl::string_view containing the contents of `cbs`
      65             :    */
      66             :   static absl::string_view cbsToString(CBS& cbs);
      67             : 
      68             :   /**
      69             :    * Parses all elements of an `ASN.1` SEQUENCE OF. `parse_element` must
      70             :    * advance its input CBS& over the entire element.
      71             :    *
      72             :    * @param cbs a CBS& that refers to an `ASN.1` SEQUENCE OF object
      73             :    * @param parse_element an `Asn1ParsingFunc<T>` used to parse each element
      74             :    * @returns ParsingResult<std::vector<T>> containing the parsed elements of the sequence
      75             :    * or an error string if `cbs` does not point to a well-formed
      76             :    * SEQUENCE OF object.
      77             :    */
      78             :   template <typename T>
      79             :   static ParsingResult<std::vector<T>> parseSequenceOf(CBS& cbs, Asn1ParsingFunc<T> parse_element);
      80             : 
      81             :   /**
      82             :    * Checks if an explicitly tagged optional element of `tag` is present and
      83             :    * if so parses its value with `parse_data`. If the element is not present,
      84             :    * `cbs` is not advanced.
      85             :    *
      86             :    * @param cbs a CBS& that refers to the current document position
      87             :    * @param parse_data an `Asn1ParsingFunc<T>` used to parse the data if present
      88             :    * @return ParsingResult<absl::optional<T>> with a `T` if `cbs` is of the specified tag,
      89             :    * nullopt if the element has a different tag, or an error string if parsing fails.
      90             :    */
      91             :   template <typename T>
      92             :   static ParsingResult<absl::optional<T>> parseOptional(CBS& cbs, Asn1ParsingFunc<T> parse_data,
      93             :                                                         unsigned tag);
      94             : 
      95             :   /**
      96             :    * Returns whether or not an element explicitly tagged with `tag` is present
      97             :    * at `cbs`. If so, `cbs` is advanced over the optional and assigns
      98             :    * `data` to the inner element, if `data` is not nullptr.
      99             :    * If `cbs` does not contain `tag`, `cbs` remains at the same position.
     100             :    *
     101             :    * @param cbs a CBS& that refers to the current document position
     102             :    * @param an unsigned explicit tag indicating an optional value
     103             :    *
     104             :    * @returns ParsingResult<bool> whether `cbs` points to an element tagged with `tag` or
     105             :    * an error string if parsing fails.
     106             :    */
     107             :   static ParsingResult<absl::optional<CBS>> getOptional(CBS& cbs, unsigned tag);
     108             : 
     109             :   /**
     110             :    * @param cbs a CBS& that refers to an `ASN.1` OBJECT IDENTIFIER element
     111             :    * @returns ParsingResult<std::string> the `OID` encoded in `cbs` or an error
     112             :    * string if `cbs` does not point to a well-formed OBJECT IDENTIFIER
     113             :    */
     114             :   static ParsingResult<std::string> parseOid(CBS& cbs);
     115             : 
     116             :   /**
     117             :    * @param cbs a CBS& that refers to an `ASN.1` `GENERALIZEDTIME` element
     118             :    * @returns ParsingResult<Envoy::SystemTime> the UTC timestamp encoded in `cbs`
     119             :    * or an error string if `cbs` does not point to a well-formed
     120             :    * `GENERALIZEDTIME`
     121             :    */
     122             :   static ParsingResult<Envoy::SystemTime> parseGeneralizedTime(CBS& cbs);
     123             : 
     124             :   /**
     125             :    * Parses an `ASN.1` INTEGER type into its hex string representation.
     126             :    * `ASN.1` INTEGER types are arbitrary precision.
     127             :    * If you're SURE the integer fits into a fixed-size int,
     128             :    * use `CBS_get_asn1_*` functions for the given integer type instead.
     129             :    *
     130             :    * @param cbs a CBS& that refers to an `ASN.1` INTEGER element
     131             :    * @returns ParsingResult<std::string> a hex representation of the integer
     132             :    * or an error string if `cbs` does not point to a well-formed INTEGER
     133             :    */
     134             :   static ParsingResult<std::string> parseInteger(CBS& cbs);
     135             : 
     136             :   /**
     137             :    * @param cbs a CBS& that refers to an `ASN.1` `OCTETSTRING` element
     138             :    * @returns ParsingResult<std::vector<uint8_t>> the octets in `cbs` or
     139             :    * an error string if `cbs` does not point to a well-formed `OCTETSTRING`
     140             :    */
     141             :   static ParsingResult<std::vector<uint8_t>> parseOctetString(CBS& cbs);
     142             : 
     143             :   /**
     144             :    * Advance `cbs` over an `ASN.1` value of the class `tag` if that
     145             :    * value is present. Otherwise, `cbs` stays in the same position.
     146             :    *
     147             :    * @param cbs a CBS& that refers to the current document position
     148             :    * @param tag the tag of the value to skip
     149             :    * @returns `ParsingResult<absl::monostate>` a unit type denoting success
     150             :    * or an error string if parsing fails.
     151             :    */
     152             :   static ParsingResult<absl::monostate> skipOptional(CBS& cbs, unsigned tag);
     153             : 
     154             :   /**
     155             :    * Advance `cbs` over an `ASN.1` value of the class `tag`.
     156             :    *
     157             :    * @param cbs a CBS& that refers to the current document position
     158             :    * @param tag the tag of the value to skip
     159             :    * @returns `ParsingResult<absl::monostate>` a unit type denoting success
     160             :    * or an error string if parsing fails.
     161             :    */
     162             :   static ParsingResult<absl::monostate> skip(CBS& cbs, unsigned tag);
     163             : };
     164             : 
     165             : template <typename T>
     166             : ParsingResult<std::vector<T>> Asn1Utility::parseSequenceOf(CBS& cbs,
     167           0 :                                                            Asn1ParsingFunc<T> parse_element) {
     168           0 :   CBS seq_elem;
     169           0 :   std::vector<T> vec;
     170             : 
     171             :   // Initialize seq_elem to first element in sequence.
     172           0 :   if (!CBS_get_asn1(&cbs, &seq_elem, CBS_ASN1_SEQUENCE)) {
     173           0 :     return "Expected sequence of ASN.1 elements.";
     174           0 :   }
     175             : 
     176           0 :   while (CBS_data(&seq_elem) < CBS_data(&cbs)) {
     177             :     // parse_element MUST advance seq_elem
     178           0 :     auto elem_res = parse_element(seq_elem);
     179           0 :     if (absl::holds_alternative<T>(elem_res)) {
     180           0 :       vec.push_back(absl::get<0>(elem_res));
     181           0 :     } else {
     182           0 :       return absl::get<1>(elem_res);
     183           0 :     }
     184           0 :   }
     185             : 
     186           0 :   RELEASE_ASSERT(CBS_data(&cbs) == CBS_data(&seq_elem),
     187           0 :                  "Sequence tag length must match actual length or element parsing would fail");
     188             : 
     189           0 :   return vec;
     190           0 : }
     191             : 
     192             : template <typename T>
     193             : ParsingResult<absl::optional<T>> Asn1Utility::parseOptional(CBS& cbs, Asn1ParsingFunc<T> parse_data,
     194           0 :                                                             unsigned tag) {
     195           0 :   auto maybe_data_res = getOptional(cbs, tag);
     196             : 
     197           0 :   if (absl::holds_alternative<absl::string_view>(maybe_data_res)) {
     198           0 :     return absl::get<absl::string_view>(maybe_data_res);
     199           0 :   }
     200             : 
     201           0 :   auto maybe_data = absl::get<absl::optional<CBS>>(maybe_data_res);
     202           0 :   if (maybe_data) {
     203           0 :     auto res = parse_data(maybe_data.value());
     204           0 :     if (absl::holds_alternative<T>(res)) {
     205           0 :       return absl::get<0>(res);
     206           0 :     }
     207           0 :     return absl::get<1>(res);
     208           0 :   }
     209             : 
     210           0 :   return absl::nullopt;
     211           0 : }
     212             : 
     213             : } // namespace Ocsp
     214             : } // namespace Tls
     215             : } // namespace TransportSockets
     216             : } // namespace Extensions
     217             : } // namespace Envoy

Generated by: LCOV version 1.15