// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BSSL_PKI_CRL_H_
#define BSSL_PKI_CRL_H_

#include <optional>

#include <openssl/base.h>

#include "general_names.h"
#include "input.h"
#include "parse_values.h"
#include "parsed_certificate.h"

namespace bssl {

struct ParsedCrlTbsCertList;
struct ParsedDistributionPoint;

// TODO(https://crbug.com/749276): This is the same enum with the same meaning
// as OCSPRevocationStatus, maybe they should be merged?
enum class CRLRevocationStatus {
  GOOD = 0,
  REVOKED = 1,
  UNKNOWN = 2,
  MAX_VALUE = UNKNOWN
};

// Parses a DER-encoded CRL "CertificateList" as specified by RFC 5280 Section
// 5.1. Returns true on success and sets the results in the |out_*| parameters.
// The contents of the output data is not validated.
//
// Note that on success the out parameters alias data from the input |crl_tlv|.
// Hence the output values are only valid as long as |crl_tlv| remains valid.
//
// On failure the out parameters have an undefined state. Some of them may have
// been updated during parsing, whereas others may not have been changed.
//
//    CertificateList  ::=  SEQUENCE  {
//         tbsCertList          TBSCertList,
//         signatureAlgorithm   AlgorithmIdentifier,
//         signatureValue       BIT STRING  }
[[nodiscard]] OPENSSL_EXPORT bool ParseCrlCertificateList(
    der::Input crl_tlv, der::Input *out_tbs_cert_list_tlv,
    der::Input *out_signature_algorithm_tlv,
    der::BitString *out_signature_value);

// Parses a DER-encoded "TBSCertList" as specified by RFC 5280 Section 5.1.
// Returns true on success and sets the results in |out|.
//
// Note that on success |out| aliases data from the input |tbs_tlv|.
// Hence the fields of the ParsedCrlTbsCertList are only valid as long as
// |tbs_tlv| remains valid.
//
// On failure |out| has an undefined state. Some of its fields may have been
// updated during parsing, whereas others may not have been changed.
//
// Refer to the per-field documentation of ParsedCrlTbsCertList for details on
// what validity checks parsing performs.
//
//    TBSCertList  ::=  SEQUENCE  {
//         version                 Version OPTIONAL,
//                                      -- if present, MUST be v2
//         signature               AlgorithmIdentifier,
//         issuer                  Name,
//         thisUpdate              Time,
//         nextUpdate              Time OPTIONAL,
//         revokedCertificates     SEQUENCE OF SEQUENCE  {
//              userCertificate         CertificateSerialNumber,
//              revocationDate          Time,
//              crlEntryExtensions      Extensions OPTIONAL
//                                       -- if present, version MUST be v2
//                                   }  OPTIONAL,
//         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
//                                       -- if present, version MUST be v2
//                                   }
[[nodiscard]] OPENSSL_EXPORT bool ParseCrlTbsCertList(
    der::Input tbs_tlv, ParsedCrlTbsCertList *out);

// Represents a CRL "Version" from RFC 5280. TBSCertList reuses the same
// Version definition from TBSCertificate, however only v1(not present) and
// v2(1) are valid values, so a unique enum is used to avoid confusion.
enum class CrlVersion {
  V1,
  V2,
};

// Corresponds with "TBSCertList" from RFC 5280 Section 5.1:
struct OPENSSL_EXPORT ParsedCrlTbsCertList {
  ParsedCrlTbsCertList();
  ~ParsedCrlTbsCertList();

  //         version                 Version OPTIONAL,
  //                                      -- if present, MUST be v2
  //
  // Parsing guarantees that the version is one of v1 or v2.
  CrlVersion version = CrlVersion::V1;

  //         signature               AlgorithmIdentifier,
  //
  // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
  // guarantees are made regarding the value of this SEQUENCE.
  //
  // This can be further parsed using SignatureValue::Create().
  der::Input signature_algorithm_tlv;

  //         issuer               Name,
  //
  // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
  // guarantees are made regarding the value of this SEQUENCE.
  der::Input issuer_tlv;

  //         thisUpdate              Time,
  //         nextUpdate              Time OPTIONAL,
  //
  // Parsing guarantees that thisUpdate and nextUpdate(if present) are valid
  // DER-encoded dates, however it DOES NOT guarantee anything about their
  // values. For instance notAfter could be before notBefore, or the dates
  // could indicate an expired CRL.
  der::GeneralizedTime this_update;
  std::optional<der::GeneralizedTime> next_update;

  //         revokedCertificates     SEQUENCE OF SEQUENCE  {
  //              userCertificate         CertificateSerialNumber,
  //              revocationDate          Time,
  //              crlEntryExtensions      Extensions OPTIONAL
  //                                       -- if present, version MUST be v2
  //                                   }  OPTIONAL,
  //
  // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
  // guarantees are made regarding the value of this SEQUENCE.
  std::optional<der::Input> revoked_certificates_tlv;

  //         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
  //                                       -- if present, version MUST be v2
  //
  // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
  // guarantees are made regarding the value of this SEQUENCE. (Note that the
  // EXPLICIT outer tag is stripped.)
  //
  // Parsing guarantees that if extensions is present the version is v2.
  std::optional<der::Input> crl_extensions_tlv;
};

// Represents the IssuingDistributionPoint certificate type constraints:
enum class ContainedCertsType {
  // Neither onlyContainsUserCerts or onlyContainsCACerts was present.
  ANY_CERTS,
  // onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
  USER_CERTS,
  // onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
  CA_CERTS,
};

// Parses a DER-encoded IssuingDistributionPoint extension value.
// Returns true on success and sets the results in the |out_*| parameters.
//
// If the IssuingDistributionPoint contains a distributionPoint fullName field,
// |out_distribution_point_names| will contain the parsed representation.
// If the distributionPoint type is nameRelativeToCRLIssuer, parsing will fail.
//
// |out_only_contains_cert_type| will contain the logical representation of the
// onlyContainsUserCerts and onlyContainsCACerts fields (or their absence).
//
// indirectCRL and onlyContainsAttributeCerts are not supported and parsing will
// fail if they are present.
//
// Note that on success |out_distribution_point_names| aliases data from the
// input |extension_value|.
//
// On failure the |out_*| parameters have undefined state.
//
// IssuingDistributionPoint ::= SEQUENCE {
//     distributionPoint          [0] DistributionPointName OPTIONAL,
//     onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
//     onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
//     onlySomeReasons            [3] ReasonFlags OPTIONAL,
//     indirectCRL                [4] BOOLEAN DEFAULT FALSE,
//     onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
[[nodiscard]] OPENSSL_EXPORT bool ParseIssuingDistributionPoint(
    der::Input extension_value,
    std::unique_ptr<GeneralNames> *out_distribution_point_names,
    ContainedCertsType *out_only_contains_cert_type);

OPENSSL_EXPORT CRLRevocationStatus
GetCRLStatusForCert(der::Input cert_serial, CrlVersion crl_version,
                    const std::optional<der::Input> &revoked_certificates_tlv);

// Checks the revocation status of the certificate |cert| by using the
// DER-encoded |raw_crl|. |cert| must already have passed certificate path
// validation.
//
// Returns GOOD if the CRL indicates the certificate is not revoked,
// REVOKED if it indicates it is revoked, or UNKNOWN for all other cases.
//
//  * |raw_crl|: A DER encoded CRL CertificateList.
//  * |valid_chain|: The validated certificate chain containing the target cert.
//  * |target_cert_index|: The index into |valid_chain| of the certificate being
//        checked for revocation.
//  * |cert_dp|: The distribution point from the target certificate's CRL
//        distribution points extension that |raw_crl| corresponds to. If
//        |raw_crl| was not specified in a distribution point, the caller must
//        synthesize a ParsedDistributionPoint object as specified by RFC 5280
//        6.3.3.
//  * |verify_time_epoch_seconds|: The time as the difference in seconds from
//        the POSIX epoch to use when checking revocation status.
//  * |max_age_seconds|: If present, the maximum age in seconds for a CRL,
//        implemented as time since the |thisUpdate| field in the CRL
//        TBSCertList. Responses older than |max_age_seconds| will be
//        considered invalid.
[[nodiscard]] OPENSSL_EXPORT CRLRevocationStatus CheckCRL(
    std::string_view raw_crl, const ParsedCertificateList &valid_chain,
    size_t target_cert_index, const ParsedDistributionPoint &cert_dp,
    int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds);

}  // namespace bssl

#endif  // BSSL_PKI_CRL_H_
