// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine Master // License Agreement. // These tests perform various end-to-end actions similar to what an application // would do. They verify that policies specified on UAT are honored on the // device. #include #include #include #include #include "cdm_engine.h" #include "clock.h" #include "config_test_env.h" #include "initialization_data.h" #include "license_request.h" #include "log.h" #include "metrics_collections.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: void SetUp() override { 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 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); } // Try to use the key to decrypt, but expect the key has expired. void FailDecrypt(const CdmSessionId& session_id, const KeyId& key_id) { 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); } void Decrypt(const CdmSessionId& session_id, const KeyId& key_id, const std::vector& input, const std::vector& iv, std::vector* output, CdmResponseType expected_status) { 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); CdmResponseType status = cdm_engine_.DecryptV16(session_id, params); ASSERT_EQ(expected_status, status); } }; // An offline license with nonce not required. TEST_F(CorePIGTest, OfflineNoNonce) { const std::string content_id = "2015_tears"; 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)); } // 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)); } } // namespace wvcdm