325 lines
13 KiB
C++
325 lines
13 KiB
C++
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
|
|
#include <string>
|
|
|
|
#include "cdm_engine.h"
|
|
#include "config_test_env.h"
|
|
#include "gtest/gtest.h"
|
|
#include "initialization_data.h"
|
|
#include "license_request.h"
|
|
#include "log.h"
|
|
#include "properties.h"
|
|
#include "scoped_ptr.h"
|
|
#include "string_conversions.h"
|
|
#include "url_request.h"
|
|
#include "wv_cdm_constants.h"
|
|
#include "wv_cdm_types.h"
|
|
|
|
namespace {
|
|
// Http OK response code.
|
|
const int kHttpOk = 200;
|
|
|
|
// 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_pssh;
|
|
wvcdm::KeyId g_key_id_unwrapped;
|
|
wvcdm::CdmKeySystem g_key_system;
|
|
std::string g_license_server;
|
|
wvcdm::KeyId g_wrong_key_id;
|
|
|
|
// This is an RSA certificate message from the provisioning server.
|
|
// The client sends this certificate to a license server for device
|
|
// authentication by the license server.
|
|
// This certificate is used to test the CDM engine's provisioning
|
|
// response handling.
|
|
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
|
|
"{\"signedResponse\": {"
|
|
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
|
|
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
|
|
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
|
|
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
|
|
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
|
|
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
|
|
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
|
|
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
|
|
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
|
|
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
|
|
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
|
|
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
|
|
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
|
|
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
|
|
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
|
|
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
|
|
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
|
|
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
|
|
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
|
|
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
|
|
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
|
|
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
|
|
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
|
|
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
|
|
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
|
|
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
|
|
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
|
|
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
|
|
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
|
|
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
|
|
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
|
|
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
|
|
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
|
|
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
|
|
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
|
|
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
|
|
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
|
|
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
|
|
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
|
|
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
|
|
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
|
|
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
|
|
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
|
|
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
|
|
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
|
|
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
|
|
|
|
const std::string kCencMimeType = "video/mp4";
|
|
const std::string kWebmMimeType = "video/webm";
|
|
|
|
} // namespace
|
|
|
|
namespace wvcdm {
|
|
|
|
class WvCdmEngineTest : public testing::Test {
|
|
public:
|
|
virtual void SetUp() {
|
|
cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
cdm_engine_.CloseSession(session_id_);
|
|
}
|
|
|
|
protected:
|
|
void GenerateKeyRequest(const std::string& key_id,
|
|
const std::string& init_data_type_string) {
|
|
CdmAppParameterMap app_parameters;
|
|
std::string server_url;
|
|
CdmKeySetId key_set_id;
|
|
|
|
InitializationData init_data(init_data_type_string, key_id);
|
|
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
cdm_engine_.GenerateKeyRequest(session_id_,
|
|
key_set_id,
|
|
init_data,
|
|
kLicenseTypeStreaming,
|
|
app_parameters,
|
|
&key_msg_,
|
|
&server_url));
|
|
}
|
|
|
|
void GenerateRenewalRequest() {
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
cdm_engine_.GenerateRenewalRequest(session_id_,
|
|
&key_msg_,
|
|
&server_url_));
|
|
}
|
|
|
|
// posts a request and extracts the drm message from the response
|
|
std::string GetKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth) {
|
|
// Use secure connection and chunk transfer coding.
|
|
UrlRequest url_request(server_url + client_auth);
|
|
if (!url_request.is_connected()) {
|
|
return "";
|
|
}
|
|
|
|
url_request.PostRequest(key_msg_);
|
|
std::string response;
|
|
bool ok = url_request.GetResponse(&response);
|
|
LOGD("response: %s\n", response.c_str());
|
|
EXPECT_TRUE(ok);
|
|
|
|
int status_code = url_request.GetStatusCode(response);
|
|
EXPECT_EQ(kHttpOk, status_code);
|
|
|
|
if (status_code != kHttpOk) {
|
|
return "";
|
|
} else {
|
|
std::string drm_msg;
|
|
LicenseRequest lic_request;
|
|
lic_request.GetDrmMessage(response, drm_msg);
|
|
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
|
|
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
|
drm_msg.size()).c_str());
|
|
return drm_msg;
|
|
}
|
|
}
|
|
|
|
void VerifyNewKeyResponse(const std::string& server_url,
|
|
const std::string& client_auth){
|
|
std::string resp = GetKeyRequestResponse(server_url,
|
|
client_auth);
|
|
CdmKeySetId key_set_id;
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id));
|
|
}
|
|
|
|
void VerifyRenewalKeyResponse(const std::string& server_url,
|
|
const std::string& client_auth) {
|
|
std::string resp = GetKeyRequestResponse(server_url,
|
|
client_auth);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
|
}
|
|
|
|
CdmEngine cdm_engine_;
|
|
std::string key_msg_;
|
|
std::string session_id_;
|
|
std::string server_url_;
|
|
};
|
|
|
|
TEST(WvCdmProvisioningTest, ProvisioningTest) {
|
|
CdmEngine cdm_engine;
|
|
CdmProvisioningRequest prov_request;
|
|
std::string provisioning_server_url;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority;
|
|
std::string cert, wrapped_key;
|
|
|
|
cdm_engine.GetProvisioningRequest(cert_type, cert_authority,
|
|
&prov_request, &provisioning_server_url);
|
|
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse,
|
|
&cert, &wrapped_key);
|
|
}
|
|
|
|
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
|
|
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
|
|
GetKeyRequestResponse(g_license_server, g_client_auth);
|
|
}
|
|
|
|
// TODO(juce): Set up with correct test data.
|
|
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
|
|
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
|
|
GetKeyRequestResponse(g_license_server, g_client_auth);
|
|
}
|
|
|
|
TEST_F(WvCdmEngineTest, WrongMessageTest) {
|
|
std::string wrong_message = a2bs_hex(g_wrong_key_id);
|
|
GenerateKeyRequest(wrong_message, kCencMimeType);
|
|
|
|
// We should receive a response with no license, i.e. the extracted license
|
|
// response message should be empty.
|
|
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
|
|
}
|
|
|
|
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
|
|
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
|
|
VerifyNewKeyResponse(g_license_server, g_client_auth);
|
|
}
|
|
|
|
// TODO(juce): Set up with correct test data.
|
|
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
|
|
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
|
|
VerifyNewKeyResponse(g_license_server, g_client_auth);
|
|
}
|
|
|
|
TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
|
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
|
|
VerifyNewKeyResponse(g_license_server, g_client_auth);
|
|
|
|
GenerateRenewalRequest();
|
|
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
|
|
g_client_auth);
|
|
}
|
|
|
|
} // namespace wvcdm
|
|
|
|
int main(int argc, char **argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
wvcdm::InitLogging(argc, argv);
|
|
|
|
wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer);
|
|
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_pssh.assign(config.key_id());
|
|
std::string license_server(g_license_server);
|
|
|
|
int show_usage = 0;
|
|
static const struct option long_options[] = {
|
|
{ "keyid", required_argument, NULL, 'k' },
|
|
{ "server", required_argument, NULL, 's' },
|
|
{ NULL, 0, NULL, '\0' }
|
|
};
|
|
|
|
int option_index = 0;
|
|
int opt = 0;
|
|
while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) {
|
|
switch (opt) {
|
|
case 'k': {
|
|
g_key_id_pssh.clear();
|
|
g_key_id_pssh.assign(optarg);
|
|
break;
|
|
}
|
|
case 's': {
|
|
g_license_server.clear();
|
|
g_license_server.assign(optarg);
|
|
break;
|
|
}
|
|
case 'v': {
|
|
// This option _may_ have already been consumed by wvcdm::InitLogging()
|
|
// above, depending on the platform-specific logging implementation.
|
|
// We only tell getopt about it so that it is not an error. We ignore
|
|
// the option here when seen.
|
|
// TODO: Stop passing argv to InitLogging, and instead set the log
|
|
// level here through the logging API. We should keep all command-line
|
|
// parsing at the application level, rather than split between various
|
|
// apps and various platform-specific logging implementations.
|
|
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 << " --server=<server_url>";
|
|
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=<key_id>";
|
|
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_pssh << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << "Server: " << g_license_server << std::endl;
|
|
std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl;
|
|
|
|
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
|
|
config.set_license_server(g_license_server);
|
|
config.set_key_id(g_key_id_pssh);
|
|
|
|
// Extract the key ID from the PSSH box.
|
|
wvcdm::InitializationData extractor(wvcdm::CENC_INIT_DATA_FORMAT,
|
|
g_key_id_pssh);
|
|
g_key_id_unwrapped = extractor.data();
|
|
|
|
return RUN_ALL_TESTS();
|
|
}
|