You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
8.6 KiB
205 lines
8.6 KiB
// Copyright 2019 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 "cast/common/certificate/cast_crl.h"
|
|
|
|
#include "cast/common/certificate/cast_cert_validator.h"
|
|
#include "cast/common/certificate/cast_cert_validator_internal.h"
|
|
#include "cast/common/certificate/proto/test_suite.pb.h"
|
|
#include "cast/common/certificate/testing/test_helpers.h"
|
|
#include "gtest/gtest.h"
|
|
#include "platform/test/paths.h"
|
|
#include "testing/util/read_file.h"
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
|
|
// TODO(crbug.com/openscreen/90): Remove these after Chromium is migrated to
|
|
// openscreen::cast
|
|
using DeviceCertTestSuite = ::cast::certificate::DeviceCertTestSuite;
|
|
using VerificationResult = ::cast::certificate::VerificationResult;
|
|
using DeviceCertTest = ::cast::certificate::DeviceCertTest;
|
|
|
|
namespace {
|
|
|
|
// Indicates the expected result of test step's verification.
|
|
enum TestStepResult {
|
|
kResultSuccess,
|
|
kResultFail,
|
|
};
|
|
|
|
// Verifies that the provided certificate chain is valid at the specified time
|
|
// and chains up to a trust anchor.
|
|
bool TestVerifyCertificate(TestStepResult expected_result,
|
|
const std::vector<std::string>& der_certs,
|
|
const DateTime& time,
|
|
TrustStore* cast_trust_store) {
|
|
std::unique_ptr<CertVerificationContext> context;
|
|
CastDeviceCertPolicy policy;
|
|
Error result = VerifyDeviceCert(der_certs, time, &context, &policy, nullptr,
|
|
CRLPolicy::kCrlOptional, cast_trust_store);
|
|
bool success = (result.code() == Error::Code::kNone) ==
|
|
(expected_result == kResultSuccess);
|
|
EXPECT_TRUE(success);
|
|
return success;
|
|
}
|
|
|
|
// Verifies that the provided Cast CRL is signed by a trusted issuer
|
|
// and that the CRL can be parsed successfully.
|
|
// The validity of the CRL is also checked at the specified time.
|
|
bool TestVerifyCRL(TestStepResult expected_result,
|
|
const std::string& crl_bundle,
|
|
const DateTime& time,
|
|
TrustStore* crl_trust_store) {
|
|
std::unique_ptr<CastCRL> crl =
|
|
ParseAndVerifyCRL(crl_bundle, time, crl_trust_store);
|
|
|
|
bool success = (crl != nullptr) == (expected_result == kResultSuccess);
|
|
EXPECT_TRUE(success);
|
|
return success;
|
|
}
|
|
|
|
// Verifies that the certificate chain provided is not revoked according to
|
|
// the provided Cast CRL at |cert_time|.
|
|
// The provided CRL is verified at |crl_time|.
|
|
// If |crl_required| is set, then a valid Cast CRL must be provided.
|
|
// Otherwise, a missing CRL is be ignored.
|
|
bool TestVerifyRevocation(Error::Code expected_result,
|
|
const std::vector<std::string>& der_certs,
|
|
const std::string& crl_bundle,
|
|
const DateTime& crl_time,
|
|
const DateTime& cert_time,
|
|
bool crl_required,
|
|
TrustStore* cast_trust_store,
|
|
TrustStore* crl_trust_store) {
|
|
std::unique_ptr<CastCRL> crl;
|
|
if (!crl_bundle.empty()) {
|
|
crl = ParseAndVerifyCRL(crl_bundle, crl_time, crl_trust_store);
|
|
EXPECT_NE(crl.get(), nullptr);
|
|
}
|
|
|
|
std::unique_ptr<CertVerificationContext> context;
|
|
CastDeviceCertPolicy policy;
|
|
CRLPolicy crl_policy =
|
|
crl_required ? CRLPolicy::kCrlRequired : CRLPolicy::kCrlOptional;
|
|
Error result = VerifyDeviceCert(der_certs, cert_time, &context, &policy,
|
|
crl.get(), crl_policy, cast_trust_store);
|
|
EXPECT_EQ(expected_result, result.code());
|
|
return expected_result == result.code();
|
|
}
|
|
|
|
const std::string& GetSpecificTestDataPath() {
|
|
static std::string data_path = GetTestDataPath() + "cast/common/certificate/";
|
|
return data_path;
|
|
}
|
|
|
|
bool RunTest(const DeviceCertTest& test_case) {
|
|
std::unique_ptr<TrustStore> crl_trust_store;
|
|
std::unique_ptr<TrustStore> cast_trust_store;
|
|
if (test_case.use_test_trust_anchors()) {
|
|
crl_trust_store = std::make_unique<TrustStore>();
|
|
cast_trust_store = std::make_unique<TrustStore>();
|
|
*crl_trust_store = TrustStore::CreateInstanceFromPemFile(
|
|
GetSpecificTestDataPath() + "certificates/cast_crl_test_root_ca.pem");
|
|
*cast_trust_store = TrustStore::CreateInstanceFromPemFile(
|
|
GetSpecificTestDataPath() + "certificates/cast_test_root_ca.pem");
|
|
|
|
EXPECT_FALSE(crl_trust_store->certs.empty());
|
|
EXPECT_FALSE(cast_trust_store->certs.empty());
|
|
}
|
|
|
|
std::vector<std::string> der_cert_path;
|
|
for (const auto& cert : test_case.der_cert_path()) {
|
|
der_cert_path.push_back(cert);
|
|
}
|
|
|
|
DateTime cert_verification_time;
|
|
EXPECT_TRUE(DateTimeFromSeconds(test_case.cert_verification_time_seconds(),
|
|
&cert_verification_time));
|
|
|
|
uint64_t crl_verify_time = test_case.crl_verification_time_seconds();
|
|
DateTime crl_verification_time;
|
|
EXPECT_TRUE(DateTimeFromSeconds(crl_verify_time, &crl_verification_time));
|
|
if (crl_verify_time == 0) {
|
|
crl_verification_time = cert_verification_time;
|
|
}
|
|
|
|
std::string crl_bundle = test_case.crl_bundle();
|
|
switch (test_case.expected_result()) {
|
|
case ::cast::certificate::PATH_VERIFICATION_FAILED:
|
|
return TestVerifyCertificate(kResultFail, der_cert_path,
|
|
cert_verification_time,
|
|
cast_trust_store.get());
|
|
case ::cast::certificate::CRL_VERIFICATION_FAILED:
|
|
return TestVerifyCRL(kResultFail, crl_bundle, crl_verification_time,
|
|
crl_trust_store.get());
|
|
case ::cast::certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL:
|
|
return TestVerifyCertificate(kResultSuccess, der_cert_path,
|
|
cert_verification_time,
|
|
cast_trust_store.get()) &&
|
|
TestVerifyCRL(kResultFail, crl_bundle, crl_verification_time,
|
|
crl_trust_store.get()) &&
|
|
TestVerifyRevocation(
|
|
Error::Code::kErrCrlInvalid, der_cert_path, crl_bundle,
|
|
crl_verification_time, cert_verification_time, true,
|
|
cast_trust_store.get(), crl_trust_store.get());
|
|
case ::cast::certificate::
|
|
CRL_EXPIRED_AFTER_INITIAL_VERIFICATION: // fallthrough
|
|
case ::cast::certificate::REVOCATION_CHECK_FAILED:
|
|
return TestVerifyCertificate(kResultSuccess, der_cert_path,
|
|
cert_verification_time,
|
|
cast_trust_store.get()) &&
|
|
TestVerifyCRL(kResultSuccess, crl_bundle, crl_verification_time,
|
|
crl_trust_store.get()) &&
|
|
TestVerifyRevocation(
|
|
Error::Code::kErrCertsRevoked, der_cert_path, crl_bundle,
|
|
crl_verification_time, cert_verification_time, true,
|
|
cast_trust_store.get(), crl_trust_store.get());
|
|
case ::cast::certificate::SUCCESS:
|
|
return (crl_bundle.empty() ||
|
|
TestVerifyCRL(kResultSuccess, crl_bundle, crl_verification_time,
|
|
crl_trust_store.get())) &&
|
|
TestVerifyCertificate(kResultSuccess, der_cert_path,
|
|
cert_verification_time,
|
|
cast_trust_store.get()) &&
|
|
TestVerifyRevocation(Error::Code::kNone, der_cert_path, crl_bundle,
|
|
crl_verification_time, cert_verification_time,
|
|
!crl_bundle.empty(), cast_trust_store.get(),
|
|
crl_trust_store.get());
|
|
case ::cast::certificate::UNSPECIFIED:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parses the provided test suite provided in wire-format proto.
|
|
// Each test contains the inputs and the expected output.
|
|
// To see the description of the test, execute the test.
|
|
// These tests are generated by a test generator in google3.
|
|
void RunTestSuite(const std::string& test_suite_file_name) {
|
|
std::string testsuite_raw = ReadEntireFileToString(test_suite_file_name);
|
|
ASSERT_FALSE(testsuite_raw.empty());
|
|
DeviceCertTestSuite test_suite;
|
|
ASSERT_TRUE(test_suite.ParseFromString(testsuite_raw));
|
|
int successes = 0;
|
|
|
|
for (auto const& test_case : test_suite.tests()) {
|
|
bool result = RunTest(test_case);
|
|
successes += result;
|
|
EXPECT_TRUE(result) << test_case.description();
|
|
}
|
|
OSP_LOG_IF(ERROR, successes != test_suite.tests().size())
|
|
<< "successes: " << successes
|
|
<< ", failures: " << (test_suite.tests().size() - successes);
|
|
}
|
|
|
|
TEST(CastCertificateTest, TestSuite1) {
|
|
RunTestSuite(GetSpecificTestDataPath() + "testsuite/testsuite1.pb");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace cast
|
|
} // namespace openscreen
|