forked from Pissandshittium/pissandshittium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
remove use of wincrypt in net/cert/pki
This moves the path builder windows trust store specific unit tests to cert/internal Bug: 1322914 Change-Id: I6e2bd00691432854a9db65b50a32952e3fd540ba Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4091288 Reviewed-by: David Benjamin <davidben@chromium.org> Commit-Queue: Bob Beck <bbe@google.com> Cr-Commit-Position: refs/heads/main@{#1081639}
- Loading branch information
Bob Beck
authored and
Chromium LUCI CQ
committed
Dec 9, 2022
1 parent
3112351
commit 7982e40
Showing
4 changed files
with
328 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
327 changes: 327 additions & 0 deletions
327
net/cert/internal/path_builder_trust_store_win_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,327 @@ | ||
// 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 <algorithm> | ||
|
||
#include "base/base_paths.h" | ||
#include "base/callback_forward.h" | ||
#include "base/files/file_util.h" | ||
#include "base/path_service.h" | ||
#include "base/test/bind.h" | ||
#include "build/build_config.h" | ||
#include "net/cert/pem.h" | ||
#include "net/cert/pki/cert_error_params.h" | ||
#include "net/cert/pki/cert_issuer_source_static.h" | ||
#include "net/cert/pki/common_cert_errors.h" | ||
#include "net/cert/pki/parsed_certificate.h" | ||
#include "net/cert/pki/simple_path_builder_delegate.h" | ||
#include "net/cert/pki/test_helpers.h" | ||
#include "net/cert/pki/trust_store_collection.h" | ||
#include "net/cert/pki/trust_store_in_memory.h" | ||
#include "net/cert/pki/verify_certificate_chain.h" | ||
#include "net/der/input.h" | ||
#include "net/net_buildflags.h" | ||
#include "net/test/test_certificate_data.h" | ||
#include "testing/gmock/include/gmock/gmock.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
#include "third_party/boringssl/src/include/openssl/pool.h" | ||
|
||
#include "base/win/wincrypt_shim.h" | ||
#include "crypto/scoped_capi_types.h" | ||
#include "net/cert/internal/trust_store_win.h" | ||
|
||
namespace net { | ||
|
||
namespace { | ||
|
||
using ::testing::_; | ||
using ::testing::ElementsAre; | ||
using ::testing::Invoke; | ||
using ::testing::NiceMock; | ||
using ::testing::Return; | ||
using ::testing::SaveArg; | ||
using ::testing::SetArgPointee; | ||
using ::testing::StrictMock; | ||
|
||
class DeadlineTestingPathBuilderDelegate : public SimplePathBuilderDelegate { | ||
public: | ||
DeadlineTestingPathBuilderDelegate(size_t min_rsa_modulus_length_bits, | ||
DigestPolicy digest_policy) | ||
: SimplePathBuilderDelegate(min_rsa_modulus_length_bits, digest_policy) {} | ||
|
||
bool IsDeadlineExpired() override { return deadline_is_expired_; } | ||
|
||
void SetDeadlineExpiredForTesting(bool deadline_is_expired) { | ||
deadline_is_expired_ = deadline_is_expired; | ||
} | ||
|
||
private: | ||
bool deadline_is_expired_ = false; | ||
}; | ||
|
||
// AsyncCertIssuerSourceStatic always returns its certs asynchronously. | ||
class AsyncCertIssuerSourceStatic : public CertIssuerSource { | ||
public: | ||
class StaticAsyncRequest : public Request { | ||
public: | ||
explicit StaticAsyncRequest(ParsedCertificateList&& issuers) { | ||
issuers_.swap(issuers); | ||
issuers_iter_ = issuers_.begin(); | ||
} | ||
|
||
StaticAsyncRequest(const StaticAsyncRequest&) = delete; | ||
StaticAsyncRequest& operator=(const StaticAsyncRequest&) = delete; | ||
|
||
~StaticAsyncRequest() override = default; | ||
|
||
void GetNext(ParsedCertificateList* out_certs) override { | ||
if (issuers_iter_ != issuers_.end()) | ||
out_certs->push_back(std::move(*issuers_iter_++)); | ||
} | ||
|
||
ParsedCertificateList issuers_; | ||
ParsedCertificateList::iterator issuers_iter_; | ||
}; | ||
|
||
~AsyncCertIssuerSourceStatic() override = default; | ||
|
||
void SetAsyncGetCallback(base::RepeatingClosure closure) { | ||
async_get_callback_ = std::move(closure); | ||
} | ||
|
||
void AddCert(std::shared_ptr<const ParsedCertificate> cert) { | ||
static_cert_issuer_source_.AddCert(std::move(cert)); | ||
} | ||
|
||
void SyncGetIssuersOf(const ParsedCertificate* cert, | ||
ParsedCertificateList* issuers) override {} | ||
void AsyncGetIssuersOf(const ParsedCertificate* cert, | ||
std::unique_ptr<Request>* out_req) override { | ||
num_async_gets_++; | ||
ParsedCertificateList issuers; | ||
static_cert_issuer_source_.SyncGetIssuersOf(cert, &issuers); | ||
auto req = std::make_unique<StaticAsyncRequest>(std::move(issuers)); | ||
*out_req = std::move(req); | ||
if (!async_get_callback_.is_null()) | ||
async_get_callback_.Run(); | ||
} | ||
int num_async_gets() const { return num_async_gets_; } | ||
|
||
private: | ||
CertIssuerSourceStatic static_cert_issuer_source_; | ||
|
||
int num_async_gets_ = 0; | ||
base::RepeatingClosure async_get_callback_; | ||
}; | ||
|
||
::testing::AssertionResult ReadTestPem(const std::string& file_name, | ||
const std::string& block_name, | ||
std::string* result) { | ||
const PemBlockMapping mappings[] = { | ||
{block_name.c_str(), result}, | ||
}; | ||
|
||
return ReadTestDataFromPemFile(file_name, mappings); | ||
} | ||
|
||
::testing::AssertionResult ReadTestCert( | ||
const std::string& file_name, | ||
std::shared_ptr<const ParsedCertificate>* result) { | ||
std::string der; | ||
::testing::AssertionResult r = ReadTestPem( | ||
"net/data/ssl/certificates/" + file_name, "CERTIFICATE", &der); | ||
if (!r) | ||
return r; | ||
CertErrors errors; | ||
*result = ParsedCertificate::Create( | ||
bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new( | ||
reinterpret_cast<const uint8_t*>(der.data()), der.size(), nullptr)), | ||
{}, &errors); | ||
if (!*result) { | ||
return ::testing::AssertionFailure() | ||
<< "ParseCertificate::Create() failed:\n" | ||
<< errors.ToDebugString(); | ||
} | ||
return ::testing::AssertionSuccess(); | ||
} | ||
|
||
const void* kKey = &kKey; | ||
class TrustStoreThatStoresUserData : public TrustStore { | ||
public: | ||
class Data : public base::SupportsUserData::Data { | ||
public: | ||
explicit Data(int value) : value(value) {} | ||
|
||
int value = 0; | ||
}; | ||
|
||
// TrustStore implementation: | ||
void SyncGetIssuersOf(const ParsedCertificate* cert, | ||
ParsedCertificateList* issuers) override {} | ||
CertificateTrust GetTrust(const ParsedCertificate* cert, | ||
base::SupportsUserData* debug_data) const override { | ||
debug_data->SetUserData(kKey, std::make_unique<Data>(1234)); | ||
return CertificateTrust::ForUnspecified(); | ||
} | ||
}; | ||
|
||
TEST(PathBuilderResultUserDataTest, ModifyUserDataInConstructor) { | ||
std::shared_ptr<const ParsedCertificate> a_by_b; | ||
ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b)); | ||
SimplePathBuilderDelegate delegate( | ||
1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1); | ||
der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0}; | ||
TrustStoreThatStoresUserData trust_store; | ||
|
||
// |trust_store| will unconditionally store user data in the | ||
// CertPathBuilder::Result. This ensures that the Result object has been | ||
// initialized before the first GetTrust call occurs (otherwise the test will | ||
// crash or fail on ASAN bots). | ||
CertPathBuilder path_builder( | ||
a_by_b, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU, | ||
InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)}, | ||
InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse); | ||
CertPathBuilder::Result result = path_builder.Run(); | ||
auto* data = static_cast<TrustStoreThatStoresUserData::Data*>( | ||
result.GetUserData(kKey)); | ||
ASSERT_TRUE(data); | ||
EXPECT_EQ(1234, data->value); | ||
} | ||
|
||
class PathBuilderMultiRootWindowsTest : public ::testing::Test { | ||
public: | ||
PathBuilderMultiRootWindowsTest() | ||
: delegate_( | ||
1024, | ||
DeadlineTestingPathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {} | ||
|
||
void SetUp() override { | ||
ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-B-by-C.pem", &b_by_c_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-B-by-F.pem", &b_by_f_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-C-by-D.pem", &c_by_d_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-C-by-E.pem", &c_by_e_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-D-by-D.pem", &d_by_d_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-E-by-E.pem", &e_by_e_)); | ||
ASSERT_TRUE(ReadTestCert("multi-root-F-by-E.pem", &f_by_e_)); | ||
} | ||
|
||
protected: | ||
std::shared_ptr<const ParsedCertificate> a_by_b_, b_by_c_, b_by_f_, c_by_d_, | ||
c_by_e_, d_by_d_, e_by_e_, f_by_e_; | ||
|
||
DeadlineTestingPathBuilderDelegate delegate_; | ||
der::GeneralizedTime time_ = {2017, 3, 1, 0, 0, 0}; | ||
|
||
const InitialExplicitPolicy initial_explicit_policy_ = | ||
InitialExplicitPolicy::kFalse; | ||
const std::set<der::Input> user_initial_policy_set_ = { | ||
der::Input(kAnyPolicyOid)}; | ||
const InitialPolicyMappingInhibit initial_policy_mapping_inhibit_ = | ||
InitialPolicyMappingInhibit::kFalse; | ||
const InitialAnyPolicyInhibit initial_any_policy_inhibit_ = | ||
InitialAnyPolicyInhibit::kFalse; | ||
}; | ||
|
||
void AddToStoreWithEKURestriction( | ||
HCERTSTORE store, | ||
const std::shared_ptr<const ParsedCertificate>& cert, | ||
LPCSTR usage_identifier) { | ||
crypto::ScopedPCCERT_CONTEXT os_cert(CertCreateCertificateContext( | ||
X509_ASN_ENCODING, cert->der_cert().UnsafeData(), | ||
cert->der_cert().Length())); | ||
|
||
CERT_ENHKEY_USAGE usage; | ||
memset(&usage, 0, sizeof(usage)); | ||
CertSetEnhancedKeyUsage(os_cert.get(), &usage); | ||
if (usage_identifier) { | ||
CertAddEnhancedKeyUsageIdentifier(os_cert.get(), usage_identifier); | ||
} | ||
CertAddCertificateContextToStore(store, os_cert.get(), CERT_STORE_ADD_ALWAYS, | ||
nullptr); | ||
} | ||
|
||
bool AreCertsEq(const std::shared_ptr<const ParsedCertificate> cert_1, | ||
const std::shared_ptr<const ParsedCertificate> cert_2) { | ||
return cert_1 && cert_2 && cert_1->der_cert() == cert_2->der_cert(); | ||
} | ||
|
||
// Test to ensure that path building stops when an intermediate cert is | ||
// encountered that is not usable for TLS because it is explicitly distrusted. | ||
TEST_F(PathBuilderMultiRootWindowsTest, TrustStoreWinOnlyFindTrustedTLSPath) { | ||
crypto::ScopedHCERTSTORE root_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
crypto::ScopedHCERTSTORE intermediate_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
crypto::ScopedHCERTSTORE disallowed_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
|
||
AddToStoreWithEKURestriction(root_store.get(), d_by_d_, | ||
szOID_PKIX_KP_SERVER_AUTH); | ||
AddToStoreWithEKURestriction(root_store.get(), e_by_e_, | ||
szOID_PKIX_KP_SERVER_AUTH); | ||
AddToStoreWithEKURestriction(intermediate_store.get(), c_by_e_, | ||
szOID_PKIX_KP_SERVER_AUTH); | ||
AddToStoreWithEKURestriction(disallowed_store.get(), c_by_d_, nullptr); | ||
|
||
std::unique_ptr<TrustStoreWin> trust_store = TrustStoreWin::CreateForTesting( | ||
std::move(root_store), std::move(intermediate_store), | ||
std::move(disallowed_store)); | ||
|
||
CertPathBuilder path_builder( | ||
b_by_c_, trust_store.get(), &delegate_, time_, KeyPurpose::ANY_EKU, | ||
initial_explicit_policy_, user_initial_policy_set_, | ||
initial_policy_mapping_inhibit_, initial_any_policy_inhibit_); | ||
|
||
// Check all paths. | ||
path_builder.SetExploreAllPaths(true); | ||
|
||
auto result = path_builder.Run(); | ||
ASSERT_TRUE(result.HasValidPath()); | ||
ASSERT_EQ(1U, result.paths.size()); | ||
const auto& path = *result.GetBestValidPath(); | ||
ASSERT_EQ(3U, path.certs.size()); | ||
EXPECT_TRUE(AreCertsEq(b_by_c_, path.certs[0])); | ||
EXPECT_TRUE(AreCertsEq(c_by_e_, path.certs[1])); | ||
EXPECT_TRUE(AreCertsEq(e_by_e_, path.certs[2])); | ||
|
||
// Should only be one valid path, the one above. | ||
const int valid_paths = std::count_if( | ||
result.paths.begin(), result.paths.end(), | ||
[](const auto& candidate_path) { return candidate_path->IsValid(); }); | ||
ASSERT_EQ(1, valid_paths); | ||
} | ||
|
||
// Test that if an intermediate is untrusted, and it is the only | ||
// path, then path building should fail, even if the root is enabled for | ||
// TLS. | ||
TEST_F(PathBuilderMultiRootWindowsTest, TrustStoreWinNoPathEKURestrictions) { | ||
crypto::ScopedHCERTSTORE root_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
crypto::ScopedHCERTSTORE intermediate_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
crypto::ScopedHCERTSTORE disallowed_store(CertOpenStore( | ||
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr)); | ||
|
||
AddToStoreWithEKURestriction(root_store.get(), d_by_d_, | ||
szOID_PKIX_KP_SERVER_AUTH); | ||
AddToStoreWithEKURestriction(disallowed_store.get(), c_by_d_, nullptr); | ||
std::unique_ptr<TrustStoreWin> trust_store = TrustStoreWin::CreateForTesting( | ||
std::move(root_store), std::move(intermediate_store), | ||
std::move(disallowed_store)); | ||
|
||
CertPathBuilder path_builder( | ||
b_by_c_, trust_store.get(), &delegate_, time_, KeyPurpose::ANY_EKU, | ||
initial_explicit_policy_, user_initial_policy_set_, | ||
initial_policy_mapping_inhibit_, initial_any_policy_inhibit_); | ||
|
||
auto result = path_builder.Run(); | ||
ASSERT_FALSE(result.HasValidPath()); | ||
} | ||
|
||
} // namespace | ||
|
||
} // namespace net |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.