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

#include <algorithm>

#include "certificate_policies.h"

#include "cert_error_params.h"
#include "cert_errors.h"
#include "input.h"
#include "parse_values.h"
#include "parser.h"
#include "tag.h"
#include <openssl/base.h>

namespace bssl {

namespace {

// ---------------------------------------------------------------
// Errors
// ---------------------------------------------------------------

DEFINE_CERT_ERROR_ID(kPolicyQualifiersEmptySequence,
                     "The policy qualifiers SEQUENCE is empty");
DEFINE_CERT_ERROR_ID(kUnknownPolicyQualifierOid,
                     "Unknown policy qualifier OID (not CPS or User Notice)");
DEFINE_CERT_ERROR_ID(kPoliciesEmptySequence, "Policies is an empty SEQUENCE");
DEFINE_CERT_ERROR_ID(kPoliciesDuplicateOid, "Policies contains duplicate OIDs");
DEFINE_CERT_ERROR_ID(kPolicyInformationTrailingData,
                     "PolicyInformation has trailing data");
DEFINE_CERT_ERROR_ID(kFailedParsingPolicyQualifiers,
                     "Failed parsing policy qualifiers");
DEFINE_CERT_ERROR_ID(kMissingQualifier,
                     "PolicyQualifierInfo is missing qualifier");
DEFINE_CERT_ERROR_ID(kPolicyQualifierInfoTrailingData,
                     "PolicyQualifierInfo has trailing data");

// Minimally parse policyQualifiers, storing in |policy_qualifiers| if non-null.
// If a policy qualifier other than User Notice/CPS is present, parsing
// will fail if |restrict_to_known_qualifiers| was set to true.
bool ParsePolicyQualifiers(bool restrict_to_known_qualifiers,
                           der::Parser* policy_qualifiers_sequence_parser,
                           std::vector<PolicyQualifierInfo>* policy_qualifiers,
                           CertErrors* errors) {
  BSSL_CHECK(errors);

  // If it is present, the policyQualifiers sequence should have at least 1
  // element.
  //
  //      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
  //                              PolicyQualifierInfo OPTIONAL }
  if (!policy_qualifiers_sequence_parser->HasMore()) {
    errors->AddError(kPolicyQualifiersEmptySequence);
    return false;
  }
  while (policy_qualifiers_sequence_parser->HasMore()) {
    // PolicyQualifierInfo ::= SEQUENCE {
    der::Parser policy_information_parser;
    if (!policy_qualifiers_sequence_parser->ReadSequence(
            &policy_information_parser)) {
      return false;
    }
    //      policyQualifierId  PolicyQualifierId,
    der::Input qualifier_oid;
    if (!policy_information_parser.ReadTag(der::kOid, &qualifier_oid))
      return false;
    if (restrict_to_known_qualifiers &&
        qualifier_oid != der::Input(kCpsPointerId) &&
        qualifier_oid != der::Input(kUserNoticeId)) {
      errors->AddError(kUnknownPolicyQualifierOid,
                       CreateCertErrorParams1Der("oid", qualifier_oid));
      return false;
    }
    //      qualifier          ANY DEFINED BY policyQualifierId }
    der::Input qualifier_tlv;
    if (!policy_information_parser.ReadRawTLV(&qualifier_tlv)) {
      errors->AddError(kMissingQualifier);
      return false;
    }
    // Should not have trailing data after qualifier.
    if (policy_information_parser.HasMore()) {
      errors->AddError(kPolicyQualifierInfoTrailingData);
      return false;
    }

    if (policy_qualifiers)
      policy_qualifiers->push_back({qualifier_oid, qualifier_tlv});
  }
  return true;
}

// RFC 5280 section 4.2.1.4.  Certificate Policies:
//
// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
//
// PolicyInformation ::= SEQUENCE {
//      policyIdentifier   CertPolicyId,
//      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
//                              PolicyQualifierInfo OPTIONAL }
//
// CertPolicyId ::= OBJECT IDENTIFIER
//
// PolicyQualifierInfo ::= SEQUENCE {
//      policyQualifierId  PolicyQualifierId,
//      qualifier          ANY DEFINED BY policyQualifierId }
//
// PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
//
// Qualifier ::= CHOICE {
//      cPSuri           CPSuri,
//      userNotice       UserNotice }
//
// CPSuri ::= IA5String
//
// UserNotice ::= SEQUENCE {
//      noticeRef        NoticeReference OPTIONAL,
//      explicitText     DisplayText OPTIONAL }
//
// NoticeReference ::= SEQUENCE {
//      organization     DisplayText,
//      noticeNumbers    SEQUENCE OF INTEGER }
//
// DisplayText ::= CHOICE {
//      ia5String        IA5String      (SIZE (1..200)),
//      visibleString    VisibleString  (SIZE (1..200)),
//      bmpString        BMPString      (SIZE (1..200)),
//      utf8String       UTF8String     (SIZE (1..200)) }
bool ParseCertificatePoliciesExtensionImpl(
    const der::Input& extension_value,
    bool fail_parsing_unknown_qualifier_oids,
    std::vector<der::Input>* policy_oids,
    std::vector<PolicyInformation>* policy_informations,
    CertErrors* errors) {
  BSSL_CHECK(policy_oids);
  BSSL_CHECK(errors);
  // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
  der::Parser extension_parser(extension_value);
  der::Parser policies_sequence_parser;
  if (!extension_parser.ReadSequence(&policies_sequence_parser))
    return false;
  // Should not have trailing data after certificatePolicies sequence.
  if (extension_parser.HasMore())
    return false;
  // The certificatePolicies sequence should have at least 1 element.
  if (!policies_sequence_parser.HasMore()) {
    errors->AddError(kPoliciesEmptySequence);
    return false;
  }

  policy_oids->clear();
  if (policy_informations)
    policy_informations->clear();

  while (policies_sequence_parser.HasMore()) {
    // PolicyInformation ::= SEQUENCE {
    der::Parser policy_information_parser;
    if (!policies_sequence_parser.ReadSequence(&policy_information_parser))
      return false;
    //      policyIdentifier   CertPolicyId,
    der::Input policy_oid;
    if (!policy_information_parser.ReadTag(der::kOid, &policy_oid))
      return false;

    policy_oids->push_back(policy_oid);

    std::vector<PolicyQualifierInfo>* policy_qualifiers = nullptr;
    if (policy_informations) {
      policy_informations->emplace_back();
      policy_informations->back().policy_oid = policy_oid;
      policy_qualifiers = &policy_informations->back().policy_qualifiers;
    }

    if (!policy_information_parser.HasMore())
      continue;

    //      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
    //                              PolicyQualifierInfo OPTIONAL }
    der::Parser policy_qualifiers_sequence_parser;
    if (!policy_information_parser.ReadSequence(
            &policy_qualifiers_sequence_parser)) {
      return false;
    }
    // Should not have trailing data after policyQualifiers sequence.
    if (policy_information_parser.HasMore()) {
      errors->AddError(kPolicyInformationTrailingData);
      return false;
    }

    // RFC 5280 section 4.2.1.4: When qualifiers are used with the special
    // policy anyPolicy, they MUST be limited to the qualifiers identified in
    // this section.
    if (!ParsePolicyQualifiers(fail_parsing_unknown_qualifier_oids ||
                                   policy_oid == der::Input(kAnyPolicyOid),
                               &policy_qualifiers_sequence_parser,
                               policy_qualifiers, errors)) {
      errors->AddError(kFailedParsingPolicyQualifiers);
      return false;
    }
  }

  // RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more
  // than once in a certificate policies extension.
  std::sort(policy_oids->begin(), policy_oids->end());
  auto dupe_policy_iter =
      std::adjacent_find(policy_oids->begin(), policy_oids->end());
  if (dupe_policy_iter != policy_oids->end()) {
    errors->AddError(kPoliciesDuplicateOid,
                     CreateCertErrorParams1Der("oid", *dupe_policy_iter));
    return false;
  }

  return true;
}

}  // namespace

PolicyInformation::PolicyInformation() = default;
PolicyInformation::~PolicyInformation() = default;
PolicyInformation::PolicyInformation(const PolicyInformation&) = default;
PolicyInformation::PolicyInformation(PolicyInformation&&) = default;

bool ParseCertificatePoliciesExtension(const der::Input& extension_value,
                                       std::vector<PolicyInformation>* policies,
                                       CertErrors* errors) {
  std::vector<der::Input> unused_policy_oids;
  return ParseCertificatePoliciesExtensionImpl(
      extension_value, /*fail_parsing_unknown_qualifier_oids=*/false,
      &unused_policy_oids, policies, errors);
}

bool ParseCertificatePoliciesExtensionOids(
    const der::Input& extension_value,
    bool fail_parsing_unknown_qualifier_oids,
    std::vector<der::Input>* policy_oids,
    CertErrors* errors) {
  return ParseCertificatePoliciesExtensionImpl(
      extension_value, fail_parsing_unknown_qualifier_oids, policy_oids,
      nullptr, errors);
}

// From RFC 5280:
//
//   PolicyConstraints ::= SEQUENCE {
//        requireExplicitPolicy           [0] SkipCerts OPTIONAL,
//        inhibitPolicyMapping            [1] SkipCerts OPTIONAL }
//
//   SkipCerts ::= INTEGER (0..MAX)
bool ParsePolicyConstraints(const der::Input& policy_constraints_tlv,
                            ParsedPolicyConstraints* out) {
  der::Parser parser(policy_constraints_tlv);

  //   PolicyConstraints ::= SEQUENCE {
  der::Parser sequence_parser;
  if (!parser.ReadSequence(&sequence_parser))
    return false;

  // RFC 5280 prohibits CAs from issuing PolicyConstraints as an empty sequence:
  //
  //   Conforming CAs MUST NOT issue certificates where policy constraints
  //   is an empty sequence.  That is, either the inhibitPolicyMapping field
  //   or the requireExplicitPolicy field MUST be present.  The behavior of
  //   clients that encounter an empty policy constraints field is not
  //   addressed in this profile.
  if (!sequence_parser.HasMore())
    return false;

  std::optional<der::Input> require_value;
  if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(0),
                                       &require_value)) {
    return false;
  }

  if (require_value) {
    uint8_t require_explicit_policy;
    if (!ParseUint8(require_value.value(), &require_explicit_policy)) {
      // TODO(eroman): Surface reason for failure if length was longer than
      // uint8.
      return false;
    }
    out->require_explicit_policy = require_explicit_policy;
  }

  std::optional<der::Input> inhibit_value;
  if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1),
                                       &inhibit_value)) {
    return false;
  }

  if (inhibit_value) {
    uint8_t inhibit_policy_mapping;
    if (!ParseUint8(inhibit_value.value(), &inhibit_policy_mapping)) {
      // TODO(eroman): Surface reason for failure if length was longer than
      // uint8.
      return false;
    }
    out->inhibit_policy_mapping = inhibit_policy_mapping;
  }

  // There should be no remaining data.
  if (sequence_parser.HasMore() || parser.HasMore())
    return false;

  return true;
}

// From RFC 5280:
//
//   InhibitAnyPolicy ::= SkipCerts
//
//   SkipCerts ::= INTEGER (0..MAX)
std::optional<uint8_t> ParseInhibitAnyPolicy(
    const der::Input& inhibit_any_policy_tlv) {
  der::Parser parser(inhibit_any_policy_tlv);
  std::optional<uint8_t> num_certs = std::make_optional<uint8_t>();

  // TODO(eroman): Surface reason for failure if length was longer than uint8.
  if (!parser.ReadUint8(&num_certs.value())) {
    return std::nullopt;
  }

  // There should be no remaining data.
  if (parser.HasMore()) {
    return std::nullopt;
  }

  return num_certs;
}

// From RFC 5280:
//
//   PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
//        issuerDomainPolicy      CertPolicyId,
//        subjectDomainPolicy     CertPolicyId }
bool ParsePolicyMappings(const der::Input& policy_mappings_tlv,
                         std::vector<ParsedPolicyMapping>* mappings) {
  mappings->clear();

  der::Parser parser(policy_mappings_tlv);

  //   PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
  der::Parser sequence_parser;
  if (!parser.ReadSequence(&sequence_parser))
    return false;

  // Must be at least 1 mapping.
  if (!sequence_parser.HasMore())
    return false;

  while (sequence_parser.HasMore()) {
    der::Parser mapping_parser;
    if (!sequence_parser.ReadSequence(&mapping_parser))
      return false;

    ParsedPolicyMapping mapping;
    if (!mapping_parser.ReadTag(der::kOid, &mapping.issuer_domain_policy))
      return false;
    if (!mapping_parser.ReadTag(der::kOid, &mapping.subject_domain_policy))
      return false;

    // There shouldn't be extra unconsumed data.
    if (mapping_parser.HasMore())
      return false;

    mappings->push_back(mapping);
  }

  // There shouldn't be extra unconsumed data.
  if (parser.HasMore())
    return false;

  return true;
}

}  // namespace net
