Add initial support for key rotation through ce cdm interface.
Merge from Widevine repo of http://go/wvgerrit/42941 Bug: 72168544 Test: tested as part of http://go/ag/4674759 Change-Id: I1a2d0f49371e5b3edf1d9dff85b85593f981d1f5
This commit is contained in:
@@ -27,6 +27,10 @@ class PolicyEngine;
|
||||
class CdmSession;
|
||||
class CryptoKey;
|
||||
|
||||
using ::google::protobuf::RepeatedPtrField;
|
||||
using video_widevine::License_KeyContainer;
|
||||
using video_widevine::WidevinePsshData_EntitledKey;
|
||||
|
||||
class CdmLicense {
|
||||
public:
|
||||
CdmLicense(const CdmSessionId& session_id);
|
||||
@@ -50,7 +54,8 @@ class CdmLicense {
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
bool is_renewal, const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleSubLicense(const InitializationData& init_data);
|
||||
virtual CdmResponseType HandleEmbeddedKeyData(
|
||||
const InitializationData& init_data);
|
||||
|
||||
virtual bool RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
@@ -101,6 +106,10 @@ class CdmLicense {
|
||||
const std::vector<CryptoKey>& key_array,
|
||||
const video_widevine::License& license);
|
||||
|
||||
CdmResponseType HandleNewEntitledKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& wrapped_keys);
|
||||
CdmResponseType HandleSubLicense(const InitializationData& init_data);
|
||||
|
||||
template <typename T>
|
||||
bool SetTypeAndId(CdmLicenseType license_type, const std::string& request_id,
|
||||
T* content_id);
|
||||
@@ -132,16 +141,19 @@ class CdmLicense {
|
||||
// CdmLicense takes ownership of the clock.
|
||||
CdmLicense(const CdmSessionId& session_id, Clock* clock);
|
||||
|
||||
// For sublicense key embedding. This key array will be initilized with any
|
||||
// sub session keys we may have received in a license response. These keys
|
||||
// may be used to support key rotation.
|
||||
std::vector<CryptoKey> sub_session_key_array_;
|
||||
|
||||
// For entitlement key licensing. This holds the keys from the init_data.
|
||||
// These keys are extracted from the pssh when we generate a license request.
|
||||
// It is used to load content keys after we have received a license and
|
||||
// entitelement keys. It is also used in updating the key status info.
|
||||
std::vector<video_widevine::WrappedKey> wrapped_keys_;
|
||||
std::vector<WidevinePsshData_EntitledKey> wrapped_keys_;
|
||||
|
||||
// For sublicense key embedding. This key array will be initialized with any
|
||||
// sub session keys we may have received in a license response. These keys
|
||||
// may be used to support key rotation.
|
||||
std::vector<CryptoKey> entitlement_key_array_;
|
||||
|
||||
CdmLicenseKeyType license_key_type_;
|
||||
RepeatedPtrField<License_KeyContainer> entitlement_keys_;
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmLicenseTest;
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace wvcdm {
|
||||
|
||||
class LicenseKeyStatus;
|
||||
|
||||
using video_widevine::WidevinePsshData_EntitledKey;
|
||||
|
||||
// Holds all content and operator session keys for a session.
|
||||
class LicenseKeys {
|
||||
public:
|
||||
@@ -59,8 +61,11 @@ class LicenseKeys {
|
||||
|
||||
// Extracts the keys from a license and makes them available for
|
||||
// querying usage and constraint settings.
|
||||
virtual void SetFromLicense(
|
||||
const video_widevine::License& license);
|
||||
virtual void SetFromLicense(const video_widevine::License& license);
|
||||
|
||||
// Sets the keys from the input entitled key data.
|
||||
virtual void SetEntitledKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& keys);
|
||||
|
||||
private:
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
@@ -70,7 +75,12 @@ class LicenseKeys {
|
||||
void Clear();
|
||||
|
||||
bool is_initialized_;
|
||||
// |keys_| can hold either content key statuses, or entitlement key statuses.
|
||||
std::map<KeyId, LicenseKeyStatus*> keys_;
|
||||
// |content_keyid_to_entitlement_key_id_| maps a content key id to an
|
||||
// entitlement_key_id. The resulting key id can be used to obtain the current
|
||||
// key status from |keys_| when using entitlement key licensing.
|
||||
std::map<KeyId, KeyId> content_keyid_to_entitlement_key_id_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeys);
|
||||
};
|
||||
@@ -104,8 +114,8 @@ class LicenseKeyStatus {
|
||||
virtual bool MeetsConstraints() const { return meets_constraints_; }
|
||||
|
||||
// Applies the given changes in resolution or HDCP settings.
|
||||
virtual void ApplyConstraints(
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
|
||||
virtual void ApplyConstraints(uint32_t new_resolution,
|
||||
CryptoSession::HdcpCapability new_hdcp_level);
|
||||
|
||||
protected:
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
@@ -121,13 +131,10 @@ class LicenseKeyStatus {
|
||||
virtual ~LicenseKeyStatus() {}
|
||||
|
||||
private:
|
||||
|
||||
void ParseContentKey(const KeyContainer& key);
|
||||
void ParseOperatorSessionKey(const KeyContainer& key);
|
||||
|
||||
bool HasConstraints() {
|
||||
return is_content_key_ && constraints_.size() != 0;
|
||||
}
|
||||
bool HasConstraints() { return is_content_key_ && constraints_.size() != 0; }
|
||||
|
||||
void SetConstraints(const ConstraintList& constraints);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::WidevinePsshData_EntitledKey;
|
||||
|
||||
class Clock;
|
||||
class CryptoSession;
|
||||
@@ -57,12 +58,18 @@ class PolicyEngine {
|
||||
// permits playback.
|
||||
virtual void SetLicense(const video_widevine::License& license);
|
||||
|
||||
// TODO(jfore): Sublicense uses this to update the keys when they are
|
||||
// changed during key rotation. Drop this method and use SetLicenseKeys
|
||||
// instead.
|
||||
virtual void UpdateLicenseKeys(const video_widevine::License& license);
|
||||
|
||||
// Used to update the currently loaded entitled content keys.
|
||||
virtual void SetEntitledLicenseKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& entitled_keys);
|
||||
|
||||
// SetLicenseForRelease is used when releasing a license. The keys in this
|
||||
// license will be ignored, and any old keys will be expired.
|
||||
virtual void SetLicenseForRelease(
|
||||
const video_widevine::License& license);
|
||||
virtual void SetLicenseForRelease(const video_widevine::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
@@ -73,8 +80,7 @@ class PolicyEngine {
|
||||
// case an exact copy is not what we want to happen. We also will receive an
|
||||
// updated license_start_time from the server. The license will transition to
|
||||
// kLicenseStateCanPlay if the license permits playback.
|
||||
virtual void UpdateLicense(
|
||||
const video_widevine::License& license);
|
||||
virtual void UpdateLicense(const video_widevine::License& license);
|
||||
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
@@ -101,8 +107,7 @@ class PolicyEngine {
|
||||
|
||||
bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; }
|
||||
bool HasPlaybackStarted(int64_t current_time) {
|
||||
if (playback_start_time_ == 0)
|
||||
return false;
|
||||
if (playback_start_time_ == 0) return false;
|
||||
|
||||
const int64_t playback_time = current_time - playback_start_time_;
|
||||
return playback_time >= policy_.play_start_grace_period_seconds();
|
||||
@@ -218,6 +223,6 @@ class PolicyEngine {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine);
|
||||
};
|
||||
|
||||
} // wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_POLICY_ENGINE_H_
|
||||
|
||||
@@ -355,18 +355,19 @@ enum CdmLicenseType {
|
||||
// Like Streaming, but stricter. Does not permit storage of any kind.
|
||||
// Named after the 'temporary' session type in EME, which has this behavior.
|
||||
kLicenseTypeTemporary,
|
||||
kLicenseTypeSubSession
|
||||
// TODO(jfore): The kLicenseTypeEmbeddedKeyData currently is to differentiate
|
||||
// between call types made to GenerateKeyRequest. This type is used to
|
||||
// differentiate between calls to generate a license renewal and a new pssh
|
||||
// with embedded keys. Please refer to CdmSession::GenerateKeyRequest. Based
|
||||
// on code review comments from go/wvgerrit/41860 this license type should not
|
||||
// be added. This type can be removed once it is no longer needed by
|
||||
// GenerateKeyRequest.
|
||||
kLicenseTypeEmbeddedKeyData
|
||||
};
|
||||
|
||||
enum CdmLicenseKeyType {
|
||||
kLicenseKeyTypeContent,
|
||||
kLicenseKeyTypeEntitlement
|
||||
};
|
||||
enum CdmLicenseKeyType { kLicenseKeyTypeContent, kLicenseKeyTypeEntitlement };
|
||||
|
||||
enum SecurityLevel {
|
||||
kLevelDefault,
|
||||
kLevel3
|
||||
};
|
||||
enum SecurityLevel { kLevelDefault, kLevel3 };
|
||||
|
||||
enum CdmSecurityLevel {
|
||||
kSecurityLevelUninitialized,
|
||||
@@ -435,10 +436,10 @@ struct CdmUsageEntryInfo {
|
||||
CdmKeySetId key_set_id;
|
||||
std::string usage_info_file_name;
|
||||
bool operator==(const CdmUsageEntryInfo& other) const {
|
||||
return storage_type == other.storage_type &&
|
||||
key_set_id == other.key_set_id &&
|
||||
(storage_type != kStorageUsageInfo ||
|
||||
usage_info_file_name == other.usage_info_file_name);
|
||||
return storage_type == other.storage_type &&
|
||||
key_set_id == other.key_set_id &&
|
||||
(storage_type != kStorageUsageInfo ||
|
||||
usage_info_file_name == other.usage_info_file_name);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -454,9 +455,7 @@ enum CdmKeySecurityLevel {
|
||||
|
||||
class CdmKeyAllowedUsage {
|
||||
public:
|
||||
CdmKeyAllowedUsage() {
|
||||
Clear();
|
||||
}
|
||||
CdmKeyAllowedUsage() { Clear(); }
|
||||
|
||||
bool Valid() const { return valid_; }
|
||||
void SetValid() { valid_ = true; }
|
||||
@@ -506,9 +505,7 @@ class CdmKeyAllowedUsage {
|
||||
struct CdmCencPatternEncryptionDescriptor {
|
||||
size_t encrypt_blocks; // number of 16 byte blocks to decrypt
|
||||
size_t skip_blocks; // number of 16 byte blocks to leave in clear
|
||||
CdmCencPatternEncryptionDescriptor()
|
||||
: encrypt_blocks(0),
|
||||
skip_blocks(0) {}
|
||||
CdmCencPatternEncryptionDescriptor() : encrypt_blocks(0), skip_blocks(0) {}
|
||||
};
|
||||
|
||||
struct CdmDecryptionParameters {
|
||||
|
||||
@@ -289,7 +289,9 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
sts = session->GenerateKeyRequest(init_data, license_type, app_parameters,
|
||||
key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (KEY_ADDED == sts) {
|
||||
return sts;
|
||||
} else if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
session->GetRequestedSecurityLevel();
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "usage_table_header.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
#include "usage_table_header.h"
|
||||
|
||||
namespace {
|
||||
const size_t kKeySetIdLength = 14;
|
||||
@@ -27,26 +27,26 @@ const size_t kKeySetIdLength = 14;
|
||||
namespace wvcdm {
|
||||
|
||||
CdmSession::CdmSession(FileSystem* file_system,
|
||||
metrics::SessionMetrics* metrics) :
|
||||
metrics_(metrics),
|
||||
initialized_(false),
|
||||
closed_(true),
|
||||
file_handle_(new DeviceFiles(file_system)),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_temporary_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_decryption_(true),
|
||||
has_decrypted_since_last_report_(false),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false),
|
||||
usage_support_type_(kNonSecureUsageSupport),
|
||||
usage_table_header_(NULL),
|
||||
usage_entry_number_(0),
|
||||
mock_license_parser_in_use_(false),
|
||||
mock_policy_engine_in_use_(false) {
|
||||
metrics::SessionMetrics* metrics)
|
||||
: metrics_(metrics),
|
||||
initialized_(false),
|
||||
closed_(true),
|
||||
file_handle_(new DeviceFiles(file_system)),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_temporary_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_decryption_(true),
|
||||
has_decrypted_since_last_report_(false),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false),
|
||||
usage_support_type_(kNonSecureUsageSupport),
|
||||
usage_table_header_(NULL),
|
||||
usage_entry_number_(0),
|
||||
mock_license_parser_in_use_(false),
|
||||
mock_policy_engine_in_use_(false) {
|
||||
assert(metrics_); // metrics_ must not be null.
|
||||
crypto_metrics_ = metrics_->GetCryptoMetrics();
|
||||
crypto_session_.reset(new CryptoSession(crypto_metrics_));
|
||||
@@ -77,27 +77,22 @@ CdmResponseType CdmSession::Init(
|
||||
return Init(cdm_client_property_set, NULL, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::Init(
|
||||
CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
|
||||
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return REINIT_ERROR;
|
||||
}
|
||||
|
||||
if (cdm_client_property_set &&
|
||||
cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
if (cdm_client_property_set && cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
requested_security_level_ = kLevel3;
|
||||
security_level_ = kSecurityLevelL3;
|
||||
}
|
||||
CdmResponseType sts;
|
||||
M_TIME(
|
||||
sts = crypto_session_->Open(requested_security_level_),
|
||||
crypto_metrics_,
|
||||
crypto_session_open_,
|
||||
sts,
|
||||
requested_security_level_);
|
||||
M_TIME(sts = crypto_session_->Open(requested_security_level_),
|
||||
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
|
||||
if (NO_ERROR != sts) return sts;
|
||||
|
||||
security_level_ = crypto_session_->GetSecurityLevel();
|
||||
@@ -137,12 +132,10 @@ CdmResponseType CdmSession::Init(
|
||||
}
|
||||
bool load_cert_sts;
|
||||
M_TIME(
|
||||
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
|
||||
wrapped_key),
|
||||
crypto_metrics_,
|
||||
crypto_session_load_certificate_private_key_,
|
||||
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(wrapped_key),
|
||||
crypto_metrics_, crypto_session_load_certificate_private_key_,
|
||||
load_cert_sts);
|
||||
if(!load_cert_sts) {
|
||||
if (!load_cert_sts) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
client_token_type = kClientTokenDrmCert;
|
||||
@@ -172,17 +165,17 @@ CdmResponseType CdmSession::Init(
|
||||
if (!mock_license_parser_in_use_)
|
||||
license_parser_.reset(new CdmLicense(session_id_));
|
||||
if (!mock_policy_engine_in_use_)
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
policy_engine_.reset(
|
||||
new PolicyEngine(session_id_, event_listener, crypto_session_.get()));
|
||||
|
||||
std::string service_certificate;
|
||||
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
|
||||
service_certificate.clear();
|
||||
|
||||
if (!license_parser_->Init(
|
||||
client_token, client_token_type, serial_number,
|
||||
Properties::UsePrivacyMode(session_id_), service_certificate,
|
||||
crypto_session_.get(), policy_engine_.get()))
|
||||
if (!license_parser_->Init(client_token, client_token_type, serial_number,
|
||||
Properties::UsePrivacyMode(session_id_),
|
||||
service_certificate, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
@@ -214,32 +207,37 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
&offline_key_renewal_response_, &offline_release_server_url_,
|
||||
&playback_start_time, &last_playback_time, &grace_period_end_time,
|
||||
&app_parameters_, &usage_entry_, &usage_entry_number_)) {
|
||||
LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. "
|
||||
"key set id = %s", key_set_id.c_str());
|
||||
LOGE(
|
||||
"CdmSession::RestoreOfflineSession: failed to retrieve license. "
|
||||
"key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return GET_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
// Do not restore a released offline license, unless a release retry
|
||||
if (!(license_type == kLicenseTypeRelease ||
|
||||
license_state == DeviceFiles::kLicenseStateActive)) {
|
||||
LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = "
|
||||
"%d, type = %d", license_state, license_type);
|
||||
LOGE(
|
||||
"CdmSession::RestoreOfflineSession: invalid offline license state = "
|
||||
"%d, type = %d",
|
||||
license_state, license_type);
|
||||
return GET_RELEASED_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
std::string provider_session_token;
|
||||
if (usage_support_type_ == kUsageEntrySupport) {
|
||||
if (!license_parser_->ExtractProviderSessionToken(
|
||||
key_response_, &provider_session_token) ||
|
||||
key_response_, &provider_session_token) ||
|
||||
usage_table_header_ == NULL) {
|
||||
provider_session_token.clear();
|
||||
} else {
|
||||
CdmResponseType sts =
|
||||
usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_,
|
||||
usage_entry_number_);
|
||||
CdmResponseType sts = usage_table_header_->LoadEntry(
|
||||
crypto_session_.get(), usage_entry_, usage_entry_number_);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = "
|
||||
"%d", sts);
|
||||
LOGE(
|
||||
"CdmSession::RestoreOfflineSession: failed to load usage entry = "
|
||||
"%d",
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
@@ -264,12 +262,15 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
CdmResponseType sts =
|
||||
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = "
|
||||
"%d", sts);
|
||||
LOGE(
|
||||
"CdmSession::RestoreOfflineSession failed to update usage entry = "
|
||||
"%d",
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
if (!StoreLicense(license_state)) {
|
||||
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
|
||||
LOGW(
|
||||
"CdmSession::RestoreUsageSession: unable to save updated usage "
|
||||
"info");
|
||||
}
|
||||
}
|
||||
@@ -302,7 +303,7 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
crypto_session_.get(), usage_entry_, usage_entry_number_);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
|
||||
sts);
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
@@ -317,11 +318,12 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d",
|
||||
sts);
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
if (!UpdateUsageInfo()) {
|
||||
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
|
||||
LOGW(
|
||||
"CdmSession::RestoreUsageSession: unable to save updated usage "
|
||||
"info");
|
||||
}
|
||||
}
|
||||
@@ -376,8 +378,12 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
|
||||
case kLicenseTypeRelease:
|
||||
is_release_ = true;
|
||||
break;
|
||||
case kLicenseTypeSubSession:
|
||||
return license_parser_->HandleSubLicense(init_data);
|
||||
// TODO(jfore): CdmSession assumes a call to this method once a license has
|
||||
// been received is a call to generate a license renewal message. Use of
|
||||
// this enum differentiates the two calls. See "else if (license_received_)"
|
||||
// below.
|
||||
case kLicenseTypeEmbeddedKeyData:
|
||||
return license_parser_->HandleEmbeddedKeyData(init_data);
|
||||
default:
|
||||
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
|
||||
license_type);
|
||||
@@ -419,11 +425,9 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
|
||||
|
||||
app_parameters_ = app_parameters;
|
||||
CdmResponseType status = license_parser_->PrepareKeyRequest(
|
||||
init_data, license_type,
|
||||
app_parameters, &key_request->message,
|
||||
&key_request->url);
|
||||
if (status != KEY_MESSAGE)
|
||||
return status;
|
||||
init_data, license_type, app_parameters, &key_request->message,
|
||||
&key_request->url);
|
||||
if (status != KEY_MESSAGE) return status;
|
||||
|
||||
key_request_ = key_request->message;
|
||||
if (is_offline_) {
|
||||
@@ -476,13 +480,10 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
|
||||
// Update or delete entry if usage table header+entries are supported
|
||||
if (usage_support_type_ == kUsageEntrySupport &&
|
||||
!provider_session_token.empty() &&
|
||||
usage_table_header_ != NULL) {
|
||||
!provider_session_token.empty() && usage_table_header_ != NULL) {
|
||||
if (sts != KEY_ADDED) {
|
||||
CdmResponseType delete_sts =
|
||||
usage_table_header_->DeleteEntry(usage_entry_number_,
|
||||
file_handle_.get(),
|
||||
crypto_metrics_);
|
||||
CdmResponseType delete_sts = usage_table_header_->DeleteEntry(
|
||||
usage_entry_number_, file_handle_.get(), crypto_metrics_);
|
||||
if (delete_sts != NO_ERROR) {
|
||||
LOGW("CdmSession::AddKey: Delete usage entry failed = %d",
|
||||
delete_sts);
|
||||
@@ -578,17 +579,16 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
|
||||
// Playback may not begin until either the start time passes or the license
|
||||
// is updated, so we treat this Decrypt call as invalid.
|
||||
if (params.is_encrypted) {
|
||||
if (!policy_engine_->CanDecryptContent(*params.key_id)) {
|
||||
if (policy_engine_->IsLicenseForFuture())
|
||||
return DECRYPT_NOT_READY;
|
||||
if (!policy_engine_->IsSufficientOutputProtection(*params.key_id))
|
||||
return INSUFFICIENT_OUTPUT_PROTECTION;
|
||||
return NEED_KEY;
|
||||
}
|
||||
if (params.is_encrypted &&
|
||||
!policy_engine_->CanDecryptContent(*params.key_id)) {
|
||||
if (policy_engine_->IsLicenseForFuture()) return DECRYPT_NOT_READY;
|
||||
if (!policy_engine_->IsSufficientOutputProtection(*params.key_id))
|
||||
return INSUFFICIENT_OUTPUT_PROTECTION;
|
||||
return NEED_KEY;
|
||||
}
|
||||
|
||||
if (!policy_engine_->CanUseKey(*params.key_id, security_level_))
|
||||
return KEY_PROHIBITED_FOR_SECURITY_LEVEL;
|
||||
if (!policy_engine_->CanUseKey(*params.key_id, security_level_)) {
|
||||
return KEY_PROHIBITED_FOR_SECURITY_LEVEL;
|
||||
}
|
||||
|
||||
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||
@@ -616,8 +616,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// License renewal
|
||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(
|
||||
CdmKeyRequest* key_request) {
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyRequest* key_request) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmSession::GenerateRenewalRequest: not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
@@ -659,8 +658,7 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(
|
||||
CdmKeyRequest* key_request) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmSession::GenerateReleaseRequest: not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
@@ -677,11 +675,13 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
|
||||
|
||||
if (has_provider_session_token() &&
|
||||
usage_support_type_ == kUsageEntrySupport) {
|
||||
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
|
||||
&usage_entry_);
|
||||
status =
|
||||
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = "
|
||||
"%d", status);
|
||||
LOGE(
|
||||
"CdmSession::GenerateReleaseRequest: Update usage entry failed = "
|
||||
"%d",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -691,8 +691,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
|
||||
return RELEASE_KEY_REQUEST_ERROR;
|
||||
} else if (!usage_provider_session_token_.empty()) {
|
||||
if (usage_support_type_ == kUsageEntrySupport) {
|
||||
if (!UpdateUsageInfo())
|
||||
return RELEASE_USAGE_INFO_FAILED;
|
||||
if (!UpdateUsageInfo()) return RELEASE_USAGE_INFO_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,7 +724,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
|
||||
}
|
||||
if (usage_support_type_ != kUsageEntrySupport) {
|
||||
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
|
||||
usage_support_type_);
|
||||
usage_support_type_);
|
||||
return INCORRECT_USAGE_SUPPORT_TYPE_1;
|
||||
}
|
||||
|
||||
@@ -734,12 +733,8 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
|
||||
CdmResponseType sts;
|
||||
crypto_session_->Close();
|
||||
crypto_session_.reset(new CryptoSession(crypto_metrics_));
|
||||
M_TIME(
|
||||
sts = crypto_session_->Open(requested_security_level_),
|
||||
crypto_metrics_,
|
||||
crypto_session_open_,
|
||||
sts,
|
||||
requested_security_level_);
|
||||
M_TIME(sts = crypto_session_->Open(requested_security_level_),
|
||||
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
usage_table_header_ = NULL;
|
||||
@@ -756,8 +751,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
|
||||
}
|
||||
|
||||
return usage_table_header_->DeleteEntry(usage_entry_number,
|
||||
file_handle_.get(),
|
||||
crypto_metrics_);
|
||||
file_handle_.get(), crypto_metrics_);
|
||||
}
|
||||
|
||||
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
|
||||
@@ -833,11 +827,10 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
|
||||
key_response_,
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
key_set_id_, usage_entry_,
|
||||
usage_entry_number_)) {
|
||||
if (!file_handle_->StoreUsageInfo(
|
||||
provider_session_token, key_request_, key_response_,
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id_, usage_entry_,
|
||||
usage_entry_number_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store usage info");
|
||||
// Usage info file is corrupt. Delete current usage entry and file.
|
||||
switch (usage_support_type_) {
|
||||
@@ -969,11 +962,8 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
|
||||
crypto_session_->GetUsageSupportType(&usage_support_type);
|
||||
|
||||
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
|
||||
M_TIME(
|
||||
sts = crypto_session_->UpdateUsageInformation(),
|
||||
crypto_metrics_,
|
||||
crypto_session_update_usage_information_,
|
||||
sts);
|
||||
M_TIME(sts = crypto_session_->UpdateUsageInformation(), crypto_metrics_,
|
||||
crypto_session_update_usage_information_, sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -982,12 +972,12 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
|
||||
if (usage_support_type_ != kUsageEntrySupport ||
|
||||
!has_provider_session_token() ||
|
||||
usage_table_header_ == NULL) {
|
||||
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, "
|
||||
!has_provider_session_token() || usage_table_header_ == NULL) {
|
||||
LOGE(
|
||||
"CdmSession::UpdateUsageEntryInformation: Unexpected state, "
|
||||
"usage support type: %d, PST present: %s, usage table header available"
|
||||
": %s", usage_support_type_,
|
||||
has_provider_session_token() ? "yes" : "no",
|
||||
": %s",
|
||||
usage_support_type_, has_provider_session_token() ? "yes" : "no",
|
||||
usage_table_header_ == NULL ? "no" : "yes");
|
||||
return INCORRECT_USAGE_SUPPORT_TYPE_2;
|
||||
}
|
||||
@@ -1003,9 +993,8 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
if (is_offline_)
|
||||
StoreLicense(is_release_
|
||||
? DeviceFiles::kLicenseStateReleasing
|
||||
: DeviceFiles::kLicenseStateActive);
|
||||
StoreLicense(is_release_ ? DeviceFiles::kLicenseStateReleasing
|
||||
: DeviceFiles::kLicenseStateActive);
|
||||
else if (!usage_provider_session_token_.empty())
|
||||
UpdateUsageInfo();
|
||||
|
||||
@@ -1022,18 +1011,10 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
CdmResponseType sts;
|
||||
M_TIME(
|
||||
sts = crypto_session_->GenericEncrypt(
|
||||
in_buffer,
|
||||
key_id,
|
||||
iv,
|
||||
algorithm,
|
||||
out_buffer),
|
||||
crypto_metrics_,
|
||||
crypto_session_generic_encrypt_,
|
||||
sts,
|
||||
metrics::Pow2Bucket(in_buffer.size()),
|
||||
algorithm);
|
||||
M_TIME(sts = crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer),
|
||||
crypto_metrics_, crypto_session_generic_encrypt_, sts,
|
||||
metrics::Pow2Bucket(in_buffer.size()), algorithm);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1047,18 +1028,10 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
CdmResponseType sts;
|
||||
M_TIME(
|
||||
sts = crypto_session_->GenericDecrypt(
|
||||
in_buffer,
|
||||
key_id,
|
||||
iv,
|
||||
algorithm,
|
||||
out_buffer),
|
||||
crypto_metrics_,
|
||||
crypto_session_generic_decrypt_,
|
||||
sts,
|
||||
metrics::Pow2Bucket(in_buffer.size()),
|
||||
algorithm);
|
||||
M_TIME(sts = crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer),
|
||||
crypto_metrics_, crypto_session_generic_decrypt_, sts,
|
||||
metrics::Pow2Bucket(in_buffer.size()), algorithm);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1072,16 +1045,9 @@ CdmResponseType CdmSession::GenericSign(const std::string& message,
|
||||
}
|
||||
CdmResponseType sts;
|
||||
M_TIME(
|
||||
sts = crypto_session_->GenericSign(
|
||||
message,
|
||||
key_id,
|
||||
algorithm,
|
||||
signature),
|
||||
crypto_metrics_,
|
||||
crypto_session_generic_sign_,
|
||||
sts,
|
||||
metrics::Pow2Bucket(message.size()),
|
||||
algorithm);
|
||||
sts = crypto_session_->GenericSign(message, key_id, algorithm, signature),
|
||||
crypto_metrics_, crypto_session_generic_sign_, sts,
|
||||
metrics::Pow2Bucket(message.size()), algorithm);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1090,17 +1056,10 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature) {
|
||||
CdmResponseType sts;
|
||||
M_TIME(
|
||||
sts = crypto_session_->GenericVerify(
|
||||
message,
|
||||
key_id,
|
||||
algorithm,
|
||||
signature),
|
||||
crypto_metrics_,
|
||||
crypto_session_generic_verify_,
|
||||
sts,
|
||||
metrics::Pow2Bucket(message.size()),
|
||||
algorithm);
|
||||
M_TIME(sts = crypto_session_->GenericVerify(message, key_id, algorithm,
|
||||
signature),
|
||||
crypto_metrics_, crypto_session_generic_verify_, sts,
|
||||
metrics::Pow2Bucket(message.size()), algorithm);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1117,8 +1076,7 @@ bool CdmSession::UpdateUsageInfo() {
|
||||
usage_data.usage_entry_number = usage_entry_number_;
|
||||
|
||||
return file_handle_->UpdateUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
usage_provider_session_token_,
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), usage_provider_session_token_,
|
||||
usage_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,8 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
|
||||
renew_with_client_id_(false),
|
||||
is_offline_(false),
|
||||
use_privacy_mode_(false),
|
||||
clock_(new Clock()) {}
|
||||
clock_(new Clock()),
|
||||
license_key_type_(kLicenseKeyTypeContent) {}
|
||||
|
||||
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
: crypto_session_(NULL),
|
||||
@@ -221,7 +222,8 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
initialized_(false),
|
||||
renew_with_client_id_(false),
|
||||
is_offline_(false),
|
||||
use_privacy_mode_(false) {
|
||||
use_privacy_mode_(false),
|
||||
license_key_type_(kLicenseKeyTypeContent) {
|
||||
clock_.reset(clock);
|
||||
}
|
||||
|
||||
@@ -638,6 +640,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
license_key_type_ = key_type;
|
||||
|
||||
if (license.has_srm_update()) crypto_session_->LoadSrm(license.srm_update());
|
||||
|
||||
@@ -661,6 +664,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
CdmResponseType resp = NO_CONTENT_KEY;
|
||||
if (kLicenseKeyTypeEntitlement == key_type) {
|
||||
entitlement_key_array_ = key_array;
|
||||
resp = HandleEntitlementKeyResponse(signed_response.msg(),
|
||||
signed_response.signature(), mac_key_iv,
|
||||
mac_key, key_array, license);
|
||||
@@ -752,55 +756,11 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleSubLicense(
|
||||
CdmResponseType CdmLicense::HandleEmbeddedKeyData(
|
||||
const InitializationData& init_data) {
|
||||
std::vector<video_widevine::SubLicense> subkeys =
|
||||
init_data.ExtractSublicenseKeys();
|
||||
std::set<KeyId> loaded_keys;
|
||||
// Build a license with the rotated keys.
|
||||
License license;
|
||||
for (size_t i = 0; i < subkeys.size(); ++i) {
|
||||
SignedMessage sm;
|
||||
if (!sm.ParseFromString(subkeys[i].key_msg())) {
|
||||
return LICENSE_REQUEST_INVALID_SUBLICENSE;
|
||||
}
|
||||
License_KeyContainer keyc;
|
||||
if (!keyc.ParseFromString(sm.msg())) {
|
||||
return LICENSE_REQUEST_INVALID_SUBLICENSE;
|
||||
}
|
||||
size_t length;
|
||||
std::vector<CryptoKey> keys;
|
||||
keys.resize(1);
|
||||
keys[0].set_key_id(keyc.id());
|
||||
|
||||
// Strip PKCS#5 padding from sublicense content keys.
|
||||
// TODO(jfore): Refactor this to use ExtractContentKeys.
|
||||
if (keyc.key().size() > KEY_SIZE) {
|
||||
length = keyc.key().size() - KEY_SIZE;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
keys[0].set_key_data(keyc.key().substr(0, length));
|
||||
keys[0].set_key_data_iv(keyc.iv());
|
||||
keys[0].set_key_control(keyc.key_control().key_control_block());
|
||||
keys[0].set_key_control_iv(keyc.key_control().iv());
|
||||
keys[0].set_track_label(keyc.track_label());
|
||||
//TODO: passing empty cipher_mode and srm_req params - OK?
|
||||
CdmResponseType result = crypto_session_->LoadKeys(
|
||||
sm.msg(), sm.signature(), std::string(), std::string(), keys,
|
||||
std::string(), std::string(), kLicenseKeyTypeContent);
|
||||
if (result != KEY_ADDED) {
|
||||
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
loaded_keys.insert(keyc.id());
|
||||
*license.add_key() = keyc;
|
||||
}
|
||||
loaded_keys_.swap(loaded_keys);
|
||||
policy_engine_->UpdateLicenseKeys(license);
|
||||
|
||||
return KEY_MESSAGE;
|
||||
return (license_key_type_ == kLicenseKeyTypeEntitlement
|
||||
? HandleNewEntitledKeys(init_data.ExtractWrappedKeys())
|
||||
: HandleSubLicense(init_data));
|
||||
}
|
||||
|
||||
bool CdmLicense::RestoreOfflineLicense(
|
||||
@@ -1143,61 +1103,112 @@ CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::vector<CryptoKey> entitled_key_array;
|
||||
entitled_key_array.reserve(key_array.size());
|
||||
// Save the entitlement keys for future use to handle key changes.
|
||||
entitlement_keys_.CopyFrom(license.key());
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
for (std::vector<video_widevine::WrappedKey>::iterator wk =
|
||||
wrapped_keys_.begin();
|
||||
wk != wrapped_keys_.end(); wk++) {
|
||||
for (std::vector<CryptoKey>::const_iterator key = key_array.begin();
|
||||
key != key_array.end(); key++) {
|
||||
if (wk->wrapping_key_id() == key->key_id()) {
|
||||
return HandleNewEntitledKeys(wrapped_keys_);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleNewEntitledKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& wrapped_keys) {
|
||||
std::vector<CryptoKey> entitled_key_array;
|
||||
entitled_key_array.reserve(entitlement_keys_.size());
|
||||
|
||||
for (RepeatedPtrField<License_KeyContainer>::const_iterator kc =
|
||||
entitlement_keys_.begin();
|
||||
kc != entitlement_keys_.end(); kc++) {
|
||||
if (kc->type() != video_widevine::License::KeyContainer::ENTITLEMENT) {
|
||||
continue;
|
||||
}
|
||||
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator wk =
|
||||
wrapped_keys.begin();
|
||||
wk != wrapped_keys.end(); wk++) {
|
||||
if (wk->entitlement_key_id() == kc->id()) {
|
||||
// Add a new entry to the key array to load oemcrypto.
|
||||
entitled_key_array.resize(entitled_key_array.size() + 1);
|
||||
|
||||
// Strip PKCS#5 padding from entitled content keys.
|
||||
std::string content_key = wk->key();
|
||||
if (content_key.size() > KEY_SIZE) {
|
||||
content_key.resize(KEY_SIZE);
|
||||
}
|
||||
|
||||
CryptoKey& this_entry = entitled_key_array.back();
|
||||
this_entry.set_key_id(wk->key_id());
|
||||
this_entry.set_key_data(wk->wrapped_key());
|
||||
this_entry.set_key_data_iv(wk->wrapping_iv());
|
||||
this_entry.set_entitlement_key_id(wk->wrapping_key_id());
|
||||
this_entry.set_key_data_iv(wk->iv());
|
||||
this_entry.set_entitlement_key_id(wk->entitlement_key_id());
|
||||
this_entry.set_key_data(content_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp = crypto_session_->LoadEntitledContentKeys(entitled_key_array);
|
||||
CdmResponseType resp =
|
||||
crypto_session_->LoadEntitledContentKeys(entitled_key_array);
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
for (std::vector<video_widevine::WrappedKey>::const_iterator it =
|
||||
wrapped_keys_.begin();
|
||||
it != wrapped_keys_.end(); ++it) {
|
||||
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator it =
|
||||
wrapped_keys.begin();
|
||||
it != wrapped_keys.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
|
||||
// TODO(jfore): Move the information to build this "license" to the
|
||||
// entitlement key session. It is used to update the policy engine and
|
||||
// key status when using entitlement licenses. It may become unnecessary
|
||||
// if policy manager ius changed to allow setting keys from the wrapped
|
||||
// keys from init_data.
|
||||
video_widevine::License entitled_license;
|
||||
entitled_license.mutable_policy()->CopyFrom(license.policy());
|
||||
entitled_license.mutable_id()->CopyFrom(license.id());
|
||||
entitled_license.mutable_key()->CopyFrom(license.key());
|
||||
entitled_license.set_license_start_time(license.license_start_time());
|
||||
for (size_t i = 0; i < wrapped_keys_.size(); ++i) {
|
||||
for (int x = 0; x < entitled_license.key().size(); ++x) {
|
||||
if (entitled_license.key(x).id() ==
|
||||
wrapped_keys_[i].wrapping_key_id()) {
|
||||
video_widevine::License::KeyContainer* kc =
|
||||
entitled_license.mutable_key(x);
|
||||
kc->set_type(video_widevine::License::KeyContainer::CONTENT);
|
||||
kc->set_key(wrapped_keys_[i].wrapped_key());
|
||||
kc->set_id(wrapped_keys_[i].key_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
policy_engine_->SetLicense(entitled_license);
|
||||
policy_engine_->SetEntitledLicenseKeys(wrapped_keys);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleSubLicense(
|
||||
const InitializationData& init_data) {
|
||||
std::vector<video_widevine::SubLicense> subkeys =
|
||||
init_data.ExtractSublicenseKeys();
|
||||
std::set<KeyId> loaded_keys;
|
||||
// Build a license with the rotated keys.
|
||||
License license;
|
||||
for (size_t i = 0; i < subkeys.size(); ++i) {
|
||||
SignedMessage sm;
|
||||
if (!sm.ParseFromString(subkeys[i].key_msg())) {
|
||||
return LICENSE_REQUEST_INVALID_SUBLICENSE;
|
||||
}
|
||||
License_KeyContainer keyc;
|
||||
if (!keyc.ParseFromString(sm.msg())) {
|
||||
return LICENSE_REQUEST_INVALID_SUBLICENSE;
|
||||
}
|
||||
size_t length;
|
||||
std::vector<CryptoKey> keys;
|
||||
keys.resize(1);
|
||||
keys[0].set_key_id(keyc.id());
|
||||
|
||||
// Strip PKCS#5 padding from sublicense content keys.
|
||||
// TODO(jfore): Refactor this to use ExtractContentKeys.
|
||||
if (keyc.key().size() > KEY_SIZE) {
|
||||
length = keyc.key().size() - KEY_SIZE;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
keys[0].set_key_data(keyc.key().substr(0, length));
|
||||
keys[0].set_key_data_iv(keyc.iv());
|
||||
keys[0].set_key_control(keyc.key_control().key_control_block());
|
||||
keys[0].set_key_control_iv(keyc.key_control().iv());
|
||||
keys[0].set_track_label(keyc.track_label());
|
||||
// TODO: passing empty cipher_mode and srm_req params - OK?
|
||||
CdmResponseType result = crypto_session_->LoadKeys(
|
||||
sm.msg(), sm.signature(), std::string(), std::string(), keys,
|
||||
std::string(), std::string(), kLicenseKeyTypeContent);
|
||||
if (result != KEY_ADDED) {
|
||||
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
loaded_keys.insert(keyc.id());
|
||||
*license.add_key() = keyc;
|
||||
}
|
||||
loaded_keys_.swap(loaded_keys);
|
||||
policy_engine_->UpdateLicenseKeys(license);
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id) {
|
||||
|
||||
@@ -35,9 +35,10 @@ wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
|
||||
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
default:
|
||||
LOGE("ContentKeyStatus::ProtobufHdcpToOemCryptoHdcp: "
|
||||
"Unknown HDCP Level: input=%d, returning HDCP_NO_DIGITAL_OUTPUT",
|
||||
input);
|
||||
LOGE(
|
||||
"ContentKeyStatus::ProtobufHdcpToOemCryptoHdcp: "
|
||||
"Unknown HDCP Level: input=%d, returning HDCP_NO_DIGITAL_OUTPUT",
|
||||
input);
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +68,8 @@ namespace wvcdm {
|
||||
bool LicenseKeys::IsContentKey(const std::string& key_id) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->IsContentKey();
|
||||
} else if (content_keyid_to_entitlement_key_id_.count(key_id) > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -75,6 +78,12 @@ bool LicenseKeys::IsContentKey(const std::string& key_id) {
|
||||
bool LicenseKeys::CanDecryptContent(const std::string& key_id) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->CanDecryptContent();
|
||||
} else if (content_keyid_to_entitlement_key_id_.count(key_id) > 0) {
|
||||
if (keys_.count(content_keyid_to_entitlement_key_id_[key_id]) > 0) {
|
||||
return keys_[content_keyid_to_entitlement_key_id_[key_id]]
|
||||
->CanDecryptContent();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -84,6 +93,12 @@ bool LicenseKeys::GetAllowedUsage(const KeyId& key_id,
|
||||
CdmKeyAllowedUsage* allowed_usage) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->GetAllowedUsage(allowed_usage);
|
||||
} else if (content_keyid_to_entitlement_key_id_.count(key_id) > 0) {
|
||||
if (keys_.count(content_keyid_to_entitlement_key_id_[key_id]) > 0) {
|
||||
return keys_[content_keyid_to_entitlement_key_id_[key_id]]
|
||||
->CanDecryptContent();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -140,29 +155,46 @@ void LicenseKeys::ApplyConstraints(
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeys::SetFromLicense(
|
||||
const video_widevine::License& license) {
|
||||
void LicenseKeys::SetFromLicense(const video_widevine::License& license) {
|
||||
this->Clear();
|
||||
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const KeyContainer& key = license.key(key_index);
|
||||
if (key.has_id() && (key.type() == KeyContainer::CONTENT ||
|
||||
key.type() == KeyContainer::OPERATOR_SESSION)) {
|
||||
key.type() == KeyContainer::OPERATOR_SESSION ||
|
||||
key.type() == KeyContainer::ENTITLEMENT)) {
|
||||
const KeyId& key_id = key.id();
|
||||
keys_[key_id] = new LicenseKeyStatus(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
|
||||
is_content_key_(false),
|
||||
key_status_(kKeyStatusInternalError),
|
||||
meets_constraints_(true),
|
||||
default_hdcp_level_(HDCP_NONE) {
|
||||
void LicenseKeys::SetEntitledKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& keys) {
|
||||
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator key =
|
||||
keys.begin();
|
||||
key != keys.end(); key++) {
|
||||
// Check to see if we have an entitlement key for this content key.
|
||||
std::map<KeyId, LicenseKeyStatus*>::iterator entitlement =
|
||||
keys_.find(key->entitlement_key_id());
|
||||
if (entitlement == keys_.end()) {
|
||||
continue;
|
||||
}
|
||||
// And set the new content key id.
|
||||
content_keyid_to_entitlement_key_id_[key->key_id()] =
|
||||
key->entitlement_key_id();
|
||||
}
|
||||
}
|
||||
|
||||
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key)
|
||||
: is_content_key_(false),
|
||||
key_status_(kKeyStatusInternalError),
|
||||
meets_constraints_(true),
|
||||
default_hdcp_level_(HDCP_NONE) {
|
||||
allowed_usage_.Clear();
|
||||
constraints_.Clear();
|
||||
|
||||
if (key.type() == KeyContainer::CONTENT) {
|
||||
if (key.type() == KeyContainer::CONTENT ||
|
||||
key.type() == KeyContainer::ENTITLEMENT) {
|
||||
ParseContentKey(key);
|
||||
} else if (key.type() == KeyContainer::OPERATOR_SESSION) {
|
||||
ParseOperatorSessionKey(key);
|
||||
@@ -171,9 +203,8 @@ LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
|
||||
|
||||
void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) {
|
||||
is_content_key_ = true;
|
||||
if (key.has_level() &&
|
||||
((key.level() == KeyContainer::HW_SECURE_DECODE) ||
|
||||
(key.level() == KeyContainer::HW_SECURE_ALL))) {
|
||||
if (key.has_level() && ((key.level() == KeyContainer::HW_SECURE_DECODE) ||
|
||||
(key.level() == KeyContainer::HW_SECURE_ALL))) {
|
||||
allowed_usage_.decrypt_to_clear_buffer = false;
|
||||
allowed_usage_.decrypt_to_secure_buffer = true;
|
||||
} else {
|
||||
@@ -251,8 +282,7 @@ bool LicenseKeyStatus::CanDecryptContent() {
|
||||
}
|
||||
|
||||
bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) {
|
||||
if (NULL == allowed_usage)
|
||||
return false;
|
||||
if (NULL == allowed_usage) return false;
|
||||
*allowed_usage = allowed_usage_;
|
||||
return true;
|
||||
}
|
||||
@@ -288,7 +318,6 @@ bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
|
||||
// device's current HDCP level.
|
||||
void LicenseKeyStatus::ApplyConstraints(
|
||||
uint32_t video_pixels, CryptoSession::HdcpCapability new_hdcp_level) {
|
||||
|
||||
VideoResolutionConstraint* current_constraint = NULL;
|
||||
if (HasConstraints() && video_pixels != HDCP_UNSPECIFIED_VIDEO_RESOLUTION) {
|
||||
current_constraint = GetConstraintForRes(video_pixels, constraints_);
|
||||
|
||||
@@ -103,8 +103,11 @@ message License {
|
||||
CONTENT = 2;
|
||||
KEY_CONTROL = 3;
|
||||
OPERATOR_SESSION = 4;
|
||||
SUB_SESSION = 5;
|
||||
ENTITLEMENT = 6;
|
||||
// TODO(jfore): Drop subsession type once subsession support is removed
|
||||
// from the cdm. For now, SUB_SESSION is defined as type 6 so that it
|
||||
// is defined to satisfy the build.
|
||||
SUB_SESSION = 6;
|
||||
ENTITLEMENT = 5;
|
||||
}
|
||||
|
||||
// The SecurityLevel enumeration allows the server to communicate the level
|
||||
|
||||
@@ -178,6 +178,11 @@ void PolicyEngine::UpdateLicenseKeys(const video_widevine::License& license) {
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetEntitledLicenseKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& entitled_keys) {
|
||||
license_keys_->SetEntitledKeys(entitled_keys);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
license_id_.Clear();
|
||||
license_id_.CopyFrom(license.id());
|
||||
@@ -257,9 +262,7 @@ void PolicyEngine::BeginDecryption() {
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::DecryptionEvent() {
|
||||
last_playback_time_ = GetCurrentTime();
|
||||
}
|
||||
void PolicyEngine::DecryptionEvent() { last_playback_time_ = GetCurrentTime(); }
|
||||
|
||||
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
SetDeviceResolution(width, height);
|
||||
@@ -280,9 +283,8 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
}
|
||||
|
||||
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine::STREAMING
|
||||
? QUERY_VALUE_STREAMING
|
||||
: QUERY_VALUE_OFFLINE;
|
||||
license_id_.type() == video_widevine::STREAMING ? QUERY_VALUE_STREAMING
|
||||
: QUERY_VALUE_OFFLINE;
|
||||
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
|
||||
policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*query_response)[QUERY_KEY_PERSIST_ALLOWED] =
|
||||
@@ -294,7 +296,8 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
ss.str("");
|
||||
ss << GetPlaybackDurationRemaining(current_time);
|
||||
(*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
|
||||
(*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
|
||||
(*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] =
|
||||
policy_.renewal_server_url();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -419,10 +422,8 @@ int64_t PolicyEngine::GetRentalExpiryTime() {
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetExpiryTime(
|
||||
int64_t current_time,
|
||||
bool ignore_soft_enforce_playback_duration) {
|
||||
if (!HasPlaybackStarted(current_time))
|
||||
return GetRentalExpiryTime();
|
||||
int64_t current_time, bool ignore_soft_enforce_playback_duration) {
|
||||
if (!HasPlaybackStarted(current_time)) return GetRentalExpiryTime();
|
||||
|
||||
const int64_t hard_limit = GetHardLicenseExpiryTime();
|
||||
if (policy_.playback_duration_seconds() == 0) return hard_limit;
|
||||
@@ -433,8 +434,7 @@ int64_t PolicyEngine::GetExpiryTime(
|
||||
const int64_t expiry_time =
|
||||
playback_start_time_ + policy_.playback_duration_seconds();
|
||||
|
||||
if (hard_limit == NEVER_EXPIRES)
|
||||
return expiry_time;
|
||||
if (hard_limit == NEVER_EXPIRES) return expiry_time;
|
||||
return std::min(hard_limit, expiry_time);
|
||||
}
|
||||
|
||||
@@ -495,8 +495,8 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
if (new_status == kKeyStatusUsable) {
|
||||
CheckDeviceHdcpStatus();
|
||||
}
|
||||
keys_changed = license_keys_->ApplyStatusChange(new_status,
|
||||
&has_new_usable_key);
|
||||
keys_changed =
|
||||
license_keys_->ApplyStatusChange(new_status, &has_new_usable_key);
|
||||
if (event_listener_ && keys_changed) {
|
||||
CdmKeyStatusMap content_keys;
|
||||
license_keys_->ExtractKeyStatuses(&content_keys);
|
||||
|
||||
@@ -127,6 +127,20 @@ class LicenseKeysTest : public ::testing::Test {
|
||||
key->set_id(key_id);
|
||||
}
|
||||
|
||||
virtual void AddEntitlementKey(
|
||||
const KeyId& key_id, bool set_level = false,
|
||||
KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO,
|
||||
bool set_hdcp = false,
|
||||
KeyContainer::OutputProtection::HDCP hdcp_value =
|
||||
KeyContainer::OutputProtection::HDCP_NONE,
|
||||
bool set_constraints = false,
|
||||
std::vector<VideoResolutionConstraint>* constraints = NULL) {
|
||||
AddContentKey(key_id, set_level, level, set_hdcp, hdcp_value,
|
||||
set_constraints, constraints);
|
||||
license_.mutable_key(license_.key_size() - 1)
|
||||
->set_type(KeyContainer::ENTITLEMENT);
|
||||
}
|
||||
|
||||
virtual void AddOperatorSessionKey(
|
||||
const KeyId& key_id, bool set_perms = false,
|
||||
KeyFlag encrypt = kKeyFlagNull, KeyFlag decrypt = kKeyFlagNull,
|
||||
@@ -375,6 +389,26 @@ TEST_F(LicenseKeysTest, ContentKey) {
|
||||
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, EntitlementKey) {
|
||||
const KeyId e_key = "entitlement_key";
|
||||
const KeyId c_key = "content_key";
|
||||
AddEntitlementKey(e_key);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(e_key));
|
||||
|
||||
license_keys_.SetFromLicense(license_);
|
||||
// TODO(juce, rfrias): For simplicity entitlement keys are indicated as
|
||||
// content keys. It doesn't break anything, but CanDecryptContent returns true
|
||||
// for and entitlement key id.
|
||||
EXPECT_TRUE(license_keys_.IsContentKey(e_key));
|
||||
|
||||
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
|
||||
entitled_keys[0].set_entitlement_key_id(e_key);
|
||||
entitled_keys[0].set_key_id(c_key);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(c_key));
|
||||
license_keys_.SetEntitledKeys(entitled_keys);
|
||||
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, OperatorSessionKey) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
|
||||
@@ -387,13 +421,17 @@ TEST_F(LicenseKeysTest, OperatorSessionKey) {
|
||||
TEST_F(LicenseKeysTest, CanDecrypt) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId c_key = "content_key";
|
||||
const KeyId e_key = "entitlement_key";
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
|
||||
AddOperatorSessionKey(os_key);
|
||||
AddContentKey(c_key);
|
||||
AddEntitlementKey(e_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
|
||||
bool new_usable_keys = false;
|
||||
bool any_change = false;
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
@@ -409,15 +447,18 @@ TEST_F(LicenseKeysTest, CanDecrypt) {
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, AllowedUsageNull) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId c_key = "content_key";
|
||||
const KeyId sign_key = "signing_key";
|
||||
const KeyId e_key = "entitlement_key";
|
||||
AddOperatorSessionKey(os_key);
|
||||
AddContentKey(c_key);
|
||||
AddSigningKey(sign_key);
|
||||
AddEntitlementKey(e_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
CdmKeyAllowedUsage usage_1;
|
||||
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &usage_1));
|
||||
@@ -431,6 +472,11 @@ TEST_F(LicenseKeysTest, AllowedUsageNull) {
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3));
|
||||
ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse,
|
||||
kKeySecurityLevelUnset);
|
||||
|
||||
CdmKeyAllowedUsage usage_4;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_4));
|
||||
ExpectAllowedUsageContent(usage_4, kContentClearFalse, kContentSecureFalse,
|
||||
kKeySecurityLevelUnset);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, AllowedUsageContent) {
|
||||
|
||||
@@ -38,6 +38,7 @@ const int64_t kHighDuration =
|
||||
const char* kRenewalServerUrl =
|
||||
"https://test.google.com/license/GetCencLicense";
|
||||
const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee";
|
||||
const KeyId kEntitlementKeyId = "entitlementkeyid";
|
||||
const KeyId kAnotherKeyId = "another_key_id";
|
||||
const KeyId kSomeRandomKeyId = "some_random_key_id";
|
||||
const KeyId kUnknownKeyId = "some_random_unknown_key_id";
|
||||
@@ -157,6 +158,16 @@ class PolicyEngineTest : public ::testing::Test {
|
||||
expected_has_new_usable_key));
|
||||
}
|
||||
|
||||
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
|
||||
bool expected_has_new_usable_key,
|
||||
KeyId expected_keyid) {
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnSessionKeysChange(
|
||||
kSessionId, UnorderedElementsAre(
|
||||
Pair(expected_keyid, expected_key_status)),
|
||||
expected_has_new_usable_key));
|
||||
}
|
||||
|
||||
void ExpectSessionKeysChange(CdmKeyStatus expected_key1_status,
|
||||
CdmKeyStatus expected_key2_status,
|
||||
bool expected_has_new_usable_key) {
|
||||
@@ -206,6 +217,73 @@ TEST_F(PolicyEngineTest, PlaybackSuccess_OfflineLicense) {
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackSuccess_EntitlementLicense) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(kLicenseStartTime + 1))
|
||||
.WillOnce(Return(kPlaybackStartTime))
|
||||
.WillOnce(Return(kLicenseStartTime + 10));
|
||||
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true, kEntitlementKeyId);
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration));
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
|
||||
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
|
||||
Return(true)));
|
||||
|
||||
License::KeyContainer* key = license_.mutable_key(0);
|
||||
key->set_type(License::KeyContainer::ENTITLEMENT);
|
||||
key->set_id(kEntitlementKeyId);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->BeginDecryption();
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
|
||||
|
||||
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
|
||||
entitled_keys[0].set_entitlement_key_id(kEntitlementKeyId);
|
||||
entitled_keys[0].set_key_id(kKeyId);
|
||||
policy_engine_->SetEntitledLicenseKeys(entitled_keys);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackSuccess_EntitlementLicenseExpiration) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(kLicenseStartTime + 1))
|
||||
.WillOnce(Return(kPlaybackStartTime))
|
||||
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10))
|
||||
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 11));
|
||||
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true, kEntitlementKeyId);
|
||||
ExpectSessionKeysChange(kKeyStatusExpired, false, kEntitlementKeyId);
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration));
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
|
||||
|
||||
License::KeyContainer* key = license_.mutable_key(0);
|
||||
key->set_type(License::KeyContainer::ENTITLEMENT);
|
||||
key->set_id(kEntitlementKeyId);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->BeginDecryption();
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
|
||||
|
||||
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
|
||||
entitled_keys[0].set_entitlement_key_id(kEntitlementKeyId);
|
||||
entitled_keys[0].set_key_id(kKeyId);
|
||||
policy_engine_->SetEntitledLicenseKeys(entitled_keys);
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackSuccess_StreamingLicense) {
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_license_duration_seconds(kLowDuration);
|
||||
|
||||
Reference in New Issue
Block a user