// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/pki/path_builder.h" #include #include "net/base/net_errors.h" #include "net/cert/pki/cert_issuer_source_static.h" #include "net/cert/pki/common_cert_errors.h" #include "net/cert/pki/crl.h" #include "net/cert/pki/parse_certificate.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/pki/simple_path_builder_delegate.h" #include "net/cert/pki/trust_store_in_memory.h" #include "net/cert/pki/verify_certificate_chain.h" #include "net/der/encode_values.h" #include "net/der/input.h" #include "third_party/boringssl/src/include/openssl/pool.h" #include "net/cert/pki/nist_pkits_unittest.h" constexpr int64_t kOneYear = 60 * 60 * 24 * 365; namespace net { namespace { class CrlCheckingPathBuilderDelegate : public SimplePathBuilderDelegate { public: CrlCheckingPathBuilderDelegate(const std::vector& der_crls, int64_t verify_time, int64_t max_age, size_t min_rsa_modulus_length_bits, DigestPolicy digest_policy) : SimplePathBuilderDelegate(min_rsa_modulus_length_bits, digest_policy), der_crls_(der_crls), verify_time_(verify_time), max_age_(max_age) {} void CheckPathAfterVerification(const CertPathBuilder& path_builder, CertPathBuilderResultPath* path) override { SimplePathBuilderDelegate::CheckPathAfterVerification(path_builder, path); if (!path->IsValid()) return; // It would be preferable if this test could use // CheckValidatedChainRevocation somehow, but that only supports getting // CRLs by http distributionPoints. So this just settles for writing a // little bit of wrapper code to test CheckCRL directly. const ParsedCertificateList& certs = path->certs; for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) { size_t i = certs.size() - reverse_i - 1; // Trust anchors bypass OCSP/CRL revocation checks. (The only way to // revoke trust anchors is via CRLSet or the built-in SPKI block list). if (reverse_i == 0 && path->last_cert_trust.IsTrustAnchor()) continue; // RFC 5280 6.3.3. [If the CRL was not specified in a distribution // point], assume a DP with both the reasons and the // cRLIssuer fields omitted and a distribution point // name of the certificate issuer. // Since this implementation only supports URI names in distribution // points, this means a default-initialized ParsedDistributionPoint is // sufficient. ParsedDistributionPoint fake_cert_dp; const ParsedDistributionPoint* cert_dp = &fake_cert_dp; // If the target cert does have a distribution point, use it. std::vector distribution_points; ParsedExtension crl_dp_extension; if (certs[i]->GetExtension(der::Input(kCrlDistributionPointsOid), &crl_dp_extension)) { ASSERT_TRUE(ParseCrlDistributionPoints(crl_dp_extension.value, &distribution_points)); // TODO(mattm): some test cases (some of the 4.14.* onlySomeReasons // tests)) have two CRLs and two distribution points, one point // corresponding to each CRL. Should select the matching point for // each CRL. (Doesn't matter currently since we don't support // reasons.) // Look for a DistributionPoint without reasons. for (const auto& dp : distribution_points) { if (!dp.reasons) { cert_dp = &dp; break; } } // If there were only DistributionPoints with reasons, just use the // first one. if (cert_dp == &fake_cert_dp && !distribution_points.empty()) cert_dp = &distribution_points[0]; } bool cert_good = false; for (const auto& der_crl : der_crls_) { CRLRevocationStatus crl_status = CheckCRL(der_crl, certs, i, *cert_dp, verify_time_, max_age_); if (crl_status == CRLRevocationStatus::REVOKED) { path->errors.GetErrorsForCert(i)->AddError( cert_errors::kCertificateRevoked); return; } if (crl_status == CRLRevocationStatus::GOOD) { cert_good = true; break; } } if (!cert_good) { // PKITS tests assume hard-fail revocation checking. // From PKITS 4.4: "When running the tests in this section, the // application should be configured in such a way that the // certification path is not accepted unless valid, up-to-date // revocation data is available for every certificate in the path." path->errors.GetErrorsForCert(i)->AddError( cert_errors::kUnableToCheckRevocation); } } } private: std::vector der_crls_; int64_t verify_time_; int64_t max_age_; }; class PathBuilderPkitsTestDelegate { public: static void RunTest(std::vector cert_ders, std::vector crl_ders, const PkitsTestInfo& orig_info) { PkitsTestInfo info = orig_info; ASSERT_FALSE(cert_ders.empty()); ParsedCertificateList certs; for (const std::string& der : cert_ders) { CertErrors errors; ASSERT_TRUE(ParsedCertificate::CreateAndAddToVector( bssl::UniquePtr( CRYPTO_BUFFER_new(reinterpret_cast(der.data()), der.size(), nullptr)), {}, &certs, &errors)) << errors.ToDebugString(); } // First entry in the PKITS chain is the trust anchor. // TODO(mattm): test with all possible trust anchors in the trust store? TrustStoreInMemory trust_store; trust_store.AddTrustAnchor(certs[0]); // TODO(mattm): test with other irrelevant certs in cert_issuer_sources? CertIssuerSourceStatic cert_issuer_source; for (size_t i = 1; i < cert_ders.size() - 1; ++i) cert_issuer_source.AddCert(certs[i]); std::shared_ptr target_cert(certs.back()); int64_t verify_time; ASSERT_TRUE(der::GeneralizedTimeToPosixTime(info.time, &verify_time)); CrlCheckingPathBuilderDelegate path_builder_delegate( crl_ders, verify_time, /*max_age=*/kOneYear * 2, 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1); std::string_view test_number = info.test_number; if (test_number == "4.4.19" || test_number == "4.5.3" || test_number == "4.5.4" || test_number == "4.5.6") { // 4.4.19 - fails since CRL is signed by a certificate that is not part // of the verified chain, which is not supported. // 4.5.3 - fails since non-URI distribution point names are not supported // 4.5.4, 4.5.6 - fails since CRL is signed by a certificate that is not // part of verified chain, and also non-URI distribution // point names not supported info.should_validate = false; } else if (test_number == "4.14.1" || test_number == "4.14.4" || test_number == "4.14.5" || test_number == "4.14.7" || test_number == "4.14.18" || test_number == "4.14.19" || test_number == "4.14.22" || test_number == "4.14.24" || test_number == "4.14.25" || test_number == "4.14.28" || test_number == "4.14.29" || test_number == "4.14.30" || test_number == "4.14.33") { // 4.14 tests: // .1 - fails since non-URI distribution point names not supported // .2, .3 - fails since non-URI distribution point names not supported // (but test is expected to fail for other reason) // .4, .5 - fails since non-URI distribution point names not supported, // also uses nameRelativeToCRLIssuer which is not supported // .6 - fails since non-URI distribution point names not supported, also // uses nameRelativeToCRLIssuer which is not supported (but test is // expected to fail for other reason) // .7 - fails since relative distributionPointName not supported // .8, .9 - fails since relative distributionPointName not supported (but // test is expected to fail for other reason) // .10, .11, .12, .13, .14, .27, .35 - PASS // .15, .16, .17, .20, .21 - fails since onlySomeReasons is not supported // (but test is expected to fail for other // reason) // .18, .19 - fails since onlySomeReasons is not supported // .22, .24, .25, .28, .29, .30, .33 - fails since indirect CRLs are not // supported // .23, .26, .31, .32, .34 - fails since indirect CRLs are not supported // (but test is expected to fail for other // reason) info.should_validate = false; } else if (test_number == "4.15.1" || test_number == "4.15.5") { // 4.15 tests: // .1 - fails due to unhandled critical deltaCRLIndicator extension // .2, .3, .6, .7, .8, .9, .10 - PASS since expected cert status is // reflected in base CRL and delta CRL is // ignored // .5 - fails, cert status is "on hold" in base CRL but the delta CRL // which removes the cert from CRL is ignored info.should_validate = false; } else if (test_number == "4.15.4") { // 4.15.4 - Invalid delta-CRL Test4 has the target cert marked revoked in // a delta-CRL. Since delta-CRLs are not supported, the chain validates // successfully. info.should_validate = true; } CertPathBuilder path_builder( std::move(target_cert), &trust_store, &path_builder_delegate, info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy, info.initial_policy_set, info.initial_policy_mapping_inhibit, info.initial_inhibit_any_policy); path_builder.AddCertIssuerSource(&cert_issuer_source); CertPathBuilder::Result result = path_builder.Run(); if (info.should_validate != result.HasValidPath()) { for (size_t i = 0; i < result.paths.size(); ++i) { const net::CertPathBuilderResultPath* result_path = result.paths[i].get(); LOG(ERROR) << "path " << i << " errors:\n" << result_path->errors.ToDebugString(result_path->certs); } } ASSERT_EQ(info.should_validate, result.HasValidPath()); if (result.HasValidPath()) { EXPECT_EQ(info.user_constrained_policy_set, result.GetBestValidPath()->user_constrained_policy_set); } } }; } // namespace INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest01SignatureVerification, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest02ValidityPeriods, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest03VerifyingNameChaining, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest04BasicCertificateRevocationTests, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P( PathBuilder, PkitsTest05VerifyingPathswithSelfIssuedCertificates, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest06VerifyingBasicConstraints, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest07KeyUsage, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest08CertificatePolicies, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest09RequireExplicitPolicy, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest10PolicyMappings, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest11InhibitPolicyMapping, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest12InhibitAnyPolicy, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest13NameConstraints, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest14DistributionPoints, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest15DeltaCRLs, PathBuilderPkitsTestDelegate); INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder, PkitsTest16PrivateCertificateExtensions, PathBuilderPkitsTestDelegate); } // namespace net