Merge "Test to ensure rollback doesn't affect key duration"

This commit is contained in:
Srujan Gaddam
2019-02-20 18:53:23 +00:00
committed by Android (Google) Code Review

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