OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "extensions/browser/api/cast_channel/cast_auth_util.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/macros.h" | |
10 #include "base/test/scoped_feature_list.h" | |
11 #include "base/time/time.h" | |
12 #include "components/cast_certificate/cast_cert_validator.h" | |
13 #include "components/cast_certificate/cast_cert_validator_test_helpers.h" | |
14 #include "components/cast_certificate/cast_crl.h" | |
15 #include "components/cast_certificate/proto/test_suite.pb.h" | |
16 #include "extensions/common/api/cast_channel/cast_channel.pb.h" | |
17 #include "net/cert/internal/trust_store_in_memory.h" | |
18 #include "net/cert/x509_certificate.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 | |
21 namespace extensions { | |
22 namespace api { | |
23 namespace cast_channel { | |
24 namespace { | |
25 | |
26 class CastAuthUtilTest : public testing::Test { | |
27 public: | |
28 CastAuthUtilTest() {} | |
29 ~CastAuthUtilTest() override {} | |
30 | |
31 void SetUp() override {} | |
32 | |
33 protected: | |
34 static AuthResponse CreateAuthResponse(std::string* signed_data) { | |
35 auto chain = cast_certificate::testing::ReadCertificateChainFromFile( | |
36 "certificates/chromecast_gen1.pem"); | |
37 CHECK(!chain.empty()); | |
38 | |
39 auto signature_data = cast_certificate::testing::ReadSignatureTestData( | |
40 "signeddata/2ZZBG9_FA8FCA3EF91A.pem"); | |
41 | |
42 AuthResponse response; | |
43 | |
44 response.set_client_auth_certificate(chain[0]); | |
45 for (size_t i = 1; i < chain.size(); ++i) | |
46 response.add_intermediate_certificate(chain[i]); | |
47 | |
48 response.set_signature(signature_data.signature_sha1); | |
49 *signed_data = signature_data.message; | |
50 | |
51 return response; | |
52 } | |
53 | |
54 // Mangles a string by inverting the first byte. | |
55 static void MangleString(std::string* str) { (*str)[0] = ~(*str)[0]; } | |
56 }; | |
57 | |
58 // Note on expiration: VerifyCredentials() depends on the system clock. In | |
59 // practice this shouldn't be a problem though since the certificate chain | |
60 // being verified doesn't expire until 2032! | |
61 TEST_F(CastAuthUtilTest, VerifySuccess) { | |
62 std::string signed_data; | |
63 AuthResponse auth_response = CreateAuthResponse(&signed_data); | |
64 base::Time now = base::Time::Now(); | |
65 AuthResult result = VerifyCredentialsForTest( | |
66 auth_response, signed_data, cast_certificate::CRLPolicy::CRL_OPTIONAL, | |
67 nullptr, nullptr, now); | |
68 EXPECT_TRUE(result.success()); | |
69 EXPECT_EQ(AuthResult::POLICY_NONE, result.channel_policies); | |
70 } | |
71 | |
72 TEST_F(CastAuthUtilTest, VerifyBadCA) { | |
73 std::string signed_data; | |
74 AuthResponse auth_response = CreateAuthResponse(&signed_data); | |
75 MangleString(auth_response.mutable_intermediate_certificate(0)); | |
76 AuthResult result = VerifyCredentials(auth_response, signed_data); | |
77 EXPECT_FALSE(result.success()); | |
78 EXPECT_EQ(AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, result.error_type); | |
79 } | |
80 | |
81 TEST_F(CastAuthUtilTest, VerifyBadClientAuthCert) { | |
82 std::string signed_data; | |
83 AuthResponse auth_response = CreateAuthResponse(&signed_data); | |
84 MangleString(auth_response.mutable_client_auth_certificate()); | |
85 AuthResult result = VerifyCredentials(auth_response, signed_data); | |
86 EXPECT_FALSE(result.success()); | |
87 // TODO(eroman): Not quite right of an error. | |
88 EXPECT_EQ(AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, result.error_type); | |
89 } | |
90 | |
91 TEST_F(CastAuthUtilTest, VerifyBadSignature) { | |
92 std::string signed_data; | |
93 AuthResponse auth_response = CreateAuthResponse(&signed_data); | |
94 MangleString(auth_response.mutable_signature()); | |
95 AuthResult result = VerifyCredentials(auth_response, signed_data); | |
96 EXPECT_FALSE(result.success()); | |
97 EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type); | |
98 } | |
99 | |
100 TEST_F(CastAuthUtilTest, VerifyBadPeerCert) { | |
101 std::string signed_data; | |
102 AuthResponse auth_response = CreateAuthResponse(&signed_data); | |
103 MangleString(&signed_data); | |
104 AuthResult result = VerifyCredentials(auth_response, signed_data); | |
105 EXPECT_FALSE(result.success()); | |
106 EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type); | |
107 } | |
108 | |
109 TEST_F(CastAuthUtilTest, VerifySenderNonceMatch) { | |
110 base::test::ScopedFeatureList scoped_feature_list; | |
111 scoped_feature_list.InitAndEnableFeature( | |
112 base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT}); | |
113 AuthContext context = AuthContext::Create(); | |
114 AuthResult result = context.VerifySenderNonce(context.nonce()); | |
115 EXPECT_TRUE(result.success()); | |
116 } | |
117 | |
118 TEST_F(CastAuthUtilTest, VerifySenderNonceMismatch) { | |
119 base::test::ScopedFeatureList scoped_feature_list; | |
120 scoped_feature_list.InitAndEnableFeature( | |
121 base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT}); | |
122 AuthContext context = AuthContext::Create(); | |
123 std::string received_nonce = "test2"; | |
124 EXPECT_NE(received_nonce, context.nonce()); | |
125 AuthResult result = context.VerifySenderNonce(received_nonce); | |
126 EXPECT_FALSE(result.success()); | |
127 EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type); | |
128 } | |
129 | |
130 TEST_F(CastAuthUtilTest, VerifySenderNonceMissing) { | |
131 base::test::ScopedFeatureList scoped_feature_list; | |
132 scoped_feature_list.InitAndEnableFeature( | |
133 base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT}); | |
134 AuthContext context = AuthContext::Create(); | |
135 std::string received_nonce = ""; | |
136 EXPECT_FALSE(context.nonce().empty()); | |
137 AuthResult result = context.VerifySenderNonce(received_nonce); | |
138 EXPECT_FALSE(result.success()); | |
139 EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type); | |
140 } | |
141 | |
142 TEST_F(CastAuthUtilTest, VerifyTLSCertificateSuccess) { | |
143 auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile( | |
144 "certificates/test_tls_cert.pem"); | |
145 | |
146 scoped_refptr<net::X509Certificate> tls_cert = | |
147 net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(), | |
148 tls_cert_der[0].size()); | |
149 std::string peer_cert_der; | |
150 AuthResult result = | |
151 VerifyTLSCertificate(*tls_cert, &peer_cert_der, tls_cert->valid_start()); | |
152 EXPECT_TRUE(result.success()); | |
153 } | |
154 | |
155 TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooEarly) { | |
156 auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile( | |
157 "certificates/test_tls_cert.pem"); | |
158 | |
159 scoped_refptr<net::X509Certificate> tls_cert = | |
160 net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(), | |
161 tls_cert_der[0].size()); | |
162 std::string peer_cert_der; | |
163 AuthResult result = VerifyTLSCertificate( | |
164 *tls_cert, &peer_cert_der, | |
165 tls_cert->valid_start() - base::TimeDelta::FromSeconds(1)); | |
166 EXPECT_FALSE(result.success()); | |
167 EXPECT_EQ(AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE, | |
168 result.error_type); | |
169 } | |
170 | |
171 TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooLate) { | |
172 auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile( | |
173 "certificates/test_tls_cert.pem"); | |
174 | |
175 scoped_refptr<net::X509Certificate> tls_cert = | |
176 net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(), | |
177 tls_cert_der[0].size()); | |
178 std::string peer_cert_der; | |
179 AuthResult result = VerifyTLSCertificate( | |
180 *tls_cert, &peer_cert_der, | |
181 tls_cert->valid_expiry() + base::TimeDelta::FromSeconds(2)); | |
182 EXPECT_FALSE(result.success()); | |
183 EXPECT_EQ(AuthResult::ERROR_TLS_CERT_EXPIRED, result.error_type); | |
184 } | |
185 | |
186 // Indicates the expected result of test step's verification. | |
187 enum TestStepResult { | |
188 RESULT_SUCCESS, | |
189 RESULT_FAIL, | |
190 }; | |
191 | |
192 // Verifies that the certificate chain provided is not revoked according to | |
193 // the provided Cast CRL at |verification_time|. | |
194 // The provided CRL is verified at |verification_time|. | |
195 // If |crl_required| is set, then a valid Cast CRL must be provided. | |
196 // Otherwise, a missing CRL is be ignored. | |
197 AuthResult TestVerifyRevocation( | |
198 const std::vector<std::string>& certificate_chain, | |
199 const std::string& crl_bundle, | |
200 const base::Time& verification_time, | |
201 bool crl_required, | |
202 net::TrustStore* cast_trust_store, | |
203 net::TrustStore* crl_trust_store) { | |
204 AuthResponse response; | |
205 | |
206 if (certificate_chain.size() > 0) { | |
207 response.set_client_auth_certificate(certificate_chain[0]); | |
208 for (size_t i = 1; i < certificate_chain.size(); ++i) | |
209 response.add_intermediate_certificate(certificate_chain[i]); | |
210 } | |
211 | |
212 response.set_crl(crl_bundle); | |
213 | |
214 cast_certificate::CRLPolicy crl_policy = | |
215 cast_certificate::CRLPolicy::CRL_REQUIRED; | |
216 if (!crl_required && crl_bundle.empty()) | |
217 crl_policy = cast_certificate::CRLPolicy::CRL_OPTIONAL; | |
218 AuthResult result = | |
219 VerifyCredentialsForTest(response, "", crl_policy, cast_trust_store, | |
220 crl_trust_store, verification_time); | |
221 // This test doesn't set the signature so it will just fail there. | |
222 EXPECT_FALSE(result.success()); | |
223 return result; | |
224 } | |
225 | |
226 // Runs a single test case. | |
227 bool RunTest(const cast_certificate::DeviceCertTest& test_case) { | |
228 std::unique_ptr<net::TrustStore> crl_trust_store; | |
229 std::unique_ptr<net::TrustStore> cast_trust_store; | |
230 if (test_case.use_test_trust_anchors()) { | |
231 crl_trust_store = cast_certificate::testing::CreateTrustStoreFromFile( | |
232 "certificates/cast_crl_test_root_ca.pem"); | |
233 cast_trust_store = cast_certificate::testing::CreateTrustStoreFromFile( | |
234 "certificates/cast_test_root_ca.pem"); | |
235 | |
236 EXPECT_TRUE(crl_trust_store.get()); | |
237 EXPECT_TRUE(cast_trust_store.get()); | |
238 } | |
239 | |
240 std::vector<std::string> certificate_chain; | |
241 for (auto const& cert : test_case.der_cert_path()) { | |
242 certificate_chain.push_back(cert); | |
243 } | |
244 | |
245 // CastAuthUtil verifies the CRL at the same time as the certificate. | |
246 base::Time verification_time; | |
247 uint64_t cert_verify_time = test_case.cert_verification_time_seconds(); | |
248 if (cert_verify_time) { | |
249 verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds( | |
250 cert_verify_time); | |
251 } else { | |
252 verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds( | |
253 test_case.crl_verification_time_seconds()); | |
254 } | |
255 | |
256 std::string crl_bundle = test_case.crl_bundle(); | |
257 AuthResult result; | |
258 switch (test_case.expected_result()) { | |
259 case cast_certificate::PATH_VERIFICATION_FAILED: | |
260 result = TestVerifyRevocation( | |
261 certificate_chain, crl_bundle, verification_time, false, | |
262 cast_trust_store.get(), crl_trust_store.get()); | |
263 EXPECT_EQ(result.error_type, | |
264 AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA); | |
265 return result.error_type == | |
266 AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA; | |
267 case cast_certificate::CRL_VERIFICATION_FAILED: | |
268 // Fall-through intended. | |
269 case cast_certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL: | |
270 result = TestVerifyRevocation( | |
271 certificate_chain, crl_bundle, verification_time, true, | |
272 cast_trust_store.get(), crl_trust_store.get()); | |
273 EXPECT_EQ(result.error_type, AuthResult::ERROR_CRL_INVALID); | |
274 return result.error_type == AuthResult::ERROR_CRL_INVALID; | |
275 case cast_certificate::CRL_EXPIRED_AFTER_INITIAL_VERIFICATION: | |
276 // By-pass this test because CRL is always verified at the time the | |
277 // certificate is verified. | |
278 return true; | |
279 case cast_certificate::REVOCATION_CHECK_FAILED: | |
280 result = TestVerifyRevocation( | |
281 certificate_chain, crl_bundle, verification_time, true, | |
282 cast_trust_store.get(), crl_trust_store.get()); | |
283 EXPECT_EQ(result.error_type, AuthResult::ERROR_CERT_REVOKED); | |
284 return result.error_type == AuthResult::ERROR_CERT_REVOKED; | |
285 case cast_certificate::SUCCESS: | |
286 result = TestVerifyRevocation( | |
287 certificate_chain, crl_bundle, verification_time, false, | |
288 cast_trust_store.get(), crl_trust_store.get()); | |
289 EXPECT_EQ(result.error_type, AuthResult::ERROR_SIGNED_BLOBS_MISMATCH); | |
290 return result.error_type == AuthResult::ERROR_SIGNED_BLOBS_MISMATCH; | |
291 case UNSPECIFIED: | |
292 return false; | |
293 } | |
294 return false; | |
295 } | |
296 | |
297 // Parses the provided test suite provided in wire-format proto. | |
298 // Each test contains the inputs and the expected output. | |
299 // To see the description of the test, execute the test. | |
300 // These tests are generated by a test generator in google3. | |
301 void RunTestSuite(const std::string& test_suite_file_name) { | |
302 std::string testsuite_raw = | |
303 cast_certificate::testing::ReadTestFileToString(test_suite_file_name); | |
304 cast_certificate::DeviceCertTestSuite test_suite; | |
305 EXPECT_TRUE(test_suite.ParseFromString(testsuite_raw)); | |
306 uint16_t success = 0; | |
307 uint16_t failed = 0; | |
308 std::vector<std::string> failed_tests; | |
309 | |
310 for (auto const& test_case : test_suite.tests()) { | |
311 LOG(INFO) << "[ RUN ] " << test_case.description(); | |
312 bool result = RunTest(test_case); | |
313 EXPECT_TRUE(result); | |
314 if (!result) { | |
315 LOG(INFO) << "[ FAILED ] " << test_case.description(); | |
316 ++failed; | |
317 failed_tests.push_back(test_case.description()); | |
318 } else { | |
319 LOG(INFO) << "[ PASSED ] " << test_case.description(); | |
320 ++success; | |
321 } | |
322 } | |
323 LOG(INFO) << "[ PASSED ] " << success << " test(s)."; | |
324 if (failed) { | |
325 LOG(INFO) << "[ FAILED ] " << failed << " test(s), listed below:"; | |
326 for (const auto& failed_test : failed_tests) { | |
327 LOG(INFO) << "[ FAILED ] " << failed_test; | |
328 } | |
329 } | |
330 } | |
331 | |
332 TEST_F(CastAuthUtilTest, CRLTestSuite) { | |
333 RunTestSuite("testsuite/testsuite1.pb"); | |
334 } | |
335 | |
336 } // namespace | |
337 } // namespace cast_channel | |
338 } // namespace api | |
339 } // namespace extensions | |
OLD | NEW |