From 826c91ba268e2b2429e4408857eb618cd4ea99ac Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 16 Jan 2017 18:17:01 -0800 Subject: [PATCH] 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 --- .../cdm/core/include/device_files.h | 3 +- libwvdrmengine/cdm/core/include/license.h | 3 +- .../cdm/core/include/policy_engine.h | 18 ++- libwvdrmengine/cdm/core/src/cdm_session.cpp | 9 +- libwvdrmengine/cdm/core/src/device_files.cpp | 8 +- .../cdm/core/src/device_files.proto | 4 + libwvdrmengine/cdm/core/src/license.cpp | 5 +- libwvdrmengine/cdm/core/src/policy_engine.cpp | 48 ++++-- .../cdm/core/test/device_files_unittest.cpp | 70 +++++---- .../cdm/core/test/policy_engine_unittest.cpp | 140 +++++++++++++++++- 10 files changed, 252 insertions(+), 56 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 6eef4ee7..40ae4d2b 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -53,6 +53,7 @@ class DeviceFiles { const std::string& release_server_url, int64_t playback_start_time, int64_t last_playback_time, + int64_t grace_period_end_time, const CdmAppParameterMap& app_parameters); virtual bool RetrieveLicense( const std::string& key_set_id, LicenseState* state, @@ -60,7 +61,7 @@ class DeviceFiles { CdmKeyResponse* key_response, CdmKeyMessage* key_renewal_request, CdmKeyResponse* key_renewal_response, std::string* release_server_url, int64_t* playback_start_time, int64_t* last_playback_time, - CdmAppParameterMap* app_parameters); + int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters); virtual bool DeleteLicense(const std::string& key_set_id); virtual bool DeleteAllFiles(); virtual bool DeleteAllLicenses(); diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 6166dee7..486ba8f0 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -44,7 +44,8 @@ class CdmLicense { 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 playback_start_time, int64_t last_playback_time, + int64_t grace_period_end_time); virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response); virtual bool HasInitData() { return stored_init_data_.get(); } diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index b24a72a6..6cd9aad1 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -80,11 +80,19 @@ class PolicyEngine { // for offline save and restore int64_t GetPlaybackStartTime() { return playback_start_time_; } int64_t GetLastPlaybackTime() { return last_playback_time_; } + int64_t GetGracePeriodEndTime() { return grace_period_end_time_; } void RestorePlaybackTimes(int64_t playback_start_time, - int64_t last_playback_time); + int64_t last_playback_time, + int64_t grace_period_end_time); bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; } - bool HasPlaybackStarted() { return playback_start_time_ > 0; } + bool HasPlaybackStarted(int64_t current_time) { + 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(); + } bool HasLicenseOrPlaybackDurationExpired(int64_t current_time); int64_t GetLicenseOrPlaybackDurationRemaining(); @@ -119,7 +127,8 @@ class PolicyEngine { int64_t GetRentalExpiryTime(); // Gets the clock time that the license expires based on whether we have // started playing. This takes into account GetHardLicenseExpiryTime. - int64_t GetExpiryTime(bool ignore_soft_enforce_playback_duration); + int64_t GetExpiryTime(int64_t current_time, + bool ignore_soft_enforce_playback_duration); int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time); int64_t GetPlaybackDurationRemaining(int64_t current_time); @@ -136,7 +145,7 @@ class PolicyEngine { // Notifies updates in expiry time and fire OnExpirationUpdate event if // expiry time changes. - void NotifyExpirationUpdate(); + void NotifyExpirationUpdate(int64_t current_time); // set_clock() is for testing only. It alters ownership of the // passed-in pointer. @@ -160,6 +169,7 @@ class PolicyEngine { int64_t playback_start_time_; int64_t last_playback_time_; int64_t last_expiry_time_; + int64_t grace_period_end_time_; bool last_expiry_time_set_; bool was_expired_on_load_; diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index d176ec19..0c2bf2a2 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -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() { diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 76a64148..7f760886 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -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(); diff --git a/libwvdrmengine/cdm/core/src/device_files.proto b/libwvdrmengine/cdm/core/src/device_files.proto index 89bd0c5e..eef367d6 100644 --- a/libwvdrmengine/cdm/core/src/device_files.proto +++ b/libwvdrmengine/cdm/core/src/device_files.proto @@ -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 { diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index e2afa55f..42bc7fcf 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -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; } diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 48bc6ad5..d4633b3b 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -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_) diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index dacf0882..4b08a553 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -118,6 +118,7 @@ struct LicenseInfo { std::string key_release_url; int64_t playback_start_time; int64_t last_playback_time; + int64_t grace_period_end_time; std::string app_parameters; std::string file_data; }; @@ -225,9 +226,9 @@ LicenseInfo license_test_data[] = { "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "BEA6CABACA1C2C"), - "https://test.google.com/license/GetCencLicense", 0x0, 0x0, "", + "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", a2bs_hex( - "0AA8150802100122A1150801121408011210303132333435363738394142434445461" + "0AAA150802100122A3150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -306,8 +307,8 @@ LicenseInfo license_test_data[] = { "106B63746C0000000000ECDCBE0000000020DBDFA68F051A20182F029E35047A3841F" "A176C74E5B387350E8D58DEA6878FF0BEA6CABACA1C2C3A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "365400048001220CD0599C2B85D9F2D573AC7893CE77CB5A10B326828BA8C89047505" - "A8C9B606AC")}, + "365400048005800122066F4FFF1CB2C0F978149A9402F3E4FF8D49B19635E646A0678" + "71AA08E7A8FDC3")}, // license 1 {"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing, @@ -406,9 +407,9 @@ LicenseInfo license_test_data[] = { "A68F051A20BDA6A56F7CBFD0942198F87C23A34AA5CBD64AFEB134277774" "CCF8E789D815DD"), "https://test.google.com/license/GetCencLicense", 0x12345678, 0x12348765, - "Name1 Value1", + 0x0, "Name1 Value1", a2bs_hex( - "0AC1150802100122BA150802121408011210303132333435363738394142434445461" + "0AC3150802100122BC150802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -487,8 +488,8 @@ LicenseInfo license_test_data[] = { "106B63746C00000000CA3A6A75000000002083E5A68F051A20BDA6A56F7CBFD094219" "8F87C23A34AA5CBD64AFEB134277774CCF8E789D815DD3A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C7565311220BE69" - "AAB25B481BCAF57B741518D9F9DB8E3A7A6911D10C53D4F4649D78393C65")}, + "36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C75653158001220" + "9C0315FC1812C6A0E5936E36D04ECE2FA56AF4AB544ECDF3C9135D54B4A26167")}, // license 2 {"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing, @@ -587,9 +588,9 @@ LicenseInfo license_test_data[] = { "A68F051A2041EF0A9267D613D17AA90E1D1DA5BE091860E5E296D41D6D0F" "75E73660C279B3"), "https://test.google.com/license/GetCencLicense", 0x0123456789abcdef, - 0x123456789abfedc, "Name1 Value1 Name2 Param2", + 0x123456789abfedc, 0x0, "Name1 Value1 Name2 Param2", a2bs_hex( - "0AE7150802100122E0150802121408011210303132333435363738394142434445461" + "0AE9150802100122E2150802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -669,9 +670,9 @@ LicenseInfo license_test_data[] = { "90E1D1DA5BE091860E5E296D41D6D0F75E73660C279B33A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910152150A054E616D6531120C5661" - "6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D321" - "2203653BA57F16FE28D66D9F7A76128B7AD7F33680815FF70A3684617DE1FBB0F9" - "F")}}; + "6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D325" + "8001220616E6AC4AF6EB4F7147E98CF7302425E2390B293BBC01F9F8E89B49F653EA3" + "45")}}; // Sample license data and related data for storage and use for offline // playback. The license data and URLs in this test are not real. @@ -774,9 +775,9 @@ LicenseInfo license_update_test_data[] = { "B68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0BD30B" "08B2C467355129"), "https://test.google.com/license/GetCencLicense", 0x0123456789abcdef, - 0x123456789abfedc, "Name1 Value1 Name2 Value2 Name3 Value3", + 0x123456789abfedc, 0x0, "Name1 Value1 Name2 Value2 Name3 Value3", a2bs_hex( - "0AB8150802100122B1150801121408011210303132333435363738394142434445461" + "0ABA150802100122B3150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82" "6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD" @@ -855,13 +856,14 @@ LicenseInfo license_update_test_data[] = { "106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6" "977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191011220C7ACA00F6877DAAE2E8F50" - "126C3222C2E584A50D08EFA75BC4FC091E7034E1DD")}, + "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122051F15CDA5B9414919D" + "B67769A781CC4F43138D314DAFFCBFBD620E53167E4AF2")}, // license being released. all fields are identical except for license // state and hashed file data - {"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, "", + {"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, 0, + "", a2bs_hex( - "0AB8150802100122B1150802121408011210303132333435363738394142434445461" + "0ABA150802100122B3150802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82" "6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD" @@ -940,8 +942,8 @@ LicenseInfo license_update_test_data[] = { "106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6" "977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910112203D8933A735A22FE27AA956" - "802B597529E8FFCB91A5F3CBBB3CE0C38E4AF3DC88")}}; + "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122093FDE0D42BC60D3932" + "02E0D5A49775E08093BF01560EF72C298321E921716E24")}}; // Application parameters were added to the License message. This data // is used to verify that a License saved without application parameters can @@ -1043,7 +1045,7 @@ LicenseInfo license_app_parameters_backwards_compatibility_test_data = { "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "BEA6CABACA1C2C"), - "https://test.google.com/license/GetCencLicense", 0x0, 0x0, "", + "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", a2bs_hex( "0AA8150802100122A1150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" @@ -1489,7 +1491,7 @@ class DeviceFilesTest : public ::testing::Test { return sizeof(DeviceFiles::LicenseState) + data.pssh_data.size() + data.key_request.size() + data.key_response.size() + data.key_renewal_request.size() + data.key_renewal_response.size() + - data.key_release_url.size() + 2 * sizeof(int64_t); + data.key_release_url.size() + 3 * sizeof(int64_t); } CdmAppParameterMap GetAppParameters(std::string str) { @@ -1747,7 +1749,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) { license_test_data[license_num].key_renewal_response, license_test_data[license_num].key_release_url, license_test_data[license_num].playback_start_time, - license_test_data[license_num].last_playback_time, app_parameters)); + license_test_data[license_num].last_playback_time, + license_test_data[license_num].grace_period_end_time, app_parameters)); } INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool()); @@ -1794,7 +1797,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) { license_test_data[i].key_renewal_response, license_test_data[i].key_release_url, license_test_data[i].playback_start_time, - license_test_data[i].last_playback_time, app_parameters)); + license_test_data[i].last_playback_time, + license_test_data[i].grace_period_end_time, app_parameters)); } } @@ -1832,6 +1836,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { CdmKeyMessage key_renewal_request; CdmKeyResponse key_renewal_response; int64_t playback_start_time, last_playback_time; + int64_t grace_period_end_time; std::string release_server_url; CdmAppParameterMap app_parameters; @@ -1841,7 +1846,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { license_test_data[i].key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, - &last_playback_time, &app_parameters)); + &last_playback_time, &grace_period_end_time, &app_parameters)); EXPECT_EQ(license_test_data[i].license_state, license_state); EXPECT_EQ(license_test_data[i].pssh_data, pssh_data); EXPECT_EQ(license_test_data[i].key_request, key_request); @@ -1850,6 +1855,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { EXPECT_EQ(license_test_data[i].key_response, key_response); EXPECT_EQ(license_test_data[i].playback_start_time, playback_start_time); EXPECT_EQ(license_test_data[i].last_playback_time, last_playback_time); + EXPECT_EQ(license_test_data[i].grace_period_end_time, + grace_period_end_time); std::map::iterator itr; for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) { @@ -1894,6 +1901,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { CdmKeyMessage key_renewal_request; CdmKeyResponse key_renewal_response; int64_t playback_start_time, last_playback_time; + int64_t grace_period_end_time; std::string release_server_url; CdmAppParameterMap app_parameters; @@ -1901,7 +1909,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { test_data->key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, &last_playback_time, - &app_parameters)); + &grace_period_end_time, &app_parameters)); EXPECT_EQ(test_data->license_state, license_state); EXPECT_EQ(test_data->pssh_data, pssh_data); EXPECT_EQ(test_data->key_request, key_request); @@ -1910,6 +1918,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { EXPECT_EQ(test_data->key_response, key_response); EXPECT_EQ(test_data->playback_start_time, playback_start_time); EXPECT_EQ(test_data->last_playback_time, last_playback_time); + EXPECT_EQ(test_data->grace_period_end_time, grace_period_end_time); EXPECT_EQ(0u, app_parameters.size()); } @@ -1945,6 +1954,7 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { license_update_test_data[0].key_release_url, license_update_test_data[0].playback_start_time, license_update_test_data[0].last_playback_time, + license_update_test_data[0].grace_period_end_time, GetAppParameters(license_test_data[0].app_parameters))); EXPECT_TRUE(device_files.StoreLicense( @@ -1958,6 +1968,7 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { license_update_test_data[0].key_release_url, license_update_test_data[0].playback_start_time, license_update_test_data[0].last_playback_time, + license_update_test_data[0].grace_period_end_time, GetAppParameters(license_test_data[0].app_parameters))); } @@ -1996,14 +2007,14 @@ TEST_F(DeviceFilesTest, DeleteLicense) { CdmKeyMessage key_renewal_request; CdmKeyResponse key_renewal_response; std::string release_server_url; - int64_t playback_start_time, last_playback_time; + int64_t playback_start_time, last_playback_time, grace_period_end_time; CdmAppParameterMap app_parameters; EXPECT_TRUE(device_files.RetrieveLicense( license_test_data[0].key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, &last_playback_time, - &app_parameters)); + &grace_period_end_time, &app_parameters)); EXPECT_EQ(license_test_data[0].license_state, license_state); EXPECT_EQ(license_test_data[0].pssh_data, pssh_data); EXPECT_EQ(license_test_data[0].key_request, key_request); @@ -2012,6 +2023,7 @@ TEST_F(DeviceFilesTest, DeleteLicense) { EXPECT_EQ(license_test_data[0].key_response, key_response); EXPECT_EQ(license_test_data[0].playback_start_time, playback_start_time); EXPECT_EQ(license_test_data[0].last_playback_time, last_playback_time); + EXPECT_EQ(license_test_data[0].grace_period_end_time, grace_period_end_time); std::map::iterator itr; for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) { EXPECT_NE(license_test_data[0].app_parameters.find(itr->first), diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 2c828cf7..23b7f3d4 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -1093,7 +1093,8 @@ TEST_F(PolicyEngineTest, LicenseExpired_SoftEnforceLoadBeforeExpire) { OnExpirationUpdate(_, kLicenseStartTime + kLicenseDuration)); policy_engine_->SetLicense(license_); - policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime); + policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime, + kPlaybackStartTime); policy_engine_->OnTimerEvent(); EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); @@ -1117,12 +1118,147 @@ TEST_F(PolicyEngineTest, LicenseExpired_SoftEnforceLoadAfterExpire) { ExpectSessionKeysChange(kKeyStatusExpired, false); policy_engine_->SetLicense(license_); - policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime); + policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime, + kPlaybackStartTime); policy_engine_->OnTimerEvent(); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); } +TEST_F(PolicyEngineTest, PlaybackOk_GracePeriod) { + const int64_t kGracePeriod = 300; // 5 minutes + License_Policy* policy = license_.mutable_policy(); + policy->set_play_start_grace_period_seconds(kGracePeriod); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kPlaybackStartTime + kGracePeriod - 5)) + .WillOnce(Return(kPlaybackStartTime + kGracePeriod + 5)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 5)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 5)); + + InSequence seq; + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + EXPECT_CALL(check_, Call(1)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(4)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + + for (int i = 1; i <= 4; ++i) { + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + policy_engine_->OnTimerEvent(); + check_.Call(i); + } + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_GracePeriodWithLoad) { + const int64_t kGracePeriod = 300; // 5 minutes + const int64_t kNewPlaybackStartTime = kPlaybackStartTime + kPlaybackDuration; + License_Policy* policy = license_.mutable_policy(); + policy->set_play_start_grace_period_seconds(kGracePeriod); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kNewPlaybackStartTime)) + .WillOnce(Return(kNewPlaybackStartTime)) + .WillOnce(Return(kNewPlaybackStartTime + kGracePeriod - 5)) + .WillOnce(Return(kNewPlaybackStartTime + kGracePeriod + 5)) + .WillOnce(Return(kNewPlaybackStartTime + kPlaybackDuration - 5)) + .WillOnce(Return(kNewPlaybackStartTime + kPlaybackDuration + 5)); + + InSequence seq; + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + EXPECT_CALL(check_, Call(1)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kNewPlaybackStartTime + kPlaybackDuration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(4)); + + policy_engine_->SetLicense(license_); + policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime, + 0); + policy_engine_->BeginDecryption(); + + for (int i = 1; i <= 4; ++i) { + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + policy_engine_->OnTimerEvent(); + check_.Call(i); + } + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_GracePeriodWithExpiredLoad) { + const int64_t kGracePeriod = 300; // 5 minutes + const int64_t kNewPlaybackStartTime = + kPlaybackStartTime + kPlaybackDuration + 5; + License_Policy* policy = license_.mutable_policy(); + policy->set_play_start_grace_period_seconds(kGracePeriod); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kNewPlaybackStartTime)) + .WillOnce(Return(kNewPlaybackStartTime)); + + InSequence seq; + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + + policy_engine_->SetLicense(license_); + policy_engine_->RestorePlaybackTimes(kPlaybackStartTime, kPlaybackStartTime, + kPlaybackStartTime); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_CanStoreGracePeriod) { + const int64_t kGracePeriod = 300; // 5 minutes + License_Policy* policy = license_.mutable_policy(); + policy->set_play_start_grace_period_seconds(kGracePeriod); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kPlaybackStartTime + 50)) + .WillOnce(Return(kPlaybackStartTime + kGracePeriod + 2)); + + InSequence seq; + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + + policy_engine_->OnTimerEvent(); + EXPECT_EQ(0, policy_engine_->GetGracePeriodEndTime()); + + policy_engine_->OnTimerEvent(); + EXPECT_EQ(kPlaybackStartTime, policy_engine_->GetGracePeriodEndTime()); +} + class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest { protected: enum KeyFlag {