Source release 17.1.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
|
||||
#include "test_base.h"
|
||||
|
||||
@@ -93,17 +93,48 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
|
||||
<< " 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;
|
||||
}
|
||||
|
||||
@@ -148,7 +179,8 @@ bool ExtractSignedMessage(const std::string& response,
|
||||
|
||||
} // namespace
|
||||
|
||||
ConfigTestEnv WvCdmTestBase::default_config_(kContentProtectionUatServer);
|
||||
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) {
|
||||
@@ -186,7 +218,7 @@ 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], key.size(),
|
||||
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);
|
||||
@@ -196,8 +228,10 @@ std::string WvCdmTestBase::SignHMAC(const std::string& message,
|
||||
TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics)
|
||||
: CryptoSession(crypto_metrics) {
|
||||
// The first CryptoSession should have initialized OEMCrypto. This is right
|
||||
// after that, so should tell oemcrypto to use a test keybox.
|
||||
// after that, so we should tell oemcrypto to use a test keybox.
|
||||
if (session_count() == 1) {
|
||||
CryptoSession::SetAllowTestKeybox(true);
|
||||
ReinitializeForTest();
|
||||
WvCdmTestBase::InstallTestRootOfTrust();
|
||||
}
|
||||
}
|
||||
@@ -207,14 +241,15 @@ CdmResponseType TestCryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
for (int i = 0; status != NO_ERROR; i++) {
|
||||
LOGV("Recovering from nonce flood.");
|
||||
if (i > 2) return status;
|
||||
TestSleep::Sleep(1);
|
||||
wvutil::TestSleep::Sleep(1);
|
||||
status = CryptoSession::GenerateNonce(nonce);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
class TestCryptoSessionFactory : public CryptoSessionFactory {
|
||||
CryptoSession* MakeCryptoSession(metrics::CryptoMetrics* crypto_metrics) {
|
||||
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
|
||||
@@ -249,12 +284,14 @@ void WvCdmTestBase::SetUp() {
|
||||
}
|
||||
|
||||
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*>(&wvoec::kTestKeybox),
|
||||
sizeof(wvoec::kTestKeybox)));
|
||||
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.
|
||||
@@ -263,13 +300,16 @@ void WvCdmTestBase::InstallTestRootOfTrust() {
|
||||
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) {
|
||||
: config_(*default_config_), binary_provisioning_(false) {
|
||||
const ::testing::TestInfo* const test_info =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
|
||||
@@ -278,19 +318,18 @@ WvCdmTestBase::WvCdmTestBase()
|
||||
|
||||
void WvCdmTestBase::Provision() {
|
||||
CdmProvisioningRequest prov_request;
|
||||
CdmProvisioningRequest binary_prov_request;
|
||||
std::string provisioning_server_url;
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
std::string cert, wrapped_key;
|
||||
|
||||
CdmSessionId session_id;
|
||||
FileSystem file_system;
|
||||
std::unique_ptr<wvutil::FileSystem> file_system(CreateTestFileSystem());
|
||||
|
||||
if (config_.provisioning_server() == "fake") {
|
||||
LOGD("Using fake provisioning server.");
|
||||
|
||||
TestCdmEngine cdm_engine(&file_system,
|
||||
TestCdmEngine cdm_engine(file_system.get(),
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
FakeProvisioningServer server;
|
||||
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
||||
@@ -298,7 +337,8 @@ void WvCdmTestBase::Provision() {
|
||||
&prov_request, &provisioning_server_url);
|
||||
ASSERT_EQ(NO_ERROR, result);
|
||||
if (!binary_provisioning_) {
|
||||
std::vector<uint8_t> prov_request_v = Base64SafeDecode(prov_request);
|
||||
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;
|
||||
@@ -309,7 +349,7 @@ void WvCdmTestBase::Provision() {
|
||||
EXPECT_EQ(NO_ERROR, result);
|
||||
} else {
|
||||
// TODO(fredgc): provision for different SPOIDs.
|
||||
TestCdmEngine cdm_engine(&file_system,
|
||||
TestCdmEngine cdm_engine(file_system.get(),
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
|
||||
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
||||
@@ -318,9 +358,7 @@ void WvCdmTestBase::Provision() {
|
||||
ASSERT_EQ(NO_ERROR, result);
|
||||
|
||||
if (binary_provisioning_) {
|
||||
binary_prov_request = prov_request;
|
||||
prov_request = std::string(Base64SafeEncodeNoPad(std::vector<uint8_t>(
|
||||
binary_prov_request.begin(), binary_prov_request.end())));
|
||||
prov_request = wvutil::Base64SafeEncodeNoPad(prov_request);
|
||||
}
|
||||
|
||||
LOGV("Provisioning request: req = %s", prov_request.c_str());
|
||||
@@ -361,7 +399,7 @@ void WvCdmTestBase::Provision() {
|
||||
|
||||
// base64 decode response to yield binary protobuf
|
||||
const std::vector<uint8_t> response_vec(
|
||||
Base64SafeDecode(protobuf_response));
|
||||
wvutil::Base64SafeDecode(protobuf_response));
|
||||
ASSERT_FALSE(response_vec.empty())
|
||||
<< "Failed to decode base64 of response: response = "
|
||||
<< protobuf_response;
|
||||
@@ -386,17 +424,38 @@ void WvCdmTestBase::Provision() {
|
||||
// that certificate from the provisioning request.
|
||||
void WvCdmTestBase::EnsureProvisioned() {
|
||||
CdmSessionId session_id;
|
||||
FileSystem file_system;
|
||||
TestCdmEngine cdm_engine(&file_system,
|
||||
std::unique_ptr<wvutil::FileSystem> file_system(CreateTestFileSystem());
|
||||
// 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.get(),
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
CdmResponseType status = cdm_engine.OpenSession(config_.key_system(), nullptr,
|
||||
nullptr, &session_id);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
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(NO_ERROR, status);
|
||||
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));
|
||||
@@ -410,6 +469,8 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
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) {
|
||||
@@ -420,7 +481,11 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
} else if (arg == "--cast") {
|
||||
is_cast_receiver = true;
|
||||
} else if (arg == "--fake_sleep") {
|
||||
wvcdm::TestSleep::set_real_sleep(false);
|
||||
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;
|
||||
@@ -437,28 +502,45 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
const std::string arg_value = arg.substr(index + 1);
|
||||
if (arg_prefix == "--license_server_id") {
|
||||
if (arg_value == "gp") {
|
||||
default_config_ = ConfigTestEnv(kGooglePlayServer);
|
||||
default_config_.reset(new ConfigTestEnv(kGooglePlayServer));
|
||||
} else if (arg_value == "cp") {
|
||||
default_config_ = ConfigTestEnv(kContentProtectionUatServer);
|
||||
default_config_.reset(new ConfigTestEnv(kContentProtectionUatServer));
|
||||
} else if (arg_value == "st") {
|
||||
default_config_ = ConfigTestEnv(kContentProtectionStagingServer);
|
||||
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);
|
||||
default_config_->set_key_id(arg_value);
|
||||
} else if (arg_prefix == "--service_certificate") {
|
||||
const std::string certificate(a2bs_hex(arg_value));
|
||||
default_config_.set_license_service_certificate(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(a2bs_hex(arg_value));
|
||||
default_config_.set_provisioning_service_certificate(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);
|
||||
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);
|
||||
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;
|
||||
@@ -472,15 +554,17 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
return false;
|
||||
}
|
||||
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
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()
|
||||
<< default_config_->provisioning_server() << std::endl;
|
||||
std::cout << "Default License Server: " << default_config_->license_server()
|
||||
<< std::endl;
|
||||
std::cout << "Default KeyID: " << default_config_.key_id() << 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
|
||||
@@ -496,240 +580,6 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
return true;
|
||||
}
|
||||
|
||||
TestLicenseHolder::TestLicenseHolder(CdmEngine* cdm_engine)
|
||||
: cdm_engine_(cdm_engine),
|
||||
session_opened_(false),
|
||||
// Keys are initialized with simple values, and the correct size:
|
||||
derived_mac_key_server_(MAC_KEY_SIZE, 'a'),
|
||||
derived_mac_key_client_(MAC_KEY_SIZE, 'b'),
|
||||
mac_key_server_(MAC_KEY_SIZE, 'c'),
|
||||
mac_key_client_(MAC_KEY_SIZE, 'd'),
|
||||
enc_key_(CONTENT_KEY_SIZE, 'e'),
|
||||
session_key_(CONTENT_KEY_SIZE, 'f') {}
|
||||
|
||||
TestLicenseHolder::~TestLicenseHolder() { CloseSession(); }
|
||||
|
||||
void TestLicenseHolder::OpenSession(const std::string& key_system) {
|
||||
CdmResponseType status =
|
||||
cdm_engine_->OpenSession(key_system, nullptr, nullptr, &session_id_);
|
||||
ASSERT_EQ(status, NO_ERROR);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
ASSERT_TRUE(cdm_engine_->IsOpenSession(session_id_));
|
||||
session_opened_ = true;
|
||||
}
|
||||
|
||||
void TestLicenseHolder::CloseSession() {
|
||||
if (session_opened_) {
|
||||
cdm_engine_->CloseSession(session_id_);
|
||||
session_opened_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TestLicenseHolder::GenerateKeyRequest(
|
||||
const std::string& key_id, const std::string& init_data_type_string) {
|
||||
ASSERT_TRUE(session_opened_);
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmKeySetId key_set_id;
|
||||
InitializationData init_data(init_data_type_string, key_id);
|
||||
if (g_cutoff >= LOG_DEBUG) init_data.DumpToLogs();
|
||||
CdmKeyRequest key_request;
|
||||
CdmResponseType result = cdm_engine_->GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data, kLicenseTypeStreaming, app_parameters,
|
||||
&key_request);
|
||||
EXPECT_EQ(KEY_MESSAGE, result);
|
||||
signed_license_request_data_ = key_request.message;
|
||||
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
|
||||
}
|
||||
|
||||
void TestLicenseHolder::CreateDefaultLicense() {
|
||||
video_widevine::SignedMessage signed_message;
|
||||
EXPECT_TRUE(signed_message.ParseFromString(signed_license_request_data_));
|
||||
license_request_data_ = signed_message.msg();
|
||||
video_widevine::LicenseRequest license_request;
|
||||
EXPECT_TRUE(license_request.ParseFromString(license_request_data_));
|
||||
video_widevine::ClientIdentification client_id = license_request.client_id();
|
||||
|
||||
EXPECT_EQ(
|
||||
video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE,
|
||||
client_id.type());
|
||||
|
||||
// Extract the RSA key from the DRM certificate.
|
||||
std::string token = client_id.token();
|
||||
video_widevine::SignedDrmDeviceCertificate signed_drm_cert;
|
||||
EXPECT_TRUE(signed_drm_cert.ParseFromString(token));
|
||||
video_widevine::DrmDeviceCertificate drm_cert;
|
||||
EXPECT_TRUE(drm_cert.ParseFromString(signed_drm_cert.drm_certificate()));
|
||||
EXPECT_TRUE(rsa_key_.Init(drm_cert.public_key()));
|
||||
EXPECT_TRUE(rsa_key_.VerifySignature(signed_message.msg(),
|
||||
signed_message.signature()));
|
||||
|
||||
DeriveKeysFromSessionKey();
|
||||
|
||||
video_widevine::LicenseIdentification* license_id = license()->mutable_id();
|
||||
license_id->set_request_id("TestCase");
|
||||
license_id->set_session_id(session_id_);
|
||||
license_id->set_type(video_widevine::STREAMING);
|
||||
license_id->set_version(0);
|
||||
|
||||
::video_widevine::License_Policy* policy = license()->mutable_policy();
|
||||
policy->set_can_play(true);
|
||||
policy->set_can_persist(false);
|
||||
policy->set_can_renew(false);
|
||||
policy->set_playback_duration_seconds(0);
|
||||
policy->set_license_duration_seconds(0);
|
||||
|
||||
AddMacKey();
|
||||
}
|
||||
|
||||
void TestLicenseHolder::AddMacKey() {
|
||||
video_widevine::License_KeyContainer* key_container = license()->add_key();
|
||||
std::vector<uint8_t> iv(KEY_IV_SIZE, 'v');
|
||||
std::string iv_s(iv.begin(), iv.end());
|
||||
key_container->set_iv(iv_s);
|
||||
key_container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING);
|
||||
|
||||
// Combine server and client mac keys.
|
||||
std::vector<uint8_t> keys(mac_key_server_);
|
||||
keys.insert(keys.end(), mac_key_client_.begin(), mac_key_client_.end());
|
||||
std::string encrypted_keys =
|
||||
WvCdmTestBase::Aes128CbcEncrypt(enc_key_, keys, iv);
|
||||
key_container->set_key(encrypted_keys);
|
||||
}
|
||||
|
||||
video_widevine::License_KeyContainer* TestLicenseHolder::AddKey(
|
||||
const KeyId& key_id, const std::vector<uint8_t>& key_data,
|
||||
const wvoec::KeyControlBlock& block_in) {
|
||||
video_widevine::License_KeyContainer* key_container = license()->add_key();
|
||||
wvoec::KeyControlBlock block = block_in;
|
||||
if (block.verification[0] == 0) {
|
||||
block.verification[0] = 'k';
|
||||
block.verification[1] = 'c';
|
||||
block.verification[2] = '1';
|
||||
// This will work until oemcrypto api 20.
|
||||
block.verification[3] = '0' + wvoec::global_features.api_version - 10;
|
||||
}
|
||||
key_container->set_id(key_id);
|
||||
key_container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
|
||||
key_container->set_level(
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO);
|
||||
|
||||
std::vector<uint8_t> iv(KEY_IV_SIZE, 'v');
|
||||
std::string iv_s(iv.begin(), iv.end());
|
||||
key_container->set_iv(iv_s);
|
||||
|
||||
std::string encrypted_key_data =
|
||||
WvCdmTestBase::Aes128CbcEncrypt(enc_key_, key_data, iv);
|
||||
// TODO(b/111069024): remove this!
|
||||
std::string padding(CONTENT_KEY_SIZE, '-');
|
||||
key_container->set_key(encrypted_key_data + padding);
|
||||
|
||||
std::vector<uint8_t> block_v(
|
||||
reinterpret_cast<const uint8_t*>(&block),
|
||||
reinterpret_cast<const uint8_t*>(&block) + sizeof(block));
|
||||
std::vector<uint8_t> block_iv(KEY_IV_SIZE, 'w');
|
||||
std::string block_iv_s(block_iv.begin(), block_iv.end());
|
||||
std::string encrypted_block =
|
||||
WvCdmTestBase::Aes128CbcEncrypt(key_data, block_v, block_iv);
|
||||
key_container->mutable_key_control()->set_iv(block_iv_s);
|
||||
key_container->mutable_key_control()->set_key_control_block(encrypted_block);
|
||||
return key_container;
|
||||
}
|
||||
|
||||
void TestLicenseHolder::SignAndLoadLicense() {
|
||||
#if 0 // Need to turn off protobuf_lite to use this.
|
||||
LOGV("License = %s\n", license_.DebugString().c_str());
|
||||
#endif
|
||||
std::string license_data;
|
||||
license_.SerializeToString(&license_data);
|
||||
|
||||
std::string signature =
|
||||
WvCdmTestBase::SignHMAC(license_data, derived_mac_key_server_);
|
||||
|
||||
std::string session_key_s(session_key_.begin(), session_key_.end());
|
||||
std::string encrypted_session_key;
|
||||
EXPECT_TRUE(rsa_key_.Encrypt(session_key_s, &encrypted_session_key));
|
||||
video_widevine::SignedMessage signed_response;
|
||||
signed_response.set_msg(license_data);
|
||||
signed_response.set_type(video_widevine::SignedMessage_MessageType_LICENSE);
|
||||
signed_response.set_session_key(encrypted_session_key);
|
||||
signed_response.set_signature(signature);
|
||||
|
||||
std::string response_data;
|
||||
signed_response.SerializeToString(&response_data);
|
||||
|
||||
CdmKeySetId key_set_id;
|
||||
CdmLicenseType license_type; // Required for AddKey. Result value ignored.
|
||||
EXPECT_EQ(KEY_ADDED, cdm_engine_->AddKey(session_id_, response_data,
|
||||
&license_type, &key_set_id));
|
||||
}
|
||||
|
||||
void TestLicenseHolder::DeriveKeysFromSessionKey() {
|
||||
std::string context;
|
||||
GenerateMacContext(license_request_data_, &context);
|
||||
std::vector<uint8_t> mac_key_context(context.begin(), context.end());
|
||||
GenerateEncryptContext(license_request_data_, &context);
|
||||
std::vector<uint8_t> enc_key_context(context.begin(), context.end());
|
||||
|
||||
ASSERT_TRUE(
|
||||
DeriveKey(session_key_, mac_key_context, 1, &derived_mac_key_server_));
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
ASSERT_TRUE(DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2));
|
||||
derived_mac_key_server_.insert(derived_mac_key_server_.end(),
|
||||
mac_key_part2.begin(), mac_key_part2.end());
|
||||
|
||||
ASSERT_TRUE(
|
||||
DeriveKey(session_key_, mac_key_context, 3, &derived_mac_key_client_));
|
||||
ASSERT_TRUE(DeriveKey(session_key_, mac_key_context, 4, &mac_key_part2));
|
||||
derived_mac_key_client_.insert(derived_mac_key_client_.end(),
|
||||
mac_key_part2.begin(), mac_key_part2.end());
|
||||
|
||||
std::vector<uint8_t> enc_key;
|
||||
ASSERT_TRUE(DeriveKey(session_key_, enc_key_context, 1, &enc_key_));
|
||||
}
|
||||
|
||||
bool TestLicenseHolder::DeriveKey(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& context,
|
||||
int counter, std::vector<uint8_t>* out) {
|
||||
if (key.empty() || counter > 4 || context.empty() || out == nullptr) {
|
||||
LOGE("DeriveKey(): bad context");
|
||||
return false;
|
||||
}
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
|
||||
if (!cmac_ctx) {
|
||||
LOGE("DeriveKey(): cmac failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) {
|
||||
LOGE("DeriveKey(): CMAC_Init");
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.push_back(counter);
|
||||
message.insert(message.end(), context.begin(), context.end());
|
||||
|
||||
if (!CMAC_Update(cmac_ctx, &message[0], message.size())) {
|
||||
LOGE("DeriveKey(): CMAC_Update");
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t reslen;
|
||||
uint8_t res[128];
|
||||
if (!CMAC_Final(cmac_ctx, res, &reslen)) {
|
||||
LOGE("DeriveKey(): CMAC_Final");
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
out->assign(res, res + reslen);
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MakePSSH(const video_widevine::WidevinePsshData& header) {
|
||||
std::string data;
|
||||
header.SerializeToString(&data);
|
||||
@@ -743,11 +593,11 @@ std::string MakePSSH(const std::string& serialized_header) {
|
||||
size_t data_size = serialized_header.size();
|
||||
size_t atom_size = data_size + system_id_size + 4 * 4;
|
||||
|
||||
std::string pssh = EncodeUint32(atom_size);
|
||||
std::string pssh = wvutil::EncodeUint32(static_cast<uint32_t>(atom_size));
|
||||
pssh.append("pssh");
|
||||
pssh.append(EncodeUint32(version));
|
||||
pssh.append(wvutil::EncodeUint32(version));
|
||||
pssh.append(system_id);
|
||||
pssh.append(EncodeUint32(data_size));
|
||||
pssh.append(wvutil::EncodeUint32(static_cast<uint32_t>(data_size)));
|
||||
pssh.append(serialized_header);
|
||||
return pssh;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user