| // 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/web_transport_fingerprint_proof_verifier.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/string_view.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/quic/core/quic_utils.h" |
| #include "quiche/quic/platform/api/quic_test.h" |
| #include "quiche/quic/test_tools/mock_clock.h" |
| #include "quiche/quic/test_tools/test_certificates.h" |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| using ::testing::HasSubstr; |
| |
| // 2020-02-01 12:35:56 UTC |
| constexpr QuicTime::Delta kValidTime = QuicTime::Delta::FromSeconds(1580560556); |
| |
| struct VerifyResult { |
| QuicAsyncStatus status; |
| WebTransportFingerprintProofVerifier::Status detailed_status; |
| std::string error; |
| }; |
| |
| class WebTransportFingerprintProofVerifierTest : public QuicTest { |
| public: |
| WebTransportFingerprintProofVerifierTest() { |
| clock_.AdvanceTime(kValidTime); |
| verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( |
| &clock_, /*max_validity_days=*/365); |
| AddTestCertificate(); |
| } |
| |
| protected: |
| VerifyResult Verify(absl::string_view certificate) { |
| VerifyResult result; |
| std::unique_ptr<ProofVerifyDetails> details; |
| uint8_t tls_alert; |
| result.status = verifier_->VerifyCertChain( |
| /*hostname=*/"", /*port=*/0, |
| std::vector<std::string>{std::string(certificate)}, |
| /*ocsp_response=*/"", |
| /*cert_sct=*/"", |
| /*context=*/nullptr, &result.error, &details, &tls_alert, |
| /*callback=*/nullptr); |
| result.detailed_status = |
| static_cast<WebTransportFingerprintProofVerifier::Details*>( |
| details.get()) |
| ->status(); |
| return result; |
| } |
| |
| void AddTestCertificate() { |
| EXPECT_TRUE(verifier_->AddFingerprint(WebTransportHash{ |
| WebTransportHash::kSha256, RawSha256(kTestCertificate)})); |
| } |
| |
| MockClock clock_; |
| std::unique_ptr<WebTransportFingerprintProofVerifier> verifier_; |
| }; |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, Sha256Fingerprint) { |
| // Computed using `openssl x509 -fingerprint -sha256`. |
| EXPECT_EQ(absl::BytesToHexString(RawSha256(kTestCertificate)), |
| "f2e5465e2bf7ecd6f63066a5a37511734aa0eb7c4701" |
| "0e86d6758ed4f4fa1b0f"); |
| } |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, SimpleFingerprint) { |
| VerifyResult result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_SUCCESS); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kValidCertificate); |
| |
| result = Verify(kWildcardCertificate); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kUnknownFingerprint); |
| |
| result = Verify("Some random text"); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| } |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, Validity) { |
| // Validity periods of kTestCertificate, according to `openssl x509 -text`: |
| // Not Before: Jan 30 18:13:59 2020 GMT |
| // Not After : Feb 2 18:13:59 2020 GMT |
| |
| // 2020-01-29 19:00:00 UTC |
| constexpr QuicTime::Delta kStartTime = |
| QuicTime::Delta::FromSeconds(1580324400); |
| clock_.Reset(); |
| clock_.AdvanceTime(kStartTime); |
| |
| VerifyResult result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kExpired); |
| |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400)); |
| result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_SUCCESS); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kValidCertificate); |
| |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(4 * 86400)); |
| result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kExpired); |
| } |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, MaxValidity) { |
| verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( |
| &clock_, /*max_validity_days=*/2); |
| AddTestCertificate(); |
| VerifyResult result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kExpiryTooLong); |
| EXPECT_THAT(result.error, HasSubstr("limit of 2 days")); |
| |
| // kTestCertificate is valid for exactly four days. |
| verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( |
| &clock_, /*max_validity_days=*/4); |
| AddTestCertificate(); |
| result = Verify(kTestCertificate); |
| EXPECT_EQ(result.status, QUIC_SUCCESS); |
| EXPECT_EQ(result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kValidCertificate); |
| } |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, InvalidCertificate) { |
| constexpr absl::string_view kInvalidCertificate = "Hello, world!"; |
| ASSERT_TRUE(verifier_->AddFingerprint(WebTransportHash{ |
| WebTransportHash::kSha256, RawSha256(kInvalidCertificate)})); |
| |
| VerifyResult result = Verify(kInvalidCertificate); |
| EXPECT_EQ(result.status, QUIC_FAILURE); |
| EXPECT_EQ( |
| result.detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kCertificateParseFailure); |
| } |
| |
| TEST_F(WebTransportFingerprintProofVerifierTest, AddCertificate) { |
| // Accept all-uppercase fingerprints. |
| verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( |
| &clock_, /*max_validity_days=*/365); |
| EXPECT_TRUE(verifier_->AddFingerprint(CertificateFingerprint{ |
| CertificateFingerprint::kSha256, |
| "F2:E5:46:5E:2B:F7:EC:D6:F6:30:66:A5:A3:75:11:73:4A:A0:EB:" |
| "7C:47:01:0E:86:D6:75:8E:D4:F4:FA:1B:0F"})); |
| EXPECT_EQ(Verify(kTestCertificate).detailed_status, |
| WebTransportFingerprintProofVerifier::Status::kValidCertificate); |
| |
| // Reject unknown hash algorithms. |
| EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{ |
| "sha-1", "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"})); |
| // Reject invalid length. |
| EXPECT_FALSE(verifier_->AddFingerprint( |
| CertificateFingerprint{CertificateFingerprint::kSha256, "00:00:00:00"})); |
| // Reject missing colons. |
| EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{ |
| CertificateFingerprint::kSha256, |
| "00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00." |
| "00.00.00.00.00.00.00.00.00.00.00.00.00"})); |
| // Reject non-hex symbols. |
| EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{ |
| CertificateFingerprint::kSha256, |
| "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:" |
| "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz"})); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace quic |