// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cert/asn1_util.h"

#include "net/der/input.h"
#include "net/der/parser.h"

namespace net {

namespace asn1 {

namespace {

// Parses input |in| which should point to the beginning of a Certificate, and
// sets |*tbs_certificate| ready to parse the SubjectPublicKeyInfo. If parsing
// fails, this function returns false and |*tbs_certificate| is left in an
// undefined state.
bool SeekToSPKI(der::Input in, der::Parser* tbs_certificate) {
  // From RFC 5280, section 4.1
  //    Certificate  ::=  SEQUENCE  {
  //      tbsCertificate       TBSCertificate,
  //      signatureAlgorithm   AlgorithmIdentifier,
  //      signatureValue       BIT STRING  }

  // TBSCertificate  ::=  SEQUENCE  {
  //      version         [0]  EXPLICIT Version DEFAULT v1,
  //      serialNumber         CertificateSerialNumber,
  //      signature            AlgorithmIdentifier,
  //      issuer               Name,
  //      validity             Validity,
  //      subject              Name,
  //      subjectPublicKeyInfo SubjectPublicKeyInfo,
  //      ... }

  der::Parser parser(in);
  der::Parser certificate;
  if (!parser.ReadSequence(&certificate))
    return false;

  // We don't allow junk after the certificate.
  if (parser.HasMore())
    return false;

  if (!certificate.ReadSequence(tbs_certificate))
    return false;

  bool unused;
  if (!tbs_certificate->SkipOptionalTag(
          der::kTagConstructed | der::kTagContextSpecific | 0, &unused)) {
    return false;
  }

  // serialNumber
  if (!tbs_certificate->SkipTag(der::kInteger))
    return false;
  // signature
  if (!tbs_certificate->SkipTag(der::kSequence))
    return false;
  // issuer
  if (!tbs_certificate->SkipTag(der::kSequence))
    return false;
  // validity
  if (!tbs_certificate->SkipTag(der::kSequence))
    return false;
  // subject
  if (!tbs_certificate->SkipTag(der::kSequence))
    return false;
  return true;
}

}  // namespace

bool ExtractSPKIFromDERCert(base::StringPiece cert,
                            base::StringPiece* spki_out) {
  der::Parser parser;
  if (!SeekToSPKI(der::Input(cert), &parser))
    return false;
  der::Input spki;
  if (!parser.ReadRawTLV(&spki))
    return false;
  *spki_out = spki.AsStringPiece();
  return true;
}

bool ExtractSubjectPublicKeyFromSPKI(base::StringPiece spki,
                                     base::StringPiece* spk_out) {
  // From RFC 5280, Section 4.1
  //   SubjectPublicKeyInfo  ::=  SEQUENCE  {
  //     algorithm            AlgorithmIdentifier,
  //     subjectPublicKey     BIT STRING  }
  //
  //   AlgorithmIdentifier  ::=  SEQUENCE  {
  //     algorithm               OBJECT IDENTIFIER,
  //     parameters              ANY DEFINED BY algorithm OPTIONAL  }

  // Step into SubjectPublicKeyInfo sequence.
  der::Parser parser((der::Input(spki)));
  der::Parser spki_parser;
  if (!parser.ReadSequence(&spki_parser))
    return false;

  // Step over algorithm field (a SEQUENCE).
  if (!spki_parser.SkipTag(der::kSequence))
    return false;

  // Extract the subjectPublicKey field.
  der::Input spk;
  if (!spki_parser.ReadTag(der::kBitString, &spk))
    return false;
  *spk_out = spk.AsStringPiece();
  return true;
}


bool ExtractCRLURLsFromDERCert(base::StringPiece cert,
                               std::vector<base::StringPiece>* urls_out) {
  urls_out->clear();
  std::vector<base::StringPiece> tmp_urls_out;

  bool present;
  der::Parser tbs_cert_parser;
  if (!SeekToSPKI(der::Input(cert), &tbs_cert_parser))
    return false;

  // From RFC 5280, section 4.1
  // TBSCertificate  ::=  SEQUENCE  {
  //      ...
  //      subjectPublicKeyInfo SubjectPublicKeyInfo,
  //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
  //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
  //      extensions      [3]  EXPLICIT Extensions OPTIONAL }

  // subjectPublicKeyInfo
  if (!tbs_cert_parser.SkipTag(der::kSequence))
    return false;
  // issuerUniqueID
  if (!tbs_cert_parser.SkipOptionalTag(
          der::kTagConstructed | der::kTagContextSpecific | 1, &present)) {
    return false;
  }
  // subjectUniqueID
  if (!tbs_cert_parser.SkipOptionalTag(
          der::kTagConstructed | der::kTagContextSpecific | 2, &present)) {
    return false;
  }

  der::Input extensions;
  if (!tbs_cert_parser.ReadOptionalTag(
          der::kTagConstructed | der::kTagContextSpecific | 3, &extensions,
          &present)) {
    return false;
  }

  if (!present)
    return true;

  // Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
  // Extension   ::=  SEQUENCE  {
  //      extnID      OBJECT IDENTIFIER,
  //      critical    BOOLEAN DEFAULT FALSE,
  //      extnValue   OCTET STRING }

  // |extensions| was EXPLICITly tagged, so we still need to remove the
  // ASN.1 SEQUENCE header.
  der::Parser explicit_extensions_parser(extensions);
  der::Parser extensions_parser;
  if (!explicit_extensions_parser.ReadSequence(&extensions_parser))
    return false;

  if (explicit_extensions_parser.HasMore())
    return false;

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

    der::Input oid;
    if (!extension_parser.ReadTag(der::kOid, &oid))
      return false;

    // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509
    // CRL Distribution Points extension.
    static const uint8_t kCRLDistributionPointsOID[] = {0x55, 0x1d, 0x1f};

    if (oid != der::Input(kCRLDistributionPointsOID))
      continue;

    // critical
    if (!extension_parser.SkipOptionalTag(der::kBool, &present))
      return false;

    // extnValue
    der::Input extension_value;
    if (!extension_parser.ReadTag(der::kOctetString, &extension_value))
      return false;

    // RFC 5280, section 4.2.1.13.
    //
    // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
    //
    // DistributionPoint ::= SEQUENCE {
    //  distributionPoint       [0]     DistributionPointName OPTIONAL,
    //  reasons                 [1]     ReasonFlags OPTIONAL,
    //  cRLIssuer               [2]     GeneralNames OPTIONAL }

    der::Parser extension_value_parser(extension_value);
    der::Parser distribution_points_parser;
    if (!extension_value_parser.ReadSequence(&distribution_points_parser))
      return false;
    if (extension_value_parser.HasMore())
      return false;

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

      der::Input name;
      if (!distrib_point_parser.ReadOptionalTag(
              der::kTagContextSpecific | der::kTagConstructed | 0, &name,
              &present)) {
        return false;
      }
      // If it doesn't contain a name then we skip it.
      if (!present)
        continue;

      if (!distrib_point_parser.SkipOptionalTag(der::kTagContextSpecific | 1,
                                                &present)) {
        return false;
      }
      // If it contains a subset of reasons then we skip it. We aren't
      // interested in subsets of CRLs and the RFC states that there MUST be
      // a CRL that covers all reasons.
      if (present)
        continue;

      if (!distrib_point_parser.SkipOptionalTag(
              der::kTagContextSpecific | der::kTagConstructed | 2, &present)) {
        return false;
      }
      // If it contains a alternative issuer, then we skip it.
      if (present)
        continue;

      // DistributionPointName ::= CHOICE {
      //   fullName                [0]     GeneralNames,
      //   nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
      der::Input general_names;
      if (!der::Parser(name).ReadOptionalTag(
              der::kTagContextSpecific | der::kTagConstructed | 0,
              &general_names, &present)) {
        return false;
      }
      if (!present)
        continue;

      // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
      // GeneralName ::= CHOICE {
      //   ...
      //   uniformResourceIdentifier [6]  IA5String,
      //   ... }
      der::Parser general_names_parser(general_names);
      while (general_names_parser.HasMore()) {
        der::Input url;
        if (!general_names_parser.ReadOptionalTag(der::kTagContextSpecific | 6,
                                                  &url, &present)) {
          return false;
        }
        if (present) {
          // This does not validate that |url| is a valid IA5String.
          tmp_urls_out.push_back(url.AsStringPiece());
        } else {
          der::Tag unused_tag;
          der::Input unused_value;
          if (!general_names_parser.ReadTagAndValue(&unused_tag,
                                                    &unused_value)) {
            return false;
          }
        }
      }
    }
  }

  urls_out->swap(tmp_urls_out);
  return true;
}

} // namespace asn1

} // namespace net