diff --git a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp index a198860a..b057f650 100644 --- a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp +++ b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp @@ -19,6 +19,7 @@ #include "license_request.h" #include "log.h" #include "metrics_collections.h" +#include "oec_device_features.h" #include "test_base.h" #include "test_printers.h" #include "test_sleep.h" @@ -115,22 +116,30 @@ class CorePIGTest : public WvCdmTestBaseWithEngine { const std::vector input(buffer_size, 0); std::vector output(buffer_size, 0); const std::vector iv(KEY_IV_SIZE, 0); - Decrypt(session_id, key_id, input, iv, &output, NO_ERROR); + ASSERT_EQ(NO_ERROR, Decrypt(session_id, key_id, input, iv, &output)); } // Try to use the key to decrypt, but expect the key has expired. - void FailDecrypt(const CdmSessionId& session_id, const KeyId& key_id) { + void FailDecrypt(const CdmSessionId& session_id, const KeyId& key_id, + CdmResponseType expected_status) { constexpr size_t buffer_size = 500; const std::vector input(buffer_size, 0); std::vector output(buffer_size, 0); const std::vector iv(KEY_IV_SIZE, 0); - Decrypt(session_id, key_id, input, iv, &output, NEED_KEY); + CdmResponseType status = Decrypt(session_id, key_id, input, iv, &output); + // If the server knows we cannot handle the key, it would not have given us + // the key. In that case, the status should indicate no key. + if (status != NEED_KEY) { + // Otherwise, we should have gotten the expected error. + ASSERT_EQ(expected_status, status); + } } - void Decrypt(const CdmSessionId& session_id, const KeyId& key_id, - const std::vector& input, - const std::vector& iv, std::vector* output, - CdmResponseType expected_status) { + // Decrypt or fail to decrypt, with the expected status. + CdmResponseType Decrypt(const CdmSessionId& session_id, const KeyId& key_id, + const std::vector& input, + const std::vector& iv, + std::vector* output) { CdmDecryptionParametersV16 params(key_id); params.is_secure = false; CdmDecryptionSample sample(input.data(), output->data(), 0, input.size(), @@ -138,9 +147,54 @@ class CorePIGTest : public WvCdmTestBaseWithEngine { CdmDecryptionSubsample subsample(0, input.size()); sample.subsamples.push_back(subsample); params.samples.push_back(sample); + return cdm_engine_.DecryptV16(session_id, params); + } + // Use the key to decrypt to a secure buffer. + void DecryptSecure(const CdmSessionId& session_id, const KeyId& key_id) { + ASSERT_TRUE(wvoec::global_features.test_secure_buffers); + constexpr size_t buffer_size = 500; + const std::vector input(buffer_size, 0); + const std::vector iv(KEY_IV_SIZE, 0); + + // To create a secure buffer, we need to know the OEMCrypto session id. + CdmQueryMap query_map; + cdm_engine_.QueryOemCryptoSessionId(session_id, &query_map); + const std::string oec_session_id_string = + query_map[QUERY_KEY_OEMCRYPTO_SESSION_ID]; + uint32_t oec_session_id = std::stoi(oec_session_id_string); + + int secure_buffer_fid; + OEMCrypto_DestBufferDesc output_descriptor; + output_descriptor.type = OEMCrypto_BufferType_Secure; + output_descriptor.buffer.secure.handle_length = buffer_size; + ASSERT_EQ( + OEMCrypto_AllocateSecureBuffer(oec_session_id, buffer_size, + &output_descriptor, &secure_buffer_fid), + OEMCrypto_SUCCESS); + + ASSERT_NE(output_descriptor.buffer.secure.handle, nullptr); + // It is OK if OEMCrypto changes the maximum size, but there must + // still be enough room for our data. + ASSERT_GE(output_descriptor.buffer.secure.handle_length, buffer_size); + output_descriptor.buffer.secure.offset = 0; + + // Now create a sample array for the CDM layer. + CdmDecryptionParametersV16 params(key_id); + params.is_secure = true; + CdmDecryptionSample sample(input.data(), + output_descriptor.buffer.secure.handle, 0, + input.size(), iv); + CdmDecryptionSubsample subsample(0, input.size()); + sample.subsamples.push_back(subsample); + params.samples.push_back(sample); CdmResponseType status = cdm_engine_.DecryptV16(session_id, params); - ASSERT_EQ(expected_status, status); + + // Free the secure buffer before we check the return status. + OEMCrypto_FreeSecureBuffer(oec_session_id, &output_descriptor, + secure_buffer_fid); + + ASSERT_EQ(status, NO_ERROR); } }; @@ -192,4 +246,36 @@ TEST_F(CorePIGTest, OfflineWithPST) { ASSERT_NO_FATAL_FAILURE(CloseSession(session_id)); } +// This test verifies that the system can download and install license with a +// key that requires secure buffers. It also verifies that we cannot decrypt to +// a non-secure buffer using this key, but that we can decrypt to a secure +// buffer, if the test harness supports secure buffers. +TEST_F(CorePIGTest, OfflineHWSecureRequired) { + const std::string content_id = "GTS_HW_SECURE_ALL"; + const KeyId key_id = "0000000000000002"; + + const CdmLicenseType license_type = kLicenseTypeOffline; + CdmSessionId session_id; + ASSERT_NO_FATAL_FAILURE(OpenSession(&session_id)); + CdmKeyRequest key_request; + ASSERT_NO_FATAL_FAILURE( + GenerateKeyRequest(session_id, content_id, &key_request, license_type)); + std::string key_response; + ASSERT_NO_FATAL_FAILURE(GetKeyResponse(key_request, &key_response)); + CdmKeySetId key_set_id; + ASSERT_NO_FATAL_FAILURE( + AddKey(session_id, key_response, license_type, &key_set_id)); + // First we try to decrypt to a non-secure buffer and verify that it fails. + // TODO(b/164517875): This error code should be something actionable. + ASSERT_NO_FATAL_FAILURE(FailDecrypt(session_id, key_id, DECRYPT_ERROR)); + // Next, if possible, we try to decrypt to a secure buffer, and verify + // success. + if (wvoec::global_features.test_secure_buffers) { + ASSERT_NO_FATAL_FAILURE(DecryptSecure(session_id, key_id)); + } else { + LOGI("Test harness cannot create secure buffers. test skipped."); + } + ASSERT_NO_FATAL_FAILURE(CloseSession(session_id)); +} + } // namespace wvcdm