From 87e84d5498ee96171d17ee3fcf1ae159ec0d128d Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Fri, 23 Apr 2021 03:57:10 -0700 Subject: [PATCH] Use Usage information from OEMCrypto on clock rollback [ Merge of http://go/wvgerrit/123103 ] This corrects setting of first and last playback times stored by the CDM on rollback. Earlier usage information from the usage entry in OEMCrypto would be ignored on rollback even when available. Information stored along with the license in persistent storage would be used instead. A new test with longer duration expiry has been added as well as some additional verification. Bug: 186199213 Test: WV unit/integration test Change-Id: I601f9584a8a0c5137ce68546f8ec833bf2e70cc5 --- libwvdrmengine/cdm/core/src/license.cpp | 8 +-- .../cdm/test/request_license_test.cpp | 69 ++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 9194f5be..f85cbfe6 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -864,13 +864,13 @@ CdmResponseType CdmLicense::RestoreOfflineLicense( break; case CryptoSession::kUsageDurationsValid: { int64_t current_time = clock_->GetCurrentTime(); - if (current_time - seconds_since_started > 0) - playback_start_time = current_time - seconds_since_started; - if (current_time - last_playback_time > 0) - last_playback_time = current_time - seconds_since_last_played; + playback_start_time = current_time - seconds_since_started; + last_playback_time = current_time - seconds_since_last_played; break; } default: + // Use playback_start_time and last_playback_time from + // persistently stored license data break; } } diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index e5dcf686..4c392c87 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -6593,6 +6593,69 @@ TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackBeforeRestoreKey) { ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); } +// The difference between this test and Offline_RollbackBeforeRestoreKey is +// that this test has a 15 second expiration window. +// Offline_RollbackBeforeRestoreKey has a 5 second window. The rollback +// before restore causes the expiration timer to run from the rollback time. +// The license in Offline_RollbackBeforeRestoreKey is expired by the time +// the rollback is undone, while this one has not yet expired. +TEST_F(WvCdmRequestLicenseRollbackTest, + Offline_LongerDurationRollbackBeforeRestoreKey) { + Unprovision(); + Provision(); + + // The default offline asset is "offline_clip2". Substitute '2' for '8'. + // "offline_clip8" has a 15 second expiration. + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); + key_id[key_id.size() - 1] = '8'; + + ASSERT_EQ(NO_ERROR, decryptor_->OpenSession(config_.key_system(), nullptr, + kDefaultCdmIdentifier, nullptr, + &session_id_)); + GenerateKeyRequest(key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(config_.license_server(), client_auth); + + // Verify that we can decrypt a subsample to begin with. + EXPECT_EQ(NO_ERROR, Decrypt(session_id_)); + + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_->CloseSession(session_id_); + + // This number must be > the time between GenerateKeyRequest and this call. + RollbackSystemTime(10 * 1000); + + session_id_.clear(); + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_); + + decryptor_->RestoreKey(session_id_, key_set_id); + + // Verify we can't decrypt. The license start time is in the future. + EXPECT_EQ(DECRYPT_NOT_READY, Decrypt(session_id_)); + + RestoreSystemTime(); + + // Sleep for a little bit to account for the execution time of OpenSession and + // RestoreKey. + std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationWindowMs_)); + + // Verify we can decrypt. + EXPECT_EQ(NO_ERROR, Decrypt(session_id_)); + + // Sleep for a while more. + std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationTimeMs_)); + + // Rollback time + kExpirationWindowMs_ + kExpirationTimeMs_ (~17s) will have + // elapsed. This is greater than the expiration window for longer duration + // licenses (15s). The license should have expired. + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + + ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); +} + TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackAndExpireAfterRestoreKey) { Unprovision(); @@ -6631,6 +6694,8 @@ TEST_F(WvCdmRequestLicenseRollbackTest, RestoreSystemTime(); + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); } @@ -6665,9 +6730,11 @@ TEST_F(WvCdmRequestLicenseRollbackTest, std::this_thread::sleep_for( std::chrono::milliseconds(kExpirationWithWindowMs_)); + // Verify that we can no longer decrypt a subsample due to key expiration. + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + RollbackSystemTime(kExpirationWithWindowMs_); - // Verify that we can no longer decrypt a subsample due to key expiration. EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); RestoreSystemTime();