Add License::Policy::play_start_grace_period_seconds

[ Merge of http://go/wvgerrit/22565 ]

When using the grace period, the CDM will need to override the values
given to use by the TEE (through OEMCrypto).  Normally the first (and
last) decrypt times are stored securely by the TEE.  To avoid extra
complexity in OEMCrypto, we will simply ignore the values given to us
by the TEE when using this feature.

However, the TEE will still enforce the (hard) license duration.  So
only the rental/playback durations will be affected by malicious
editing of files.

b/34211676

Test: Reran unittests including newly added tests. All tests other than
some oemcrypto, request_license_test passed. Those tests failed with
or without this CL.

Change-Id: I6d7b5bfb669fd8603b474b68c2f7175b0c30901d
This commit is contained in:
Rahul Frias
2017-01-16 18:17:01 -08:00
parent b492f7b73b
commit 826c91ba26
10 changed files with 252 additions and 56 deletions

View File

@@ -129,12 +129,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
int64_t grace_period_end_time;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &app_parameters_)) {
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
return GET_LICENSE_ERROR;
@@ -156,7 +158,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time)) {
playback_start_time, last_playback_time, grace_period_end_time)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
@@ -568,7 +570,8 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(), app_parameters_);
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_);
}
CdmResponseType CdmSession::ReleaseCrypto() {

View File

@@ -168,7 +168,8 @@ bool DeviceFiles::StoreLicense(
const CdmKeyMessage& license_renewal_request,
const CdmKeyResponse& license_renewal,
const std::string& release_server_url, int64_t playback_start_time,
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
int64_t last_playback_time, int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -201,6 +202,7 @@ bool DeviceFiles::StoreLicense(
license->set_release_server_url(release_server_url);
license->set_playback_start_time(playback_start_time);
license->set_last_playback_time(last_playback_time);
license->set_grace_period_end_time(grace_period_end_time);
NameValue* app_params;
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
iter != app_parameters.end(); ++iter) {
@@ -221,7 +223,8 @@ bool DeviceFiles::RetrieveLicense(
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
std::string* release_server_url, int64_t* playback_start_time,
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
int64_t* last_playback_time, int64_t* grace_period_end_time,
CdmAppParameterMap* app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -270,6 +273,7 @@ bool DeviceFiles::RetrieveLicense(
*release_server_url = license.release_server_url();
*playback_start_time = license.playback_start_time();
*last_playback_time = license.last_playback_time();
*grace_period_end_time = license.grace_period_end_time();
for (int i = 0; i < license.app_parameters_size(); ++i) {
(*app_parameters)[license.app_parameters(i).name()] =
license.app_parameters(i).value();

View File

@@ -39,6 +39,10 @@ message License {
optional int64 playback_start_time = 8 [default = 0];
optional int64 last_playback_time = 9 [default = 0];
repeated NameValue app_parameters = 10;
// This will be 0/missing if the grace period has not expired; otherwise it
// contains the playback_start_time we should use as an override. This is
// ignored if there is no grace period.
optional int64 grace_period_end_time = 11 [default = 0];
}
message UsageInfo {

View File

@@ -674,7 +674,7 @@ bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response, int64_t playback_start_time,
int64_t last_playback_time) {
int64_t last_playback_time, int64_t grace_period_end_time) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -742,7 +742,8 @@ bool CdmLicense::RestoreOfflineLicense(
}
}
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time);
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time,
grace_period_end_time);
return true;
}

View File

@@ -31,6 +31,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
playback_start_time_(0),
last_playback_time_(0),
last_expiry_time_(0),
grace_period_end_time_(0),
last_expiry_time_set_(false),
was_expired_on_load_(false),
next_renewal_time_(0),
@@ -78,6 +79,12 @@ void PolicyEngine::CheckDevice(int64_t current_time) {
void PolicyEngine::OnTimerEvent() {
int64_t current_time = clock_->GetCurrentTime();
// If we have passed the grace period, the expiration will update.
if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) {
grace_period_end_time_ = playback_start_time_;
NotifyExpirationUpdate(current_time);
}
// License expiration trumps all.
if (HasLicenseOrPlaybackDurationExpired(current_time) &&
license_state_ != kLicenseStateExpired) {
@@ -195,7 +202,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
license_state_ = kLicenseStatePending;
NotifyKeysChange(kKeyStatusPending);
}
NotifyExpirationUpdate();
NotifyExpirationUpdate(current_time);
}
void PolicyEngine::BeginDecryption() {
@@ -206,11 +213,13 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = clock_->GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.play_start_grace_period_seconds() == 0)
grace_period_end_time_ = playback_start_time_;
if (policy_.renew_with_usage()) {
license_state_ = kLicenseStateNeedRenewal;
}
NotifyExpirationUpdate();
NotifyExpirationUpdate(playback_start_time_);
break;
case kLicenseStateInitial:
case kLicenseStatePending:
@@ -290,26 +299,38 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
}
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
int64_t current_time = clock_->GetCurrentTime();
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(/* ignore_soft_enforce_playback_duration */ false);
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
if (expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (expiry_time < current_time) return 0;
return expiry_time - current_time;
}
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time) {
int64_t last_playback_time,
int64_t grace_period_end_time) {
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
grace_period_end_time_ = grace_period_end_time;
if (policy_.play_start_grace_period_seconds() != 0) {
// If we are using grace period, we may need to override some of the values
// given to us by OEMCrypto. |grace_period_end_time| will be 0 if the grace
// period has not expired (effectively playback has not begun). Otherwise,
// |grace_period_end_time| contains the playback start time we should use.
playback_start_time_ = grace_period_end_time;
}
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(/* ignore_soft_enforce_playback_duration */ true);
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ true);
was_expired_on_load_ =
expiry_time != NEVER_EXPIRES && expiry_time < current_time;
NotifyExpirationUpdate();
NotifyExpirationUpdate(current_time);
}
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
@@ -319,8 +340,9 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) {
const int64_t expiry_time =
GetExpiryTime(/* ignore_soft_enforce_playback_duration */ false);
return (expiry_time != NEVER_EXPIRES) && expiry_time <= current_time;
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
return expiry_time != NEVER_EXPIRES && expiry_time <= current_time;
}
// For the policy time fields checked in the following methods, a value of 0
@@ -343,8 +365,9 @@ int64_t PolicyEngine::GetRentalExpiryTime() {
}
int64_t PolicyEngine::GetExpiryTime(
int64_t current_time,
bool ignore_soft_enforce_playback_duration) {
if (!HasPlaybackStarted())
if (!HasPlaybackStarted(current_time))
return GetRentalExpiryTime();
const int64_t hard_limit = GetHardLicenseExpiryTime();
@@ -418,9 +441,10 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
}
}
void PolicyEngine::NotifyExpirationUpdate() {
void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
const int64_t expiry_time =
GetExpiryTime(/* ignore_soft_enforce_playback_duration */ false);
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
last_expiry_time_ = expiry_time;
if (event_listener_)