Introduce basic X.509 parser into QUIC.

Currently, this only extracts subject, SPKI and SANs.  More interesting features will be added later.  The end goal is to replace most of the quic/platform APIs that deal with certificates.

gfe-relnote: n/a (code not used)
PiperOrigin-RevId: 294762954
Change-Id: Iee6038f0b50c04b78c832c3b0d7a7a14eeee9630
diff --git a/quic/core/crypto/certificate_view.cc b/quic/core/crypto/certificate_view.cc
new file mode 100644
index 0000000..dc95287
--- /dev/null
+++ b/quic/core/crypto/certificate_view.cc
@@ -0,0 +1,247 @@
+// Copyright 2020 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/third_party/quiche/src/quic/core/crypto/certificate_view.h"
+
+#include <cstdint>
+#include <memory>
+
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "net/third_party/quiche/src/quic/core/crypto/boring_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_optional.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+
+// The literals below were encoded using `ascii2der | xxd -i`.  The comments
+// above the literals are the contents in the der2ascii syntax.
+namespace {
+
+// X.509 version 3 (version numbering starts with zero).
+// INTEGER { 2 }
+constexpr uint8_t kX509Version[] = {0x02, 0x01, 0x02};
+
+// 2.5.29.17
+constexpr uint8_t kSubjectAltNameOid[] = {0x55, 0x1d, 0x11};
+
+}  // namespace
+
+namespace quic {
+
+std::unique_ptr<CertificateView> CertificateView::ParseSingleCertificate(
+    quiche::QuicheStringPiece certificate) {
+  std::unique_ptr<CertificateView> result(new CertificateView());
+  CBS top = StringPieceToCbs(certificate);
+
+  CBS top_certificate, tbs_certificate, signature_algorithm, signature;
+  if (!CBS_get_asn1(&top, &top_certificate, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&top) != 0) {
+    return nullptr;
+  }
+
+  // Certificate  ::=  SEQUENCE  {
+  if (
+      //   tbsCertificate       TBSCertificate,
+      !CBS_get_asn1(&top_certificate, &tbs_certificate, CBS_ASN1_SEQUENCE) ||
+
+      //   signatureAlgorithm   AlgorithmIdentifier,
+      !CBS_get_asn1(&top_certificate, &signature_algorithm,
+                    CBS_ASN1_SEQUENCE) ||
+
+      //   signature            BIT STRING  }
+      !CBS_get_asn1(&top_certificate, &signature, CBS_ASN1_BITSTRING) ||
+      CBS_len(&top_certificate) != 0) {
+    return nullptr;
+  }
+
+  int has_version, has_extensions;
+  CBS version, serial, signature_algorithm_inner, issuer, validity, subject,
+      spki, issuer_id, subject_id, extensions_outer;
+  // TBSCertificate  ::=  SEQUENCE  {
+  if (
+      //   version         [0]  Version DEFAULT v1,
+      !CBS_get_optional_asn1(
+          &tbs_certificate, &version, &has_version,
+          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+
+      //   serialNumber         CertificateSerialNumber,
+      !CBS_get_asn1(&tbs_certificate, &serial, CBS_ASN1_INTEGER) ||
+
+      //   signature            AlgorithmIdentifier,
+      !CBS_get_asn1(&tbs_certificate, &signature_algorithm_inner,
+                    CBS_ASN1_SEQUENCE) ||
+
+      //   issuer               Name,
+      !CBS_get_asn1(&tbs_certificate, &issuer, CBS_ASN1_SEQUENCE) ||
+
+      //   validity             Validity,
+      !CBS_get_asn1(&tbs_certificate, &validity, CBS_ASN1_SEQUENCE) ||
+
+      //   subject              Name,
+      !CBS_get_asn1(&tbs_certificate, &subject, CBS_ASN1_SEQUENCE) ||
+
+      //   subjectPublicKeyInfo SubjectPublicKeyInfo,
+      !CBS_get_asn1_element(&tbs_certificate, &spki, CBS_ASN1_SEQUENCE) ||
+
+      //   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+      //                        -- If present, version MUST be v2 or v3
+      !CBS_get_optional_asn1(&tbs_certificate, &issuer_id, nullptr,
+                             CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
+
+      //   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+      //                        -- If present, version MUST be v2 or v3
+      !CBS_get_optional_asn1(&tbs_certificate, &subject_id, nullptr,
+                             CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
+
+      //   extensions      [3]  Extensions OPTIONAL
+      //                        -- If present, version MUST be v3 --  }
+      !CBS_get_optional_asn1(
+          &tbs_certificate, &extensions_outer, &has_extensions,
+          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3) ||
+
+      CBS_len(&tbs_certificate) != 0) {
+    return nullptr;
+  }
+
+  result->public_key_.reset(EVP_parse_public_key(&spki));
+  if (result->public_key_ == nullptr) {
+    QUIC_DLOG(WARNING) << "Failed to parse the public key";
+    return nullptr;
+  }
+  if (!result->ValidatePublicKeyParameters()) {
+    QUIC_DLOG(WARNING) << "Public key has invalid parameters";
+    return nullptr;
+  }
+
+  // Only support X.509v3.
+  if (!has_version ||
+      !CBS_mem_equal(&version, kX509Version, sizeof(kX509Version))) {
+    QUIC_DLOG(WARNING) << "Bad X.509 version";
+    return nullptr;
+  }
+
+  if (!has_extensions) {
+    return nullptr;
+  }
+
+  CBS extensions;
+  if (!CBS_get_asn1(&extensions_outer, &extensions, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&extensions_outer) != 0) {
+    QUIC_DLOG(WARNING) << "Failed to extract the extension sequence";
+    return nullptr;
+  }
+  if (!result->ParseExtensions(extensions)) {
+    QUIC_DLOG(WARNING) << "Failed to parse extensions";
+    return nullptr;
+  }
+
+  return result;
+}
+
+bool CertificateView::ParseExtensions(CBS extensions) {
+  while (CBS_len(&extensions) != 0) {
+    CBS extension, oid, critical, payload;
+    if (
+        // Extension  ::=  SEQUENCE  {
+        !CBS_get_asn1(&extensions, &extension, CBS_ASN1_SEQUENCE) ||
+        //     extnID      OBJECT IDENTIFIER,
+        !CBS_get_asn1(&extension, &oid, CBS_ASN1_OBJECT) ||
+        //     critical    BOOLEAN DEFAULT FALSE,
+        !CBS_get_optional_asn1(&extension, &critical, nullptr,
+                               CBS_ASN1_BOOLEAN) ||
+        //     extnValue   OCTET STRING
+        //                 -- contains the DER encoding of an ASN.1 value
+        //                 -- corresponding to the extension type identified
+        //                 -- by extnID
+        !CBS_get_asn1(&extension, &payload, CBS_ASN1_OCTETSTRING) ||
+        CBS_len(&extension) != 0) {
+      QUIC_DLOG(WARNING) << "Bad extension entry";
+      return false;
+    }
+
+    if (CBS_mem_equal(&oid, kSubjectAltNameOid, sizeof(kSubjectAltNameOid))) {
+      CBS alt_names;
+      if (!CBS_get_asn1(&payload, &alt_names, CBS_ASN1_SEQUENCE) ||
+          CBS_len(&payload) != 0) {
+        QUIC_DLOG(WARNING) << "Failed to parse subjectAltName";
+        return false;
+      }
+      while (CBS_len(&alt_names) != 0) {
+        CBS alt_name_cbs;
+        unsigned int alt_name_tag;
+        if (!CBS_get_any_asn1(&alt_names, &alt_name_cbs, &alt_name_tag)) {
+          QUIC_DLOG(WARNING) << "Failed to parse subjectAltName";
+          return false;
+        }
+
+        quiche::QuicheStringPiece alt_name = CbsToStringPiece(alt_name_cbs);
+        QuicIpAddress ip_address;
+        // GeneralName ::= CHOICE {
+        switch (alt_name_tag) {
+          // dNSName                   [2]  IA5String,
+          case CBS_ASN1_CONTEXT_SPECIFIC | 2:
+            subject_alt_name_domains_.push_back(alt_name);
+            break;
+
+          // iPAddress                 [7]  OCTET STRING,
+          case CBS_ASN1_CONTEXT_SPECIFIC | 7:
+            if (!ip_address.FromPackedString(alt_name.data(),
+                                             alt_name.size())) {
+              QUIC_DLOG(WARNING) << "Failed to parse subjectAltName IP address";
+              return false;
+            }
+            subject_alt_name_ips_.push_back(ip_address);
+            break;
+
+          default:
+            QUIC_DLOG(WARNING) << "Invalid subjectAltName tag";
+            return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool CertificateView::ValidatePublicKeyParameters() {
+  // The profile here affects what certificates can be used:
+  // (1) when QUIC is used as a server library without any custom certificate
+  //     provider logic,
+  // (2) when QuicTransport is handling self-signed certificates.
+  // The goal is to allow at minimum any certificate that would be allowed on a
+  // regular Web session over TLS 1.3 while ensuring we do not expose any
+  // algorithms we don't want to support long-term.
+  switch (EVP_PKEY_id(public_key_.get())) {
+    case EVP_PKEY_RSA:
+      return EVP_PKEY_bits(public_key_.get()) >= 2048;
+    case EVP_PKEY_EC: {
+      const EC_KEY* key = EVP_PKEY_get0_EC_KEY(public_key_.get());
+      if (key == nullptr) {
+        return false;
+      }
+      const EC_GROUP* group = EC_KEY_get0_group(key);
+      if (group == nullptr) {
+        return false;
+      }
+      const int curve_nid = EC_GROUP_get_curve_name(group);
+      switch (curve_nid) {
+        case NID_X9_62_prime256v1:
+        case NID_secp384r1:
+          return true;
+        default:
+          return false;
+      }
+    }
+    case EVP_PKEY_ED25519:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace quic