Source release 16.3.0

This commit is contained in:
John W. Bruce
2020-07-24 14:30:03 -07:00
parent b830b1d1fb
commit 160df9f57a
74 changed files with 4632 additions and 2561 deletions

View File

@@ -14,12 +14,6 @@
#include <openssl/x509.h>
#include <stdint.h>
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/time.h>
#endif
#include <gtest/gtest.h>
#include <algorithm>
#include <chrono>
@@ -122,37 +116,6 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
// const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576};
// clang-format on
/** @return The Unix time of the given time point. */
template <typename Duration>
uint64_t UnixTime(
const std::chrono::time_point<std::chrono::system_clock, Duration>& point) {
return point.time_since_epoch() / std::chrono::seconds(1);
}
#ifdef _WIN32
using NativeTime = SYSTEMTIME;
#else
using NativeTime = timeval;
#endif
void AddNativeTime(int64_t delta_seconds, NativeTime* time) {
#ifdef _WIN32
// See remarks from this for why this series is used.
// https://msdn.microsoft.com/en-us/f77cdf86-0f97-4a89-b565-95b46fa7d65b
FILETIME file_time;
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
long_time +=
delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
file_time.dwHighDateTime = long_time >> 32;
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
#else
time->tv_sec += delta_seconds;
#endif
}
} // namespace
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
@@ -191,13 +154,13 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
// tests are failing when the device has the wrong keybox installed.
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 16.2. Tests last updated 2020-03-27";
"OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01";
cout << " " << log_message << "\n";
LOGI("%s", log_message.c_str());
// If any of the following fail, then it is time to update the log message
// above.
EXPECT_EQ(ODK_MAJOR_VERSION, 16);
EXPECT_EQ(ODK_MINOR_VERSION, 2);
EXPECT_EQ(ODK_MINOR_VERSION, 3);
EXPECT_EQ(kCurrentAPI, 16u);
const char* level = OEMCrypto_SecurityLevel();
ASSERT_NE(nullptr, level);
@@ -725,6 +688,7 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
ASSERT_NO_FATAL_FAILURE(s.open());
// Install the DRM Cert's RSA key.
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey());
// Request the OEM Cert. -- This should NOT load the OEM Private key.
vector<uint8_t> public_cert;
size_t public_cert_length = 0;
@@ -890,7 +854,13 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// Load license in a different session, which did not create the request.
Session session2;
ASSERT_NO_FATAL_FAILURE(session2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
}
// Verify that a license may be loaded with a nonce.
@@ -1865,7 +1835,7 @@ TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) {
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
const size_t max_size = GetResourceValue(kLargeMessageSize);
license_messages_.set_message_size(max_size);
renewal_messages.set_message_size(max_size);
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
}
@@ -2233,10 +2203,11 @@ class OEMCryptoSessionTestsDecryptTests
void TestDecryptCENC() {
OEMCryptoResult sts;
// OEMCrypto only supports providing a decrypt hash for one sample.
if (samples_.size() > 1) verify_crc_ = false;
// If supported, check the decrypt hashes.
if (verify_crc_) {
// OEMCrypto only supports providing a decrypt hash for the first sample
// in the sample array.
const TestSample& sample = samples_[0];
uint32_t hash =
@@ -2291,7 +2262,7 @@ class OEMCryptoSessionTestsDecryptTests
}
}
}
if (global_features.supports_crc) {
if (verify_crc_) {
uint32_t frame;
ASSERT_EQ(OEMCrypto_GetHashErrorCode(session_.session_id(), &frame),
OEMCrypto_SUCCESS);
@@ -2611,18 +2582,6 @@ TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) {
session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// If analog is forbidden, then decrypt to a clear buffer should be forbidden.
TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(wvoec::kControlDisableAnalogOutput);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT));
}
// Test that key duration is honored.
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
@@ -4201,6 +4160,11 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
}
}
void ResizeBuffer(size_t new_size) {
buffer_size_ = new_size;
InitializeClearBuffer(); // Re-initialize the clear buffer.
}
void EncryptAndLoadKeys() {
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
@@ -4536,7 +4500,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) {
// Test Generic_Encrypt with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
@@ -4559,7 +4523,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
// Test Generic_Decrypt with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) {
// Some applications are known to pass in a block that is almost 400k.
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 1;
vector<uint8_t> encrypted;
@@ -4580,7 +4544,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) {
// Test Generic_Sign with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) {
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 2;
vector<uint8_t> expected_signature;
@@ -4608,7 +4572,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) {
// Test Generic_Verify with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 3;
vector<uint8_t> signature;
@@ -4917,6 +4881,7 @@ class LicenseWithUsageEntry {
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(session_.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_));
ASSERT_NO_FATAL_FAILURE(session_.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
@@ -5719,19 +5684,29 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
void ShrinkHeader(uint32_t new_size,
OEMCryptoResult expected_result = OEMCrypto_SUCCESS) {
// We call OEMCrypto_ShrinkUsageTableHeader once with a zero length buffer,
// so that OEMCrypto can tell us how big the buffer should be.
size_t header_buffer_length = 0;
OEMCryptoResult sts = OEMCrypto_ShrinkUsageTableHeader(
new_size, nullptr, &header_buffer_length);
// If we are expecting success, then the first call shall return
// SHORT_BUFFER. However, if we are not expecting success, this first call
// may return either SHORT_BUFFER or the expect error.
if (expected_result == OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
} else {
ASSERT_NE(OEMCrypto_SUCCESS, sts);
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return;
} else if (sts != OEMCrypto_ERROR_SHORT_BUFFER) {
// If we got any thing from the first call, it should be the expected
// error, and we don't need to call a second time.
ASSERT_EQ(expected_result, sts);
return;
}
// If the first call resulted in SHORT_BUFFER, we should resize the buffer
// and try again.
ASSERT_LT(0u, header_buffer_length);
encrypted_usage_header_.resize(header_buffer_length);
sts = OEMCrypto_ShrinkUsageTableHeader(
new_size, encrypted_usage_header_.data(), &header_buffer_length);
// For the second call, we always demand the expected result.
ASSERT_EQ(expected_result, sts);
}
};
@@ -6030,6 +6005,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
old_usage_entry_1.data(),
old_usage_entry_1.size()));
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
}
@@ -6200,44 +6176,12 @@ class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
public:
void SetUp() override {
OEMCryptoUsageTableTest::SetUp();
did_change_system_time_ = false;
test_start_steady_ = steady_clock_.now();
#ifdef _WIN32
GetSystemTime(&test_start_wall_);
#else
ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr));
#endif
}
void TearDown() override {
if (did_change_system_time_) {
const auto delta = steady_clock_.now() - test_start_steady_;
const int64_t delta_sec = delta / std::chrono::seconds(1);
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec));
}
wvcdm::TestSleep::ResetRollback();
OEMCryptoUsageTableTest::TearDown();
}
protected:
/**
* Sets the current wall-clock time to a delta based on the start of the
* test.
*/
void SetWallTimeDelta(int64_t delta_seconds) {
did_change_system_time_ = true;
NativeTime time = test_start_wall_;
ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time));
#ifdef _WIN32
ASSERT_TRUE(SetSystemTime(&time));
#else
ASSERT_EQ(0, settimeofday(&time, nullptr));
#endif
}
std::chrono::steady_clock steady_clock_;
bool did_change_system_time_;
NativeTime test_start_wall_;
std::chrono::time_point<std::chrono::steady_clock> test_start_steady_;
};
// NOTE: This test needs root access since clock_settime messes with the system
@@ -6246,61 +6190,104 @@ class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
// We don't test roll-forward protection or instances where the user rolls back
// the time to the last decrypt call since this requires hardware-secure clocks
// to guarantee.
//
// This test overlaps two tests in parallel because they each have several
// seconds of sleeping, then we roll the system clock back, and then we sleep
// some more.
// For the first test, we use entry1. The playback duration is 6 short
// intervals. We play for 3, roll the clock back 2, and then play for 3 more.
// We then sleep until after the allowed playback duration and try to play. If
// OEMCrypto allows the rollback, then there is only 5 intervals, which is
// legal. But if OEMCrypto forbids the rollback, then there is 8 intervals of
// playback, which is not legal.
//
// For the second test, we use entry2. The rental duration is 6 short
// intervals. The times are the same as for entry1, except we do not start
// playback for entry2 until the end.
// clang-format off
// [--][--][--][--][--][--][--] -- playback or rental limit.
//
// Here's what the system clock sees with rollback:
// [--][--][--] 3 short intervals of playback or sleep
// <------> Rollback 2 short intervals.
// [--][--][--] 3 short intervals of playback or sleep
// [--] 1 short intervals of sleep.
//
// Here's what the system clock sees without rollback:
// [--][--][--] 3 short intervals of playback or sleep
// [--][--][--] 3 short intervals of playback or sleep
// [--][--]X 2 short intervals of sleep.
//
// |<---------------------------->| 8 short intervals from license received
// until pst reports generated.
// clang-format on
TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
cout << "This test temporarily rolls back the system time in order to verify "
<< "that the usage report accounts for the change. It then rolls "
<< "the time back forward to the absolute time." << endl;
LicenseWithUsageEntry entry;
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
std::chrono::system_clock wall_clock;
std::chrono::steady_clock monotonic_clock;
const auto loaded = wall_clock.now();
<< "that the usage report accounts for the change. After the test, it "
<< "rolls the clock back forward." << endl;
constexpr int kRollBackTime = kShortSleep * 2;
constexpr int kPlaybackCount = 3;
constexpr int kTotalTime = kShortSleep * 8;
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
const auto first_decrypt = wall_clock.now();
// Monotonic clock can't be changed. We use this since system clock will be
// unreliable.
const auto first_decrypt_monotonic = monotonic_clock.now();
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
LicenseWithUsageEntry entry1;
entry1.license_messages()
.core_response()
.timer_limits.total_playback_duration_seconds = 7 * kShortSleep;
entry1.MakeOfflineAndClose(this);
Session& s1 = entry1.session();
ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this));
// Imitate playback.
wvcdm::TestSleep::Sleep(kLongDuration * 2);
LicenseWithUsageEntry entry2;
entry2.license_messages()
.core_response()
.timer_limits.rental_duration_seconds = 7 * kShortSleep;
entry2.MakeOfflineAndClose(this);
Session& s2 = entry2.session();
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
// Start with three short intervals of playback for entry1.
for (int i = 0; i < kPlaybackCount; i++) {
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
wvcdm::TestSleep::Sleep(kShortSleep);
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
}
// Rollback the wall clock time.
cout << "Rolling the system time back..." << endl;
ASSERT_TRUE(wvcdm::TestSleep::RollbackSystemTime(kRollBackTime));
// Three more short intervals of playback after the rollback.
for (int i = 0; i < kPlaybackCount; i++) {
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
wvcdm::TestSleep::Sleep(kShortSleep);
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
}
// One short interval of sleep to push us past the 6 interval duration.
wvcdm::TestSleep::Sleep(2 * kShortSleep);
// Should not be able to continue playback in entry1.
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(-static_cast<int64_t>(kLongDuration) * 10));
// Try to playback again.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
const auto third_decrypt_monotonic = monotonic_clock.now();
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
Test_PST_Report expected(entry.pst(), kActive);
// Restore wall clock to its original position to verify that OEMCrypto does
// not report negative times.
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
cout << "Rolling the system time forward to the absolute time..." << endl;
entry1.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
// Should not be able to start playback in entry2.
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(test_duration / std::chrono::seconds(1)));
// Need to update time created since the verification checks the time of PST
// report creation.
expected.time_created = UnixTime(wall_clock.now());
entry2.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED));
const auto end_time = first_decrypt + test_duration;
ASSERT_NO_FATAL_FAILURE(s.VerifyReport(
expected, UnixTime(loaded), UnixTime(first_decrypt), UnixTime(end_time)));
ASSERT_NO_FATAL_FAILURE(s.close());
// Now we look at the usage reports:
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(entry1.pst()));
wvcdm::Unpacked_PST_Report report1 = s1.pst_report();
EXPECT_EQ(report1.status(), kActive);
EXPECT_GE(report1.seconds_since_license_received(), kTotalTime);
EXPECT_GE(report1.seconds_since_first_decrypt(), kTotalTime);
ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(entry2.pst()));
wvcdm::Unpacked_PST_Report report2 = s2.pst_report();
EXPECT_EQ(report2.status(), kUnused);
EXPECT_GE(report2.seconds_since_license_received(), kTotalTime);
}
// Verify that a large PST can be used with usage table entries.