1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cast/common/certificate/cast_crl.h"
6
7 #include "cast/common/certificate/cast_cert_validator.h"
8 #include "cast/common/certificate/cast_cert_validator_internal.h"
9 #include "cast/common/certificate/proto/test_suite.pb.h"
10 #include "cast/common/certificate/testing/test_helpers.h"
11 #include "gtest/gtest.h"
12 #include "platform/test/paths.h"
13 #include "testing/util/read_file.h"
14 #include "util/osp_logging.h"
15
16 namespace openscreen {
17 namespace cast {
18
19 // TODO(crbug.com/openscreen/90): Remove these after Chromium is migrated to
20 // openscreen::cast
21 using DeviceCertTestSuite = ::cast::certificate::DeviceCertTestSuite;
22 using VerificationResult = ::cast::certificate::VerificationResult;
23 using DeviceCertTest = ::cast::certificate::DeviceCertTest;
24
25 namespace {
26
27 // Indicates the expected result of test step's verification.
28 enum TestStepResult {
29 kResultSuccess,
30 kResultFail,
31 };
32
33 // Verifies that the provided certificate chain is valid at the specified time
34 // and chains up to a trust anchor.
TestVerifyCertificate(TestStepResult expected_result,const std::vector<std::string> & der_certs,const DateTime & time,TrustStore * cast_trust_store)35 bool TestVerifyCertificate(TestStepResult expected_result,
36 const std::vector<std::string>& der_certs,
37 const DateTime& time,
38 TrustStore* cast_trust_store) {
39 std::unique_ptr<CertVerificationContext> context;
40 CastDeviceCertPolicy policy;
41 Error result = VerifyDeviceCert(der_certs, time, &context, &policy, nullptr,
42 CRLPolicy::kCrlOptional, cast_trust_store);
43 bool success = (result.code() == Error::Code::kNone) ==
44 (expected_result == kResultSuccess);
45 EXPECT_TRUE(success);
46 return success;
47 }
48
49 // Verifies that the provided Cast CRL is signed by a trusted issuer
50 // and that the CRL can be parsed successfully.
51 // The validity of the CRL is also checked at the specified time.
TestVerifyCRL(TestStepResult expected_result,const std::string & crl_bundle,const DateTime & time,TrustStore * crl_trust_store)52 bool TestVerifyCRL(TestStepResult expected_result,
53 const std::string& crl_bundle,
54 const DateTime& time,
55 TrustStore* crl_trust_store) {
56 std::unique_ptr<CastCRL> crl =
57 ParseAndVerifyCRL(crl_bundle, time, crl_trust_store);
58
59 bool success = (crl != nullptr) == (expected_result == kResultSuccess);
60 EXPECT_TRUE(success);
61 return success;
62 }
63
64 // Verifies that the certificate chain provided is not revoked according to
65 // the provided Cast CRL at |cert_time|.
66 // The provided CRL is verified at |crl_time|.
67 // If |crl_required| is set, then a valid Cast CRL must be provided.
68 // Otherwise, a missing CRL is be ignored.
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)69 bool TestVerifyRevocation(Error::Code expected_result,
70 const std::vector<std::string>& der_certs,
71 const std::string& crl_bundle,
72 const DateTime& crl_time,
73 const DateTime& cert_time,
74 bool crl_required,
75 TrustStore* cast_trust_store,
76 TrustStore* crl_trust_store) {
77 std::unique_ptr<CastCRL> crl;
78 if (!crl_bundle.empty()) {
79 crl = ParseAndVerifyCRL(crl_bundle, crl_time, crl_trust_store);
80 EXPECT_NE(crl.get(), nullptr);
81 }
82
83 std::unique_ptr<CertVerificationContext> context;
84 CastDeviceCertPolicy policy;
85 CRLPolicy crl_policy =
86 crl_required ? CRLPolicy::kCrlRequired : CRLPolicy::kCrlOptional;
87 Error result = VerifyDeviceCert(der_certs, cert_time, &context, &policy,
88 crl.get(), crl_policy, cast_trust_store);
89 EXPECT_EQ(expected_result, result.code());
90 return expected_result == result.code();
91 }
92
GetSpecificTestDataPath()93 const std::string& GetSpecificTestDataPath() {
94 static std::string data_path = GetTestDataPath() + "cast/common/certificate/";
95 return data_path;
96 }
97
RunTest(const DeviceCertTest & test_case)98 bool RunTest(const DeviceCertTest& test_case) {
99 std::unique_ptr<TrustStore> crl_trust_store;
100 std::unique_ptr<TrustStore> cast_trust_store;
101 if (test_case.use_test_trust_anchors()) {
102 crl_trust_store = std::make_unique<TrustStore>();
103 cast_trust_store = std::make_unique<TrustStore>();
104 *crl_trust_store = TrustStore::CreateInstanceFromPemFile(
105 GetSpecificTestDataPath() + "certificates/cast_crl_test_root_ca.pem");
106 *cast_trust_store = TrustStore::CreateInstanceFromPemFile(
107 GetSpecificTestDataPath() + "certificates/cast_test_root_ca.pem");
108
109 EXPECT_FALSE(crl_trust_store->certs.empty());
110 EXPECT_FALSE(cast_trust_store->certs.empty());
111 }
112
113 std::vector<std::string> der_cert_path;
114 for (const auto& cert : test_case.der_cert_path()) {
115 der_cert_path.push_back(cert);
116 }
117
118 DateTime cert_verification_time;
119 EXPECT_TRUE(DateTimeFromSeconds(test_case.cert_verification_time_seconds(),
120 &cert_verification_time));
121
122 uint64_t crl_verify_time = test_case.crl_verification_time_seconds();
123 DateTime crl_verification_time;
124 EXPECT_TRUE(DateTimeFromSeconds(crl_verify_time, &crl_verification_time));
125 if (crl_verify_time == 0) {
126 crl_verification_time = cert_verification_time;
127 }
128
129 std::string crl_bundle = test_case.crl_bundle();
130 switch (test_case.expected_result()) {
131 case ::cast::certificate::PATH_VERIFICATION_FAILED:
132 return TestVerifyCertificate(kResultFail, der_cert_path,
133 cert_verification_time,
134 cast_trust_store.get());
135 case ::cast::certificate::CRL_VERIFICATION_FAILED:
136 return TestVerifyCRL(kResultFail, crl_bundle, crl_verification_time,
137 crl_trust_store.get());
138 case ::cast::certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL:
139 return TestVerifyCertificate(kResultSuccess, der_cert_path,
140 cert_verification_time,
141 cast_trust_store.get()) &&
142 TestVerifyCRL(kResultFail, crl_bundle, crl_verification_time,
143 crl_trust_store.get()) &&
144 TestVerifyRevocation(
145 Error::Code::kErrCrlInvalid, der_cert_path, crl_bundle,
146 crl_verification_time, cert_verification_time, true,
147 cast_trust_store.get(), crl_trust_store.get());
148 case ::cast::certificate::
149 CRL_EXPIRED_AFTER_INITIAL_VERIFICATION: // fallthrough
150 case ::cast::certificate::REVOCATION_CHECK_FAILED:
151 return TestVerifyCertificate(kResultSuccess, der_cert_path,
152 cert_verification_time,
153 cast_trust_store.get()) &&
154 TestVerifyCRL(kResultSuccess, crl_bundle, crl_verification_time,
155 crl_trust_store.get()) &&
156 TestVerifyRevocation(
157 Error::Code::kErrCertsRevoked, der_cert_path, crl_bundle,
158 crl_verification_time, cert_verification_time, true,
159 cast_trust_store.get(), crl_trust_store.get());
160 case ::cast::certificate::SUCCESS:
161 return (crl_bundle.empty() ||
162 TestVerifyCRL(kResultSuccess, crl_bundle, crl_verification_time,
163 crl_trust_store.get())) &&
164 TestVerifyCertificate(kResultSuccess, der_cert_path,
165 cert_verification_time,
166 cast_trust_store.get()) &&
167 TestVerifyRevocation(Error::Code::kNone, der_cert_path, crl_bundle,
168 crl_verification_time, cert_verification_time,
169 !crl_bundle.empty(), cast_trust_store.get(),
170 crl_trust_store.get());
171 case ::cast::certificate::UNSPECIFIED:
172 return false;
173 }
174 return false;
175 }
176
177 // Parses the provided test suite provided in wire-format proto.
178 // Each test contains the inputs and the expected output.
179 // To see the description of the test, execute the test.
180 // These tests are generated by a test generator in google3.
RunTestSuite(const std::string & test_suite_file_name)181 void RunTestSuite(const std::string& test_suite_file_name) {
182 std::string testsuite_raw = ReadEntireFileToString(test_suite_file_name);
183 ASSERT_FALSE(testsuite_raw.empty());
184 DeviceCertTestSuite test_suite;
185 ASSERT_TRUE(test_suite.ParseFromString(testsuite_raw));
186 int successes = 0;
187
188 for (auto const& test_case : test_suite.tests()) {
189 bool result = RunTest(test_case);
190 successes += result;
191 EXPECT_TRUE(result) << test_case.description();
192 }
193 OSP_LOG_IF(ERROR, successes != test_suite.tests().size())
194 << "successes: " << successes
195 << ", failures: " << (test_suite.tests().size() - successes);
196 }
197
TEST(CastCertificateTest,TestSuite1)198 TEST(CastCertificateTest, TestSuite1) {
199 RunTestSuite(GetSpecificTestDataPath() + "testsuite/testsuite1.pb");
200 }
201
202 } // namespace
203 } // namespace cast
204 } // namespace openscreen
205