odk: core serialization structs & functions

odk directory copied from wvgerrit.
branch oemcrypto-v16
commit 0c9a7dc

Bug: 140758896
Test: odk_test
Change-Id: I0c631f771b794468a63e4395f6b9c3b60a1dfd4f
This commit is contained in:
Robert Shih
2019-09-12 23:31:31 -07:00
parent 9ea47dc64a
commit 2443fe807a
22 changed files with 4642 additions and 0 deletions

View File

@@ -0,0 +1,936 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <gtest/gtest.h>
#include "odk.h"
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
namespace {
constexpr uint64_t kTolerance = 1; // Allow 1 second of roundoff.
} // namespace
namespace odk_test {
struct ServerExpiry {
bool soft_rental;
bool soft_playback;
};
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;
uint64_t time = 42;
ODK_InitializeClockValues(&clock_values, time);
EXPECT_EQ(clock_values.time_of_license_signed, time);
EXPECT_EQ(clock_values.time_of_first_decrypt, 0);
EXPECT_EQ(clock_values.time_of_last_decrypt, 0);
EXPECT_EQ(clock_values.time_when_timer_expires, 0);
EXPECT_EQ(clock_values.timer_status, 0);
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;
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_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, 0u);
EXPECT_EQ(clock_values.status, status);
}
// ************************************************************************
// Test that we can only start playback within the rental window. This
// simulates requesting a license at rental_clock_start_, and a rental window of
// 50-150s relative to license request, or 100-200s absolute.
class OdkTimerRentalWindow : public ::testing::Test {
public:
OdkTimerRentalWindow() {
rental_clock_start_ = 10000u;
rental_window_start_ = 50u;
rental_window_duration_ = 100u;
}
protected:
void SetUp() override {
::testing::Test::SetUp();
// Start rental clocks at rental_clock_start_.
ODK_InitializeClockValues(&clock_values_, rental_clock_start_);
// Simple license values:
timer_limits_.soft_expiry = false;
timer_limits_.earliest_playback_start_seconds = rental_window_start_;
timer_limits_.latest_playback_start_seconds =
rental_window_start_ + rental_window_duration_;
timer_limits_.initial_playback_duration_seconds = 0;
timer_limits_.renewal_playback_duration_seconds = 0;
timer_limits_.license_duration_seconds = 0;
}
// Simulate 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 ReloadClock(uint64_t system_time) {
ODK_ClockValues old_clock_values = clock_values_;
// First clear out the old clock values.
ODK_InitializeClockValues(&clock_values_, 0u);
ODK_ReloadClockValues(&clock_values_,
old_clock_values.time_of_license_signed,
old_clock_values.time_of_first_decrypt,
old_clock_values.time_of_last_decrypt,
old_clock_values.status, system_time);
}
uint64_t system_time(uint64_t rental_clock) {
return rental_clock_start_ + rental_clock;
}
ODK_TimerLimits timer_limits_;
ODK_ClockValues clock_values_;
// The rental clock starts when the request is signed.
uint64_t rental_clock_start_;
// start of rental window in seconds since rental clock start.
uint64_t rental_window_start_;
// The "width" of window.
uint64_t rental_window_duration_;
};
TEST_F(OdkTimerRentalWindow, EarlyTest) {
uint64_t timer_value = 0;
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
const uint64_t bad_start_time =
system_time(timer_limits_.earliest_playback_start_seconds - 10);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
&clock_values_, &timer_value));
const uint64_t good_start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(good_start_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(good_start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(good_start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0, clock_values_.time_when_timer_expires);
}
TEST_F(OdkTimerRentalWindow, NullTimer) {
// If OEMCrypto passes in a nullpointer, then the ODK should allow this.
uint64_t* timer_value_pointer = nullptr;
const uint64_t bad_start_time =
system_time(timer_limits_.earliest_playback_start_seconds - 10);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
&clock_values_, timer_value_pointer));
const uint64_t good_start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(good_start_time, &timer_limits_,
&clock_values_, timer_value_pointer));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(good_start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(good_start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0, clock_values_.time_when_timer_expires);
}
// Verify that an attempt to start playback outside the rental window fails.
TEST_F(OdkTimerRentalWindow, LateTest) {
uint64_t timer_value = 0;
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
const uint64_t start_time = system_time(201);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kUnused, clock_values_.status);
}
// ************************************************************************
// The Hard Rental use case is a strict time limit on playback.
class OdkTimerHardTest : public OdkTimerRentalWindow {
protected:
void SetUp() override {
OdkTimerRentalWindow::SetUp();
timer_limits_.soft_expiry = false; // Hard expiry.
// License duration is 200. The license starts when it was signed at 50,
// so the license is valid from 50-250. The rental window is 100-200 -- as
// inherited from the ODKRentalWindow class.
timer_limits_.license_duration_seconds = 200u;
}
};
TEST_F(OdkTimerHardTest, EarlyTest) {
uint64_t timer_value = 0;
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
const uint64_t bad_start_time =
system_time(timer_limits_.earliest_playback_start_seconds - 10);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(kUnused, clock_values_.status);
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = false, we should set a timer.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
}
TEST_F(OdkTimerHardTest, NormalTest) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = false, we should set a timer.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 1;
// This play time is outside the rental window. We are allowed to continue
// playback after the rental window expires as long as the first decrypt is
// within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time);
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
// Try to play after the cutoff time is not valid.
const uint64_t late_play_time = cutoff_time + 1;
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// This verifies that the rental window only affects the first load for the
// license, not the first load for the session.
TEST_F(OdkTimerHardTest, Reload) {
uint64_t timer_value = 0;
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(75, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kUnused, clock_values_.status);
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = false, we should set a timer.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
// We can restart playback before the cutoff time.
const uint64_t valid_restart_time = cutoff_time - 10;
ReloadClock(valid_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
// This restart is outside the rental window. We are allowed to
// restart playback after the rental window expires as long as the first
// decrypt for the license is within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_restart_time);
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(valid_restart_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance);
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 1;
// This play time is outside the rental window. That means we are allowed
// to continue playback after the rental window expires as long as the first
// decrypt is within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time);
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
// Try to play after the cutoff time is not valid.
const uint64_t late_play_time = cutoff_time + 1;
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
TEST_F(OdkTimerHardTest, ReloadLate) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = false, we should set a timer.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
// We can not restart playback after the cutoff time.
const uint64_t late_restart_time = cutoff_time + 10;
ReloadClock(late_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
// Calling UpdateLastPlaybackTimer should not be done, but if it were done,
// it should also fail.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
}
// ************************************************************************
// The Soft Rental use case is a strict time limit on playback.
class OdkTimerSoftTest : public OdkTimerRentalWindow {
protected:
void SetUp() override {
OdkTimerRentalWindow::SetUp();
timer_limits_.soft_expiry = true; // Soft expiry.
// License duration is 200. The license starts when it was signed at 50,
// so the license is valid from 50-250. The rental window is 100-200 -- as
// inherited from the ODKRentalWindow class.
timer_limits_.license_duration_seconds = 200u;
}
};
TEST_F(OdkTimerSoftTest, EarlyTest) {
uint64_t timer_value = 0;
// An attempt to start playback before the timer starts is an error.
// We use the TIMER_EXPIRED error to mean both early or late.
const uint64_t bad_start_time =
system_time(timer_limits_.earliest_playback_start_seconds - 10);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
&clock_values_, &timer_value));
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = true, we should not set a timer.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0u, clock_values_.time_when_timer_expires);
}
TEST_F(OdkTimerSoftTest, NormalTest) {
uint64_t timer_value = 0;
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = true, we should not set a timer.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0u, clock_values_.time_when_timer_expires);
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 1;
// This play time is outside the rental window. We are allowed to continue
// playback after the rental window expires as long as the first decrypt is
// within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time);
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
// Try to play after the cutoff time is still valid for soft expiry.
const uint64_t late_play_time = cutoff_time + 1;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
}
// This verifies that the rental window only affects the first load for the
// license, not the first load for the session.
TEST_F(OdkTimerSoftTest, Reload) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = true, we should not set a timer.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0, clock_values_.time_when_timer_expires);
// We can restart playback before the cutoff time.
const uint64_t valid_restart_time = cutoff_time - 10;
ReloadClock(valid_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
// This restart is outside the rental window. We are allowed to
// restart playback after the rental window expires as long as the first
// decrypt for the license is within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_restart_time);
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(valid_restart_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0, clock_values_.time_when_timer_expires);
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 1;
// This play time is outside the rental window. That means we are allowed
// to continue playback after the rental window expires as long as the first
// decrypt is within the rental window.
EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time);
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0u, clock_values_.time_when_timer_expires);
// Try to play after the cutoff time is still valid.
const uint64_t late_play_time = cutoff_time + 1;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0u, clock_values_.time_when_timer_expires);
}
TEST_F(OdkTimerSoftTest, ReloadLate) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = true, we should not set a timer.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
EXPECT_EQ(0u, clock_values_.time_when_timer_expires);
// We can not restart playback after the cutoff time.
const uint64_t late_restart_time = cutoff_time + 10;
ReloadClock(late_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
// Calling UpdateLastPlaybackTimer should not be done, but if it were done,
// it should also fail.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
}
// ************************************************************************
// ************************************************************************
// From the server's point of view, there are two flags that decide soft or
// hard limits: the rental duration, and the playback duration. From
// OEMCrypto's point of view, there is only playback duration. A soft or hard
// rental duration is translated into different rental and license durations.
// The four test classes below all have a 700 rental window and a 200 playback
// duration. We'll use the server description, and then set the OEMCrypto
// restraints in the test SetUp() function. Note, it's easier to use the word
// "day" but really the rental window is 700 seconds, not 7 days. These tests
// have some coverage overlap with the ones above, but it is better to have
// these all grouped together to make sure we cover all of the server team's
// needs.
// ************************************************************************
class Odk7DayTest : public OdkTimerRentalWindow,
public WithParamInterface<ServerExpiry> {
public:
Odk7DayTest() {
rental_window_duration_ = 700;
rental_window_start_ = 100u;
}
protected:
void SetUp() override {
OdkTimerRentalWindow::SetUp();
server_expiry_ = GetParam();
const uint64_t playback_duration = 200;
// Beginning of rental window. it is unusual to start it in the future,
// but it is a supported use case, so we'll test it here.
timer_limits_.earliest_playback_start_seconds = rental_window_start_;
// The rental window is 700 long.
timer_limits_.latest_playback_start_seconds =
timer_limits_.earliest_playback_start_seconds + rental_window_duration_;
// Playback duration is 200 starting from first playback.
timer_limits_.initial_playback_duration_seconds = playback_duration;
if (server_expiry_.soft_rental) {
// The license duration limits any restart. For soft rental window, the
// server will set this to the rental end + playback duration.
// License duration is in seconds since license signed.
timer_limits_.license_duration_seconds =
timer_limits_.latest_playback_start_seconds + playback_duration;
} else {
// The license duration limits any restart. For hard rental window, the
// server will set this to the rental end.
// License duration is in seconds since license signed.
timer_limits_.license_duration_seconds =
timer_limits_.latest_playback_start_seconds;
}
timer_limits_.soft_expiry = server_expiry_.soft_playback;
}
ServerExpiry server_expiry_;
};
TEST_P(Odk7DayTest, StartDay3) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t three_days = 300u;
const uint64_t start_time = system_time(rental_window_start_ + three_days);
const uint64_t cutoff_time =
start_time + timer_limits_.initial_playback_duration_seconds;
if (server_expiry_.soft_playback) {
// If the playback expiry is soft, then the timer is disabled.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_,
&clock_values_, &timer_value));
} else {
// If the playback expiry is hard, then the timer should be set.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
}
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 50;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
if (!server_expiry_.soft_playback) {
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
}
TEST_P(Odk7DayTest, StartDay3Reload) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t three_days = 300u;
const uint64_t start_time = system_time(rental_window_start_ + three_days);
const uint64_t cutoff_time =
start_time + timer_limits_.initial_playback_duration_seconds;
EXPECT_NE(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
// -----------------------------------------------------------------------
// Try to reload and play before the cutoff time is valid.
uint64_t valid_restart_time = cutoff_time - 10;
ReloadClock(valid_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
if (server_expiry_.soft_playback) {
// If the playback expiry is soft, then the timer is disabled.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
} else {
// If the playback expiry is hard, then the timer should be set.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance);
}
// Try to play before the cutoff time is valid.
const uint64_t valid_play_time = cutoff_time - 1;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
if (!server_expiry_.soft_playback) {
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// Try to play after the cutoff time is valid for soft expiry, but not hard.
const uint64_t late_play_time = cutoff_time + 1;
if (server_expiry_.soft_playback) {
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// Last decrypt should change because we were allowed to decrypt.
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
} else {
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// First decrypt should NOT change for either soft or hard.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// -----------------------------------------------------------------------
// Try to reload after the cutoff time is not valid for soft or hard
// playback expiry.
const uint64_t late_restart_time = cutoff_time + 1;
ReloadClock(late_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Calling UpdateLastPlaybackTimer should not be done, but if it were done,
// it should also fail.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should not change.
if (server_expiry_.soft_playback) {
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
} else {
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
}
}
TEST_P(Odk7DayTest, StartDay6Reload) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t six_days = 600u;
const uint64_t start_time = system_time(rental_window_start_ + six_days);
const uint64_t cutoff_time =
server_expiry_.soft_rental
?
// If the rental expiry is soft, we can continue playing and
// reloading for the full playback duration.
start_time + timer_limits_.initial_playback_duration_seconds
// If the rental expiry is hard, we can reload only within the
// rental window.
: system_time(timer_limits_.latest_playback_start_seconds);
// Let's double check that math:
if (server_expiry_.soft_rental) {
// If that was not clear, the cutoff = 100 start of window + 600 start time
// + 200 playback duration...
EXPECT_EQ(system_time(900u), cutoff_time);
} else {
// ...and for hard rental, the cutoff = 100 start of window + 700 rental
// duration.
EXPECT_EQ(system_time(800u), cutoff_time);
}
if (server_expiry_.soft_playback) {
// If the playback expiry is soft, then the timer is disabled.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_,
&clock_values_, &timer_value));
} else {
// If the playback expiry is hard, then the timer should be set.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance);
}
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt);
// Try to play before the cutoff time is valid.
uint64_t valid_play_time = cutoff_time - 50;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
if (!server_expiry_.soft_playback) {
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// -----------------------------------------------------------------------
// Try to reload and play before the cutoff time is valid.
uint64_t valid_restart_time = cutoff_time - 10;
ReloadClock(valid_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
if (server_expiry_.soft_playback) {
// If the playback expiry is soft, then the timer is disabled.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
} else {
// If the playback expiry is hard, then the timer should be set.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance);
}
// Try to play before the cutoff time is valid.
valid_play_time = cutoff_time - 1;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_,
&clock_values_));
EXPECT_EQ(kActive, clock_values_.status);
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should change.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
if (!server_expiry_.soft_playback) {
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// Try to play after the cutoff time is valid for soft expiry, but not hard.
const uint64_t late_play_time = cutoff_time + 1;
if (server_expiry_.soft_playback) {
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// Last decrypt should change because we were allowed to decrypt.
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
} else {
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_,
&clock_values_));
// Last decrypt should NOT change because we were not allowed to decrypt.
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance);
}
// First decrypt should NOT change for either soft or hard.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// -----------------------------------------------------------------------
// Try to reload after the cutoff time is not valid for soft or hard
// playback expiry.
const uint64_t late_restart_time = cutoff_time + 2;
ReloadClock(late_restart_time);
EXPECT_EQ(kActive, clock_values_.status);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Calling UpdateLastPlaybackTimer should not be done, but if it were done,
// it should also fail.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_,
&clock_values_));
// First decrypt should NOT change.
EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt);
// Last decrypt should not change.
if (server_expiry_.soft_playback) {
EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt);
} else {
EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt);
}
}
// This test explicitly shows the difference between hard and soft rental
// expiry. This is not an OEMCrypto concept, but it is used on the serer, and
// this test verifies the logic is handled correctly when we translate it into
// OEMCrypto concepts.
TEST_P(Odk7DayTest, StartDay6ReloadDay7) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t six_days = 600u;
const uint64_t seven_days = 700u;
const uint64_t start_time = system_time(rental_window_start_ + six_days);
EXPECT_NE(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
// Reload time is 1 second after end of rental window.
const uint64_t restart_time =
system_time(timer_limits_.latest_playback_start_seconds + 1);
ReloadClock(restart_time);
if (server_expiry_.soft_rental) {
if (server_expiry_.soft_playback) {
// If the playback expiry is soft, then the timer is disabled.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(restart_time, &timer_limits_,
&clock_values_, &timer_value));
} else {
const uint64_t cutoff_time =
start_time + timer_limits_.initial_playback_duration_seconds;
// If the playback expiry is hard, then the timer should be set.
EXPECT_EQ(ODK_SET_TIMER,
ODK_AttemptFirstPlayback(restart_time, &timer_limits_,
&clock_values_, &timer_value));
EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires,
kTolerance);
EXPECT_NEAR(cutoff_time - restart_time, timer_value, kTolerance);
}
} else {
// For hard rental expiry, reloading after the rental window fails.
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(restart_time, &timer_limits_,
&clock_values_, &timer_value));
}
}
TEST_P(Odk7DayTest, StartDay8) {
uint64_t timer_value = 0;
// Starting playback after the rental window should not work.
const uint64_t eight_days = 800u;
const uint64_t start_time = system_time(rental_window_start_ + eight_days);
EXPECT_EQ(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
&timer_value));
EXPECT_EQ(kUnused, clock_values_.status);
EXPECT_EQ(0u, clock_values_.time_of_first_decrypt);
EXPECT_EQ(0u, clock_values_.time_of_last_decrypt);
// Calling UpdateLastPlaybackTimer should not be done, but if it were done,
// it should also fail.
EXPECT_EQ(ODK_TIMER_EXPIRED, ODK_UpdateLastPlaybackTime(
start_time, &timer_limits_, &clock_values_));
EXPECT_EQ(kUnused, clock_values_.status);
EXPECT_EQ(0u, clock_values_.time_of_first_decrypt);
EXPECT_EQ(0u, clock_values_.time_of_last_decrypt);
}
INSTANTIATE_TEST_CASE_P(OdkSoftHard, Odk7DayTest,
Values(ServerExpiry({true, true}),
ServerExpiry({true, false}),
ServerExpiry({false, true}),
ServerExpiry({false, false})));
// ************************************************************************
// ************************************************************************
// TODO(b/140765031): Cover all tests in Use Cases document.
// Limited Duration License
// 7 day with renewal.
// Streaming with renewal
// Persistent with renewal
} // namespace odk_test