| // 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 "quiche/quic/core/crypto/certificate_view.h" | 
 |  | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include "absl/algorithm/container.h" | 
 | #include "absl/strings/escaping.h" | 
 | #include "absl/strings/string_view.h" | 
 | #include "openssl/base.h" | 
 | #include "openssl/bytestring.h" | 
 | #include "openssl/evp.h" | 
 | #include "openssl/ssl.h" | 
 | #include "quiche/quic/core/crypto/boring_utils.h" | 
 | #include "quiche/quic/core/quic_time.h" | 
 | #include "quiche/quic/platform/api/quic_ip_address.h" | 
 | #include "quiche/quic/platform/api/quic_test.h" | 
 | #include "quiche/quic/test_tools/test_certificates.h" | 
 | #include "quiche/common/platform/api/quiche_time_utils.h" | 
 |  | 
 | namespace quic { | 
 | namespace test { | 
 | namespace { | 
 |  | 
 | using ::testing::ElementsAre; | 
 | using ::testing::HasSubstr; | 
 | using ::testing::Optional; | 
 |  | 
 | TEST(CertificateViewTest, PemParser) { | 
 |   std::stringstream stream(kTestCertificatePem); | 
 |   PemReadResult result = ReadNextPemMessage(&stream); | 
 |   EXPECT_EQ(result.status, PemReadResult::kOk); | 
 |   EXPECT_EQ(result.type, "CERTIFICATE"); | 
 |   EXPECT_EQ(result.contents, kTestCertificate); | 
 |  | 
 |   result = ReadNextPemMessage(&stream); | 
 |   EXPECT_EQ(result.status, PemReadResult::kEof); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, Parse) { | 
 |   std::unique_ptr<CertificateView> view = | 
 |       CertificateView::ParseSingleCertificate(kTestCertificate); | 
 |   ASSERT_TRUE(view != nullptr); | 
 |  | 
 |   EXPECT_THAT(view->subject_alt_name_domains(), | 
 |               ElementsAre(absl::string_view("www.example.org"), | 
 |                           absl::string_view("mail.example.org"), | 
 |                           absl::string_view("mail.example.com"))); | 
 |   EXPECT_THAT(view->subject_alt_name_ips(), | 
 |               ElementsAre(QuicIpAddress::Loopback4())); | 
 |   EXPECT_EQ(EVP_PKEY_id(view->public_key()), EVP_PKEY_RSA); | 
 |  | 
 |   const QuicWallTime validity_start = QuicWallTime::FromUNIXSeconds( | 
 |       *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 1, 30, 18, 13, 59)); | 
 |   EXPECT_EQ(view->validity_start(), validity_start); | 
 |   const QuicWallTime validity_end = QuicWallTime::FromUNIXSeconds( | 
 |       *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 2, 2, 18, 13, 59)); | 
 |   EXPECT_EQ(view->validity_end(), validity_end); | 
 |   EXPECT_EQ(view->public_key_type(), PublicKeyType::kRsa); | 
 |   EXPECT_EQ(PublicKeyTypeToString(view->public_key_type()), "RSA"); | 
 |  | 
 |   EXPECT_EQ("C=US,ST=California,L=Mountain View,O=QUIC Server,CN=127.0.0.1", | 
 |             view->GetHumanReadableSubject()); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, ParseCertWithUnknownSanType) { | 
 |   std::stringstream stream(kTestCertWithUnknownSanTypePem); | 
 |   PemReadResult result = ReadNextPemMessage(&stream); | 
 |   EXPECT_EQ(result.status, PemReadResult::kOk); | 
 |   EXPECT_EQ(result.type, "CERTIFICATE"); | 
 |  | 
 |   std::unique_ptr<CertificateView> view = | 
 |       CertificateView::ParseSingleCertificate(result.contents); | 
 |   EXPECT_TRUE(view != nullptr); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, PemSingleCertificate) { | 
 |   std::stringstream pem_stream(kTestCertificatePem); | 
 |   std::vector<std::string> chain = | 
 |       CertificateView::LoadPemFromStream(&pem_stream); | 
 |   EXPECT_THAT(chain, ElementsAre(kTestCertificate)); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, PemMultipleCertificates) { | 
 |   std::stringstream pem_stream(kTestCertificateChainPem); | 
 |   std::vector<std::string> chain = | 
 |       CertificateView::LoadPemFromStream(&pem_stream); | 
 |   EXPECT_THAT(chain, | 
 |               ElementsAre(kTestCertificate, HasSubstr("QUIC Server Root CA"))); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, PemNoCertificates) { | 
 |   std::stringstream pem_stream("one\ntwo\nthree\n"); | 
 |   std::vector<std::string> chain = | 
 |       CertificateView::LoadPemFromStream(&pem_stream); | 
 |   EXPECT_TRUE(chain.empty()); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, SignAndVerify) { | 
 |   std::unique_ptr<CertificatePrivateKey> key = | 
 |       CertificatePrivateKey::LoadFromDer(kTestCertificatePrivateKey); | 
 |   ASSERT_TRUE(key != nullptr); | 
 |  | 
 |   std::string data = "A really important message"; | 
 |   std::string signature = key->Sign(data, SSL_SIGN_RSA_PSS_RSAE_SHA256); | 
 |   ASSERT_FALSE(signature.empty()); | 
 |  | 
 |   std::unique_ptr<CertificateView> view = | 
 |       CertificateView::ParseSingleCertificate(kTestCertificate); | 
 |   ASSERT_TRUE(view != nullptr); | 
 |   EXPECT_TRUE(key->MatchesPublicKey(*view)); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       view->VerifySignature(data, signature, SSL_SIGN_RSA_PSS_RSAE_SHA256)); | 
 |   EXPECT_FALSE(view->VerifySignature("An unimportant message", signature, | 
 |                                      SSL_SIGN_RSA_PSS_RSAE_SHA256)); | 
 |   EXPECT_FALSE(view->VerifySignature(data, "Not a signature", | 
 |                                      SSL_SIGN_RSA_PSS_RSAE_SHA256)); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, PrivateKeyPem) { | 
 |   std::unique_ptr<CertificateView> view = | 
 |       CertificateView::ParseSingleCertificate(kTestCertificate); | 
 |   ASSERT_TRUE(view != nullptr); | 
 |  | 
 |   std::stringstream pem_stream(kTestCertificatePrivateKeyPem); | 
 |   std::unique_ptr<CertificatePrivateKey> pem_key = | 
 |       CertificatePrivateKey::LoadPemFromStream(&pem_stream); | 
 |   ASSERT_TRUE(pem_key != nullptr); | 
 |   EXPECT_TRUE(pem_key->MatchesPublicKey(*view)); | 
 |  | 
 |   std::stringstream legacy_stream(kTestCertificatePrivateKeyLegacyPem); | 
 |   std::unique_ptr<CertificatePrivateKey> legacy_key = | 
 |       CertificatePrivateKey::LoadPemFromStream(&legacy_stream); | 
 |   ASSERT_TRUE(legacy_key != nullptr); | 
 |   EXPECT_TRUE(legacy_key->MatchesPublicKey(*view)); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, PrivateKeyEcdsaPem) { | 
 |   std::stringstream pem_stream(kTestEcPrivateKeyLegacyPem); | 
 |   std::unique_ptr<CertificatePrivateKey> key = | 
 |       CertificatePrivateKey::LoadPemFromStream(&pem_stream); | 
 |   ASSERT_TRUE(key != nullptr); | 
 |   EXPECT_TRUE(key->ValidForSignatureAlgorithm(SSL_SIGN_ECDSA_SECP256R1_SHA256)); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, DerTime) { | 
 |   EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Z"), | 
 |               Optional(QuicWallTime::FromUNIXSeconds(24))); | 
 |   EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19710101000024Z"), | 
 |               Optional(QuicWallTime::FromUNIXSeconds(365 * 86400 + 24))); | 
 |   EXPECT_THAT(ParseDerTime(CBS_ASN1_UTCTIME, "700101000024Z"), | 
 |               Optional(QuicWallTime::FromUNIXSeconds(24))); | 
 |   EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "200101000024Z").has_value()); | 
 |  | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, ""), std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.001Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Q"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024-0500"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "700101000024ZZ"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.00Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "197O0101000024Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.0O1Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "-9700101000024Z"), | 
 |             std::nullopt); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "1970-101000024Z"), | 
 |             std::nullopt); | 
 |  | 
 |   EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "490101000024Z").has_value()); | 
 |   // This should parse as 1950, which predates UNIX epoch. | 
 |   EXPECT_FALSE(ParseDerTime(CBS_ASN1_UTCTIME, "500101000024Z").has_value()); | 
 |  | 
 |   EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101230000Z"), | 
 |               Optional(QuicWallTime::FromUNIXSeconds(23 * 3600))); | 
 |   EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101240000Z"), | 
 |             std::nullopt); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, NameAttribute) { | 
 |   // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.112411 } | 
 |   // UTF8String { "Test" } | 
 |   std::string unknown_oid; | 
 |   ASSERT_TRUE(absl::HexStringToBytes("060b2a864886f712040186ee1b0c0454657374", | 
 |                                      &unknown_oid)); | 
 |   EXPECT_EQ("1.2.840.113554.4.1.112411=Test", | 
 |             X509NameAttributeToString(StringPieceToCbs(unknown_oid))); | 
 |  | 
 |   // OBJECT_IDENTIFIER { 2.5.4.3 } | 
 |   // UTF8String { "Bell: \x07" } | 
 |   std::string non_printable; | 
 |   ASSERT_TRUE( | 
 |       absl::HexStringToBytes("06035504030c0742656c6c3a2007", &non_printable)); | 
 |   EXPECT_EQ(R"(CN=Bell: \x07)", | 
 |             X509NameAttributeToString(StringPieceToCbs(non_printable))); | 
 |  | 
 |   // OBJECT_IDENTIFIER { "\x55\x80" } | 
 |   // UTF8String { "Test" } | 
 |   std::string invalid_oid; | 
 |   ASSERT_TRUE(absl::HexStringToBytes("060255800c0454657374", &invalid_oid)); | 
 |   EXPECT_EQ("(5580)=Test", | 
 |             X509NameAttributeToString(StringPieceToCbs(invalid_oid))); | 
 | } | 
 |  | 
 | TEST(CertificateViewTest, SupportedSignatureAlgorithmsForQuicIsUpToDate) { | 
 |   QuicSignatureAlgorithmVector supported = | 
 |       SupportedSignatureAlgorithmsForQuic(); | 
 |   for (int i = 0; i < std::numeric_limits<uint16_t>::max(); i++) { | 
 |     uint16_t sigalg = static_cast<uint16_t>(i); | 
 |     PublicKeyType key_type = PublicKeyTypeFromSignatureAlgorithm(sigalg); | 
 |     if (absl::c_find(supported, sigalg) == supported.end()) { | 
 |       EXPECT_EQ(key_type, PublicKeyType::kUnknown); | 
 |     } else { | 
 |       EXPECT_NE(key_type, PublicKeyType::kUnknown); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace test | 
 | }  // namespace quic |