Files
ce_cdm/oemcrypto/odk/test/odk_timer_test.cpp
2024-03-29 10:49:35 -07:00

1335 lines
57 KiB
C++

/* Copyright 2019 Google LLC. This file and proprietary
* source code may only be used and distributed under the Widevine
* License Agreement.
*/
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "odk_test_helper.h"
namespace {
// The rental clock starts when the request is signed. If any test fails
// because a time is off by exactly 1000, that is a strong indication that we
// confused rental and system clocks. The system clock should only be used
// internally on the device, because it might not be synchronized from one
// device to another. Rental clock is used in the license message from the
// server.
constexpr uint64_t kRentalClockStart = 1000u;
// The renewal grace period is used by the server and the CDM layer to allow
// time between when the renewal is requested and when playback is cutoff if the
// renewal is not loaded.
constexpr uint64_t kGracePeriod = 5u;
constexpr uint32_t kExtraPayloadSize = 128u;
constexpr uint32_t kSystemTime = 20u;
namespace wvodk_test {
TEST(OdkTimerBasicTest, ParseLicenseTimerSet) {
// playback timer is successfully started
::wvodk_test::ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params, ODK_MAJOR_VERSION);
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
params.parsed_license.timer_limits.soft_enforce_rental_duration = false;
params.parsed_license.timer_limits.soft_enforce_playback_duration = false;
params.parsed_license.timer_limits.earliest_playback_start_seconds = 10;
params.parsed_license.timer_limits.total_playback_duration_seconds = 0;
params.parsed_license.timer_limits.rental_duration_seconds = 10;
params.parsed_license.timer_limits.initial_renewal_duration_seconds = 0;
OEMCryptoResult result =
ODK_InitializeClockValues(&params.clock_values, kSystemTime);
EXPECT_EQ(OEMCrypto_SUCCESS, result);
params.clock_values.time_of_license_request_signed = 5;
params.clock_values.status = kActive;
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
result = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, kSystemTime, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license), nullptr);
EXPECT_EQ(ODK_SET_TIMER, result);
delete[] buf;
}
TEST(OdkTimerBasicTest, ParseLicenseTimerDisabled) {
// playback timer is successfully started
::wvodk_test::ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params, ODK_MAJOR_VERSION);
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
params.parsed_license.timer_limits.soft_enforce_rental_duration = true;
params.parsed_license.timer_limits.earliest_playback_start_seconds = 3;
params.parsed_license.timer_limits.total_playback_duration_seconds = 0;
params.parsed_license.timer_limits.initial_renewal_duration_seconds = 0;
params.clock_values.time_of_first_decrypt = 10;
params.clock_values.time_of_license_request_signed = 5;
params.clock_values.status = kActive;
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
OEMCryptoResult result = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, kSystemTime, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license), nullptr);
EXPECT_EQ(ODK_DISABLE_TIMER, result);
delete[] buf;
}
TEST(OdkTimerBasicTest, ParseRenewalTimerExpired) {
// playback timer is successfully started
::wvodk_test::ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params, ODK_MAJOR_VERSION);
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
params.parsed_license.timer_limits.rental_duration_seconds = 5;
params.parsed_license.timer_limits.earliest_playback_start_seconds = 3;
OEMCryptoResult result =
ODK_InitializeClockValues(&params.clock_values, kSystemTime);
EXPECT_EQ(OEMCrypto_SUCCESS, result);
params.clock_values.time_of_license_request_signed = 5;
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
result = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, kSystemTime, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license), nullptr);
EXPECT_EQ(ODK_TIMER_EXPIRED, result);
delete[] buf;
}
} // namespace wvodk_test
TEST(OdkTimerBasicTest, NullTest) {
// Assert that nullptr does not cause a core dump.
ODK_InitializeClockValues(nullptr, 0u);
ODK_ReloadClockValues(nullptr, 0u, 0u, 0u, kActive, 0u);
ODK_AttemptFirstPlayback(0u, nullptr, nullptr, nullptr);
ODK_UpdateLastPlaybackTime(0, nullptr, nullptr);
ASSERT_TRUE(true);
}
TEST(OdkTimerBasicTest, Init) {
// Verify that basic initialization sets all of the fields.
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
uint64_t time = 42;
ODK_InitializeClockValues(&clock_values, time);
EXPECT_EQ(clock_values.time_of_license_request_signed, time);
EXPECT_EQ(clock_values.time_of_first_decrypt, 0u);
EXPECT_EQ(clock_values.time_of_last_decrypt, 0u);
EXPECT_EQ(clock_values.time_when_timer_expires, 0u);
EXPECT_EQ(clock_values.timer_status,
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
EXPECT_EQ(clock_values.status, kUnused);
}
TEST(OdkTimerBasicTest, Reload) {
// Verify that reloading clock values uses the same values
// for fields that can be saved, and sets others to 0.
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
uint64_t time = 42u;
uint64_t lic_signed = 1u;
uint64_t first_decrypt = 2u;
uint64_t last_decrypt = 3u;
enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed;
ODK_ReloadClockValues(&clock_values, lic_signed, first_decrypt, last_decrypt,
status, time);
EXPECT_EQ(clock_values.time_of_license_request_signed, lic_signed);
EXPECT_EQ(clock_values.time_of_first_decrypt, first_decrypt);
EXPECT_EQ(clock_values.time_of_last_decrypt, last_decrypt);
EXPECT_EQ(clock_values.time_when_timer_expires, 0u);
EXPECT_EQ(clock_values.timer_status,
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
EXPECT_EQ(clock_values.status, status);
}
// All of the following test cases are derived from this base class. It
// simulates loading a license, attempting playback, and reloading a license.
class ODKTimerTest : public ::testing::Test {
public:
ODKTimerTest() {
// These are reasonable initial values for most tests. This is an unlimited
// license. Most of the tests below will add some restrictions by changing
// these values, and then verify that the ODK library behaves correctly.
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.soft_enforce_playback_duration = true;
timer_limits_.earliest_playback_start_seconds = 0u;
// A duration of 0 means infinite.
timer_limits_.rental_duration_seconds = 0u;
timer_limits_.total_playback_duration_seconds = 0u;
timer_limits_.initial_renewal_duration_seconds = 0u;
// This is when we will attempt the first valid playback.
start_of_playback_ =
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
}
protected:
void SetUp() override {
::testing::Test::SetUp();
// Start rental clock at kRentalClockStart. This happens when the license
// request is signed.
ODK_InitializeClockValues(&clock_values_, kRentalClockStart);
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
}
// Simulate loading or reloading a license in a new session. An offline
// license should have several of the clock_value's fields saved into the
// usage table. When it is reloaded those values should be reloaded. From
// these fields, the ODK function can tell if this is the first playback for
// the license, or just the first playback for this session. The key fields
// that should be saved are the status, and the times for license signed, and
// first and last playback times.
void ReloadLicense(uint64_t system_time) {
ODK_ClockValues old_clock_values = clock_values_;
// First clear out the old clock values.
memset(&clock_values_, 0, sizeof(clock_values_));
// When the session is opened, the clock values are initialized.
ODK_InitializeClockValues(&clock_values_, 0);
// When the usage entry is reloaded, the clock values are reloaded.
ODK_ReloadClockValues(&clock_values_,
old_clock_values.time_of_license_request_signed,
old_clock_values.time_of_first_decrypt,
old_clock_values.time_of_last_decrypt,
old_clock_values.status, system_time);
EXPECT_EQ(clock_values_.timer_status,
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
// These shall not change:
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
EXPECT_EQ(clock_values_.time_of_first_decrypt,
old_clock_values.time_of_first_decrypt);
EXPECT_EQ(clock_values_.time_of_last_decrypt,
old_clock_values.time_of_last_decrypt);
// ODK_ParseLicense sets the new timer state.
clock_values_.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
}
// Simulate loading or reloading a license, then verify that we are allowed
// playback from |start| to |stop|. If |cutoff| is not 0, then expect the
// timer to expire at that time. If |cutoff| is 0, expect the timer is not
// set. If you refer to the diagrams in "License Duration and Renewal", this
// tests a cyan bar with a green check mark.
// When nonzero |start|, |stop|, and |cutoff| are all system times.
void LoadAndAllowPlayback(uint64_t start, uint64_t stop, uint64_t cutoff) {
ReloadLicense(start);
AllowPlayback(start, stop, cutoff);
}
// Verify that we are allowed playback from |start| to |stop|. If |cutoff| is
// not 0, then expect the timer to expire at that time. If |cutoff| is 0,
// expect the timer is not set. If you refer to the diagrams in "License
// Duration and Renewal", this tests a cyan bar with a green check mark.
// When nonzero |start|, |stop|, and |cutoff| are all system times.
void AllowPlayback(uint64_t start, uint64_t stop, uint64_t cutoff) {
ASSERT_LT(start, stop);
if (cutoff > 0) ASSERT_LE(stop, cutoff);
uint64_t timer_value;
const OEMCryptoResult result = ODK_AttemptFirstPlayback(
start, &timer_limits_, &clock_values_, &timer_value);
// After first playback, the license is active.
EXPECT_EQ(clock_values_.status, kActive);
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
if (cutoff > 0) { // If we expect the timer to be set.
EXPECT_EQ(result, ODK_SET_TIMER);
EXPECT_EQ(timer_value, cutoff - start);
} else {
EXPECT_EQ(result, ODK_DISABLE_TIMER);
}
CheckClockValues(start);
const uint64_t mid = (start + stop) / 2;
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
OEMCrypto_SUCCESS);
CheckClockValues(mid);
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
OEMCrypto_SUCCESS);
CheckClockValues(stop);
}
// Simulate loading or reloading a license, then attempt to play from |start|
// to |cutoff|. Verify that we are allowed playback from |start| to |cutoff|,
// but playback is not allowed after |cutoff|. If you refer to the diagrams in
// "License Duration and Renewal", this tests a cyan bar with a black X. When
// nonzero |start|, and |cutoff| are all system times.
void LoadAndTerminatePlayback(uint64_t start, uint64_t cutoff) {
ReloadLicense(start);
TerminatePlayback(start, cutoff, cutoff + 10);
}
// Attempt to play from |start| to |stop|. Verify that we are allowed playback
// from |start| to |cutoff|, but playback not allowed after |cutoff|. If you
// refer to the diagrams in "License Duration and Renewal", this tests a cyan
// bar with a black X. This assumes that |cutoff| is before |stop|.
// When nonzero |start|, |stop|, and |cutoff| are all system times.
void TerminatePlayback(uint64_t start, uint64_t cutoff, uint64_t stop) {
ASSERT_LT(start, cutoff);
ASSERT_LT(cutoff, stop);
AllowPlayback(start, cutoff, cutoff);
EXPECT_EQ(
ODK_UpdateLastPlaybackTime(cutoff + 1, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(cutoff);
const uint64_t mid = (cutoff + stop) / 2;
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
// times do not change if playback was not allowed.
CheckClockValues(cutoff);
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(cutoff);
}
// Verify that we are not allowed playback at the |start| time. If you refer
// to the diagrams in "License Duration and Renewal", this tests a cyan line
// followed by a black X. The parameter |start| is system time.
void ForbidPlayback(uint64_t start) {
ReloadLicense(start);
ODK_ClockValues old_clock_values = clock_values_;
uint64_t timer_value;
EXPECT_EQ(ODK_AttemptFirstPlayback(start, &timer_limits_, &clock_values_,
&timer_value),
ODK_TIMER_EXPIRED);
// These should not have changed. In particular, if the license was unused
// before, it should reamin unused.
EXPECT_EQ(clock_values_.time_of_license_request_signed,
old_clock_values.time_of_license_request_signed);
EXPECT_EQ(clock_values_.time_of_first_decrypt,
old_clock_values.time_of_first_decrypt);
EXPECT_EQ(clock_values_.time_of_last_decrypt,
old_clock_values.time_of_last_decrypt);
EXPECT_EQ(clock_values_.status, old_clock_values.status);
}
// Verify that the clock values are correct.
void CheckClockValues(uint64_t time_of_last_decrypt) {
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
EXPECT_EQ(clock_values_.time_of_first_decrypt, start_of_playback_);
EXPECT_EQ(clock_values_.time_of_last_decrypt, time_of_last_decrypt);
EXPECT_EQ(clock_values_.status, kActive);
}
// Convert from rental time to system time. By "system time", we mean
// OEMCrypto's montonic clock. The spec does not specify a starting time for
// this clock.
uint64_t GetSystemTime(uint64_t rental_clock) {
return kRentalClockStart + rental_clock;
}
// The end of the playback window. (using system clock)
// This is not useful if the playback duration is 0.
uint64_t EndOfPlaybackWindow() {
return start_of_playback_ + timer_limits_.total_playback_duration_seconds;
}
// The end of the rental window. (using system clock)
// This is not useful if the rental duration is 0.
uint64_t EndOfRentalWindow() {
return GetSystemTime(timer_limits_.earliest_playback_start_seconds) +
timer_limits_.rental_duration_seconds;
}
ODK_TimerLimits timer_limits_;
ODK_ClockValues clock_values_;
// The start of playback. This is set to the planned start at the beginning of
// the test. (using system clock)
uint64_t start_of_playback_;
};
TEST_F(ODKTimerTest, SimplePlayback) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
}
// This tests that we are not allowed to start playback before the rental window
// opens.
TEST_F(ODKTimerTest, EarlyTest) {
timer_limits_.earliest_playback_start_seconds = 100u;
// This is earlier than we are allowed.
const uint64_t bad_start_time =
GetSystemTime(timer_limits_.earliest_playback_start_seconds - 10);
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
ForbidPlayback(bad_start_time);
// And times were not updated:
EXPECT_EQ(clock_values_.status, kUnused);
// This is when we will attempt the first valid playback.
start_of_playback_ =
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
}
// This runs the same test as above, but explicitly gives the ODK library a null
// pointer for the timer value. A null pointer is allowed for OEMCrypto
// implementations that do not manage their own timer.
TEST_F(ODKTimerTest, NullTimer) {
timer_limits_.earliest_playback_start_seconds = 100u;
// This is the earlier, invalid start time.
const uint64_t bad_start_time =
GetSystemTime(timer_limits_.earliest_playback_start_seconds - 10);
timer_limits_.rental_duration_seconds = 300;
timer_limits_.soft_enforce_rental_duration = false;
ReloadLicense(GetSystemTime(bad_start_time));
// If OEMCrypto passes in a null pointer, then the ODK should allow this.
uint64_t* timer_value_pointer = nullptr;
EXPECT_EQ(ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
&clock_values_, timer_value_pointer),
ODK_TIMER_EXPIRED);
start_of_playback_ =
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
EXPECT_EQ(ODK_AttemptFirstPlayback(start_of_playback_, &timer_limits_,
&clock_values_, timer_value_pointer),
ODK_SET_TIMER);
CheckClockValues(start_of_playback_);
}
/*****************************************************************************/
// Note on Use Case tests. The test classes below correspond to the use cases
// in the doucment "License Duration and Renewal.". Each diagram in that
// document has a test class below to verify the use case is supported.
//
// In the document, we use realistic rental times in hours or days. In these
// tests, we will use round numbers so that it is easier to read. For example,
// instead of a seven day rental duration, we will use a 700 rental duration.
/*****************************************************************************/
/*****************************************************************************/
// Streaming is the simplest use case. The user has three hours to watch the
// movie from the time of the rental. (See above for note on Use Case tests)
class ODKUseCase_Streaming : public ODKTimerTest {
public:
ODKUseCase_Streaming() {
// Rental duration = 3 hours hard. (use 300 for readability)
// Playback duration = 0 (unlimited)
timer_limits_.soft_enforce_rental_duration = false;
timer_limits_.rental_duration_seconds = 300;
timer_limits_.total_playback_duration_seconds = 0;
}
};
// Playback within rental duration.
TEST_F(ODKUseCase_Streaming, Case1) {
// Allow playback within the rental window.
LoadAndAllowPlayback(start_of_playback_, EndOfRentalWindow(),
EndOfRentalWindow());
}
// Playback exceeds rental duration.
TEST_F(ODKUseCase_Streaming, Case2) {
// Allow playback within the rental window, but not beyond.
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
}
// Playback with stops/restarts within rental duration, last one exceeds rental
// duration.
TEST_F(ODKUseCase_Streaming, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfRentalWindow());
LoadAndAllowPlayback(start_of_playback_ + 110, start_of_playback_ + 120,
EndOfRentalWindow());
LoadAndTerminatePlayback(start_of_playback_ + 200, EndOfRentalWindow());
}
// Playback within rental duration, restart exceeds rental duration.
TEST_F(ODKUseCase_Streaming, Case4) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfRentalWindow());
ForbidPlayback(EndOfRentalWindow() + 10);
}
// Initial playback exceeds rental duration.
TEST_F(ODKUseCase_Streaming, Case5) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
/*****************************************************************************/
// Streaming Quick Start. The user must start watching within 30 seconds, and
// then has three hours to finish. (See above for note on Use Case tests)
class ODKUseCase_StreamingQuickStart : public ODKTimerTest {
public:
ODKUseCase_StreamingQuickStart() {
// Rental duration = 30 seconds, soft.
// Playback duration = 3 hours (use 300 for readability)
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.rental_duration_seconds = 30;
timer_limits_.total_playback_duration_seconds = 300;
timer_limits_.soft_enforce_playback_duration = false;
// A valid start of playback time.
start_of_playback_ =
GetSystemTime(timer_limits_.rental_duration_seconds - 10);
}
};
// Playback starts within rental duration, continues within playback duration.
TEST_F(ODKUseCase_StreamingQuickStart, Case1) {
// As seen in the drawing, the playback window exceeds the rental window.
EXPECT_LE(EndOfRentalWindow(), EndOfPlaybackWindow());
// Allow playback within the playback window.
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
EndOfPlaybackWindow());
}
// Playback exceeds playback duration.
TEST_F(ODKUseCase_StreamingQuickStart, Case2) {
// Allow playback within the playback window, but not beyond.
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_StreamingQuickStart, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfPlaybackWindow());
LoadAndAllowPlayback(start_of_playback_ + 110, start_of_playback_ + 120,
EndOfPlaybackWindow());
LoadAndTerminatePlayback(start_of_playback_ + 200, EndOfPlaybackWindow());
}
// Initial playback exceeds rental duration.
TEST_F(ODKUseCase_StreamingQuickStart, Case4) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
/*****************************************************************************/
// Seven Day / Two Day. The user must start watching within 7 days. Once
// started, the user has two days to finish the video. Playback is cutoff by the
// smaller of the two time limits. The first four cases start on day
// three. (See above for note on Use Case tests)
class ODKUseCase_SevenHardTwoHard_Start3 : public ODKTimerTest {
public:
ODKUseCase_SevenHardTwoHard_Start3() {
// Rental duration = 700, hard
// Playback duration = 200, hard
timer_limits_.rental_duration_seconds = 700;
timer_limits_.soft_enforce_rental_duration = false;
timer_limits_.total_playback_duration_seconds = 200;
timer_limits_.soft_enforce_playback_duration = false;
start_of_playback_ = GetSystemTime(300);
}
};
// Playback within playback and rental duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case1) {
// As seen in the drawing, the playback window is within the rental window.
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
EndOfPlaybackWindow());
}
// Playback exceeds playback duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case2) {
// Allow playback within the playback window, but not beyond.
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfPlaybackWindow());
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
EndOfPlaybackWindow());
LoadAndTerminatePlayback(start_of_playback_ + 125, EndOfPlaybackWindow());
}
// Restart exceeds playback duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case4) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfPlaybackWindow());
ForbidPlayback(EndOfPlaybackWindow() + 10);
}
// The next four cases start on day six.
class ODKUseCase_SevenHardTwoHard_Start6
: public ODKUseCase_SevenHardTwoHard_Start3 {
public:
ODKUseCase_SevenHardTwoHard_Start6() {
start_of_playback_ = GetSystemTime(600);
}
};
// Playback exceeds rental duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case5) {
// As seen in the drawing, the playback window is exceeds the rental window.
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
}
// Playback with stops/restarts within playback duration, last one is terminated
// at the end of the rental duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case6) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfRentalWindow());
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
EndOfRentalWindow());
// Allow playback that starts within rental window, but terminate at end of
// rental window.
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfRentalWindow());
}
// Playback within playback duration, restart exceeds rental duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case7) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfRentalWindow());
// Restart does not work after end of playback window.
ForbidPlayback(EndOfRentalWindow() + 10);
}
// Playback exceeds rental and playback duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case8) {
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
}
// First playback cannot exceed rental duration.
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case9) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
/*****************************************************************************/
// Seven Day / Two Day. The user must start watching within 7 days. Once
// started, the user has two days to finish the video. Playback is cutoff by the
// rental duration time limits. The first four cases start on day three. (See
// above for note on Use Case tests)
class ODKUseCase_SevenHardTwoSoft_Start3 : public ODKTimerTest {
public:
ODKUseCase_SevenHardTwoSoft_Start3() {
// Rental duration = 700, hard
// Playback duration = 200, hard
timer_limits_.rental_duration_seconds = 700;
timer_limits_.soft_enforce_rental_duration = false;
timer_limits_.total_playback_duration_seconds = 200;
timer_limits_.soft_enforce_playback_duration = true;
start_of_playback_ = GetSystemTime(300);
}
};
// Playback within playback and rental duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case1) {
// As seen in the drawing, the playback window is within the rental window.
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
EndOfRentalWindow());
}
// Playback exceeds playback duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case2) {
// Allow playback within the playback window, and a little after.
// Timer expires at end of rental window.
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 50,
EndOfRentalWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfRentalWindow());
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
EndOfRentalWindow());
LoadAndAllowPlayback(start_of_playback_ + 125, EndOfPlaybackWindow() + 50,
EndOfRentalWindow());
}
// Restart exceeds playback duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case4) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfRentalWindow());
ForbidPlayback(EndOfPlaybackWindow() + 10);
}
// The next four cases start on day six.
class ODKUseCase_SevenHardTwoSoft_Start6
: public ODKUseCase_SevenHardTwoSoft_Start3 {
public:
ODKUseCase_SevenHardTwoSoft_Start6() {
start_of_playback_ = GetSystemTime(600);
}
};
// Playback exceeds rental duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case5) {
// As seen in the drawing, the playback window is exceeds the rental window.
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// rental duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case6) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfRentalWindow());
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
EndOfRentalWindow());
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfRentalWindow());
}
// Playback within playback duration, restart exceeds rental duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case7) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfRentalWindow());
// Restart does not work after end of playback window.
ForbidPlayback(EndOfRentalWindow() + 10);
}
// Playback exceeds rental and playback duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case8) {
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
}
// First playback cannot exceed rental duration.
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case9) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
/*****************************************************************************/
// Seven Day / Two Day. The user must start watching within 7 days. Once
// started, the user has two days to finish the video. Playback is cutoff by the
// playback duration. The first four cases start on day three. (See above for
// note on Use Case tests)
class ODKUseCase_SevenSoftTwoHard_Start3 : public ODKTimerTest {
public:
ODKUseCase_SevenSoftTwoHard_Start3() {
// Rental duration = 700, hard
// Playback duration = 300, hard
timer_limits_.rental_duration_seconds = 700;
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.total_playback_duration_seconds = 200;
timer_limits_.soft_enforce_playback_duration = false;
start_of_playback_ = GetSystemTime(300);
}
};
// Playback within playback and rental duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case1) {
// As seen in the drawing, the playback window is within the rental window.
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
EndOfPlaybackWindow());
}
// Playback exceeds playback duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case2) {
// Allow playback within the playback window, but not beyond.
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfPlaybackWindow());
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
EndOfPlaybackWindow());
LoadAndTerminatePlayback(start_of_playback_ + 125, EndOfPlaybackWindow());
}
// Restart exceeds playback duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case4) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
EndOfPlaybackWindow());
ForbidPlayback(EndOfPlaybackWindow() + 10);
}
// The next four cases start on day six.
class ODKUseCase_SevenSoftTwoHard_Start6
: public ODKUseCase_SevenSoftTwoHard_Start3 {
public:
ODKUseCase_SevenSoftTwoHard_Start6() {
start_of_playback_ = GetSystemTime(600);
}
};
// Playback exceeds rental duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case5) {
// As seen in the drawing, the playback window exceeds the rental window.
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback to continue beyond the rental window, but not beyond the
// playback window.
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
}
// Playback with stops/restarts within playback duration, last one exceeds
// rental duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case6) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfPlaybackWindow());
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
EndOfPlaybackWindow());
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfPlaybackWindow());
}
// Restart exceeds rental duration, playback exceeds playback duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case7) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
EndOfPlaybackWindow());
LoadAndTerminatePlayback(EndOfRentalWindow() + 10, EndOfPlaybackWindow());
}
// Playback exceeds rental and playback duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case8) {
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
}
// First playback cannot exceed rental duration.
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case9) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
/*****************************************************************************/
// Seven Day / Two Day. The user must start watching within 7 days. Once
// started, the user has two days to finish the video. Playback is not cutoff,
// but restarts are not allowed after playback duration. The first four cases
// start on day three. (See above for note on Use Case tests)
class ODKUseCase_SevenSoftTwoSoft_Start3 : public ODKTimerTest {
public:
ODKUseCase_SevenSoftTwoSoft_Start3() {
// Rental duration = 700, hard
// Playback duration = 200, hard
timer_limits_.rental_duration_seconds = 700;
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.total_playback_duration_seconds = 200;
timer_limits_.soft_enforce_playback_duration = true;
start_of_playback_ = GetSystemTime(300);
}
};
// Playback within playback and rental duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case1) {
// As seen in the drawing, the playback window is within the rental window.
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
// Allow playback within the rental window.
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
}
// Playback exceeds playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case2) {
// Allow playback within the playback window, and beyond. No timer limit.
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 50, 0);
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case3) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50, 0);
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100, 0);
LoadAndAllowPlayback(start_of_playback_ + 125, EndOfPlaybackWindow() + 50, 0);
}
// Restart exceeds playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case4) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50, 0);
ForbidPlayback(EndOfPlaybackWindow() + 10);
}
// The next four cases start on day six.
class ODKUseCase_SevenSoftTwoSoft_Start6
: public ODKUseCase_SevenSoftTwoSoft_Start3 {
public:
ODKUseCase_SevenSoftTwoSoft_Start6() {
start_of_playback_ = GetSystemTime(600);
}
};
// Playback exceeds rental duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case5) {
// As seen in the drawing, the playback window exceeds the rental window.
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow() + 25);
// Allow playback past the rental window, but within the playback window.
LoadAndAllowPlayback(start_of_playback_, EndOfRentalWindow() + 25, 0);
}
// Playback with stops/restarts within playback duration, last one exceeds
// rental and playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case6) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25, 0);
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75, 0);
LoadAndAllowPlayback(start_of_playback_ + 100, EndOfPlaybackWindow() + 50, 0);
}
// Playback with stops/restarts within playback duration, last one exceeds
// playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case7) {
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25, 0);
// Allow playback to start after end of rental window, and continue after
// playback window.
LoadAndAllowPlayback(EndOfRentalWindow() + 10, EndOfPlaybackWindow() + 10, 0);
// But forbid restart after playback window.
ForbidPlayback(EndOfPlaybackWindow() + 20);
}
// Playback exceeds rental and playback duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case8) {
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 100, 0);
}
// First playback cannot exceed rental duration.
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case9) {
ForbidPlayback(EndOfRentalWindow() + 10);
}
class RenewalTest : public ODKTimerTest {
protected:
// Verify that a renewal can be processed and playback may continue from
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
// with a green check mark.
void RenewAndContinue(uint64_t start, uint64_t stop, uint64_t cutoff,
uint64_t renewal_duration_seconds) {
uint64_t timer_value;
RenewAndContinue(start, stop, cutoff, renewal_duration_seconds,
&timer_value);
}
// Verify that a renewal can be processed and playback may continue from
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
// with a green check mark.
// This does the work of the function above, except it allows the caller to
// explicitly set the timer_value_pointer. The timer_value_pointer can be a
// nullptr.
void RenewAndContinue(uint64_t start, uint64_t stop, uint64_t cutoff,
uint64_t renewal_duration_seconds,
uint64_t* timer_value_pointer) {
ASSERT_LT(start, stop);
if (cutoff > 0) ASSERT_LE(stop, cutoff);
// We'll fake instantaneous renewal requests. Flight time not important.
clock_values_.time_of_renewal_request = start;
const OEMCryptoResult result = ODK_ComputeRenewalDuration(
&timer_limits_, &clock_values_, start, renewal_duration_seconds,
timer_value_pointer);
// After first playback, the license is active.
EXPECT_EQ(clock_values_.status, kActive);
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
if (cutoff > 0) { // If we expect the timer to be set.
EXPECT_EQ(result, ODK_SET_TIMER);
if (timer_value_pointer != nullptr)
EXPECT_EQ(*timer_value_pointer, cutoff - start);
} else {
EXPECT_EQ(result, ODK_DISABLE_TIMER);
}
EXPECT_EQ(ODK_UpdateLastPlaybackTime(start, &timer_limits_, &clock_values_),
OEMCrypto_SUCCESS);
CheckClockValues(start);
const uint64_t mid = (start + stop) / 2;
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
OEMCrypto_SUCCESS);
CheckClockValues(mid);
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
OEMCrypto_SUCCESS);
CheckClockValues(stop);
}
// Verify that a renewal can be processed and attempt to play from |start| to
// after |cutoff|. Verify that we are allowed playback from |start| to
// |cutoff|, but playback not allowed after |cutoff|. If you refer to the
// diagrams in "License Duration and Renewal", this tests a cyan bar with a
// black X.
void RenewAndTerminate(uint64_t start, uint64_t cutoff,
uint64_t renewal_duration_seconds) {
RenewAndTerminate(start, cutoff, cutoff + 100, renewal_duration_seconds);
}
// Verify that a renewal can be processed and attempt to play from |start| to
// |stop|. Verify that we are allowed playback from |start| to |cutoff|, but
// playback not allowed after |cutoff|. If you refer to the diagrams in
// "License Duration and Renewal", this tests a cyan bar with a black X. This
// assumes that |cutoff| is between |start| and |stop|.
void RenewAndTerminate(uint64_t start, uint64_t cutoff, uint64_t stop,
uint64_t renewal_duration_seconds) {
ASSERT_LT(start, cutoff);
ASSERT_LT(cutoff, stop);
RenewAndContinue(start, cutoff, cutoff, renewal_duration_seconds);
EXPECT_EQ(
ODK_UpdateLastPlaybackTime(cutoff + 1, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(cutoff);
const uint64_t mid = (cutoff + stop) / 2;
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(
cutoff); // times do not change if playback was not allowed.
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(cutoff);
}
// Verify that a renewal can be processed and playback may start from
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
// with a green check mark. This is different from the previous functions,
// because the renewal is loaded before the first playback.
void RenewAndStart(uint64_t start, uint64_t stop, uint64_t cutoff,
uint64_t renewal_duration_seconds) {
ASSERT_LT(start, stop);
if (cutoff > 0) ASSERT_LE(stop, cutoff);
// We'll fake instantaneous renewal requests. Flight time not important.
clock_values_.time_of_renewal_request = start;
uint64_t timer_value;
const OEMCryptoResult result =
ODK_ComputeRenewalDuration(&timer_limits_, &clock_values_, start,
renewal_duration_seconds, &timer_value);
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
if (cutoff > 0) { // If we expect the timer to be set.
EXPECT_EQ(result, ODK_SET_TIMER);
EXPECT_EQ(timer_value, cutoff - start);
} else {
EXPECT_EQ(result, ODK_DISABLE_TIMER);
}
AllowPlayback(start, stop, cutoff);
}
};
// License with Renewal, limited by playback duration. (See above for note on
// Use Case tests) These tests are parameterized. If the parameter is 0, we
// limit the playback duration. If the parameter is 1, we limit the rental
// duration. The behavior is basically the same.
class ODKUseCase_LicenseWithRenewal
: public RenewalTest,
public ::testing::WithParamInterface<int> {
public:
ODKUseCase_LicenseWithRenewal() {
// Either Playback or rental duration = 2 days hard
if (GetParam() == 0) {
timer_limits_.soft_enforce_rental_duration = false;
timer_limits_.rental_duration_seconds = 2000;
} else {
timer_limits_.soft_enforce_playback_duration = false;
timer_limits_.total_playback_duration_seconds = 2000;
}
timer_limits_.initial_renewal_duration_seconds = 50;
}
void SetUp() override {
RenewalTest::SetUp();
renewal_interval_ =
timer_limits_.initial_renewal_duration_seconds - kGracePeriod;
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
// Allow playback within the initial renewal window.
LoadAndAllowPlayback(start_of_playback_, next_renewal,
next_renewal + kGracePeriod);
}
uint64_t playback_end_restriction() {
if (GetParam() == 0) {
return EndOfRentalWindow();
} else {
return EndOfPlaybackWindow();
}
}
protected:
// How long to wait before we load the next renewal. i.e. cutoff -
// kGracePeriod. This is because the renewal_duration includes the grace
// period.
uint64_t renewal_interval_;
};
// Playback within rental duration and renewal duration.
TEST_P(ODKUseCase_LicenseWithRenewal, Case1) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
for (int i = 0; i < 4; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
}
// Playback within rental duration, last renewal_interval_ exceeds renewal
// duration.
TEST_P(ODKUseCase_LicenseWithRenewal, Case2) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
for (int i = 0; i < 4; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// We attempt to continue playing beyond the renewal renewal_interval_.
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// Playback interrupted by late renewal.
TEST_P(ODKUseCase_LicenseWithRenewal, Case3) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
for (int i = 0; i < 4; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// We attempt to continue playing beyond the renewal renewal_interval_.
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
next_renewal + kGracePeriod, // stop: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
const uint64_t late_renewal = next_renewal + kGracePeriod + 10;
next_renewal = late_renewal + renewal_interval_;
RenewAndContinue(late_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// Playback & restart within playback duration and renewal duration. Note: this
// simulates reloading a persistent license, and then reloading the last
// renewal.
TEST_P(ODKUseCase_LicenseWithRenewal, Case4) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
for (int i = 0; i < 2; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// Reload license after timer expired.
uint64_t reload_time = next_renewal + kGracePeriod + 100;
next_renewal = reload_time + renewal_interval_;
ReloadLicense(reload_time);
RenewAndStart(reload_time, next_renewal, next_renewal + kGracePeriod,
timer_limits_.initial_renewal_duration_seconds);
for (int i = 0; i < 2; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
}
// Playback within renewal duration, but exceeds playback duration.
TEST_P(ODKUseCase_LicenseWithRenewal, Case5) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
do {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
} while ((next_renewal + renewal_interval_ + kGracePeriod) <
playback_end_restriction());
// Attempt playing beyond the playback window.
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
playback_end_restriction(), // stop: when timer expires.
timer_limits_.initial_renewal_duration_seconds);
}
// Renewal duration increases over time.
TEST_P(ODKUseCase_LicenseWithRenewal, Case6) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
do {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
renewal_interval_ + kGracePeriod);
// Renewal_Interval_ increases with each renewal.
renewal_interval_ += 100;
} while (next_renewal + renewal_interval_ + kGracePeriod <
playback_end_restriction());
// Attempt playing beyond the playback window:
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
playback_end_restriction(), // cutoff: when timer expires.
renewal_interval_ + kGracePeriod);
}
// Increasing renewal duration, playback exceeds last renewal.
// This is a mix between case 2 and case 6.
TEST_P(ODKUseCase_LicenseWithRenewal, Case7) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
for (int i = 0; i < 4; i++) {
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndContinue(
current_renewal, // start: when renewal is loaded.
next_renewal, // stop: expect play allowed.
next_renewal + kGracePeriod, // cutoff: when timer expires.
renewal_interval_ + kGracePeriod);
// Renewal_Interval_ increases with each renewal.
renewal_interval_ += 100;
}
const uint64_t current_renewal = next_renewal;
next_renewal = current_renewal + renewal_interval_;
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
next_renewal + kGracePeriod, // cutoff: when timer expires.
renewal_interval_ + kGracePeriod);
}
// This is just like Case1, except we use a null pointer for the timer values in
// RenewAndContinue. It is not shown in the use case document.
TEST_P(ODKUseCase_LicenseWithRenewal, NullPointerTest) {
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
const uint64_t start = next_renewal;
const uint64_t stop = start + renewal_interval_;
const uint64_t cutoff = stop + kGracePeriod;
const uint64_t renewal_duration_seconds =
timer_limits_.initial_renewal_duration_seconds;
uint64_t* timer_value_pointer = nullptr;
RenewAndContinue(start, stop, cutoff, renewal_duration_seconds,
timer_value_pointer);
}
INSTANTIATE_TEST_SUITE_P(RestrictRenewal, ODKUseCase_LicenseWithRenewal,
::testing::Values(0, 1));
// Limited Duration License. (See above for notes on Use Case tests). The user
// has 15 minutes to begin watching the movie. If a renewal is not received,
// playback is terminated after 30 seconds. If a renewal is received, playback
// may continue for two hours from playback start.
class ODKUseCase_LimitedDurationLicense : public RenewalTest {
public:
ODKUseCase_LimitedDurationLicense() {
renewal_delay_ = 30u;
time_of_renewal_ = start_of_playback_ + renewal_delay_;
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.rental_duration_seconds = 150; // 15 minutes.
timer_limits_.soft_enforce_playback_duration = false;
timer_limits_.total_playback_duration_seconds = 2000; // Two hours.
timer_limits_.initial_renewal_duration_seconds =
renewal_delay_ + kGracePeriod;
}
uint64_t renewal_delay_;
uint64_t time_of_renewal_;
};
// Playback started within rental window and continues.
TEST_F(ODKUseCase_LimitedDurationLicense, Case1) {
// Allow playback within the initial renewal window.
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
time_of_renewal_ + kGracePeriod);
const uint64_t renewal_duration = 2000; // two hour renewal.
const uint64_t play_for_one_hour = GetSystemTime(1000);
RenewAndContinue(time_of_renewal_, // start: when renewal is loaded.
play_for_one_hour, // stop: expect play allowed.
EndOfPlaybackWindow(), // cutoff: when timer expires.
renewal_duration);
}
// Playback started after rental duration.
TEST_F(ODKUseCase_LimitedDurationLicense, Case2) {
start_of_playback_ = EndOfRentalWindow() + 1;
ForbidPlayback(start_of_playback_);
}
// Playback started within rental window but renewal not received.
TEST_F(ODKUseCase_LimitedDurationLicense, Case3) {
// Allow playback within the initial renewal window.
LoadAndTerminatePlayback(start_of_playback_, time_of_renewal_ + kGracePeriod);
}
// Playback started within rental window, renewal is received, and playback
// continues.
TEST_F(ODKUseCase_LimitedDurationLicense, Case4) {
// Allow playback within the initial renewal window.
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
time_of_renewal_ + kGracePeriod);
const uint64_t renewal_duration = 2000; // two hour renewal.
RenewAndTerminate(time_of_renewal_, // start: when renewal is loaded.
EndOfPlaybackWindow(), // stop: when timer expires.
renewal_duration);
}
// Playback may be restarted.
TEST_F(ODKUseCase_LimitedDurationLicense, Case5) {
// Allow playback within the initial renewal window.
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
time_of_renewal_ + kGracePeriod);
const uint64_t renewal_duration = 2000; // two hour renewal.
const uint64_t play_for_one_hour = GetSystemTime(1000);
RenewAndContinue(time_of_renewal_, // start: when renewal is loaded.
play_for_one_hour, // stop: expect play allowed.
EndOfPlaybackWindow(), // cutoff: when timer expires.
renewal_duration);
const uint64_t reload_time = play_for_one_hour + 100;
ReloadLicense(reload_time);
// Simulate reloading the license, and then reloading the renewal, and then
// restarting playback. That is allowed, and playback shall be terminated at
// the end of the original playback window.
RenewAndStart(reload_time, EndOfPlaybackWindow(), EndOfPlaybackWindow(),
renewal_duration);
// But not one second more.
EXPECT_EQ(ODK_UpdateLastPlaybackTime(EndOfPlaybackWindow() + 1,
&timer_limits_, &clock_values_),
ODK_TIMER_EXPIRED);
CheckClockValues(EndOfPlaybackWindow());
}
// Verify that the backwards compatible function, ODK_InitializeV15Values, can
// set up the clock values and timer limits.
TEST_F(RenewalTest, V15Test) {
const uint32_t key_duration = 25;
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
const uint64_t license_loaded = GetSystemTime(10);
EXPECT_EQ(
ODK_InitializeV15Values(&timer_limits_, &clock_values_, &nonce_values,
key_duration, license_loaded),
OEMCrypto_SUCCESS);
const uint64_t later_on = GetSystemTime(200);
TerminatePlayback(license_loaded, license_loaded + key_duration, later_on);
const uint32_t new_key_duration = 100;
RenewAndTerminate(later_on, later_on + new_key_duration, new_key_duration);
}
} // namespace