123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- // Copyright 2014 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 "components/cast_channel/cast_auth_util.h"
- #include <string>
- #include "base/files/file_util.h"
- #include "base/logging.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/time/time.h"
- #include "components/cast_certificate/cast_cert_reader.h"
- #include "components/cast_certificate/cast_cert_test_helpers.h"
- #include "components/cast_certificate/cast_cert_validator.h"
- #include "components/cast_certificate/cast_crl.h"
- #include "net/cert/pki/trust_store_in_memory.h"
- #include "net/cert/x509_certificate.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/openscreen/src/cast/common/certificate/proto/test_suite.pb.h"
- #include "third_party/openscreen/src/cast/common/channel/proto/cast_channel.pb.h"
- using cast::channel::SHA1;
- using cast::channel::SHA256;
- namespace cast_channel {
- namespace {
- class CastAuthUtilTest : public testing::Test {
- public:
- CastAuthUtilTest() {}
- ~CastAuthUtilTest() override {}
- void SetUp() override {}
- protected:
- static AuthResponse CreateAuthResponse(
- std::string* signed_data,
- cast::channel::HashAlgorithm digest_algorithm) {
- auto chain = cast_certificate::ReadCertificateChainFromFile(
- cast_certificate::testing::GetCastCertificatesSubDirectory()
- .AppendASCII("chromecast_gen1.pem"));
- CHECK(!chain.empty());
- auto signature_data = cast_certificate::testing::ReadSignatureTestData(
- "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
- AuthResponse response;
- response.set_client_auth_certificate(chain[0]);
- for (size_t i = 1; i < chain.size(); ++i)
- response.add_intermediate_certificate(chain[i]);
- response.set_hash_algorithm(digest_algorithm);
- switch (digest_algorithm) {
- case SHA1:
- response.set_signature(signature_data.signature_sha1);
- break;
- case SHA256:
- response.set_signature(signature_data.signature_sha256);
- break;
- }
- *signed_data = signature_data.message;
- return response;
- }
- // Mangles a string by inverting the first byte.
- static void MangleString(std::string* str) { (*str)[0] = ~(*str)[0]; }
- };
- // Note on expiration: VerifyCredentials() depends on the system clock. In
- // practice this shouldn't be a problem though since the certificate chain
- // being verified doesn't expire until 2032!
- TEST_F(CastAuthUtilTest, VerifySuccess) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- base::Time now = base::Time::Now();
- AuthResult result = VerifyCredentialsForTest(
- auth_response, signed_data, cast_certificate::CRLPolicy::CRL_OPTIONAL,
- nullptr, nullptr, now);
- EXPECT_TRUE(result.success());
- EXPECT_EQ(static_cast<unsigned>(AuthResult::POLICY_NONE),
- result.channel_policies);
- }
- TEST_F(CastAuthUtilTest, VerifyBadCA) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- MangleString(auth_response.mutable_intermediate_certificate(0));
- AuthResult result = VerifyCredentials(auth_response, signed_data);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_CERT_PARSING_FAILED, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyBadClientAuthCert) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- MangleString(auth_response.mutable_client_auth_certificate());
- AuthResult result = VerifyCredentials(auth_response, signed_data);
- EXPECT_FALSE(result.success());
- // TODO(eroman): Not quite right of an error.
- EXPECT_EQ(AuthResult::ERROR_CERT_PARSING_FAILED, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyBadSignature) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- MangleString(auth_response.mutable_signature());
- AuthResult result = VerifyCredentials(auth_response, signed_data);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyEmptySignature) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- auth_response.mutable_signature()->clear();
- AuthResult result = VerifyCredentials(auth_response, signed_data);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_SIGNATURE_EMPTY, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyUnsupportedDigest) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- base::Feature{"CastSHA256Enforced", base::FEATURE_DISABLED_BY_DEFAULT});
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA1);
- base::Time now = base::Time::Now();
- AuthResult result = VerifyCredentialsForTest(
- auth_response, signed_data, cast_certificate::CRLPolicy::CRL_OPTIONAL,
- nullptr, nullptr, now);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_DIGEST_UNSUPPORTED, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyBackwardsCompatibleDigest) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA1);
- base::Time now = base::Time::Now();
- AuthResult result = VerifyCredentialsForTest(
- auth_response, signed_data, cast_certificate::CRLPolicy::CRL_OPTIONAL,
- nullptr, nullptr, now);
- EXPECT_TRUE(result.success());
- }
- TEST_F(CastAuthUtilTest, VerifyBadPeerCert) {
- std::string signed_data;
- AuthResponse auth_response = CreateAuthResponse(&signed_data, SHA256);
- MangleString(&signed_data);
- AuthResult result = VerifyCredentials(auth_response, signed_data);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifySenderNonceMatch) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
- AuthContext context = AuthContext::Create();
- AuthResult result = context.VerifySenderNonce(context.nonce());
- EXPECT_TRUE(result.success());
- }
- TEST_F(CastAuthUtilTest, VerifySenderNonceMismatch) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
- AuthContext context = AuthContext::Create();
- std::string received_nonce = "test2";
- EXPECT_NE(received_nonce, context.nonce());
- AuthResult result = context.VerifySenderNonce(received_nonce);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifySenderNonceMissing) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
- AuthContext context = AuthContext::Create();
- std::string received_nonce;
- EXPECT_FALSE(context.nonce().empty());
- AuthResult result = context.VerifySenderNonce(received_nonce);
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyTLSCertificateSuccess) {
- auto tls_cert_der = cast_certificate::ReadCertificateChainFromFile(
- cast_certificate::testing::GetCastCertificatesSubDirectory().AppendASCII(
- "test_tls_cert.pem"));
- scoped_refptr<net::X509Certificate> tls_cert =
- net::X509Certificate::CreateFromBytes(
- base::as_bytes(base::make_span(tls_cert_der[0])));
- std::string peer_cert_der;
- AuthResult result =
- VerifyTLSCertificate(*tls_cert, &peer_cert_der, tls_cert->valid_start());
- EXPECT_TRUE(result.success());
- }
- TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooEarly) {
- auto tls_cert_der = cast_certificate::ReadCertificateChainFromFile(
- cast_certificate::testing::GetCastCertificatesSubDirectory().AppendASCII(
- "test_tls_cert.pem"));
- scoped_refptr<net::X509Certificate> tls_cert =
- net::X509Certificate::CreateFromBytes(
- base::as_bytes(base::make_span(tls_cert_der[0])));
- std::string peer_cert_der;
- AuthResult result = VerifyTLSCertificate(
- *tls_cert, &peer_cert_der, tls_cert->valid_start() - base::Seconds(1));
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE,
- result.error_type);
- }
- TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooLate) {
- auto tls_cert_der = cast_certificate::ReadCertificateChainFromFile(
- cast_certificate::testing::GetCastCertificatesSubDirectory().AppendASCII(
- "test_tls_cert.pem"));
- scoped_refptr<net::X509Certificate> tls_cert =
- net::X509Certificate::CreateFromBytes(
- base::as_bytes(base::make_span(tls_cert_der[0])));
- std::string peer_cert_der;
- AuthResult result = VerifyTLSCertificate(
- *tls_cert, &peer_cert_der, tls_cert->valid_expiry() + base::Seconds(2));
- EXPECT_FALSE(result.success());
- EXPECT_EQ(AuthResult::ERROR_TLS_CERT_EXPIRED, result.error_type);
- }
- // Indicates the expected result of test step's verification.
- enum TestStepResult {
- RESULT_SUCCESS,
- RESULT_FAIL,
- };
- // Verifies that the certificate chain provided is not revoked according to
- // the provided Cast CRL at |verification_time|.
- // The provided CRL is verified at |verification_time|.
- // If |crl_required| is set, then a valid Cast CRL must be provided.
- // Otherwise, a missing CRL is be ignored.
- AuthResult TestVerifyRevocation(
- const std::vector<std::string>& certificate_chain,
- const std::string& crl_bundle,
- const base::Time& verification_time,
- bool crl_required,
- net::TrustStore* cast_trust_store,
- net::TrustStore* crl_trust_store) {
- AuthResponse response;
- if (certificate_chain.size() > 0) {
- response.set_client_auth_certificate(certificate_chain[0]);
- for (size_t i = 1; i < certificate_chain.size(); ++i)
- response.add_intermediate_certificate(certificate_chain[i]);
- }
- response.set_crl(crl_bundle);
- cast_certificate::CRLPolicy crl_policy =
- cast_certificate::CRLPolicy::CRL_REQUIRED;
- if (!crl_required && crl_bundle.empty())
- crl_policy = cast_certificate::CRLPolicy::CRL_OPTIONAL;
- AuthResult result =
- VerifyCredentialsForTest(response, "", crl_policy, cast_trust_store,
- crl_trust_store, verification_time);
- // This test doesn't set the signature so it will just fail there.
- EXPECT_FALSE(result.success());
- return result;
- }
- // Runs a single test case.
- bool RunTest(const cast::certificate::DeviceCertTest& test_case) {
- std::unique_ptr<net::TrustStoreInMemory> cast_trust_store =
- test_case.use_test_trust_anchors()
- ? cast_certificate::testing::LoadTestCert("cast_test_root_ca.pem")
- : nullptr;
- std::unique_ptr<net::TrustStoreInMemory> crl_trust_store =
- test_case.use_test_trust_anchors()
- ? cast_certificate::testing::LoadTestCert("cast_crl_test_root_ca.pem")
- : nullptr;
- std::vector<std::string> certificate_chain;
- for (auto const& cert : test_case.der_cert_path()) {
- certificate_chain.push_back(cert);
- }
- // CastAuthUtil verifies the CRL at the same time as the certificate.
- base::Time verification_time;
- uint64_t cert_verify_time = test_case.cert_verification_time_seconds();
- if (cert_verify_time) {
- verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds(
- cert_verify_time);
- } else {
- verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds(
- test_case.crl_verification_time_seconds());
- }
- std::string crl_bundle = test_case.crl_bundle();
- AuthResult result;
- switch (test_case.expected_result()) {
- case cast::certificate::PATH_VERIFICATION_FAILED:
- result = TestVerifyRevocation(
- certificate_chain, crl_bundle, verification_time, false,
- cast_trust_store.get(), crl_trust_store.get());
- EXPECT_EQ(result.error_type,
- AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA);
- return result.error_type ==
- AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA;
- case cast::certificate::CRL_VERIFICATION_FAILED:
- // Fall-through intended.
- case cast::certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL:
- result = TestVerifyRevocation(
- certificate_chain, crl_bundle, verification_time, true,
- cast_trust_store.get(), crl_trust_store.get());
- EXPECT_EQ(result.error_type, AuthResult::ERROR_CRL_INVALID);
- return result.error_type == AuthResult::ERROR_CRL_INVALID;
- case cast::certificate::CRL_EXPIRED_AFTER_INITIAL_VERIFICATION:
- // By-pass this test because CRL is always verified at the time the
- // certificate is verified.
- return true;
- case cast::certificate::REVOCATION_CHECK_FAILED:
- result = TestVerifyRevocation(
- certificate_chain, crl_bundle, verification_time, true,
- cast_trust_store.get(), crl_trust_store.get());
- EXPECT_EQ(result.error_type, AuthResult::ERROR_CERT_REVOKED);
- return result.error_type == AuthResult::ERROR_CERT_REVOKED;
- case cast::certificate::SUCCESS:
- result = TestVerifyRevocation(
- certificate_chain, crl_bundle, verification_time, false,
- cast_trust_store.get(), crl_trust_store.get());
- EXPECT_EQ(result.error_type, AuthResult::ERROR_SIGNED_BLOBS_MISMATCH);
- return result.error_type == AuthResult::ERROR_SIGNED_BLOBS_MISMATCH;
- 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;
- base::ReadFileToString(
- cast_certificate::testing::GetCastCertificateDirectory().AppendASCII(
- test_suite_file_name),
- &testsuite_raw);
- cast::certificate::DeviceCertTestSuite test_suite;
- EXPECT_TRUE(test_suite.ParseFromString(testsuite_raw));
- uint16_t success = 0;
- uint16_t failed = 0;
- std::vector<std::string> failed_tests;
- for (auto const& test_case : test_suite.tests()) {
- LOG(INFO) << "[ RUN ] " << test_case.description();
- bool result = RunTest(test_case);
- EXPECT_TRUE(result);
- if (!result) {
- LOG(INFO) << "[ FAILED ] " << test_case.description();
- ++failed;
- failed_tests.push_back(test_case.description());
- } else {
- LOG(INFO) << "[ PASSED ] " << test_case.description();
- ++success;
- }
- }
- LOG(INFO) << "[ PASSED ] " << success << " test(s).";
- if (failed) {
- LOG(INFO) << "[ FAILED ] " << failed << " test(s), listed below:";
- for (const auto& failed_test : failed_tests) {
- LOG(INFO) << "[ FAILED ] " << failed_test;
- }
- }
- }
- TEST_F(CastAuthUtilTest, CRLTestSuite) {
- RunTestSuite("testsuite/testsuite1.pb");
- }
- } // namespace
- } // namespace cast_channel
|