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/sender/channel/cast_auth_util.h"
6
7 #include <string>
8
9 #include "cast/common/certificate/cast_cert_validator.h"
10 #include "cast/common/certificate/cast_crl.h"
11 #include "cast/common/certificate/proto/test_suite.pb.h"
12 #include "cast/common/certificate/testing/test_helpers.h"
13 #include "cast/common/channel/proto/cast_channel.pb.h"
14 #include "gtest/gtest.h"
15 #include "platform/api/time.h"
16 #include "testing/util/read_file.h"
17 #include "util/logging.h"
18
19 namespace openscreen {
20 namespace cast {
21
22 // TODO(crbug.com/openscreen/90): Remove these after Chromium is migrated to
23 // openscreen::cast
24 using DeviceCertTestSuite = ::cast::certificate::DeviceCertTestSuite;
25 using VerificationResult = ::cast::certificate::VerificationResult;
26 using DeviceCertTest = ::cast::certificate::DeviceCertTest;
27
28 namespace {
29
30 using ::cast::channel::AuthResponse;
31
ConvertTimeSeconds(const DateTime & time,uint64_t * seconds)32 bool ConvertTimeSeconds(const DateTime& time, uint64_t* seconds) {
33 static constexpr uint64_t kDaysPerYear = 365;
34 static constexpr uint64_t kHoursPerDay = 24;
35 static constexpr uint64_t kMinutesPerHour = 60;
36 static constexpr uint64_t kSecondsPerMinute = 60;
37
38 static constexpr uint64_t kSecondsPerDay =
39 kSecondsPerMinute * kMinutesPerHour * kHoursPerDay;
40 static constexpr uint64_t kDaysPerQuadYear = 4 * kDaysPerYear + 1;
41 static constexpr uint64_t kDaysPerCentury =
42 kDaysPerQuadYear * 24 + kDaysPerYear * 4;
43 static constexpr uint64_t kDaysPerQuadCentury = 4 * kDaysPerCentury + 1;
44
45 static constexpr uint64_t kDaysPerMonth[] = {
46 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
47 };
48
49 bool is_leap_year =
50 (time.year % 4 == 0 && (time.year % 100 != 0 || time.year % 400 == 0));
51 if (time.year < 1970 || time.month < 1 || time.day < 1 ||
52 time.day > (kDaysPerMonth[time.month - 1] + is_leap_year) ||
53 time.month > 12 || time.hour > 23 || time.minute > 59 ||
54 time.second > 60) {
55 return false;
56 }
57 uint64_t result = 0;
58 uint64_t year = time.year - 1970;
59 uint64_t first_two_years = year >= 2;
60 result += first_two_years * 2 * kDaysPerYear * kSecondsPerDay;
61 year -= first_two_years * 2;
62
63 if (first_two_years) {
64 uint64_t twenty_eight_years = year >= 28;
65 result += twenty_eight_years * 7 * kDaysPerQuadYear * kSecondsPerDay;
66 year -= twenty_eight_years * 28;
67
68 if (twenty_eight_years) {
69 uint64_t quad_centuries = year / 400;
70 result += quad_centuries * kDaysPerQuadCentury * kSecondsPerDay;
71 year -= quad_centuries * 400;
72
73 uint64_t first_century = year >= 100;
74 result += first_century * (kDaysPerCentury + 1) * kSecondsPerDay;
75 year -= first_century * 100;
76
77 uint64_t centuries = year / 100;
78 result += centuries * kDaysPerCentury * kSecondsPerDay;
79 year -= centuries * 100;
80 }
81
82 uint64_t quad_years = year / 4;
83 result += quad_years * kDaysPerQuadYear * kSecondsPerDay;
84 year -= quad_years * 4;
85
86 uint64_t first_year = year >= 1;
87 result += first_year * (kDaysPerYear + 1) * kSecondsPerDay;
88 year -= first_year;
89
90 result += year * kDaysPerYear * kSecondsPerDay;
91 OSP_DCHECK_LE(year, 2);
92 }
93
94 for (int i = 0; i < time.month - 1; ++i) {
95 uint64_t days = kDaysPerMonth[i];
96 result += days * kSecondsPerDay;
97 }
98 if (time.month >= 3 && is_leap_year) {
99 result += kSecondsPerDay;
100 }
101 result += (time.day - 1) * kSecondsPerDay;
102 result += time.hour * kMinutesPerHour * kSecondsPerMinute;
103 result += time.minute * kSecondsPerMinute;
104 result += time.second;
105
106 *seconds = result;
107 return true;
108 }
109
110 #define TEST_DATA_PREFIX OPENSCREEN_TEST_DATA_DIR "cast/common/certificate/"
111
112 class CastAuthUtilTest : public ::testing::Test {
113 public:
CastAuthUtilTest()114 CastAuthUtilTest() {}
~CastAuthUtilTest()115 ~CastAuthUtilTest() override {}
116
SetUp()117 void SetUp() override {}
118
119 protected:
CreateAuthResponse(std::vector<uint8_t> * signed_data,::cast::channel::HashAlgorithm digest_algorithm)120 static AuthResponse CreateAuthResponse(
121 std::vector<uint8_t>* signed_data,
122 ::cast::channel::HashAlgorithm digest_algorithm) {
123 std::vector<std::string> chain = testing::ReadCertificatesFromPemFile(
124 TEST_DATA_PREFIX "certificates/chromecast_gen1.pem");
125 OSP_CHECK(!chain.empty());
126
127 testing::SignatureTestData signatures = testing::ReadSignatureTestData(
128 TEST_DATA_PREFIX "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
129
130 AuthResponse response;
131
132 response.set_client_auth_certificate(chain[0]);
133 for (size_t i = 1; i < chain.size(); ++i) {
134 response.add_intermediate_certificate(chain[i]);
135 }
136
137 response.set_hash_algorithm(digest_algorithm);
138 switch (digest_algorithm) {
139 case ::cast::channel::SHA1:
140 response.set_signature(
141 std::string(reinterpret_cast<const char*>(signatures.sha1.data),
142 signatures.sha1.length));
143 break;
144 case ::cast::channel::SHA256:
145 response.set_signature(
146 std::string(reinterpret_cast<const char*>(signatures.sha256.data),
147 signatures.sha256.length));
148 break;
149 }
150 *signed_data = std::vector<uint8_t>(
151 signatures.message.data,
152 signatures.message.data + signatures.message.length);
153
154 return response;
155 }
156
157 // Mangles a string by inverting the first byte.
MangleString(std::string * str)158 static void MangleString(std::string* str) { (*str)[0] = ~(*str)[0]; }
159
160 // Mangles a vector by inverting the first byte.
MangleData(std::vector<uint8_t> * data)161 static void MangleData(std::vector<uint8_t>* data) {
162 (*data)[0] = ~(*data)[0];
163 }
164 };
165
166 // Note on expiration: VerifyCredentials() depends on the system clock. In
167 // practice this shouldn't be a problem though since the certificate chain
168 // being verified doesn't expire until 2032.
TEST_F(CastAuthUtilTest,VerifySuccess)169 TEST_F(CastAuthUtilTest, VerifySuccess) {
170 std::vector<uint8_t> signed_data;
171 AuthResponse auth_response =
172 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
173 DateTime now = {};
174 ASSERT_TRUE(DateTimeFromSeconds(GetWallTimeSinceUnixEpoch().count(), &now));
175 ErrorOr<CastDeviceCertPolicy> result =
176 VerifyCredentialsForTest(auth_response, signed_data,
177 CRLPolicy::kCrlOptional, nullptr, nullptr, now);
178 EXPECT_TRUE(result);
179 EXPECT_EQ(CastDeviceCertPolicy::kUnrestricted, result.value());
180 }
181
TEST_F(CastAuthUtilTest,VerifyBadCA)182 TEST_F(CastAuthUtilTest, VerifyBadCA) {
183 std::vector<uint8_t> signed_data;
184 AuthResponse auth_response =
185 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
186 MangleString(auth_response.mutable_intermediate_certificate(0));
187 ErrorOr<CastDeviceCertPolicy> result =
188 VerifyCredentials(auth_response, signed_data);
189 EXPECT_FALSE(result);
190 EXPECT_EQ(Error::Code::kErrCertsParse, result.error().code());
191 }
192
TEST_F(CastAuthUtilTest,VerifyBadClientAuthCert)193 TEST_F(CastAuthUtilTest, VerifyBadClientAuthCert) {
194 std::vector<uint8_t> signed_data;
195 AuthResponse auth_response =
196 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
197 MangleString(auth_response.mutable_client_auth_certificate());
198 ErrorOr<CastDeviceCertPolicy> result =
199 VerifyCredentials(auth_response, signed_data);
200 EXPECT_FALSE(result);
201 EXPECT_EQ(Error::Code::kErrCertsParse, result.error().code());
202 }
203
TEST_F(CastAuthUtilTest,VerifyBadSignature)204 TEST_F(CastAuthUtilTest, VerifyBadSignature) {
205 std::vector<uint8_t> signed_data;
206 AuthResponse auth_response =
207 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
208 MangleString(auth_response.mutable_signature());
209 ErrorOr<CastDeviceCertPolicy> result =
210 VerifyCredentials(auth_response, signed_data);
211 EXPECT_FALSE(result);
212 EXPECT_EQ(Error::Code::kCastV2SignedBlobsMismatch, result.error().code());
213 }
214
TEST_F(CastAuthUtilTest,VerifyEmptySignature)215 TEST_F(CastAuthUtilTest, VerifyEmptySignature) {
216 std::vector<uint8_t> signed_data;
217 AuthResponse auth_response =
218 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
219 auth_response.mutable_signature()->clear();
220 ErrorOr<CastDeviceCertPolicy> result =
221 VerifyCredentials(auth_response, signed_data);
222 EXPECT_FALSE(result);
223 EXPECT_EQ(Error::Code::kCastV2SignatureEmpty, result.error().code());
224 }
225
TEST_F(CastAuthUtilTest,VerifyUnsupportedDigest)226 TEST_F(CastAuthUtilTest, VerifyUnsupportedDigest) {
227 std::vector<uint8_t> signed_data;
228 AuthResponse auth_response =
229 CreateAuthResponse(&signed_data, ::cast::channel::SHA1);
230 DateTime now = {};
231 ASSERT_TRUE(DateTimeFromSeconds(GetWallTimeSinceUnixEpoch().count(), &now));
232 ErrorOr<CastDeviceCertPolicy> result = VerifyCredentialsForTest(
233 auth_response, signed_data, CRLPolicy::kCrlOptional, nullptr, nullptr,
234 now, true);
235 EXPECT_FALSE(result);
236 EXPECT_EQ(Error::Code::kCastV2DigestUnsupported, result.error().code());
237 }
238
TEST_F(CastAuthUtilTest,VerifyBackwardsCompatibleDigest)239 TEST_F(CastAuthUtilTest, VerifyBackwardsCompatibleDigest) {
240 std::vector<uint8_t> signed_data;
241 AuthResponse auth_response =
242 CreateAuthResponse(&signed_data, ::cast::channel::SHA1);
243 DateTime now = {};
244 ASSERT_TRUE(DateTimeFromSeconds(GetWallTimeSinceUnixEpoch().count(), &now));
245 ErrorOr<CastDeviceCertPolicy> result =
246 VerifyCredentialsForTest(auth_response, signed_data,
247 CRLPolicy::kCrlOptional, nullptr, nullptr, now);
248 EXPECT_TRUE(result);
249 }
250
TEST_F(CastAuthUtilTest,VerifyBadPeerCert)251 TEST_F(CastAuthUtilTest, VerifyBadPeerCert) {
252 std::vector<uint8_t> signed_data;
253 AuthResponse auth_response =
254 CreateAuthResponse(&signed_data, ::cast::channel::SHA256);
255 MangleData(&signed_data);
256 ErrorOr<CastDeviceCertPolicy> result =
257 VerifyCredentials(auth_response, signed_data);
258 EXPECT_FALSE(result);
259 EXPECT_EQ(Error::Code::kCastV2SignedBlobsMismatch, result.error().code());
260 }
261
TEST_F(CastAuthUtilTest,VerifySenderNonceMatch)262 TEST_F(CastAuthUtilTest, VerifySenderNonceMatch) {
263 AuthContext context = AuthContext::Create();
264 const Error result = context.VerifySenderNonce(context.nonce(), true);
265 EXPECT_TRUE(result.ok());
266 }
267
TEST_F(CastAuthUtilTest,VerifySenderNonceMismatch)268 TEST_F(CastAuthUtilTest, VerifySenderNonceMismatch) {
269 AuthContext context = AuthContext::Create();
270 std::string received_nonce = "test2";
271 EXPECT_NE(received_nonce, context.nonce());
272 ErrorOr<CastDeviceCertPolicy> result =
273 context.VerifySenderNonce(received_nonce, true);
274 EXPECT_FALSE(result);
275 EXPECT_EQ(Error::Code::kCastV2SenderNonceMismatch, result.error().code());
276 }
277
TEST_F(CastAuthUtilTest,VerifySenderNonceMissing)278 TEST_F(CastAuthUtilTest, VerifySenderNonceMissing) {
279 AuthContext context = AuthContext::Create();
280 std::string received_nonce;
281 EXPECT_FALSE(context.nonce().empty());
282 ErrorOr<CastDeviceCertPolicy> result =
283 context.VerifySenderNonce(received_nonce, true);
284 EXPECT_FALSE(result);
285 EXPECT_EQ(Error::Code::kCastV2SenderNonceMismatch, result.error().code());
286 }
287
TEST_F(CastAuthUtilTest,VerifyTLSCertificateSuccess)288 TEST_F(CastAuthUtilTest, VerifyTLSCertificateSuccess) {
289 std::vector<std::string> tls_cert_der = testing::ReadCertificatesFromPemFile(
290 TEST_DATA_PREFIX "certificates/test_tls_cert.pem");
291 std::string& der_cert = tls_cert_der[0];
292 const uint8_t* data = (const uint8_t*)der_cert.data();
293 X509* tls_cert = d2i_X509(nullptr, &data, der_cert.size());
294 DateTime not_before;
295 DateTime not_after;
296 ASSERT_TRUE(GetCertValidTimeRange(tls_cert, ¬_before, ¬_after));
297 uint64_t x;
298 ASSERT_TRUE(ConvertTimeSeconds(not_before, &x));
299 std::chrono::seconds s(x);
300
301 const Error result = VerifyTLSCertificateValidity(tls_cert, s);
302 EXPECT_TRUE(result.ok());
303 X509_free(tls_cert);
304 }
305
TEST_F(CastAuthUtilTest,VerifyTLSCertificateTooEarly)306 TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooEarly) {
307 std::vector<std::string> tls_cert_der = testing::ReadCertificatesFromPemFile(
308 TEST_DATA_PREFIX "certificates/test_tls_cert.pem");
309 std::string& der_cert = tls_cert_der[0];
310 const uint8_t* data = (const uint8_t*)der_cert.data();
311 X509* tls_cert = d2i_X509(nullptr, &data, der_cert.size());
312 DateTime not_before;
313 DateTime not_after;
314 ASSERT_TRUE(GetCertValidTimeRange(tls_cert, ¬_before, ¬_after));
315 uint64_t x;
316 ASSERT_TRUE(ConvertTimeSeconds(not_before, &x));
317 std::chrono::seconds s(x - 1);
318
319 ErrorOr<CastDeviceCertPolicy> result =
320 VerifyTLSCertificateValidity(tls_cert, s);
321 EXPECT_FALSE(result);
322 EXPECT_EQ(Error::Code::kCastV2TlsCertValidStartDateInFuture,
323 result.error().code());
324 X509_free(tls_cert);
325 }
326
TEST_F(CastAuthUtilTest,VerifyTLSCertificateTooLate)327 TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooLate) {
328 std::vector<std::string> tls_cert_der = testing::ReadCertificatesFromPemFile(
329 TEST_DATA_PREFIX "certificates/test_tls_cert.pem");
330 std::string& der_cert = tls_cert_der[0];
331 const uint8_t* data = (const uint8_t*)der_cert.data();
332 X509* tls_cert = d2i_X509(nullptr, &data, der_cert.size());
333 DateTime not_before;
334 DateTime not_after;
335 ASSERT_TRUE(GetCertValidTimeRange(tls_cert, ¬_before, ¬_after));
336 uint64_t x;
337 ASSERT_TRUE(ConvertTimeSeconds(not_after, &x));
338 std::chrono::seconds s(x + 2);
339
340 ErrorOr<CastDeviceCertPolicy> result =
341 VerifyTLSCertificateValidity(tls_cert, s);
342 EXPECT_FALSE(result);
343 EXPECT_EQ(Error::Code::kCastV2TlsCertExpired, result.error().code());
344 X509_free(tls_cert);
345 }
346
347 // Indicates the expected result of test step's verification.
348 enum TestStepResult {
349 RESULT_SUCCESS,
350 RESULT_FAIL,
351 };
352
353 // Verifies that the certificate chain provided is not revoked according to
354 // the provided Cast CRL at |verification_time|.
355 // The provided CRL is verified at |verification_time|.
356 // If |crl_required| is set, then a valid Cast CRL must be provided.
357 // Otherwise, a missing CRL is be ignored.
TestVerifyRevocation(const std::vector<std::string> & certificate_chain,const std::string & crl_bundle,const DateTime & verification_time,bool crl_required,TrustStore * cast_trust_store,TrustStore * crl_trust_store)358 ErrorOr<CastDeviceCertPolicy> TestVerifyRevocation(
359 const std::vector<std::string>& certificate_chain,
360 const std::string& crl_bundle,
361 const DateTime& verification_time,
362 bool crl_required,
363 TrustStore* cast_trust_store,
364 TrustStore* crl_trust_store) {
365 AuthResponse response;
366
367 if (certificate_chain.size() > 0) {
368 response.set_client_auth_certificate(certificate_chain[0]);
369 for (size_t i = 1; i < certificate_chain.size(); ++i) {
370 response.add_intermediate_certificate(certificate_chain[i]);
371 }
372 }
373
374 response.set_crl(crl_bundle);
375
376 CRLPolicy crl_policy = CRLPolicy::kCrlRequired;
377 if (!crl_required && crl_bundle.empty())
378 crl_policy = CRLPolicy::kCrlOptional;
379 ErrorOr<CastDeviceCertPolicy> result = VerifyCredentialsForTest(
380 response, std::vector<uint8_t>(), crl_policy, cast_trust_store,
381 crl_trust_store, verification_time);
382 // This test doesn't set the signature so it will just fail there.
383 EXPECT_FALSE(result);
384 return result;
385 }
386
387 // Runs a single test case.
RunTest(const DeviceCertTest & test_case)388 bool RunTest(const DeviceCertTest& test_case) {
389 std::unique_ptr<TrustStore> crl_trust_store;
390 std::unique_ptr<TrustStore> cast_trust_store;
391 if (test_case.use_test_trust_anchors()) {
392 crl_trust_store = testing::CreateTrustStoreFromPemFile(
393 TEST_DATA_PREFIX "certificates/cast_crl_test_root_ca.pem");
394 cast_trust_store = testing::CreateTrustStoreFromPemFile(
395 TEST_DATA_PREFIX "certificates/cast_test_root_ca.pem");
396
397 EXPECT_FALSE(crl_trust_store->certs.empty());
398 EXPECT_FALSE(cast_trust_store->certs.empty());
399 }
400
401 std::vector<std::string> certificate_chain;
402 for (auto const& cert : test_case.der_cert_path()) {
403 certificate_chain.push_back(cert);
404 }
405
406 // CastAuthUtil verifies the CRL at the same time as the certificate.
407 DateTime verification_time;
408 uint64_t cert_verify_time = test_case.cert_verification_time_seconds();
409 if (!cert_verify_time) {
410 cert_verify_time = test_case.crl_verification_time_seconds();
411 }
412 OSP_DCHECK(DateTimeFromSeconds(cert_verify_time, &verification_time));
413
414 std::string crl_bundle = test_case.crl_bundle();
415 ErrorOr<CastDeviceCertPolicy> result(CastDeviceCertPolicy::kUnrestricted);
416 switch (test_case.expected_result()) {
417 case ::cast::certificate::PATH_VERIFICATION_FAILED:
418 result = TestVerifyRevocation(
419 certificate_chain, crl_bundle, verification_time, false,
420 cast_trust_store.get(), crl_trust_store.get());
421 EXPECT_EQ(result.error().code(),
422 Error::Code::kCastV2CertNotSignedByTrustedCa);
423 return result.error().code() ==
424 Error::Code::kCastV2CertNotSignedByTrustedCa;
425 case ::cast::certificate::CRL_VERIFICATION_FAILED:
426 // Fall-through intended.
427 case ::cast::certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL:
428 result = TestVerifyRevocation(
429 certificate_chain, crl_bundle, verification_time, true,
430 cast_trust_store.get(), crl_trust_store.get());
431 EXPECT_EQ(result.error().code(), Error::Code::kErrCrlInvalid);
432 return result.error().code() == Error::Code::kErrCrlInvalid;
433 case ::cast::certificate::CRL_EXPIRED_AFTER_INITIAL_VERIFICATION:
434 // By-pass this test because CRL is always verified at the time the
435 // certificate is verified.
436 return true;
437 case ::cast::certificate::REVOCATION_CHECK_FAILED:
438 result = TestVerifyRevocation(
439 certificate_chain, crl_bundle, verification_time, true,
440 cast_trust_store.get(), crl_trust_store.get());
441 EXPECT_EQ(result.error().code(), Error::Code::kErrCertsRevoked);
442 return result.error().code() == Error::Code::kErrCertsRevoked;
443 case ::cast::certificate::SUCCESS:
444 result = TestVerifyRevocation(
445 certificate_chain, crl_bundle, verification_time, false,
446 cast_trust_store.get(), crl_trust_store.get());
447 EXPECT_EQ(result.error().code(), Error::Code::kCastV2SignedBlobsMismatch);
448 return result.error().code() == Error::Code::kCastV2SignedBlobsMismatch;
449 case ::cast::certificate::UNSPECIFIED:
450 return false;
451 }
452 return false;
453 }
454
455 // Parses the provided test suite provided in wire-format proto.
456 // Each test contains the inputs and the expected output.
457 // To see the description of the test, execute the test.
458 // These tests are generated by a test generator in google3.
RunTestSuite(const std::string & test_suite_file_name)459 void RunTestSuite(const std::string& test_suite_file_name) {
460 std::string testsuite_raw = ReadEntireFileToString(test_suite_file_name);
461 DeviceCertTestSuite test_suite;
462 EXPECT_TRUE(test_suite.ParseFromString(testsuite_raw));
463 uint16_t successes = 0;
464
465 for (auto const& test_case : test_suite.tests()) {
466 bool result = RunTest(test_case);
467 EXPECT_TRUE(result) << test_case.description();
468 successes += result;
469 }
470 OSP_LOG_IF(ERROR, successes != test_suite.tests().size())
471 << "successes: " << successes
472 << ", failures: " << (test_suite.tests().size() - successes);
473 }
474
TEST_F(CastAuthUtilTest,CRLTestSuite)475 TEST_F(CastAuthUtilTest, CRLTestSuite) {
476 RunTestSuite("testsuite/testsuite1.pb");
477 }
478
479 } // namespace
480 } // namespace cast
481 } // namespace openscreen
482