// Copyright 2016 The Chromium Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "trust_store.h"

#include <cassert>
#include <cstring>
#include <optional>

#include <openssl/base.h>
#include <openssl/bytestring.h>
#include <openssl/mem.h>
#include <openssl/pool.h>

#include "merkle_tree.h"
#include "parse_certificate.h"
#include "parsed_certificate.h"
#include "string_util.h"

BSSL_NAMESPACE_BEGIN

namespace {

constexpr char kUnspecifiedStr[] = "UNSPECIFIED";
constexpr char kDistrustedStr[] = "DISTRUSTED";
constexpr char kTrustedAnchorStr[] = "TRUSTED_ANCHOR";
constexpr char kTrustedAnchorOrLeafStr[] = "TRUSTED_ANCHOR_OR_LEAF";
constexpr char kTrustedLeafStr[] = "TRUSTED_LEAF";

constexpr char kEnforceAnchorExpiry[] = "enforce_anchor_expiry";
constexpr char kEnforceAnchorConstraints[] = "enforce_anchor_constraints";
constexpr char kRequireAnchorBasicConstraints[] =
    "require_anchor_basic_constraints";
constexpr char kRequireLeafSelfsigned[] = "require_leaf_selfsigned";

}  // namespace

bool CertificateTrust::IsTrustAnchor() const {
  switch (type) {
    case CertificateTrustType::DISTRUSTED:
    case CertificateTrustType::UNSPECIFIED:
    case CertificateTrustType::TRUSTED_LEAF:
      return false;
    case CertificateTrustType::TRUSTED_ANCHOR:
    case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
      return true;
  }

  assert(0);  // NOTREACHED
  return false;
}

bool CertificateTrust::IsTrustLeaf() const {
  switch (type) {
    case CertificateTrustType::TRUSTED_LEAF:
    case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
      return true;
    case CertificateTrustType::DISTRUSTED:
    case CertificateTrustType::UNSPECIFIED:
    case CertificateTrustType::TRUSTED_ANCHOR:
      return false;
  }

  assert(0);  // NOTREACHED
  return false;
}

bool CertificateTrust::IsDistrusted() const {
  switch (type) {
    case CertificateTrustType::DISTRUSTED:
      return true;
    case CertificateTrustType::UNSPECIFIED:
    case CertificateTrustType::TRUSTED_ANCHOR:
    case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
    case CertificateTrustType::TRUSTED_LEAF:
      return false;
  }

  assert(0);  // NOTREACHED
  return false;
}

bool CertificateTrust::HasUnspecifiedTrust() const {
  switch (type) {
    case CertificateTrustType::UNSPECIFIED:
      return true;
    case CertificateTrustType::DISTRUSTED:
    case CertificateTrustType::TRUSTED_ANCHOR:
    case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
    case CertificateTrustType::TRUSTED_LEAF:
      return false;
  }

  assert(0);  // NOTREACHED
  return true;
}

std::string CertificateTrust::ToDebugString() const {
  std::string result;
  switch (type) {
    case CertificateTrustType::UNSPECIFIED:
      result = kUnspecifiedStr;
      break;
    case CertificateTrustType::DISTRUSTED:
      result = kDistrustedStr;
      break;
    case CertificateTrustType::TRUSTED_ANCHOR:
      result = kTrustedAnchorStr;
      break;
    case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
      result = kTrustedAnchorOrLeafStr;
      break;
    case CertificateTrustType::TRUSTED_LEAF:
      result = kTrustedLeafStr;
      break;
  }
  if (enforce_anchor_expiry) {
    result += '+';
    result += kEnforceAnchorExpiry;
  }
  if (enforce_anchor_constraints) {
    result += '+';
    result += kEnforceAnchorConstraints;
  }
  if (require_anchor_basic_constraints) {
    result += '+';
    result += kRequireAnchorBasicConstraints;
  }
  if (require_leaf_selfsigned) {
    result += '+';
    result += kRequireLeafSelfsigned;
  }
  return result;
}

// static
std::optional<CertificateTrust> CertificateTrust::FromDebugString(
    const std::string &trust_string) {
  std::vector<std::string_view> split =
      string_util::SplitString(trust_string, '+');

  if (split.empty()) {
    return std::nullopt;
  }

  CertificateTrust trust;

  if (string_util::IsEqualNoCase(split[0], kUnspecifiedStr)) {
    trust = CertificateTrust::ForUnspecified();
  } else if (string_util::IsEqualNoCase(split[0], kDistrustedStr)) {
    trust = CertificateTrust::ForDistrusted();
  } else if (string_util::IsEqualNoCase(split[0], kTrustedAnchorStr)) {
    trust = CertificateTrust::ForTrustAnchor();
  } else if (string_util::IsEqualNoCase(split[0], kTrustedAnchorOrLeafStr)) {
    trust = CertificateTrust::ForTrustAnchorOrLeaf();
  } else if (string_util::IsEqualNoCase(split[0], kTrustedLeafStr)) {
    trust = CertificateTrust::ForTrustedLeaf();
  } else {
    return std::nullopt;
  }

  for (auto i = ++split.begin(); i != split.end(); ++i) {
    if (string_util::IsEqualNoCase(*i, kEnforceAnchorExpiry)) {
      trust = trust.WithEnforceAnchorExpiry();
    } else if (string_util::IsEqualNoCase(*i, kEnforceAnchorConstraints)) {
      trust = trust.WithEnforceAnchorConstraints();
    } else if (string_util::IsEqualNoCase(*i, kRequireAnchorBasicConstraints)) {
      trust = trust.WithRequireAnchorBasicConstraints();
    } else if (string_util::IsEqualNoCase(*i, kRequireLeafSelfsigned)) {
      trust = trust.WithRequireLeafSelfSigned();
    } else {
      return std::nullopt;
    }
  }

  return trust;
}

MTCAnchor::MTCAnchor(bssl::Span<const uint8_t> log_id,
                     Span<const TrustedSubtree> trusted_subtrees)
    : log_id_(log_id.begin(), log_id.end()),
      trusted_subtrees_(trusted_subtrees.begin(), trusted_subtrees.end()) {
  CreateSyntheticCert(log_id);
}

bool MTCAnchor::IsValid() const {
  if (!synthetic_cert_) {
    return false;
  }
  Subtree min_subtree;
  for (const auto &subtree : trusted_subtrees_) {
    if (!subtree.range.IsValid() || subtree.range < min_subtree) {
      return false;
    }
    min_subtree = subtree.range;
  }
  return true;
}

der::Input MTCAnchor::NormalizedSubject() const {
  return synthetic_cert_->normalized_subject();
}

CertificateTrust MTCAnchor::CertTrust() const {
  CertificateTrust trust;
  trust.type = CertificateTrustType::TRUSTED_ANCHOR;
  return trust;
}

std::shared_ptr<const ParsedCertificate> MTCAnchor::AsCert() const {
  return synthetic_cert_;
}

std::optional<TreeHashConstSpan> MTCAnchor::SubtreeHash(
    Subtree target_range) const {
  auto it = std::lower_bound(
      trusted_subtrees_.begin(), trusted_subtrees_.end(), target_range,
      [](const TrustedSubtree &subtree, Subtree range) -> bool {
        return subtree.range < range;
      });
  if (it == trusted_subtrees_.end() || it->range != target_range) {
    return std::nullopt;
  }
  return it->hash;
}

void MTCAnchor::CreateSyntheticCert(bssl::Span<const uint8_t> log_id) {
  bssl::ScopedCBB cbb;
  CBB cert, tbs_cert, version, validity, subject_seq, subject_set, subject_log,
      signature;
  BSSL_CHECK(CBB_init(cbb.get(), 0));
  BSSL_CHECK(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE));
  // Fill the TBSCertificate
  BSSL_CHECK(CBB_add_asn1(&cert, &tbs_cert, CBS_ASN1_SEQUENCE));
  // version
  BSSL_CHECK(
      CBB_add_asn1(&tbs_cert, &version,
                   CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
  BSSL_CHECK(CBB_add_asn1_uint64(&version, 2));  // 2 is the enum value for v3
  // serialNumber
  BSSL_CHECK(CBB_add_asn1_uint64(&tbs_cert, 0));
  // signature
  BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));
  // issuer
  BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));
  // validity
  BSSL_CHECK(CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE));
  static const uint8_t notBefore[] = "00000101000000Z";
  static const uint8_t notAfter[] = "99991231235960Z";
  BSSL_CHECK(CBB_add_asn1_element(&validity, CBS_ASN1_GENERALIZEDTIME,
                                  notBefore, sizeof(notBefore) - 1));
  BSSL_CHECK(CBB_add_asn1_element(&validity, CBS_ASN1_GENERALIZEDTIME, notAfter,
                                  sizeof(notAfter) - 1));

  // subject
  BSSL_CHECK(CBB_add_asn1(&tbs_cert, &subject_seq, CBS_ASN1_SEQUENCE));
  BSSL_CHECK(CBB_add_asn1(&subject_seq, &subject_set, CBS_ASN1_SET));
  BSSL_CHECK(CBB_add_asn1(&subject_set, &subject_log, CBS_ASN1_SEQUENCE));

  // Section 5.2: Use OID 1.3.6.1.4.1.44363.47.1 as the attribute type for the
  // log ID's name. Note that this is the early experimentation OID in the
  // draft rather than the real value of `id-rdna-trustAnchorID`.
  static uint8_t log_attr_oid[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                   0x82, 0xda, 0x4b, 0x2f, 0x01};
  BSSL_CHECK(CBB_add_asn1_element(&subject_log, CBS_ASN1_OBJECT, log_attr_oid,
                                  sizeof(log_attr_oid)));

  // Section 5.2's note for initial experimentation also says to use UTF8String
  // to represent the attribute's value rather than RELATIVE-OID.

  //  Convert the relative OID |log_id| to a string. This can fail.
  CBS log_id_oid(log_id);
  bssl::UniquePtr<char> log_id_text(CBS_asn1_relative_oid_to_text(&log_id_oid));
  if (!log_id_text) {
    return;
  }
  BSSL_CHECK(
      CBB_add_asn1_element(&subject_log, CBS_ASN1_UTF8STRING,
                           reinterpret_cast<const uint8_t *>(log_id_text.get()),
                           strlen(log_id_text.get())));

  // subjectPublicKeyInfo
  BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));

  // finish the outer Certificate with the signatureAlgorithm and signature.
  BSSL_CHECK(CBB_add_asn1_element(&cert, CBS_ASN1_SEQUENCE, nullptr, 0));
  BSSL_CHECK(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING));
  BSSL_CHECK(CBB_add_u8(&signature, 0));

  BSSL_CHECK(CBB_flush(cbb.get()));

  synthetic_cert_ = ParsedCertificate::Create(
      bssl::UniquePtr<CRYPTO_BUFFER>(
          CRYPTO_BUFFER_new(CBB_data(cbb.get()), CBB_len(cbb.get()), nullptr)),
      ParseCertificateOptions{}, nullptr);
}

TrustAnchor::TrustAnchor(CertificateTrust trust)
    : TrustAnchor(trust, nullptr) {}
TrustAnchor::TrustAnchor(CertificateTrust trust,
                         std::shared_ptr<const bssl::MTCAnchor> mtc_anchor)
    : trust_(trust), mtc_anchor_(mtc_anchor) {}

void TrustAnchor::OverrideCertTrust(CertificateTrust new_trust) {
  trust_ = new_trust;
}

CertificateTrust TrustAnchor::CertTrust() const { return trust_; }

std::shared_ptr<const MTCAnchor> TrustAnchor::MTCAnchor() const {
  return mtc_anchor_;
}

TrustStore::TrustStore() = default;

std::shared_ptr<const MTCAnchor> TrustStore::GetTrustedMTCIssuerOf(
    const ParsedCertificate *cert) {
  return nullptr;
}

void TrustStore::AsyncGetIssuersOf(const ParsedCertificate *cert,
                                   std::unique_ptr<Request> *out_req) {
  out_req->reset();
}

BSSL_NAMESPACE_END
