Test to ensure rollback doesn't affect key duration

Bug: b/78359032
Merge of http://go/wvgerrit/70544
Test: Android unit + integration tests/manual checking of system time to
make sure it's restored correctly

Integration tests are added to make sure that clock rollback of the
device system time does not affect key duration for both streaming and
offline licenses. These tests require root access as they modify system
time and restore them.

Change-Id: I3e1802205e2fc2056093c1e39b0ff6e0a8816abc
This commit is contained in:
Srujan Gaddam
2019-01-16 16:51:02 -08:00
parent 05599927b9
commit 6bd5c564d8

View File

@@ -3,7 +3,11 @@
// License Agreement.
#include <errno.h>
#include <time.h>
#include <chrono>
#include <sstream>
#include <thread>
#include <android-base/properties.h>
#include <gmock/gmock.h>
@@ -4873,7 +4877,7 @@ TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) {
session_id_,
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
;
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
const std::string kCpKeyId = a2bs_hex(
@@ -5363,4 +5367,273 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
decryptor_.CloseSession(session_id_);
}
class WvCdmRequestLicenseRollbackTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SubSampleInfo*> {
public:
WvCdmRequestLicenseRollbackTest() {
SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry;
decrypt_buffer_.resize(data->encrypt_data.size());
decryption_parameters_ = CdmDecryptionParameters(
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
&data->iv, data->block_offset, &decrypt_buffer_[0]);
decryption_parameters_.is_encrypted = data->is_encrypted;
decryption_parameters_.is_secure = data->is_secure;
decryption_parameters_.subsample_flags = data->subsample_flags;
validate_key_id_ = data->validate_key_id;
}
~WvCdmRequestLicenseRollbackTest() {}
protected:
void RollbackSystemTime(time_t rollback_time_ms) {
if (!in_rollback_state_) {
LOGW("Rolling back system time %d ms.", rollback_time_ms);
wall_time_before_rollback_ = std::chrono::system_clock::now();
monotonic_time_before_rollback_ = std::chrono::steady_clock::now();
auto modified_wall_time = wall_time_before_rollback_ -
std::chrono::milliseconds(rollback_time_ms);
timespec modified_wall_time_spec;
modified_wall_time_spec.tv_sec =
std::chrono::duration_cast<std::chrono::seconds>(
modified_wall_time.time_since_epoch())
.count();
modified_wall_time_spec.tv_nsec =
std::chrono::duration_cast<std::chrono::nanoseconds>(
modified_wall_time.time_since_epoch())
.count() %
(1000 * 1000 * 1000);
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &modified_wall_time_spec));
in_rollback_state_ = true;
} else {
LOGE("Can't rollback system time more than once without restoring.");
}
}
void RestoreSystemTime() {
if (in_rollback_state_) {
LOGW("Restoring the system time.");
auto monotonic_time_after_rollback = std::chrono::steady_clock::now();
auto monotonic_time_diff =
monotonic_time_after_rollback - monotonic_time_before_rollback_;
auto real_time = wall_time_before_rollback_ + monotonic_time_diff;
timespec real_time_spec;
real_time_spec.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
real_time.time_since_epoch())
.count();
real_time_spec.tv_nsec =
std::chrono::duration_cast<std::chrono::nanoseconds>(
real_time.time_since_epoch())
.count() %
(1000 * 1000 * 1000);
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &real_time_spec));
in_rollback_state_ = false;
} else {
LOGW("System time has already been restored.");
}
}
CdmResponseType Decrypt(CdmSessionId session_id) {
std::fill(decrypt_buffer_.begin(), decrypt_buffer_.end(), 0);
return decryptor_.Decrypt(session_id, validate_key_id_,
decryption_parameters_);
}
std::chrono::time_point<std::chrono::steady_clock>
monotonic_time_before_rollback_;
std::chrono::time_point<std::chrono::system_clock> wall_time_before_rollback_;
bool in_rollback_state_ = false;
const std::string init_data_with_expiry_ = a2bs_hex(
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"); // pssh data
// Expiration of the key corresponding to init_data_with_expiry_ with a window
// to ensure expiration.
const time_t kExpirationTimeMs_ =
kSingleEncryptedSubSampleIcpLicenseDurationExpiration * 1000;
const time_t kExpirationWindowMs_ =
kSingleEncryptedSubSampleIcpLicenseExpirationWindow * 1000;
const time_t kExpirationWithWindowMs_ =
(kSingleEncryptedSubSampleIcpLicenseDurationExpiration +
kSingleEncryptedSubSampleIcpLicenseExpirationWindow) *
1000;
CdmDecryptionParameters decryption_parameters_;
std::vector<uint8_t> decrypt_buffer_;
bool validate_key_id_;
};
TEST_F(WvCdmRequestLicenseRollbackTest, Streaming_ExpireAfterRollback) {
Unprovision();
Provision(kLevelDefault);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr,
&session_id_));
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
// Verify that we can decrypt a subsample to begin with.
EXPECT_EQ(NO_ERROR, Decrypt(session_id_));
RollbackSystemTime(kExpirationWithWindowMs_);
// Elapse time so that the key should now be considered expired.
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_));
RestoreSystemTime();
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
}
TEST_F(WvCdmRequestLicenseRollbackTest, Streaming_ExpireBeforeRollback) {
Unprovision();
Provision(kLevelDefault);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr,
&session_id_));
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
// Elapse time so that the key should now be considered expired.
std::this_thread::sleep_for(
std::chrono::milliseconds(kExpirationWithWindowMs_));
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
RollbackSystemTime(kExpirationWithWindowMs_);
// Verify that we still can't decrypt even if we rollbacked the clock.
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
RestoreSystemTime();
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
}
TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackBeforeRestoreKey) {
Unprovision();
Provision(kLevelDefault);
std::string unused_key_id;
std::string client_auth;
GetOfflineConfiguration(&unused_key_id, &client_auth);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr,
&session_id_));
GenerateKeyRequest(init_data_with_expiry_, 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.
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(kExpirationTimeMs_ / 2));
// Verify we can decrypt.
EXPECT_EQ(NO_ERROR, Decrypt(session_id_));
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
}
TEST_F(WvCdmRequestLicenseRollbackTest,
Offline_RollbackAndExpireAfterRestoreKey) {
Unprovision();
Provision(kLevelDefault);
std::string unused_key_id;
std::string client_auth;
GetOfflineConfiguration(&unused_key_id, &client_auth);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr,
&session_id_));
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
RollbackSystemTime(kExpirationWithWindowMs_);
// Elapse time so that the key should now be considered expired.
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_));
RestoreSystemTime();
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
}
TEST_F(WvCdmRequestLicenseRollbackTest,
Offline_ExpireAndRollbackAfterRestoreKey) {
Unprovision();
Provision(kLevelDefault);
std::string unused_key_id;
std::string client_auth;
GetOfflineConfiguration(&unused_key_id, &client_auth);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr,
&session_id_));
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
// Elapse time so that the key should now be considered expired.
std::this_thread::sleep_for(
std::chrono::milliseconds(kExpirationWithWindowMs_));
RollbackSystemTime(kExpirationWithWindowMs_);
// Verify that we can no longer decrypt a subsample due to key expiration.
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
RestoreSystemTime();
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
}
} // namespace wvcdm