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

@@ -7,6 +7,8 @@
#include "oec_device_features.h"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>
@@ -141,6 +143,11 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
if (api_version < 12) FilterOut(&filter, "*API12*");
if (api_version < 13) FilterOut(&filter, "*API13*");
if (api_version < 14) FilterOut(&filter, "*API14*");
// Some tests may require root access. If user is not root, filter these tests
// out.
if (getuid()) {
FilterOut(&filter, "UsageTableTest.TimeRollbackPrevention");
}
// Performance tests take a long time. Filter them out if they are not
// specifically requested.
if (filter.find("Performance") == std::string::npos) {

View File

@@ -1199,7 +1199,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
ASSERT_EQ(expected.pst, computed_pst);
time_t now = time(NULL);
time_t now = time(NULL);
int64_t age = now - expected.time_created; // How old is this report.
EXPECT_NEAR(expected.seconds_since_license_received + age,
computed.seconds_since_license_received(),
@@ -1235,13 +1235,10 @@ static int64_t MaybeAdjustTime(int64_t t, time_t now) {
return t;
}
void Session::GenerateVerifyReport(const std::string& pst,
OEMCrypto_Usage_Entry_Status status,
int64_t time_license_received,
int64_t time_first_decrypt,
int64_t time_last_decrypt) {
ASSERT_NO_FATAL_FAILURE(GenerateReport(pst));
Test_PST_Report expected(pst, status);
void Session::VerifyReport(Test_PST_Report expected,
int64_t time_license_received,
int64_t time_first_decrypt,
int64_t time_last_decrypt) {
time_t now = time(NULL);
expected.seconds_since_license_received =
MaybeAdjustTime(time_license_received, now);
@@ -1251,6 +1248,17 @@ void Session::GenerateVerifyReport(const std::string& pst,
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
}
void Session::GenerateVerifyReport(const std::string& pst,
OEMCrypto_Usage_Entry_Status status,
int64_t time_license_received,
int64_t time_first_decrypt,
int64_t time_last_decrypt) {
ASSERT_NO_FATAL_FAILURE(GenerateReport(pst));
Test_PST_Report expected(pst, status);
ASSERT_NO_FATAL_FAILURE(VerifyReport(expected, time_license_received,
time_first_decrypt, time_last_decrypt));
}
void Session::CreateOldEntry(const Test_PST_Report& report) {
OEMCryptoResult result = OEMCrypto_CreateOldUsageEntry(
report.seconds_since_license_received,

View File

@@ -332,9 +332,14 @@ class Session {
// Verify the values in the PST report. The signature should have been
// verified in GenerateReport, above.
void VerifyPST(const Test_PST_Report& report);
// Generate and Verify the Usage Report. If any time is greater than 10
// minutes, it is assumed to be an absolute time, and time_since will be
// computed relative to now.
// Verify the Usage Report. If any time is greater than 10 minutes, it is
// assumed to be an absolute time, and time_since will be computed relative to
// now.
void VerifyReport(Test_PST_Report report,
int64_t time_license_received = 0,
int64_t time_first_decrypt = 0,
int64_t time_last_decrypt = 0);
// Same as above, but generates the report with the given status.
void GenerateVerifyReport(const std::string& pst,
OEMCrypto_Usage_Entry_Status status,
int64_t time_license_received = 0,

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