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

#include "parsed_certificate.h"

#include "cert_errors.h"
#include "certificate_policies.h"
#include "extended_key_usage.h"
#include "name_constraints.h"
#include "signature_algorithm.h"
#include "verify_name_match.h"
#include "parser.h"
#include <openssl/pool.h>

namespace bssl {

namespace {

DEFINE_CERT_ERROR_ID(kFailedParsingCertificate, "Failed parsing Certificate");
DEFINE_CERT_ERROR_ID(kFailedParsingTbsCertificate,
                     "Failed parsing TBSCertificate");
DEFINE_CERT_ERROR_ID(kFailedReadingIssuerOrSubject,
                     "Failed reading issuer or subject");
DEFINE_CERT_ERROR_ID(kFailedNormalizingSubject, "Failed normalizing subject");
DEFINE_CERT_ERROR_ID(kFailedNormalizingIssuer, "Failed normalizing issuer");
DEFINE_CERT_ERROR_ID(kFailedParsingExtensions, "Failed parsing extensions");
DEFINE_CERT_ERROR_ID(kFailedParsingBasicConstraints,
                     "Failed parsing basic constraints");
DEFINE_CERT_ERROR_ID(kFailedParsingKeyUsage, "Failed parsing key usage");
DEFINE_CERT_ERROR_ID(kFailedParsingEku, "Failed parsing extended key usage");
DEFINE_CERT_ERROR_ID(kFailedParsingSubjectAltName,
                     "Failed parsing subjectAltName");
DEFINE_CERT_ERROR_ID(kSubjectAltNameNotCritical,
                     "Empty subject and subjectAltName is not critical");
DEFINE_CERT_ERROR_ID(kFailedParsingNameConstraints,
                     "Failed parsing name constraints");
DEFINE_CERT_ERROR_ID(kFailedParsingAia, "Failed parsing authority info access");
DEFINE_CERT_ERROR_ID(kFailedParsingPolicies,
                     "Failed parsing certificate policies");
DEFINE_CERT_ERROR_ID(kFailedParsingPolicyConstraints,
                     "Failed parsing policy constraints");
DEFINE_CERT_ERROR_ID(kFailedParsingPolicyMappings,
                     "Failed parsing policy mappings");
DEFINE_CERT_ERROR_ID(kFailedParsingInhibitAnyPolicy,
                     "Failed parsing inhibit any policy");
DEFINE_CERT_ERROR_ID(kFailedParsingAuthorityKeyIdentifier,
                     "Failed parsing authority key identifier");
DEFINE_CERT_ERROR_ID(kFailedParsingSubjectKeyIdentifier,
                     "Failed parsing subject key identifier");

[[nodiscard]] bool GetSequenceValue(const der::Input& tlv, der::Input* value) {
  der::Parser parser(tlv);
  return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
}

}  // namespace

bool ParsedCertificate::GetExtension(const der::Input& extension_oid,
                                     ParsedExtension* parsed_extension) const {
  if (!tbs_.extensions_tlv)
    return false;

  auto it = extensions_.find(extension_oid);
  if (it == extensions_.end()) {
    *parsed_extension = ParsedExtension();
    return false;
  }

  *parsed_extension = it->second;
  return true;
}

ParsedCertificate::ParsedCertificate(PrivateConstructor) {}
ParsedCertificate::~ParsedCertificate() = default;

// static
std::shared_ptr<const ParsedCertificate> ParsedCertificate::Create(
    bssl::UniquePtr<CRYPTO_BUFFER> backing_data,
    const ParseCertificateOptions& options,
    CertErrors* errors) {
  // |errors| is an optional parameter, but to keep the code simpler, use a
  // dummy object when one wasn't provided.
  CertErrors unused_errors;
  if (!errors)
    errors = &unused_errors;

  auto result = std::make_shared<ParsedCertificate>(PrivateConstructor{});
  result->cert_data_ = std::move(backing_data);
  result->cert_ = der::Input(CRYPTO_BUFFER_data(result->cert_data_.get()),
                             CRYPTO_BUFFER_len(result->cert_data_.get()));

  if (!ParseCertificate(result->cert_, &result->tbs_certificate_tlv_,
                        &result->signature_algorithm_tlv_,
                        &result->signature_value_, errors)) {
    errors->AddError(kFailedParsingCertificate);
    return nullptr;
  }

  if (!ParseTbsCertificate(result->tbs_certificate_tlv_, options, &result->tbs_,
                           errors)) {
    errors->AddError(kFailedParsingTbsCertificate);
    return nullptr;
  }

  // Attempt to parse the signature algorithm contained in the Certificate.
  result->signature_algorithm_ =
      ParseSignatureAlgorithm(result->signature_algorithm_tlv_);

  der::Input subject_value;
  if (!GetSequenceValue(result->tbs_.subject_tlv, &subject_value)) {
    errors->AddError(kFailedReadingIssuerOrSubject);
    return nullptr;
  }
  if (!NormalizeName(subject_value, &result->normalized_subject_, errors)) {
    errors->AddError(kFailedNormalizingSubject);
    return nullptr;
  }
  der::Input issuer_value;
  if (!GetSequenceValue(result->tbs_.issuer_tlv, &issuer_value)) {
    errors->AddError(kFailedReadingIssuerOrSubject);
    return nullptr;
  }
  if (!NormalizeName(issuer_value, &result->normalized_issuer_, errors)) {
    errors->AddError(kFailedNormalizingIssuer);
    return nullptr;
  }

  // Parse the standard X.509 extensions.
  if (result->tbs_.extensions_tlv) {
    // ParseExtensions() ensures there are no duplicates, and maps the (unique)
    // OID to the extension value.
    if (!ParseExtensions(result->tbs_.extensions_tlv.value(),
                         &result->extensions_)) {
      errors->AddError(kFailedParsingExtensions);
      return nullptr;
    }

    ParsedExtension extension;

    // Basic constraints.
    if (result->GetExtension(der::Input(kBasicConstraintsOid), &extension)) {
      result->has_basic_constraints_ = true;
      if (!ParseBasicConstraints(extension.value,
                                 &result->basic_constraints_)) {
        errors->AddError(kFailedParsingBasicConstraints);
        return nullptr;
      }
    }

    // Key Usage.
    if (result->GetExtension(der::Input(kKeyUsageOid), &extension)) {
      result->has_key_usage_ = true;
      if (!ParseKeyUsage(extension.value, &result->key_usage_)) {
        errors->AddError(kFailedParsingKeyUsage);
        return nullptr;
      }
    }

    // Extended Key Usage.
    if (result->GetExtension(der::Input(kExtKeyUsageOid), &extension)) {
      result->has_extended_key_usage_ = true;
      if (!ParseEKUExtension(extension.value, &result->extended_key_usage_)) {
        errors->AddError(kFailedParsingEku);
        return nullptr;
      }
    }

    // Subject alternative name.
    if (result->GetExtension(der::Input(kSubjectAltNameOid),
                             &result->subject_alt_names_extension_)) {
      // RFC 5280 section 4.2.1.6:
      // SubjectAltName ::= GeneralNames
      result->subject_alt_names_ = GeneralNames::Create(
          result->subject_alt_names_extension_.value, errors);
      if (!result->subject_alt_names_) {
        errors->AddError(kFailedParsingSubjectAltName);
        return nullptr;
      }
      // RFC 5280 section 4.1.2.6:
      // If subject naming information is present only in the subjectAltName
      // extension (e.g., a key bound only to an email address or URI), then the
      // subject name MUST be an empty sequence and the subjectAltName extension
      // MUST be critical.
      if (subject_value.Length() == 0 &&
          !result->subject_alt_names_extension_.critical) {
        errors->AddError(kSubjectAltNameNotCritical);
        return nullptr;
      }
    }

    // Name constraints.
    if (result->GetExtension(der::Input(kNameConstraintsOid), &extension)) {
      result->name_constraints_ =
          NameConstraints::Create(extension.value, extension.critical, errors);
      if (!result->name_constraints_) {
        errors->AddError(kFailedParsingNameConstraints);
        return nullptr;
      }
    }

    // Authority information access.
    if (result->GetExtension(der::Input(kAuthorityInfoAccessOid),
                             &result->authority_info_access_extension_)) {
      result->has_authority_info_access_ = true;
      if (!ParseAuthorityInfoAccessURIs(
              result->authority_info_access_extension_.value,
              &result->ca_issuers_uris_, &result->ocsp_uris_)) {
        errors->AddError(kFailedParsingAia);
        return nullptr;
      }
    }

    // Policies.
    if (result->GetExtension(der::Input(kCertificatePoliciesOid), &extension)) {
      result->has_policy_oids_ = true;
      if (!ParseCertificatePoliciesExtensionOids(
              extension.value, false /*fail_parsing_unknown_qualifier_oids*/,
              &result->policy_oids_, errors)) {
        errors->AddError(kFailedParsingPolicies);
        return nullptr;
      }
    }

    // Policy constraints.
    if (result->GetExtension(der::Input(kPolicyConstraintsOid), &extension)) {
      result->has_policy_constraints_ = true;
      if (!ParsePolicyConstraints(extension.value,
                                  &result->policy_constraints_)) {
        errors->AddError(kFailedParsingPolicyConstraints);
        return nullptr;
      }
    }

    // Policy mappings.
    if (result->GetExtension(der::Input(kPolicyMappingsOid), &extension)) {
      result->has_policy_mappings_ = true;
      if (!ParsePolicyMappings(extension.value, &result->policy_mappings_)) {
        errors->AddError(kFailedParsingPolicyMappings);
        return nullptr;
      }
    }

    // Inhibit Any Policy.
    if (result->GetExtension(der::Input(kInhibitAnyPolicyOid), &extension)) {
      result->has_inhibit_any_policy_ = true;
      if (!ParseInhibitAnyPolicy(extension.value,
                                 &result->inhibit_any_policy_)) {
        errors->AddError(kFailedParsingInhibitAnyPolicy);
        return nullptr;
      }
    }

    // Subject Key Identifier.
    if (result->GetExtension(der::Input(kSubjectKeyIdentifierOid),
                             &extension)) {
      result->subject_key_identifier_ = std::make_optional<der::Input>();
      if (!ParseSubjectKeyIdentifier(
              extension.value, &result->subject_key_identifier_.value())) {
        errors->AddError(kFailedParsingSubjectKeyIdentifier);
        return nullptr;
      }
    }

    // Authority Key Identifier.
    if (result->GetExtension(der::Input(kAuthorityKeyIdentifierOid),
                             &extension)) {
      result->authority_key_identifier_ =
          std::make_optional<ParsedAuthorityKeyIdentifier>();
      if (!ParseAuthorityKeyIdentifier(
              extension.value, &result->authority_key_identifier_.value())) {
        errors->AddError(kFailedParsingAuthorityKeyIdentifier);
        return nullptr;
      }
    }
  }

  return result;
}

// static
bool ParsedCertificate::CreateAndAddToVector(
    bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
    const ParseCertificateOptions& options,
    std::vector<std::shared_ptr<const bssl::ParsedCertificate>>* chain,
    CertErrors* errors) {
  std::shared_ptr<const ParsedCertificate> cert(
      Create(std::move(cert_data), options, errors));
  if (!cert)
    return false;
  chain->push_back(std::move(cert));
  return true;
}

}  // namespace net
