Source release 14.2.0

This commit is contained in:
John W. Bruce
2018-10-12 19:55:47 -07:00
parent c32e8d0490
commit f51edaba5a
632 changed files with 196557 additions and 66444 deletions

View File

@@ -23,6 +23,7 @@
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "test_base.h"
#include "test_printers.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
@@ -30,109 +31,38 @@
namespace wvcdm {
using drm_metrics::WvCdmMetrics;
using drm_metrics::DistributionMetric;
using drm_metrics::WvCdmMetrics;
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;
KeyId g_key_id_pssh;
KeyId g_key_id_unwrapped;
CdmKeySystem g_key_system;
std::string g_license_server;
std::string g_provisioning_server;
std::string g_provisioning_service_certificate;
std::string g_license_service_certificate;
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
static void CommonSetup(ServerConfigurationId which,
bool bin_prov = false) {
Properties::set_provisioning_messages_are_binary(bin_prov);
Properties::Init();
// NOTE: Select configuration
ConfigTestEnv config(which);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_license_server.assign(config.license_server());
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh);
g_key_id_unwrapped = extractor.data();
}
/*
* 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
class WvCdmEnginePreProvTest : public testing::Test {
class WvCdmEnginePreProvTest : public WvCdmTestBase {
public:
WvCdmEnginePreProvTest() : cdm_engine_(&file_system_),
session_opened_(false) {}
WvCdmEnginePreProvTest()
: cdm_engine_(&file_system_), session_opened_(false) {}
virtual ~WvCdmEnginePreProvTest() {}
virtual void SetUp() {
WvCdmTestBase::SetUp();
session_opened_ = false;
}
virtual void OpenSession() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
cdm_engine_.OpenSession(config_.key_system(), NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
status = cdm_engine_.OpenSession(config_.key_system(), NULL, NULL,
&session_id_);
}
ASSERT_EQ(status, NO_ERROR);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
@@ -154,13 +84,12 @@ class WvCdmEnginePreProvTest : public testing::Test {
}
protected:
// Trade request for response via the license server.
virtual bool LicenseServerRequestResponse(const std::string& request,
std::string* response) {
LOGV("LicenseServerRequestResponse: server url: %s",
g_license_server.c_str());
UrlRequest url_request(g_license_server + g_client_auth);
config_.license_server().c_str());
UrlRequest url_request(config_.license_server() + config_.client_auth());
url_request.PostRequest(request);
std::string http_response;
@@ -175,58 +104,12 @@ class WvCdmEnginePreProvTest : public testing::Test {
license_request.GetDrmMessage(http_response, *response);
LOGV("response: size=%u, string:\n%s\n", response->size(),
Base64SafeEncode(std::vector<uint8_t>(response->begin(),
response->end())).c_str());
Base64SafeEncode(
std::vector<uint8_t>(response->begin(), response->end()))
.c_str());
return true;
}
virtual void Provision() {
LOGV("WvCdmEnginePreProvTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
// Keep a crypto session alive so that OEMCrypto won't terminate while we
// try to provision. This is needed for testing nonce floods.
CryptoSession keep_alive(cdm_engine_.GetMetrics()->GetCryptoMetrics());
CdmResponseType result = NO_ERROR;
for(int i = 0; i < 2; ++i) { // Retry once if there is a nonce problem.
result = cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, g_provisioning_service_certificate,
&prov_request, &provisioning_server_url);
if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) {
LOGW("Woops. Nonce problem. Try again?");
sleep(1);
} else {
break;
}
}
ASSERT_EQ(NO_ERROR, result);
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok) << http_message;
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(http_message,
&cert, &wrapped_key))
<< "message = " << http_message;
}
FileSystem file_system_;
CdmEngine cdm_engine_;
bool session_opened_;
@@ -236,125 +119,44 @@ class WvCdmEnginePreProvTest : public testing::Test {
class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStaging() {}
virtual ~WvCdmEnginePreProvTestStaging() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingServer);
WvCdmEnginePreProvTestStaging() {
config_ = ConfigTestEnv(kContentProtectionStagingServer);
}
};
class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestProd() {}
virtual ~WvCdmEnginePreProvTestProd() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionProductionServer);
WvCdmEnginePreProvTestProd() {
config_ = ConfigTestEnv(kContentProtectionProductionServer);
}
};
class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUat() {}
virtual ~WvCdmEnginePreProvTestUat() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatServer);
WvCdmEnginePreProvTestUat() {
config_ = ConfigTestEnv(kContentProtectionUatServer);
}
};
class WvCdmEnginePreProvTestUatBinary : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUatBinary() {}
virtual ~WvCdmEnginePreProvTestUatBinary() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
WvCdmEnginePreProvTestUatBinary() {
config_ = ConfigTestEnv(kContentProtectionUatServer);
// Override default setting of provisioning_messages_are_binary property
CommonSetup(kContentProtectionUatServer, true);
binary_provisioning_ = true;
}
protected:
virtual void Provision() {
LOGV("WvCdmEnginePreProvTestProv30Binary::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest binary_prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, g_provisioning_service_certificate,
&binary_prov_request, &provisioning_server_url));
// prov_request is binary - base64 encode it
std::string prov_request(Base64SafeEncodeNoPad(
std::vector<uint8_t>(binary_prov_request.begin(),
binary_prov_request.end())));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
// extract provisioning response from received message
// Extracts signed response from JSON string, result is serialized protobuf.
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string protobuf_response;
EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&protobuf_response)) <<
"Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response "
"message: \n%s\n", protobuf_response.c_str());
// base64 decode response to yield binary protobuf
std::vector<uint8_t> response_vec(Base64SafeDecode(
std::string(protobuf_response.begin(), protobuf_response.end())));
std::string binary_protobuf_response(response_vec.begin(),
response_vec.end());
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(binary_protobuf_response,
&cert, &wrapped_key));
}
};
class WvCdmEngineTest : public WvCdmEnginePreProvTest {
public:
WvCdmEngineTest() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatServer);
}
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
cdm_engine_.OpenSession(config_.key_system(), NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
status = cdm_engine_.OpenSession(config_.key_system(), NULL, NULL, &session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
@@ -362,7 +164,6 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
}
protected:
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
CdmAppParameterMap app_parameters;
@@ -372,19 +173,9 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
CdmKeyRequest key_request;
CdmResponseType result = NO_ERROR;
for(int i=0; i < 2; i++) { // Retry once if there is a nonce problem.
result = cdm_engine_.GenerateKeyRequest(
session_id_, key_set_id, init_data,
kLicenseTypeStreaming, app_parameters,
&key_request);
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGW("Woops. Nonce problem. Try again?");
sleep(1);
} else {
break;
}
}
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id_, key_set_id, init_data, kLicenseTypeStreaming,
app_parameters, &key_request);
EXPECT_EQ(KEY_MESSAGE, result);
key_msg_ = key_request.message;
@@ -393,16 +184,8 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
void GenerateRenewalRequest() {
CdmKeyRequest request;
CdmResponseType result = NO_ERROR;
for (int i = 0; i < 2; i++) { // Retry once if there is a nonce problem.
result = cdm_engine_.GenerateRenewalRequest(session_id_, &request);
if (result == LICENSE_RENEWAL_NONCE_GENERATION_ERROR) {
LOGW("Woops. Nonce problem. Try again?");
sleep(1);
} else {
break;
}
}
CdmResponseType result =
cdm_engine_.GenerateRenewalRequest(session_id_, &request);
EXPECT_EQ(KEY_MESSAGE, result);
key_msg_ = request.message;
@@ -439,8 +222,8 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
EXPECT_TRUE(ok);
int status_code = url_request.GetStatusCode(response);
if (expect_success) EXPECT_EQ(kHttpOk, status_code)
<< "Error response: " << response;
if (expect_success)
EXPECT_EQ(kHttpOk, status_code) << "Error response: " << response;
if (status_code != kHttpOk) {
return "";
@@ -450,7 +233,8 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
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());
drm_msg.size())
.c_str());
return drm_msg;
}
}
@@ -459,8 +243,7 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
const std::string& client_auth) {
std::string resp = GetKeyRequestResponse(server_url, client_auth);
CdmKeySetId key_set_id;
EXPECT_EQ(KEY_ADDED,
cdm_engine_.AddKey(session_id_, resp, &key_set_id));
EXPECT_EQ(KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id));
VerifyLicenseRequestLatency(kKeyRequestTypeInitial,
*cdm_engine_.GetMetrics());
}
@@ -487,8 +270,9 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
j++) {
DistributionMetric latency_distribution =
session_metrics.cdm_session_license_request_latency_ms(j);
if (latency_distribution.attributes().key_request_type()
== key_request_type && latency_distribution.operation_count() > 0) {
if (latency_distribution.attributes().key_request_type() ==
key_request_type &&
latency_distribution.operation_count() > 0) {
has_request_type = true;
}
}
@@ -505,88 +289,88 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
// Tests to validate service certificate
TEST_F(WvCdmEnginePreProvTestUat, ProvisioningServiceCertificateValidTest) {
ASSERT_EQ(
cdm_engine_.ValidateServiceCertificate(
g_provisioning_service_certificate),
NO_ERROR);
ASSERT_EQ(cdm_engine_.ValidateServiceCertificate(
config_.provisioning_service_certificate()),
NO_ERROR);
};
TEST_F(WvCdmEnginePreProvTestUat, ProvisioningServiceCertificateInvalidTest) {
std::string certificate = g_provisioning_service_certificate;
std::string certificate = config_.provisioning_service_certificate();
// Add four nulls to the beginning of the cert to invalidate it
certificate.insert(0, 4, '\0');
ASSERT_NE(cdm_engine_.ValidateServiceCertificate(certificate), NO_ERROR);
};
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEnginePreProvTestStaging, DISABLED_ProvisioningTest) {
uint32_t nonce = 0;
uint8_t buffer[1];
size_t size = 0;
int result = OEMCrypto_RewrapDeviceRSAKey(
0, buffer, 0, buffer, 0, &nonce, buffer, 0, buffer, buffer, &size);
int result30 = OEMCrypto_RewrapDeviceRSAKey30(
0, &nonce, buffer, 0, buffer, 0, buffer, buffer, &size);
int method = OEMCrypto_GetProvisioningMethod(kLevelDefault);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED &&
result30 == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGW("WARNING: Skipping ProvisioningTest because the device does not "
"support provisioning. If you are using a baked-in certificate, "
"this is expected. Otherwise, something is wrong.");
ASSERT_EQ(method, OEMCrypto_DrmCertificate);
} else {
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(method, OEMCrypto_OEMCertificate);
} else {
ASSERT_EQ(method, OEMCrypto_Keybox);
}
}
Provision();
}
TEST_F(WvCdmEnginePreProvTestStaging, ProvisioningTest) { Provision(); }
// TODO(b/112046733): This test is broken. It should be fixed.
TEST_F(WvCdmEnginePreProvTestUatBinary, DISABLED_ProvisioningTest) {
Provision();
}
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, DISABLED_ProvisioningTest) {
// Test that provisioning works.
TEST_F(WvCdmEngineTest, ProvisioningTest) {
Provision();
}
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, ReprovisioningTest) {
// Provision once.
Provision();
// Verify that we can provision a second time, even though we already
// provisioned once.
Provision();
}
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
GenerateKeyRequest(binary_key_id(), kCencMimeType);
GetKeyRequestResponse(config_.license_server(), config_.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);
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT, binary_key_id());
KeyId key_id_unwrapped = extractor.data();
GenerateKeyRequest(key_id_unwrapped, kWebmMimeType);
GetKeyRequestResponse(config_.license_server(), config_.client_auth());
}
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
GenerateKeyRequest(binary_key_id(), kCencMimeType);
VerifyNewKeyResponse(config_.license_server(), config_.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);
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT, binary_key_id());
KeyId key_id_unwrapped = extractor.data();
GenerateKeyRequest(key_id_unwrapped, kWebmMimeType);
VerifyNewKeyResponse(config_.license_server(), config_.client_auth());
}
TEST_F(WvCdmEngineTest, LoadKey) {
EnsureProvisioned();
TestLicenseHolder holder(&cdm_engine_);
holder.OpenSession(config_.key_system());
holder.GenerateKeyRequest(binary_key_id(), ISO_BMFF_VIDEO_MIME_TYPE);
holder.CreateDefaultLicense();
std::vector<uint8_t> key_data(CONTENT_KEY_SIZE, '1');
wvoec::KeyControlBlock block = {};
holder.AddKey("key_one", key_data, block);
holder.SignAndLoadLicense();
}
TEST_F(WvCdmEngineTest, LicenseRenewal) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
GenerateKeyRequest(binary_key_id(), kCencMimeType);
VerifyNewKeyResponse(config_.license_server(), config_.client_auth());
GenerateRenewalRequest();
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
g_client_auth);
VerifyRenewalKeyResponse(
server_url_.empty() ? config_.license_server() : server_url_,
config_.client_auth());
}
} // namespace wvcdm

View File

@@ -11,6 +11,7 @@
#include "scoped_ptr.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "test_base.h"
#include "test_printers.h"
#include "usage_table_header.h"
#include "wv_cdm_constants.h"
@@ -122,10 +123,10 @@ class MockUsageTableHeader : public UsageTableHeader {
CdmUsageEntry* usage_entry));
};
class MockCryptoSession : public CryptoSession {
class MockCryptoSession : public TestCryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) {
: TestCryptoSession(crypto_metrics) {
// By default, call the concrete implementation of GetUsageSupportType.
ON_CALL(*this, GetUsageSupportType(_))
.WillByDefault(
@@ -167,9 +168,10 @@ class MockCdmLicense : public CdmLicense {
} // namespace
class CdmSessionTest : public ::testing::Test {
class CdmSessionTest : public WvCdmTestBase {
protected:
virtual void SetUp() {
WvCdmTestBase::SetUp();
cdm_session_.reset(new CdmSession(NULL, &metrics_));
// Inject testing mocks.
license_parser_ = new MockCdmLicense(cdm_session_->session_id());

View File

@@ -232,7 +232,7 @@ const CdmInitData kEmptyData;
// Google Play license server data
const std::string kGpLicenseServer =
"https://jmt17.google.com/video/license/GetCencLicense";
"https://play.google.com/video-qa/license/GetWhitelistCencLicense";
// Test client authorization string.
// NOTE: Append a userdata attribute to place a unique marker that the
// server team can use to track down specific requests during debugging
@@ -260,14 +260,17 @@ const std::string kGpClientOfflineReleaseQueryParameters =
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kCpProductionProvisioningServerUrl, ""},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
// TODO(rfrias): replace when b/62880305 is addressed. For now use production
kCpProductionProvisioningServerUrl,
kCpProductionProvisioningServiceCertificate},
{kContentProtectionStagingServer, kCpStagingLicenseServer,
kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kCpStagingProvisioningServerUrl, kCpStagingProvisioningServiceCertificate},
{kContentProtectionProductionServer, kCpProductionLicenseServer,
kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kCpProductionProvisioningServerUrl,
@@ -303,7 +306,21 @@ ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming,
}
}
ConfigTestEnv& ConfigTestEnv::operator=(const ConfigTestEnv &other) {
this->server_id_ = other.server_id_;
this->client_auth_ = other.client_auth_;
this->key_id_ = other.key_id_;
this->key_system_ = other.key_system_;
this->license_server_ = other.license_server_;
this->provisioning_server_ = other.provisioning_server_;
this->license_service_certificate_ = other.license_service_certificate_;
this->provisioning_service_certificate_ =
other.provisioning_service_certificate_;
return *this;
}
void ConfigTestEnv::Init(ServerConfigurationId server_id) {
this->server_id_ = server_id;
client_auth_ = license_servers[server_id].client_tag;
key_id_ = license_servers[server_id].key_id;
key_system_ = kWidevineKeySystem;

View File

@@ -49,20 +49,25 @@ class ConfigTestEnv {
typedef struct {
ServerConfigurationId id;
std::string license_server_url;
std::string license_service_certificate;
std::string license_service_certificate; // Binary (not hex).
std::string client_tag;
std::string key_id;
std::string key_id; // Hex.
std::string offline_key_id;
std::string provisioning_server_url;
std::string provisioning_service_certificate;
std::string provisioning_service_certificate; // Binary (not hex).
} LicenseServerConfiguration;
explicit ConfigTestEnv(ServerConfigurationId server_id);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming, bool renew,
bool release);
// Allow copy and assign. Performance is not an issue in test initialization.
ConfigTestEnv(const ConfigTestEnv &other) { *this = other; };
ConfigTestEnv& operator=(const ConfigTestEnv &other);
~ConfigTestEnv() {};
ServerConfigurationId server_id() { return server_id_; }
const std::string& client_auth() const { return client_auth_; }
const KeyId& key_id() const { return key_id_; }
const CdmKeySystem& key_system() const { return key_system_; }
@@ -88,13 +93,22 @@ class ConfigTestEnv {
void set_license_server(std::string& license_server) {
license_server_.assign(license_server);
}
void set_license_service_certificate(
std::string& license_service_certificate) {
license_service_certificate_.assign(license_service_certificate);
}
void set_provisioning_server(std::string& provisioning_server) {
provisioning_server_.assign(provisioning_server);
}
void set_provisioning_service_certificate(
std::string& provisioning_service_certificate) {
provisioning_service_certificate_.assign(provisioning_service_certificate);
}
private:
void Init(ServerConfigurationId server_id);
ServerConfigurationId server_id_;
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
@@ -102,8 +116,6 @@ class ConfigTestEnv {
std::string provisioning_server_;
std::string license_service_certificate_;
std::string provisioning_service_certificate_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
};
} // namespace wvcdm

View File

@@ -15,6 +15,8 @@
#include "metrics.pb.h"
#include "metrics_collections.h"
#include "scoped_ptr.h"
#include "test_base.h"
#include "test_printers.h"
#include "wv_cdm_types.h"
namespace {
@@ -240,10 +242,10 @@ const uint32_t kOemCertSystemId = 7346;
namespace wvcdm {
class CryptoSessionForTest : public CryptoSession, public testing::Test {
class CryptoSessionForTest : public TestCryptoSession, public WvCdmTestBase {
public:
using CryptoSession::ExtractSystemIdFromOemCert;
CryptoSessionForTest() : CryptoSession(metrics_.GetCryptoMetrics()) {}
CryptoSessionForTest() : TestCryptoSession(metrics_.GetCryptoMetrics()) {}
void SetUp() {}
@@ -264,7 +266,7 @@ TEST(CryptoSessionTest, CanExtractSystemIdFromOemCertificate) {
ASSERT_EQ(kOemCertSystemId, system_id);
}
class CryptoSessionMetricsTest : public testing::Test {
class CryptoSessionMetricsTest : public WvCdmTestBase {
protected:
uint32_t FindKeyboxSystemID() {
OEMCryptoResult sts;
@@ -280,11 +282,12 @@ class CryptoSessionMetricsTest : public testing::Test {
TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) {
metrics::CryptoMetrics crypto_metrics;
CryptoSession session(&crypto_metrics);
session.Open(wvcdm::kLevelDefault);
scoped_ptr<CryptoSession> session(
CryptoSession::MakeCryptoSession(&crypto_metrics));
session->Open(wvcdm::kLevelDefault);
// Exercise a method that will touch a metric.
CdmUsageSupportType usage_type;
ASSERT_EQ(NO_ERROR, session.GetUsageSupportType(&usage_type));
ASSERT_EQ(NO_ERROR, session->GetUsageSupportType(&usage_type));
drm_metrics::WvCdmMetrics::CryptoMetrics metrics_proto;
crypto_metrics.Serialize(&metrics_proto);
@@ -305,7 +308,7 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) {
metrics_proto.oemcrypto_usage_table_support().int_value());
// Validate metrics that differ based on provisioning type.
CdmClientTokenType token_type = session.GetPreProvisionTokenType();
CdmClientTokenType token_type = session->GetPreProvisionTokenType();
if (token_type == kClientTokenKeybox) {
uint32_t system_id = FindKeyboxSystemID();
@@ -337,18 +340,19 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) {
TEST_F(CryptoSessionMetricsTest, GetProvisioningTokenValidMetrics) {
metrics::CryptoMetrics crypto_metrics;
CryptoSession session(&crypto_metrics);
scoped_ptr<CryptoSession> session(
CryptoSession::MakeCryptoSession(&crypto_metrics));
ASSERT_EQ(NO_ERROR, session.Open(wvcdm::kLevelDefault));
ASSERT_EQ(NO_ERROR, session->Open(wvcdm::kLevelDefault));
CdmClientTokenType token_type = session.GetPreProvisionTokenType();
CdmClientTokenType token_type = session->GetPreProvisionTokenType();
LOGI("token_type: %d", token_type);
// DRM Certificate provisioning method does not support a provisioning
// token. Otherwise, we should be able to fetch the token.
std::string token;
if (token_type != kClientTokenDrmCert) {
ASSERT_TRUE(session.GetProvisioningToken(&token));
ASSERT_TRUE(session->GetProvisioningToken(&token));
}
drm_metrics::WvCdmMetrics::CryptoMetrics metrics_proto;

View File

@@ -20,438 +20,285 @@
#include "oemcrypto_session_tests_helper.h"
#include "oemcrypto_types.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "test_base.h"
#include "test_printers.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace {
const std::string kKeySystem = "com.widevine.alpha";
std::string g_provisioning_server;
std::string g_license_service_certificate;
std::string g_provisioning_service_certificate;
} // namespace
namespace wvcdm {
class WvGenericOperationsTest : public testing::Test,
public wvoec::SessionUtil {
class WvGenericOperationsTest : public WvCdmTestBase {
public:
WvGenericOperationsTest() : crypto_session_(NULL) {}
WvGenericOperationsTest()
: cdm_engine_(&file_system_), holder_(&cdm_engine_) {}
virtual void SetUp() {
::testing::Test::SetUp();
WvCdmTestBase::SetUp();
EnsureProvisioned();
holder_.OpenSession(config_.key_system());
holder_.GenerateKeyRequest(binary_key_id(), ISO_BMFF_VIDEO_MIME_TYPE);
holder_.CreateDefaultLicense();
ConfigTestEnv config(kContentProtectionStagingLicense);
Properties::set_provisioning_messages_are_binary(false);
ency_id_ = "ency";
StripeBuffer(&ency_key_, CONTENT_KEY_SIZE, 'e');
AddOneKey(ency_id_, ency_key_, wvoec::kControlAllowEncrypt);
dency_id_ = "dency";
StripeBuffer(&dency_key_, CONTENT_KEY_SIZE, 'd');
AddOneKey(dency_id_, dency_key_, wvoec::kControlAllowDecrypt);
siggy_id_ = "siggy";
StripeBuffer(&siggy_key_, MAC_KEY_SIZE, 's');
AddOneKey(siggy_id_, siggy_key_, wvoec::kControlAllowSign);
vou_id_ = "vou";
StripeBuffer(&vou_key_, MAC_KEY_SIZE, 'v');
AddOneKey(vou_id_, vou_key_, wvoec::kControlAllowVerify);
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
StripeBuffer(&in_vector_, CONTENT_KEY_SIZE * 15, '1');
in_buffer_ = std::string(in_vector_.begin(), in_vector_.end());
// Ensure that OEMCrypto has a test keybox if needed.
EnsureTestKeys();
// Perform CdmEngine setup
cdm_engine_.reset(new CdmEngine(&file_system_));
Provision();
CdmResponseType status =
cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
ASSERT_TRUE(cdm_engine_->IsOpenSession(session_id_));
// Get OEMCrypto session ID from the CDM
CdmQueryMap query;
cdm_engine_->QueryOemCryptoSessionId(session_id_, &query);
std::istringstream parse_int;
parse_int.str(query[QUERY_KEY_OEMCRYPTO_SESSION_ID]);
parse_int >> oec_session_id_;
// Construct and install keys into the CDM's OEMCrypto session.
OecSessionSetup(oec_session_id_);
EncryptAndLoadKeys();
StripeBuffer(&iv_vector_, KEY_IV_SIZE, 'a');
iv_ = std::string(iv_vector_.begin(), iv_vector_.end());
}
virtual void TearDown() {
oec_util_session_.close();
if (cdm_engine_.get() != NULL) {
cdm_engine_->CloseSession(session_id_);
}
// OEMCrypto_Terminate() will be performed during the test class's
// destruction (specifically by the CryptoSession destructor)
}
virtual void TearDown() { holder_.CloseSession(); }
void OecSessionSetup(uint32_t oec_session_id) {
buffer_size_ = 160;
oec_util_session_.SetSessionId(oec_session_id);
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
oec_util_session_.GenerateDerivedKeysFromKeybox();
MakeFourKeys();
}
enum GenericKeyType {
kGenericEncrypt = 0,
kGenericDecrypt = 1,
kGenericSign = 2,
kGenericVerify = 3
};
virtual void MakeFourKeys(
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
uint32_t nonce = 0, const std::string& pst = "") {
ASSERT_NO_FATAL_FAILURE(
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
htonl(wvoec::kControlAllowEncrypt);
oec_util_session_.license().keys[kGenericDecrypt].control.control_bits |=
htonl(wvoec::kControlAllowDecrypt);
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
htonl(wvoec::kControlAllowSign);
oec_util_session_.license().keys[kGenericVerify].control.control_bits |=
htonl(wvoec::kControlAllowVerify);
oec_util_session_.license().keys[kGenericSign].key_data_length =
wvcdm::MAC_KEY_SIZE;
oec_util_session_.license().keys[kGenericVerify].key_data_length =
wvcdm::MAC_KEY_SIZE;
clear_buffer_.assign(buffer_size_, 0);
for (size_t i = 0; i < clear_buffer_.size(); i++) {
clear_buffer_[i] = 1 + i % 250;
}
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
iv_[i] = i;
}
}
std::string GetKeyId(GenericKeyType type) {
std::string key_id;
size_t key_id_length = oec_util_session_.license().keys[0].key_id_length;
key_id.assign(
&(oec_util_session_.license().keys[type].key_id[0]),
&(oec_util_session_.license().keys[type].key_id[key_id_length]));
return key_id;
}
std::string GetClearBuffer() {
std::string buffer;
size_t buffer_length = clear_buffer_.size();
buffer.assign(&clear_buffer_[0], &clear_buffer_[buffer_length]);
return buffer;
}
std::string GetEncryptedBuffer() {
std::string buffer;
size_t buffer_length = encrypted_buffer_.size();
buffer.assign(&encrypted_buffer_[0], &encrypted_buffer_[buffer_length]);
return buffer;
}
std::string GetIvBlock() {
std::string buffer;
size_t buffer_length = wvcdm::KEY_IV_SIZE;
buffer.assign(&iv_[0], &iv_[buffer_length]);
return buffer;
}
std::string GetSignatureBuffer() {
std::string buffer;
buffer.resize(SHA256_DIGEST_LENGTH);
return buffer;
}
void EncryptAndLoadKeys() {
ASSERT_NO_FATAL_FAILURE(oec_util_session_.EncryptAndSign());
oec_util_session_.LoadTestKeys();
// Create a single key, and add it to the license.
void AddOneKey(const KeyId& key_id, const std::vector<uint8_t>& key_data,
uint32_t key_control_block) {
wvoec::KeyControlBlock block = {};
block.control_bits = htonl(key_control_block);
holder_.AddKey(key_id, key_data, block);
}
protected:
virtual void Provision() {
LOGE("WvGenericOperationsTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_->SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
LOGV("WvGenericOperationsTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvGenericOperationsTest::Provision: http_message: \n%s\n",
http_message.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_->HandleProvisioningResponse(http_message,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_->SetServiceCertificate(g_license_service_certificate));
}
// This CryptoSession object handles Initialization and Termination
// calls on OEMCrypto for the duration of the test. CryptoSessions
// created by the CDM will share the OEMCrypto state of this CryptoSession,
// including, for example, a test keybox.
CryptoSession crypto_session_;
FileSystem file_system_;
std::unique_ptr<CdmEngine> cdm_engine_;
std::string key_msg_;
std::string session_id_;
std::string server_url_;
uint32_t oec_session_id_;
wvoec::Session oec_util_session_;
size_t buffer_size_;
vector<uint8_t> clear_buffer_;
vector<uint8_t> encrypted_buffer_;
uint8_t iv_[wvcdm::KEY_IV_SIZE];
CdmEngine cdm_engine_;
TestLicenseHolder holder_;
KeyId ency_id_;
KeyId dency_id_;
KeyId siggy_id_;
KeyId vou_id_;
std::vector<uint8_t> ency_key_;
std::vector<uint8_t> dency_key_;
std::vector<uint8_t> siggy_key_;
std::vector<uint8_t> vou_key_;
std::vector<uint8_t> in_vector_;
std::vector<uint8_t> iv_vector_;
std::string in_buffer_;
std::string iv_;
};
TEST_F(WvGenericOperationsTest, NormalSessionOpenClose) {
wvoec::Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.close());
}
TEST_F(WvGenericOperationsTest, GenerateSessionKeys) {
wvoec::Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox());
ASSERT_NO_FATAL_FAILURE(s.close());
}
TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// No key
KeyId key_id("xyz");
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(NO_CONTENT_KEY_2, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Wrong key
std::string key_id = GetKeyId(kGenericDecrypt);
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
TEST_F(WvGenericOperationsTest, LoadSpecialKeys) {
holder_.SignAndLoadLicense();
}
TEST_F(WvGenericOperationsTest, GenericEncryptGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
std::string encrypted = Aes128CbcEncrypt(ency_key_, in_vector_, iv_vector_);
std::string out_buffer;
// Good key
std::string key_id = GetKeyId(kGenericEncrypt);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericEncrypt(
holder_.session_id(), in_buffer_, ency_id_, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(encrypted, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericDecryptKeyNotAllowed) {
TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
std::string encrypted = Aes128CbcEncrypt(ency_key_, in_vector_, iv_vector_);
std::string out_buffer;
KeyId key_id("no_key");
// Wrong key
std::string key_id = GetKeyId(kGenericEncrypt);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericEncrypt(
holder_.session_id(), in_buffer_, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
EXPECT_EQ(NO_CONTENT_KEY_3, cdm_sts);
EXPECT_NE(encrypted, out_buffer);
}
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) {
CdmResponseType cdm_sts;
// Trying to use Decrypt key to encrypt, which is not allowed.
KeyId key_id = dency_id_;
std::string encrypted = Aes128CbcEncrypt(dency_key_, in_vector_, iv_vector_);
std::string out_buffer;
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericEncrypt(
holder_.session_id(), in_buffer_, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
EXPECT_NE(encrypted, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericDecryptGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
std::string decrypted = Aes128CbcDecrypt(dency_key_, in_vector_, iv_vector_);
std::string out_buffer;
// Good key
std::string key_id = GetKeyId(kGenericDecrypt);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericDecrypt(
holder_.session_id(), in_buffer_, dency_id_, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(decrypted, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericDecryptNoKey) {
CdmResponseType cdm_sts;
std::string decrypted = Aes128CbcDecrypt(dency_key_, in_vector_, iv_vector_);
std::string out_buffer;
KeyId key_id = "no_key";
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericDecrypt(
holder_.session_id(), in_buffer_, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
EXPECT_EQ(NO_CONTENT_KEY_3, cdm_sts);
EXPECT_NE(decrypted, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericDecryptKeyNotAllowed) {
CdmResponseType cdm_sts;
// Trying to use Encrypt key to decrypt, which is not allowed.
KeyId key_id = ency_id_;
std::string decrypted = Aes128CbcDecrypt(ency_key_, in_vector_, iv_vector_);
std::string out_buffer;
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericDecrypt(
holder_.session_id(), in_buffer_, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
EXPECT_NE(decrypted, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericSignKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer;
// Wrong key
std::string key_id = GetKeyId(kGenericVerify);
std::string key_id = vou_id_;
std::string out_buffer;
std::string signature = SignHMAC(in_buffer_, siggy_key_);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
holder_.SignAndLoadLicense();
cdm_sts =
cdm_engine_.GenericSign(holder_.session_id(), in_buffer_, key_id,
wvcdm::kSigningAlgorithmHmacSha256, &out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
EXPECT_NE(signature, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericSignGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer;
std::string out_buffer;
std::string signature = SignHMAC(in_buffer_, siggy_key_);
// Good key
std::string key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
holder_.SignAndLoadLicense();
cdm_sts =
cdm_engine_.GenericSign(holder_.session_id(), in_buffer_, siggy_id_,
wvcdm::kSigningAlgorithmHmacSha256, &out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(signature, out_buffer);
}
TEST_F(WvGenericOperationsTest, GenericVerifyKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
// Wrong key
std::string key_id = GetKeyId(kGenericSign);
std::string key_id = siggy_id_;
std::string signature = SignHMAC(in_buffer_, siggy_key_);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericVerify(holder_.session_id(), in_buffer_, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericVerifyBadSignautre) {
CdmResponseType cdm_sts;
std::string signature(MAC_KEY_SIZE, 's');
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericVerify(
holder_.session_id(), in_buffer_, vou_id_,
wvcdm::kSigningAlgorithmHmacSha256, signature);
// OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericVerifyGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
std::string signature = SignHMAC(in_buffer_, vou_key_);
// Good key - signature not set.
std::string key_id = GetKeyId(kGenericVerify);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
// OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
holder_.SignAndLoadLicense();
cdm_sts =
cdm_engine_.GenericVerify(holder_.session_id(), in_buffer_, vou_id_,
wvcdm::kSigningAlgorithmHmacSha256, signature);
EXPECT_EQ(NO_ERROR, cdm_sts);
}
class WvGenericOperationsDataTest : public WvGenericOperationsTest {
public:
// Construct keys for encrypt/decrypt and for sign/verify
virtual void MakeFourKeys(
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
uint32_t nonce = 0, const std::string& pst = "") {
ASSERT_NO_FATAL_FAILURE(
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
htonl(wvoec::kControlAllowEncrypt |
wvoec::kControlAllowDecrypt);
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
htonl(wvoec::kControlAllowSign | wvoec::kControlAllowVerify);
oec_util_session_.license().keys[kGenericSign].key_data_length =
wvcdm::MAC_KEY_SIZE;
clear_buffer_.assign(buffer_size_, 0);
for (size_t i = 0; i < clear_buffer_.size(); i++) {
clear_buffer_[i] = 1 + i % 250;
}
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
iv_[i] = i;
}
}
};
TEST_F(WvGenericOperationsDataTest, GenericEncryptDecrypt) {
TEST_F(WvGenericOperationsTest, GenericEncryptDecrypt) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string encrypted_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
std::string out_buffer;
std::string clear_buffer;
// Encrypt
std::string key_id = GetKeyId(kGenericEncrypt);
KeyId key_id = "enc and dec";
cdm_sts = cdm_engine_->GenericEncrypt(
session_id_, in_buffer, key_id, iv, wvcdm::kEncryptionAlgorithmAesCbc128,
&encrypted_buffer);
std::vector<uint8_t> key_data;
StripeBuffer(&key_data, CONTENT_KEY_SIZE, '3');
AddOneKey(key_id, key_data,
wvoec::kControlAllowEncrypt | wvoec::kControlAllowDecrypt);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericEncrypt(
holder_.session_id(), in_buffer_, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
// Decrypt, use same key as encrypt.
key_id = GetKeyId(kGenericEncrypt);
std::string final_buffer;
final_buffer.resize(in_buffer.size());
cdm_sts = cdm_engine_->GenericDecrypt(
session_id_, encrypted_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128, &final_buffer);
cdm_sts = cdm_engine_.GenericDecrypt(
holder_.session_id(), out_buffer, key_id, iv_,
wvcdm::kEncryptionAlgorithmAesCbc128, &clear_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(0, in_buffer.compare(final_buffer));
EXPECT_EQ(in_buffer_, clear_buffer);
}
TEST_F(WvGenericOperationsDataTest, GenericSignVerify) {
TEST_F(WvGenericOperationsTest, GenericSignVerify) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
std::string signature_buffer;
// Signing key
std::string key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
KeyId key_id = "sign and ver";
// Verify signature, use same key as sign.
key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
std::vector<uint8_t> key_data;
StripeBuffer(&key_data, MAC_KEY_SIZE, '4');
AddOneKey(key_id, key_data,
wvoec::kControlAllowSign | wvoec::kControlAllowVerify);
std::string signature = SignHMAC(in_buffer_, key_data);
holder_.SignAndLoadLicense();
cdm_sts = cdm_engine_.GenericSign(holder_.session_id(), in_buffer_, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(MAC_KEY_SIZE, signature_buffer.size());
cdm_sts = cdm_engine_.GenericVerify(holder_.session_id(), in_buffer_, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(signature, signature_buffer);
}
} // namespace wvcdm

View File

@@ -304,7 +304,7 @@ class LicenseKeysTest : public ::testing::Test {
constraints.clear();
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
AddConstraint(constraints, key_hi_res_min, key_hi_res_max,
AddConstraint(constraints, key_hi_res_min, key_hi_res_max, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
AddContentKey(ck_NO_HDCP_dual_res, true, KeyContainer::HW_SECURE_ALL, true,
@@ -835,12 +835,12 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);

View File

@@ -8,6 +8,7 @@
#include <memory>
#include "clock.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "initialization_data.h"
#include "license.h"
@@ -15,6 +16,8 @@
#include "properties.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "test_base.h"
#include "test_printers.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
@@ -142,10 +145,24 @@ const CryptoSession::SupportedCertificateTypes kDefaultSupportedCertTypes = {
true
};
class MockCryptoSession : public CryptoSession {
const std::string kFakeEntitlementKeyId =
a2bs_hex("2a538231c616c67143032a645f9c545d");
const std::string kFakeEntitledKeyId =
a2bs_hex("f93c7a81e62d4e9a988ff20bca60f52d");
const std::string kFakeUnpaddedKey =
a2bs_hex("b2047e7fab08b3a4dac76b7b82e8cd4d");
const std::string kFakePaddedKey =
a2bs_hex("42f804e9ce0fa693692e1c4ffaeb0e14"
"10101010101010101010101010101010");
const std::string kFakeKeyTooLong =
a2bs_hex("d4bc8605d662878a46adb2adb6bf3c0b30a54a0c2f");
const std::string kFakeKeyTooShort = a2bs_hex("06e247e7f924208011");
const std::string kFakeIv = a2bs_hex("3d515a3ee0be1687080ac59da9e0d69a");
class MockCryptoSession : public TestCryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
: TestCryptoSession(crypto_metrics) { }
MOCK_METHOD0(IsOpen, bool());
MOCK_METHOD1(GenerateRequestId, bool(std::string*));
MOCK_METHOD1(UsageInformationSupport, bool(bool*));
@@ -157,12 +174,16 @@ class MockCryptoSession : public CryptoSession {
MOCK_METHOD3(GenerateSubSessionNonce,
bool(const std::string& sub_session_key_id, bool* exists,
uint32_t* nonce));
MOCK_METHOD1(LoadEntitledContentKeys,
CdmResponseType(const std::vector<CryptoKey>& key_array));
};
class MockPolicyEngine : public PolicyEngine {
public:
MockPolicyEngine(CryptoSession* crypto)
: PolicyEngine("mock_session_id", NULL, crypto) {}
MOCK_METHOD1(SetEntitledLicenseKeys,
void(const std::vector<video_widevine::WidevinePsshData_EntitledKey>&));
};
class MockClock : public Clock {
@@ -183,22 +204,40 @@ class MockInitializationData : public InitializationData {
// Protobuf generated classes
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::ClientIdentification;
using video_widevine::License;
using video_widevine::License_KeyContainer;
using video_widevine::LicenseRequest;
using video_widevine::SignedMessage;
using video_widevine::WidevinePsshData_EntitledKey;
// gmock methods
using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::PrintToStringParamName;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::UnorderedElementsAre;
using ::testing::Values;
class CdmLicenseTest : public ::testing::Test {
class CdmLicenseTestPeer : public CdmLicense {
public:
CdmLicenseTestPeer(const CdmSessionId& session_id, Clock* clock)
: CdmLicense(session_id, clock) {}
using CdmLicense::HandleNewEntitledKeys;
void set_entitlement_keys(License license) {
entitlement_keys_.CopyFrom(license.key());
}
};
class CdmLicenseTest : public WvCdmTestBase {
protected:
CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh))
: pssh_(pssh) {}
virtual void SetUp() {
WvCdmTestBase::SetUp();
clock_ = new MockClock();
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT, pssh_);
@@ -218,11 +257,11 @@ class CdmLicenseTest : public ::testing::Test {
}
virtual void CreateCdmLicense() {
cdm_license_ = new CdmLicense(kCdmSessionId, clock_);
cdm_license_ = new CdmLicenseTestPeer(kCdmSessionId, clock_);
clock_ = NULL;
}
CdmLicense* cdm_license_;
CdmLicenseTestPeer* cdm_license_;
MockClock* clock_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
@@ -413,6 +452,85 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
EXPECT_EQ(kNonce, license_request.key_control_nonce());
}
struct EntitledKeyVariant {
EntitledKeyVariant(const char* name, const std::string& key,
bool should_succeed)
: name(name),
key(key),
should_succeed(should_succeed) {}
const std::string name;
const std::string key;
const bool should_succeed;
friend void PrintTo(const EntitledKeyVariant& self, std::ostream* os) {
*os << self.name;
}
};
class CdmLicenseEntitledKeyTest
: public CdmLicenseTest,
public ::testing::WithParamInterface<EntitledKeyVariant> {};
TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) {
EntitledKeyVariant variant = GetParam();
// Set up a known, fake entitlement key
License entitlement_license;
License_KeyContainer* entitlement_key = entitlement_license.add_key();
entitlement_key->set_type(
video_widevine::License_KeyContainer_KeyType_ENTITLEMENT);
entitlement_key->set_id(kFakeEntitlementKeyId);
// Set up a fake entitled key that matches the entitlement key
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
WidevinePsshData_EntitledKey& padded_key = entitled_keys[0];
padded_key.set_entitlement_key_id(kFakeEntitlementKeyId);
padded_key.set_key_id(kFakeEntitledKeyId);
padded_key.set_key(variant.key);
padded_key.set_iv(kFakeIv);
// Set the expected downstream calls
EXPECT_CALL(*crypto_session_, IsOpen())
.WillOnce(Return(true));
if (variant.should_succeed) {
EXPECT_CALL(*crypto_session_, LoadEntitledContentKeys(_))
.WillOnce(Return(KEY_ADDED));
EXPECT_CALL(*policy_engine_, SetEntitledLicenseKeys(_))
.Times(1);
} else {
EXPECT_CALL(*crypto_session_, LoadEntitledContentKeys(_))
.Times(0);
EXPECT_CALL(*policy_engine_, SetEntitledLicenseKeys(_))
.Times(0);
}
// Set up the CdmLicense with the mocks and fake entitlement key
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(
kToken, kClientTokenDrmCert, kEmptyString, true,
kDefaultServiceCertificate, crypto_session_, policy_engine_));
cdm_license_->set_entitlement_keys(entitlement_license);
// Call the function under test and check its return value
CdmResponseType ret = cdm_license_->HandleNewEntitledKeys(entitled_keys);
if (variant.should_succeed) {
EXPECT_EQ(KEY_ADDED, ret);
} else {
EXPECT_NE(KEY_ADDED, ret);
}
}
INSTANTIATE_TEST_CASE_P(
EntitledKeyTests, CdmLicenseEntitledKeyTest,
Values(
EntitledKeyVariant("UnpaddedKey", kFakeUnpaddedKey, true),
EntitledKeyVariant("PaddedKey", kFakePaddedKey, true),
EntitledKeyVariant("KeyTooLong", kFakeKeyTooLong, true),
EntitledKeyVariant("KeyTooShort", kFakeKeyTooShort, false)),
PrintToStringParamName());
// TODO(jfore): The pssh has changed in ways that are not compatible with
//sublicenses. Restructure or remove sublicense support including this test.
TEST_F(SubLicenseTest, DISABLED_VerifySubSessionData) {

View File

@@ -11,6 +11,8 @@
#include "policy_engine.h"
#include "mock_clock.h"
#include "scoped_ptr.h"
#include "test_base.h"
#include "test_printers.h"
#include "wv_cdm_event_listener.h"
#include "wv_cdm_types.h"
@@ -62,10 +64,10 @@ const OutputProtection::HDCP kHdcpV2_1 = OutputProtection::HDCP_V2_1;
// should match kHdcpCheckInterval in policy_engine.cpp
const int64_t kHdcpInterval = 10;
class HdcpOnlyMockCryptoSession : public CryptoSession {
class HdcpOnlyMockCryptoSession : public TestCryptoSession {
public:
HdcpOnlyMockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
: TestCryptoSession(crypto_metrics) { }
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
};
@@ -82,7 +84,7 @@ class MockCdmEventListener : public WvCdmEventListener {
} // namespace
class PolicyEngineConstraintsTest : public Test {
class PolicyEngineConstraintsTest : public WvCdmTestBase {
public:
PolicyEngineConstraintsTest() :
crypto_session_(&dummy_metrics_) {
@@ -90,6 +92,7 @@ class PolicyEngineConstraintsTest : public Test {
protected:
virtual void SetUp() {
WvCdmTestBase::SetUp();
current_time_ = 0;
policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_,

View File

@@ -14,6 +14,7 @@
#include "mock_clock.h"
#include "policy_engine.h"
#include "scoped_ptr.h"
#include "test_base.h"
#include "test_printers.h"
#include "wv_cdm_event_listener.h"
#include "wv_cdm_constants.h"
@@ -57,10 +58,10 @@ int64_t ParseInt(const std::string& str) {
return ret;
}
class HdcpOnlyMockCryptoSession : public CryptoSession {
class HdcpOnlyMockCryptoSession : public TestCryptoSession {
public:
HdcpOnlyMockCryptoSession(metrics::CryptoMetrics* metrics) :
CryptoSession(metrics) {}
TestCryptoSession(metrics) {}
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
bool DoRealGetHdcpCapabilities(HdcpCapability* current,
@@ -99,12 +100,13 @@ using ::testing::Return;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
class PolicyEngineTest : public ::testing::Test {
class PolicyEngineTest : public WvCdmTestBase {
public:
PolicyEngineTest() : crypto_session_(&dummy_metrics_) {
}
protected:
virtual void SetUp() {
WvCdmTestBase::SetUp();
policy_engine_.reset(
new PolicyEngine(kSessionId, &mock_event_listener_,
&crypto_session_));
@@ -1670,7 +1672,7 @@ class PolicyEngineKeySecurityLevelTest
: public PolicyEngineTest,
public ::testing::WithParamInterface<KeySecurityLevelParams*> {};
TEST_P(PolicyEngineKeySecurityLevelTest, CanUseKey) {
TEST_P(PolicyEngineKeySecurityLevelTest, CanUseKeyForSecurityLevel) {
KeySecurityLevelParams* param = GetParam();
@@ -1691,13 +1693,17 @@ TEST_P(PolicyEngineKeySecurityLevelTest, CanUseKey) {
policy_engine_->SetLicense(license_);
EXPECT_EQ(param->expect_can_L1_use_key,
policy_engine_->CanUseKey(kKeyId, kSecurityLevelL1));
policy_engine_->CanUseKeyForSecurityLevel(kKeyId,
kSecurityLevelL1));
EXPECT_EQ(param->expect_can_L2_use_key,
policy_engine_->CanUseKey(kKeyId, kSecurityLevelL2));
policy_engine_->CanUseKeyForSecurityLevel(kKeyId,
kSecurityLevelL2));
EXPECT_EQ(param->expect_can_L3_use_key,
policy_engine_->CanUseKey(kKeyId, kSecurityLevelL3));
policy_engine_->CanUseKeyForSecurityLevel(kKeyId,
kSecurityLevelL3));
EXPECT_EQ(param->expect_can_level_unknown_use_key,
policy_engine_->CanUseKey(kKeyId, kSecurityLevelUnknown));
policy_engine_->CanUseKeyForSecurityLevel(kKeyId,
kSecurityLevelUnknown));
}
INSTANTIATE_TEST_CASE_P(

676
core/test/test_base.cpp Normal file
View File

@@ -0,0 +1,676 @@
// 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"
#include <getopt.h>
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/cmac.h>
#include <stdlib.h>
#include <sstream>
#include "cdm_engine.h"
#include "crypto_session.h"
#include "file_store.h"
#include "license.h"
#include "log.h"
#include "oec_device_features.h"
#include "oec_test_data.h"
#include "properties.h"
#include "test_printers.h"
#include "url_request.h"
namespace wvcdm {
namespace {
void show_menu(char* prog_name) {
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 << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl
<< std::endl;
std::cout << " -v/--verbose" << std::endl;
std::cout << " increase logging verbosity (may be repeated)" << std::endl
<< std::endl;
std::cout << " -f/--no_filter" << std::endl;
std::cout << " Do not filter out inappropriate tests" << std::endl
<< std::endl;
std::cout << " -c/--cast" << std::endl;
std::cout << " Run tests appropriate for a Cast Receiver" << std::endl
<< std::endl;
std::cout << " -i/--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 << " -k/--keyid=<key_id>" << std::endl;
std::cout << " configure the key id or pssh, in hex format" << std::endl
<< std::endl;
std::cout << " -s/--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 << " -S/--provisioning_certificate=<cert>" << std::endl;
std::cout << " configure the signed provisioning service certificate" << std::endl
<< " in hex" << std::endl << std::endl;
std::cout << " -u/--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 << " -p/--provisioning_server_url=<url>" << std::endl;
std::cout << " configure the provisioning server url, please include http[s]"
<< " in the url" << std::endl
<< 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
ConfigTestEnv WvCdmTestBase::default_config_(kContentProtectionUatServer);
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], 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 should tell oemcrypto to use a test keybox.
if (session_count() == 1) {
WvCdmTestBase::InstallTestRootOfTrust();
}
}
bool TestCryptoSession::GenerateNonce(uint32_t* nonce) {
for (int i = 0; !CryptoSession::GenerateNonce(nonce); i++) {
LOGV("Recovering from nonce flood.");
if (i > 2) return false;
sleep(1);
}
return true;
}
class TestCryptoSessionFactory : public CryptoSessionFactory {
CryptoSession* MakeCryptoSession(metrics::CryptoMetrics* crypto_metrics) {
return new TestCryptoSession(crypto_metrics);
}
};
void WvCdmTestBase::SetUp() {
::testing::Test::SetUp();
Properties::set_provisioning_messages_are_binary(binary_provisioning_);
Properties::Init();
// 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::stringstream ss;
ss << test_info->test_case_name() << "." << test_info->name();
int overwrite = 1; // Set value even if already set.
setenv("MODEL_NAME", ss.str().c_str(), overwrite);
CryptoSession::SetCryptoSessionFactory(new TestCryptoSessionFactory());
// TODO(fredgc): Add a test version of DeviceFiles.
}
void WvCdmTestBase::InstallTestRootOfTrust() {
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)));
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;
default:
FAIL() << "Cannot run test without test keybox or RSA key installed.";
}
}
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;
// TODO(fredgc): provision for different SPOIDs.
CdmEngine cdm_engine(&file_system);
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("WvCdmTestBase::Provision: 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());
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok) << http_message;
LOGV("WvCdmTestBase::Provision: 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.
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string protobuf_response;
EXPECT_TRUE(ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&protobuf_response))
<< "Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response message: \n"
"%s\n", protobuf_response.c_str());
// base64 decode response to yield binary protobuf
std::vector<uint8_t> response_vec(Base64SafeDecode(
std::string(protobuf_response.begin(), protobuf_response.end())));
std::string binary_protobuf_response(response_vec.begin(),
response_vec.end());
ASSERT_EQ(NO_ERROR, cdm_engine.HandleProvisioningResponse(
binary_protobuf_response, &cert, &wrapped_key))
<< "message = " << http_message;
} else {
ASSERT_EQ(NO_ERROR, cdm_engine.HandleProvisioningResponse(
http_message, &cert, &wrapped_key))
<< "message = " << http_message;
}
}
// 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;
FileSystem file_system;
CdmEngine cdm_engine(&file_system);
CdmResponseType status =
cdm_engine.OpenSession(config_.key_system(), NULL, NULL, &session_id);
if (status == NEED_PROVISIONING) {
Provision();
status =
cdm_engine.OpenSession(config_.key_system(), NULL, NULL, &session_id);
}
ASSERT_EQ(NO_ERROR, 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, char **argv) {
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;
static const struct option long_options[] = {
{"license_server_id", required_argument, NULL, 'i'},
{"keyid", required_argument, NULL, 'k'},
{"service_certificate", required_argument, NULL, 's'},
{"provisioning_certificate", required_argument, NULL, 'S'},
{"license_server_url", required_argument, NULL, 'u'},
{"provisioning_server_url", required_argument, NULL, 'p'},
{"cast", no_argument, NULL, 'c'},
{"no_filter", no_argument, NULL, 'f'},
{"verbose", no_argument, NULL, 'v'},
{NULL, 0, NULL, '\0'}};
int option_index = 0;
int opt = 0;
int verbosity = 0;
while ((opt = getopt_long(argc, argv, "i:k:s:S:u:p:cfv", long_options,
&option_index)) != -1) {
switch (opt) {
case 'i': {
std::string license_id(optarg);
if (!license_id.compare("gp")) {
default_config_ = ConfigTestEnv(kGooglePlayServer);
} else if (!license_id.compare("cp")) {
default_config_ = ConfigTestEnv(kContentProtectionUatServer);
} else if (!license_id.compare("st")) {
default_config_ = ConfigTestEnv(kContentProtectionStagingServer);
} else {
std::cout << "Invalid license server id" << optarg << std::endl;
show_usage = true;
}
break;
}
case 'k': {
std::string key_id(optarg);
default_config_.set_key_id(key_id);
break;
}
case 's': {
std::string certificate(a2bs_hex(optarg));
default_config_.set_license_service_certificate(certificate);
break;
}
case 'S': {
std::string certificate(a2bs_hex(optarg));
default_config_.set_provisioning_service_certificate(certificate);
break;
}
case 'u': {
std::string server(optarg);
default_config_.set_license_server(server);
break;
}
case 'p': {
std::string server(optarg);
default_config_.set_provisioning_server(server);
break;
}
case 'c': {
is_cast_receiver = true;
break;
}
case 'f': {
filter_tests = false;
break;
}
case 'v': {
++verbosity;
break;
}
case '?': {
show_usage = true;
break;
}
}
}
if (show_usage) {
show_menu(argv[0]);
return false;
}
g_cutoff = static_cast<LogPriority>(verbosity);
// Displays server url, port and key Id being used
std::cout << std::endl;
std::cout << "Default 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);
// 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;
}
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, NULL, NULL, &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);
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;
EXPECT_EQ(KEY_ADDED,
cdm_engine_->AddKey(session_id_, response_data, &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 == NULL) {
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;
}
} // namespace wvcdm

141
core/test/test_base.h Normal file
View File

@@ -0,0 +1,141 @@
// 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.
#ifndef WVCDM_CORE_TEST_BASE_H_
#define WVCDM_CORE_TEST_BASE_H_
#include <gtest/gtest.h>
#include "cdm_engine.h"
#include "config_test_env.h"
#include "crypto_session.h"
#include "metrics_collections.h"
#include "oec_session_util.h"
#include "string_conversions.h"
namespace wvcdm {
// This is the base class for Widevine CDM integration tests. It's main use is
// to configure OEMCrypto to use a test keybox.
class WvCdmTestBase : public ::testing::Test {
public:
WvCdmTestBase() : config_(default_config_), binary_provisioning_(false) {}
virtual ~WvCdmTestBase() {}
virtual void SetUp();
virtual std::string binary_key_id() const { return a2bs_hex(config_.key_id()); }
// Returns true if the test program should continue, if false, the caller
// should exit. This should be called by main() to allow the user to pass in
// command line switches.
static bool Initialize(int argc, char **argv);
// Install a test keybox, if appropriate.
static void InstallTestRootOfTrust();
// Send provisioning request to the server and handle response.
virtual void Provision();
// Calls Provision() if not already provisioned.
virtual void EnsureProvisioned();
// Fill a buffer with some nonconstant data of the given size. The first byte
// will be set to <init> to help you find the buffer when debugging.
static void StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
uint8_t init);
// Helper method for doing cryptography.
static std::string Aes128CbcEncrypt(std::vector<uint8_t> key,
const std::vector<uint8_t>& clear,
const std::vector<uint8_t> iv);
// Helper method for doing cryptography.
static std::string Aes128CbcDecrypt(std::vector<uint8_t> key,
const std::vector<uint8_t>& clear,
const std::vector<uint8_t> iv);
// Helper method for doing cryptography.
static std::string SignHMAC(const std::string& message,
const std::vector<uint8_t>& key);
// The default test configuration. This is influenced by command line
// arguments before any tests are created.
static ConfigTestEnv default_config_;
// Configuration for an individual test. This is initialized to be the
// default configuration, but can be modified by the test itself.
ConfigTestEnv config_;
// This should be set by test subclasses BEFORE calling SetUp -- i.e. in the
// tests's constructor.
bool binary_provisioning_;
};
class TestCryptoSession : public CryptoSession {
public:
explicit TestCryptoSession(metrics::CryptoMetrics* crypto_metrics);
// This intercepts nonce flood errors, which is useful for tests that request
// many nonces and are not time critical.
bool GenerateNonce(uint32_t* nonce);
};
// A holder for a license. Users of this class will first open a session with
// OpenSession, then generate a key request with GenerateKeyRequest, and then
// call CreateDefaultLicense to create a bare-bones license with no keys in it.
// The user may then access the license to adjust the policy, or use AddKey to
// add keys to the license. The license is then loaded via SignAndLoadLicense.
class TestLicenseHolder {
public:
// cdm_engine must exist and outlive the TestLicenseHolder.
TestLicenseHolder(CdmEngine *cdm_engine);
~TestLicenseHolder();
// Caller must ensure device already provisioned.
void OpenSession(const std::string& key_system);
void CloseSession();
// Use the cdm_engine to generate a key request in the session. This should
// be called after OpenSession. This saves the signed license request, so
// that the DRM certificate can be extracted in CreateDefaultLicense.
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string);
// Create a bare-bones license from the license request. After this, the user
// may access and modify the license using license() below.
void CreateDefaultLicense();
// Sign the license using the DRM certificate's RSA key. Then the license is
// passed to the cdm_engine using AddKey. After this, the license is loaded
// and the keys may be used.
void SignAndLoadLicense();
// The session id. This is only valid after a call to OpenSession.
const std::string& session_id() { return session_id_; }
// The license protobuf. This is only valid after CreateDefaultLicense.
video_widevine::License* license() { return &license_; };
// Add a key with the given key control block and key data.
// If the block's verification is empty, it will be set to a valid value.
// The key data is encrypted correctly.
video_widevine::License_KeyContainer* AddKey(
const KeyId& key_id, const std::vector<uint8_t>& key_data,
const wvoec::KeyControlBlock& block);
private:
// Helper method to generate mac keys and encryption keys for the license.
void DeriveKeysFromSessionKey();
// Derive a single mac key or encryption key using CMAC.
bool DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context, int counter,
std::vector<uint8_t>* out);
// Add the mac keys to the license.
void AddMacKey();
CdmEngine* cdm_engine_;
std::string signed_license_request_data_;
std::string license_request_data_;
std::string session_id_;
bool session_opened_;
RsaPublicKey rsa_key_; // From the DRM Certificate.
video_widevine::License license_;
std::vector<uint8_t> derived_mac_key_server_;
std::vector<uint8_t> derived_mac_key_client_;
std::vector<uint8_t> mac_key_server_;
std::vector<uint8_t> mac_key_client_;
std::vector<uint8_t> enc_key_;
std::vector<uint8_t> session_key_;
};
} // namespace wvcdm
#endif // WVCDM_CORE_TEST_BASE_H_

17
core/test/test_main.cpp Normal file
View File

@@ -0,0 +1,17 @@
// 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.
// Use in place of the gtest_main in order to initialize the WvCdmTestBase using
// command line parameters.
#include <stdio.h>
#include "log.h"
#include "test_base.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (!wvcdm::WvCdmTestBase::Initialize(argc, argv)) return 0;
return RUN_ALL_TESTS();
}

View File

@@ -184,7 +184,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case KEY_REQUEST_ERROR_1: *os << "KEY_REQUEST_ERROR_1";
break;
case KEY_SIZE_ERROR: *os << "KEY_SIZE_ERROR";
case KEY_SIZE_ERROR_1: *os << "KEY_SIZE_ERROR_1";
break;
case KEY_SIZE_ERROR_2: *os << "KEY_SIZE_ERROR_2";
break;
case KEYSET_ID_NOT_FOUND_1: *os << "KEYSET_ID_NOT_FOUND_1";
break;
@@ -395,6 +397,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case NO_CONTENT_KEY_2: *os << "NO_CONTENT_KEY_2";
break;
case NO_CONTENT_KEY_3: *os << "NO_CONTENT_KEY_3";
break;
case INSUFFICIENT_CRYPTO_RESOURCES_2:
*os << "INSUFFICIENT_CRYPTO_RESOURCES_2";
break;

File diff suppressed because it is too large Load Diff