Policy integration tests: use license holder
[ Merge of http://go/wvgerrit/143634 ] Refactor the policy integration tests to use the license holder. Bug: 195691232 Test: GtsMediaTestCases on sunfish Change-Id: I58ffa64caec05c617065e4781657e85914f8369e
This commit is contained in:
@@ -13,27 +13,15 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "clock.h"
|
||||
#include "config_test_env.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license_request.h"
|
||||
#include "license_holder.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"
|
||||
#include "url_request.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
constexpr int kHttpOk = 200;
|
||||
const std::string kCencMimeType = "cenc";
|
||||
} // namespace
|
||||
|
||||
// Core Policy Integration Test
|
||||
class CorePIGTest : public WvCdmTestBaseWithEngine {
|
||||
protected:
|
||||
@@ -41,210 +29,44 @@ class CorePIGTest : public WvCdmTestBaseWithEngine {
|
||||
WvCdmTestBase::SetUp();
|
||||
EnsureProvisioned();
|
||||
}
|
||||
|
||||
void OpenSession(CdmSessionId* session_id) {
|
||||
CdmResponseType status = cdm_engine_.OpenSession(
|
||||
config_.key_system(), nullptr, nullptr, session_id);
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_TRUE(cdm_engine_.IsOpenSession(*session_id));
|
||||
}
|
||||
|
||||
void CloseSession(const CdmSessionId& session_id) {
|
||||
CdmResponseType status = cdm_engine_.CloseSession(session_id);
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_FALSE(cdm_engine_.IsOpenSession(session_id));
|
||||
}
|
||||
|
||||
// Create a license request for the given content_id and requesting the
|
||||
// specified license_type.
|
||||
void GenerateKeyRequest(const CdmSessionId& session_id,
|
||||
const std::string& content_id,
|
||||
CdmKeyRequest* key_request,
|
||||
CdmLicenseType license_type) {
|
||||
video_widevine::WidevinePsshData pssh;
|
||||
pssh.set_content_id(content_id);
|
||||
const std::string init_data_string = MakePSSH(pssh);
|
||||
const InitializationData init_data(kCencMimeType, init_data_string);
|
||||
init_data.DumpToLogs();
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
CdmKeySetId empty_key_set_id;
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, empty_key_set_id, init_data, license_type,
|
||||
empty_app_parameters, key_request);
|
||||
ASSERT_EQ(KEY_MESSAGE, result);
|
||||
ASSERT_EQ(kKeyRequestTypeInitial, key_request->type);
|
||||
}
|
||||
|
||||
// Send the request to the server and get the response.
|
||||
void GetKeyResponse(const CdmKeyRequest& key_request,
|
||||
std::string* key_response) {
|
||||
const std::string url = config_.license_server() + config_.client_auth();
|
||||
UrlRequest url_request(url);
|
||||
ASSERT_TRUE(url_request.is_connected());
|
||||
|
||||
std::string http_response;
|
||||
url_request.PostRequest(key_request.message);
|
||||
ASSERT_TRUE(url_request.GetResponse(&http_response));
|
||||
int status_code = url_request.GetStatusCode(http_response);
|
||||
ASSERT_EQ(kHttpOk, status_code);
|
||||
|
||||
LicenseRequest license_request;
|
||||
license_request.GetDrmMessage(http_response, *key_response);
|
||||
}
|
||||
|
||||
// Load the license response into the specified session. Verify it has the
|
||||
// correct license type (either streaming or offline).
|
||||
void AddKey(const CdmSessionId& session_id, const std::string& key_response,
|
||||
CdmLicenseType expected_license_type, CdmKeySetId* key_set_id) {
|
||||
CdmLicenseType license_type;
|
||||
CdmResponseType status =
|
||||
cdm_engine_.AddKey(session_id, key_response, &license_type, key_set_id);
|
||||
ASSERT_EQ(KEY_ADDED, status);
|
||||
ASSERT_EQ(expected_license_type, license_type);
|
||||
}
|
||||
|
||||
// Reload the license response into the specified session.
|
||||
void RestoreKey(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id) {
|
||||
CdmResponseType status = cdm_engine_.RestoreKey(session_id, key_set_id);
|
||||
ASSERT_EQ(KEY_ADDED, status);
|
||||
}
|
||||
|
||||
// Use the key to decrypt.
|
||||
void Decrypt(const CdmSessionId& session_id, const KeyId& key_id) {
|
||||
constexpr size_t buffer_size = 500;
|
||||
const std::vector<uint8_t> input(buffer_size, 0);
|
||||
std::vector<uint8_t> output(buffer_size, 0);
|
||||
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
|
||||
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,
|
||||
CdmResponseType expected_status) {
|
||||
constexpr size_t buffer_size = 500;
|
||||
const std::vector<uint8_t> input(buffer_size, 0);
|
||||
std::vector<uint8_t> output(buffer_size, 0);
|
||||
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt or fail to decrypt, with the expected status.
|
||||
CdmResponseType Decrypt(const CdmSessionId& session_id, const KeyId& key_id,
|
||||
const std::vector<uint8_t>& input,
|
||||
const std::vector<uint8_t>& iv,
|
||||
std::vector<uint8_t>* output) {
|
||||
CdmDecryptionParametersV16 params(key_id);
|
||||
params.is_secure = false;
|
||||
CdmDecryptionSample sample(input.data(), output->data(), 0, input.size(),
|
||||
iv);
|
||||
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<uint8_t> input(buffer_size, 0);
|
||||
const std::vector<uint8_t> 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.secure_buffer_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.secure_buffer, 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.secure_buffer_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.secure_buffer, 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
// An offline license with nonce not required.
|
||||
TEST_F(CorePIGTest, OfflineNoNonce) {
|
||||
const std::string content_id = "2015_tears";
|
||||
LicenseHolder holder("CDM_OfflineNoNonce", &cdm_engine_, config_);
|
||||
holder.set_can_persist(true);
|
||||
const KeyId key_id = "0000000000000000";
|
||||
|
||||
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));
|
||||
ASSERT_NO_FATAL_FAILURE(Decrypt(session_id, key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(OpenSession(&session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(RestoreKey(session_id, key_set_id));
|
||||
ASSERT_NO_FATAL_FAILURE(Decrypt(session_id, key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
// Should be able to close the previous session, open a new session,
|
||||
// and reload the license.
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
}
|
||||
|
||||
// An offline license with nonce and provider session token.
|
||||
TEST_F(CorePIGTest, OfflineWithPST) {
|
||||
const std::string content_id = "offline_clip2";
|
||||
const KeyId key_id =
|
||||
"\x32\x60\xF3\x9E\x12\xCC\xF6\x53\x52\x99\x90\x16\x8A\x35\x83\xFF";
|
||||
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));
|
||||
ASSERT_NO_FATAL_FAILURE(Decrypt(session_id, key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(OpenSession(&session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(RestoreKey(session_id, key_set_id));
|
||||
ASSERT_NO_FATAL_FAILURE(Decrypt(session_id, key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
LicenseHolder holder("CDM_OfflineWithPST", &cdm_engine_, config_);
|
||||
holder.set_can_persist(true);
|
||||
const KeyId key_id = "0000000000000000";
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
// Should be able to close the previous session, open a new session,
|
||||
// and reload the license.
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
}
|
||||
|
||||
// This test verifies that the system can download and install license with a
|
||||
@@ -252,31 +74,39 @@ TEST_F(CorePIGTest, OfflineWithPST) {
|
||||
// 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";
|
||||
LicenseHolder holder("CDM_OfflineHWSecureRequired", &cdm_engine_, config_);
|
||||
holder.set_can_persist(true);
|
||||
const KeyId sw_key_id = "0000000000000000";
|
||||
const KeyId hw_key_id = "0000000000000001";
|
||||
|
||||
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));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(sw_key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FailDecrypt(hw_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));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.DecryptSecure(hw_key_id));
|
||||
} else {
|
||||
LOGI("Test harness cannot create secure buffers. test skipped.");
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
|
||||
// Should be able to close the previous session, open a new session,
|
||||
// and reload the license.
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(sw_key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FailDecrypt(hw_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(holder.DecryptSecure(hw_key_id));
|
||||
} else {
|
||||
LOGI("Test harness cannot create secure buffers. test skipped.");
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user