| // 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 "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, ""), absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.001Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Q"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024-0500"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "700101000024ZZ"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.00Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "197O0101000024Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.0O1Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "-9700101000024Z"), | |
| absl::nullopt); | |
| EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "1970-101000024Z"), | |
| absl::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"), | |
| absl::nullopt); | |
| } | |
| TEST(CertificateViewTest, NameAttribute) { | |
| // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.112411 } | |
| // UTF8String { "Test" } | |
| std::string unknown_oid = | |
| absl::HexStringToBytes("060b2a864886f712040186ee1b0c0454657374"); | |
| 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 = | |
| absl::HexStringToBytes("06035504030c0742656c6c3a2007"); | |
| EXPECT_EQ(R"(CN=Bell: \x07)", | |
| X509NameAttributeToString(StringPieceToCbs(non_printable))); | |
| // OBJECT_IDENTIFIER { "\x55\x80" } | |
| // UTF8String { "Test" } | |
| std::string invalid_oid = absl::HexStringToBytes("060255800c0454657374"); | |
| 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 |