Commit e6d5e2fe authored by Matt Mueller's avatar Matt Mueller Committed by Commit Bot

[pki library]: Add SetExploreAllPaths setting for PathBuilder.

The flag is used in the pathbuilder impl in cert_verify_tool, and
will also be useful in unittests.

Change-Id: I4613701d01a1e4e96bcfd8501074ecdb965150da
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1894104Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Commit-Queue: Matt Mueller <mattm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711493}
parent 1872ee47
......@@ -596,6 +596,10 @@ void CertPathBuilder::SetDeadline(base::TimeTicks deadline) {
deadline_ = deadline;
}
void CertPathBuilder::SetExploreAllPaths(bool explore_all_paths) {
explore_all_paths_ = explore_all_paths;
}
CertPathBuilder::Result CertPathBuilder::Run() {
uint32_t iteration_count = 0;
......@@ -634,10 +638,9 @@ CertPathBuilder::Result CertPathBuilder::Run() {
AddResultPath(std::move(result_path));
if (path_is_good) {
if (path_is_good && !explore_all_paths_) {
RecordIterationCountHistogram(iteration_count);
// Found a valid path, return immediately.
// TODO(mattm): add debug/test mode that tries all possible paths.
return std::move(out_result_);
}
// Path did not verify. Try more paths.
......@@ -646,10 +649,12 @@ CertPathBuilder::Result CertPathBuilder::Run() {
void CertPathBuilder::AddResultPath(
std::unique_ptr<CertPathBuilderResultPath> result_path) {
// TODO(mattm): set best_result_index based on number or severity of errors.
if (result_path->IsValid())
// TODO(mattm): If there are no valid paths, set best_result_index based on
// number or severity of errors. If there are multiple valid paths, could set
// best_result_index based on prioritization (since due to AIA and such, the
// actual order results were discovered may not match the ideal).
if (result_path->IsValid() && !out_result_.HasValidPath())
out_result_.best_result_index = out_result_.paths.size();
// TODO(mattm): add flag to only return a single path or all attempted paths?
out_result_.paths.push_back(std::move(result_path));
}
......
......@@ -180,7 +180,8 @@ class NET_EXPORT CertPathBuilder {
void AddCertIssuerSource(CertIssuerSource* cert_issuer_source);
// Sets a limit to the number of times to repeat the process of considering a
// new intermediate over all potential paths.
// new intermediate over all potential paths. Setting |limit| to 0 disables
// the iteration limit, which is the default.
void SetIterationLimit(uint32_t limit);
// Sets a deadline for completing path building. If |deadline| has passed and
......@@ -189,6 +190,12 @@ class NET_EXPORT CertPathBuilder {
// will be when path building is aborted.
void SetDeadline(base::TimeTicks deadline);
// If |explore_all_paths| is false (the default), path building will stop as
// soon as a valid path is found. If |explore_all_paths| is true, path
// building will continue until all possible paths have been exhausted (or
// iteration limit / deadline is exceeded).
void SetExploreAllPaths(bool explore_all_paths);
// Returns the deadline for path building, if any. If no deadline is set,
// |deadline().is_null()| will be true.
base::TimeTicks deadline() const { return deadline_; }
......@@ -215,6 +222,7 @@ class NET_EXPORT CertPathBuilder {
const InitialAnyPolicyInhibit initial_any_policy_inhibit_;
uint32_t max_iteration_count_ = 0;
base::TimeTicks deadline_;
bool explore_all_paths_ = false;
DISALLOW_COPY_AND_ASSIGN(CertPathBuilder);
};
......
......@@ -856,6 +856,104 @@ TEST_F(PathBuilderKeyRolloverTest, TestRolloverLongChain) {
EXPECT_EQ(oldroot_, path2.certs[3]);
}
// Tests that when SetExploreAllPaths is combined with SetIterationLimit the
// path builder will return all the paths that were able to be built before the
// iteration limit was reached.
TEST_F(PathBuilderKeyRolloverTest, ExploreAllPathsWithIterationLimit) {
struct Expectation {
int iteration_limit;
size_t expected_num_paths;
} kExpectations[] = {
// No iteration limit. All possible paths should be built.
{0, 4},
// Limit 1 is only enough to reach the intermediate, no paths should be
// built.
{1, 0},
// Limit 2 allows reaching the root on the first path.
{2, 1},
// Next iteration uses oldroot instead of newroot.
{3, 2},
// Backtracking to the target cert.
{4, 2},
// Adding oldintermediate.
{5, 2},
// Trying newroot.
{6, 3},
// Trying oldroot.
{7, 4},
};
// Trust both old and new roots.
TrustStoreInMemory trust_store;
trust_store.AddTrustAnchor(oldroot_);
trust_store.AddTrustAnchor(newroot_);
// Intermediates and root rollover are all provided synchronously.
CertIssuerSourceStatic sync_certs;
sync_certs.AddCert(oldintermediate_);
sync_certs.AddCert(newintermediate_);
for (const auto& expectation : kExpectations) {
CertPathBuilder path_builder(
target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
initial_explicit_policy_, user_initial_policy_set_,
initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
path_builder.AddCertIssuerSource(&sync_certs);
// Explore all paths, rather than stopping at the first valid path.
path_builder.SetExploreAllPaths(true);
// Limit the number of iterations.
path_builder.SetIterationLimit(expectation.iteration_limit);
auto result = path_builder.Run();
EXPECT_EQ(expectation.expected_num_paths > 0, result.HasValidPath());
ASSERT_EQ(expectation.expected_num_paths, result.paths.size());
if (expectation.expected_num_paths > 0) {
// Path builder will first build path: target <- newintermediate <-
// newroot
const auto& path0 = *result.paths[0];
EXPECT_TRUE(path0.IsValid());
ASSERT_EQ(3U, path0.certs.size());
EXPECT_EQ(target_, path0.certs[0]);
EXPECT_EQ(newintermediate_, path0.certs[1]);
EXPECT_EQ(newroot_, path0.certs[2]);
}
if (expectation.expected_num_paths > 1) {
// Next path: target <- newintermediate <- oldroot
const auto& path1 = *result.paths[1];
EXPECT_FALSE(path1.IsValid());
ASSERT_EQ(3U, path1.certs.size());
EXPECT_EQ(target_, path1.certs[0]);
EXPECT_EQ(newintermediate_, path1.certs[1]);
EXPECT_EQ(oldroot_, path1.certs[2]);
}
if (expectation.expected_num_paths > 2) {
// Next path: target <- oldintermediate <- newroot
const auto& path2 = *result.paths[2];
EXPECT_FALSE(path2.IsValid());
ASSERT_EQ(3U, path2.certs.size());
EXPECT_EQ(target_, path2.certs[0]);
EXPECT_EQ(oldintermediate_, path2.certs[1]);
EXPECT_EQ(newroot_, path2.certs[2]);
}
if (expectation.expected_num_paths > 3) {
// Final path: target <- oldintermediate <- oldroot
const auto& path3 = *result.paths[3];
EXPECT_TRUE(path3.IsValid());
ASSERT_EQ(3U, path3.certs.size());
EXPECT_EQ(target_, path3.certs[0]);
EXPECT_EQ(oldintermediate_, path3.certs[1]);
EXPECT_EQ(oldroot_, path3.certs[2]);
}
}
}
// If the target cert is a trust anchor, however is not itself *signed* by a
// trust anchor, then it is not considered valid (the SPKI and name of the
// trust anchor matches the SPKI and subject of the targe certificate, but the
......
......@@ -178,6 +178,9 @@ bool VerifyUsingPathBuilder(
path_builder.AddCertIssuerSource(aia_cert_issuer_source.get());
}
// TODO(mattm): should this be a command line flag?
path_builder.SetExploreAllPaths(true);
// Run the path builder.
net::CertPathBuilder::Result result = path_builder.Run();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment