From 1f32bef75bcedbee2bf70a2e4e1e187610d4c683 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 11 Sep 2017 00:00:57 -0700 Subject: [PATCH] Clock consistency checks [ Merge from http://go/wvgerrit/33440 ] Introduce checks to guard against clock tampering. Bug: 62037413 Test: WV unit, integration tests on angler Test: Manual clock rollback test Test: Playback testing (Netflix, Play movies) on sailfish Test: GTS test on angler Change-Id: I47938109adb5c0f5e9aefc58eb5dac156b9f16ef --- .../cdm/core/include/policy_engine.h | 6 ++++ libwvdrmengine/cdm/core/src/policy_engine.cpp | 31 +++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index f16c0e18..7f99cb15 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -160,6 +160,9 @@ class PolicyEngine { // expiry time changes. void NotifyExpirationUpdate(int64_t current_time); + // Guard against clock rollbacks + int64_t GetCurrentTime(); + // set_clock() is for testing only. It alters ownership of the // passed-in pointer. void set_clock(Clock* clock); @@ -191,6 +194,9 @@ class PolicyEngine { // calculate the time where renewal retries should occur. int64_t next_renewal_time_; + // to assist in clock rollback checks + int64_t last_recorded_current_time_; + // Used to dispatch CDM events. CdmSessionId session_id_; WvCdmEventListener* event_listener_; diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index c933605a..b1d2d846 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -16,6 +16,8 @@ using video_widevine::License; namespace { +const int kCdmPolicyTimerDurationSeconds = 1; +const int kClockSkewDelta = 5; // seconds const int64_t kHdcpCheckInterval = 10; } // namespace @@ -34,6 +36,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, last_expiry_time_set_(false), was_expired_on_load_(false), next_renewal_time_(0), + last_recorded_current_time_(0), session_id_(session_id), event_listener_(event_listener), license_keys_(new LicenseKeys), @@ -84,7 +87,8 @@ void PolicyEngine::CheckDevice(int64_t current_time) { } void PolicyEngine::OnTimerEvent() { - int64_t current_time = clock_->GetCurrentTime(); + last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds; + int64_t current_time = GetCurrentTime(); // If we have passed the grace period, the expiration will update. if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) { @@ -191,7 +195,7 @@ void PolicyEngine::UpdateLicense(const License& license) { license_start_time_ = license.license_start_time(); next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds(); - int64_t current_time = clock_->GetCurrentTime(); + int64_t current_time = GetCurrentTime(); if (!policy_.can_play() || HasLicenseOrPlaybackDurationExpired(current_time)) { license_state_ = kLicenseStateExpired; @@ -216,7 +220,7 @@ void PolicyEngine::BeginDecryption() { case kLicenseStateCanPlay: case kLicenseStateNeedRenewal: case kLicenseStateWaitingLicenseUpdate: - playback_start_time_ = clock_->GetCurrentTime(); + playback_start_time_ = GetCurrentTime(); last_playback_time_ = playback_start_time_; if (policy_.play_start_grace_period_seconds() == 0) grace_period_end_time_ = playback_start_time_; @@ -236,7 +240,7 @@ void PolicyEngine::BeginDecryption() { } void PolicyEngine::DecryptionEvent() { - last_playback_time_ = clock_->GetCurrentTime(); + last_playback_time_ = GetCurrentTime(); } void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) { @@ -250,7 +254,7 @@ void PolicyEngine::NotifySessionExpiration() { CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { std::stringstream ss; - int64_t current_time = clock_->GetCurrentTime(); + int64_t current_time = GetCurrentTime(); if (license_state_ == kLicenseStateInitial) { query_response->clear(); @@ -291,7 +295,7 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage( bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) { if (playback_start_time_ == 0) return false; - *seconds_since_started = clock_->GetCurrentTime() - playback_start_time_; + *seconds_since_started = GetCurrentTime() - playback_start_time_; return (*seconds_since_started >= 0) ? true : false; } @@ -299,12 +303,12 @@ bool PolicyEngine::GetSecondsSinceLastPlayed( int64_t* seconds_since_last_played) { if (last_playback_time_ == 0) return false; - *seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_; + *seconds_since_last_played = GetCurrentTime() - last_playback_time_; return (*seconds_since_last_played >= 0) ? true : false; } int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() { - const int64_t current_time = clock_->GetCurrentTime(); + const int64_t current_time = GetCurrentTime(); const int64_t expiry_time = GetExpiryTime(current_time, /* ignore_soft_enforce_playback_duration */ false); @@ -328,7 +332,7 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time, playback_start_time_ = grace_period_end_time; } - const int64_t current_time = clock_->GetCurrentTime(); + const int64_t current_time = GetCurrentTime(); const int64_t expiry_time = GetExpiryTime(current_time, /* ignore_soft_enforce_playback_duration */ true); @@ -466,6 +470,15 @@ void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) { last_expiry_time_set_ = true; } +int64_t PolicyEngine::GetCurrentTime() { + int64_t current_time = clock_->GetCurrentTime(); + if (current_time + kClockSkewDelta < last_recorded_current_time_) + current_time = last_recorded_current_time_; + else + last_recorded_current_time_ = current_time; + return current_time; +} + void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); } } // namespace wvcdm