(This is a merge of http://go/wvgerrit/152969.) C++ makes absolutely no guarantees about the order of initialization of global variables in different compilation units. The class-scope static WvCdmTestBase::default_config_ in test_base.cpp invokes the ConfigTestEnv constructor on creation, which depends on the prior initialization of several file-scope static variables in config_test_env.cpp. Since those are different compilation units, there is no guarantee that they will initialize in the correct order to avoid referencing uninitialized memory. This is one of the reasons Google Style really encourages people not to have global-scope variables with complex types. As it happens, on all our internal platforms, these files get linked in such a way that the variables get initialized in the right order and there is no crash. But that's not guaranteed, and some partners have reported crashes here. In at least one case, the "right" linker order was platform-dependent, and the partner ended up having to maintain separate linker orders for separate platforms. This patch defers default_config_ initialization until WvCdmTestBase::Initialize() is called. By that time, all static variables will be initialized, so it will be safe to reference them. Bug: 173252165 Test: x86-64 Test: build_and_run_all_unit_tests.sh Change-Id: If31128a999c7d6945f47293ca57f08e43d8274de
606 lines
24 KiB
C++
606 lines
24 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#include "test_base.h"
|
|
|
|
#include <openssl/aes.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/cmac.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <chrono>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#include "cdm_engine.h"
|
|
#include "clock.h"
|
|
#include "crypto_session.h"
|
|
#include "fake_provisioning_server.h"
|
|
#include "file_store.h"
|
|
#include "license.h"
|
|
#include "log.h"
|
|
#include "oec_device_features.h"
|
|
#include "oec_test_data.h"
|
|
#include "platform.h"
|
|
#include "properties.h"
|
|
#include "test_printers.h"
|
|
#include "test_sleep.h"
|
|
#include "url_request.h"
|
|
|
|
using wvcdm::metrics::EngineMetrics;
|
|
|
|
namespace wvcdm {
|
|
namespace {
|
|
void show_menu(const char* prog_name, const std::string& extra_help_text) {
|
|
std::cout << std::endl;
|
|
std::cout << "usage: " << prog_name << " [options]" << std::endl << std::endl;
|
|
std::cout << " enclose multiple arguments in '' when using adb shell"
|
|
<< std::endl;
|
|
std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'"
|
|
<< std::endl;
|
|
|
|
std::cout << " --verbose or -v" << std::endl;
|
|
std::cout << " increase logging verbosity (may be repeated)" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --no_filter" << std::endl;
|
|
std::cout << " Do not filter out inappropriate tests" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --cast" << std::endl;
|
|
std::cout << " Run tests appropriate for a Cast Receiver" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --license_server_id=<gp/cp/st>" << std::endl;
|
|
std::cout << " specifies which default server settings to use: "
|
|
<< std::endl;
|
|
std::cout << " gp for GooglePlay server" << std::endl;
|
|
std::cout << " cp for Content Protection UAT server" << std::endl;
|
|
std::cout << " st for Content Protection Staging server" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --keyid=<key_id>" << std::endl;
|
|
std::cout << " configure the key id or pssh, in hex format" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --service_certificate=<cert>" << std::endl;
|
|
std::cout << " configure the signed license service certificate"
|
|
<< std::endl;
|
|
std::cout << " Specify the SignedDeviceCertificate (from "
|
|
<< "device_certificate.proto) " << std::endl;
|
|
std::cout << " in hex format." << std::endl;
|
|
std::cout << " Due to the length of the argument use, " << std::endl;
|
|
std::cout << " echo \"/system/bin/request_license_test -s \\\""
|
|
<< "0ABF02...A29914\\\"\" \\" << std::endl;
|
|
std::cout << " > run_request_license_test.sh" << std::endl;
|
|
std::cout << " chmod +x run_request_license_test.sh" << std::endl;
|
|
std::cout << " adb push run_request_license_test.sh /system/bin"
|
|
<< std::endl;
|
|
std::cout << " adb shell sh /system/bin/run_request_license_test.sh"
|
|
<< std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --provisioning_certificate=<cert>" << std::endl;
|
|
std::cout << " configure the signed provisioning service certificate"
|
|
<< std::endl
|
|
<< " in hex" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --license_server_url=<url>" << std::endl;
|
|
std::cout << " configure the license server url, please include http[s]"
|
|
<< " in the url" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --renewal_server_url=<url>" << std::endl;
|
|
std::cout << " configure the renewal server url, please include http[s] "
|
|
"in the url"
|
|
<< std::endl
|
|
<< " If not set, this defaults to be the same url used by the "
|
|
"license server."
|
|
<< std::endl
|
|
<< " Some tests, such as LicenseRenewalSpecifiedServer, will "
|
|
"ignore this setting. "
|
|
<< std::endl
|
|
<< " See comments in the code for an explanation." << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --provisioning_server_url=<url>" << std::endl;
|
|
std::cout
|
|
<< " configure the provisioning server url, please include http[s]"
|
|
<< " in the url" << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --qa_provisioning" << std::endl;
|
|
std::cout << " use the QA provisioning cert and QA test keybox"
|
|
<< std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --fake_sleep" << std::endl;
|
|
std::cout << " Use a fake clock to sleep for duration tests. This cannot"
|
|
<< " be used with a real OEMCrypto." << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --initial_time=<time>" << std::endl;
|
|
std::cout << " Set the initial time on the fake clock. This is ignored"
|
|
<< " if fake_sleep was not set." << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --pass=<N>" << std::endl;
|
|
std::cout << " Run test pass N. This is used for reboot tests that "
|
|
<< "require several passes." << std::endl
|
|
<< std::endl;
|
|
|
|
std::cout << " --test_data_path=<path>" << std::endl;
|
|
std::cout << " Where to store test data for reboot tests." << std::endl;
|
|
|
|
std::cout << extra_help_text << std::endl;
|
|
}
|
|
|
|
/*
|
|
* Locate the portion of the server's response message that is between
|
|
* the strings jason_start_substr and json_end_substr. Returns the string
|
|
* through *result. If the start substring match fails, assume the entire
|
|
* string represents a serialized protobuf mesaage and return true with
|
|
* the entire string. If the end_substring match fails, return false with
|
|
* an empty *result.
|
|
*/
|
|
bool ExtractSignedMessage(const std::string& response,
|
|
const std::string& json_start_substr,
|
|
const std::string& json_end_substr,
|
|
std::string* result) {
|
|
std::string response_string;
|
|
size_t start = response.find(json_start_substr);
|
|
|
|
if (start == response.npos) {
|
|
// Assume serialized protobuf message.
|
|
result->assign(response);
|
|
} else {
|
|
// Assume JSON-wrapped protobuf.
|
|
size_t end =
|
|
response.find(json_end_substr, start + json_start_substr.length());
|
|
if (end == response.npos) {
|
|
LOGE("ExtractSignedMessage cannot locate end substring");
|
|
result->clear();
|
|
return false;
|
|
}
|
|
size_t result_string_size = end - start - json_start_substr.length();
|
|
result->assign(response, start + json_start_substr.length(),
|
|
result_string_size);
|
|
}
|
|
|
|
if (result->empty()) {
|
|
LOGE("ExtractSignedMessage: Response message is empty");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<ConfigTestEnv> WvCdmTestBase::default_config_;
|
|
bool WvCdmTestBase::use_qa_test_keybox_ = false;
|
|
|
|
void WvCdmTestBase::StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
|
|
uint8_t init) {
|
|
buffer->assign(size, 0);
|
|
for (size_t i = 0; i < size; i++) {
|
|
(*buffer)[i] = init + i % 250;
|
|
}
|
|
}
|
|
|
|
std::string WvCdmTestBase::Aes128CbcEncrypt(std::vector<uint8_t> key,
|
|
const std::vector<uint8_t>& clear,
|
|
const std::vector<uint8_t> iv) {
|
|
std::vector<uint8_t> encrypted(clear.size());
|
|
std::vector<uint8_t> iv_mod(iv.begin(), iv.end());
|
|
AES_KEY aes_key;
|
|
AES_set_encrypt_key(&key[0], 128, &aes_key);
|
|
AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv_mod[0],
|
|
AES_ENCRYPT);
|
|
return std::string(encrypted.begin(), encrypted.end());
|
|
}
|
|
|
|
std::string WvCdmTestBase::Aes128CbcDecrypt(std::vector<uint8_t> key,
|
|
const std::vector<uint8_t>& clear,
|
|
const std::vector<uint8_t> iv) {
|
|
std::vector<uint8_t> encrypted(clear.size());
|
|
std::vector<uint8_t> iv_mod(iv.begin(), iv.end());
|
|
AES_KEY aes_key;
|
|
AES_set_decrypt_key(&key[0], 128, &aes_key);
|
|
AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv_mod[0],
|
|
AES_DECRYPT);
|
|
return std::string(encrypted.begin(), encrypted.end());
|
|
}
|
|
|
|
std::string WvCdmTestBase::SignHMAC(const std::string& message,
|
|
const std::vector<uint8_t>& key) {
|
|
uint8_t signature[SHA256_DIGEST_LENGTH];
|
|
unsigned int md_len = SHA256_DIGEST_LENGTH;
|
|
HMAC(EVP_sha256(), &key[0], static_cast<int>(key.size()),
|
|
reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
|
signature, &md_len);
|
|
std::string result(signature, signature + SHA256_DIGEST_LENGTH);
|
|
return result;
|
|
}
|
|
|
|
TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics)
|
|
: CryptoSession(crypto_metrics) {
|
|
// The first CryptoSession should have initialized OEMCrypto. This is right
|
|
// after that, so we should tell oemcrypto to use a test keybox.
|
|
if (session_count() == 1) {
|
|
CryptoSession::SetAllowTestKeybox(true);
|
|
ReinitializeForTest();
|
|
WvCdmTestBase::InstallTestRootOfTrust();
|
|
}
|
|
}
|
|
|
|
CdmResponseType TestCryptoSession::GenerateNonce(uint32_t* nonce) {
|
|
CdmResponseType status = CryptoSession::GenerateNonce(nonce);
|
|
for (int i = 0; status != NO_ERROR; i++) {
|
|
LOGV("Recovering from nonce flood.");
|
|
if (i > 2) return status;
|
|
wvutil::TestSleep::Sleep(1);
|
|
status = CryptoSession::GenerateNonce(nonce);
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
class TestCryptoSessionFactory : public CryptoSessionFactory {
|
|
CryptoSession* MakeCryptoSession(
|
|
metrics::CryptoMetrics* crypto_metrics) override {
|
|
// We need to add extra locking here because we need to make sure that there
|
|
// are no other OEMCrypto calls between OEMCrypto_Initialize and
|
|
// InstallTestRootOfTrust. OEMCrypto_Initialize is called in the production
|
|
// CryptoSession::Init and is wrapped in crypto_lock_, but
|
|
// InstallTestRootOfTrust is only called in the constructor of the
|
|
// TestCryptoSession, above.
|
|
std::unique_lock<std::mutex> auto_lock(init_lock_);
|
|
return new TestCryptoSession(crypto_metrics);
|
|
}
|
|
std::mutex init_lock_;
|
|
};
|
|
|
|
void WvCdmTestBase::SetUp() {
|
|
::testing::Test::SetUp();
|
|
Properties::Init();
|
|
Properties::set_provisioning_messages_are_binary(binary_provisioning_);
|
|
// Log the current test name, to help with debugging when the log and stdout
|
|
// are not the same.
|
|
const ::testing::TestInfo* const test_info =
|
|
::testing::UnitTest::GetInstance()->current_test_info();
|
|
LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name());
|
|
// Some test environments allow the model name of the device to be set
|
|
// dynamically using an environment variable. The model name will show up in
|
|
// the license server logs as the part of the device idenfication as
|
|
// "model_name".
|
|
std::string model_name =
|
|
std::string(test_info->test_case_name()) + "." + test_info->name();
|
|
int overwrite = 1; // Set value even if already set.
|
|
setenv("MODEL_NAME", model_name.c_str(), overwrite);
|
|
CryptoSession::SetCryptoSessionFactory(new TestCryptoSessionFactory());
|
|
// TODO(fredgc): Add a test version of DeviceFiles.
|
|
}
|
|
|
|
void WvCdmTestBase::InstallTestRootOfTrust() {
|
|
const wvoec::WidevineKeybox& test_keybox =
|
|
use_qa_test_keybox_ ? wvoec::kQATestKeybox : wvoec::kTestKeybox;
|
|
switch (wvoec::global_features.derive_key_method) {
|
|
case wvoec::DeviceFeatures::LOAD_TEST_KEYBOX:
|
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
|
OEMCrypto_LoadTestKeybox(
|
|
reinterpret_cast<const uint8_t*>(&test_keybox),
|
|
sizeof(test_keybox)));
|
|
break;
|
|
case wvoec::DeviceFeatures::LOAD_TEST_RSA_KEY:
|
|
// Rare case: used by devices with baked in DRM cert.
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey());
|
|
break;
|
|
case wvoec::DeviceFeatures::TEST_PROVISION_30:
|
|
// Can use oem certificate to install test rsa key.
|
|
break;
|
|
case wvoec::DeviceFeatures::TEST_PROVISION_40:
|
|
// OEM certificate is retrieved from the server.
|
|
break;
|
|
default:
|
|
FAIL() << "Cannot run test without test keybox or RSA key installed.";
|
|
}
|
|
}
|
|
|
|
WvCdmTestBase::WvCdmTestBase()
|
|
: config_(*default_config_), binary_provisioning_(false) {
|
|
const ::testing::TestInfo* const test_info =
|
|
::testing::UnitTest::GetInstance()->current_test_info();
|
|
|
|
LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name());
|
|
}
|
|
|
|
void WvCdmTestBase::Provision() {
|
|
CdmProvisioningRequest prov_request;
|
|
std::string provisioning_server_url;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority;
|
|
std::string cert, wrapped_key;
|
|
|
|
CdmSessionId session_id;
|
|
wvutil::FileSystem file_system;
|
|
|
|
if (config_.provisioning_server() == "fake") {
|
|
LOGD("Using fake provisioning server.");
|
|
|
|
TestCdmEngine cdm_engine(&file_system,
|
|
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
|
FakeProvisioningServer server;
|
|
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
|
cert_type, cert_authority, server.service_certificate(), kLevelDefault,
|
|
&prov_request, &provisioning_server_url);
|
|
ASSERT_EQ(NO_ERROR, result);
|
|
if (!binary_provisioning_) {
|
|
std::vector<uint8_t> prov_request_v =
|
|
wvutil::Base64SafeDecode(prov_request);
|
|
prov_request = std::string(prov_request_v.begin(), prov_request_v.end());
|
|
}
|
|
std::string response;
|
|
ASSERT_TRUE(server.MakeResponse(prov_request, &response))
|
|
<< "Fake provisioning server could not provision";
|
|
result = cdm_engine.HandleProvisioningResponse(response, kLevelDefault,
|
|
&cert, &wrapped_key);
|
|
EXPECT_EQ(NO_ERROR, result);
|
|
} else {
|
|
// TODO(fredgc): provision for different SPOIDs.
|
|
TestCdmEngine cdm_engine(&file_system,
|
|
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
|
|
|
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
|
cert_type, cert_authority, config_.provisioning_service_certificate(),
|
|
kLevelDefault, &prov_request, &provisioning_server_url);
|
|
ASSERT_EQ(NO_ERROR, result);
|
|
|
|
if (binary_provisioning_) {
|
|
prov_request = wvutil::Base64SafeEncodeNoPad(prov_request);
|
|
}
|
|
|
|
LOGV("Provisioning request: req = %s", prov_request.c_str());
|
|
|
|
// Ignore URL provided by CdmEngine. Use ours, as configured
|
|
// for test vs. production server.
|
|
provisioning_server_url.assign(config_.provisioning_server());
|
|
|
|
// Make request.
|
|
UrlRequest url_request(provisioning_server_url);
|
|
if (!url_request.is_connected()) {
|
|
LOGE("Failed to connect to provisioning server: url = %s",
|
|
provisioning_server_url.c_str());
|
|
}
|
|
url_request.PostCertRequestInQueryString(prov_request);
|
|
|
|
// Receive and parse response.
|
|
std::string http_message;
|
|
ASSERT_TRUE(url_request.GetResponse(&http_message))
|
|
<< "Failed to get provisioning response";
|
|
LOGV("http_message: \n%s\n", http_message.c_str());
|
|
|
|
if (binary_provisioning_) {
|
|
// extract provisioning response from received message
|
|
// Extracts signed response from JSON string, result is serialized
|
|
// protobuf.
|
|
static const std::string kMessageStart = "\"signedResponse\": \"";
|
|
static const std::string kMessageEnd = "\"";
|
|
std::string protobuf_response;
|
|
const bool extract_ok = ExtractSignedMessage(
|
|
http_message, kMessageStart, kMessageEnd, &protobuf_response);
|
|
ASSERT_TRUE(extract_ok) << "Failed to extract signed serialized "
|
|
"response from JSON response";
|
|
LOGV("Extracted response message: \n%s\n", protobuf_response.c_str());
|
|
|
|
ASSERT_FALSE(protobuf_response.empty())
|
|
<< "Protobuf response is unexpectedly empty";
|
|
|
|
// base64 decode response to yield binary protobuf
|
|
const std::vector<uint8_t> response_vec(
|
|
wvutil::Base64SafeDecode(protobuf_response));
|
|
ASSERT_FALSE(response_vec.empty())
|
|
<< "Failed to decode base64 of response: response = "
|
|
<< protobuf_response;
|
|
|
|
const std::string binary_protobuf_response(response_vec.begin(),
|
|
response_vec.end());
|
|
|
|
ASSERT_EQ(NO_ERROR, cdm_engine.HandleProvisioningResponse(
|
|
binary_protobuf_response, kLevelDefault, &cert,
|
|
&wrapped_key));
|
|
} else {
|
|
ASSERT_EQ(NO_ERROR,
|
|
cdm_engine.HandleProvisioningResponse(
|
|
http_message, kLevelDefault, &cert, &wrapped_key));
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(fredgc): Replace this with a pre-defined DRM certificate. We could do
|
|
// that because either the device is using a known test keybox with a known
|
|
// device key, or the device is using an OEM certificate, and we can extract
|
|
// that certificate from the provisioning request.
|
|
void WvCdmTestBase::EnsureProvisioned() {
|
|
CdmSessionId session_id;
|
|
wvutil::FileSystem file_system;
|
|
// OpenSession will check if a DRM certificate exists, while
|
|
// GenerateKeyRequest will actually load the wrapped private key.
|
|
// Either may return a NEED_PROVISIONING error, so both have to be checked.
|
|
TestCdmEngine cdm_engine(&file_system,
|
|
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
|
CdmResponseType status = cdm_engine.OpenSession(config_.key_system(), nullptr,
|
|
nullptr, &session_id);
|
|
CdmAppParameterMap app_parameters;
|
|
CdmKeySetId key_set_id;
|
|
InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id());
|
|
CdmKeyRequest key_request;
|
|
if (status == NO_ERROR) {
|
|
status = cdm_engine.GenerateKeyRequest(session_id, key_set_id, init_data,
|
|
kLicenseTypeStreaming,
|
|
app_parameters, &key_request);
|
|
}
|
|
|
|
// There are situations where we need two provisioning steps.
|
|
for (int count = 0; count < 2 && status == NEED_PROVISIONING; count++) {
|
|
Provision();
|
|
status = cdm_engine.OpenSession(config_.key_system(), nullptr, nullptr,
|
|
&session_id);
|
|
if (status == NEED_PROVISIONING) {
|
|
continue;
|
|
}
|
|
ASSERT_EQ(NO_ERROR, status);
|
|
status = cdm_engine.GenerateKeyRequest(session_id, key_set_id, init_data,
|
|
kLicenseTypeStreaming,
|
|
app_parameters, &key_request);
|
|
}
|
|
ASSERT_EQ(KEY_MESSAGE, status);
|
|
ASSERT_NE("", session_id) << "Could not open CDM session.";
|
|
ASSERT_TRUE(cdm_engine.IsOpenSession(session_id));
|
|
ASSERT_EQ(NO_ERROR, cdm_engine.CloseSession(session_id));
|
|
}
|
|
|
|
bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
|
const std::string& extra_help_text) {
|
|
Properties::Init();
|
|
bool is_cast_receiver = false;
|
|
bool filter_tests = true;
|
|
bool show_usage = false;
|
|
int verbosity = 0;
|
|
|
|
default_config_.reset(new ConfigTestEnv(kContentProtectionUatServer));
|
|
|
|
// Skip the first element, which is the program name.
|
|
const std::vector<std::string> args(argv + 1, argv + argc);
|
|
for (const std::string& arg : args) {
|
|
if (arg == "--verbose" || arg == "-v") {
|
|
++verbosity;
|
|
} else if (arg == "--no_filter") {
|
|
filter_tests = false;
|
|
} else if (arg == "--cast") {
|
|
is_cast_receiver = true;
|
|
} else if (arg == "--fake_sleep") {
|
|
wvutil::TestSleep::set_real_sleep(false);
|
|
} else if (arg == "--qa_provisioning") {
|
|
use_qa_test_keybox_ = true;
|
|
default_config_->set_provisioning_service_certificate(
|
|
default_config_->QAProvisioningServiceCertificate());
|
|
} else if (arg.find("--gtest") == 0) {
|
|
// gtest arguments will be passed to gtest by the main program.
|
|
continue;
|
|
} else {
|
|
const auto index = arg.find('=');
|
|
if (index == std::string::npos) {
|
|
std::cerr << "Argument values need to be specified using --arg=foo"
|
|
<< std::endl;
|
|
show_usage = true;
|
|
break;
|
|
}
|
|
|
|
const std::string arg_prefix = arg.substr(0, index);
|
|
const std::string arg_value = arg.substr(index + 1);
|
|
if (arg_prefix == "--license_server_id") {
|
|
if (arg_value == "gp") {
|
|
default_config_.reset(new ConfigTestEnv(kGooglePlayServer));
|
|
} else if (arg_value == "cp") {
|
|
default_config_.reset(new ConfigTestEnv(kContentProtectionUatServer));
|
|
} else if (arg_value == "st") {
|
|
default_config_.reset(
|
|
new ConfigTestEnv(kContentProtectionStagingServer));
|
|
} else {
|
|
std::cerr << "Invalid license server id: " << arg_value << std::endl;
|
|
show_usage = true;
|
|
break;
|
|
}
|
|
} else if (arg_prefix == "--keyid") {
|
|
default_config_->set_key_id(arg_value);
|
|
} else if (arg_prefix == "--service_certificate") {
|
|
const std::string certificate(wvutil::a2bs_hex(arg_value));
|
|
default_config_->set_license_service_certificate(certificate);
|
|
} else if (arg_prefix == "--provisioning_certificate") {
|
|
const std::string certificate(wvutil::a2bs_hex(arg_value));
|
|
default_config_->set_provisioning_service_certificate(certificate);
|
|
} else if (arg_prefix == "--license_server_url") {
|
|
default_config_->set_license_server(arg_value);
|
|
} else if (arg_prefix == "--renewal_server_url") {
|
|
default_config_->set_renewal_server(arg_value);
|
|
} else if (arg_prefix == "--provisioning_server_url") {
|
|
default_config_->set_provisioning_server(arg_value);
|
|
} else if (arg_prefix == "--pass") {
|
|
default_config_->set_test_pass(std::stoi(arg_value));
|
|
std::cout << "Running test pass " << default_config_->test_pass()
|
|
<< std::endl;
|
|
} else if (arg_prefix == "--initial_time") {
|
|
if (wvutil::TestSleep::real_sleep()) {
|
|
LOGD("Ignoring initial time %s because using a real clock",
|
|
arg_value.c_str());
|
|
} else {
|
|
LOGE("Setting initial fake clock time to %s", arg_value.c_str());
|
|
wvutil::TestSleep::SetFakeClock(stol(arg_value));
|
|
}
|
|
} else if (arg_prefix == "--test_data_path") {
|
|
default_config_->set_test_data_path(arg_value);
|
|
} else {
|
|
std::cerr << "Unknown argument " << arg_prefix << std::endl;
|
|
show_usage = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (show_usage) {
|
|
show_menu(argv[0], extra_help_text);
|
|
return false;
|
|
}
|
|
|
|
wvutil::g_cutoff = static_cast<wvutil::LogPriority>(verbosity);
|
|
|
|
// Displays server url, port and key Id being used
|
|
std::cout << std::endl;
|
|
std::cout << "Default Provisioning Server: "
|
|
<< default_config_->provisioning_server() << std::endl;
|
|
std::cout << "Default License Server: " << default_config_->license_server()
|
|
<< std::endl;
|
|
std::cout << "Default Renewal Server: " << default_config_->renewal_server()
|
|
<< std::endl;
|
|
std::cout << "Default KeyID: " << default_config_->key_id() << std::endl
|
|
<< std::endl;
|
|
|
|
// Figure out which tests are appropriate for OEMCrypto, based on features
|
|
// supported.
|
|
wvoec::global_features.Initialize();
|
|
wvoec::global_features.set_cast_receiver(is_cast_receiver);
|
|
// If the user requests --no_filter, we don't change the filter, otherwise, we
|
|
// filter out features that are not supported.
|
|
if (filter_tests) {
|
|
::testing::GTEST_FLAG(filter) =
|
|
wvoec::global_features.RestrictFilter(::testing::GTEST_FLAG(filter));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string MakePSSH(const video_widevine::WidevinePsshData& header) {
|
|
std::string data;
|
|
header.SerializeToString(&data);
|
|
return MakePSSH(data);
|
|
}
|
|
|
|
std::string MakePSSH(const std::string& serialized_header) {
|
|
const uint32_t version = 0;
|
|
const std::string system_id = InitializationData::WidevineSystemID();
|
|
size_t system_id_size = system_id.size();
|
|
size_t data_size = serialized_header.size();
|
|
size_t atom_size = data_size + system_id_size + 4 * 4;
|
|
|
|
std::string pssh = wvutil::EncodeUint32(static_cast<uint32_t>(atom_size));
|
|
pssh.append("pssh");
|
|
pssh.append(wvutil::EncodeUint32(version));
|
|
pssh.append(system_id);
|
|
pssh.append(wvutil::EncodeUint32(static_cast<uint32_t>(data_size)));
|
|
pssh.append(serialized_header);
|
|
return pssh;
|
|
}
|
|
|
|
} // namespace wvcdm
|