Address offline playback with rollbacked time

Merge from http://go/wvgerrit/47640

Test: unit/integration tests

Bug: b/62058202

The usage table keeps track of license duration by using the current
system time. However, if a user were to rollback the time, they can
effectively continue offline playback indefinitely. This changes the way
we compute time by computing offsets by which the user rollbacked the
time and adding it to the current time. This change also includes a test
to verify protection against rollback for usage entries that is only run
when the user is root.

Change-Id: I97c430e1443747b0f9759ae5390b8f5d06bdebf1
This commit is contained in:
Srujan Gaddam
2018-04-05 11:29:53 -07:00
parent d873f40d80
commit 73c3007c24
10 changed files with 692522 additions and 707026 deletions

View File

@@ -5617,6 +5617,86 @@ TEST_F(UsageTableTest, VerifyUsageTimes) {
s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// NOTE: This test needs root access since clock_settime messes with the system
// time in order to verify that OEMCrypto protects against rollbacks in usage
// entries. Therefore, this test is filtered if not run as root.
// We don't test roll-forward protection or instances where the user rolls back
// the time to the last decrypt call since this requires hardware-secure clocks
// to guarantee.
TEST_F(UsageTableTest, TimeRollbackPrevention) {
std::string pst = "my_pst";
Session s1;
cout << "This test temporarily rolls back the system time in order to verify "
<< "that the usage report accounts for the change. It then rolls "
<< "the time back forward to the absolute time." << endl;
// We use clock_gettime(CLOCK_REALTIME, ...) over time(...) so we can easily
// set the time using clock_settime.
timespec current_time;
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &current_time));
time_t loaded = current_time.tv_sec;
ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst));
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_));
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &current_time));
time_t first_decrypt = current_time.tv_sec;
// Monotonic clock can't be changed. We use this since system clock will be
// unreliable.
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &current_time));
time_t first_decrypt_monotonic = current_time.tv_sec;
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.close());
// Imitate playback.
sleep(kLongDuration * 2);
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_));
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.close());
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &current_time));
// Rollback the wall clock time.
current_time.tv_sec -= kLongDuration * 10;
cout << "Rolling the system time back..." << endl;
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &current_time));
// Try to playback again.
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_));
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &current_time));
time_t third_decrypt = current_time.tv_sec;
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(pst));
Test_PST_Report expected(pst, kActive);
// Restore wall clock to its original position to verify that OEMCrypto does
// not report negative times.
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &current_time));
current_time.tv_sec =
first_decrypt + current_time.tv_sec - first_decrypt_monotonic;
cout << "Rolling the system time forward to the absolute time..." << endl;
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &current_time));
// Need to update time created since the verification checks the time of PST
// report creation.
expected.time_created = current_time.tv_sec;
ASSERT_NO_FATAL_FAILURE(
s1.VerifyReport(expected, loaded, first_decrypt,
first_decrypt + third_decrypt - first_decrypt_monotonic));
ASSERT_NO_FATAL_FAILURE(s1.close());
}
// This is a special case where a group of assets can be licensed with a master
// key. In order for this to work, a single session must first load a device
// specific license, and then a shared content license. This shared license is