// Copyright 2013 Google Inc. All Rights Reserved. #include #include #include "config_test_env.h" #include "gtest/gtest.h" #include "license_request.h" #include "log.h" #include "string_conversions.h" #include "url_request.h" #include "wv_cdm_constants.h" #include "wv_content_decryption_module.h" namespace { // Default license server, can be configured using --server command line option // Default key id (pssh), can be configured using --keyid command line option std::string g_client_auth; wvcdm::KeyId g_key_id; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; std::string g_port; wvcdm::KeyId g_wrong_key_id; int g_use_full_path = 0; // cannot use boolean in getopt_long } // namespace namespace wvcdm { class WvCdmRequestLicenseTest : public testing::Test { public: WvCdmRequestLicenseTest() {} ~WvCdmRequestLicenseTest() {} protected: void GenerateKeyRequest(const std::string& key_system, const std::string& init_data) { wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, app_parameters, &key_msg_), wvcdm::KEY_MESSAGE); } void GenerateRenewalRequest(const std::string& key_system, const std::string& init_data) { // TODO application makes a license request, CDM will renew the license // when appropriate. wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, app_parameters, &key_msg_), wvcdm::KEY_MESSAGE); } // posts a request and extracts the drm message from the response std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth, int expected_response) { UrlRequest url_request(server_url + client_auth, g_port); if (!url_request.is_connected()) { return ""; } url_request.PostRequest(key_msg_); std::string response; int resp_bytes = url_request.GetResponse(response); LOGD("response:\r\n%s", response.c_str()); LOGD("end %d bytes response dump", resp_bytes); // Youtube server returns 400 for invalid message while play server returns // 500, so just test inequity here for invalid message int status_code = url_request.GetStatusCode(response); if (expected_response == 200) { EXPECT_EQ(200, status_code); } else { EXPECT_NE(200, status_code); } std::string drm_msg; if (200 == status_code) { LicenseRequest lic_request; lic_request.GetDrmMessage(response, drm_msg); LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), HexEncode(reinterpret_cast(drm_msg.data()), drm_msg.size()).c_str()); } return drm_msg; } void VerifyKeyRequestResponse(const std::string& server_url, const std::string& client_auth, std::string& init_data, bool is_renewal) { std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); if (is_renewal) { // TODO application makes a license request, CDM will renew the license // when appropriate wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, app_parameters, &key_msg_), wvcdm::KEY_ADDED); } else { EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED); } } wvcdm::WvContentDecryptionModule decryptor_; std::string key_msg_; std::string session_id_; }; TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); GetKeyRequestResponse(g_license_server, g_client_auth, 200); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { decryptor_.OpenSession(g_key_system, &session_id_); std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); GenerateKeyRequest(g_key_system, wrong_message); GetKeyRequestResponse(g_license_server, g_client_auth, 500); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, NormalDecryption) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } #if 0 // TODO License renewal is not yet implented TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); GenerateRenewalRequest(g_key_system, g_key_id); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); decryptor_.CloseSession(session_id_); } #endif TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); CdmQueryMap query_info; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info)); EXPECT_EQ(wvcdm::QUERY_VALUE_STREAMING, query_info[wvcdm::QUERY_KEY_LICENSE_TYPE]); EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, query_info[wvcdm::QUERY_KEY_PLAY_ALLOWED]); EXPECT_EQ(wvcdm::QUERY_VALUE_FALSE, query_info[wvcdm::QUERY_KEY_PERSIST_ALLOWED]); EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, query_info[wvcdm::QUERY_KEY_RENEW_ALLOWED]); int64_t remaining_time; std::istringstream ss; ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; EXPECT_LE(0, remaining_time); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; EXPECT_LE(0, remaining_time); EXPECT_LE(0, (int)query_info[QUERY_KEY_RENEWAL_SERVER_URL].size()); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, QueryStatus) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); CdmQueryMap query_info; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(&query_info)); EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3, query_info[wvcdm::QUERY_KEY_SECURITY_LEVEL]); decryptor_.CloseSession(session_id_); } } // namespace wvcdm int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); wvcdm::ConfigTestEnv config; g_client_auth.assign(config.client_auth()); g_key_system.assign(config.key_system()); g_wrong_key_id.assign(config.wrong_key_id()); // The following variables are configurable through command line options. g_license_server.assign(config.license_server()); g_key_id.assign(config.key_id()); g_port.assign(config.port()); std::string license_server(g_license_server); int show_usage = 0; static const struct option long_options[] = { { "use_full_path", no_argument, &g_use_full_path, 0 }, { "keyid", required_argument, NULL, 'k' }, { "port", required_argument, NULL, 'p' }, { "server", required_argument, NULL, 's' }, { NULL, 0, NULL, '\0' } }; int option_index = 0; int opt = 0; while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { switch (opt) { case 'k': { g_key_id.clear(); g_key_id.assign(optarg); break; } case 'p': { g_port.clear(); g_port.assign(optarg); break; } case 's': { g_license_server.clear(); g_license_server.assign(optarg); break; } case 'u': { g_use_full_path = 1; break; } case '?': { show_usage = 1; break; } } } if (show_usage) { std::cout << std::endl; std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl; std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; std::cout << std::setw(30) << std::left << " --port="; std::cout << "specifies the port number, in decimal format" << std::endl; std::cout << std::setw(30) << std::left << " "; std::cout << "default: " << g_port << std::endl; std::cout << std::setw(30) << std::left << " --server="; std::cout << "configure the license server url, please include http[s] in the url" << std::endl; std::cout << std::setw(30) << std::left << " "; std::cout << "default: " << license_server << std::endl; std::cout << std::setw(30) << std::left << " --keyid="; std::cout << "configure the key id or pssh, in hex format" << std::endl; std::cout << std::setw(30) << std::left << " default keyid:"; std::cout << g_key_id << std::endl; std::cout << std::setw(30) << std::left << " --use_full_path"; std::cout << "specify server url is not a proxy server" << std::endl; std::cout << std::endl; return 0; } std::cout << std::endl; std::cout << "Server: " << g_license_server << std::endl; std::cout << "Port: " << g_port << std::endl; std::cout << "KeyID: " << g_key_id << std::endl << std::endl; g_key_id = wvcdm::a2bs_hex(g_key_id); config.set_license_server(g_license_server); config.set_port(g_port); config.set_key_id(g_key_id); return RUN_ALL_TESTS(); }