667 lines
24 KiB
C++
667 lines
24 KiB
C++
// Copyright 2017 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/internal/revocation_checker.h"
|
|
|
|
#include "base/time/time.h"
|
|
#include "net/cert/mock_cert_net_fetcher.h"
|
|
#include "net/cert/pki/cert_errors.h"
|
|
#include "net/cert/pki/common_cert_errors.h"
|
|
#include "net/cert/pki/parse_certificate.h"
|
|
#include "net/cert/pki/parsed_certificate.h"
|
|
#include "net/test/cert_builder.h"
|
|
#include "net/test/revocation_builder.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "url/gurl.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
using ::testing::_;
|
|
using ::testing::ByMove;
|
|
using ::testing::Mock;
|
|
using ::testing::Return;
|
|
using ::testing::StrictMock;
|
|
|
|
bool AddCertsToList(std::vector<CertBuilder*> builders,
|
|
ParsedCertificateList* out_certs) {
|
|
for (auto* builder : builders) {
|
|
if (!ParsedCertificate::CreateAndAddToVector(
|
|
builder->DupCertBuffer(), {}, out_certs, /*errors=*/nullptr)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TEST(RevocationChecker, NoRevocationMechanism) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
|
|
{
|
|
// Require revocation methods to be presented.
|
|
policy.allow_missing_info = false;
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kNoRevocationMechanism));
|
|
}
|
|
|
|
{
|
|
// Allow certs without revocation methods.
|
|
policy.allow_missing_info = true;
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
|
|
{
|
|
// Revocation checking disabled.
|
|
policy.check_revocation = false;
|
|
// Require revocation methods to be presented, but this does not matter if
|
|
// check_revocation is false.
|
|
policy.allow_missing_info = false;
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
}
|
|
|
|
TEST(RevocationChecker, ValidCRL) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kTestCrlUrl("http://example.com/crl1");
|
|
leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.allow_missing_info = false;
|
|
policy.allow_unable_to_check = false;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
{
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
|
|
{
|
|
policy.networking_allowed = false;
|
|
policy.crl_allowed = true;
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kUnableToCheckRevocation));
|
|
}
|
|
|
|
{
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = false;
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
// Since CRLs were not considered, the error should be "no revocation
|
|
// mechanism".
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kNoRevocationMechanism));
|
|
}
|
|
}
|
|
|
|
TEST(RevocationChecker, RevokedCRL) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kTestCrlUrl("http://example.com/crl1");
|
|
leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{leaf->GetSerialNumber()});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
{
|
|
// These should have no effect on an affirmatively revoked response.
|
|
policy.allow_missing_info = false;
|
|
policy.allow_unable_to_check = false;
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kCertificateRevoked));
|
|
}
|
|
|
|
{
|
|
// These should have no effect on an affirmatively revoked response.
|
|
policy.allow_missing_info = true;
|
|
policy.allow_unable_to_check = true;
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kCertificateRevoked));
|
|
}
|
|
}
|
|
|
|
TEST(RevocationChecker, CRLRequestFails) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kTestCrlUrl("http://example.com/crl1");
|
|
leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
|
|
{
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(
|
|
ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kUnableToCheckRevocation));
|
|
}
|
|
|
|
{
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = true; // Should have no effect.
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(
|
|
ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kUnableToCheckRevocation));
|
|
}
|
|
|
|
{
|
|
policy.allow_unable_to_check = true;
|
|
policy.allow_missing_info = false;
|
|
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
|
|
.WillOnce(Return(
|
|
ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
}
|
|
|
|
TEST(RevocationChecker, CRLNonHttpUrl) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kTestCrlUrl("https://example.com/crl1");
|
|
leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
// HTTPS CRL URLs should not be fetched.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kNoRevocationMechanism));
|
|
}
|
|
|
|
TEST(RevocationChecker, SkipEntireInvalidCRLDistributionPoints) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
|
|
|
|
// SEQUENCE {
|
|
// # First distribution point: this is invalid, thus the entire
|
|
// # crlDistributionPoints extension should be ignored and revocation
|
|
// # checking should fail.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// # [9] is not a valid tag in GeneralNames
|
|
// [9 PRIMITIVE] { "foo" }
|
|
// }
|
|
// }
|
|
// }
|
|
// # Second distribution point. Even though this is an acceptable
|
|
// # distributionPoint, it should not be used.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
const uint8_t crldp[] = {0x30, 0x31, 0x30, 0x09, 0xa0, 0x07, 0xa0, 0x05, 0x89,
|
|
0x03, 0x66, 0x6f, 0x6f, 0x30, 0x24, 0xa0, 0x22, 0xa0,
|
|
0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
|
|
0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d,
|
|
0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
|
|
0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
|
|
leaf->SetExtension(
|
|
der::Input(kCrlDistributionPointsOid),
|
|
std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
// No methods on |mock_fetcher| should be called.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
// Should fail since the entire cRLDistributionPoints extension was skipped
|
|
// and no other revocation method is present.
|
|
EXPECT_TRUE(errors.ContainsHighSeverityErrors());
|
|
EXPECT_TRUE(errors.ContainsError(cert_errors::kNoRevocationMechanism));
|
|
}
|
|
|
|
TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithNonUriFullname) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
|
|
|
|
// SEQUENCE {
|
|
// # First distribution point: this should be ignored since it has a non-URI
|
|
// # fullName field.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [4] {
|
|
// SEQUENCE {
|
|
// SET {
|
|
// SEQUENCE {
|
|
// # countryName
|
|
// OBJECT_IDENTIFIER { 2.5.4.6 }
|
|
// PrintableString { "US" }
|
|
// }
|
|
// }
|
|
// SET {
|
|
// SEQUENCE {
|
|
// # commonName
|
|
// OBJECT_IDENTIFIER { 2.5.4.3 }
|
|
// PrintableString { "foo" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// # Second distribution point. This should be used since it only has a
|
|
// # fullName URI.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
const uint8_t crldp[] = {
|
|
0x30, 0x4b, 0x30, 0x23, 0xa0, 0x21, 0xa0, 0x1f, 0xa4, 0x1d, 0x30,
|
|
0x1b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
|
|
0x02, 0x55, 0x53, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04,
|
|
0x03, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x30, 0x24, 0xa0, 0x22, 0xa0,
|
|
0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
|
|
0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
|
|
0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
|
|
leaf->SetExtension(
|
|
der::Input(kCrlDistributionPointsOid),
|
|
std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
// The first crldp should be skipped, the second should be retrieved.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
|
|
TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithReasons) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
|
|
|
|
// SEQUENCE {
|
|
// # First distribution point: this should be ignored since it has a reasons
|
|
// # field.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/foo.crl" }
|
|
// }
|
|
// }
|
|
// # reasons
|
|
// [1 PRIMITIVE] { b`011` }
|
|
// }
|
|
// # Second distribution point. This should be used since it only has a
|
|
// # fullName URI.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
const uint8_t crldp[] = {
|
|
0x30, 0x50, 0x30, 0x28, 0xa0, 0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74,
|
|
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61,
|
|
0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f,
|
|
0x2e, 0x63, 0x72, 0x6c, 0x81, 0x02, 0x05, 0x60, 0x30, 0x24, 0xa0, 0x22,
|
|
0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
|
|
0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
|
|
0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
|
|
leaf->SetExtension(
|
|
der::Input(kCrlDistributionPointsOid),
|
|
std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
// The first crldp should be skipped, the second should be retrieved.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
|
|
TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithCrlIssuer) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
|
|
|
|
// SEQUENCE {
|
|
// # First distribution point: this should be ignored since it has a
|
|
// crlIssuer field.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/foo.crl" }
|
|
// }
|
|
// }
|
|
// [2] {
|
|
// [4] {
|
|
// SEQUENCE {
|
|
// SET {
|
|
// SEQUENCE {
|
|
// # countryName
|
|
// OBJECT_IDENTIFIER { 2.5.4.6 }
|
|
// PrintableString { "US" }
|
|
// }
|
|
// }
|
|
// SET {
|
|
// SEQUENCE {
|
|
// # organizationName
|
|
// OBJECT_IDENTIFIER { 2.5.4.10 }
|
|
// PrintableString { "Test Certificates 2011" }
|
|
// }
|
|
// }
|
|
// SET {
|
|
// SEQUENCE {
|
|
// # organizationUnitName
|
|
// OBJECT_IDENTIFIER { 2.5.4.11 }
|
|
// PrintableString { "indirectCRL CA3 cRLIssuer" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// # Second distribution point. This should be used since it only has a
|
|
// # fullName URI.
|
|
// SEQUENCE {
|
|
// [0] {
|
|
// [0] {
|
|
// [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
const uint8_t crldp[] = {
|
|
0x30, 0x81, 0xa4, 0x30, 0x7c, 0xa0, 0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68,
|
|
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78,
|
|
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f,
|
|
0x6f, 0x2e, 0x63, 0x72, 0x6c, 0xa2, 0x56, 0xa4, 0x54, 0x30, 0x52, 0x31,
|
|
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
|
|
0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16, 0x54,
|
|
0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
|
0x61, 0x74, 0x65, 0x73, 0x20, 0x32, 0x30, 0x31, 0x31, 0x31, 0x22, 0x30,
|
|
0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x19, 0x69, 0x6e, 0x64, 0x69,
|
|
0x72, 0x65, 0x63, 0x74, 0x43, 0x52, 0x4c, 0x20, 0x43, 0x41, 0x33, 0x20,
|
|
0x63, 0x52, 0x4c, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x30, 0x24, 0xa0,
|
|
0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
|
|
0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
|
|
0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
|
|
leaf->SetExtension(
|
|
der::Input(kCrlDistributionPointsOid),
|
|
std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
|
|
|
|
ParsedCertificateList chain;
|
|
ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
|
|
|
|
RevocationPolicy policy;
|
|
policy.check_revocation = true;
|
|
policy.networking_allowed = true;
|
|
policy.crl_allowed = true;
|
|
policy.allow_unable_to_check = false;
|
|
policy.allow_missing_info = false;
|
|
|
|
std::string crl_data_as_string_for_some_reason =
|
|
BuildCrl(root->GetSubject(), root->GetKey(),
|
|
/*revoked_serials=*/{});
|
|
std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
|
|
crl_data_as_string_for_some_reason.end());
|
|
|
|
// The first crldp should be skipped, the second should be retrieved.
|
|
auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
|
|
EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
|
|
.WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
|
|
|
|
CertPathErrors errors;
|
|
CheckValidatedChainRevocation(
|
|
chain, policy, /*deadline=*/base::TimeTicks(),
|
|
/*stapled_leaf_ocsp_response=*/base::StringPiece(), mock_fetcher.get(),
|
|
&errors, /*stapled_ocsp_verify_result=*/nullptr);
|
|
|
|
EXPECT_FALSE(errors.ContainsHighSeverityErrors());
|
|
}
|
|
|
|
// TODO(mattm): Add more unittests (deadlines, OCSP, stapled OCSP, CRLSets).
|
|
// Currently those features are exercised indirectly through tests in
|
|
// url_request_unittest.cc, cert_verify_proc_unittest.cc, etc.
|
|
|
|
} // namespace
|
|
|
|
} // namespace net
|