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

#include "chrome/common/net/x509_certificate_model.h"

#include <ranges>
#include <string_view>
#include <variant>

#include "base/check.h"
#include "base/containers/fixed_flat_map.h"
#include "base/i18n/number_formatting.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/grit/generated_resources.h"
#include "components/certificate_model/x509_certificate_constants.h"
#include "components/strings/grit/components_strings.h"
#include "components/url_formatter/url_formatter.h"
#include "net/base/ip_address.h"
#include "net/cert/ct_objects_extractor.h"
#include "net/cert/qwac.h"
#include "net/cert/time_conversions.h"
#include "net/cert/x509_util.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/mem.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
#include "third_party/boringssl/src/pki/cert_errors.h"
#include "third_party/boringssl/src/pki/certificate_policies.h"
#include "third_party/boringssl/src/pki/extended_key_usage.h"
#include "third_party/boringssl/src/pki/input.h"
#include "third_party/boringssl/src/pki/parse_certificate.h"
#include "third_party/boringssl/src/pki/parse_name.h"
#include "third_party/boringssl/src/pki/parse_values.h"
#include "third_party/boringssl/src/pki/parser.h"
#include "third_party/boringssl/src/pki/verify_signed_data.h"
#include "ui/base/l10n/l10n_util.h"

namespace x509_certificate_model {

namespace {

// Old Netscape OIDs. Do we still need all these?
// #define NETSCAPE_OID 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42
// #define NETSCAPE_CERT_EXT NETSCAPE_OID, 0x01
//
// CONST_OID nsExtCertType[] = { NETSCAPE_CERT_EXT, 0x01 };
constexpr uint8_t kNetscapeCertificateTypeOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                   0xf8, 0x42, 0x01, 0x01};

// CONST_OID nsExtBaseURL[] = { NETSCAPE_CERT_EXT, 0x02 };
constexpr uint8_t kNetscapeBaseURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                           0xf8, 0x42, 0x01, 0x02};

// CONST_OID nsExtRevocationURL[] = { NETSCAPE_CERT_EXT, 0x03 };
constexpr uint8_t kNetscapeRevocationURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                 0xf8, 0x42, 0x01, 0x03};

// CONST_OID nsExtCARevocationURL[] = { NETSCAPE_CERT_EXT, 0x04 };
constexpr uint8_t kNetscapeCARevocationURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                   0xf8, 0x42, 0x01, 0x04};

// CONST_OID nsExtCACertURL[] = { NETSCAPE_CERT_EXT, 0x06 };
constexpr uint8_t kNetscapeCACertURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                             0xf8, 0x42, 0x01, 0x06};

// CONST_OID nsExtCertRenewalURL[] = { NETSCAPE_CERT_EXT, 0x07 };
constexpr uint8_t kNetscapeRenewalURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                              0xf8, 0x42, 0x01, 0x07};

// CONST_OID nsExtCAPolicyURL[] = { NETSCAPE_CERT_EXT, 0x08 };
constexpr uint8_t kNetscapeCAPolicyURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                               0xf8, 0x42, 0x01, 0x08};

// CONST_OID nsExtSSLServerName[] = { NETSCAPE_CERT_EXT, 0x0c };
constexpr uint8_t kNetscapeSSLServerNameOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                 0xf8, 0x42, 0x01, 0x0c};

// CONST_OID nsExtComment[] = { NETSCAPE_CERT_EXT, 0x0d };
constexpr uint8_t kNetscapeCommentOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                           0xf8, 0x42, 0x01, 0x0d};

// CONST_OID nsExtLostPasswordURL[] = { NETSCAPE_CERT_EXT, 0x0e };
constexpr uint8_t kNetscapeLostPasswordURLOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                   0xf8, 0x42, 0x01, 0x0e};

// CONST_OID nsExtCertRenewalTime[] = { NETSCAPE_CERT_EXT, 0x0f };
constexpr uint8_t kNetscapeRenewalTimeOid[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                               0xf8, 0x42, 0x01, 0x0f};

constexpr uint8_t kNetscapeServerGatedCrypto[] = {0x60, 0x86, 0x48, 0x01, 0x86,
                                                  0xf8, 0x42, 0x04, 0x01};

// Microsoft OIDs. Do we still need all these?
//
// 1.3.6.1.4.1.311.20.2
constexpr uint8_t kMsCertExtCerttype[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                          0x82, 0x37, 0x14, 0x02};

// 1.3.6.1.4.1.311.21.1
constexpr uint8_t kMsCertsrvCaVersion[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                           0x82, 0x37, 0x15, 0x01};

// 1.3.6.1.4.1.311.20.2.3
constexpr uint8_t kMsNtPrincipalName[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                          0x82, 0x37, 0x14, 0x02, 0x03};

// 1.3.6.1.4.1.311.25.1
constexpr uint8_t kMsNtdsReplication[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                          0x82, 0x37, 0x19, 0x01};

// 1.3.6.1.4.1.311.21.7
constexpr uint8_t kMsCertTemplate[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                       0x82, 0x37, 0x15, 0x07};

// 1.3.6.1.4.1.311.2.1.21
constexpr uint8_t kEkuMsIndividualCodeSigning[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x15};

// 1.3.6.1.4.1.311.2.1.22
constexpr uint8_t kEkuMsCommercialCodeSigning[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x16};

// 1.3.6.1.4.1.311.10.3.1
constexpr uint8_t kEkuMsTrustListSigning[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                              0x82, 0x37, 0x0a, 0x03, 0x01};

// 1.3.6.1.4.1.311.10.3.2
constexpr uint8_t kEkuMsTimeStamping[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                          0x82, 0x37, 0x0a, 0x03, 0x02};

// 1.3.6.1.4.1.311.10.3.3
constexpr uint8_t kEkuMsServerGatedCrypto[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                               0x82, 0x37, 0x0a, 0x03, 0x03};

// 1.3.6.1.4.1.311.10.3.4
constexpr uint8_t kEkuMsEncryptingFileSystem[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                                  0x82, 0x37, 0x0a, 0x03, 0x04};

// 1.3.6.1.4.1.311.10.3.4.1
constexpr uint8_t kEkuMsFileRecovery[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
                                          0x37, 0x0a, 0x03, 0x04, 0x01};

// 1.3.6.1.4.1.311.10.3.5
constexpr uint8_t kEkuMsWindowsHardwareDriverVerification[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x05};

// 1.3.6.1.4.1.311.10.3.10
constexpr uint8_t kEkuMsQualifiedSubordination[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x0a};

// 1.3.6.1.4.1.311.10.3.11
constexpr uint8_t kEkuMsKeyRecovery[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                         0x82, 0x37, 0x0a, 0x03, 0x0b};

// 1.3.6.1.4.1.311.10.3.12
constexpr uint8_t kEkuMsDocumentSigning[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                             0x82, 0x37, 0x0a, 0x03, 0x0c};

// 1.3.6.1.4.1.311.10.3.13
constexpr uint8_t kEkuMsLifetimeSigning[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                             0x82, 0x37, 0x0a, 0x03, 0x0d};

// 1.3.6.1.4.1.311.20.2.2
constexpr uint8_t kEkuMsSmartCardLogon[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                            0x82, 0x37, 0x14, 0x02, 0x02};

// 1.3.6.1.4.1.311.21.6
constexpr uint8_t kEkuMsKeyRecoveryAgent[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
                                              0x82, 0x37, 0x15, 0x06};

std::string ProcessRawBytes(base::span<const uint8_t> data) {
  return ProcessRawBytesWithSeparators(data, ' ', '\n');
}

constexpr auto kOidStringMap = base::MakeFixedFlatMap<bssl::der::Input, int>({
    // Distinguished Name fields:
    {bssl::der::Input(bssl::kTypeCommonNameOid), IDS_CERT_OID_AVA_COMMON_NAME},
    {bssl::der::Input(bssl::kTypeStateOrProvinceNameOid),
     IDS_CERT_OID_AVA_STATE_OR_PROVINCE},
    {bssl::der::Input(bssl::kTypeOrganizationNameOid),
     IDS_CERT_OID_AVA_ORGANIZATION_NAME},
    {bssl::der::Input(bssl::kTypeOrganizationUnitNameOid),
     IDS_CERT_OID_AVA_ORGANIZATIONAL_UNIT_NAME},
    {bssl::der::Input(kTypeDnQualifierOid), IDS_CERT_OID_AVA_DN_QUALIFIER},
    {bssl::der::Input(bssl::kTypeCountryNameOid),
     IDS_CERT_OID_AVA_COUNTRY_NAME},
    {bssl::der::Input(bssl::kTypeSerialNumberOid),
     IDS_CERT_OID_AVA_SERIAL_NUMBER},
    {bssl::der::Input(bssl::kTypeLocalityNameOid), IDS_CERT_OID_AVA_LOCALITY},
    {bssl::der::Input(bssl::kTypeDomainComponentOid), IDS_CERT_OID_AVA_DC},
    {bssl::der::Input(kRFC1274MailOid), IDS_CERT_OID_RFC1274_MAIL},
    {bssl::der::Input(kRFC1274UidOid), IDS_CERT_OID_RFC1274_UID},
    {bssl::der::Input(bssl::kTypeEmailAddressOid),
     IDS_CERT_OID_PKCS9_EMAIL_ADDRESS},

    // Extended Validation (EV) name fields:
    {bssl::der::Input(kTypeBusinessCategory), IDS_CERT_OID_BUSINESS_CATEGORY},
    {bssl::der::Input(kEVJurisdictionLocalityName),
     IDS_CERT_OID_EV_INCORPORATION_LOCALITY},
    {bssl::der::Input(kEVJurisdictionStateOrProvinceName),
     IDS_CERT_OID_EV_INCORPORATION_STATE},
    {bssl::der::Input(kEVJurisdictionCountryName),
     IDS_CERT_OID_EV_INCORPORATION_COUNTRY},
    {bssl::der::Input(bssl::kTypeStreetAddressOid),
     IDS_CERT_OID_AVA_STREET_ADDRESS},
    {bssl::der::Input(kTypePostalCode), IDS_CERT_OID_AVA_POSTAL_CODE},

    // Extension fields (including details of extensions):
    {bssl::der::Input(kNetscapeCertificateTypeOid), IDS_CERT_EXT_NS_CERT_TYPE},
    {bssl::der::Input(kNetscapeBaseURLOid), IDS_CERT_EXT_NS_CERT_BASE_URL},
    {bssl::der::Input(kNetscapeRevocationURLOid),
     IDS_CERT_EXT_NS_CERT_REVOCATION_URL},
    {bssl::der::Input(kNetscapeCARevocationURLOid),
     IDS_CERT_EXT_NS_CA_REVOCATION_URL},
    {bssl::der::Input(kNetscapeRenewalURLOid),
     IDS_CERT_EXT_NS_CERT_RENEWAL_URL},
    {bssl::der::Input(kNetscapeCAPolicyURLOid), IDS_CERT_EXT_NS_CA_POLICY_URL},
    {bssl::der::Input(kNetscapeSSLServerNameOid),
     IDS_CERT_EXT_NS_SSL_SERVER_NAME},
    {bssl::der::Input(kNetscapeCommentOid), IDS_CERT_EXT_NS_COMMENT},
    {bssl::der::Input(kNetscapeLostPasswordURLOid),
     IDS_CERT_EXT_NS_LOST_PASSWORD_URL},
    {bssl::der::Input(kNetscapeRenewalTimeOid),
     IDS_CERT_EXT_NS_CERT_RENEWAL_TIME},
    {bssl::der::Input(kSubjectDirectoryAttributesOid),
     IDS_CERT_X509_SUBJECT_DIRECTORY_ATTR},
    {bssl::der::Input(bssl::kSubjectKeyIdentifierOid),
     IDS_CERT_X509_SUBJECT_KEYID},
    {bssl::der::Input(bssl::kAuthorityKeyIdentifierOid),
     IDS_CERT_X509_AUTH_KEYID},
    {bssl::der::Input(bssl::kKeyUsageOid), IDS_CERT_X509_KEY_USAGE},
    {bssl::der::Input(bssl::kSubjectAltNameOid),
     IDS_CERT_X509_SUBJECT_ALT_NAME},
    {bssl::der::Input(kIssuerAltNameOid), IDS_CERT_X509_ISSUER_ALT_NAME},
    {bssl::der::Input(bssl::kBasicConstraintsOid),
     IDS_CERT_X509_BASIC_CONSTRAINTS},
    {bssl::der::Input(bssl::kNameConstraintsOid),
     IDS_CERT_X509_NAME_CONSTRAINTS},
    {bssl::der::Input(bssl::kCrlDistributionPointsOid),
     IDS_CERT_X509_CRL_DIST_POINTS},
    {bssl::der::Input(bssl::kCertificatePoliciesOid),
     IDS_CERT_X509_CERT_POLICIES},
    {bssl::der::Input(bssl::kPolicyMappingsOid), IDS_CERT_X509_POLICY_MAPPINGS},
    {bssl::der::Input(bssl::kPolicyConstraintsOid),
     IDS_CERT_X509_POLICY_CONSTRAINTS},
    {bssl::der::Input(bssl::kExtKeyUsageOid), IDS_CERT_X509_EXT_KEY_USAGE},
    {bssl::der::Input(bssl::kAuthorityInfoAccessOid),
     IDS_CERT_X509_AUTH_INFO_ACCESS},
    {bssl::der::Input(net::ct::kEmbeddedSCTOid), IDS_CERT_X509_SCT_LIST},
    {bssl::der::Input(net::kQcStatementsOid), IDS_CERT_QC_STATEMENTS},

    // Extended Key Usages:
    {bssl::der::Input(kNetscapeServerGatedCrypto),
     IDS_CERT_EKU_NETSCAPE_INTERNATIONAL_STEP_UP},

    // Policies:
    {bssl::der::Input(net::kQevcpwOid), IDS_CERT_POLICY_ETSI_QEVCP_W},
    {bssl::der::Input(net::kQncpwOid), IDS_CERT_POLICY_ETSI_QNCP_W},

    // Microsoft oids:
    {bssl::der::Input(kMsCertExtCerttype), IDS_CERT_EXT_MS_CERT_TYPE},
    {bssl::der::Input(kMsCertsrvCaVersion), IDS_CERT_EXT_MS_CA_VERSION},
    {bssl::der::Input(kMsNtPrincipalName), IDS_CERT_EXT_MS_NT_PRINCIPAL_NAME},
    {bssl::der::Input(kMsNtdsReplication), IDS_CERT_EXT_MS_NTDS_REPLICATION},
    {bssl::der::Input(bssl::kMSApplicationPoliciesOid),
     IDS_CERT_EXT_MS_APP_POLICIES},
    {bssl::der::Input(kMsCertTemplate), IDS_CERT_EXT_MS_CERT_TEMPLATE},
    {bssl::der::Input(kEkuMsIndividualCodeSigning),
     IDS_CERT_EKU_MS_INDIVIDUAL_CODE_SIGNING},
    {bssl::der::Input(kEkuMsCommercialCodeSigning),
     IDS_CERT_EKU_MS_COMMERCIAL_CODE_SIGNING},
    {bssl::der::Input(kEkuMsTrustListSigning),
     IDS_CERT_EKU_MS_TRUST_LIST_SIGNING},
    {bssl::der::Input(kEkuMsTimeStamping), IDS_CERT_EKU_MS_TIME_STAMPING},
    {bssl::der::Input(kEkuMsServerGatedCrypto),
     IDS_CERT_EKU_MS_SERVER_GATED_CRYPTO},
    {bssl::der::Input(kEkuMsEncryptingFileSystem),
     IDS_CERT_EKU_MS_ENCRYPTING_FILE_SYSTEM},
    {bssl::der::Input(kEkuMsFileRecovery), IDS_CERT_EKU_MS_FILE_RECOVERY},
    {bssl::der::Input(kEkuMsWindowsHardwareDriverVerification),
     IDS_CERT_EKU_MS_WINDOWS_HARDWARE_DRIVER_VERIFICATION},
    {bssl::der::Input(kEkuMsQualifiedSubordination),
     IDS_CERT_EKU_MS_QUALIFIED_SUBORDINATION},
    {bssl::der::Input(kEkuMsKeyRecovery), IDS_CERT_EKU_MS_KEY_RECOVERY},
    {bssl::der::Input(kEkuMsDocumentSigning), IDS_CERT_EKU_MS_DOCUMENT_SIGNING},
    {bssl::der::Input(kEkuMsLifetimeSigning), IDS_CERT_EKU_MS_LIFETIME_SIGNING},
    {bssl::der::Input(kEkuMsSmartCardLogon), IDS_CERT_EKU_MS_SMART_CARD_LOGON},
    {bssl::der::Input(kEkuMsKeyRecoveryAgent),
     IDS_CERT_EKU_MS_KEY_RECOVERY_AGENT},
});

std::optional<std::string> GetOidText(bssl::der::Input oid) {
  const auto i = kOidStringMap.find(oid);
  if (i != kOidStringMap.end())
    return l10n_util::GetStringUTF8(i->second);

  // Fall through to common OIDs shared across platforms.
  std::optional<int> common_id = GetCommonOidStringId(oid);
  if (common_id.has_value()) {
    return l10n_util::GetStringUTF8(*common_id);
  }

  return std::nullopt;
}

std::string GetOidTextOrNumeric(bssl::der::Input oid) {
  std::optional<std::string> oid_text = GetOidText(oid);
  return oid_text ? *oid_text : OidToNumericString(oid);
}

std::string ProcessRDN(const bssl::RelativeDistinguishedName& rdn) {
  std::string rv;
  // In X.509, RelativeDistinguishedName is a Set, so "last" has no meaning,
  // and generally only has one element anyway.  Just traverse in encoded
  // order.
  for (const bssl::X509NameAttribute& name_attribute : rdn) {
    std::string oid_text = GetOidTextOrNumeric(name_attribute.type);
    if (oid_text.empty())
      return std::string();
    rv += oid_text;
    std::string value;
    if (!name_attribute.ValueAsStringWithUnsafeOptions(kNameStringHandling,
                                                       &value)) {
      return std::string();
    }
    rv += " = ";
    if (name_attribute.type == bssl::der::Input(bssl::kTypeCommonNameOid)) {
      value = ProcessIDN(value);
    }
    // TODO(mattm): do something about newlines (or other control chars)?
    rv += value;
    rv += "\n";
  }
  return rv;
}

// Note: This was called ProcessName in the x509_certificate_model_nss impl.
OptionalStringOrError RDNSequenceToStringMultiLine(
    const bssl::RDNSequence& rdns) {
  if (rdns.empty())
    return NotPresent();

  std::string rv;
  // Note: this has high level similarity to net::ConvertToRFC2253, but
  // this one is multi-line, and prints in reverse order, and has a different
  // set of oids that it has printable names for, and different handling of
  // unprintable values, and IDN processing...
  for (const bssl::RelativeDistinguishedName& rdn : std::views::reverse(rdns)) {
    std::string rdn_value = ProcessRDN(rdn);
    if (rdn_value.empty())
      return Error();
    rv += rdn_value;
  }
  return rv;
}

// Returns a comma-separated string of the strings in `string_map` for the bits
// in `bitfield` that are set.
// string_map may contain -1 for reserved positions that should not be set.
std::optional<std::string> ProcessBitField(bssl::der::BitString bitfield,
                                           base::span<const int> string_map,
                                           char separator) {
  std::string rv;
  for (size_t i = 0; i < string_map.size(); ++i) {
    if (bitfield.AssertsBit(i)) {
      int string_id = string_map[i];
      // TODO(mattm): is returning an error here correct? Or should it encode
      // some generic string like "reserved bit N set"?
      if (string_id < 0)
        return std::nullopt;
      if (!rv.empty())
        rv += separator;
      rv += l10n_util::GetStringUTF8(string_id);
    }
  }
  // TODO(mattm): should it be an error if bitfield asserts bits beyond `len`?
  // Or encode them with some generic string like "bit N set"?
  return rv;
}

// Returns nullopt on error, or empty string if no bits were set.
std::optional<std::string> ProcessBitStringValue(
    bssl::der::Input value,
    base::span<const int> string_map,
    char separator) {
  std::optional<bssl::der::BitString> decoded =
      bssl::der::ParseBitString(value);
  if (!decoded) {
    return std::nullopt;
  }
  return ProcessBitField(decoded.value(), string_map, separator);
}

std::optional<std::string> ProcessBitStringExtension(
    bssl::der::Input extension_data,
    base::span<const int> string_map,
    char separator) {
  bssl::der::Input value;
  bssl::der::Parser parser(extension_data);
  if (!parser.ReadTag(CBS_ASN1_BITSTRING, &value) || parser.HasMore()) {
    return std::nullopt;
  }

  return ProcessBitStringValue(value, string_map, separator);
}

std::optional<std::string> ProcessNSCertTypeExtension(
    bssl::der::Input extension_data) {
  static const int usage_strings[] = {
      IDS_CERT_USAGE_SSL_CLIENT,
      IDS_CERT_USAGE_SSL_SERVER,
      IDS_CERT_EXT_NS_CERT_TYPE_EMAIL,
      IDS_CERT_USAGE_OBJECT_SIGNER,
      -1,  // reserved
      IDS_CERT_USAGE_SSL_CA,
      IDS_CERT_EXT_NS_CERT_TYPE_EMAIL_CA,
      IDS_CERT_USAGE_OBJECT_SIGNER,
  };
  return ProcessBitStringExtension(extension_data, usage_strings, '\n');
}

std::optional<std::string> ProcessKeyUsageExtension(
    bssl::der::Input extension_data) {
  static const int usage_strings[] = {
      IDS_CERT_X509_KEY_USAGE_SIGNING,
      IDS_CERT_X509_KEY_USAGE_NONREP,
      IDS_CERT_X509_KEY_USAGE_ENCIPHERMENT,
      IDS_CERT_X509_KEY_USAGE_DATA_ENCIPHERMENT,
      IDS_CERT_X509_KEY_USAGE_KEY_AGREEMENT,
      IDS_CERT_X509_KEY_USAGE_CERT_SIGNER,
      IDS_CERT_X509_KEY_USAGE_CRL_SIGNER,
      IDS_CERT_X509_KEY_USAGE_ENCIPHER_ONLY,
      IDS_CERT_X509_KEY_USAGE_DECIPHER_ONLY,
  };
  std::optional<std::string> rv =
      ProcessBitStringExtension(extension_data, usage_strings, '\n');
  if (rv && rv->empty()) {
    // RFC 5280 4.2.1.3:
    // When the keyUsage extension appears in a certificate, at least one of
    // the bits MUST be set to 1.
    return std::nullopt;
  }
  return rv;
}

std::optional<std::string> ProcessBasicConstraints(
    bssl::der::Input extension_data) {
  bssl::ParsedBasicConstraints basic_constraints;
  if (!bssl::ParseBasicConstraints(extension_data, &basic_constraints)) {
    return std::nullopt;
  }

  std::string rv;
  if (basic_constraints.is_ca)
    rv = l10n_util::GetStringUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_IS_CA);
  else
    rv = l10n_util::GetStringUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_IS_NOT_CA);
  rv += '\n';
  if (basic_constraints.is_ca) {
    std::u16string depth;
    if (!basic_constraints.has_path_len) {
      depth = l10n_util::GetStringUTF16(
          IDS_CERT_X509_BASIC_CONSTRAINT_PATH_LEN_UNLIMITED);
    } else {
      depth = base::FormatNumber(basic_constraints.path_len);
    }
    rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_PATH_LEN,
                                    depth);
  }
  return rv;
}

std::optional<std::string> ProcessExtKeyUsage(bssl::der::Input extension_data) {
  std::vector<bssl::der::Input> extended_key_usage;
  if (!bssl::ParseEKUExtension(extension_data, &extended_key_usage)) {
    return std::nullopt;
  }

  std::string rv;
  for (const auto& oid : extended_key_usage) {
    std::string numeric_oid = OidToNumericString(oid);
    std::optional<std::string> oid_text = GetOidText(oid);

    // If oid is one that is recognized, display the text description along
    // with the numeric_oid. If we don't recognize the OID just display the
    // numeric OID alone.
    if (!oid_text) {
      rv += numeric_oid;
    } else {
      rv += l10n_util::GetStringFUTF8(IDS_CERT_EXT_KEY_USAGE_FORMAT,
                                      base::UTF8ToUTF16(*oid_text),
                                      base::UTF8ToUTF16(numeric_oid));
    }
    rv += '\n';
  }
  return rv;
}

OptionalStringOrError ProcessNameValue(bssl::der::Input name_value) {
  bssl::RDNSequence rdns;
  if (!bssl::ParseNameValue(name_value, &rdns)) {
    return Error();
  }
  return RDNSequenceToStringMultiLine(rdns);
}

std::string FormatGeneralName(std::u16string key, std::string_view value) {
  return l10n_util::GetStringFUTF8(IDS_CERT_UNKNOWN_OID_INFO_FORMAT, key,
                                   base::UTF8ToUTF16(value)) +
         '\n';
}

std::string FormatGeneralName(int key_string_id, std::string_view value) {
  return FormatGeneralName(l10n_util::GetStringUTF16(key_string_id), value);
}

std::optional<std::string> ProcessGeneralNames(
    const bssl::GeneralNames& names) {
  // Note: The old x509_certificate_model_nss impl would process names in the
  // order they appeared in the certificate, whereas this impl parses names
  // into different lists by each type and then processes those in order.
  // Probably doesn't matter.
  std::string rv;
  for (const auto& other_name : names.other_names) {
    bssl::der::Input type;
    bssl::der::Input value;
    if (!ParseOtherName(other_name, &type, &value)) {
      return std::nullopt;
    }
    // x509_certificate_model_nss went a bit further in parsing certain
    // otherName types, but it probably isn't worth bothering.
    rv += FormatGeneralName(base::UTF8ToUTF16(GetOidTextOrNumeric(type)),
                            ProcessRawBytes(value));
  }
  for (const auto& rfc822_name : names.rfc822_names) {
    // TODO(mattm): do something about newlines (or other control chars)?
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_RFC822_NAME, rfc822_name);
  }
  for (const auto& dns_name : names.dns_names) {
    // TODO(mattm): Should probably do ProcessIDN on dnsNames from
    // subjectAltName like we do on subject commonName?
    // TODO(mattm): do something about newlines (or other control chars)?
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_DNS_NAME, dns_name);
  }
  for (const auto& x400_address : names.x400_addresses) {
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_X400_ADDRESS,
                            ProcessRawBytes(x400_address));
  }
  for (const auto& directory_name : names.directory_names) {
    OptionalStringOrError name = ProcessNameValue(directory_name);
    if (!std::holds_alternative<std::string>(name)) {
      return std::nullopt;
    }
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_DIRECTORY_NAME,
                            std::get<std::string>(name));
  }
  for (const auto& edi_party_name : names.edi_party_names) {
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_EDI_PARTY_NAME,
                            ProcessRawBytes(edi_party_name));
  }
  for (const auto& uniform_resource_identifier :
       names.uniform_resource_identifiers) {
    // TODO(mattm): do something about newlines (or other control chars)?
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_URI,
                            uniform_resource_identifier);
  }
  for (const auto& ip_address_bytes : names.ip_addresses) {
    net::IPAddress ip_address(ip_address_bytes);
    // The `GeneralNames` parser should guarantee this.
    CHECK(ip_address.IsValid());
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_IP_ADDRESS,
                            ip_address.ToString());
  }
  for (const auto& ip_address_range : names.ip_address_ranges) {
    net::IPAddress ip_address(ip_address_range.first);
    net::IPAddress mask(ip_address_range.second);
    // The `GeneralNames` parser should guarantee this.
    CHECK(ip_address.IsValid());
    CHECK(mask.IsValid());
    rv += FormatGeneralName(
        IDS_CERT_GENERAL_NAME_IP_ADDRESS,
        ip_address.ToString() + '/' +
            base::NumberToString(net::MaskPrefixLength(mask)));
  }
  for (const auto& registered_id : names.registered_ids) {
    rv += FormatGeneralName(IDS_CERT_GENERAL_NAME_REGISTERED_ID,
                            GetOidTextOrNumeric(registered_id));
  }

  return rv;
}

std::optional<std::string> ProcessGeneralNamesTlv(
    bssl::der::Input extension_data) {
  bssl::CertErrors unused_errors;
  std::unique_ptr<bssl::GeneralNames> alt_names =
      bssl::GeneralNames::Create(extension_data, &unused_errors);
  if (!alt_names)
    return std::nullopt;
  return ProcessGeneralNames(*alt_names);
}

std::optional<std::string> ProcessGeneralNamesValue(
    bssl::der::Input general_names_value) {
  bssl::CertErrors unused_errors;
  std::unique_ptr<bssl::GeneralNames> alt_names =
      bssl::GeneralNames::CreateFromValue(general_names_value, &unused_errors);
  if (!alt_names)
    return std::nullopt;
  return ProcessGeneralNames(*alt_names);
}

std::optional<std::string> ProcessSubjectKeyId(
    bssl::der::Input extension_data) {
  bssl::der::Input subject_key_identifier;
  if (!bssl::ParseSubjectKeyIdentifier(extension_data,
                                       &subject_key_identifier)) {
    return std::nullopt;
  }
  return l10n_util::GetStringFUTF8(
      IDS_CERT_KEYID_FORMAT,
      base::ASCIIToUTF16(ProcessRawBytes(subject_key_identifier)));
}

std::optional<std::string> ProcessAuthorityKeyId(
    bssl::der::Input extension_data) {
  bssl::ParsedAuthorityKeyIdentifier authority_key_id;
  if (!bssl::ParseAuthorityKeyIdentifier(extension_data, &authority_key_id)) {
    return std::nullopt;
  }

  std::string rv;
  if (authority_key_id.key_identifier) {
    rv += l10n_util::GetStringFUTF8(
        IDS_CERT_KEYID_FORMAT,
        base::ASCIIToUTF16(ProcessRawBytes(*authority_key_id.key_identifier)));
    rv += '\n';
  }
  if (authority_key_id.authority_cert_issuer) {
    std::optional<std::string> s =
        ProcessGeneralNamesValue(*authority_key_id.authority_cert_issuer);
    if (!s)
      return std::nullopt;
    rv += l10n_util::GetStringFUTF8(IDS_CERT_ISSUER_FORMAT,
                                    base::UTF8ToUTF16(*s));
    rv += '\n';
  }
  if (authority_key_id.authority_cert_serial_number) {
    rv += l10n_util::GetStringFUTF8(
        IDS_CERT_SERIAL_NUMBER_FORMAT,
        base::ASCIIToUTF16(
            ProcessRawBytes(*authority_key_id.authority_cert_serial_number)));
    rv += '\n';
  }

  return rv;
}

std::optional<std::string> ProcessUserNotice(bssl::der::Input qualifier) {
  // RFC 5280 section 4.2.1.4:
  //
  //    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)) }

  bssl::der::Parser outer_parser(qualifier);
  bssl::der::Parser parser;
  if (!outer_parser.ReadSequence(&parser) || outer_parser.HasMore())
    return std::nullopt;

  std::optional<bssl::der::Input> notice_ref_value;
  if (!parser.ReadOptionalTag(CBS_ASN1_SEQUENCE, &notice_ref_value)) {
    return std::nullopt;
  }

  std::string rv;
  if (notice_ref_value) {
    bssl::der::Parser notice_ref_parser(*notice_ref_value);
    CBS_ASN1_TAG organization_tag;
    bssl::der::Input organization_value;
    if (!notice_ref_parser.ReadTagAndValue(&organization_tag,
                                           &organization_value)) {
      return std::nullopt;
    }
    std::optional<std::string> s =
        ProcessUserNoticeDisplayText(organization_tag, organization_value);
    if (!s)
      return std::nullopt;
    rv += *s;
    rv += " - ";

    bssl::der::Parser notice_numbers_parser;
    if (!notice_ref_parser.ReadSequence(&notice_numbers_parser))
      return std::nullopt;
    bool first = true;
    while (notice_numbers_parser.HasMore()) {
      bssl::der::Input notice_number;
      if (!notice_numbers_parser.ReadTag(CBS_ASN1_INTEGER, &notice_number)) {
        return std::nullopt;
      }
      if (!first)
        rv += ", ";
      rv += '#';
      uint64_t number;
      if (bssl::der::ParseUint64(notice_number, &number)) {
        rv += base::NumberToString(number);
      } else {
        rv += ProcessRawBytes(notice_number);
      }
      first = false;
    }
  }

  if (parser.HasMore()) {
    CBS_ASN1_TAG explicit_text_tag;
    bssl::der::Input explicit_text_value;
    if (!parser.ReadTagAndValue(&explicit_text_tag, &explicit_text_value))
      return std::nullopt;
    rv += "\n    ";
    std::optional<std::string> s =
        ProcessUserNoticeDisplayText(explicit_text_tag, explicit_text_value);
    if (!s)
      return std::nullopt;
    rv += *s;
  }

  if (parser.HasMore())
    return std::nullopt;

  return rv;
}

std::optional<std::string> ProcessCertificatePolicies(
    bssl::der::Input extension_data) {
  std::vector<bssl::PolicyInformation> policies;
  bssl::CertErrors errors;
  if (!bssl::ParseCertificatePoliciesExtension(extension_data, &policies,
                                               &errors)) {
    return std::nullopt;
  }
  std::string rv;
  for (const auto& policy_info : policies) {
    std::string key = GetOidTextOrNumeric(policy_info.policy_oid);
    // If there are policy qualifiers, display the oid text
    // with a ':', otherwise just put the oid text and a newline.
    if (policy_info.policy_qualifiers.empty()) {
      rv += key;
    } else {
      rv += l10n_util::GetStringFUTF8(IDS_CERT_MULTILINE_INFO_START_FORMAT,
                                      base::UTF8ToUTF16(key));
    }
    rv += '\n';

    if (!policy_info.policy_qualifiers.empty()) {
      for (const auto& qualifier_info : policy_info.policy_qualifiers) {
        rv += "  ";
        rv += l10n_util::GetStringFUTF8(IDS_CERT_MULTILINE_INFO_START_FORMAT,
                                        base::UTF8ToUTF16(GetOidTextOrNumeric(
                                            qualifier_info.qualifier_oid)));
        if (qualifier_info.qualifier_oid ==
            bssl::der::Input(bssl::kCpsPointerId)) {
          std::optional<std::string> s =
              ProcessIA5String(qualifier_info.qualifier);
          if (!s)
            return std::nullopt;
          rv += "    ";
          rv += *s;
        } else if (qualifier_info.qualifier_oid ==
                   bssl::der::Input(bssl::kUserNoticeId)) {
          std::optional<std::string> s =
              ProcessUserNotice(qualifier_info.qualifier);
          if (!s)
            return std::nullopt;
          rv += *s;
        } else {
          rv += ProcessRawBytes(qualifier_info.qualifier);
        }
        rv += '\n';
      }
    }
  }
  return rv;
}

std::optional<std::string> ProcessCrlDistributionPoints(
    bssl::der::Input extension_data) {
  std::vector<bssl::ParsedDistributionPoint> distribution_points;
  if (!ParseCrlDistributionPoints(extension_data, &distribution_points))
    return std::nullopt;

  //    ReasonFlags ::= BIT STRING {
  static const int kReasonStrings[] = {
      //         unused                  (0),
      IDS_CERT_REVOCATION_REASON_UNUSED,
      //         keyCompromise           (1),
      IDS_CERT_REVOCATION_REASON_KEY_COMPROMISE,
      //         cACompromise            (2),
      IDS_CERT_REVOCATION_REASON_CA_COMPROMISE,
      //         affiliationChanged      (3),
      IDS_CERT_REVOCATION_REASON_AFFILIATION_CHANGED,
      //         superseded              (4),
      IDS_CERT_REVOCATION_REASON_SUPERSEDED,
      //         cessationOfOperation    (5),
      IDS_CERT_REVOCATION_REASON_CESSATION_OF_OPERATION,
      //         certificateHold         (6),
      IDS_CERT_REVOCATION_REASON_CERTIFICATE_HOLD,
      // These aren't included as they would be challenging to translate and
      // are irrelevant for a web browser. (Actually all of these are
      // kinda irrelevant as we don't support CRL reasons.)
      //         privilegeWithdrawn      (7),
      //         aACompromise            (8) }
  };

  std::string rv;
  for (const auto& dp : distribution_points) {
    if (dp.distribution_point_fullname) {
      std::optional<std::string> s =
          ProcessGeneralNames(*dp.distribution_point_fullname);
      if (!s)
        return std::nullopt;
      rv += *s;
    }

    if (dp.distribution_point_name_relative_to_crl_issuer) {
      bssl::RelativeDistinguishedName name_relative_to_crl_issuer;
      bssl::der::Parser rdnParser(
          *dp.distribution_point_name_relative_to_crl_issuer);
      if (!bssl::ReadRdn(&rdnParser, &name_relative_to_crl_issuer)) {
        return std::nullopt;
      }
      std::string s = ProcessRDN(name_relative_to_crl_issuer);
      if (s.empty())
        return std::nullopt;
      rv += s;
    }

    if (dp.reasons) {
      std::optional<std::string> s =
          ProcessBitStringValue(*dp.reasons, kReasonStrings, ',');
      if (!s)
        return std::nullopt;
      rv += *s + '\n';
    }

    if (dp.crl_issuer) {
      bssl::CertErrors unused_errors;
      auto crl_issuer =
          bssl::GeneralNames::CreateFromValue(*dp.crl_issuer, &unused_errors);
      if (!crl_issuer)
        return std::nullopt;
      std::optional<std::string> s = ProcessGeneralNames(*crl_issuer);
      if (!s)
        return std::nullopt;
      rv += l10n_util::GetStringFUTF8(IDS_CERT_ISSUER_FORMAT,
                                      base::UTF8ToUTF16(*s));
    }
  }

  return rv;
}

std::optional<std::string> ProcessAuthorityInfoAccess(
    bssl::der::Input extension_data) {
  std::vector<bssl::AuthorityInfoAccessDescription> access_descriptions;
  if (!bssl::ParseAuthorityInfoAccess(extension_data, &access_descriptions)) {
    return std::nullopt;
  }

  std::string rv;
  for (const auto& access_description : access_descriptions) {
    bssl::GeneralNames name;
    bssl::CertErrors unused_errors;
    if (!bssl::ParseGeneralName(access_description.access_location,
                                bssl::GeneralNames::IP_ADDRESS_ONLY, &name,
                                &unused_errors)) {
      return std::nullopt;
    }

    std::optional<std::string> s = ProcessGeneralNames(name);
    if (!s)
      return std::nullopt;
    std::u16string location_str = base::UTF8ToUTF16(*s);
    if (access_description.access_method_oid ==
        bssl::der::Input(bssl::kAdOcspOid)) {
      rv += l10n_util::GetStringFUTF8(IDS_CERT_OCSP_RESPONDER_FORMAT,
                                      location_str);
    } else if (access_description.access_method_oid ==
               bssl::der::Input(bssl::kAdCaIssuersOid)) {
      rv += l10n_util::GetStringFUTF8(IDS_CERT_CA_ISSUERS_FORMAT, location_str);
    } else {
      rv += l10n_util::GetStringFUTF8(
          IDS_CERT_UNKNOWN_OID_INFO_FORMAT,
          base::UTF8ToUTF16(
              GetOidTextOrNumeric(access_description.access_method_oid)),
          location_str);
    }
  }

  return rv;
}

bool ParseSubjectPublicKeyInfo(bssl::der::Input spki_tlv,
                               bssl::der::Input* algorithm_tlv,
                               bssl::der::Input* subject_public_key_value) {
  bssl::der::Parser spki_parser(spki_tlv);

  //    SubjectPublicKeyInfo  ::=  SEQUENCE  {
  //         algorithm            AlgorithmIdentifier,
  //         subjectPublicKey     BIT STRING  }
  bssl::der::Parser sequence_parser;
  if (!spki_parser.ReadSequence(&sequence_parser))
    return false;

  if (!sequence_parser.ReadRawTLV(algorithm_tlv))
    return false;

  if (!sequence_parser.ReadTag(CBS_ASN1_BITSTRING, subject_public_key_value)) {
    return false;
  }

  if (sequence_parser.HasMore())
    return false;

  return true;
}

std::vector<uint8_t> BIGNUMBytes(const BIGNUM* bn) {
  std::vector<uint8_t> ret(BN_num_bytes(bn));
  BN_bn2bin(bn, ret.data());
  return ret;
}

std::optional<std::string> ProcessQcStatements(
    bssl::der::Input extension_data) {
  std::optional<std::vector<net::QcStatement>> qc_statements =
      net::ParseQcStatements(extension_data);
  if (!qc_statements.has_value()) {
    return std::nullopt;
  }

  std::string rv;
  for (const auto& statement : qc_statements.value()) {
    rv += GetOidTextOrNumeric(statement.id);
    if (!statement.info.empty()) {
      rv += " = ";
      // The `statement.info` is dependent on the `id`, so `info` can only be
      // processed for known ids. Otherwise the raw bytes of `info` are shown.
      if (statement.id == bssl::der::Input(net::kEtsiQcsQcTypeOid)) {
        std::optional<std::vector<bssl::der::Input>> qc_types =
            net::ParseQcTypeInfo(statement.info);
        if (!qc_types.has_value()) {
          return std::nullopt;
        }
        std::string sep;
        for (const auto& qc_type_id : qc_types.value()) {
          rv += sep;
          rv += GetOidTextOrNumeric(qc_type_id);
          sep = ", ";
        }
      } else {
        rv += ProcessRawBytes(statement.info);
      }
    }
    rv += "\n";
  }
  return rv;
}

}  // namespace

X509CertificateModel::X509CertificateModel(
    bssl::UniquePtr<CRYPTO_BUFFER> cert_data)
    : X509CertificateModelBase(std::move(cert_data)) {}

X509CertificateModel::X509CertificateModel(X509CertificateModel&& other) =
    default;

X509CertificateModel::~X509CertificateModel() = default;

OptionalStringOrError X509CertificateModel::GetIssuerName() const {
  CHECK(is_valid());
  return RDNSequenceToStringMultiLine(issuer_rdns_);
}

OptionalStringOrError X509CertificateModel::GetSubjectName() const {
  CHECK(is_valid());
  return RDNSequenceToStringMultiLine(subject_rdns_);
}

std::vector<Extension> X509CertificateModel::GetExtensions(
    std::string_view critical_label,
    std::string_view non_critical_label) const {
  CHECK(is_valid());
  std::vector<Extension> extensions;
  for (const auto& extension : extensions_) {
    Extension processed_extension;
    processed_extension.name = GetOidTextOrNumeric(extension.oid);
    processed_extension.value =
        ProcessExtension(critical_label, non_critical_label, extension);
    extensions.push_back(processed_extension);
  }
  return extensions;
}

std::string X509CertificateModel::ProcessExtension(
    std::string_view critical_label,
    std::string_view non_critical_label,
    const bssl::ParsedExtension& extension) const {
  std::string_view criticality =
      extension.critical ? critical_label : non_critical_label;
  std::optional<std::string> processed_extension =
      ProcessExtensionData(extension);
  return base::StrCat(
      {criticality, "\n",
       (processed_extension
            ? *processed_extension
            : l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR))});
}

std::optional<std::string> X509CertificateModel::ProcessExtensionData(
    const bssl::ParsedExtension& extension) const {
  if (extension.oid == bssl::der::Input(kNetscapeCertificateTypeOid)) {
    return ProcessNSCertTypeExtension(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kKeyUsageOid)) {
    return ProcessKeyUsageExtension(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kBasicConstraintsOid)) {
    return ProcessBasicConstraints(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kExtKeyUsageOid)) {
    return ProcessExtKeyUsage(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kSubjectAltNameOid)) {
    // The subjectAltName extension was already parsed in the constructor, use
    // that rather than parse it again.
    CHECK(subject_alt_names_);
    return ProcessGeneralNames(*subject_alt_names_);
  }
  if (extension.oid == bssl::der::Input(kIssuerAltNameOid)) {
    return ProcessGeneralNamesTlv(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kSubjectKeyIdentifierOid)) {
    return ProcessSubjectKeyId(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kAuthorityKeyIdentifierOid)) {
    return ProcessAuthorityKeyId(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kCertificatePoliciesOid)) {
    return ProcessCertificatePolicies(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kCrlDistributionPointsOid)) {
    return ProcessCrlDistributionPoints(extension.value);
  }
  if (extension.oid == bssl::der::Input(bssl::kAuthorityInfoAccessOid)) {
    return ProcessAuthorityInfoAccess(extension.value);
  }
  if (extension.oid == bssl::der::Input(kNetscapeBaseURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeRevocationURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeCARevocationURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeCACertURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeRenewalURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeCAPolicyURLOid) ||
      extension.oid == bssl::der::Input(kNetscapeSSLServerNameOid) ||
      extension.oid == bssl::der::Input(kNetscapeCommentOid) ||
      extension.oid == bssl::der::Input(kNetscapeLostPasswordURLOid)) {
    return ProcessIA5String(extension.value);
  }
  if (extension.oid == bssl::der::Input(net::kQcStatementsOid)) {
    return ProcessQcStatements(extension.value);
  }
  // TODO(crbug.com/41395047): SCT
  // TODO(mattm): name constraints
  // TODO(mattm): policy mappings
  // TODO(mattm): policy constraints
  return ProcessRawBytes(extension.value);
}

std::string X509CertificateModel::ProcessSubjectPublicKeyInfo() const {
  CHECK(is_valid());
  std::string rv = ProcessRawSubjectPublicKeyInfo(tbs_.spki_tlv);
  if (rv.empty())
    return std::string();
  return rv;
}

std::string X509CertificateModel::ProcessRawBitsSignatureWrap() const {
  CHECK(is_valid());
  return ProcessRawBytes(signature_value_.bytes());
}

// TODO(crbug.com/41453265): move to anonymous namespace once
// x509_certificate_model_nss is removed.
std::string ProcessIDN(const std::string& input) {
  if (!base::IsStringASCII(input))
    return input;

  // Convert the ASCII input to a string16 for ICU.
  std::u16string input16;
  input16.reserve(input.length());
  input16.insert(input16.end(), input.begin(), input.end());

  std::u16string output16 = url_formatter::IDNToUnicode(input);
  if (input16 == output16)
    return input;  // Input did not contain any encoded data.

  // Input contained encoded data, return formatted string showing original and
  // decoded forms.
  return l10n_util::GetStringFUTF8(IDS_CERT_INFO_IDN_VALUE_FORMAT, input16,
                                   output16);
}

std::string ProcessRawSubjectPublicKeyInfo(base::span<const uint8_t> spki_der) {
  bssl::UniquePtr<EVP_PKEY> public_key;
  if (!bssl::ParsePublicKey(bssl::der::Input(spki_der), &public_key)) {
    return std::string();
  }
  switch (EVP_PKEY_id(public_key.get())) {
    case EVP_PKEY_RSA: {
      RSA* rsa = EVP_PKEY_get0_RSA(public_key.get());
      // EVP_PKEY_get0_RSA can only fail if the type was wrong, which was just
      // checked in the switch.
      CHECK(rsa);
      const BIGNUM* modulus = RSA_get0_n(rsa);
      const BIGNUM* public_exponent = RSA_get0_e(rsa);
      CHECK(modulus);
      CHECK(public_exponent);

      return l10n_util::GetStringFUTF8(
          IDS_CERT_RSA_PUBLIC_KEY_DUMP_FORMAT,
          base::NumberToString16(BN_num_bits(modulus)),
          base::UTF8ToUTF16(ProcessRawBytes(BIGNUMBytes(modulus))),
          base::NumberToString16(BN_num_bits(public_exponent)),
          base::UTF8ToUTF16(ProcessRawBytes(BIGNUMBytes(public_exponent))));
    }
      // TODO(mattm): handle other key types? (eg EVP_PKEY_EC)
  }

  bssl::der::Input unused_algorithm_tlv;
  bssl::der::Input subject_public_key_value;
  if (!ParseSubjectPublicKeyInfo(bssl::der::Input(spki_der),
                                 &unused_algorithm_tlv,
                                 &subject_public_key_value)) {
    return std::string();
  }
  return ProcessRawBytes(subject_public_key_value);
}

}  // namespace x509_certificate_model
