OEMCrypto v16.1
Merge of http://go/wvgerrit/93404 This CL updates the Widevine CDM to support OEMCrypto v16.1 Test: Tested in 16.2 CL Bug: 141247171 Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
@@ -1,8 +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.
|
||||
// This file adds some print methods so that when unit tests fail, the
|
||||
// will print the name of an enumeration instead of the numeric value.
|
||||
|
||||
#include "test_base.h"
|
||||
|
||||
@@ -17,7 +15,9 @@
|
||||
#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"
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "platform.h"
|
||||
#include "properties.h"
|
||||
#include "test_printers.h"
|
||||
#include "test_sleep.h"
|
||||
#include "url_request.h"
|
||||
|
||||
using wvcdm::metrics::EngineMetrics;
|
||||
@@ -98,6 +99,11 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
|
||||
<< " in the url" << 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 << extra_help_text << std::endl;
|
||||
}
|
||||
|
||||
@@ -201,7 +207,7 @@ CdmResponseType TestCryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
for (int i = 0; status != NO_ERROR; i++) {
|
||||
LOGV("Recovering from nonce flood.");
|
||||
if (i > 2) return status;
|
||||
sleep(1);
|
||||
TestSleep::Sleep(1);
|
||||
status = CryptoSession::GenerateNonce(nonce);
|
||||
}
|
||||
return NO_ERROR;
|
||||
@@ -273,104 +279,127 @@ void WvCdmTestBase::Provision() {
|
||||
CdmSessionId session_id;
|
||||
FileSystem file_system;
|
||||
|
||||
// TODO(fredgc): provision for different SPOIDs.
|
||||
CdmEngine cdm_engine(&file_system,
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
if (config_.provisioning_server() == "fake") {
|
||||
LOGD("Using fake provisioning server.");
|
||||
|
||||
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
||||
cert_type, cert_authority, config_.provisioning_service_certificate(),
|
||||
&prov_request, &provisioning_server_url);
|
||||
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())));
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// TODO(b/139361531): Remove loop once provisioning service is stable.
|
||||
std::string http_message;
|
||||
size_t attempt_num = 0;
|
||||
bool provision_success = false;
|
||||
do {
|
||||
if (attempt_num > 0) {
|
||||
// Sleep between attempts.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
CdmEngine cdm_engine(&file_system,
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
FakeProvisioningServer server;
|
||||
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
||||
cert_type, cert_authority, server.service_certificate(), &prov_request,
|
||||
&provisioning_server_url);
|
||||
ASSERT_EQ(NO_ERROR, result);
|
||||
if (!binary_provisioning_) {
|
||||
std::vector<uint8_t> prov_request_v = Base64SafeDecode(prov_request);
|
||||
prov_request = std::string(prov_request_v.begin(), prov_request_v.end());
|
||||
}
|
||||
++attempt_num;
|
||||
std::string response;
|
||||
ASSERT_TRUE(server.MakeResponse(prov_request, &response))
|
||||
<< "Fake provisioning server could not provision";
|
||||
result =
|
||||
cdm_engine.HandleProvisioningResponse(response, &cert, &wrapped_key);
|
||||
EXPECT_EQ(NO_ERROR, result);
|
||||
} else {
|
||||
// TODO(fredgc): provision for different SPOIDs.
|
||||
CdmEngine cdm_engine(&file_system,
|
||||
std::shared_ptr<EngineMetrics>(new EngineMetrics));
|
||||
|
||||
// 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());
|
||||
continue;
|
||||
}
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
|
||||
// Receive and parse response.
|
||||
if (!url_request.GetResponse(&http_message)) {
|
||||
LOGE("Failed to get provisioning response");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGV("http_message: \n%s\n", http_message.c_str());
|
||||
CdmResponseType result = cdm_engine.GetProvisioningRequest(
|
||||
cert_type, cert_authority, config_.provisioning_service_certificate(),
|
||||
&prov_request, &provisioning_server_url);
|
||||
ASSERT_EQ(NO_ERROR, result);
|
||||
|
||||
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;
|
||||
if (!ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
|
||||
&protobuf_response)) {
|
||||
LOGE("Failed to extract signed serialized response from JSON response");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGV("Extracted response message: \n%s\n", protobuf_response.c_str());
|
||||
|
||||
// base64 decode response to yield binary protobuf
|
||||
std::vector<uint8_t> response_vec(Base64SafeDecode(protobuf_response));
|
||||
if (response_vec.empty() && !protobuf_response.empty()) {
|
||||
LOGE("Failed to decode base64 of response: response = %s",
|
||||
protobuf_response.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string binary_protobuf_response(response_vec.begin(),
|
||||
response_vec.end());
|
||||
|
||||
if (cdm_engine.HandleProvisioningResponse(binary_protobuf_response, &cert,
|
||||
&wrapped_key) != NO_ERROR) {
|
||||
LOGE("Failed to handle provisioning response");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (cdm_engine.HandleProvisioningResponse(http_message, &cert,
|
||||
&wrapped_key) != NO_ERROR) {
|
||||
LOGE("Failed to handle binary provisioning response");
|
||||
continue;
|
||||
}
|
||||
binary_prov_request = prov_request;
|
||||
prov_request = std::string(Base64SafeEncodeNoPad(std::vector<uint8_t>(
|
||||
binary_prov_request.begin(), binary_prov_request.end())));
|
||||
}
|
||||
provision_success = true;
|
||||
} while (attempt_num <= kDefaultMaxProvisioningAttempts &&
|
||||
!provision_success);
|
||||
|
||||
if (attempt_num > 1) {
|
||||
LOGW("Provisioning request failed at least once: attempts = %zu",
|
||||
attempt_num);
|
||||
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());
|
||||
|
||||
// TODO(b/139361531): Remove loop once provisioning service is stable.
|
||||
std::string http_message;
|
||||
size_t attempt_num = 0;
|
||||
bool provision_success = false;
|
||||
do {
|
||||
if (attempt_num > 0) {
|
||||
// Sleep between attempts.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
++attempt_num;
|
||||
|
||||
// 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());
|
||||
continue;
|
||||
}
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
|
||||
// Receive and parse response.
|
||||
if (!url_request.GetResponse(&http_message)) {
|
||||
LOGE("Failed to get provisioning response");
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
|
||||
&protobuf_response)) {
|
||||
LOGE(
|
||||
"Failed to extract signed serialized response from JSON "
|
||||
"response");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGV("Extracted response message: \n%s\n", protobuf_response.c_str());
|
||||
|
||||
// base64 decode response to yield binary protobuf
|
||||
std::vector<uint8_t> response_vec(Base64SafeDecode(protobuf_response));
|
||||
if (response_vec.empty() && !protobuf_response.empty()) {
|
||||
LOGE("Failed to decode base64 of response: response = %s",
|
||||
protobuf_response.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string binary_protobuf_response(response_vec.begin(),
|
||||
response_vec.end());
|
||||
|
||||
if (cdm_engine.HandleProvisioningResponse(
|
||||
binary_protobuf_response, &cert, &wrapped_key) != NO_ERROR) {
|
||||
LOGE("Failed to handle provisioning response");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (cdm_engine.HandleProvisioningResponse(http_message, &cert,
|
||||
&wrapped_key) != NO_ERROR) {
|
||||
LOGE("Failed to handle binary provisioning response");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
provision_success = true;
|
||||
} while (attempt_num <= kDefaultMaxProvisioningAttempts &&
|
||||
!provision_success);
|
||||
|
||||
if (attempt_num > 1) {
|
||||
LOGW("Provisioning request failed at least once: attempts = %zu",
|
||||
attempt_num);
|
||||
}
|
||||
ASSERT_TRUE(provision_success)
|
||||
<< "Failed to provision: message = " << http_message;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(provision_success)
|
||||
<< "Failed to provision: message = " << http_message;
|
||||
}
|
||||
|
||||
// TODO(fredgc): Replace this with a pre-defined DRM certificate. We could do
|
||||
@@ -399,7 +428,6 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
const std::string& extra_help_text) {
|
||||
Properties::Init();
|
||||
bool is_cast_receiver = false;
|
||||
bool force_load_test_keybox = false; // TODO(fredgc): obsolete. remove.
|
||||
bool filter_tests = true;
|
||||
bool show_usage = false;
|
||||
int verbosity = 0;
|
||||
@@ -413,6 +441,11 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
filter_tests = false;
|
||||
} else if (arg == "--cast") {
|
||||
is_cast_receiver = true;
|
||||
} else if (arg == "--fake_sleep") {
|
||||
wvcdm::TestSleep::set_real_sleep(false);
|
||||
} 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) {
|
||||
@@ -465,14 +498,17 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
|
||||
// Displays server url, port and key Id being used
|
||||
std::cout << std::endl;
|
||||
std::cout << "Default Server: " << default_config_.license_server()
|
||||
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 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(is_cast_receiver, force_load_test_keybox);
|
||||
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) {
|
||||
@@ -715,4 +751,26 @@ bool TestLicenseHolder::DeriveKey(const std::vector<uint8_t>& key,
|
||||
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 = EncodeUint32(atom_size);
|
||||
pssh.append("pssh");
|
||||
pssh.append(EncodeUint32(version));
|
||||
pssh.append(system_id);
|
||||
pssh.append(EncodeUint32(data_size));
|
||||
pssh.append(serialized_header);
|
||||
return pssh;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user