// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine // License Agreement. // /** * @mainpage OEMCrypto Unit Tests * * The OEMCrypto unit tests are designed to verify that an implementation of * OEMCrypto is correctly supporting the OEMCrypto API. * * @defgroup basic Basic Functionality Tests * Basic functionality tests. * * @defgroup provision Provisioning Tests * Test for provisioning and certificate key processing. These tests cover * Provsioning 2.0, 3.0 and 4.0. Tests for the wrong provisioning scheme should * be skipped. * * @defgroup license License Request Tests * Test for requesting and loading licenses. * * @defgroup renewal License Renewal Tests * Tests for renewing licenses. * * @defgroup decrypt Decrypt Tests * Tests for decrypting content. * * @defgroup usage_table Usage Table Tests * Tests that use the usage table. * * @defgroup entitle Entitlement License tests * Tests for entitlement licenses. * * @defgroup cas Conditional Access System Tests * Tests for OEMCrypto implementations that support MediaCAS. * * @defgroup cast Cast Test * Tests for OEMCrypto implementations that support being a Cast receiver. * * @defgroup android Android Tests * Tests that enforce requirements that are specific to Android. * * @defgroup generic Generic Crypto Tests * Tests for the Generic Crypto functionality. * * @defgroup security Security Tests * Buffer overflow tests, off-by-one tests, and other security tests. * * The way the huge buffer tests work is to create a large buffer and then call * the API. The test then loops and doubles the buffer until the API returns an * error. An error is considered a passing test. We expect OEMCrypto to fail * gracefully on a huge buffer rather than crashing. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OEMCryptoCENC.h" #include "clock.h" #include "log.h" #include "oec_decrypt_fallback_chain.h" #include "oec_device_features.h" #include "oec_extra_test_keys.h" #include "oec_session_util.h" #include "oec_test_data.h" #include "oemcrypto_basic_test.h" #include "oemcrypto_corpus_generator_helper.h" #include "oemcrypto_fuzz_structs.h" #include "oemcrypto_license_test.h" #include "oemcrypto_provisioning_test.h" #include "oemcrypto_resource_test.h" #include "oemcrypto_session_tests_helper.h" #include "oemcrypto_types.h" #include "oemcrypto_usage_table_test.h" #include "platform.h" #include "string_conversions.h" #include "test_sleep.h" #include "wvcrc32.h" using ::testing::Range; using ::testing::tuple; using namespace std; namespace std { // GTest wants PrintTo to be in the std namespace. void PrintTo(const tuple& param, ostream* os) { OEMCrypto_CENCEncryptPatternDesc pattern = ::testing::get<0>(param); OEMCryptoCipherMode mode = ::testing::get<1>(param); wvoec::OutputType output = ::testing::get<2>(param); bool decrypt_inplace = output.decrypt_inplace; OEMCryptoBufferType type = output.type; *os << ((mode == OEMCrypto_CipherMode_CENC) ? "CENC mode" : "CBCS mode") << ", pattern=(encrypt:" << pattern.encrypt << ", skip:" << pattern.skip << ")"; switch (type) { case OEMCrypto_BufferType_Clear: *os << ", BufferType = Clear"; break; case OEMCrypto_BufferType_Secure: *os << ", BufferType = Secure"; break; case OEMCrypto_BufferType_Direct: *os << ", BufferType = Direct"; break; default: *os << ", type = "; break; } if (decrypt_inplace) *os << " (in place)"; } } // namespace std namespace wvoec { /// @addtogroup entitle /// @{ class OEMCryptoEntitlementLicenseTest : public OEMCryptoLicenseTest { protected: void LoadEntitlementLicense() { license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } }; /** This verifies that entitlement keys and entitled content keys can be loaded. */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); EntitledMessage entitled_message_2(&license_messages_); entitled_message_2.FillKeyArray(); entitled_message_2.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(true)); } /** * This verifies that entitled content keys cannot be loaded if we have not yet * loaded the entitlement keys. */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(false)); } /** * This verifies that entitled content keys cannot be loaded if we have loaded * the wrong entitlement keys. */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); const std::string key_id = "no_key"; entitled_message_1.SetEntitlementKeyId(0, key_id); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(false)); } /** * This verifies that entitled content keys cannot be loaded if we specify an * entitled key session that has not been created. */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysWrongEntitledKeySessionAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); const uint32_t wrong_key_session_id = key_session_id == 0 ? 1 : 0; entitled_message_1.SetEntitledKeySession(wrong_key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(false)); } INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoEntitlementLicenseTest, Range(kCoreMessagesAPI, kCurrentAPI + 1)); TEST_P(OEMCryptoLicenseTest, GetKeyHandleEntitledKeyAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); const char* content_key_id = "content_key_id"; entitled_message_1.SetContentKeyId(0, content_key_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); vector key_handle; ASSERT_EQ( OEMCrypto_SUCCESS, GetKeyHandleIntoVector( key_session_id, reinterpret_cast(content_key_id), strlen(content_key_id), OEMCrypto_CipherMode_CENC, key_handle)); } // SelectEntitledKey should fail if we attempt to select a key that has not been // loaded. Also, the error should be NO_CONTENT_KEY. TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); const char* content_key_id = "no_key"; ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_ERROR_INVALID_CONTEXT, key_session_id, reinterpret_cast(content_key_id), strlen(content_key_id))); } // This verifies that entitled key sessions can be created and removed. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id_1; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id_1)); ASSERT_NE(key_session_id_1, 0u); // 0 is a reserved id number. uint32_t key_session_id_2; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id_2)); ASSERT_NE(key_session_id_2, 0u); // 0 is a reserved id number. // Entitled key sessions should have unique ids. ASSERT_NE(key_session_id_1, key_session_id_2); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_RemoveEntitledKeySession(key_session_id_1)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_RemoveEntitledKeySession(key_session_id_2)); } TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsCloseWithOEMCryptoSessionAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id_1; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id_1)); // Close the OEMCrypto session. session_.close(); // All entitled key sessions associated with the OEMCrypto session should // already be destroyed. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_RemoveEntitledKeySession(key_session_id_1)); // Open a new session just for OEMCryptoLicenseTest TearDown. session_.open(); } // This verifies that within an entitled key session, each entitlement key can // corresponds to only one content key at most. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionOneContentKeyPerEntitlementAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); // Construct and load content keys to entitled key session. EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); const char* content_key_id_1 = "content_key_id_1"; entitled_message_1.SetContentKeyId(0, content_key_id_1); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); // We can select content key 1 in entitled key session. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_SUCCESS, key_session_id, reinterpret_cast(content_key_id_1), strlen(content_key_id_1))); // Load content key with new content id. const char* content_key_id_2 = "content_key_id_2"; entitled_message_1.SetContentKeyId(0, content_key_id_2); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); // We can select content key 2 in entitled key session. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_SUCCESS, key_session_id, reinterpret_cast(content_key_id_2), strlen(content_key_id_2))); // Content key one is no longer in the entitled key session as they use the // same entitlement key. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_ERROR_NO_CONTENT_KEY, key_session_id, reinterpret_cast(content_key_id_1), strlen(content_key_id_1))); } // Decrypt should fail if the license is entitlement license, and the key handle // is requested from the oemcrypto session (should use entitled key session id // instead). TEST_P(OEMCryptoLicenseTest, RejectOecSessionDecryptWithEntitlementLicenseAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); // Skip the rest of this test on platforms that do not support separate // entitlement and entitled sessions. if (global_features.supports_cas || session_.session_id() != key_session_id) { // Construct and load content keys to entitled key session. EntitledMessage entitled_message(&license_messages_); entitled_message.FillKeyArray(); entitled_message.SetEntitledKeySession(key_session_id); constexpr char kContentKeyId[] = "content_key_id"; const size_t content_key_id_length = strlen(kContentKeyId); entitled_message.SetContentKeyId(0, kContentKeyId); ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(true)); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_SUCCESS, key_session_id, reinterpret_cast(kContentKeyId), content_key_id_length)); // Try to get a key handle with the oemcrypto session id. vector key_handle; EXPECT_NE(GetKeyHandleIntoVector( session_.session_id(), reinterpret_cast(kContentKeyId), content_key_id_length, OEMCrypto_CipherMode_CENC, key_handle), OEMCrypto_SUCCESS); } } // This verifies that an entitled key session can be reassociated to an // OEMCrypto session. TEST_P(OEMCryptoEntitlementLicenseTest, ReassociateEntitledKeySessionAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // Setup an entitled key session in the first OEMCrypto session. uint32_t key_session_id; OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EntitledMessage entitled_message(&license_messages_); entitled_message.FillKeyArray(); entitled_message.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(true)); // Setup another session. Session session2; ASSERT_NO_FATAL_FAILURE(session2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session2)); // session2 does not have entitlement keys. Re-associating the entitled key // session to session2 should fail OEMCryptoResult status = OEMCrypto_ReassociateEntitledKeySession( key_session_id, session2.session_id()); if (status == OEMCrypto_ERROR_NOT_IMPLEMENTED && !global_features.supports_cas) { GTEST_SKIP() << "Skipping test because " "OEMCrypto_ReassociateEntitledKeySession not implemented."; } EXPECT_NE(OEMCrypto_SUCCESS, status); // session2 loads the correct entitlement keys. LicenseRoundTrip license_messages2(&session2); license_messages2.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages2.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); // Re-associating to session2 should succeed. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_ReassociateEntitledKeySession( key_session_id, session2.session_id())); // Now reassociate the entitled key session back to the first OEMCrypto // session. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_ReassociateEntitledKeySession( key_session_id, session_.session_id())); ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(true)); // session3 has unmatched key policies Session session3; ASSERT_NO_FATAL_FAILURE(session3.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session3)); LicenseRoundTrip license_messages3(&session3); license_messages3.set_license_type(OEMCrypto_EntitlementLicense); license_messages3.set_control(license_messages_.control() + 1); ASSERT_NO_FATAL_FAILURE(license_messages3.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages3.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages3.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages3.LoadResponse()); // Re-associating to session3 should fail. EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_ReassociateEntitledKeySession( key_session_id, session3.session_id())); } /// @} /// @addtogroup cas /// @{ TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys( /*load_even=*/true, /*load_odd=*/true, OEMCrypto_SUCCESS)); EntitledMessage entitled_message_2(&license_messages_); entitled_message_2.FillKeyArray(); entitled_message_2.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadCasKeys( /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); EntitledMessage entitled_message_3(&license_messages_); entitled_message_3.FillKeyArray(); entitled_message_3.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_3.LoadCasKeys( /*load_even=*/false, /*load_odd=*/true, OEMCrypto_SUCCESS)); ASSERT_NO_FATAL_FAILURE(entitled_message_3.LoadCasKeys( /*load_even=*/false, /*load_odd=*/false, OEMCrypto_SUCCESS)); } /** * This verifies that entitled content keys cannot be loaded if we have loaded * the wrong entitlement keys. */ TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysNoEntitlementKeysAPI17) { if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys( /*load_even=*/true, /*load_odd=*/true, OEMCrypto_ERROR_INVALID_CONTEXT)); } TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysWrongEntitlementKeysAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); const std::string key_id = "no_key"; entitled_message_1.SetEntitlementKeyId(0, key_id); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys( /*load_even=*/true, /*load_odd=*/true, OEMCrypto_KEY_NOT_ENTITLED)); } TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysWrongEntitledKeySessionAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); const uint32_t wrong_key_session_id = key_session_id == 0 ? 1 : 0; entitled_message_1.SetEntitledKeySession(wrong_key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys( /*load_even=*/true, /*load_odd=*/true, OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION)); } /** * This verifies that entitled content keys cannot be loaded if we specify an * entitled key session that is actually an oemcrypto session. */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI17) { if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); if (session_.session_id() == key_session_id) { GTEST_SKIP() << "Skipping test because entitled and entitlement sessions are both " << key_session_id << "."; } entitled_message_1.SetEntitledKeySession(session_.session_id()); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(false)); } TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysOemcryptoSessionAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(session_.session_id()); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys( /*load_even=*/true, /*load_odd=*/true, OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION)); } /** * Select key with entitlement license fails if the key id is entitlement key * id. */ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) { if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); if (session_.session_id() == key_session_id) { GTEST_SKIP() << "Skipping test because entitled and entitlement sessions are both " << key_session_id << "."; } ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_ERROR_INVALID_CONTEXT, session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length)); } // This verifies that multiple entitled key sessions can be created. They can // load and select keys independently. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { if (!global_features.supports_cas) { GTEST_SKIP() << "OEMCrypto does not support CAS"; } if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t key_session_id_1; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id_1)); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id_1); const char* content_key_id_1 = "content_key_id_1"; entitled_message_1.SetContentKeyId(0, content_key_id_1); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(true)); // We can select content key 1 in entitled key session 1. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_SUCCESS, key_session_id_1, reinterpret_cast(content_key_id_1), strlen(content_key_id_1))); // Create another entitled key session. uint32_t key_session_id_2; OEMCryptoResult status = OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id_2); // For DRM, but not for CAS, we allow there to be only a single entitled // session. if (!global_features.supports_cas && (key_session_id_2 == key_session_id_1 || status == OEMCrypto_ERROR_TOO_MANY_SESSIONS)) { GTEST_SKIP() << "Skipping test because multiple entitled sessions not supported."; } ASSERT_EQ(OEMCrypto_SUCCESS, status); // Entitled key sessions should have unique ids. ASSERT_NE(key_session_id_1, key_session_id_2); EntitledMessage entitled_message_2(&license_messages_); entitled_message_2.FillKeyArray(); entitled_message_2.SetEntitledKeySession(key_session_id_2); const char* content_key_id_2 = "content_key_id_2"; entitled_message_2.SetContentKeyId(0, content_key_id_2); ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(true)); // We can select content key 2 in entitled key session 2. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_SUCCESS, key_session_id_2, reinterpret_cast(content_key_id_2), strlen(content_key_id_2))); // Content key id 1 is not in entitled key session 2. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_ERROR_NO_CONTENT_KEY, key_session_id_2, reinterpret_cast(content_key_id_1), strlen(content_key_id_1))); // Content key id 2 is not in entitled key session 1. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptEntitled( OEMCrypto_ERROR_NO_CONTENT_KEY, key_session_id_1, reinterpret_cast(content_key_id_2), strlen(content_key_id_2))); } /// @} /// @addtogroup cas /// @{ #ifdef CAS_TEST # include "tuner_hal.h" class OEMCryptoCasDemoTest : public OEMCryptoEntitlementLicenseTest {}; TEST_P(OEMCryptoCasDemoTest, BasicFlow) { // License contains entitlement keys, function reused from // OEMCryptoEntitlementLicenseTest LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); EntitledMessage entitled_message(&license_messages_); // Randomly generate entitled content keys entitled_message.FillKeyArray(); if (session_.session_id() == key_session_id) { GTEST_SKIP() << "Skipping test because entitled and entitlement sessions are both " << key_session_id << "."; } entitled_message.SetEntitledKeySession(key_session_id); // Encrypt and load 0th key (even key) into OEMCrypto ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys( /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); // // Perform DecryptCTR() but for CAS // vector unencrypted_data(256, 0); vector encrypted_data(256, 0); vector output_buffer(256, 0); unencrypted_data.resize(encrypted_data.size()); output_buffer.resize(encrypted_data.size()); OEMCrypto_SampleDescription sample_description; OEMCrypto_SubSampleDescription subsample_description; GenerateSimpleSampleDescription(encrypted_data, output_buffer, &sample_description, &subsample_description); // Use 0th entitled content key and IV to encrypt test data EncryptCTR(unencrypted_data, entitled_message.entitled_key_data()->content_key_data, entitled_message.entitled_key_data()->content_iv, &encrypted_data); // Assume 0,0 pattern for CTR example OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; // Demo only -- copy IV into sample description so we can use // WTPI_DecryptSample() in the Tuner decrypt impl. A real implementation would // use the IV from the entitled content key, but the demo relies on the // existing decrypt which uses SampleDescription IV. memcpy(sample_description.iv, entitled_message.entitled_key_data()->content_iv, 16); // Get key token to send to Tuner for decrypt std::vector key_token; size_t key_token_length = key_token.size(); OEMCryptoResult res = OEMCrypto_GetOEMKeyToken( key_session_id, key_token.data(), &key_token_length); if (res == OEMCrypto_ERROR_SHORT_BUFFER) { key_token.resize(key_token_length); res = OEMCrypto_GetOEMKeyToken(key_session_id, key_token.data(), &key_token_length); } ASSERT_EQ(OEMCrypto_SUCCESS, res); // Decrypt the data ASSERT_EQ(TUNER_HAL_SUCCESS, TunerHal_Decrypt(key_token.data(), key_token_length, TunerHal_KeyParityType_EvenKey, &sample_description, // an array of samples. 1, // the number of samples. &pattern)); ASSERT_EQ(unencrypted_data, output_buffer); } INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoCasDemoTest, Range(kCoreMessagesAPI, kCurrentAPI + 1)); #endif /// @} } // namespace wvoec