Allow license renewals after expiry

Licenses could be renewed uptil the point of expiry. After that point
we expected that the session would have to be closed and a new one
opened with a new license loaded. Clank requested that we support
renewal of sessions past expiry.

In addition, the error returned on decryption, if OEMCrypto
determines that the KCB duration has expired, is NEED_KEY rather than
KEY_ERROR.

Merge of https://widevine-internal-review.googlesource.com/#/c/8240
from the widevine cdm repo.

b/11390539

Change-Id: I023320f3f25514cd07b368701a92100429ce1c04
This commit is contained in:
Rahul Frias
2013-10-31 12:07:28 -07:00
parent 49e593d127
commit 774a078f1d
5 changed files with 165 additions and 86 deletions

View File

@@ -53,6 +53,9 @@ class PolicyEngine {
return license_id_; return license_id_;
} }
bool IsLicenseDurationExpired(int64_t current_time);
bool IsPlaybackDurationExpired(int64_t current_time);
private: private:
typedef enum { typedef enum {
kLicenseStateInitial, kLicenseStateInitial,
@@ -65,8 +68,6 @@ class PolicyEngine {
void Init(Clock* clock); void Init(Clock* clock);
bool IsLicenseDurationExpired(int64_t current_time);
bool IsPlaybackDurationExpired(int64_t current_time);
bool IsRenewalDelayExpired(int64_t current_time); bool IsRenewalDelayExpired(int64_t current_time);
bool IsRenewalRecoveryDurationExpired(int64_t current_time); bool IsRenewalRecoveryDurationExpired(int64_t current_time);
bool IsRenewalRetryIntervalExpired(int64_t current_time); bool IsRenewalRetryIntervalExpired(int64_t current_time);

View File

@@ -272,7 +272,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
return crypto_session_->Decrypt(params); CdmResponseType status = crypto_session_->Decrypt(params);
// TODO(rfrias): Remove after support for OEMCrypto_ERROR_KEY_EXPIRED is in
if (UNKNOWN_ERROR == status) {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
policy_engine_.IsPlaybackDurationExpired(current_time)) {
return NEED_KEY;
}
}
return status;
} }
// License renewal // License renewal

13
libwvdrmengine/cdm/core/src/crypto_session.cpp Executable file → Normal file
View File

@@ -588,10 +588,15 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
params.is_encrypted, &(*params.iv).front(), params.block_offset, params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, params.subsample_flags); &buffer_descriptor, params.subsample_flags);
if (OEMCrypto_ERROR_INSUFFICIENT_RESOURCES == sts) { switch (sts) {
return INSUFFICIENT_CRYPTO_RESOURCES; case OEMCrypto_SUCCESS:
} else if (OEMCrypto_SUCCESS != sts) { break;
return UNKNOWN_ERROR; case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_KEY_EXPIRED:
return NEED_KEY;
default:
return UNKNOWN_ERROR;
} }
return NO_ERROR; return NO_ERROR;
} }

View File

@@ -106,95 +106,82 @@ void PolicyEngine::SetLicense(
void PolicyEngine::UpdateLicense( void PolicyEngine::UpdateLicense(
const video_widevine_server::sdk::License& license) { const video_widevine_server::sdk::License& license) {
if (!license.has_policy() || kLicenseStateExpired == license_state_) if (!license.has_policy())
return; return;
policy_.MergeFrom(license.policy()); policy_.MergeFrom(license.policy());
switch (license_state_) { if (!policy_.can_play()) {
case kLicenseStateExpired: license_state_ = kLicenseStateExpired;
// Ignore policy updates. return;
}
// some basic license validation
if (license_state_ == kLicenseStateInitial) {
// license start time needs to be present in the initial response
if (!license.has_license_start_time())
return; return;
}
else {
// TODO(edwingwong, rfrias): Check back with Thomas and see if
// we need to enforce that all duration windows are absent if
// license_start_time is not present. This is a TBD.
case kLicenseStateInitial: // if renewal, discard license if version has not been updated
case kLicenseStateInitialPendingUsage: if (license.id().version() > license_id_.version())
case kLicenseStateCanPlay: license_id_.CopyFrom(license.id());
case kLicenseStateNeedRenewal: else
case kLicenseStateWaitingLicenseUpdate: return;
if (!policy_.can_play()) { }
license_state_ = kLicenseStateExpired;
return;
}
// some basic license validation // Update time information
if (license_state_ == kLicenseStateInitial) { int64_t current_time = clock_->GetCurrentTime();
// license start time needs to be present in the initial response // TODO(edwingwong, rfrias): Check back with Thomas and see if
if (!license.has_license_start_time()) // we need to enforce that all duration windows are absent if
return; // license_start_time is not present. This is a TBD.
} if (license.has_license_start_time())
else { license_start_time_ = license.license_start_time();
// TODO(edwingwong, rfrias): Check back with Thomas and see if license_received_time_ = current_time;
// we need to enforce that all duration windows are absent if next_renewal_time_ = current_time +
// license_start_time is not present. This is a TBD. policy_.renewal_delay_seconds();
// if renewal, discard license if version has not been updated // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
if (license.id().version() > license_id_.version()) // will be set to the minimum of the following policies :
license_id_.CopyFrom(license.id()); // rental_duration_seconds and license_duration_seconds.
else // The value is used to determine when the license expires.
return; policy_max_duration_seconds_ = 0;
}
// Update time information if (policy_.has_rental_duration_seconds())
int64_t current_time = clock_->GetCurrentTime(); policy_max_duration_seconds_ = policy_.rental_duration_seconds();
// TODO(edwingwong, rfrias): Check back with Thomas and see if
// we need to enforce that all duration windows are absent if
// license_start_time is not present. This is a TBD.
if (license.has_license_start_time())
license_start_time_ = license.license_start_time();
license_received_time_ = current_time;
next_renewal_time_ = current_time +
policy_.renewal_delay_seconds();
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ if ((policy_.license_duration_seconds() > 0) &&
// will be set to the minimum of the following policies : ((policy_.license_duration_seconds() <
// rental_duration_seconds and license_duration_seconds. policy_max_duration_seconds_) ||
// The value is used to determine when the license expires. policy_max_duration_seconds_ == 0)) {
policy_max_duration_seconds_ = 0; policy_max_duration_seconds_ = policy_.license_duration_seconds();
}
if (policy_.has_rental_duration_seconds()) if (Properties::begin_license_usage_when_received())
policy_max_duration_seconds_ = policy_.rental_duration_seconds(); playback_start_time_ = current_time;
if ((policy_.license_duration_seconds() > 0) && // Update state
((policy_.license_duration_seconds() < if (Properties::begin_license_usage_when_received()) {
policy_max_duration_seconds_) || if (policy_.renew_with_usage()) {
policy_max_duration_seconds_ == 0)) { license_state_ = kLicenseStateNeedRenewal;
policy_max_duration_seconds_ = policy_.license_duration_seconds(); }
} else {
license_state_ = kLicenseStateCanPlay;
if (Properties::begin_license_usage_when_received()) can_decrypt_ = true;
playback_start_time_ = current_time; }
}
// Update state else {
if (Properties::begin_license_usage_when_received()) { if (license_state_ == kLicenseStateInitial) {
if (policy_.renew_with_usage()) { license_state_ = kLicenseStateInitialPendingUsage;
license_state_ = kLicenseStateNeedRenewal; }
} else {
else { license_state_ = kLicenseStateCanPlay;
license_state_ = kLicenseStateCanPlay; can_decrypt_ = true;
can_decrypt_ = true; }
}
}
else {
if (license_state_ == kLicenseStateInitial) {
license_state_ = kLicenseStateInitialPendingUsage;
}
else {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
}
}
break;
} }
} }

View File

@@ -438,7 +438,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
EXPECT_FALSE(policy_engine_->can_decrypt()); EXPECT_FALSE(policy_engine_->can_decrypt());
} }
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) { TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()) EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ - .WillOnce(Return(license_start_time_ + license_duration_ -
@@ -496,7 +496,83 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
EXPECT_FALSE(policy_engine_->can_decrypt()); EXPECT_FALSE(policy_engine_->can_decrypt());
} }
TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) { TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
.WillOnce(Return(license_start_time_ + license_duration_ + 10))
.WillOnce(Return(license_start_time_ + license_duration_ + 30))
.WillOnce(Return(license_start_time_ + license_duration_ + 40));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
license_.set_license_start_time(license_start_time_ +
license_duration_ + 20);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_playback_duration_seconds(playback_duration_ + 100);
policy->set_license_duration_seconds(license_duration_ + 100);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()) EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ - .WillOnce(Return(license_start_time_ + license_duration_ -