Source release v3.2.0
This commit is contained in:
17
cdm/cdm.gyp
17
cdm/cdm.gyp
@@ -1,6 +1,7 @@
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Refer to the distribution package's README for information about
|
||||
# Refer to the distribution package's integration guide
|
||||
# (Widevine_CE_CDM_IntegrationGuide_v3.2.x.pdf) for information about
|
||||
# setting up your system, performing the build, and using/testing
|
||||
# the build targets.
|
||||
|
||||
@@ -57,7 +58,9 @@
|
||||
'target_name': 'license_protocol',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'sources': ['../core/src/license_protocol.proto',],
|
||||
'sources': [
|
||||
'../core/src/license_protocol.proto',
|
||||
],
|
||||
'variables': {
|
||||
'proto_in_dir': '../core/src',
|
||||
},
|
||||
@@ -114,6 +117,7 @@
|
||||
'../core/include/privacy_crypto.h',
|
||||
'../core/include/properties.h',
|
||||
'../core/include/scoped_ptr.h',
|
||||
'../core/include/service_certificate.h',
|
||||
'../core/include/string_conversions.h',
|
||||
'../core/include/wv_cdm_constants.h',
|
||||
'../core/include/wv_cdm_event_listener.h',
|
||||
@@ -131,6 +135,7 @@
|
||||
'../core/src/policy_engine.cpp',
|
||||
'../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
|
||||
'../core/src/properties.cpp',
|
||||
'../core/src/service_certificate.cpp',
|
||||
'../core/src/string_conversions.cpp',
|
||||
'../third_party/jsmn/jsmn.h',
|
||||
'../third_party/jsmn/jsmn.c',
|
||||
@@ -156,10 +161,16 @@
|
||||
}],
|
||||
['oemcrypto_version < 11', {
|
||||
'sources': [
|
||||
# Include APIs introduced in v10.
|
||||
# Include APIs introduced in v11.
|
||||
'../core/src/oemcrypto_adapter_static_v11.cpp',
|
||||
],
|
||||
}],
|
||||
['oemcrypto_version < 12', {
|
||||
'sources': [
|
||||
# Include APIs introduced in v12.
|
||||
'../core/src/oemcrypto_adapter_static_v12.cpp',
|
||||
],
|
||||
}],
|
||||
['privacy_crypto_impl=="openssl"', {
|
||||
'conditions': [
|
||||
['openssl_config == "target"', {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
{
|
||||
'variables': {
|
||||
'oemcrypto_lib%': '',
|
||||
'oemcrypto_stubs%': '',
|
||||
'openssl_config%': 'system',
|
||||
'openssl_target%': '',
|
||||
},
|
||||
@@ -38,13 +39,21 @@
|
||||
'../third_party/gmock.gyp:gtest',
|
||||
],
|
||||
'conditions': [
|
||||
['oemcrypto_lib==""', {
|
||||
['oemcrypto_stubs!=""', {
|
||||
'dependencies': [
|
||||
'../oemcrypto/mock/oec_mock.gyp:oec_mock',
|
||||
'../oemcrypto/stubs/stubs.gyp:oec_stubs_v<(oemcrypto_version)',
|
||||
],
|
||||
}, {
|
||||
'libraries': [
|
||||
'<(oemcrypto_lib)',
|
||||
'conditions': [
|
||||
['oemcrypto_lib==""', {
|
||||
'dependencies': [
|
||||
'../oemcrypto/mock/oec_mock.gyp:oec_mock',
|
||||
],
|
||||
}, {
|
||||
'libraries': [
|
||||
'<(oemcrypto_lib)',
|
||||
],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
],
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef __int64 int64_t;
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Define CDM_EXPORT to export functionality across shared library boundaries.
|
||||
#if defined(WIN32)
|
||||
@@ -217,6 +218,11 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
virtual bool remove(const std::string& name) = 0;
|
||||
virtual int32_t size(const std::string& name) = 0;
|
||||
|
||||
// populates |file_names| with the name of each file in the file system.
|
||||
// This is assumed to be a flat filename space (top level directory is
|
||||
// unnamed, and there are no subdirectories).
|
||||
virtual bool list(std::vector<std::string>* file_names) = 0;
|
||||
|
||||
protected:
|
||||
IStorage() {}
|
||||
virtual ~IStorage() {}
|
||||
@@ -259,7 +265,7 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// Client information, provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
// These parameters end up as client indentification in license requests.
|
||||
// These parameters end up as client identification in license requests.
|
||||
// All fields may be used by a license server proxy to drive business logic.
|
||||
// Some fields are required (indicated below), but please fill out as many
|
||||
// as make sense for your application.
|
||||
@@ -325,16 +331,37 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
|
||||
virtual ~Cdm() {}
|
||||
|
||||
// Provides a server certificate to be used to encrypt messages to the
|
||||
// license server.
|
||||
// If |privacy_mode| was true in create() and setServerCertificate() is not
|
||||
// called, the CDM will attempt to provision a server certificate through
|
||||
// IEventListener::onMessage() with messageType == kLicenseRequest.
|
||||
// May not be called if |privacy_mode| was false.
|
||||
virtual Status setServerCertificate(const std::string& certificate) = 0;
|
||||
// Sets up a service certificate for the CDM. It is used to encrypt
|
||||
// outgoing messages (the ClientIdentification portion of the license and
|
||||
// provisioning requests). It also holds the provider ID setting, used in
|
||||
// the provisioning request.
|
||||
// If setServiceCertificate() is not called, provider ID will not be set
|
||||
// in the provisioning request. If this function is not called and
|
||||
// privacy mode is enabled, a service certificate request will be initiated
|
||||
// as a preliminary step in the license request.
|
||||
virtual Status setServiceCertificate(const std::string& certificate) = 0;
|
||||
|
||||
// Determine if the device has a Device Certificate (for the current origin).
|
||||
// The Device Certificate is origin-specific, and the origin is
|
||||
// dertermined by the CDM's current IStorage object.
|
||||
virtual bool isProvisioned() = 0;
|
||||
|
||||
// Remove the device's Device Certificate (for the current origin).
|
||||
// The Device Certificate is origin-specific, and the origin is
|
||||
// determined by the CDM's current IStorage object.
|
||||
virtual Status removeProvisioning() = 0;
|
||||
|
||||
// Remove the device's usage table.
|
||||
// This calls on OEMCrypto to delete its usage records.
|
||||
virtual Status removeUsageTable() = 0;
|
||||
|
||||
// Get the current list of offline licenses on the system.
|
||||
// License storage is origin-specific, and the origin is determined by the
|
||||
// CDM's current IStorage object.
|
||||
virtual Status listStoredLicenses(std::vector<std::string>* key_set_ids) = 0;
|
||||
|
||||
// Creates a new session.
|
||||
// Do not use this to load an existing persistent session.
|
||||
// Do not use this to load an existing persistent session (use load()).
|
||||
// If successful, the session_id is returned via |sessionId|.
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) = 0;
|
||||
@@ -568,6 +595,15 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) = 0;
|
||||
|
||||
// Enable enforcement of Video Resolution Constraints.
|
||||
// This function should be called during session startup and any time
|
||||
// the video resolution changes.
|
||||
// Video resolution in license policy is a 32-bit value representing pixels.
|
||||
// If the product of width and height is greater than or equal to 1^32,
|
||||
// return kRangeError.
|
||||
virtual Status setVideoResolution(const std::string& session_id,
|
||||
uint32_t width, uint32_t height) = 0;
|
||||
|
||||
protected:
|
||||
Cdm() {}
|
||||
};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Widevine CE CDM Version
|
||||
#define CDM_VERSION "v3.1.0-0-g63dfeca-ce"
|
||||
#define CDM_VERSION "v3.2.0-0-g565f4378-ce"
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
(__GNUC__ == 4 && __GNUC_MINOR__ >= 7) \
|
||||
)
|
||||
|
||||
#if defined(COMPILER_MSVC) || defined(__clang__) || GCC_HAS_OVERRIDE
|
||||
#if !defined(DISABLE_CPP_11) && \
|
||||
(defined(COMPILER_MSVC) || defined(__clang__) || GCC_HAS_OVERRIDE)
|
||||
#define OVERRIDE override
|
||||
#else
|
||||
#define OVERRIDE
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
'defines': [
|
||||
'OEMCRYPTO_TESTS',
|
||||
],
|
||||
'libraries': [
|
||||
'-lcrypto',
|
||||
],
|
||||
}
|
||||
|
||||
182
cdm/src/cdm.cpp
182
cdm/src/cdm.cpp
@@ -17,6 +17,7 @@
|
||||
#include "license.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
@@ -114,7 +115,16 @@ class CdmImpl : public Cdm,
|
||||
virtual ~CdmImpl();
|
||||
|
||||
// Cdm:
|
||||
virtual Status setServerCertificate(const std::string& certificate) OVERRIDE;
|
||||
virtual Status setServiceCertificate(const std::string& certificate) OVERRIDE;
|
||||
|
||||
virtual bool isProvisioned() OVERRIDE;
|
||||
|
||||
virtual Status removeProvisioning() OVERRIDE;
|
||||
|
||||
virtual Status removeUsageTable() OVERRIDE;
|
||||
|
||||
virtual Status listStoredLicenses(
|
||||
std::vector<std::string>* key_set_ids) OVERRIDE;
|
||||
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) OVERRIDE;
|
||||
@@ -177,6 +187,8 @@ class CdmImpl : public Cdm,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) OVERRIDE;
|
||||
|
||||
virtual Status setVideoResolution(const std::string& session_id,
|
||||
uint32_t width, uint32_t height) OVERRIDE;
|
||||
// ITimerClient:
|
||||
virtual void onTimerExpired(void* context) OVERRIDE;
|
||||
|
||||
@@ -251,29 +263,55 @@ CdmImpl::~CdmImpl() {
|
||||
host.timer->cancel(this);
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
|
||||
if (!property_set_.use_privacy_mode()) {
|
||||
LOGE("Cannot set server certificate if privacy mode is disabled.");
|
||||
return kNotSupported;
|
||||
}
|
||||
Cdm::Status CdmImpl::setServiceCertificate(const std::string& certificate) {
|
||||
|
||||
if (certificate.empty()) {
|
||||
LOGE("An empty server certificate is invalid.");
|
||||
return kTypeError;
|
||||
LOGW("An empty licensing service certificate may be invalid.");
|
||||
}
|
||||
|
||||
if (CdmLicense::VerifySignedServiceCertificate(certificate) != NO_ERROR) {
|
||||
LOGE("Invalid server certificate!");
|
||||
// Check for properly signed and well-formed certificate.
|
||||
// Keep results. NOTE: an empty certificate should be accepted here.
|
||||
CdmResponseType status = cdm_engine_.SetServiceCertificate(certificate);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Invalid service certificate! Error code = %d", status);
|
||||
return kTypeError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
property_set_.set_service_certificate(certificate);
|
||||
bool CdmImpl::isProvisioned() {
|
||||
return cdm_engine_.IsProvisioned(kSecurityLevelL1);
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::removeProvisioning() {
|
||||
if (cdm_engine_.Unprovision(kSecurityLevelL1) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::removeUsageTable() {
|
||||
if (cdm_engine_.DeleteUsageTable(kSecurityLevelL1) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
|
||||
if (key_set_ids == NULL) {
|
||||
LOGE("Missing vector parameter to receive key_set_ids.");
|
||||
return kTypeError;
|
||||
}
|
||||
if (cdm_engine_.ListStoredLicenses(kSecurityLevelL1, key_set_ids) !=
|
||||
NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
std::string* session_id) {
|
||||
if (NULL == session_id) {
|
||||
if (session_id == NULL) {
|
||||
LOGE("Missing session ID pointer.");
|
||||
return kTypeError;
|
||||
}
|
||||
@@ -420,21 +458,8 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
sessions_[session_id].callable = true;
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
|
||||
MessageType message_type;
|
||||
if (property_set_.use_privacy_mode() &&
|
||||
property_set_.service_certificate().empty()) {
|
||||
// We can deduce that this is a server cert request, even though CdmEgine
|
||||
// cannot currently inform us of this.
|
||||
// Previously, we used message type kIndividiualizationRequest for this.
|
||||
// The EME editor has clarified that this was a misinterpretation of the
|
||||
// spec, and that this should also be kLicenseRequest.
|
||||
LOGI("A server certificate request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
} else {
|
||||
LOGI("A license request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
}
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
LOGI("A license request has been generated.");
|
||||
listener_->onMessage(session_id, kLicenseRequest, key_request.message);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
@@ -449,8 +474,9 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, session_id, this);
|
||||
CdmResponseType result = cdm_engine_.OpenSession("com.widevine.alpha",
|
||||
&property_set_, session_id,
|
||||
this);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
break;
|
||||
@@ -504,6 +530,13 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
if (!policy_timer_enabled_) {
|
||||
policy_timer_enabled_ = true;
|
||||
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
|
||||
this,
|
||||
kPolicyTimerContext);
|
||||
}
|
||||
|
||||
sessions_[session_id].type = kPersistentLicense;
|
||||
sessions_[session_id].callable = true;
|
||||
return kSuccess;
|
||||
@@ -590,34 +623,10 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
CdmResponseType result =
|
||||
cdm_engine_.AddKey(session_id, response, &key_set_id);
|
||||
|
||||
if (result == NEED_KEY) {
|
||||
// We just provisioned a server certificate.
|
||||
assert(property_set_.use_privacy_mode());
|
||||
|
||||
// The cert is now available to all sessions in this CDM instance.
|
||||
// This is consistent with the behavior of the Chrome CDM.
|
||||
assert(!property_set_.service_certificate().empty());
|
||||
|
||||
// The underlying session in CdmEngine has stored a copy of the original
|
||||
// init data, so we can use an empty one this time.
|
||||
InitializationData empty_init_data;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
|
||||
app_parameters_, &key_request);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A deferred license request has been generated.");
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
MessageType message_type = kLicenseRequest;
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
return kSuccess;
|
||||
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
|
||||
// result should only be NEED_KEY after server certificate provisioning, which
|
||||
// should no longer happen in this version of the CDM.
|
||||
assert(result != NEED_KEY);
|
||||
if (result == OFFLINE_LICENSE_PROHIBITED) {
|
||||
LOGE("A temporary session cannot be used for a persistent license.");
|
||||
return kRangeError;
|
||||
} else if (result == STORAGE_PROHIBITED) {
|
||||
@@ -986,6 +995,22 @@ Cdm::Status CdmImpl::genericVerify(
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::setVideoResolution(const std::string& session_id,
|
||||
uint32_t width, uint32_t height) {
|
||||
// Verify that width * height will fit into a 32-bit quantity.
|
||||
// This is done to be compatible with the video resolution in the
|
||||
// license policy settings.
|
||||
uint64_t pixels = width;
|
||||
pixels *= height;
|
||||
if (pixels >= (1ULL << 32))
|
||||
return kRangeError;
|
||||
if (cdm_engine_.NotifyResolution(session_id, width, height)) {
|
||||
return kSuccess;
|
||||
} else {
|
||||
return kSessionNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
void CdmImpl::onTimerExpired(void* context) {
|
||||
if (context == kPolicyTimerContext) {
|
||||
if (policy_timer_enabled_) {
|
||||
@@ -1127,16 +1152,20 @@ Cdm::Status Cdm::initialize(
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
LogLevel verbosity) {
|
||||
// If you want to direct-render on L3, CryptoSession will pass that request
|
||||
// along to OEMCrypto. But if you want to use an opaque handle on L3,
|
||||
// CryptoSession will silently ignore you and tell OEMCrypto to treat the
|
||||
// address as a clear buffer. :-(
|
||||
|
||||
// Specify the maximum severity of message that will be output to
|
||||
// the console. See core/include/log.h for the valid priority values.
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
// If you want to direct-render on L3, CryptoSession will pass that
|
||||
// request along to OEMCrypto. But if you want to use an opaque
|
||||
// handle on L3, CryptoSession will silently ignore you and tell
|
||||
// OEMCrypto to treat the address as a clear buffer.
|
||||
//
|
||||
// So this logic mirrors that in CryptoSession. Effectively, we are
|
||||
// detecting at init time the conditions that would prevent CryptoSession (in
|
||||
// its current form) from passing the desired buffer type constant to
|
||||
// OEMCrypto.
|
||||
// TODO: Discuss changes to CryptoSession.
|
||||
// So this logic mirrors that in CryptoSession. Effectively, we
|
||||
// are detecting at init time the conditions that would prevent
|
||||
// CryptoSession (in its current form) from passing the desired
|
||||
// buffer type constant to OEMCrypto.
|
||||
switch (secure_output_type) {
|
||||
case kOpaqueHandle:
|
||||
// This output type requires an OEMCrypto that reports L1.
|
||||
@@ -1166,9 +1195,6 @@ Cdm::Status Cdm::initialize(
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
// Our enum values match those in core/include/log.h
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
PropertiesCE::SetSecureOutputType(secure_output_type);
|
||||
PropertiesCE::SetClientInfo(client_info);
|
||||
Properties::Init();
|
||||
@@ -1267,14 +1293,14 @@ class FileSystem::Impl {
|
||||
widevine::Cdm::IStorage* storage;
|
||||
};
|
||||
|
||||
FileSystem::FileSystem() : FileSystem("", NULL) {}
|
||||
FileSystem::FileSystem() : impl_(new Impl) {
|
||||
impl_->storage = host.storage;
|
||||
}
|
||||
|
||||
FileSystem::FileSystem(const std::string& origin, void* extra_data)
|
||||
: impl_(new Impl), origin_(origin) {
|
||||
if (extra_data)
|
||||
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
|
||||
else
|
||||
impl_->storage = host.storage;
|
||||
assert(NULL != extra_data);
|
||||
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem() {
|
||||
@@ -1316,4 +1342,12 @@ ssize_t FileSystem::FileSize(const std::string& file_path) {
|
||||
return impl_->storage->size(file_path);
|
||||
}
|
||||
|
||||
bool FileSystem::List(const std::string&,
|
||||
std::vector<std::string>* file_names) {
|
||||
if (!impl_ || !file_names)
|
||||
return false;
|
||||
|
||||
return impl_->storage->list(file_names);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -143,4 +143,9 @@ bool Properties::AlwaysUseKeySetIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::UseProviderIdInProvisioningRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "cdm.h"
|
||||
#include "cdm_test_printers.h"
|
||||
#include "decryption_test_data.h"
|
||||
#include "default_service_certificate.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
@@ -29,33 +31,12 @@ const int kHttpOk = 200;
|
||||
|
||||
const int kRenewalTestDelayMs = 3 * 60 * 1000;
|
||||
const int kExpirationTestDelayMs = 5 * 60 * 1000;
|
||||
const int kOfflineLicenseDurationMs = 604800 * 1000;
|
||||
|
||||
const Cdm::SessionType kBogusSessionType = static_cast<Cdm::SessionType>(-1);
|
||||
const Cdm::InitDataType kBogusInitDataType = static_cast<Cdm::InitDataType>(-1);
|
||||
const std::string kBogusSessionId = "asdf";
|
||||
|
||||
const std::string kDefaultServerCertificate = a2bs_hex(
|
||||
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010A02"
|
||||
"82010100B52112B8D05D023FCC5D95E2C251C1C649B4177CD8D2BEEF355BB06743DE661E3D"
|
||||
"2ABC3182B79946D55FDC08DFE95407815E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94"
|
||||
"B2516F075B66EF811D0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1E"
|
||||
"F9B6AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A040C50B09BB"
|
||||
"C740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A0E498CC01F00532BAC217850"
|
||||
"BD905E90923656B7DFEFEF42486767F33EF6283D4F4254AB72589390BEE55808F1D668080D"
|
||||
"45D893C2BCA2F74D60A0C0D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD9"
|
||||
"4302030100013A1273746167696E672E676F6F676C652E636F6D128003983E30352675F40B"
|
||||
"A715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AAEFC5E27BC980DAEADABF"
|
||||
"3FC386D084A02C82537848CC753FF497B011A7DA97788A00E2AA6B84CD7D71C07A48EBF616"
|
||||
"02CCA5A3F32030A7295C30DA915B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE"
|
||||
"18FA82E81BB032630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0EF"
|
||||
"D45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F288F0D9D45960E259E"
|
||||
"85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F869321F6ADE18905F4D92F9A6DA6536DB84"
|
||||
"75871D168E870BB2303CF70C6E9784C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D25"
|
||||
"92C72429F8C01742BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F39"
|
||||
"40383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D838540F8A0C22"
|
||||
"7C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A250A4EB9C84AB3E6539F6B6FDF"
|
||||
"56899EA29914");
|
||||
|
||||
const std::string kProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
@@ -123,98 +104,6 @@ const Cdm::Pattern kPatternRecommended(1, 9);
|
||||
// decryption by having one encrypted block and no clear blocks.
|
||||
const Cdm::Pattern kPatternHlsAudio(1, 0);
|
||||
|
||||
// Dummy encrypted data using the CENC 3.0 "cenc" mode. Encrypted using the
|
||||
// key matching kKeyIdCtr.
|
||||
const std::vector<uint8_t> kInputCenc = a2b_hex(
|
||||
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
||||
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
||||
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
||||
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
||||
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
||||
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
||||
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
||||
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
|
||||
const std::vector<uint8_t> kIvCenc = a2b_hex(
|
||||
"f6f4b1e600a5b67813ed2bded913ba9f");
|
||||
const std::vector<uint8_t> kOutputCenc = a2b_hex(
|
||||
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
||||
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
||||
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
||||
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
||||
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
||||
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
||||
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
||||
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659");
|
||||
|
||||
// Dummy encrypted data using the CENC 3.0 "cens" mode. Encrypted using the
|
||||
// key matching kKeyIdCtr.
|
||||
const std::vector<uint8_t> kInputCens = a2b_hex(
|
||||
"1660a777a301908b5e8c15b465ed7fa434793f65a8be816278f9479d741a78e0"
|
||||
"b245e17629d63bbc2b15a5fa98b21daf62bdaf054113604ef19311adc5c3b74c"
|
||||
"6167dc3160f27c4920d2f9ae4a7f8dfd029dde48bce29b2751f27f12503d369d"
|
||||
"0ceb8b347e2884f51715f612badf15934aaa39db886e749afb8d8bdd29a18dd6"
|
||||
"2b0c4355935c4dcc5ec0153307154ace5bfedcdaa2b670052660889f3d64c4b3"
|
||||
"e363b16dc312d7e20373e873c760fae8b8bb39eccb6fe16e0198f6818ba24c30"
|
||||
"39dec55ef91ddc47c320ec284e24d1c8cdd62515e8ce5c0cb01bea2fbf36ce99"
|
||||
"246f5f8a2aca37719524dadffd4926a75a06402779a945d0b2c14a9c3f060a34");
|
||||
const std::vector<uint8_t> kIvCens = a2b_hex(
|
||||
"a891b8000af53049d7b24bdc19074839");
|
||||
const std::vector<uint8_t> kOutputCens = a2b_hex(
|
||||
"4bc4abcd79205e54188f04f99ea7e02534793f65a8be816278f9479d741a78e0"
|
||||
"b245e17629d63bbc2b15a5fa98b21daf62bdaf054113604ef19311adc5c3b74c"
|
||||
"6167dc3160f27c4920d2f9ae4a7f8dfd029dde48bce29b2751f27f12503d369d"
|
||||
"0ceb8b347e2884f51715f612badf15934aaa39db886e749afb8d8bdd29a18dd6"
|
||||
"2b0c4355935c4dcc5ec0153307154ace5bfedcdaa2b670052660889f3d64c4b3"
|
||||
"f6104e15275ecb58324fb8f25ccde60db8bb39eccb6fe16e0198f6818ba24c30"
|
||||
"39dec55ef91ddc47c320ec284e24d1c8cdd62515e8ce5c0cb01bea2fbf36ce99"
|
||||
"246f5f8a2aca37719524dadffd4926a75a06402779a945d0b2c14a9c3f060a34");
|
||||
|
||||
// Dummy encrypted data using the CENC 3.0 "cbc1" mode. Encrypted using the
|
||||
// key matching kKeyIdCbc.
|
||||
const std::vector<uint8_t> kInputCbc1 = a2b_hex(
|
||||
"a69c76294ccd7e709fc3d11b1e0ccd4a74c9ffa8ce31ab92437c4da03b85822d"
|
||||
"6f0da6d7935121cd585950ecc61efc83d2d86be9c32b3091cf546de987d9b480"
|
||||
"fae8b8c35222f6fb7e2939b1af4c1445b6bd3ac22aeafc06ec016b011d465bf0"
|
||||
"9d9a3a18865518bca314b1208830f0a18e6922b1d0a451df8f2c09efb416ca1d"
|
||||
"0bdf93a7610f40da65fd23fc65531bb01373a85658043ed238e79d2b3f3c49e7"
|
||||
"842ea0488a862932850153849f5ac20ce8181594240d16bb309d7523ffb9a7f0"
|
||||
"edd976a6dcb0c90bf6895dad90b8f373b22162c397b0d0e3e49041dce4f7a34f"
|
||||
"1dbe1e2c0f3f6be9d5bbc3e783743a70df89bf488de8dd97106c7fb9fdbbf662");
|
||||
const std::vector<uint8_t> kIvCbc1 = a2b_hex(
|
||||
"0111321322793b04f871aab28f6b066e");
|
||||
const std::vector<uint8_t> kOutputCbc1 = a2b_hex(
|
||||
"d5c7a71abfbfa2b490916d0e316c7b7e928b2cdaf9768b682b98f4087d664faa"
|
||||
"c8f05bd97fede1c678dc4320df4ac65674ad63370616df3ee85acc145b4bc7a8"
|
||||
"9169214197489350faa658ddff36959cf8dc2328bca5b1ccf26da4e1ce717595"
|
||||
"a11ddf354a9811890afbb2207e90367bf007df42d99c682e6024cf7671273523"
|
||||
"06d3e68a0fa2914640842759911bfdf90be7fc84742031989bb0b676d93a1904"
|
||||
"4ba6811a032ddafd9e2d2caa44ec17363794b661d2460aa4517b1e349f0eeb23"
|
||||
"9c2e83d31584f56a31b1688f89a4c64917e0037ae6aa7e483cd641dec38c3aba"
|
||||
"195ca7942df98c124d4be96524edbda671aab2a52a2305637101f274e031bbc7");
|
||||
|
||||
// Dummy encrypted data using the CENC 3.0 "cbcs" mode. Encrypted using the
|
||||
// key matching kKeyIdCbc.
|
||||
const std::vector<uint8_t> kInputCbcs = a2b_hex(
|
||||
"7d8665445b3ac25fda29054e81626ed89f528f87315bdb07ba7fdad32835808f"
|
||||
"6458893d28a247c37ec56c48f89ba6f941757edf22fea1b69980833746526dba"
|
||||
"a8e9193125a4ac73893df76784aeb954124c59aa9771e5e5f91478ed720d2cfe"
|
||||
"7421c5f7acbdfb762e75da0b48ba3f7bbf1dda3e8ff8ad1b625622438c100bd7"
|
||||
"a063a0518b21bcbc6ebc2809478ebfd612e3400b515018b64fc1ba23c5b38b6d"
|
||||
"45aaa4c0c394c3b75066390b646bf273fc4913f7e20b8c856d20561337fd4383"
|
||||
"86938f189e0354ee9425974fc7153e61e006c3b472f21e2f3e33a1c94435d747"
|
||||
"da153cd7f0cb8a7e4d1ee43acbf51027604cfe93808b42a2b6f30b4667d056e4");
|
||||
const std::vector<uint8_t> kIvCbcs = a2b_hex(
|
||||
"8e261c9660f5930ebe1734510cb9bc23");
|
||||
const std::vector<uint8_t> kOutputCbcs = a2b_hex(
|
||||
"bf7d1e9edc64a1782e884870edde98399f528f87315bdb07ba7fdad32835808f"
|
||||
"6458893d28a247c37ec56c48f89ba6f941757edf22fea1b69980833746526dba"
|
||||
"a8e9193125a4ac73893df76784aeb954124c59aa9771e5e5f91478ed720d2cfe"
|
||||
"7421c5f7acbdfb762e75da0b48ba3f7bbf1dda3e8ff8ad1b625622438c100bd7"
|
||||
"a063a0518b21bcbc6ebc2809478ebfd612e3400b515018b64fc1ba23c5b38b6d"
|
||||
"d48cfbfc9e08ff501c62d5e85200dab0fc4913f7e20b8c856d20561337fd4383"
|
||||
"86938f189e0354ee9425974fc7153e61e006c3b472f21e2f3e33a1c94435d747"
|
||||
"da153cd7f0cb8a7e4d1ee43acbf51027604cfe93808b42a2b6f30b4667d056e4");
|
||||
|
||||
const std::string kValue = "A Value";
|
||||
const std::string kNewValue = "A New Value";
|
||||
|
||||
@@ -264,7 +153,6 @@ class CdmTest : public Test, public Cdm::IEventListener {
|
||||
|
||||
// Make a fresh CDM.
|
||||
RecreateCdm(true /* privacy_mode */);
|
||||
SetDefaultServerCertificate();
|
||||
}
|
||||
|
||||
virtual void TearDown() OVERRIDE {
|
||||
@@ -276,6 +164,7 @@ class CdmTest : public Test, public Cdm::IEventListener {
|
||||
|
||||
void RecreateCdm(bool privacy_mode) {
|
||||
CreateAdditionalCdm(privacy_mode, &cdm_);
|
||||
cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
}
|
||||
|
||||
void CreateAdditionalCdm(bool privacy_mode, scoped_ptr<Cdm>* cdm) {
|
||||
@@ -283,12 +172,6 @@ class CdmTest : public Test, public Cdm::IEventListener {
|
||||
ASSERT_NE((Cdm*)0, cdm->get());
|
||||
}
|
||||
|
||||
void SetDefaultServerCertificate() {
|
||||
// Set the default server certificate.
|
||||
Cdm::Status status = cdm_->setServerCertificate(kDefaultServerCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
bool Fetch(const std::string& url,
|
||||
const std::string& message,
|
||||
std::string* response,
|
||||
@@ -354,7 +237,11 @@ class CdmTest : public Test, public Cdm::IEventListener {
|
||||
Cdm::InitDataType init_data_type,
|
||||
std::string* session_id,
|
||||
std::string* message) {
|
||||
Cdm::Status status = cdm_->createSession(session_type, session_id);
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
status = cdm_->createSession(session_type, session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
std::string init_data;
|
||||
@@ -365,7 +252,7 @@ class CdmTest : public Test, public Cdm::IEventListener {
|
||||
init_data = kHlsInitData;
|
||||
}
|
||||
} else if (session_type == Cdm::kPersistentLicense ||
|
||||
session_type == Cdm::kPersistentUsageRecord) {
|
||||
session_type == Cdm::kPersistentUsageRecord) {
|
||||
if (init_data_type == Cdm::kCenc) {
|
||||
init_data = kCencPersistentInitData;
|
||||
}
|
||||
@@ -578,23 +465,23 @@ TEST_F(CdmTest, Initialize) {
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, SetServerCertificate) {
|
||||
// Can't set a server certificate if privacy mode is disabled.
|
||||
TEST_F(CdmTest, SetServiceCertificate) {
|
||||
// Set a server certificate with privacy mode disabled - should work.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(false /* privacy_mode */));
|
||||
Cdm::Status status = cdm_->setServerCertificate(kDefaultServerCertificate);
|
||||
EXPECT_EQ(Cdm::kNotSupported, status);
|
||||
Cdm::Status status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Can set a server certificate if privacy mode is enabled.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
status = cdm_->setServerCertificate(kDefaultServerCertificate);
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// It is invalid to set an empty cert.
|
||||
status = cdm_->setServerCertificate("");
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
// Setting an empty cert is allowed.
|
||||
status = cdm_->setServiceCertificate("");
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// It is invalid to set a malformed cert.
|
||||
status = cdm_->setServerCertificate("asdf");
|
||||
status = cdm_->setServiceCertificate("asdf");
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
}
|
||||
|
||||
@@ -627,7 +514,11 @@ TEST_F(CdmTest, CreateSession) {
|
||||
|
||||
TEST_F(CdmTest, GenerateRequest) {
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Generate a license request for CENC.
|
||||
@@ -716,6 +607,10 @@ TEST_F(CdmTest, GenerateRequest) {
|
||||
TEST_F(CdmTest, Update) {
|
||||
std::string session_id;
|
||||
std::string message;
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndGenerateRequest(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id, &message));
|
||||
|
||||
@@ -726,7 +621,7 @@ TEST_F(CdmTest, Update) {
|
||||
|
||||
// Update the session.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
Cdm::Status status = cdm_->update(session_id, response);
|
||||
status = cdm_->update(session_id, response);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
@@ -773,12 +668,16 @@ TEST_F(CdmTest, Close) {
|
||||
TEST_F(CdmTest, LoadTemporary) {
|
||||
std::string session_id;
|
||||
std::string response;
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id, &response));
|
||||
|
||||
// Update the temporary session.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
Cdm::Status status = cdm_->update(session_id, response);
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
@@ -794,12 +693,14 @@ TEST_F(CdmTest, LoadTemporary) {
|
||||
TEST_F(CdmTest, LoadPersistent) {
|
||||
std::string session_id;
|
||||
std::string response;
|
||||
Cdm::Status status;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense(
|
||||
Cdm::kPersistentLicense, Cdm::kCenc, &session_id, &response));
|
||||
|
||||
// Update the persistent session.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
Cdm::Status status = cdm_->update(session_id, response);
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
@@ -813,7 +714,6 @@ TEST_F(CdmTest, LoadPersistent) {
|
||||
|
||||
// Should be able to load the session again after recreating the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
ASSERT_NO_FATAL_FAILURE(SetDefaultServerCertificate());
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
status = cdm_->load(session_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
@@ -829,6 +729,27 @@ TEST_F(CdmTest, LoadPersistent) {
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, LoadWillFireExpiration) {
|
||||
// There was a bug where calling load() would not start the PolicyEngine timer
|
||||
// because it was only started in update().
|
||||
std::string session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(
|
||||
Cdm::kPersistentLicense, Cdm::kCenc, &session_id));
|
||||
|
||||
// Should be able to load the session again after recreating the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
ASSERT_EQ(Cdm::kSuccess, cdm_->load(session_id));
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Let the key expire, make sure we get a key status update.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRenewal, _))
|
||||
.Times(AtLeast(1));
|
||||
g_host->ElapseTime(kOfflineLicenseDurationMs);
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, PerOriginLoadPersistent) {
|
||||
std::string session_id;
|
||||
std::string response;
|
||||
@@ -843,7 +764,6 @@ TEST_F(CdmTest, PerOriginLoadPersistent) {
|
||||
|
||||
// Should be able to load the session again after recreating the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
ASSERT_NO_FATAL_FAILURE(SetDefaultServerCertificate());
|
||||
status = cdm_->load(session_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
@@ -852,7 +772,7 @@ TEST_F(CdmTest, PerOriginLoadPersistent) {
|
||||
scoped_ptr<Cdm> other_cdm(
|
||||
Cdm::create(this, &other_host, /* privacy_mode */ true));
|
||||
ASSERT_TRUE(other_cdm.get());
|
||||
status = other_cdm->setServerCertificate(kDefaultServerCertificate);
|
||||
status = other_cdm->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Should not be able to load from another origin.
|
||||
@@ -887,7 +807,6 @@ TEST_F(CdmTest, LoadUsageRecord) {
|
||||
|
||||
// Should be able to load the session again after recreating the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
ASSERT_NO_FATAL_FAILURE(SetDefaultServerCertificate());
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(0);
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRelease, _)).Times(0);
|
||||
status = cdm_->load(session_id);
|
||||
@@ -1092,7 +1011,6 @@ TEST_F(CdmTest, RemoveIncomplete) {
|
||||
|
||||
// Recreate the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
ASSERT_NO_FATAL_FAILURE(SetDefaultServerCertificate());
|
||||
|
||||
// Load the partially removed session.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(0);
|
||||
@@ -1133,6 +1051,14 @@ TEST_F(CdmTest, RemoveIncomplete) {
|
||||
ASSERT_EQ(Cdm::kSessionNotFound, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, RemoveUsageTable) {
|
||||
Cdm::Status status;
|
||||
status = cdm_->removeUsageTable();
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
status = cdm_->removeUsageTable();
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, RemoveUsageRecordIncomplete) {
|
||||
std::string session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(
|
||||
@@ -1156,7 +1082,6 @@ TEST_F(CdmTest, RemoveUsageRecordIncomplete) {
|
||||
|
||||
// Recreate the CDM.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
ASSERT_NO_FATAL_FAILURE(SetDefaultServerCertificate());
|
||||
|
||||
// Load the partially removed session.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(0);
|
||||
@@ -1215,8 +1140,10 @@ TEST_F(CdmTest, RequestPersistentLicenseWithWrongInitData) {
|
||||
// Generate a request for a persistent license without using the correct
|
||||
// persistent content init data.
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kPersistentLicense,
|
||||
&session_id);
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
status = cdm_->createSession(Cdm::kPersistentLicense, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
std::string message;
|
||||
@@ -1233,7 +1160,10 @@ TEST_F(CdmTest, RequestPersistentLicenseWithWrongInitData) {
|
||||
TEST_F(CdmTest, RequestTemporaryLicenseWithWrongInitData) {
|
||||
// Generate a request for a temporary license using persistent init data.
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
std::string message;
|
||||
@@ -1287,138 +1217,6 @@ TEST_F(CdmTest, Renewal) {
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, ServerCertificateProvisioning) {
|
||||
// Do not set a server cert. Provisioning will be required.
|
||||
ASSERT_NO_FATAL_FAILURE(RecreateCdm(true /* privacy_mode */));
|
||||
|
||||
// Create a session.
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Expect a license request type message, but this is actually a server cert
|
||||
// provisioning request.
|
||||
std::string message;
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _))
|
||||
.WillOnce(SaveArg<2>(&message));
|
||||
status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Relay it to the server.
|
||||
std::string response;
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &response));
|
||||
|
||||
// No keys will change, since this wasn't a license.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(0);
|
||||
// We should get another license request generated during update.
|
||||
message.clear();
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).WillOnce(
|
||||
SaveArg<2>(&message));
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// There are no keys yet.
|
||||
Cdm::KeyStatusMap map;
|
||||
status = cdm_->getKeyStatuses(session_id, &map);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
ASSERT_TRUE(map.empty());
|
||||
|
||||
// Relay the license request to the server.
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &response));
|
||||
|
||||
// Update the session. The keys will change now.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(1);
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// The keys should be usable.
|
||||
status = cdm_->getKeyStatuses(session_id, &map);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
ASSERT_FALSE(map.empty());
|
||||
EXPECT_EQ(Cdm::kUsable, map.begin()->second);
|
||||
|
||||
|
||||
// Create another session. This one should not require server certificate
|
||||
// provisioning.
|
||||
status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Expect a license request.
|
||||
message.clear();
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).WillOnce(
|
||||
SaveArg<2>(&message));
|
||||
status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Relay it to the server.
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &response));
|
||||
|
||||
// Keys will change, since this was an actual license.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(1);
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
|
||||
// Create a second CDM instance.
|
||||
scoped_ptr<Cdm> cdm2;
|
||||
CreateAdditionalCdm(true /* privacy_mode */, &cdm2);
|
||||
|
||||
// Create a session on the second CDM instance. This one should require
|
||||
// provisioning, since provisioned certs should not be shared across CDM
|
||||
// instances.
|
||||
status = cdm2->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
message.clear();
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _))
|
||||
.WillOnce(SaveArg<2>(&message));
|
||||
status = cdm2->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Relay it to the server.
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &response));
|
||||
|
||||
// No keys will change, since this wasn't a license.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(0);
|
||||
// We should get another license request generated during update.
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _));
|
||||
status = cdm2->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
|
||||
// Create another session on the first CDM. This one should not require
|
||||
// server certificate provisioning. This proves that the creation of the
|
||||
// second CDM instance did not affect the state of the first.
|
||||
status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
message.clear();
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).WillOnce(
|
||||
SaveArg<2>(&message));
|
||||
status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Relay it to the server.
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &response));
|
||||
|
||||
// Keys will change, since this was an actual license.
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id)).Times(1);
|
||||
status = cdm_->update(session_id, response);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, SetAppParameters) {
|
||||
// Must use privacy_mode = false to ensure that the message is in plain-text.
|
||||
std::string session_id;
|
||||
@@ -1499,6 +1297,41 @@ TEST_F(CdmTest, SetAppParameters) {
|
||||
ASSERT_EQ(Cdm::kTypeError, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, SetVideoResolutionBadSession) {
|
||||
std::string session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id));
|
||||
|
||||
Cdm::Status status = cdm_->setVideoResolution(kBogusSessionId, 100, 100);
|
||||
ASSERT_EQ(Cdm::kSessionNotFound, status);
|
||||
|
||||
cdm_->close(session_id);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, SetVideoResolutionOK) {
|
||||
std::string session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id));
|
||||
|
||||
Cdm::Status status = cdm_->setVideoResolution(session_id, 100, 100);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
cdm_->close(session_id);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, SetVideoResolutionOverflow) {
|
||||
std::string session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id));
|
||||
|
||||
const uint32_t kUintTooLarge = (1 << 16);
|
||||
Cdm::Status status = cdm_->setVideoResolution(session_id, kUintTooLarge,
|
||||
kUintTooLarge);
|
||||
ASSERT_EQ(Cdm::kRangeError, status);
|
||||
|
||||
cdm_->close(session_id);
|
||||
}
|
||||
|
||||
TEST_P(CdmTestWithDecryptParam, DecryptToClearBuffer) {
|
||||
DecryptParam param = GetParam();
|
||||
|
||||
@@ -1536,19 +1369,19 @@ TEST_P(CdmTestWithDecryptParam, DecryptToClearBuffer) {
|
||||
INSTANTIATE_TEST_CASE_P(CdmDecryptTest, CdmTestWithDecryptParam, Values(
|
||||
DecryptParam("CENC 3.0 cenc Mode",
|
||||
Cdm::kCenc, kKeyIdCtr, kIvCenc, Cdm::kAesCtr,
|
||||
kPatternNone, kInputCenc, kOutputCenc),
|
||||
kPatternNone, kInput, kOutputCenc),
|
||||
DecryptParam("CENC 3.0 cens Mode",
|
||||
Cdm::kCenc, kKeyIdCtr, kIvCens, Cdm::kAesCtr,
|
||||
kPatternRecommended, kInputCens, kOutputCens),
|
||||
kPatternRecommended, kInput, kOutputCens),
|
||||
DecryptParam("CENC 3.0 cbc1 Mode",
|
||||
Cdm::kHls, kKeyIdCbc, kIvCbc1, Cdm::kAesCbc,
|
||||
kPatternNone, kInputCbc1, kOutputCbc1),
|
||||
kPatternNone, kInput, kOutputCbc1),
|
||||
DecryptParam("CENC 3.0 cbcs Mode",
|
||||
Cdm::kHls, kKeyIdCbc, kIvCbcs, Cdm::kAesCbc,
|
||||
kPatternRecommended, kInputCbcs, kOutputCbcs),
|
||||
kPatternRecommended, kInput, kOutputCbcs),
|
||||
DecryptParam("HLS Audio (CENC 3.0 cbcs Mode Without a Pattern)",
|
||||
Cdm::kHls, kKeyIdCbc, kIvCbc1, Cdm::kAesCbc,
|
||||
kPatternHlsAudio, kInputCbc1, kOutputCbc1)
|
||||
kPatternHlsAudio, kInput, kOutputCbc1)
|
||||
));
|
||||
|
||||
// TODO: add infrastructure to test secure buffer decrypt for some platforms
|
||||
@@ -1590,6 +1423,52 @@ TEST_F(CdmIndividualizationTest, BasicFlow) {
|
||||
// Clear any existing certificates.
|
||||
g_host->remove("cert.bin");
|
||||
|
||||
// Creating a session should succeed.
|
||||
std::string session_id;
|
||||
Cdm::Status status;
|
||||
status = cdm_->setServiceCertificate(kDefaultServiceCertificate);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Should get an individualization request when we generate request.
|
||||
std::string message;
|
||||
EXPECT_CALL(*this, onDirectIndividualizationRequest(session_id, _))
|
||||
.WillOnce(SaveArg<1>(&message));
|
||||
status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Complete the provisioning request.
|
||||
std::string reply = GetProvisioningResponse(message);
|
||||
ASSERT_FALSE(reply.empty());
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).Times(1);
|
||||
EXPECT_CALL(*this, onDeferredComplete(_, _)).Times(0);
|
||||
status = cdm_->update(session_id, reply);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// We should now be able to create a session and generate a request.
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSessionAndGenerateRequest(
|
||||
Cdm::kTemporary, Cdm::kCenc, &session_id, &message));
|
||||
|
||||
// Acquire a license and update the session.
|
||||
ASSERT_NO_FATAL_FAILURE(FetchLicense(
|
||||
kLicenseServerAppspot, message, &reply));
|
||||
EXPECT_CALL(*this, onKeyStatusesChange(session_id));
|
||||
status = cdm_->update(session_id, reply);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmIndividualizationTest, IsProvisioned) {
|
||||
if (!CheckProvisioningSupport()) return;
|
||||
|
||||
// Clear any existing certificates.
|
||||
g_host->remove("cert.bin");
|
||||
|
||||
EXPECT_FALSE(cdm_->isProvisioned());
|
||||
|
||||
// Creating a session should succeed.
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
@@ -1611,6 +1490,45 @@ TEST_F(CdmIndividualizationTest, BasicFlow) {
|
||||
status = cdm_->update(session_id, reply);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
EXPECT_TRUE(cdm_->isProvisioned());
|
||||
}
|
||||
|
||||
TEST_F(CdmIndividualizationTest, RemoveProvisioning) {
|
||||
if (!CheckProvisioningSupport()) return;
|
||||
|
||||
// Clear any existing certificates.
|
||||
EXPECT_EQ(Cdm::kSuccess, cdm_->removeProvisioning());
|
||||
|
||||
EXPECT_FALSE(cdm_->isProvisioned());
|
||||
|
||||
// Creating a session should succeed.
|
||||
std::string session_id;
|
||||
Cdm::Status status = cdm_->createSession(Cdm::kTemporary, &session_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Should get an individualization request when we generate request.
|
||||
std::string message;
|
||||
EXPECT_CALL(*this, onDirectIndividualizationRequest(session_id, _))
|
||||
.WillOnce(SaveArg<1>(&message));
|
||||
status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Complete the provisioning request.
|
||||
std::string reply = GetProvisioningResponse(message);
|
||||
ASSERT_FALSE(reply.empty());
|
||||
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).Times(1);
|
||||
EXPECT_CALL(*this, onDeferredComplete(_, _)).Times(0);
|
||||
status = cdm_->update(session_id, reply);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
EXPECT_TRUE(cdm_->isProvisioned());
|
||||
|
||||
EXPECT_EQ(Cdm::kSuccess, cdm_->removeProvisioning());
|
||||
|
||||
EXPECT_FALSE(cdm_->isProvisioned());
|
||||
}
|
||||
|
||||
TEST_F(CdmIndividualizationTest, WillNotSendRequestTwice) {
|
||||
|
||||
32849
cdm/test/decryption_test_data.h
Normal file
32849
cdm/test/decryption_test_data.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,12 @@ bool TestHost::exists(const std::string& name) {
|
||||
|
||||
bool TestHost::remove(const std::string& name) {
|
||||
LOGD("remove: %s", name.c_str());
|
||||
files_.erase(name);
|
||||
if (name.empty()) {
|
||||
// If no name, delete all files (see DeviceFiles::DeleteAllFiles())
|
||||
files_.clear();
|
||||
} else {
|
||||
files_.erase(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +87,14 @@ int32_t TestHost::size(const std::string& name) {
|
||||
return it->second.size();
|
||||
}
|
||||
|
||||
bool TestHost::list(std::vector<std::string>* names) {
|
||||
names->clear();
|
||||
for (StorageMap::iterator it = files_.begin(); it != files_.end(); it++) {
|
||||
names->push_back(it->first);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t TestHost::now() {
|
||||
return now_;
|
||||
}
|
||||
@@ -107,5 +120,5 @@ void TestHost::cancel(IClient* client) {
|
||||
}
|
||||
|
||||
// Now swap the queues.
|
||||
timers_.swap(others);
|
||||
std::swap(timers_, others);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define WVCDM_CDM_TEST_TEST_HOST_H_
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "cdm.h"
|
||||
#include "override.h"
|
||||
@@ -24,6 +25,7 @@ class TestHost : public widevine::Cdm::IStorage,
|
||||
virtual bool exists(const std::string& name) OVERRIDE;
|
||||
virtual bool remove(const std::string& name) OVERRIDE;
|
||||
virtual int32_t size(const std::string& name) OVERRIDE;
|
||||
virtual bool list(std::vector<std::string>* names) OVERRIDE;
|
||||
|
||||
virtual int64_t now() OVERRIDE;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user