Files
android/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp
Cong Lin 8c4c238324 Add unit test for clear KCB in LS SDK 16.4 response
This is a merge from:
https://widevine-internal-review.googlesource.com/c/cdm/+/152897
and http://go/wvgerrit/153709

Adding a new OEMCrypto unit test will allow partners to correct a
problem earlier in their integration.

Verifies current oemcrypto implementation handles clear KCB in a
mocked 16.4 license response.

Unit test release date updated to 2022-06-17.

Test: run_x86_64_tests; opk_ta
Bug: 235870170
Bug: 234645065
Change-Id: I59fef2c25f5c007624447d4f46147d96adeddad9
2022-06-17 15:02:09 -07:00

10211 lines
430 KiB
C++

// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
//
/**
* @mainpage OEMCrypto Unit Tests
*
* The OEMCrypto unit tests are designed to verify that an implementation of
* OEMCrypto is correctly supporting the OEMCrypto API.
*
* @defgroup basic Basic Functionality Tests
* Basic functionality tests.
*
* @defgroup license License Request Tests
* Test for requesting and loading licenses.
*
* @defgroup renewal License Renewal Tests
* Tests for renewing licenses.
*
* @defgroup decrypt Decrypt Tests
* Tests for decrypting content.
*
* @defgroup usage_table Usage Table Tests
* Tests that use the usage table.
*/
#include <ctype.h>
#include <gtest/gtest.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include <stdint.h>
#include <algorithm>
#include <chrono>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "OEMCryptoCENC.h"
#include "clock.h"
#include "log.h"
#include "oec_decrypt_fallback_chain.h"
#include "oec_device_features.h"
#include "oec_extra_test_keys.h"
#include "oec_session_util.h"
#include "oec_test_data.h"
#include "oemcrypto_corpus_generator_helper.h"
#include "oemcrypto_fuzz_structs.h"
#include "oemcrypto_session_tests_helper.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "string_conversions.h"
#include "test_sleep.h"
#include "wvcrc32.h"
using ::testing::Bool;
using ::testing::Combine;
using ::testing::Range;
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
using namespace std;
namespace std { // GTest wants PrintTo to be in the std namespace.
void PrintTo(const tuple<OEMCrypto_CENCEncryptPatternDesc, OEMCryptoCipherMode,
wvoec::OutputType>& param,
ostream* os) {
OEMCrypto_CENCEncryptPatternDesc pattern = ::testing::get<0>(param);
OEMCryptoCipherMode mode = ::testing::get<1>(param);
wvoec::OutputType output = ::testing::get<2>(param);
bool decrypt_inplace = output.decrypt_inplace;
OEMCryptoBufferType type = output.type;
*os << ((mode == OEMCrypto_CipherMode_CENC) ? "CENC mode" : "CBCS mode")
<< ", pattern=(encrypt:" << pattern.encrypt << ", skip:" << pattern.skip
<< ")";
switch (type) {
case OEMCrypto_BufferType_Clear:
*os << ", BufferType = Clear";
break;
case OEMCrypto_BufferType_Secure:
*os << ", BufferType = Secure";
break;
case OEMCrypto_BufferType_Direct:
*os << ", BufferType = Direct";
break;
default:
*os << ", type = <bad type " << type << ">";
break;
}
if (decrypt_inplace) *os << " (in place)";
}
} // namespace std
namespace wvoec {
namespace {
constexpr size_t kBufferOverrunPadding = 16;
// Resource tiers:
constexpr size_t KiB = 1024;
constexpr size_t MiB = 1024 * 1024;
// Huge input buffer length used for OEMCryptoMemory* tests.
constexpr size_t kHugeInputBufferLength = 100 * MiB;
constexpr bool kCheckStatus = true;
constexpr bool kUpdateCoreMessageSubstringValues = true;
constexpr bool kDecryptCENCSecureBuffer = true;
constexpr size_t kHugeRandomNumber = 541236;
// With OEMCrypto v15 and above, we have different resource requirements
// depending on the resource rating reported by OEMCrypto. This function looks
// up the required value for the specified resource for the target OEMCrypto
// library.
template <typename T, size_t N>
T GetResourceValue(T (&resource_values)[N]) {
if (global_features.resource_rating < 1) return resource_values[0];
if (global_features.resource_rating > N) return resource_values[N - 1];
return resource_values[global_features.resource_rating - 1];
}
// Used for testing oemcrypto APIs with huge buffers.
typedef const std::function<OEMCryptoResult(size_t)> oemcrypto_function;
// Function to test APIs that expect a buffer length as input
// by passing huge buffer lengths upto end_buffer_length and test that the API
// doesn't crash.
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
size_t start_buffer_length,
size_t end_buffer_length,
bool check_status) {
OEMCryptoResult sts = OEMCrypto_SUCCESS;
for (size_t buffer_length = start_buffer_length;
buffer_length < end_buffer_length &&
(sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER ||
!check_status);
buffer_length *= 2) {
sts = f(buffer_length);
if (check_status && sts != OEMCrypto_SUCCESS &&
sts != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGI("Test exits huge buffer loop for length:%zu, status:%d",
buffer_length, sts);
}
}
}
// Function to test APIs that expect a buffer length as input
// by passing huge buffer lengths upto kHugeInputBufferLength and test that
// the API doesn't crash.
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) {
TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status);
}
// After API 16, we require 300 entries in the usage table. Before API 16, we
// required 200.
size_t RequiredUsageSize() {
return global_features.api_version < 16 ? 200 : 300;
}
// These are the maximum sizes we test. That means it is the minimum size that
// OEMCrypto must support.
// clang-format off
const size_t kMaxSampleSize[] = { 1*MiB, 2*MiB, 4*MiB, 16*MiB};
const size_t kMaxNumberSubsamples[] = { 10, 16, 32, 64};
const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB, 4*MiB};
const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB, 1*MiB};
const size_t kMaxConcurrentSession[] = { 10, 20, 30, 40};
const size_t kMaxKeysPerSession[] = { 4, 20, 20, 30};
const size_t kMaxTotalKeys[] = { 16, 40, 80, 90};
const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
const size_t kMaxTotalDRMPrivateKeys[] = { 2, 4, 6, 8};
// Note: Frame rate and simultaneous playback are specified by resource rating,
// but are tested at the system level, so there are no unit tests for frame
// rate. Similarly, number of subsamples for AV1
// const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576};
// clang-format on
// Return a printable string from data. If all the characters are printable,
// then just use the string. Otherwise, convert to hex.
std::string MaybeHex(const uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) {
if (!isprint(data[i])) return "0x" + wvutil::HexEncode(data, length);
}
return std::string(reinterpret_cast<const char*>(data), length);
}
std::string MaybeHex(const std::vector<uint8_t>& data) {
return MaybeHex(data.data(), data.size());
}
} // namespace
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
protected:
OEMCryptoClientTest() {}
void SetUp() override {
::testing::Test::SetUp();
wvutil::TestSleep::SyncFakeClock();
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name());
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
}
void TearDown() override {
OEMCrypto_Terminate();
::testing::Test::TearDown();
}
const uint8_t* find(const vector<uint8_t>& message,
const vector<uint8_t>& substring) {
vector<uint8_t>::const_iterator pos = search(
message.begin(), message.end(), substring.begin(), substring.end());
if (pos == message.end()) {
return nullptr;
}
return &(*pos);
}
OEMCryptoResult CopyBuffer(
OEMCrypto_SESSION session, OEMCrypto_SharedMemory* input_buffer,
size_t input_buffer_size,
const OEMCrypto_DestBufferDesc* dest_buffer_descriptor,
uint8_t subsample_flags) {
if (ShouldGenerateCorpus() && input_buffer != nullptr &&
dest_buffer_descriptor != nullptr) {
OEMCrypto_Copy_Buffer_Fuzz fuzzed_structure;
fuzzed_structure.dest_buffer_desc = *dest_buffer_descriptor;
fuzzed_structure.subsample_flags = subsample_flags;
const std::string file_name =
GetFileName("oemcrypto_copy_buffer_fuzz_seed_corpus");
// Corpus for copy buffer fuzzer should be in the format:
// (dest_buffer_descriptor | subsample_flags | input_buffer).
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name, reinterpret_cast<const char*>(&input_buffer),
input_buffer_size);
}
return OEMCrypto_CopyBuffer(session, input_buffer, input_buffer_size,
dest_buffer_descriptor, subsample_flags);
}
};
TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
Session s;
s.open();
OEMCrypto_DestBufferDesc output_descriptor;
int secure_fd = kHugeRandomNumber;
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_FreeSecureBuffer(s.session_id(), &output_descriptor,
secure_fd));
s.close();
}
/// @addtogroup basic
/// @{
/**
* Verifies initialization and logs version information.
* This test is first, because it might give an idea why other
* 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 17.1. Tests last updated 2022-06-17";
cout << " " << log_message << "\n";
cout << " "
<< "These tests are part of Android T."
<< "\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, 17);
EXPECT_EQ(ODK_MINOR_VERSION, 0);
EXPECT_EQ(kCurrentAPI, 17u);
OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel();
EXPECT_GT(level, OEMCrypto_Level_Unknown);
EXPECT_LE(level, OEMCrypto_Level3);
cout << " OEMCrypto Security Level is L" << level << endl;
uint32_t version = OEMCrypto_APIVersion();
uint32_t minor_version = OEMCrypto_MinorAPIVersion();
cout << " OEMCrypto API version is " << version << "."
<< minor_version << endl;
if (OEMCrypto_SupportsUsageTable()) {
cout << " OEMCrypto supports usage tables" << endl;
} else {
cout << " OEMCrypto does not support usage tables" << endl;
}
if (version >= 15) {
const uint32_t tier = OEMCrypto_ResourceRatingTier();
cout << " Resource Rating Tier: " << tier << endl;
}
if (version >= 17) {
OEMCryptoResult sts = OEMCrypto_ProductionReady();
if (sts != OEMCrypto_SUCCESS) {
LOGW("Device is not production ready, returns %d", sts);
}
sts = OEMCrypto_SUCCESS;
std::string build_info;
size_t buf_length = 0;
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
build_info.resize(buf_length);
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
if (build_info.size() != buf_length) {
build_info.resize(buf_length);
}
cout << " BuildInformation: " << build_info << endl;
OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport();
cout << " WatermarkingSupport: " << support << endl;
}
ASSERT_GE(version, 8u);
ASSERT_LE(version, kCurrentAPI);
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryAllocateSecureBufferForHugeBufferSize) {
Session s;
s.open();
auto oemcrypto_function = [&s](size_t buffer_size) {
OEMCrypto_DestBufferDesc output_descriptor;
int secure_fd;
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
s.session_id(), buffer_size, &output_descriptor, &secure_fd);
if (sts == OEMCrypto_SUCCESS) {
OEMCrypto_FreeSecureBuffer(s.session_id(), &output_descriptor, secure_fd);
}
return sts;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
s.close();
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeKeyboxLength) {
auto oemcrypto_function = [](size_t keybox_length) {
vector<uint8_t> keybox_buffer(keybox_length);
size_t wrapped_keybox_length = keybox_length + 50;
vector<uint8_t> wrapped_keybox_buffer(wrapped_keybox_length);
vector<uint8_t> transport_key_buffer(20);
memcpy(keybox_buffer.data(), &kTestKeybox, sizeof(kTestKeybox));
return OEMCrypto_WrapKeyboxOrOEMCert(
keybox_buffer.data(), keybox_length, wrapped_keybox_buffer.data(),
&wrapped_keybox_length, transport_key_buffer.data(),
transport_key_buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, sizeof(kTestKeybox),
kHugeInputBufferLength, kCheckStatus);
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeWrappedKeyboxLength) {
auto oemcrypto_function = [](size_t buffer_length) {
size_t wrapped_keybox_length = buffer_length;
vector<uint8_t> wrapped_keybox_buffer(wrapped_keybox_length);
vector<uint8_t> transport_key_buffer(20);
return OEMCrypto_WrapKeyboxOrOEMCert(
reinterpret_cast<const uint8_t*>(&kTestKeybox), sizeof(kTestKeybox),
wrapped_keybox_buffer.data(), &wrapped_keybox_length,
transport_key_buffer.data(), transport_key_buffer.size());
};
// API expects keybox length and wrapped keybox length to be equal. We would
// like to test for various values of wrapped keybox lengths, hence skipping
// status check.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeTransportKey) {
auto oemcrypto_function = [](size_t transport_key_length) {
size_t wrapped_keybox_length = sizeof(&kTestKeybox) + 50;
vector<uint8_t> wrapped_keybox_buffer(wrapped_keybox_length);
vector<uint8_t> transport_key_buffer(transport_key_length);
return OEMCrypto_WrapKeyboxOrOEMCert(
reinterpret_cast<const uint8_t*>(&kTestKeybox), sizeof(kTestKeybox),
wrapped_keybox_buffer.data(), &wrapped_keybox_length,
transport_key_buffer.data(), transport_key_buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(
OEMCryptoClientTest,
OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeKeyboxLengthStartingFromLength1) {
auto oemcrypto_function = [](size_t keybox_length) {
vector<uint8_t> keybox_buffer(keybox_length);
size_t wrapped_keybox_length = keybox_length + 50;
vector<uint8_t> wrapped_keybox_buffer(wrapped_keybox_length);
vector<uint8_t> transport_key_buffer(20);
return OEMCrypto_WrapKeyboxOrOEMCert(
keybox_buffer.data(), keybox_length, wrapped_keybox_buffer.data(),
&wrapped_keybox_length, transport_key_buffer.data(),
transport_key_buffer.size());
};
// Cannot check status as keybox will not be valid for this test.
// We still want to test what happens if buffer lengths is less that keybox
// length.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
/**
* The resource rating is a number from 1 to 4. The first three levels
* were initially defined in API 15 and they were expanded in API 16.
*/
TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u);
ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u);
}
/**
* OEMCrypto must declare what type of provisioning scheme it uses.
*/
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
OEMCrypto_ProvisioningMethod provisioning_method =
OEMCrypto_GetProvisioningMethod();
cout << " Provisioning method = "
<< ProvisioningMethodName(provisioning_method) << endl;
ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method);
}
const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
switch (value) {
case HDCP_NONE:
return "No HDCP supported, no secure data path";
case HDCP_V1:
return "HDCP version 1.x";
case HDCP_V1_0:
return "HDCP version 1.0";
case HDCP_V1_1:
return "HDCP version 1.1";
case HDCP_V1_2:
return "HDCP version 1.2";
case HDCP_V1_3:
return "HDCP version 1.3";
case HDCP_V1_4:
return "HDCP version 1.4";
case HDCP_V2:
return "HDCP version 2.0";
case HDCP_V2_1:
return "HDCP version 2.1";
case HDCP_V2_2:
return "HDCP version 2.2";
case HDCP_V2_3:
return "HDCP version 2.3";
case HDCP_NO_DIGITAL_OUTPUT:
return "No HDCP device attached/using local display with secure path";
default:
return "<INVALID VALUE>";
}
}
TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) {
OEMCryptoResult sts;
OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
printf(" Current HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(current), HDCPCapabilityAsString(current));
printf(" Maximum HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(maximum), HDCPCapabilityAsString(maximum));
}
TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
// This just tests some trivial functionality of the SRM update functions.
uint16_t version = 0;
OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version);
if (current_result == OEMCrypto_SUCCESS) {
printf(" Current SRM Version: %d.\n", version);
EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(nullptr));
} else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) {
printf(" Current SRM Status: Local Display Only.\n");
} else {
EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result);
}
}
TEST_F(OEMCryptoClientTest, CheckNullBuildInformationAPI17) {
OEMCryptoResult sts;
std::string build_info;
sts = OEMCrypto_BuildInformation(&build_info[0], nullptr);
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
size_t buf_length = 0;
sts = OEMCrypto_BuildInformation(nullptr, &buf_length);
// Previous versions of the test expected the wrong error code.
// Although OEMCrypto_ERROR_INVALID_CONTEXT is still accepted by
// the tests, vendors should return OEMCrypto_ERROR_SHORT_BUFFER if
// |buffer| is null and |buf_length| is zero, assigning
// the correct length to |buf_length|.
// TODO(231514699): Remove case for ERROR_INVALID_CONTEXT.
ASSERT_TRUE(OEMCrypto_ERROR_SHORT_BUFFER == sts ||
OEMCrypto_ERROR_INVALID_CONTEXT == sts);
if (sts == OEMCrypto_ERROR_INVALID_CONTEXT) {
printf(
"Warning: OEMCrypto_BuildInformation should return "
"ERROR_SHORT_BUFFER.\n");
}
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
constexpr size_t kZero = 0;
ASSERT_GT(buf_length, kZero);
}
}
TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
size_t sessions_count;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetNumberOfOpenSessions(&sessions_count));
ASSERT_EQ(0u, sessions_count);
size_t maximum;
OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
printf(" Max Number of Sessions: %zu.\n", maximum);
size_t required_max = GetResourceValue(kMaxConcurrentSession);
ASSERT_GE(maximum, required_max);
}
TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) {
const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize();
printf(" Max Usage Table Size: %zu.\n", maximum);
// A maximum of 0 means the table is constrained by dynamic memory allocation.
if (maximum > 0) {
ASSERT_GE(maximum, RequiredUsageSize());
}
}
//
// initialization tests
//
TEST_F(OEMCryptoClientTest, NormalInitTermination) {
// Should be able to terminate OEMCrypto, and then restart it.
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate());
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
}
// Test that set sandbox doesn't crash for a large sandbox id leangth.
TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForHugeSandboxIdLength) {
auto oemcrypto_function = [](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
return OEMCrypto_SetSandbox(buffer.data(), buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoClientTest, CheckDTCP2CapabilityAPI17) {
OEMCryptoResult sts;
OEMCrypto_DTCP2_Capability capability;
sts = OEMCrypto_GetDTCP2Capability(&capability);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
switch (capability) {
case OEMCrypto_NO_DTCP2:
printf(" Current DTCP Support: DTCP2 not supported.\n");
break;
case OEMCrypto_DTCP2_V1:
printf(
" Current DTCP Support: Version 1 (or higher) of "
"DTCP2 is supported.\n");
break;
}
}
//
// Session Tests
//
TEST_F(OEMCryptoClientTest, NormalSessionOpenClose) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.close());
}
TEST_F(OEMCryptoClientTest, TwoSessionsOpenClose) {
Session s1;
Session s2;
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(s1.close());
ASSERT_NO_FATAL_FAILURE(s2.close());
}
// This test verifies that OEMCrypto can open approximately as many sessions as
// it claims.
TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) {
size_t sessions_count;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetNumberOfOpenSessions(&sessions_count));
ASSERT_EQ(0u, sessions_count);
size_t max_sessions;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetMaxNumberOfSessions(&max_sessions));
// We expect OEMCrypto implementations support at least this many sessions.
size_t required_number = GetResourceValue(kMaxConcurrentSession);
ASSERT_GE(max_sessions, required_number);
// We allow GetMaxNumberOfSessions to return an estimate. This tests with a
// pad of 5%. Even if it's just an estimate, we still require 8 sessions.
size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number);
vector<OEMCrypto_SESSION> sessions;
// Limit the number of sessions for testing.
const size_t kMaxNumberOfSessionsForTesting = 0x100u;
for (size_t i = 0; i < kMaxNumberOfSessionsForTesting; i++) {
OEMCrypto_SESSION session_id;
OEMCryptoResult sts = OEMCrypto_OpenSession(&session_id);
// GetMaxNumberOfSessions might be an estimate. We allow OEMCrypto to report
// a max that is less than what is actually supported. Assume the number
// returned is |max|. OpenSessions shall not fail if number of active
// sessions is less than |max|; OpenSessions should fail with
// OEMCrypto_ERROR_TOO_MANY_SESSIONS if too many sessions are open.
if (sts != OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_ERROR_TOO_MANY_SESSIONS, sts);
ASSERT_GE(i, max_sessions_with_pad);
break;
}
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetNumberOfOpenSessions(&sessions_count));
ASSERT_EQ(i + 1, sessions_count);
sessions.push_back(session_id);
}
for (size_t i = 0; i < sessions.size(); i++) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(sessions[i]));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetNumberOfOpenSessions(&sessions_count));
ASSERT_EQ(sessions.size() - i - 1, sessions_count);
}
if (sessions.size() == kMaxNumberOfSessionsForTesting) {
printf(
" MaxSessionsOpenClose: reaches "
"kMaxNumberOfSessionsForTesting(%zu). GetMaxNumberOfSessions = %zu. "
"ERROR_TOO_MANY_SESSIONS not tested.",
kMaxNumberOfSessionsForTesting, max_sessions);
}
}
// Verify that GetRandom does work, and does some sanity checks on how random
// the data is. Basically, we say that calling GetRandom twice should not
// generate much overlap.
TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) {
// 32 bytes. Not very large, but that's all we really need in one call.
const size_t size = 32;
uint8_t data1[size];
uint8_t data2[size];
memset(data1, 0, size);
memset(data2, 0, size);
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data1, size));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data2, size));
// We don't have enough data to see that the data is really random,
// so we'll just do a spot check that two calls don't return the same values.
int count = 0;
for (size_t i = 0; i < size; i++) {
if (data1[i] == data2[i]) count++;
}
ASSERT_LE(count, 6); // P(count > 6) = 4.3e-11
}
// Verify that GetRandom doesn't crash for large input lengths.
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryGetRandomForHugeBuffer) {
auto oemcrypto_function = [](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
// TODO(ellurubharath, fredgc): Need to re-evaluate this on a real device
// as GetRandom can be slow.
return OEMCrypto_GetRandom(buffer.data(), buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoClientTest, GenerateNonce) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
s.GenerateNonce();
}
// Prevent a nonce flood even if each nonce is in a different session.
TEST_F(OEMCryptoClientTest, PreventNonceFlood2API16) {
int error_counter = 0;
const int64_t test_start = wvutil::Clock().GetCurrentTime();
// More than 200 nonces per second should generate an error.
// To allow for some slop, we actually test for more.
const int flood_cutoff = 200;
const int loop_count = flood_cutoff * 2;
for (int i = 0; i < loop_count; i++) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
s.GenerateNonce(&error_counter);
}
const int64_t test_end = wvutil::Clock().GetCurrentTime();
int valid_counter = loop_count - error_counter;
// Either oemcrypto should enforce a delay, or it should return an error from
// GenerateNonce -- in either case the number of valid nonces is rate
// limited. We add two seconds to allow for round off error in both
// test_start and test_end.
EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2));
error_counter = 0;
// After a pause, we should be able to regenerate nonces.
wvutil::TestSleep::Sleep(2);
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
s.GenerateNonce(&error_counter);
EXPECT_EQ(0, error_counter);
}
// Prevent a nonce flood even if some nonces are in a different session. This
// is different from the test above because there are several session open at
// the same time. We want to make sure you can't get a flood of nonces by
// opening a flood of sessions.
TEST_F(OEMCryptoClientTest, PreventNonceFlood3API16) {
int request_counter = 0;
int error_counter = 0;
const int64_t test_start = wvutil::Clock().GetCurrentTime();
// More than 200 nonces per second should generate an error.
// To allow for some slop, we actually test for more.
const int flood_cutoff = 200;
const size_t session_count = GetResourceValue(kMaxConcurrentSession);
const size_t loop_count = 2 * flood_cutoff / session_count + 1;
for (size_t i = 0; i < loop_count; i++) {
std::vector<Session> s(session_count);
for (size_t j = 0; j < session_count; j++) {
ASSERT_NO_FATAL_FAILURE(s[j].open());
request_counter++;
s[j].GenerateNonce(&error_counter);
}
}
const int64_t test_end = wvutil::Clock().GetCurrentTime();
int valid_counter = request_counter - error_counter;
// Either oemcrypto should enforce a delay, or it should return an error from
// GenerateNonce -- in either case the number of valid nonces is rate
// limited. We add two seconds to allow for round off error in both
// test_start and test_end.
EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2));
error_counter = 0;
// After a pause, we should be able to regenerate nonces.
wvutil::TestSleep::Sleep(2);
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
s.GenerateNonce(&error_counter);
EXPECT_EQ(0, error_counter);
}
// This verifies that CopyBuffer works, even before a license has been loaded.
TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
const int kDataSize = 256;
vector<uint8_t> input_buffer(kDataSize);
GetRandBytes(input_buffer.data(), input_buffer.size());
vector<uint8_t> output_buffer(kDataSize);
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
dest_buffer_descriptor.buffer.clear.clear_buffer = output_buffer.data();
dest_buffer_descriptor.buffer.clear.clear_buffer_length =
output_buffer.size();
ASSERT_EQ(OEMCrypto_SUCCESS,
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
ASSERT_EQ(input_buffer, output_buffer);
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT,
CopyBuffer(s.session_id(), nullptr, input_buffer.size(),
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
ASSERT_EQ(
OEMCrypto_ERROR_INVALID_CONTEXT,
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
nullptr, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
dest_buffer_descriptor.buffer.clear.clear_buffer = nullptr;
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT,
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
dest_buffer_descriptor.buffer.clear.clear_buffer = output_buffer.data();
dest_buffer_descriptor.buffer.clear.clear_buffer_length =
output_buffer.size() - 1;
ASSERT_NE(OEMCrypto_SUCCESS,
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
}
// This verifies that CopyBuffer works on the maximum required buffer size.
TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
size_t max_size = GetResourceValue(kMaxSubsampleSize);
vector<uint8_t> input_buffer(max_size);
GetRandBytes(input_buffer.data(), input_buffer.size());
vector<uint8_t> output_buffer(max_size);
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
dest_buffer_descriptor.buffer.clear.clear_buffer = output_buffer.data();
dest_buffer_descriptor.buffer.clear.clear_buffer_length =
output_buffer.size();
ASSERT_EQ(OEMCrypto_SUCCESS,
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
ASSERT_EQ(input_buffer, output_buffer);
}
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForHugeBufferLengths) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> input_buffer;
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
auto oemcrypto_function = [&s, &dest_buffer_descriptor,
&input_buffer](size_t buffer_length) {
input_buffer.resize(buffer_length);
int secure_fd;
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
s.session_id(), buffer_length, &dest_buffer_descriptor, &secure_fd);
if (sts != OEMCrypto_SUCCESS) {
LOGI("Secure buffers are not supported.");
return sts;
}
dest_buffer_descriptor.buffer.secure.secure_buffer_length = buffer_length;
OEMCryptoResult status = OEMCrypto_CopyBuffer(
s.session_id(), input_buffer.data(), buffer_length,
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor,
secure_fd);
return status;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryCopyBufferDirectForHugeBufferLengths) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> input_buffer;
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Direct;
dest_buffer_descriptor.buffer.direct.is_video = false;
auto oemcrypto_function = [&s, &dest_buffer_descriptor,
&input_buffer](size_t buffer_length) {
input_buffer.resize(buffer_length);
OEMCryptoResult status = OEMCrypto_CopyBuffer(
s.session_id(), input_buffer.data(), buffer_length,
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
return status;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForOutOfRangeOffset) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> input_buffer;
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
size_t buffer_length = KiB;
input_buffer.resize(buffer_length);
int secure_fd;
if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length,
&dest_buffer_descriptor,
&secure_fd) != OEMCrypto_SUCCESS) {
LOGI("Secure buffers are not supported.");
return;
}
dest_buffer_descriptor.buffer.secure.secure_buffer_length = buffer_length;
auto oemcrypto_function = [&s, &dest_buffer_descriptor, &input_buffer,
&buffer_length](size_t offset) {
dest_buffer_descriptor.buffer.secure.offset = offset;
return OEMCrypto_CopyBuffer(
s.session_id(), input_buffer.data(), buffer_length,
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor,
secure_fd);
}
TEST_F(OEMCryptoClientTest,
OEMCryptoMemoryCopyBufferForOutOfRangeHandleLength) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> input_buffer;
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
size_t buffer_length = KiB;
input_buffer.resize(buffer_length);
int secure_fd;
if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length,
&dest_buffer_descriptor,
&secure_fd) != OEMCrypto_SUCCESS) {
LOGI("Secure buffers are not supported.");
return;
}
dest_buffer_descriptor.buffer.secure.secure_buffer_length =
kHugeInputBufferLength;
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), buffer_length,
&dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor,
secure_fd);
}
TEST_F(OEMCryptoClientTest, ClearCopyTestInvalidSubsampleFlag) {
uint8_t oemcrypto_invalid_subsample_flag = 85;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
size_t max_size = GetResourceValue(kMaxSubsampleSize);
vector<uint8_t> input_buffer(max_size);
GetRandBytes(input_buffer.data(), input_buffer.size());
vector<uint8_t> output_buffer(max_size);
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
dest_buffer_descriptor.buffer.clear.clear_buffer = output_buffer.data();
dest_buffer_descriptor.buffer.clear.clear_buffer_length =
output_buffer.size();
ASSERT_NO_FATAL_FAILURE(
CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(),
&dest_buffer_descriptor, oemcrypto_invalid_subsample_flag));
}
TEST_F(OEMCryptoClientTest, CanLoadTestKeys) {
ASSERT_NE(DeviceFeatures::NO_METHOD, global_features.derive_key_method)
<< "Session tests cannot run with out a test keybox or RSA cert.";
}
// Tests using this class are only used for devices with a keybox. They are not
// run for devices with an OEM Certificate.
class OEMCryptoKeyboxTest : public OEMCryptoClientTest {
void SetUp() override {
OEMCryptoClientTest::SetUp();
OEMCryptoResult sts = OEMCrypto_IsKeyboxValid();
// If the production keybox is valid, use it for these tests. Most of the
// other tests will use a test keybox anyway, but it's nice to check the
// device ID for the real keybox if we can.
if (sts == OEMCrypto_SUCCESS) return;
printf("Production keybox is NOT valid. All tests use test keybox.\n");
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_LoadTestKeybox(reinterpret_cast<const uint8_t*>(&kTestKeybox),
sizeof(kTestKeybox)));
}
};
/******** Dangerous Tests - DO NOT RUN ***********/
/*The following tests try to test InstallKeybox API with random buffers of
varying length in order to catch any overflow issues. These tests override the
actual keybox on the device. Remove the if and endif statement to run these
tests on a device ONLY IF YOU ARE ABLE TO RECOVER THE KEYBOX on the device.*/
#if 0
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
return OEMCrypto_InstallKeyboxOrOEMCert(keybox.data(), keybox.size());
};
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
// at beginning of generated buffers.
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryInstallKeyboxForHugeKeyboxBufferStartingFromLength1) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
return OEMCrypto_InstallKeyboxOrOEMCert(keybox.data(), keybox.size());
};
// We are testing for keybox lengths starting from 1 which would return error,
// hence skipping status check.
TestHugeLengthDoesNotCrashAPI(f, !kCheckStatus);
}
#endif
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
return OEMCrypto_LoadTestKeybox(keybox.data(), keybox.size());
};
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
// at beginning of generated buffers.
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBufferStartingFromLength1) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
return OEMCrypto_LoadTestKeybox(keybox.data(), keybox.size());
};
// We are testing for keybox lengths starting from 1 which would return error,
// hence skipping status check.
TestHugeLengthDoesNotCrashAPI(f, !kCheckStatus);
}
// This test is used to print the device ID to stdout.
TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
OEMCryptoResult sts;
uint8_t dev_id[128] = {0};
size_t dev_id_len = 128;
sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
cout << " NormalGetDeviceId: dev_id = "
<< MaybeHex(dev_id, dev_id_len) << " len = " << dev_id_len << endl;
}
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) {
auto oemcrypto_function = [](size_t input_length) {
size_t device_id_length = input_length;
vector<uint8_t> device_id(device_id_length);
return OEMCrypto_GetDeviceID(device_id.data(), &device_id_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) {
OEMCryptoResult sts;
uint8_t dev_id[128];
for (int i = 0; i < 128; ++i) {
dev_id[i] = 0x55;
}
dev_id[127] = '\0';
size_t dev_id_len = 0;
sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
// On short buffer error, function should return minimum buffer length
ASSERT_GT(dev_id_len, 0u);
// Should also return short buffer if passed a zero length and a null buffer.
dev_id_len = 0;
sts = OEMCrypto_GetDeviceID(nullptr, &dev_id_len);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
// On short buffer error, function should return minimum buffer length
ASSERT_GT(dev_id_len, 0u);
}
TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) {
OEMCryptoResult sts;
uint8_t key_data[256];
size_t key_data_len = sizeof(key_data);
sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
printf(" NormalGetKeyData: system_id = %u = 0x%04X, version=%u\n",
htonl(data[1]), htonl(data[1]), htonl(data[0]));
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetKeyIdForHugeIdLength) {
auto oemcrypto_function = [](size_t input_length) {
size_t key_data_length = input_length;
vector<uint8_t> key_data(key_data_length);
return OEMCrypto_GetKeyData(key_data.data(), &key_data_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) {
OEMCryptoResult sts;
uint8_t key_data[256];
sts = OEMCrypto_GetKeyData(key_data, nullptr);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
}
// This test makes sure the installed keybox is valid. It doesn't really check
// that it is a production keybox. That must be done by an integration test.
TEST_F(OEMCryptoKeyboxTest, ProductionKeyboxValid) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
}
// This tests GenerateDerivedKeys with an 8k context.
TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
const size_t max_size = GetResourceValue(kLargeMessageSize);
vector<uint8_t> mac_context(max_size);
vector<uint8_t> enc_context(max_size);
// Stripe the data so the two vectors are not identical, and not all zeroes.
for (size_t i = 0; i < max_size; i++) {
mac_context[i] = i % 0x100;
enc_context[i] = (3 * i) % 0x100;
}
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys(
s.session_id(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryGenerateDerivedKeysForHugeMacContextLength) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
auto oemcrypto_function = [&s, &mac_context,
&enc_context](size_t buffer_length) {
mac_context.resize(buffer_length);
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryGenerateDerivedKeysForHugeEncContextLength) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
auto oemcrypto_function = [&s, &mac_context,
&enc_context](size_t buffer_length) {
enc_context.resize(buffer_length);
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// This class is for tests that have an OEM Certificate instead of a keybox.
class OEMCryptoProv30Test : public OEMCryptoClientTest {};
// This verifies that the device really does claim to have a certificate.
// It should be filtered out for devices that have a keybox.
TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) {
ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod());
}
TEST_F(OEMCryptoProv30Test, GetDeviceId) {
OEMCryptoResult sts;
std::vector<uint8_t> dev_id(128, 0);
size_t dev_id_len = dev_id.size();
sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len);
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
ASSERT_GT(dev_id_len, 0u);
dev_id.resize(dev_id_len);
sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len);
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl;
}
// The OEM certificate must be valid.
TEST_F(OEMCryptoProv30Test, CertValidAPI15) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid());
}
TEST_F(OEMCryptoProv30Test, OEMCertValid) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
bool kVerify = true;
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify.
}
/// @}
/// @addtogroup license
/// @{
// This verifies that the OEM Certificate cannot be used for other RSA padding
// schemes. Those schemes should only be used by cast receiver certificates.
TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
OEMCryptoResult sts;
// Sign a Message
vector<uint8_t> data(500);
GetRandBytes(data.data(), data.size());
size_t signature_length = 0;
// We need a size one vector to pass as a pointer.
vector<uint8_t> signature(1, 0);
vector<uint8_t> zero(1, 0);
sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(),
signature.data(), &signature_length,
kSign_PKCS1_Block1);
if (OEMCrypto_ERROR_SHORT_BUFFER == sts) {
// The OEMCrypto could complain about buffer length first, so let's
// resize and check if it's writing to the signature again.
signature.resize(signature_length, 0);
zero.resize(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(),
data.size(), signature.data(),
&signature_length, kSign_PKCS1_Block1);
}
EXPECT_NE(OEMCrypto_SUCCESS, sts)
<< "OEM Cert Signed with forbidden kSign_PKCS1_Block1.";
ASSERT_EQ(zero, signature); // signature should not be computed.
}
// Calling OEMCrypto_GetOEMPublicCertificate should not change the session's
// private key.
TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
if (wrapped_rsa_key_.size() == 0) {
// If we don't have a wrapped key yet, create one.
// This wrapped key will be shared by all sessions in the test.
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
}
Session s;
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;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
OEMCrypto_GetOEMPublicCertificate(nullptr, &public_cert_length));
ASSERT_LT(0u, public_cert_length);
public_cert.resize(public_cert_length);
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetOEMPublicCertificate(
public_cert.data(), &public_cert_length));
// Derive keys from the session key -- this should use the DRM Cert's key. It
// should NOT use the OEM Private key because that key should not have been
// loaded.
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey());
// Now fill a message and try to load it.
LicenseRoundTrip license_messages(&s);
license_messages.set_control(0);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForHugeCertLength) {
if (wrapped_rsa_key_.size() == 0) {
// If we don't have a wrapped key yet, create one.
// This wrapped key will be shared by all sessions in the test.
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
}
Session s;
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());
auto oemcrypto_function = [](size_t input_length) {
size_t public_cert_length = input_length;
vector<uint8_t> public_cert(public_cert_length);
return OEMCrypto_GetOEMPublicCertificate(public_cert.data(),
&public_cert_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// This class is for tests that have boot certificate chain instead of a keybox.
class OEMCryptoProv40Test : public OEMCryptoClientTest {};
// This verifies that the device really does claim to have BCC.
// It should be filtered out for devices that have a keybox or factory OEM cert.
TEST_F(OEMCryptoProv40Test, DeviceClaimsBootCertificateChain) {
ASSERT_EQ(OEMCrypto_GetProvisioningMethod(), OEMCrypto_BootCertificateChain);
}
// Verifies that short buffer error returns when the buffer is short.
TEST_F(OEMCryptoProv40Test, GetBootCertificateChainShortBuffer) {
std::vector<uint8_t> bcc;
size_t bcc_size = 0;
std::vector<uint8_t> additional_signature;
size_t additional_signature_size = 0;
ASSERT_EQ(OEMCrypto_GetBootCertificateChain(bcc.data(), &bcc_size,
additional_signature.data(),
&additional_signature_size),
OEMCrypto_ERROR_SHORT_BUFFER);
ASSERT_NE(bcc_size, 0uL);
}
// Verifies BCC can be successfully returned.
TEST_F(OEMCryptoProv40Test, GetBootCertificateChainSuccess) {
std::vector<uint8_t> bcc;
size_t bcc_size = 0;
std::vector<uint8_t> additional_signature;
size_t additional_signature_size = 0;
ASSERT_EQ(OEMCrypto_GetBootCertificateChain(bcc.data(), &bcc_size,
additional_signature.data(),
&additional_signature_size),
OEMCrypto_ERROR_SHORT_BUFFER);
bcc.resize(bcc_size);
additional_signature.resize(additional_signature_size);
ASSERT_EQ(OEMCrypto_GetBootCertificateChain(bcc.data(), &bcc_size,
additional_signature.data(),
&additional_signature_size),
OEMCrypto_SUCCESS);
}
// Verifies that short buffer error returns when the buffer is short.
TEST_F(OEMCryptoProv40Test, GenerateCertificateKeyPairShortBuffer) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
std::vector<uint8_t> public_key;
size_t public_key_size = 0;
std::vector<uint8_t> public_key_signature;
size_t public_key_signature_size = 0;
std::vector<uint8_t> wrapped_private_key;
size_t wrapped_private_key_size = 0;
OEMCrypto_PrivateKeyType key_type;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key.data(), &public_key_size,
public_key_signature.data(), &public_key_signature_size,
wrapped_private_key.data(), &wrapped_private_key_size, &key_type),
OEMCrypto_ERROR_SHORT_BUFFER);
ASSERT_NE(public_key_size, 0uL);
ASSERT_NE(public_key_signature_size, 0uL);
ASSERT_NE(wrapped_private_key_size, 0uL);
}
// Verifies a pair of key can be successfully returned.
TEST_F(OEMCryptoProv40Test, GenerateCertificateKeyPairSuccess) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
std::vector<uint8_t> public_key;
size_t public_key_size = 0;
std::vector<uint8_t> public_key_signature;
size_t public_key_signature_size = 0;
std::vector<uint8_t> wrapped_private_key;
size_t wrapped_private_key_size = 0;
OEMCrypto_PrivateKeyType key_type;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key.data(), &public_key_size,
public_key_signature.data(), &public_key_signature_size,
wrapped_private_key.data(), &wrapped_private_key_size, &key_type),
OEMCrypto_ERROR_SHORT_BUFFER);
public_key.resize(public_key_size);
public_key_signature.resize(public_key_signature_size);
wrapped_private_key.resize(wrapped_private_key_size);
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key.data(), &public_key_size,
public_key_signature.data(), &public_key_signature_size,
wrapped_private_key.data(), &wrapped_private_key_size, &key_type),
OEMCrypto_SUCCESS);
public_key.resize(public_key_size);
public_key_signature.resize(public_key_signature_size);
wrapped_private_key.resize(wrapped_private_key_size);
// Parse the public key generated to make sure it is correctly formatted.
if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) {
ASSERT_NO_FATAL_FAILURE(
s.SetRsaPublicKey(public_key.data(), public_key_size));
} else if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) {
ASSERT_NO_FATAL_FAILURE(
s.SetEcPublicKey(public_key.data(), public_key_size));
} else {
FAIL() << "Unknown private key type: " << static_cast<int>(key_type);
}
}
// Verifies the generated key pairs are different on each call.
TEST_F(OEMCryptoProv40Test, GenerateCertificateKeyPairsAreDifferent) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// Large buffer to make sure it is large enough.
size_t public_key_size1 = 10000;
std::vector<uint8_t> public_key1(public_key_size1);
size_t public_key_signature_size1 = 10000;
std::vector<uint8_t> public_key_signature1(public_key_signature_size1);
size_t wrapped_private_key_size1 = 10000;
std::vector<uint8_t> wrapped_private_key1(wrapped_private_key_size1);
OEMCrypto_PrivateKeyType key_type1;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key1.data(), &public_key_size1,
public_key_signature1.data(), &public_key_signature_size1,
wrapped_private_key1.data(), &wrapped_private_key_size1, &key_type1),
OEMCrypto_SUCCESS);
EXPECT_NE(public_key_size1, 0UL);
EXPECT_NE(public_key_signature_size1, 0UL);
EXPECT_NE(wrapped_private_key_size1, 0UL);
public_key1.resize(public_key_size1);
public_key_signature1.resize(public_key_signature_size1);
wrapped_private_key1.resize(wrapped_private_key_size1);
size_t public_key_size2 = 10000;
std::vector<uint8_t> public_key2(public_key_size2);
size_t public_key_signature_size2 = 10000;
std::vector<uint8_t> public_key_signature2(public_key_signature_size2);
size_t wrapped_private_key_size2 = 10000;
std::vector<uint8_t> wrapped_private_key2(wrapped_private_key_size2);
OEMCrypto_PrivateKeyType key_type2;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key2.data(), &public_key_size2,
public_key_signature2.data(), &public_key_signature_size2,
wrapped_private_key2.data(), &wrapped_private_key_size2, &key_type2),
OEMCrypto_SUCCESS);
EXPECT_NE(public_key_size2, 0UL);
EXPECT_NE(public_key_signature_size2, 0UL);
EXPECT_NE(wrapped_private_key_size2, 0UL);
public_key2.resize(public_key_size2);
public_key_signature2.resize(public_key_signature_size2);
wrapped_private_key2.resize(wrapped_private_key_size2);
EXPECT_NE(public_key1, public_key2);
EXPECT_NE(public_key_signature1, public_key_signature2);
EXPECT_NE(wrapped_private_key1, wrapped_private_key2);
}
// Verifies the an OEM private key can be installed.
TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeySuccess) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// First generate a key pair.
// Large buffer to make sure it is large enough.
size_t public_key_size = 10000;
std::vector<uint8_t> public_key(public_key_size);
size_t public_key_signature_size = 10000;
std::vector<uint8_t> public_key_signature(public_key_signature_size);
size_t wrapped_private_key_size = 10000;
std::vector<uint8_t> wrapped_private_key(wrapped_private_key_size);
OEMCrypto_PrivateKeyType key_type;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key.data(), &public_key_size,
public_key_signature.data(), &public_key_signature_size,
wrapped_private_key.data(), &wrapped_private_key_size, &key_type),
OEMCrypto_SUCCESS);
public_key.resize(public_key_size);
public_key_signature.resize(public_key_signature_size);
wrapped_private_key.resize(wrapped_private_key_size);
// Install the generated private key.
ASSERT_EQ(OEMCrypto_InstallOemPrivateKey(s.session_id(), key_type,
wrapped_private_key.data(),
wrapped_private_key_size),
OEMCrypto_SUCCESS);
}
// If data is empty or random, the API should return non-success status.
TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyInvalidDataFail) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// Empty key fails.
std::vector<uint8_t> wrapped_private_key;
OEMCrypto_PrivateKeyType key_type = OEMCrypto_RSA_Private_Key;
ASSERT_NE(OEMCrypto_InstallOemPrivateKey(s.session_id(), key_type,
wrapped_private_key.data(),
wrapped_private_key.size()),
OEMCrypto_SUCCESS);
// Random key data fails.
wrapped_private_key = {1, 2, 3};
ASSERT_NE(OEMCrypto_InstallOemPrivateKey(s.session_id(), key_type,
wrapped_private_key.data(),
wrapped_private_key.size()),
OEMCrypto_SUCCESS);
}
// Verifies the an OEM private key can be installed, and used by
// GenerateCertificateKeyPair call.
TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// First generate a key pair.
size_t public_key_size1 = 10000;
std::vector<uint8_t> public_key1(public_key_size1);
size_t public_key_signature_size1 = 10000;
std::vector<uint8_t> public_key_signature1(public_key_signature_size1);
size_t wrapped_private_key_size1 = 10000;
std::vector<uint8_t> wrapped_private_key1(wrapped_private_key_size1);
OEMCrypto_PrivateKeyType key_type1;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key1.data(), &public_key_size1,
public_key_signature1.data(), &public_key_signature_size1,
wrapped_private_key1.data(), &wrapped_private_key_size1, &key_type1),
OEMCrypto_SUCCESS);
EXPECT_NE(public_key_size1, 0UL);
EXPECT_NE(public_key_signature_size1, 0UL);
EXPECT_NE(wrapped_private_key_size1, 0UL);
public_key1.resize(public_key_size1);
public_key_signature1.resize(public_key_signature_size1);
wrapped_private_key1.resize(wrapped_private_key_size1);
// Install the generated private key.
ASSERT_EQ(OEMCrypto_InstallOemPrivateKey(s.session_id(), key_type1,
wrapped_private_key1.data(),
wrapped_private_key_size1),
OEMCrypto_SUCCESS);
// Now calling GenerateCertificateKeyPair should use wrapped_private_key to
// sign the newly generated public key.
size_t public_key_size2 = 10000;
std::vector<uint8_t> public_key2(public_key_size2);
size_t public_key_signature_size2 = 10000;
std::vector<uint8_t> public_key_signature2(public_key_signature_size2);
size_t wrapped_private_key_size2 = 10000;
std::vector<uint8_t> wrapped_private_key2(wrapped_private_key_size2);
OEMCrypto_PrivateKeyType key_type2;
ASSERT_EQ(
OEMCrypto_GenerateCertificateKeyPair(
s.session_id(), public_key2.data(), &public_key_size2,
public_key_signature2.data(), &public_key_signature_size2,
wrapped_private_key2.data(), &wrapped_private_key_size2, &key_type2),
OEMCrypto_SUCCESS);
EXPECT_NE(public_key_size2, 0UL);
EXPECT_NE(public_key_signature_size2, 0UL);
EXPECT_NE(wrapped_private_key_size2, 0UL);
public_key2.resize(public_key_size2);
public_key_signature2.resize(public_key_signature_size2);
wrapped_private_key2.resize(wrapped_private_key_size2);
// Verify public_key_signature2 with public_key1.
ASSERT_NO_FATAL_FAILURE(
s.SetRsaPublicKey(public_key1.data(), public_key1.size()));
ASSERT_NO_FATAL_FAILURE(
s.VerifyRSASignature(public_key2, public_key_signature2.data(),
public_key_signature2.size(), kSign_RSASSA_PSS));
}
//
// AddKey Tests
//
// These tests will use either a test keybox or a test certificate to derive
// session keys.
class OEMCryptoSessionTests : public OEMCryptoClientTest {
public:
vector<uint8_t> encrypted_usage_header_;
protected:
OEMCryptoSessionTests() {}
void SetUp() override {
OEMCryptoClientTest::SetUp();
EnsureTestKeys();
if (global_features.usage_table) {
CreateUsageTableHeader();
}
}
void CreateUsageTableHeader(bool expect_success = true) {
size_t header_buffer_length = 0;
OEMCryptoResult sts =
OEMCrypto_CreateUsageTableHeader(nullptr, &header_buffer_length);
if (expect_success) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
} else {
ASSERT_NE(OEMCrypto_SUCCESS, sts);
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return;
}
encrypted_usage_header_.resize(header_buffer_length);
sts = OEMCrypto_CreateUsageTableHeader(encrypted_usage_header_.data(),
&header_buffer_length);
if (expect_success) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
encrypted_usage_header_.resize(header_buffer_length);
} else {
ASSERT_NE(OEMCrypto_SUCCESS, sts);
}
}
void TestPrepareLicenseRequestForHugeBufferLengths(
const std::function<void(size_t, LicenseRoundTrip*)> f,
bool check_status) {
auto oemcrypto_function = [&](size_t message_length) {
Session s;
s.open();
InstallTestRSAKey(&s);
LicenseRoundTrip license_messages(&s);
f(message_length, &license_messages);
OEMCryptoResult result =
license_messages.SignAndCreateRequestWithCustomBufferLengths();
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
OEMCryptoResult LoadLicense(Session& s, LicenseRoundTrip& license_messages) {
InstallTestRSAKey(&s);
license_messages.SignAndVerifyRequest();
license_messages.CreateDefaultResponse();
license_messages.EncryptAndSignResponse();
return license_messages.LoadResponse();
}
};
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryCreateUsageTableHeaderForHugeHeaderBufferLength) {
auto oemcrypto_function = [](size_t buffer_length) {
size_t header_buffer_length = buffer_length;
vector<uint8_t> usage_table_header(header_buffer_length);
return OEMCrypto_CreateUsageTableHeader(usage_table_header.data(),
&header_buffer_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {};
TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
}
// This class is for testing a single license with the default API version
// of 16.
class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests {
public:
OEMCryptoLicenseTestAPI16()
: license_api_version_(kCurrentAPI), license_messages_(&session_) {}
void SetUp() override {
OEMCryptoSessionTests::SetUp();
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_));
}
void TearDown() override {
ASSERT_NO_FATAL_FAILURE(session_.close());
OEMCryptoSessionTests::TearDown();
}
protected:
Session session_;
uint32_t license_api_version_;
LicenseRoundTrip license_messages_;
};
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) {
TestPrepareLicenseRequestForHugeBufferLengths(
[](size_t message_size, LicenseRoundTrip* license_messages) {
license_messages->set_message_size(message_size);
},
kCheckStatus);
}
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryPrepareLicenseRequestForHugeCoreMessageLength) {
TestPrepareLicenseRequestForHugeBufferLengths(
[](size_t core_message_size, LicenseRoundTrip* license_messages) {
license_messages->set_core_message_size(core_message_size);
},
kCheckStatus);
}
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryPrepareLicenseRequestForHugeSignatureLength) {
// There is a limit of signature length that gets validated. Hence not
// checking status as we would like to test it for all possible signature
// lengths.
TestPrepareLicenseRequestForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->set_request_signature_size(length);
},
!kCheckStatus);
}
class OEMCryptoLicenseOverflowTest : public OEMCryptoSessionTests,
public WithParamInterface<uint32_t> {
public:
OEMCryptoLicenseOverflowTest() : license_api_version_(kCurrentAPI) {}
void SetUp() override {
OEMCryptoSessionTests::SetUp();
license_api_version_ = GetParam();
}
void TearDown() override { OEMCryptoSessionTests::TearDown(); }
void TestLoadLicenseForHugeBufferLengths(
const std::function<void(size_t, LicenseRoundTrip*)> f, bool check_status,
bool update_core_message_substring_values) {
auto oemcrypto_function = [&](size_t message_length) {
Session s;
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
s.open();
InstallTestRSAKey(&s);
bool verify_keys_loaded = true;
license_messages.SignAndVerifyRequest();
license_messages.CreateDefaultResponse();
if (update_core_message_substring_values) {
// Make the license message big enough so that updated core message
// substring offset and length values from tests are still able to read
// data from license_message buffer rather than reading some garbage
// data.
license_messages.set_message_size(
sizeof(license_messages.response_data()) + message_length);
}
f(message_length, &license_messages);
if (update_core_message_substring_values) {
// We will be updating offset for these tests, which will cause verify
// keys to fail with an assertion. Hence skipping verification.
verify_keys_loaded = false;
}
license_messages.EncryptAndSignResponse();
OEMCryptoResult result =
license_messages.LoadResponse(&s, verify_keys_loaded);
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
void TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
const std::function<void(size_t, LicenseRoundTrip*)> f) {
Session s;
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
s.open();
InstallTestRSAKey(&s);
license_messages.SignAndVerifyRequest();
license_messages.CreateDefaultResponse();
size_t message_length = sizeof(license_messages.response_data());
f(message_length, &license_messages);
license_messages.EncryptAndSignResponse();
OEMCryptoResult result = license_messages.LoadResponse();
s.close();
// Verifying error is not due to signature failure which can be caused due
// to test code.
ASSERT_NE(OEMCrypto_ERROR_SIGNATURE_FAILURE, result);
ASSERT_NE(OEMCrypto_SUCCESS, result);
}
protected:
uint32_t license_api_version_;
};
// This class is for testing a single license with the default API version
// of 16. Used for buffer overflow tests.
class OEMCryptoMemoryLicenseTest : public OEMCryptoLicenseTestAPI16 {
public:
OEMCryptoMemoryLicenseTest() : entitled_message_(&license_messages_) {}
void SetUp() override {
OEMCryptoLicenseTestAPI16::SetUp();
SetUpEntitledMessage();
entitlement_response_length_ = entitled_message_.entitled_key_data_size();
}
void LoadLicense() {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
void SetUpEntitledMessage() {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
LoadLicense();
entitled_message_.FillKeyArray();
entitled_message_.EncryptContentKey();
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
entitled_message_.SetEntitledKeySession(key_session_id);
}
void TearDown() override { OEMCryptoLicenseTestAPI16::TearDown(); }
protected:
EntitledMessage entitled_message_;
size_t entitlement_response_length_;
void TestLoadEntitledKeysForHugeBufferLengths(
const std::function<void(size_t, EntitledMessage*)> f,
bool check_status) {
size_t entitled_key_data_size = entitled_message_.entitled_key_data_size();
vector<uint8_t> message(entitled_key_data_size);
memcpy(message.data(), entitled_message_.entitled_key_data(),
entitled_key_data_size);
auto oemcrypto_function = [&](size_t length) {
// Make entitled message big enough so that updated substring offset and
// length fields by core message substring tests can still be able to read
// valid data from entitled message buffer rather than some garbage data.
message.resize(entitled_key_data_size + length);
f(length, &entitled_message_);
return entitled_message_.LoadKeys(message);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
};
// This class is used to test a license that is from a server either that is
// current or one version old.
class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16,
public WithParamInterface<uint32_t> {
protected:
void SetUp() override {
// The only difference between this class and its parent is that we use a
// different license api:
license_api_version_ = GetParam();
license_messages_.set_api_version(license_api_version_);
OEMCryptoLicenseTestAPI16::SetUp();
}
void LoadLicense() {
license_messages_.SignAndVerifyRequest();
license_messages_.CreateDefaultResponse();
license_messages_.EncryptAndSignResponse();
license_messages_.LoadResponse();
}
void TestDecryptCENCForHugeBufferLengths(
const std::function<void(size_t, OEMCrypto_SampleDescription*)> f,
bool check_status) {
LoadLicense();
auto oemcrypto_function = [&](size_t message_length) {
OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CENC);
size_t input_buffer_size = 1;
vector<uint8_t> in_buffer(input_buffer_size + message_length);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(
in_buffer, out_buffer, &sample_description, &subsample_description);
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description.subsamples);
// Actual tests modifies either of these fields to match clear + encrypted
// = in_buffer.size().
sub_samples[0].num_bytes_clear = 0;
sub_samples[0].num_bytes_encrypted = input_buffer_size;
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
int secure_fd = 0;
f(message_length, &sample_description);
if (sample_description.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Secure) {
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
session_.session_id(), in_buffer.size(),
&sample_description.buffers.output_descriptor, &secure_fd);
if (sts != OEMCrypto_SUCCESS) {
LOGI("Secure buffers are not supported.");
return sts;
}
}
// Try to decrypt the data
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), &sample_description, 1, &pattern);
if (sample_description.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Secure) {
OEMCrypto_FreeSecureBuffer(
session_.session_id(),
&sample_description.buffers.output_descriptor, secure_fd);
}
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
void TestDecryptCENCForOutOfRangeOffsetsAndLengths(
const std::function<void(OEMCrypto_SampleDescription*)> f,
bool update_secure_buffer) {
LoadLicense();
OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CENC);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
int secure_fd = 0;
if (update_secure_buffer) {
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
session_.session_id(), in_buffer.size(),
&sample_description.buffers.output_descriptor, &secure_fd);
if (sts != OEMCrypto_SUCCESS) {
LOGI("Secure buffers are not supported.");
return;
}
}
f(&sample_description);
// Try to decrypt the data
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), &sample_description, 1, &pattern);
if (update_secure_buffer) {
OEMCrypto_FreeSecureBuffer(session_.session_id(),
&sample_description.buffers.output_descriptor,
secure_fd);
}
ASSERT_NE(OEMCrypto_SUCCESS, result);
}
};
// This class is used to test a license that is only for v15 license.
class OEMCryptoLicenseTestAPI15 : public OEMCryptoLicenseTestAPI16 {
void SetUp() override {
// The only difference between this class and its parent is that we use a
// different license api:
license_api_version_ = 15;
license_messages_.set_api_version(license_api_version_);
OEMCryptoLicenseTestAPI16::SetUp();
}
};
// This class is used to test each key control block verification string in the
// range kc09-kc1?. This test is parameterized by the API number in the key
// control lock.
class OEMCryptoLicenseTestRangeAPI : public OEMCryptoLicenseTest {};
// Verify that a license may be signed.
TEST_P(OEMCryptoLicenseTest, SignLicenseRequest) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
}
// Verify that a large license request may be signed.
TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) {
const size_t max_size = GetResourceValue(kLargeMessageSize);
license_messages_.set_message_size(max_size);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
}
// Verify that a license may be loaded without a nonce.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
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_SUCCESS));
}
// Verify that a preloaded license may be loaded without first signing the
// request. This test is important for the preloaded licenses used by ATSC and
// CAS.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
if (license_api_version_ > global_features.api_version) {
// We should not attempt to preload a license with an API higher than that
// of OEMCrypto.
license_api_version_ = global_features.api_version;
license_messages_.set_api_version(license_api_version_);
}
license_messages_.set_control(0);
// Notice that we do not call SignAndVerifyRequest -- we do not need a request
// in order to generate a response for a preloaded license.
// The test code uses the core request to create the core response.
license_messages_.core_request().api_major_version =
global_features.api_version;
license_messages_.core_request().api_minor_version = 0;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// 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));
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a license may be reloaded without a nonce, but with a nonzero
// rental duration. In order to start the rental clock, we sign a dummy license
// instead.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// It is not recommended for a license without a nonce to have a nonzero
// rental duration. But there are content providers that have licenses with
// this policy.
license_messages_.core_response().timer_limits.rental_duration_seconds =
kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// 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));
// However, in order to start the rental clock, we have to sign something. So
// we will sign a dummy license request.
LicenseRoundTrip dummy_license(&session2);
ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a license may be loaded with a nonce.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
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_SUCCESS));
}
// Verify that a second license may not be loaded in a session.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
license_messages_.skip_nonce_check();
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// A second load, should NOT succeed.
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
}
// Verify that a second license may not be loaded in a session.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// A second load, should NOT succeed.
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
}
class OEMCryptoEntitlementLicenseTest : public OEMCryptoLicenseTest {
protected:
void LoadEntitlementLicense() {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
};
// This verifies that entitlement keys and entitled content keys can be loaded.
TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
EntitledMessage entitled_message_2(&license_messages_);
entitled_message_2.FillKeyArray();
entitled_message_2.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS));
}
TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true, OEMCrypto_SUCCESS));
EntitledMessage entitled_message_2(&license_messages_);
entitled_message_2.FillKeyArray();
entitled_message_2.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS));
EntitledMessage entitled_message_3(&license_messages_);
entitled_message_3.FillKeyArray();
entitled_message_3.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_3.LoadCasKeys(
/*load_even=*/false, /*load_odd=*/true, OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_3.LoadCasKeys(
/*load_even=*/false, /*load_odd=*/false, OEMCrypto_SUCCESS));
}
// This verifies that entitled content keys cannot be loaded if we have not yet
// loaded the entitlement keys.
TEST_P(OEMCryptoEntitlementLicenseTest,
LoadEntitlementKeysNoEntitlementKeysAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(
entitled_message_1.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT));
}
// This verifies that entitled content keys cannot be loaded if we have loaded
// the wrong entitlement keys.
TEST_P(OEMCryptoEntitlementLicenseTest,
CasOnlyLoadCasKeysNoEntitlementKeysAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true, OEMCrypto_ERROR_INVALID_CONTEXT));
}
// This verifies that entitled content keys cannot be loaded if we have loaded
// the wrong entitlement keys.
TEST_P(OEMCryptoEntitlementLicenseTest,
LoadEntitlementKeysWrongEntitlementKeysAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
const std::string key_id = "no_key";
entitled_message_1.SetEntitlementKeyId(0, key_id);
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(
entitled_message_1.LoadKeys(OEMCrypto_KEY_NOT_ENTITLED));
}
TEST_P(OEMCryptoEntitlementLicenseTest,
CasOnlyLoadCasKeysWrongEntitlementKeysAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
const std::string key_id = "no_key";
entitled_message_1.SetEntitlementKeyId(0, key_id);
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true, OEMCrypto_KEY_NOT_ENTITLED));
}
// This verifies that entitled content keys cannot be loaded if we specify an
// entitled key session that has not been created.
TEST_P(OEMCryptoEntitlementLicenseTest,
LoadEntitlementKeysWrongEntitledKeySessionAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(0);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
}
TEST_P(OEMCryptoEntitlementLicenseTest,
CasOnlyLoadCasKeysWrongEntitledKeySessionAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(0);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true,
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
}
// This verifies that entitled content keys cannot be loaded if we specify an
// entitled key session that is actually an oemcrypto session.
TEST_P(OEMCryptoEntitlementLicenseTest,
LoadEntitlementKeysOemcryptoSessionAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(session_.session_id());
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
}
TEST_P(OEMCryptoEntitlementLicenseTest,
CasOnlyLoadCasKeysOemcryptoSessionAPI17) {
LoadEntitlementLicense();
uint32_t key_session_id = 0;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(session_.session_id());
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true,
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoEntitlementLicenseTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryPrepareRenewalRequestForHugeBufferLength) {
RenewalRoundTrip renewal_messages(&license_messages_);
auto oemcrypto_function = [&renewal_messages](size_t buffer_length) {
renewal_messages.set_message_size(buffer_length);
return renewal_messages.SignAndCreateRequestWithCustomBufferLengths();
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryPrepareRenewalRequestForHugeSignatureLength) {
RenewalRoundTrip renewal_messages(&license_messages_);
auto oemcrypto_function = [&renewal_messages](size_t buffer_length) {
renewal_messages.set_request_signature_size(buffer_length);
return renewal_messages.SignAndCreateRequestWithCustomBufferLengths();
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryPrepareRenewalRequestForHugeCoreMessageLength) {
RenewalRoundTrip renewal_messages(&license_messages_);
auto oemcrypto_function = [&renewal_messages](size_t buffer_length) {
renewal_messages.set_core_message_size(buffer_length);
return renewal_messages.SignAndCreateRequestWithCustomBufferLengths();
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyIdLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_id.length =
key_id_length;
},
!kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyIdOffset) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_offset, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_id.offset =
key_id_offset;
},
!kCheckStatus);
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyIdLength) {
auto& content_key_id =
entitled_message_.entitled_key_array()[0].content_key_id;
content_key_id.length =
entitlement_response_length_ - content_key_id.offset + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyIdOffset) {
auto& content_key_id =
entitled_message_.entitled_key_array()[0].content_key_id;
content_key_id.offset =
entitlement_response_length_ - content_key_id.length + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringEntitlementKeyIdLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].entitlement_key_id.length =
key_id_length;
},
!kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringEntitlementKeyIdOffset) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_offset, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].entitlement_key_id.offset =
key_id_offset;
},
!kCheckStatus);
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringEntitlementKeyIdLength) {
auto& entitlement_key_id =
entitled_message_.entitled_key_array()[0].entitlement_key_id;
entitlement_key_id.length =
entitlement_response_length_ - entitlement_key_id.offset + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringEntitlementKeyIdOffset) {
auto& entitlement_key_id =
entitled_message_.entitled_key_array()[0].entitlement_key_id;
entitlement_key_id.offset =
entitlement_response_length_ - entitlement_key_id.length + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataIvLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t content_key_data_iv_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_data_iv.length =
content_key_data_iv_length;
},
!kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataIvOffset) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t content_key_data_iv_offset, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_data_iv.offset =
content_key_data_iv_offset;
},
!kCheckStatus);
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataIvLength) {
auto& content_key_data_iv =
entitled_message_.entitled_key_array()[0].content_key_data_iv;
content_key_data_iv.length =
entitlement_response_length_ - content_key_data_iv.offset + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataIvOffset) {
auto& content_key_data_iv =
entitled_message_.entitled_key_array()[0].content_key_data_iv;
content_key_data_iv.offset =
entitlement_response_length_ - content_key_data_iv.length + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t content_key_data_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_data.length =
content_key_data_length;
},
!kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataOffset) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t content_key_data_offset, EntitledMessage* entitled_message) {
entitled_message->entitled_key_array()[0].content_key_data.offset =
content_key_data_offset;
},
!kCheckStatus);
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataLength) {
auto& content_key_data =
entitled_message_.entitled_key_array()[0].content_key_data;
content_key_data.length =
entitlement_response_length_ - content_key_data.offset + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(
OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataOffset) {
auto& content_key_data =
entitled_message_.entitled_key_array()[0].content_key_data;
content_key_data.offset =
entitlement_response_length_ - content_key_data.length + 1;
ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys());
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeEntitlementKeyIdLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_data()->entitlement_key_id_length =
key_id_length;
},
!kCheckStatus);
}
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeContentKeyIdLength) {
TestLoadEntitledKeysForHugeBufferLengths(
[](size_t key_id_length, EntitledMessage* entitled_message) {
entitled_message->entitled_key_data()->content_key_id_length =
key_id_length;
},
!kCheckStatus);
}
// This verifies that entitled content keys API does not crash for unreasonable
// input message buffer lengths.
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryLoadEntitledKeysForHugeBufferLength) {
auto oemcrypto_function = [&](size_t buffer_length) {
size_t entitled_key_data_length =
entitled_message_.entitled_key_data_size();
vector<uint8_t> message(entitled_key_data_length);
memcpy(message.data(), entitled_message_.entitled_key_data(),
entitled_key_data_length);
message.resize(buffer_length);
return entitled_message_.LoadKeys(message);
};
// We are not constructing a valid message for load entitled content keys.
// Hence skipping status check.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
// This tests load license with an 8k license response.
TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
const size_t max_size = GetResourceValue(kLargeMessageSize);
license_messages_.set_message_size(max_size);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Verify that you can't use LoadKeys on a v16 license.
TEST_F(OEMCryptoLicenseTestAPI16, UseWrongLoadAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// After the license round trip was create for v16, we now tell it to use v15
// so it call LoadKeys instead of LoadLicense. This means the license request
// was made from a v16 device, and the response was created and signed by a
// v16 server. So OEMCrypto should only accept it if we load it using
// LoadLicense. A call to LoadKeys should fail.
license_messages_.set_api_version(kCoreMessagesAPI - 1);
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
// Each of the following LoadKeyWithBadRange_* tests is similar. They verify
// that OEMCrypto_LoadLicense checks the range of all the pointers. It should
// reject a message if the pointer does not point into the message buffer.
//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().enc_mac_keys.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().enc_mac_keys_iv.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().key_array[0].key_id.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().key_array[1].key_data.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().key_array[1].key_data_iv.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().key_array[2].key_control.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().key_array[2].key_control_iv.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) {
license_messages_.set_control(wvoec::kControlNonceOrEntry);
license_messages_.set_pst("my_pst");
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size.
license_messages_.core_response().pst.offset +=
sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// If we have a pst, then we need a usage entry.
ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
// The IV should not be identical to the data right before the encrypted mac
// keys. This requirement was added in 15.2, so it frequently fails on
// production devices.
// This test is being restricted to v16 devices on rvc-dev branch because we
// only required v15.1 on Android for Q.
TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIVAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// This is suspicious: the data right before the mac keys is identical to the
// iv.
memcpy(license_messages_.response_data().padding,
license_messages_.response_data().mac_key_iv,
sizeof(license_messages_.response_data().padding));
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Test that LoadKeys fails when a key is loaded with no key control block.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControl) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_response().key_array[2].key_control.offset = 0;
license_messages_.core_response().key_array[2].key_control.length = 0;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Verify that LoadKeys fails when a key's nonce is wrong.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
license_messages_.response_data().keys[i].control.nonce ^= 42;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// Verify that LoadKeys fails when the core message's nonce is wrong.
TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce2) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_request().nonce ^= 42;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// Verify that LoadKeys fails when the core message's session is wrong.
TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce3) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_request().session_id++;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// Verify that LoadKeys fails when an attempt is made to use a nonce twice.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithRepeatNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
const uint32_t nonce = session_.nonce();
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// This is the first attempt. It should succeed.
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// Now, open a new session and try to load a license with the same nonce.
session_.close();
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_));
license_messages_.skip_nonce_check();
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// Repeat the nonce.
license_messages_.core_request().nonce = nonce;
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// This tests that a nonce cannot be used in new session. This is similar to
// the previous test, but does not use the nonce in the first session. The nonce
// should be tied to a session, so generating a nonce in the first session and
// then using it in the second session should fail.
TEST_P(OEMCryptoLicenseTest, LoadKeyNonceReopenSession) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
uint32_t nonce = session_.nonce();
// Do not use the nonce now. Close session and use it after re-opening.
ASSERT_NO_FATAL_FAILURE(session_.close());
// Actually, this isn't the same session. OEMCrypto opens a new session, but
// we are guarding against the possiblity that it re-uses the session data
// and might not clear out the nonce correctly.
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_));
license_messages_.skip_nonce_check();
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_request().nonce = nonce;
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// This tests that a nonce cannot be used in wrong session. This is similar to
// the previous test, except we do not close session 1 before we open session 2.
TEST_P(OEMCryptoLicenseTest, LoadKeyNonceWrongSession) {
// First, open a session and generate a nonce. We don't use the nonce in this
// session.
Session s2;
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2));
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
uint32_t nonce = s2.nonce();
// Do not use the nonce. Also, leave the session open. We want to make sure
// that session_ and s2 do NOT share a nonce. This is different from
// the LoadKeyNonceReopenSession in that we do not close s1.
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_request().nonce = nonce;
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
}
// LoadKeys should fail if the key control block as a bad verification string.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadVerification) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.response_data().keys[1].control.verification[2] = 'Z';
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// This test verifies that LoadKeys still works when the message is not aligned
// in memory on a word (2 or 4 byte) boundary.
TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
std::vector<uint8_t> buffer(1, '0'); // A string of 1 byte long.
size_t offset = buffer.size();
ASSERT_EQ(1u, offset);
// We assume that vectors are allocated on as a small chunk of data that is
// aligned on a word boundary. I.e. we assume buffer is word aligned. Next,
// we append the message to buffer after the single padding byte.
buffer.insert(buffer.end(),
license_messages_.encrypted_response_buffer().begin(),
license_messages_.encrypted_response_buffer().end());
// Thus, buffer[offset] is NOT word aligned.
const uint8_t* unaligned_message = &buffer[offset];
if (license_api_version_ < kCoreMessagesAPI) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadKeys(
session_.session_id(), unaligned_message,
license_messages_.encrypted_response_buffer().size(),
license_messages_.response_signature().data(),
license_messages_.response_signature().size(),
license_messages_.core_response().enc_mac_keys_iv,
license_messages_.core_response().enc_mac_keys,
license_messages_.core_response().key_array_length,
license_messages_.core_response().key_array,
license_messages_.core_response().pst,
license_messages_.core_response().srm_restriction_data,
static_cast<OEMCrypto_LicenseType>(
license_messages_.core_response().license_type)));
} else {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadLicense(
session_.session_id(), unaligned_message,
license_messages_.encrypted_response_buffer().size(),
license_messages_.serialized_core_message().size(),
license_messages_.response_signature().data(),
license_messages_.response_signature().size()));
}
}
// Verifies that a session can't reload a license without being closed and
// reopened.
TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
}
TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
// Re-set the API version. The function VerifyRequestSignature sets the api to
// be a sane value. But in this test, we want to verify an unsupported version
// is rejected.
license_messages_.set_api_version(license_api_version_);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// If this is a future API, then LoadKeys should fail.
if (global_features.api_version < license_api_version_) {
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse())
<< "Load License succeeded for future api kc" << license_api_version_;
} else {
// Otherwise, LoadKeys should succeed.
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse())
<< "Load License failed for key control block kc"
<< license_api_version_;
}
}
// Range of API versions to test. This should start several versions old, and
// go to the current API + 2. We use +2 because we want to test at least 1
// future API, and the ::testing::Range is not inclusive.
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTestRangeAPI,
Range<uint32_t>(10, kCurrentAPI + 2));
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
license_messages_.response_signature()[0] ^= 42;
ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
license_messages_.LoadResponse());
}
/// @}
/// @addtogroup decrypt
/// @{
// LoadKeys should fail if we try to load keys with no keys.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
license_messages_.set_num_keys(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Like the previous test, except we ask for a nonce first.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_num_keys(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Following two tests will test huge values for num bytes clear, num bytes
// encrypted, input data length and clear address, clear address_length.
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForHugeNumBytesClearAndBuffers) {
TestDecryptCENCForHugeBufferLengths(
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description->subsamples);
sub_samples[0].num_bytes_clear =
sub_samples[0].num_bytes_clear + message_size;
},
!kCheckStatus);
}
TEST_P(OEMCryptoLicenseTest,
DecryptCENCForNumBytesClearPlusEncryptedOverflowsSize) {
LoadLicense();
OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CENC);
size_t input_buffer_size = 1;
vector<uint8_t> in_buffer(input_buffer_size);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description.subsamples);
// If Decrypt cenc API does not check for overflow on clear + encrypted
// addition operation. This will result in 1 which will match with input data
// length, which causes validation to pass.
sub_samples[0].num_bytes_clear = 2;
sub_samples[0].num_bytes_encrypted = ~0;
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern));
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForHugeNumBytesEncryptedAndBuffers) {
TestDecryptCENCForHugeBufferLengths(
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description->subsamples);
sub_samples[0].num_bytes_encrypted =
sub_samples[0].num_bytes_encrypted + message_size;
},
!kCheckStatus);
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForHugeSecureHandleLength) {
TestDecryptCENCForHugeBufferLengths(
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description->subsamples);
// TestDecryptCENCForHugeBufferLengths alloctes huge secure handle
// buffer.
sample_description->buffers.output_descriptor.type =
OEMCrypto_BufferType_Secure;
sub_samples[0].num_bytes_clear =
sub_samples[0].num_bytes_clear + message_size;
},
!kCheckStatus);
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesClear) {
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
[](OEMCrypto_SampleDescription* sample_description) {
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description->subsamples);
sub_samples[0].num_bytes_clear = sub_samples[0].num_bytes_clear + 1;
},
!kDecryptCENCSecureBuffer);
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesEncrypted) {
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
[](OEMCrypto_SampleDescription* sample_description) {
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description->subsamples);
sub_samples[0].num_bytes_encrypted =
sub_samples[0].num_bytes_encrypted + 1;
},
!kDecryptCENCSecureBuffer);
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForOutOfRangeSecureBufferOffset) {
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
[](OEMCrypto_SampleDescription* sample_description) {
sample_description->buffers.output_descriptor.type =
OEMCrypto_BufferType_Secure;
sample_description->buffers.output_descriptor.buffer.secure.offset =
sample_description->buffers.output_descriptor.buffer.secure
.secure_buffer_length +
1;
},
kDecryptCENCSecureBuffer);
}
// Cannot decrypt without first selecting a key.
TEST_P(OEMCryptoLicenseTest, FailDecryptWithoutSelect) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
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(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// SelectKey should fail if we attempt to select a key that has not been loaded.
// Also, the error should be NO_CONTENT_KEY.
// This test should pass for v15 devices, except that the exact error code was
// not specified until v16.
TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
const char* key_id = "no_key";
OEMCryptoResult sts = OEMCrypto_SelectKey(
session_.session_id(), reinterpret_cast<const uint8_t*>(key_id),
strlen(key_id), OEMCrypto_CipherMode_CENC);
if (sts != OEMCrypto_SUCCESS) {
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
} else {
// Delayed error code. If select key was a success, then we should
// eventually see the error when we decrypt.
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Generate test data
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_EQ(sts, OEMCrypto_ERROR_NO_CONTENT_KEY);
}
}
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
const char* content_key_id = "content_key_id";
entitled_message_1.SetContentKeyId(0, content_key_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id),
strlen(content_key_id), OEMCrypto_CipherMode_CENC));
}
// SelectEntitledKey should fail if we attempt to select a key that has not been
// loaded. Also, the error should be NO_CONTENT_KEY.
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
const char* content_key_id = "no_key";
ASSERT_EQ(
OEMCrypto_ERROR_NO_CONTENT_KEY,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id),
strlen(content_key_id), OEMCrypto_CipherMode_CENC));
}
// Select key with entitlement license fails if the key id is entitilement key
// id.
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CENC));
}
// This verifies that entitled key sessions can be created and removed.
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id_1;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_1));
ASSERT_NE(key_session_id_1, 0u); // 0 is a reserved id number.
uint32_t key_session_id_2;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_2));
ASSERT_NE(key_session_id_2, 0u); // 0 is a reserved id number.
// Entitled key sessions should have unique ids.
ASSERT_NE(key_session_id_1, key_session_id_2);
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_RemoveEntitledKeySession(key_session_id_1));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_RemoveEntitledKeySession(key_session_id_2));
}
// This verifies that multiple entitled key sessions can be created. They can
// load and select keys independently.
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id_1;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_1));
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id_1);
const char* content_key_id_1 = "content_key_id_1";
entitled_message_1.SetContentKeyId(0, content_key_id_1);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
// We can select content key 1 in entitled key session 1.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id_1,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CENC));
// Create another entitled key session.
uint32_t key_session_id_2;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_2));
// Entitled key sessions should have unique ids.
ASSERT_NE(key_session_id_1, key_session_id_2);
EntitledMessage entitled_message_2(&license_messages_);
entitled_message_2.FillKeyArray();
entitled_message_2.SetEntitledKeySession(key_session_id_2);
const char* content_key_id_2 = "content_key_id_2";
entitled_message_2.SetContentKeyId(0, content_key_id_2);
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS));
// We can select content key 2 in entitled key session 2.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id_2,
reinterpret_cast<const uint8_t*>(content_key_id_2),
strlen(content_key_id_2), OEMCrypto_CipherMode_CENC));
// Content key id 1 is not in entitled key session 2.
ASSERT_EQ(
OEMCrypto_ERROR_NO_CONTENT_KEY,
OEMCrypto_SelectKey(key_session_id_2,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CENC));
// Content key id 2 is not in entitled key session 1.
ASSERT_EQ(
OEMCrypto_ERROR_NO_CONTENT_KEY,
OEMCrypto_SelectKey(key_session_id_1,
reinterpret_cast<const uint8_t*>(content_key_id_2),
strlen(content_key_id_2), OEMCrypto_CipherMode_CENC));
}
// This verifies that within an entitled key session, each entitlement key can
// corresponds to only one content key at most.
TEST_P(OEMCryptoLicenseTest,
EntitledKeySessionOneContentKeyPerEntitlementAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
// Construct and load content keys to entitled key session.
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
const char* content_key_id_1 = "content_key_id_1";
entitled_message_1.SetContentKeyId(0, content_key_id_1);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
// We can select content key 1 in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CENC));
// Load content key with new content id.
const char* content_key_id_2 = "content_key_id_2";
entitled_message_1.SetContentKeyId(0, content_key_id_2);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
// We can select content key 2 in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_2),
strlen(content_key_id_2), OEMCrypto_CipherMode_CENC));
// Content key one is no longer in the entitled key session as they use the
// same entitlement key.
ASSERT_EQ(
OEMCrypto_ERROR_NO_CONTENT_KEY,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CENC));
}
// Decrypt should fail if the license is entitlement license, and the decrypt
// call is made with oemcrypto session id (should use entitled key session id
// instead).
TEST_P(OEMCryptoLicenseTest,
RejectOecSessionDecryptWithEntitlementLicenseAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id));
// Construct and load content keys to entitled key session.
EntitledMessage entitled_message(&license_messages_);
entitled_message.FillKeyArray();
entitled_message.SetEntitledKeySession(key_session_id);
const char* content_key_id = "content_key_id";
entitled_message.SetContentKeyId(0, content_key_id);
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id),
strlen(content_key_id), OEMCrypto_CipherMode_CENC));
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
in_buffer, out_buffer, &sample_description, &subsample_description));
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data with oemcrypto session id.
EXPECT_EQ(OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern),
OEMCrypto_ERROR_INVALID_CONTEXT);
// Decrypt the data with entitled key session id succeed.
EXPECT_EQ(
OEMCrypto_DecryptCENC(key_session_id, &sample_description, 1, &pattern),
OEMCrypto_SUCCESS);
}
// This verifies that an entitled key session can be reassociated to an
// OEMCrypto session.
TEST_P(OEMCryptoLicenseTest, ReassociateEntitledKeySessionAPI17) {
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// Setup another session.
Session session2;
ASSERT_NO_FATAL_FAILURE(session2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
// Setup an entitled key session in the first OEMCrypto session.
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message(&license_messages_);
entitled_message.FillKeyArray();
entitled_message.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(OEMCrypto_SUCCESS));
// Now reassociate the entitled key session to the second OEMCrypto session.
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_ReassociateEntitledKeySession(
key_session_id, session2.session_id()));
// session2 does not have entitlement keys.
ASSERT_NO_FATAL_FAILURE(
entitled_message.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT));
// Now reassociate the entitled key session back to the first OEMCrypto
// session.
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_ReassociateEntitledKeySession(
key_session_id, session_.session_id()));
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadKeys(OEMCrypto_SUCCESS));
}
// 'cens' mode is no longer supported in v16
TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCryptoResult sts;
sts = OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Create a non-zero pattern to indicate this is 'cens'
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
// 'cbc1' mode is no longer supported in v16
TEST_P(OEMCryptoLicenseTest, RejectCbc1API16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCryptoResult sts;
sts = OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CBCS);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Create a zero pattern to indicate this is 'cbc1'
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
TEST_P(OEMCryptoLicenseTest, RejectCbcsWithBlockOffset) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCryptoResult sts;
sts = OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CBCS);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
subsample_description.block_offset = 5; // Any value 1-15 will do.
// Create a non-zero pattern to indicate this is 'cbcs'.
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
TEST_P(OEMCryptoLicenseTest, RejectOversizedBlockOffset) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCryptoResult sts;
sts = OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
subsample_description.block_offset = 0xFF; // Anything 16+
// Create a zero pattern to indicate this is 'cenc'.
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
// Try again with the minimum invalid value
subsample_description.block_offset = 16;
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
}
// After loading keys, we should be able to query the key control block. If we
// attempt to query a key that has not been loaded, the error should be
// NO_CONTENT_KEY.
TEST_P(OEMCryptoLicenseTest, QueryKeyControl) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// Note: successful cases are tested in VerifyTestKeys.
KeyControlBlock block;
size_t size = sizeof(block) - 1;
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
session_.session_id(), license_messages_.response_data().keys[0].key_id,
license_messages_.response_data().keys[0].key_id_length,
reinterpret_cast<uint8_t*>(&block), &size);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
const char* key_id = "no_key";
size = sizeof(block);
ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY,
OEMCrypto_QueryKeyControl(
session_.session_id(), reinterpret_cast<const uint8_t*>(key_id),
strlen(key_id), reinterpret_cast<uint8_t*>(&block), &size));
}
// This case tests against the issue where certain 16.4.x SDK versions return a
// clear key control block (KCB) in the license response. An OEMCrypto v17.1+
// implementation should be able to handle the clear KCB in the 16.4.x response
// and load the license correctly.
TEST_F(OEMCryptoSessionTests, ClearKcbAPI16_4) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
// Set odk version in the license response to be 16.4
oemcrypto_core_message::features::CoreMessageFeatures features = {};
features.maximum_major_version = 16;
features.maximum_minor_version = 4;
constexpr bool kForceClearKcb = true;
ASSERT_NO_FATAL_FAILURE(
license_messages.EncryptAndSignResponseWithCoreMessageFeatures(
features, kForceClearKcb));
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
s.session_id(), license_messages.response_data().keys[0].key_id,
license_messages.response_data().keys[0].key_id_length,
reinterpret_cast<uint8_t*>(&block), &size);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryLoadLicenseForHugeSignatureLength) {
auto oemcrypto_function = [&](size_t signature_size) {
Session s;
LicenseRoundTrip license_messages(&s);
s.open();
InstallTestRSAKey(&s);
license_messages.SignAndVerifyRequest();
license_messages.CreateDefaultResponse();
license_messages.EncryptAndSignResponse();
vector<uint8_t> signature(signature_size);
OEMCryptoResult result = OEMCrypto_LoadLicense(
s.session_id(), license_messages.encrypted_response_buffer().data(),
license_messages.encrypted_response_buffer().size(),
license_messages.serialized_core_message().size(), signature.data(),
signature_size);
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_id.length = length;
license_messages->response_data().keys[0].key_id_length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_id.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_id = license_messages->core_response().key_array[0].key_id;
key_id.length = response_message_length - key_id.offset + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_id = license_messages->core_response().key_array[0].key_id;
key_id.offset = response_message_length - key_id.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_data_iv.length =
length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_data_iv.offset =
offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_data_iv =
license_messages->core_response().key_array[0].key_data_iv;
key_data_iv.length = response_message_length - key_data_iv.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_data_iv =
license_messages->core_response().key_array[0].key_data_iv;
key_data_iv.offset = response_message_length - key_data_iv.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_data.length = length;
license_messages->response_data().keys[0].key_data_length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_data.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_data =
license_messages->core_response().key_array[0].key_data;
key_data.length = response_message_length - key_data.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_data =
license_messages->core_response().key_array[0].key_data;
key_data.offset = response_message_length - key_data.length + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_control_iv.length =
length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_control_iv.offset =
offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_control_iv =
license_messages->core_response().key_array[0].key_control_iv;
key_control_iv.length =
response_message_length - key_control_iv.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_control_iv =
license_messages->core_response().key_array[0].key_control_iv;
key_control_iv.offset =
response_message_length - key_control_iv.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_control.length =
length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().key_array[0].key_control.offset =
offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_control =
license_messages->core_response().key_array[0].key_control;
key_control.length = response_message_length - key_control.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& key_control =
license_messages->core_response().key_array[0].key_control;
key_control.offset = response_message_length - key_control.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().enc_mac_keys_iv.length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().enc_mac_keys_iv.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& enc_mac_keys_iv =
license_messages->core_response().enc_mac_keys_iv;
enc_mac_keys_iv.length =
response_message_length - enc_mac_keys_iv.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& enc_mac_keys_iv =
license_messages->core_response().enc_mac_keys_iv;
enc_mac_keys_iv.offset =
response_message_length - enc_mac_keys_iv.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().enc_mac_keys.length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().enc_mac_keys.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& enc_mac_keys = license_messages->core_response().enc_mac_keys;
enc_mac_keys.length = response_message_length - enc_mac_keys.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& enc_mac_keys = license_messages->core_response().enc_mac_keys;
enc_mac_keys.offset = response_message_length - enc_mac_keys.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().pst.length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().pst.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& pst = license_messages->core_response().pst;
pst.length = response_message_length - pst.offset + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& pst = license_messages->core_response().pst;
pst.offset = response_message_length - pst.length + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t length, LicenseRoundTrip* license_messages) {
license_messages->core_response().srm_restriction_data.length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataOffset) {
TestLoadLicenseForHugeBufferLengths(
[](size_t offset, LicenseRoundTrip* license_messages) {
license_messages->core_response().srm_restriction_data.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataLength) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& srm_restriction_data =
license_messages->core_response().srm_restriction_data;
srm_restriction_data.length =
response_message_length - srm_restriction_data.offset + 1;
});
}
TEST_P(
OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataOffset) {
TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths(
[](size_t response_message_length, LicenseRoundTrip* license_messages) {
auto& srm_restriction_data =
license_messages->core_response().srm_restriction_data;
srm_restriction_data.offset =
response_message_length - srm_restriction_data.length + 1;
});
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeResponseLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t message_size, LicenseRoundTrip* license_messages) {
license_messages->set_message_size(message_size);
},
!kCheckStatus, !kUpdateCoreMessageSubstringValues);
}
TEST_P(OEMCryptoLicenseOverflowTest,
OEMCryptoMemoryLoadLicenseForHugeCoreMessageLength) {
TestLoadLicenseForHugeBufferLengths(
[](size_t message_size, LicenseRoundTrip* license_messages) {
license_messages->set_core_message_size(message_size);
},
!kCheckStatus, !kUpdateCoreMessageSubstringValues);
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseOverflowTest,
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeResponseLength) {
auto oemcrypto_function = [&](size_t message_size) {
Session s;
LicenseRoundTrip license_messages(&s);
s.open();
LoadLicense(s, license_messages);
RenewalRoundTrip renewal_messages(&license_messages);
renewal_messages.SignAndVerifyRequest();
renewal_messages.CreateDefaultResponse();
renewal_messages.set_message_size(message_size);
renewal_messages.EncryptAndSignResponse();
OEMCryptoResult result = renewal_messages.LoadResponse();
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryLoadRenewalForHugeSignatureLength) {
auto oemcrypto_function = [&](size_t signature_size) {
Session s;
LicenseRoundTrip license_messages(&s);
s.open();
LoadLicense(s, license_messages);
RenewalRoundTrip renewal_messages(&license_messages);
renewal_messages.SignAndVerifyRequest();
renewal_messages.CreateDefaultResponse();
renewal_messages.EncryptAndSignResponse();
vector<uint8_t> signature(signature_size);
OEMCryptoResult result = OEMCrypto_LoadRenewal(
s.session_id(), renewal_messages.encrypted_response_buffer().data(),
renewal_messages.encrypted_response_buffer().size(),
renewal_messages.serialized_core_message().size(), signature.data(),
signature_size);
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryLoadRenewalForHugeCoreMessageLength) {
auto oemcrypto_function = [&](size_t core_message_size) {
Session s;
LicenseRoundTrip license_messages(&s);
s.open();
LoadLicense(s, license_messages);
RenewalRoundTrip renewal_messages(&license_messages);
renewal_messages.SignAndVerifyRequest();
renewal_messages.CreateDefaultResponse();
renewal_messages.set_core_message_size(core_message_size);
renewal_messages.EncryptAndSignResponse();
OEMCryptoResult result = renewal_messages.LoadResponse();
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_id_length.
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryQueryKeyControlForHugeKeyIdLength) {
Session session;
LicenseRoundTrip license_messages(&session);
session.open();
LoadLicense(session, license_messages);
OEMCrypto_SESSION session_id = session.session_id();
vector<uint8_t> valid_key_id(
license_messages.response_data().keys[0].key_id,
license_messages.response_data().keys[0].key_id + kTestKeyIdMaxLength);
auto oemcrypto_function = [&session_id,
&valid_key_id](size_t additional_key_id_length) {
vector<uint8_t> key_id(valid_key_id);
key_id.resize(valid_key_id.size() + additional_key_id_length);
KeyControlBlock block;
size_t size = sizeof(block);
return OEMCrypto_QueryKeyControl(session_id, key_id.data(), key_id.size(),
reinterpret_cast<uint8_t*>(&block), &size);
};
// We do not want to stop as soon as API results in an error as it would
// return error on first iteration as key_id is invalid.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block
// length.
TEST_F(OEMCryptoSessionTests,
OEMCryptoMemoryQueryKeyControlForHugeKeyControlBlockLength) {
Session session;
LicenseRoundTrip license_messages(&session);
session.open();
LoadLicense(session, license_messages);
OEMCrypto_SESSION session_id = session.session_id();
uint8_t* key_id = license_messages.response_data().keys[0].key_id;
size_t key_id_length = license_messages.response_data().keys[0].key_id_length;
auto oemcrypto_function = [&session_id, &key_id,
&key_id_length](size_t buffer_length) {
size_t key_control_block_length = buffer_length;
vector<uint8_t> key_control_block(key_control_block_length);
return OEMCrypto_QueryKeyControl(session_id, key_id, key_id_length,
key_control_block.data(),
&key_control_block_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// If the device says it supports anti-rollback in the hardware, then it should
// accept a key control block with the anti-rollback hardware bit set.
// Otherwise, it should reject that key control block.
TEST_P(OEMCryptoLicenseTest, AntiRollbackHardwareRequired) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(wvoec::kControlRequireAntiRollbackHardware);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
OEMCryptoResult sts = license_messages_.LoadResponse();
if (OEMCrypto_IsAntiRollbackHwPresent()) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
} else {
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts);
}
}
// This test verifies that OEMCrypto can load the number of keys required for
// the reported resource level.
TEST_P(OEMCryptoLicenseTest, MinimumKeys) {
const size_t num_keys = GetResourceValue(kMaxKeysPerSession);
ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating.";
license_messages_.set_num_keys(static_cast<uint32_t>(num_keys));
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
constexpr bool kSelectKeyFirst = true;
for (size_t key_index = 0; key_index < num_keys; key_index++) {
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
}
}
// This test verifies that OEMCrypto can load the total number of keys required
// for the reported resource level.
void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) {
const size_t max_total_keys = GetResourceValue(kMaxTotalKeys);
ASSERT_LE(num_keys_per_session, kMaxNumKeys) << "Update test constants.";
std::vector<std::unique_ptr<Session>> sessions;
std::vector<std::unique_ptr<LicenseRoundTrip>> licenses;
size_t total_keys = 0;
for (size_t i = 0; total_keys < max_total_keys; i++) {
sessions.push_back(std::unique_ptr<Session>(new Session()));
licenses.push_back(std::unique_ptr<LicenseRoundTrip>(
new LicenseRoundTrip(sessions[i].get())));
const size_t num_keys =
std::min(max_total_keys - total_keys, num_keys_per_session);
licenses[i]->set_num_keys(static_cast<uint32_t>(num_keys));
total_keys += num_keys;
ASSERT_NO_FATAL_FAILURE(sessions[i]->open());
ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(sessions[i].get()));
ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest());
}
for (size_t i = 0; i < licenses.size(); i++) {
ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse());
}
constexpr bool kSelectKeyFirst = true;
for (size_t i = 0; i < licenses.size(); i++) {
for (size_t key_index = 0; key_index < licenses[i]->num_keys();
key_index++) {
ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR(
kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
}
}
// Second call to decrypt for each session.
for (size_t i = 0; i < licenses.size(); i++) {
for (size_t key_index = 0; key_index < licenses[i]->num_keys();
key_index++) {
ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR(
kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
}
}
}
// This test verifies that OEMCrypto can load the total number of keys required
// for the reported resource level. This maximizes keys per session.
TEST_P(OEMCryptoLicenseTest, MaxTotalKeysPerSession) {
const size_t max_num_keys = GetResourceValue(kMaxKeysPerSession);
TestMaxKeys(this, max_num_keys);
}
// This test verifies that OEMCrypto can load the total number of keys required
// for the reported resource level. This maximizes number of sessions.
TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) {
const size_t max_total_keys = GetResourceValue(kMaxTotalKeys);
const size_t max_sessions = GetResourceValue(kMaxConcurrentSession);
const size_t max_num_keys = max_total_keys / max_sessions + 1;
TestMaxKeys(this, max_num_keys);
}
// This test verifies that the minimum patch level can be required. The device
// should accept a key control block with the current patch level, and it should
// reject any key control blocks with a future patch level.
TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) {
uint8_t patch_level = OEMCrypto_Security_Patch_Level();
printf(" Current Patch Level: %u.\n", patch_level);
{
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control(patch_level
<< wvoec::kControlSecurityPatchLevelShift);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
EXPECT_EQ(global_features.api_version, license_messages.api_version());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
// Reject any future patch levels.
if (patch_level < 0x3F) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control((patch_level + 1)
<< wvoec::kControlSecurityPatchLevelShift);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, license_messages.LoadResponse());
}
// Accept an old patch level.
if (patch_level > 0) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control((patch_level - 1)
<< wvoec::kControlSecurityPatchLevelShift);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
}
// Used to test the different HDCP versions. This test is parameterized by the
// required HDCP version in the key control block.
class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests,
public WithParamInterface<int> {
protected:
void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) {
OEMCryptoResult sts;
OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control((version << wvoec::kControlHDCPVersionShift) |
wvoec::kControlObserveHDCP |
wvoec::kControlHDCPRequired);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
if (((version <= HDCP_V2_3 || current >= HDCP_V1_0) && version > current) ||
(current == HDCP_V1 && version >= HDCP_V1_0)) {
if (global_features.api_version >= 16) {
// Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or
// OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be
// reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected.
ASSERT_NO_FATAL_FAILURE(
s.TestDecryptCTR(true, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
} else {
ASSERT_NO_FATAL_FAILURE(
s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
}
} else {
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
}
}
};
TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) {
// Test parameterized by HDCP version.
DecryptWithHDCP(static_cast<OEMCrypto_HDCP_Capability>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP,
Range(1, 6));
// Used to test the different HDCP versions. This test is parameterized by the
// required HDCP version in the key control block.
class OEMCryptoSessionTestLoadCasKeysWithHDCP : public OEMCryptoSessionTests,
public WithParamInterface<int> {
protected:
void LoadCasKeysWithHDCP(OEMCrypto_HDCP_Capability version) {
OEMCryptoResult sts;
OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control((version << wvoec::kControlHDCPVersionShift) |
wvoec::kControlObserveHDCP |
wvoec::kControlHDCPRequired);
license_messages.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
uint32_t key_session_id;
sts = OEMCrypto_CreateEntitledKeySession(s.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
if (((version <= HDCP_V2_3 || current >= HDCP_V1_0) && version > current) ||
(current == HDCP_V1 && version >= HDCP_V1_0)) {
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true,
OEMCrypto_ERROR_INSUFFICIENT_HDCP))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
} else {
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
/*load_even=*/true, /*load_odd=*/true, OEMCrypto_SUCCESS))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
}
}
};
TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) {
// Test parameterized by HDCP version.
LoadCasKeysWithHDCP(static_cast<OEMCrypto_HDCP_Capability>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP,
Range(1, 6));
/// @}
/// @addtogroup renewal
/// @{
//
// Load, Refresh Keys Test
//
class OEMCryptoRefreshTest : public OEMCryptoLicenseTest {
protected:
void SetUp() override {
OEMCryptoLicenseTest::SetUp();
// These values allow us to run a few simple duration tests or just start
// playback right away. All times are in seconds since the license was
// signed.
// Soft expiry false means timers are strictly enforce.
timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.soft_enforce_playback_duration = false;
// Playback may begin immediately.
timer_limits_.earliest_playback_start_seconds = 0;
// First playback may be within the first two seconds.
timer_limits_.rental_duration_seconds = kDuration;
// Once started, playback may last two seconds without a renewal.
timer_limits_.initial_renewal_duration_seconds = kDuration;
// Total playback is not limited.
timer_limits_.total_playback_duration_seconds = 0;
}
void LoadLicense() {
license_messages_.core_response().timer_limits = timer_limits_;
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
void MakeRenewalRequest(RenewalRoundTrip* renewal_messages) {
ASSERT_NO_FATAL_FAILURE(renewal_messages->SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(renewal_messages->CreateDefaultResponse());
}
void LoadRenewal(RenewalRoundTrip* renewal_messages,
OEMCryptoResult expected_result) {
ASSERT_NO_FATAL_FAILURE(renewal_messages->EncryptAndSignResponse());
ASSERT_EQ(expected_result, renewal_messages->LoadResponse());
}
ODK_TimerLimits timer_limits_;
};
// This class is for the refresh tests that should only be run on licenses with
// a core message.
class OEMCryptoRefreshTestAPI16 : public OEMCryptoRefreshTest {};
// Refresh keys should work if the license uses a nonce.
TEST_P(OEMCryptoRefreshTest, RefreshWithNonce) {
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
}
// Refresh keys should work if the license does not use a nonce.
TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) {
license_messages_.set_control(0);
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
}
// Refresh keys should NOT work if a license has not been loaded.
TEST_P(OEMCryptoRefreshTestAPI16, RefreshNoLicense) {
Session s;
s.open();
constexpr size_t message_size = kMaxCoreMessage + 42;
std::vector<uint8_t> data(message_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
size_t gen_signature_length = 0;
size_t core_message_length = 0;
OEMCryptoResult sts = OEMCrypto_PrepAndSignRenewalRequest(
s.session_id(), data.data(), data.size(), &core_message_length, nullptr,
&gen_signature_length);
ASSERT_LT(core_message_length, message_size);
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
vector<uint8_t> gen_signature(gen_signature_length);
sts = OEMCrypto_PrepAndSignRenewalRequest(
s.session_id(), data.data(), data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length);
}
ASSERT_NE(OEMCrypto_SUCCESS, sts);
}
// Refresh keys should fail if the nonce is not in the session.
TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadNonce) {
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
MakeRenewalRequest(&renewal_messages);
renewal_messages.core_request().nonce ^= 42;
LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE);
}
// Refresh keys should fail if the session_id does not match the license.
TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadSessionID) {
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
MakeRenewalRequest(&renewal_messages);
renewal_messages.core_request().session_id += 1;
LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE);
}
// Refresh keys should handle the maximum message size.
TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) {
LoadLicense();
RenewalRoundTrip renewal_messages(&license_messages_);
const size_t max_size = GetResourceValue(kLargeMessageSize);
renewal_messages.set_message_size(max_size);
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
}
// This situation would occur if an app only uses one key in the license. When
// that happens, SelectKey would be called before the first decrypt, and then
// would not need to be called again, even if the license is refreshed.
TEST_P(OEMCryptoRefreshTest, RefreshWithNoSelectKey) {
LoadLicense();
// Call select key before the refresh. No calls below to TestDecryptCTR with
// select key set to true.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true));
// This should still be valid key, even if the refresh failed, because this
// is before the original license duration.
wvutil::TestSleep::Sleep(kShortSleep);
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false));
// This should be after duration of the original license, but before the
// expiration of the refresh message. This should fail until we have loaded
// the renewal.
wvutil::TestSleep::Sleep(kShortSleep + kLongSleep);
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
RenewalRoundTrip renewal_messages(&license_messages_);
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
// After we've loaded the renewal, decrypt should succeed again.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false));
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoRefreshTest,
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
// These tests only work when the license has a core message.
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
// If the license does not allow a hash, then we should not compute one.
TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) {
uint32_t hash_type = OEMCrypto_SupportsDecryptHash();
// If hash is not supported, or is vendor defined, don't try to test it.
if (hash_type != OEMCrypto_CRC_Clear_Buffer) return;
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t frame_number = 1;
uint32_t hash = 42;
// It is OK to set the hash before loading the keys
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_SetDecryptHash(session_.session_id(), frame_number,
reinterpret_cast<const uint8_t*>(&hash),
sizeof(hash)));
// It is OK to select the key and decrypt.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
// But the error code should be bad.
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number));
}
// This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very
// large hash buffer.
TEST_F(OEMCryptoMemoryLicenseTest,
OEMCryptoMemoryDecryptHashForHugeHashBuffer) {
uint32_t session_id = session_.session_id();
auto f = [session_id](size_t hash_length) {
uint32_t frame_number = 1;
vector<uint8_t> hash_buffer(hash_length);
return OEMCrypto_SetDecryptHash(session_id, frame_number,
hash_buffer.data(), hash_buffer.size());
};
TestHugeLengthDoesNotCrashAPI(f, kCheckStatus);
}
// This test verifies OEMCrypto_SetDecryptHash for out of range frame number.
TEST_P(OEMCryptoLicenseTest, DecryptHashForOutOfRangeFrameNumber) {
uint32_t frame_number = kHugeRandomNumber;
uint32_t hash = 42;
ASSERT_NO_FATAL_FAILURE(OEMCrypto_SetDecryptHash(
session_.session_id(), frame_number,
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
}
//
// Decrypt Tests -- these test Decrypt CTR mode only.
//
TEST_P(OEMCryptoLicenseTest, Decrypt) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
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());
}
// Verify that a zero duration means infinite license duration.
TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = 0;
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());
}
struct SubsampleSize {
size_t clear_size;
size_t encrypted_size;
SubsampleSize(size_t clear, size_t encrypted)
: clear_size(clear), encrypted_size(encrypted) {}
};
// Struct for holding the data for one test sample in the decrypt tests.
struct TestSample {
// Encrypted data -- this is input to OEMCrypto, and output from EncryptData.
std::vector<uint8_t> encrypted_buffer;
std::vector<uint8_t> clear_buffer; // OEMCrypto store clear output here.
std::vector<uint8_t> truth_buffer; // Truth data for clear text.
OEMCrypto_SampleDescription description;
std::vector<OEMCrypto_SubSampleDescription> subsamples;
int secure_buffer_fid;
};
// A class of tests that test decryption for a variety of patterns and modes.
// This test is parameterized by three parameters:
// 1. The pattern used for pattern decryption.
// 2. The cipher mode for decryption: either CTR or CBC.
// 3. A boolean that determines if decrypt in place should be done. When the
// output buffer is clear, it should be possible for the input and output
// buffers to be the same.
class OEMCryptoSessionTestsDecryptTests
: public OEMCryptoLicenseTestAPI16,
public WithParamInterface<tuple<OEMCrypto_CENCEncryptPatternDesc,
OEMCryptoCipherMode, OutputType>> {
protected:
void SetUp() override {
OEMCryptoLicenseTestAPI16::SetUp();
pattern_ = ::testing::get<0>(GetParam());
cipher_mode_ = ::testing::get<1>(GetParam());
decrypt_inplace_ = ::testing::get<2>(GetParam()).decrypt_inplace;
output_buffer_type_ = ::testing::get<2>(GetParam()).type;
verify_crc_ = global_features.supports_crc;
// Pick a random key.
EXPECT_EQ(GetRandBytes(key_, sizeof(key_)), 1);
// Pick a random starting iv. Some tests override this before using it.
EXPECT_EQ(GetRandBytes(initial_iv_, sizeof(initial_iv_)), 1);
}
void TearDown() override {
FreeSecureBuffers();
OEMCryptoLicenseTestAPI16::TearDown();
}
void SetSubsampleSizes(std::vector<SubsampleSize> subsample_sizes) {
// This is just sugar for having one sample with the given subsamples in it.
SetSampleSizes({subsample_sizes});
}
void SetSampleSizes(std::vector<std::vector<SubsampleSize>> sample_sizes) {
ASSERT_GT(sample_sizes.size(), 0u);
samples_.clear();
samples_.reserve(sample_sizes.size());
// Convert all the size arrays to TestSample structs
for (const std::vector<SubsampleSize>& subsample_sizes : sample_sizes) {
// This could be one line if we had C++17
samples_.emplace_back();
TestSample& sample = samples_.back();
ASSERT_GT(subsample_sizes.size(), 0u);
sample.subsamples.reserve(subsample_sizes.size());
// Convert all the sizes to subsample descriptions and tally the total
// sample size
size_t sample_size = 0;
size_t current_block_offset = 0;
for (const SubsampleSize& size : subsample_sizes) {
sample.subsamples.push_back(OEMCrypto_SubSampleDescription{
size.clear_size, size.encrypted_size,
0, // Subsample Flags, to be filled in after the loop
current_block_offset});
// Update the rolling variables
sample_size += size.clear_size + size.encrypted_size;
if (cipher_mode_ == OEMCrypto_CipherMode_CENC) {
current_block_offset =
(current_block_offset + size.encrypted_size) % AES_BLOCK_SIZE;
}
}
// Set the subsample flags now that all the subsamples are processed
sample.subsamples.front().subsample_flags |= OEMCrypto_FirstSubsample;
sample.subsamples.back().subsample_flags |= OEMCrypto_LastSubsample;
// Set related information on the sample description
sample.description.subsamples = sample.subsamples.data();
sample.description.subsamples_length = sample.subsamples.size();
sample.description.buffers.input_data_length = sample_size;
}
}
// Set up the input buffer and either a clear or secure output buffer for each
// test sample. This should be called after SetSubsampleSizes().
void MakeBuffers() {
for (TestSample& sample : samples_) {
const size_t total_size = sample.description.buffers.input_data_length;
ASSERT_GT(total_size, 0u);
sample.encrypted_buffer.clear();
sample.truth_buffer.clear();
sample.clear_buffer.clear();
sample.encrypted_buffer.resize(total_size);
sample.truth_buffer.resize(total_size);
for (size_t i = 0; i < total_size; i++) sample.truth_buffer[i] = i % 256;
OEMCrypto_DestBufferDesc& output_descriptor =
sample.description.buffers.output_descriptor;
output_descriptor.type = output_buffer_type_;
switch (output_descriptor.type) {
case OEMCrypto_BufferType_Clear:
if (decrypt_inplace_) {
// Add some padding to verify there is no overrun.
sample.encrypted_buffer.resize(total_size + kBufferOverrunPadding,
0xaa);
output_descriptor.buffer.clear.clear_buffer =
sample.encrypted_buffer.data();
} else {
// Add some padding to verify there is no overrun.
sample.clear_buffer.resize(total_size + kBufferOverrunPadding,
0xaa);
output_descriptor.buffer.clear.clear_buffer =
sample.clear_buffer.data();
}
output_descriptor.buffer.clear.clear_buffer_length = total_size;
break;
case OEMCrypto_BufferType_Secure:
output_descriptor.buffer.secure.secure_buffer_length = total_size;
ASSERT_EQ(OEMCrypto_AllocateSecureBuffer(
session_.session_id(), total_size, &output_descriptor,
&sample.secure_buffer_fid),
OEMCrypto_SUCCESS);
ASSERT_NE(output_descriptor.buffer.secure.secure_buffer, nullptr);
// It is OK if OEMCrypto changes the maximum size, but there must
// still be enough room for our data.
ASSERT_GE(output_descriptor.buffer.secure.secure_buffer_length,
total_size);
output_descriptor.buffer.secure.offset = 0;
break;
case OEMCrypto_BufferType_Direct:
output_descriptor.buffer.direct.is_video = false;
break;
} // switch (output_descriptor.type)
} // sample loop
}
void FreeSecureBuffers() {
for (TestSample& sample : samples_) {
OEMCrypto_DestBufferDesc& output_descriptor =
sample.description.buffers.output_descriptor;
if (output_descriptor.type == OEMCrypto_BufferType_Secure) {
ASSERT_EQ(OEMCrypto_FreeSecureBuffer(session_.session_id(),
&output_descriptor,
sample.secure_buffer_fid),
OEMCrypto_SUCCESS);
}
}
}
void EncryptData() {
AES_KEY aes_key;
AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key);
for (TestSample& sample : samples_) {
uint8_t iv[KEY_IV_SIZE]; // Current IV
memcpy(iv, initial_iv_, KEY_IV_SIZE);
memcpy(sample.description.iv, initial_iv_, KEY_IV_SIZE);
size_t buffer_index = 0; // byte index into in and out.
size_t block_offset = 0; // byte index into current block.
for (const OEMCrypto_SubSampleDescription& subsample :
sample.subsamples) {
// Copy clear content.
if (subsample.num_bytes_clear > 0) {
memcpy(&sample.encrypted_buffer[buffer_index],
&sample.truth_buffer[buffer_index], subsample.num_bytes_clear);
buffer_index += subsample.num_bytes_clear;
}
// The IV resets at the start of each subsample in the 'cbcs' schema.
if (cipher_mode_ == OEMCrypto_CipherMode_CBCS) {
memcpy(iv, initial_iv_, KEY_IV_SIZE);
}
size_t pattern_offset = 0;
const size_t subsample_end =
buffer_index + subsample.num_bytes_encrypted;
while (buffer_index < subsample_end) {
const size_t size =
min(subsample_end - buffer_index, AES_BLOCK_SIZE - block_offset);
const size_t pattern_length = pattern_.encrypt + pattern_.skip;
const bool skip_block =
(pattern_offset >= pattern_.encrypt) && (pattern_length > 0);
if (pattern_length > 0) {
pattern_offset = (pattern_offset + 1) % pattern_length;
}
// CBC mode should just copy a partial block at the end. If there
// is a partial block at the beginning, an error is returned, so we
// can put whatever we want in the output buffer.
if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBCS) &&
(size < AES_BLOCK_SIZE))) {
memcpy(&sample.encrypted_buffer[buffer_index],
&sample.truth_buffer[buffer_index], size);
block_offset = 0; // Next block should be complete.
} else {
if (cipher_mode_ == OEMCrypto_CipherMode_CENC) {
uint8_t aes_output[AES_BLOCK_SIZE];
AES_encrypt(iv, aes_output, &aes_key);
for (size_t n = 0; n < size; n++) {
sample.encrypted_buffer[buffer_index + n] =
aes_output[n + block_offset] ^
sample.truth_buffer[buffer_index + n];
}
if (size + block_offset < AES_BLOCK_SIZE) {
// Partial block. Don't increment iv. Compute next block
// offset.
block_offset = block_offset + size;
} else {
EXPECT_EQ(block_offset + size,
static_cast<size_t>(AES_BLOCK_SIZE));
// Full block. Increment iv, and set offset to 0 for next
// block.
ctr128_inc64(1, iv);
block_offset = 0;
}
} else {
uint8_t aes_input[AES_BLOCK_SIZE];
for (size_t n = 0; n < size; n++) {
aes_input[n] = sample.truth_buffer[buffer_index + n] ^ iv[n];
}
AES_encrypt(aes_input, &sample.encrypted_buffer[buffer_index],
&aes_key);
memcpy(iv, &sample.encrypted_buffer[buffer_index],
AES_BLOCK_SIZE);
// CBC mode should always start on block boundary.
block_offset = 0;
}
}
buffer_index += size;
} // encryption loop
} // per-subsample loop
} // per-sample loop
}
void LoadLicense() {
uint32_t control = wvoec::kControlNonceEnabled;
if (verify_crc_) control |= kControlAllowHashVerification;
if (output_buffer_type_ == OEMCrypto_BufferType_Secure)
control |= kControlObserveDataPath | kControlDataPathSecure;
license_messages_.set_control(control);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_response()
.timer_limits.initial_renewal_duration_seconds = kDuration;
memcpy(license_messages_.response_data().keys[0].key_data, key_,
sizeof(key_));
license_messages_.response_data().keys[0].cipher_mode = cipher_mode_;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_EQ(OEMCrypto_SelectKey(
session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length, cipher_mode_),
OEMCrypto_SUCCESS);
}
void TestDecryptCENC() { ASSERT_EQ(DecryptCENC(), OEMCrypto_SUCCESS); }
void ValidateDecryptedData() {
for (TestSample& sample : samples_) {
if (sample.description.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
const size_t total_size = sample.description.buffers.input_data_length;
// To verify there is no buffer overrun after decrypting, look at the
// padded bytes just after the data buffer that was written. It
// should not have changed from the original 0xaa that we set in
// MakeBuffer function.
if (decrypt_inplace_) {
EXPECT_EQ(std::count(sample.encrypted_buffer.begin() + total_size,
sample.encrypted_buffer.end(), 0xaa),
static_cast<int32_t>(kBufferOverrunPadding))
<< "Buffer overrun.";
sample.encrypted_buffer.resize(total_size); // Remove padding.
// We expect encrypted buffer to have been changed by OEMCrypto.
EXPECT_EQ(sample.encrypted_buffer, sample.truth_buffer);
} else {
EXPECT_EQ(std::count(sample.clear_buffer.begin() + total_size,
sample.clear_buffer.end(), 0xaa),
static_cast<int32_t>(kBufferOverrunPadding))
<< "Buffer overrun.";
sample.clear_buffer.resize(total_size); // Remove padding.
EXPECT_EQ(sample.clear_buffer, sample.truth_buffer);
}
}
}
if (verify_crc_) {
uint32_t frame;
ASSERT_EQ(OEMCrypto_GetHashErrorCode(session_.session_id(), &frame),
OEMCrypto_SUCCESS);
}
}
OEMCryptoResult DecryptCENC() {
// 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_) {
const TestSample& sample = samples_[0];
uint32_t hash =
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
OEMCrypto_SetDecryptHash(session_.session_id(), 1,
reinterpret_cast<const uint8_t*>(&hash),
sizeof(hash));
}
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
// Perform decryption using the test data that was previously set up.
OEMCryptoResult result = DecryptFallbackChain::Decrypt(
session_.session_id(), sample_descriptions.data(),
sample_descriptions.size(), cipher_mode_, &pattern_);
if (result != OEMCrypto_SUCCESS) return result;
ValidateDecryptedData();
return result;
}
// Parameters of test case
OEMCrypto_CENCEncryptPatternDesc pattern_;
OEMCryptoCipherMode cipher_mode_;
bool decrypt_inplace_; // If true, input and output buffers are the same.
OEMCryptoBufferType output_buffer_type_;
bool verify_crc_;
uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key.
uint8_t initial_iv_[KEY_IV_SIZE]; // Starting IV for every sample.
std::vector<TestSample> samples_;
};
TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
// This subsample size is larger than a few encrypt/skip patterns. Most
// test cases use a pattern length of 160, so we'll run through at least two
// full patterns if we have more than 320 -- round up to 400.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 400},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// When the pattern length is 10 blocks, there is a discrepancy between the
// HLS and the CENC standards for samples of size 160*N+16, for N = 1, 2, 3...
// We require the CENC standard for OEMCrypto, and let a layer above us break
// samples into pieces if they wish to use the HLS standard.
TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 160 + 16},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Test that a single block can be decrypted.
TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 16},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the ability to decrypt multiple subsamples with no offset.
// There is no offset within the block, used by CTR mode.
TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{25, 160},
{50, 256},
{25, 160},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests an offset into the block for the second encrypted subsample.
// This should only work for CTR mode, for CBC mode an error is expected in
// the decrypt step.
// If this test fails for CTR mode, then it is probably handling the
// block_offset incorrectly.
TEST_P(OEMCryptoSessionTestsDecryptTests, EvenOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{25, 8},
{25, 32},
{25, 50},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
// CTR Mode is self-inverse -- i.e. We can pick the encrypted data and
// compute the unencrypted data. By picking the encrypted data to be all 0,
// it is easier to re-encrypt the data and debug problems. Similarly, we
// pick an iv = 0.
memset(initial_iv_, 0, KEY_IV_SIZE);
TestSample& sample = samples_[0]; // There is only one sample in this test
sample.truth_buffer.assign(sample.description.buffers.input_data_length, 0);
ASSERT_NO_FATAL_FAILURE(EncryptData());
if (decrypt_inplace_) {
const size_t total_size = sample.description.buffers.input_data_length;
// In case of decrypt_inplace_, encrypted_buffer contains padded bytes
// which is used for buffer overrun validation. Do not copy the padded
// bytes to truth_buffer.
sample.truth_buffer.assign(sample.encrypted_buffer.begin(),
sample.encrypted_buffer.begin() + total_size);
} else {
sample.truth_buffer =
sample.encrypted_buffer; // truth_buffer_ = encrypted zero buffer.
}
// Run EncryptData to re-encrypt this buffer. For CTR mode, we should get
// back to zeros.
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// If the EvenOffset test passes, but this one doesn't, then DecryptCENC might
// be using the wrong definition of block offset. Adding the block offset to
// the block boundary should give you the beginning of the encrypted data.
// This should only work for CTR mode, for CBC mode, the block offset must be
// 0, so an error is expected in the decrypt step.
// Another way to view the block offset is with the formula:
// block_boundary + block_offset = beginning of subsample.
TEST_P(OEMCryptoSessionTestsDecryptTests, OddOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{10, 50},
{10, 75},
{10, 75},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests that the algorithm used to increment the counter for
// AES-CTR mode is correct. There are two possible implementations:
// 1) increment the counter as if it were a 128 bit number,
// 2) increment the low 64 bits as a 64 bit number and leave the high bits
// alone.
// For CENC, the algorithm we should use is the second one. OpenSSL defaults to
// the first. If this test is not passing, you should look at the way you
// increment the counter. Look at the example code in ctr128_inc64 above.
// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you
// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
memcpy(initial_iv_,
wvutil::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE").data(),
KEY_IV_SIZE);
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 256},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the case where an encrypted sample is not an even number of
// blocks. For CTR mode, the partial block is encrypted. For CBC mode the
// partial block should be a copy of the clear data.
TEST_P(OEMCryptoSessionTestsDecryptTests, PartialBlock) {
// Note: for more complete test coverage, we want a sample size that is in
// the encrypted range for some tests, e.g. (3,7), and in the skip range for
// other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 50},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Based on the resource rating, OEMCrypto should be able to handle the maximum
// amount of data that can be passed to it. This is the lesser of:
//
// 1) The maximum total sample size
// 2) The maximum number of subsamples multiplied by the maximum subsample size
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
const size_t max_sample_size = GetResourceValue(kMaxSampleSize);
const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples);
// The +1 on this line ensures that, even in cases where max_sample_size is
// not evenly divisible by max_num_subsamples and thus the division gets
// truncated, (max_num_subsamples * subsample_size) will be greater than
// max_sample_size.
const size_t subsample_size =
std::min(max_sample_size / max_num_subsamples + 1, max_subsample_size);
size_t bytes_remaining = max_sample_size;
std::vector<SubsampleSize> subsample_sizes;
while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) {
const size_t this_subsample_size =
std::min(subsample_size, bytes_remaining);
const size_t clear_size = this_subsample_size / 2;
const size_t encrypted_size = this_subsample_size - clear_size;
subsample_sizes.push_back({clear_size, encrypted_size});
bytes_remaining -= this_subsample_size;
}
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes(subsample_sizes));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryDecryptCENCForHugeNumberOfSubSamples) {
auto oemcrypto_function = [&](size_t number_of_subsamples) {
std::vector<SubsampleSize> subsample_sizes;
while (number_of_subsamples-- > 0) {
subsample_sizes.push_back({1, 1});
}
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
OEMCryptoResult result = DecryptCENC();
// Closing the session and opening it for next iteration.
// If it is last iteration, session will be closed in teardown method of
// class.
session_.close();
session_.open();
InstallTestRSAKey(&session_);
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeNumberOfSubSamples) {
size_t number_of_subsamples = 10000;
std::vector<SubsampleSize> subsample_sizes;
while (number_of_subsamples-- > 0) {
subsample_sizes.push_back({100, 100});
}
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
LOGD("Large number of subsamples test has return code %d", result);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeSubSample) {
std::vector<SubsampleSize> subsample_sizes;
subsample_sizes.push_back({100000, 100000});
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
LOGD("Large subsample test has return code %d", result);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryDecryptCENCForHugeNumberOfSamples) {
auto oemcrypto_function = [&](size_t number_of_samples) {
std::vector<std::vector<SubsampleSize>> samples;
std::vector<SubsampleSize> subsample_sizes;
subsample_sizes.push_back({1, 1});
while (number_of_samples-- > 0) {
samples.push_back(subsample_sizes);
}
SetSampleSizes(samples);
LoadLicense();
MakeBuffers();
EncryptData();
OEMCryptoResult result = DecryptCENC();
// Closing the session and opening it for next iteration.
// If it is last iteration, session will be closed in teardown method of
// class.
session_.close();
session_.open();
InstallTestRSAKey(&session_);
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
}
// Based on the resource rating, OEMCrypto should be able to handle the maximum
// subsample size.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
const size_t max = GetResourceValue(kMaxSubsampleSize);
const size_t half_max = max / 2;
// This test assumes that the maximum sample size is always more than three
// times the maximum subsample size.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{max, 0},
{0, max},
{half_max, max - half_max},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// There are probably no frames this small, but we should handle them anyway.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{5, 5},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Test the case where there is only a clear subsample and no encrypted
// subsample.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{256, 0},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) {
// Do not try to compute the CRC because we have not loaded a license.
verify_crc_ = false;
// Single clear subsample
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{400, 0},
}));
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
// Clear data should be copied even if there is no key selected, and no
// license loaded.
// ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the ability to decrypt multiple samples at once.
TEST_P(OEMCryptoSessionTestsDecryptTests, MultipleSamples) {
ASSERT_NO_FATAL_FAILURE(SetSampleSizes({
{
{52, 160},
{25, 256},
{25, 320},
},
{
{300, 64},
{50, 160},
{2, 160},
{24, 160},
{128, 256},
},
{
{70, 320},
{160, 160},
},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests that calling OEMCrypto_Idle and OEMCrypto_Wake once or multiple
// times doesn't break anything.
TEST_P(OEMCryptoSessionTestsDecryptTests, IdleAndWake) {
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
}
// This tests that after an idle and a wake, decryption can continue in an
// open session.
TEST_P(OEMCryptoSessionTestsDecryptTests, ContinueDecryptionAfterIdleAndWake) {
// This subsample size is larger than a few encrypt/skip patterns. Most
// test cases use a pattern length of 160, so we'll run through at least two
// full patterns if we have more than 320 -- round up to 400.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 400},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
// Set state to idle then wake again and try to reencrypt/decrypt
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Used to construct a specific pattern.
constexpr OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt,
size_t skip) {
return {encrypt, skip};
}
INSTANTIATE_TEST_SUITE_P(
CTRTests, OEMCryptoSessionTestsDecryptTests,
Combine(Values(MakePattern(0, 0)), Values(OEMCrypto_CipherMode_CENC),
::testing::ValuesIn(global_features.GetOutputTypes())));
// Decrypt in place for CBC tests was only required in v13.
INSTANTIATE_TEST_SUITE_P(
CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests,
Combine(
Values(MakePattern(3, 7), MakePattern(9, 1),
// HLS edge cases. We should follow the CENC spec, not HLS spec.
MakePattern(1, 9), MakePattern(1, 0),
// AV1 patterns not already covered above.
MakePattern(5, 5), MakePattern(10, 0)),
Values(OEMCrypto_CipherMode_CBCS),
::testing::ValuesIn(global_features.GetOutputTypes())));
// A request to decrypt data to a clear buffer when the key control block
// requires a secure data path.
TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(wvoec::kControlObserveDataPath |
wvoec::kControlDataPathSecure);
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_UNKNOWN_FAILURE));
}
// Test that key duration is honored.
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
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_SUCCESS));
wvutil::TestSleep::Sleep(kShortSleep); // Should still be valid key.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false, OEMCrypto_SUCCESS));
wvutil::TestSleep::Sleep(kLongSleep); // Should be expired key.
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(0));
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
//
// Certificate Root of Trust Tests
//
class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {
protected:
void TestPrepareProvisioningRequestForHugeBufferLengths(
const std::function<void(size_t, ProvisioningRoundTrip*)> f,
bool check_status) {
auto oemcrypto_function = [&](size_t message_length) {
Session s;
s.open();
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
s.LoadOEMCert(true);
} else {
s.GenerateDerivedKeysFromKeybox(keybox_);
}
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
f(message_length, &provisioning_messages);
return provisioning_messages
.SignAndCreateRequestWithCustomBufferLengths();
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
void TestLoadProvisioningForHugeBufferLengths(
const std::function<void(size_t, ProvisioningRoundTrip*)> f,
bool check_status, bool update_core_message_substring_values) {
auto oemcrypto_function = [&](size_t message_length) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
provisioning_messages.SignAndVerifyRequest();
provisioning_messages.CreateDefaultResponse();
if (update_core_message_substring_values) {
// Make provisioning message big enough so that updated core message
// substring offset and length values from tests are still able to read
// valid data from provisioning_message buffer rather than some garbage
// data.
provisioning_messages.set_message_size(
sizeof(provisioning_messages.response_data()) + message_length);
}
f(message_length, &provisioning_messages);
provisioning_messages
.EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength();
OEMCryptoResult result = provisioning_messages.LoadResponse();
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status);
}
void TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
const std::function<void(size_t, ProvisioningRoundTrip*)> f) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
provisioning_messages.SignAndVerifyRequest();
provisioning_messages.CreateDefaultResponse();
size_t message_length = sizeof(provisioning_messages.response_data());
f(message_length, &provisioning_messages);
provisioning_messages
.EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength();
OEMCryptoResult result = provisioning_messages.LoadResponse();
s.close();
// Verifying error is not due to signature failure which can be caused due
// to test code.
ASSERT_NE(OEMCrypto_ERROR_SIGNATURE_FAILURE, result);
ASSERT_NE(OEMCrypto_SUCCESS, result);
}
};
// This test verifies that we can create a wrapped RSA key, and then reload it.
TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) {
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
}
TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
s.LoadOEMCert(true);
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox);
s.GenerateDerivedKeysFromKeybox(keybox_);
}
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
}
// This tests a large message size. The size is larger than we required in v15.
TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
s.LoadOEMCert(true);
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox);
s.GenerateDerivedKeysFromKeybox(keybox_);
}
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
const size_t max_size = GetResourceValue(kLargeMessageSize);
provisioning_messages.set_message_size(max_size);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
}
// This creates a wrapped RSA key, and then does the sanity check that the
// unencrypted key is not found in the wrapped key. The wrapped key should be
// encrypted.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
// We should not be able to find the rsa key in the wrapped key. It should
// be encrypted.
EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(),
provisioning_messages.encoded_rsa_key()));
}
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
// Encrypt and sign once, so that we can use the size of the response.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
provisioning_messages.core_response().enc_private_key.offset =
provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
// Encrypt and sign once, so that we can use the size of the response.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
provisioning_messages.core_response().enc_private_key_iv.offset =
provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
// Encrypt and sign once, so that we can use the size of the response.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
// If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected.
provisioning_messages.core_response().enc_private_key.offset =
provisioning_messages.encrypted_response_buffer().size() - 5;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
// Encrypt and sign once, so that we can use the size of the response.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
// If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected.
provisioning_messages.core_response().enc_private_key_iv.offset =
provisioning_messages.encrypted_response_buffer().size() - 5;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
// Encrypt and sign once, so that we can use the size of the response.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
// If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected.
provisioning_messages.core_response().encrypted_message_key.offset =
provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Test that RewrapDeviceRSAKey verifies the message signature.
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
TEST_F(OEMCryptoLoadsCertificate,
CertificateProvisionBadSignatureKeyboxTestAPI16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
provisioning_messages.response_signature()[4] ^= 42; // bad signature.
ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Test that RewrapDeviceRSAKey verifies the nonce is current.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
provisioning_messages.core_request().nonce ^= 42; // bad nonce.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE,
provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Test that RewrapDeviceRSAKey verifies the RSA key is valid.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
provisioning_messages.response_data().rsa_key[4] ^= 42; // bad key.
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Test that RewrapDeviceRSAKey verifies the RSA key is valid.
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
TEST_F(OEMCryptoLoadsCertificate,
CertificateProvisionBadRSAKeyKeyboxTestAPI16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
size_t rsa_offset =
provisioning_messages.core_response().enc_private_key.offset;
// Offsets are relative to the message body, after the core message.
rsa_offset += provisioning_messages.serialized_core_message().size();
rsa_offset += 4; // Change the middle of the key.
provisioning_messages.encrypted_response_buffer()[rsa_offset] ^= 42;
ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed();
}
// Test that RewrapDeviceRSAKey accepts the maximum message size.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
const size_t max_size = GetResourceValue(kLargeMessageSize);
provisioning_messages.set_message_size(max_size);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
// We should not be able to find the rsa key in the wrapped key. It should
// be encrypted.
EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(),
provisioning_messages.encoded_rsa_key()));
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeResponseLength) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t message_size, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->set_message_size(message_size);
},
!kCheckStatus, !kUpdateCoreMessageSubstringValues);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageLength) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t message_size, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->set_core_message_size(message_size);
},
!kCheckStatus, !kUpdateCoreMessageSubstringValues);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyLength) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t length, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().enc_private_key.length = length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyOffset) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t offset, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().enc_private_key.offset = offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyLength) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& enc_private_key =
provisioning_messages->core_response().enc_private_key;
enc_private_key.length =
response_message_length - enc_private_key.offset + 1;
});
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyOffset) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& enc_private_key =
provisioning_messages->core_response().enc_private_key;
enc_private_key.offset =
response_message_length - enc_private_key.length + 1;
});
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvLength) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t length, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().enc_private_key_iv.length =
length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvOffset) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t offset, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().enc_private_key_iv.offset =
offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvLength) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& enc_private_key_iv =
provisioning_messages->core_response().enc_private_key_iv;
enc_private_key_iv.length =
response_message_length - enc_private_key_iv.offset + 1;
});
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvOffset) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& enc_private_key_iv =
provisioning_messages->core_response().enc_private_key_iv;
enc_private_key_iv.offset =
response_message_length - enc_private_key_iv.length + 1;
});
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyLength) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t length, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().encrypted_message_key.length =
length;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyOffset) {
TestLoadProvisioningForHugeBufferLengths(
[](size_t offset, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->core_response().encrypted_message_key.offset =
offset;
},
!kCheckStatus, kUpdateCoreMessageSubstringValues);
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyLength) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& encrypted_message_key =
provisioning_messages->core_response().encrypted_message_key;
encrypted_message_key.length =
response_message_length - encrypted_message_key.offset + 1;
});
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyOffset) {
TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths(
[](size_t response_message_length,
ProvisioningRoundTrip* provisioning_messages) {
auto& encrypted_message_key =
provisioning_messages->core_response().encrypted_message_key;
encrypted_message_key.offset =
response_message_length - encrypted_message_key.length + 1;
});
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeSignatureLength) {
auto oemcrypto_function = [&](size_t signature_size) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
provisioning_messages.SignAndVerifyRequest();
provisioning_messages.CreateDefaultResponse();
provisioning_messages.EncryptAndSignResponse();
vector<uint8_t> signature(signature_size);
size_t wrapped_private_key_length = 0;
// Find wrapped_private_key_length.
OEMCrypto_LoadProvisioning(
s.session_id(),
provisioning_messages.encrypted_response_buffer().data(),
provisioning_messages.encrypted_response_buffer().size(),
provisioning_messages.serialized_core_message().size(),
signature.data(), signature_size, nullptr, &wrapped_private_key_length);
std::vector<uint8_t> wrapped_rsa_key(wrapped_private_key_length);
OEMCryptoResult result = OEMCrypto_LoadProvisioning(
s.session_id(),
provisioning_messages.encrypted_response_buffer().data(),
provisioning_messages.encrypted_response_buffer().size(),
provisioning_messages.serialized_core_message().size(),
signature.data(), signature_size, wrapped_rsa_key.data(),
&wrapped_private_key_length);
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadProvisioningForHugeWrappedRsaKeyLength) {
auto oemcrypto_function = [&](size_t buffer_length) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
provisioning_messages.SignAndVerifyRequest();
provisioning_messages.CreateDefaultResponse();
provisioning_messages.EncryptAndSignResponse();
size_t wrapped_private_key_length = buffer_length;
vector<uint8_t> wrapped_private_key(wrapped_private_key_length);
OEMCryptoResult result = OEMCrypto_LoadProvisioning(
s.session_id(),
provisioning_messages.encrypted_response_buffer().data(),
provisioning_messages.encrypted_response_buffer().size(),
provisioning_messages.serialized_core_message().size(),
provisioning_messages.response_signature().data(),
provisioning_messages.response_signature().size(),
wrapped_private_key.data(), &wrapped_private_key_length);
s.close();
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// Test that a wrapped RSA key can be loaded.
TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
OEMCryptoResult sts;
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLength) {
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) {
Session s;
s.open();
vector<uint8_t> wrapped_rsa_key_buffer(wrapped_rsa_key_length);
memcpy(wrapped_rsa_key_buffer.data(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
OEMCryptoResult result = OEMCrypto_LoadDRMPrivateKey(
s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_buffer.data(), wrapped_rsa_key_buffer.size());
s.close();
return result;
};
// It is hard to generate varying length valid wrapped rsa key with valid
// signature. Hence we just call function with random data and do not check
// status to test API with varying length wrapped rsa key.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, wrapped_rsa_key_.size(),
kHugeInputBufferLength, !kCheckStatus);
}
TEST_F(
OEMCryptoLoadsCertificate,
OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLengthStartingFromLength1) {
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) {
Session s;
s.open();
vector<uint8_t> wrapped_rsa_key_buffer(wrapped_rsa_key_length);
OEMCryptoResult result = OEMCrypto_LoadDRMPrivateKey(
s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_buffer.data(), wrapped_rsa_key_buffer.size());
s.close();
return result;
};
// It is hard to generate varying length valid wrapped rsa key with valid
// signature. Hence we just call function with random data and do not check
// status to test API with varying length wrapped rsa key.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate {
public:
void TestKey(const uint8_t* key, size_t key_length) {
encoded_rsa_key_.assign(key, key + key_length);
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages(&s);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
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(s.TestDecryptCTR());
}
};
// Test a 3072 bit RSA key certificate.
TEST_F(OEMCryptoLoadsCertVariousKeys, TestLargeRSAKey3072) {
TestKey(kTestRSAPKCS8PrivateKeyInfo3_3072,
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
}
// Test an RSA key certificate which has a private key generated using the
// Carmichael totient.
TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelRSAKey) {
TestKey(kTestKeyRSACarmichael_2048, sizeof(kTestKeyRSACarmichael_2048));
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroNormalDer) {
TestKey(kCarmichaelNonZeroNormalDer, kCarmichaelNonZeroNormalDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroShortDer) {
TestKey(kCarmichaelNonZeroShortDer, kCarmichaelNonZeroShortDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroNormalDer) {
TestKey(kCarmichaelZeroNormalDer, kCarmichaelZeroNormalDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroShortDer) {
TestKey(kCarmichaelZeroShortDer, kCarmichaelZeroShortDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroNormalDer) {
TestKey(kDualNonZeroNormalDer, kDualNonZeroNormalDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroShortDer) {
TestKey(kDualNonZeroShortDer, kDualNonZeroShortDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroNormalDer) {
TestKey(kDualZeroNormalDer, kDualZeroNormalDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroShortDer) {
TestKey(kDualZeroShortDer, kDualZeroShortDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerNonZeroNormalDer) {
TestKey(kEulerNonZeroNormalDer, kEulerNonZeroNormalDerLen);
}
TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) {
TestKey(kEulerZeroNormalDer, kEulerZeroNormalDerLen);
}
// This tests that two sessions can use different RSA keys simultaneously.
TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
Session s1; // Session s1 loads the default rsa key, but doesn't use it
// until after s2 uses its key.
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(
s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDRMPrivateKey(
s1.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
Session s2; // Session s2 uses a different rsa key.
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048,
kTestRSAPKCS8PrivateKeyInfo4_2048 +
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(
s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages2(&s2);
ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages2.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages2.LoadResponse());
ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR());
s2.close();
// After s2 has loaded its rsa key, we continue using s1's key.
LicenseRoundTrip license_messages1(&s1);
ASSERT_NO_FATAL_FAILURE(license_messages1.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages1.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages1.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages1.LoadResponse());
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
}
// This tests the maximum number of DRM private keys that OEMCrypto can load
TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) {
const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys);
std::vector<std::unique_ptr<Session>> sessions;
std::vector<std::unique_ptr<LicenseRoundTrip>> licenses;
// It should be able to load up to kMaxTotalDRMPrivateKeys keys
for (size_t i = 0; i < max_total_keys; i++) {
sessions.push_back(std::unique_ptr<Session>(new Session()));
licenses.push_back(std::unique_ptr<LicenseRoundTrip>(
new LicenseRoundTrip(sessions[i].get())));
const size_t key_index = i % kTestRSAPKCS8PrivateKeys_2048.size();
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeys_2048[key_index].begin(),
kTestRSAPKCS8PrivateKeys_2048[key_index].end());
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
ASSERT_NO_FATAL_FAILURE(sessions[i]->open());
ASSERT_NO_FATAL_FAILURE(sessions[i]->PreparePublicKey(
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
sessions[i]->InstallRSASessionTestKey(wrapped_rsa_key_));
}
// Attempts to load one more key than the kMaxTotalDRMPrivateKeys
Session s;
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo2_2048,
kTestRSAPKCS8PrivateKeyInfo2_2048 +
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048));
Session ps;
ProvisioningRoundTrip provisioning_messages(&ps, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
OEMCryptoResult result = provisioning_messages.LoadResponse();
// Key loading is allowed to fail due to resource restriction
if (result != OEMCrypto_SUCCESS) {
ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES ||
result == OEMCrypto_ERROR_TOO_MANY_KEYS);
}
// Verifies that the DRM keys which are already loaded should still function
for (size_t i = 0; i < licenses.size(); i++) {
ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse());
ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR());
}
}
// Devices that load certificates, should at least support RSA 2048 keys.
TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) {
ASSERT_NE(0u,
OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates())
<< "Supported certificates is only " << OEMCrypto_SupportedCertificates();
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryPrepareProvisioningRequestForHugeRequestMessageLength) {
TestPrepareProvisioningRequestForHugeBufferLengths(
[](size_t message_size, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->set_message_size(message_size);
},
kCheckStatus);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryPrepareProvisioningRequestForHugeSignatureLength) {
TestPrepareProvisioningRequestForHugeBufferLengths(
[](size_t message_size, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->set_request_signature_size(message_size);
},
!kCheckStatus);
}
TEST_F(OEMCryptoLoadsCertificate,
OEMCryptoMemoryPrepareProvisioningRequestForHugeCoreMessageLength) {
TestPrepareProvisioningRequestForHugeBufferLengths(
[](size_t message_size, ProvisioningRoundTrip* provisioning_messages) {
provisioning_messages->set_core_message_size(message_size);
},
kCheckStatus);
}
// These tests are run by all L1 devices that load and use certificates. It is
// also run by a few L3 devices that use a baked in certificate, but cannot load
// a certificate.
class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate {
protected:
void SetUp() override {
OEMCryptoLoadsCertificate::SetUp();
ASSERT_NO_FATAL_FAILURE(session_.open());
if (global_features.derive_key_method ==
DeviceFeatures::LOAD_TEST_RSA_KEY) {
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
} else {
InstallTestRSAKey(&session_);
}
}
void TearDown() override {
ASSERT_NO_FATAL_FAILURE(session_.close());
OEMCryptoLoadsCertificate::TearDown();
}
Session session_;
};
// This test is not run by default, because it takes a long time and
// is used to measure RSA performance, not test functionality.
TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
const std::chrono::milliseconds kTestDuration(5000);
OEMCryptoResult sts;
std::chrono::steady_clock clock;
wvutil::TestSleep::Sleep(kShortSleep); // Make sure we are not nonce limited.
auto start_time = clock.now();
int count = 15;
for (int i = 0; i < count; i++) { // Only 20 nonce available.
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
}
auto delta_time = clock.now() - start_time;
const double provision_time =
delta_time / std::chrono::milliseconds(1) / count;
Session session;
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
start_time = clock.now();
count = 0;
while (clock.now() - start_time < kTestDuration) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
const size_t size = 50;
vector<uint8_t> licenseRequest(size);
GetRandBytes(licenseRequest.data(), licenseRequest.size());
size_t signature_length = 0;
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
licenseRequest.size(), nullptr,
&signature_length, kSign_RSASSA_PSS);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length);
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_generate_rsa_signature_fuzz_seed_corpus");
OEMCrypto_Generate_RSA_Signature_Fuzz fuzzed_structure;
fuzzed_structure.padding_scheme = kSign_RSASSA_PSS;
fuzzed_structure.signature_length = signature_length;
// Cipher mode and algorithm.
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name,
reinterpret_cast<const char*>(licenseRequest.data()),
licenseRequest.size());
}
std::vector<uint8_t> signature(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, kSign_RSASSA_PSS);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
count++;
}
delta_time = clock.now() - start_time;
const double license_request_time =
delta_time / std::chrono::milliseconds(1) / count;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDRMPrivateKey(
s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
enc_session_key = wvutil::a2b_hex(
"7789c619aa3b9fa3c0a53f57a4abc6"
"02157c8aa57e3c6fb450b0bea22667fb"
"0c3200f9d9d618e397837c720dc2dadf"
"486f33590744b2a4e54ca134ae7dbf74"
"434c2fcf6b525f3e132262f05ea3b3c1"
"198595c0e52b573335b2e8a3debd0d0d"
"d0306f8fcdde4e76476be71342957251"
"e1688c9ca6c1c34ed056d3b989394160"
"cf6937e5ce4d39cc73d11a2e93da21a2"
"fa019d246c852fe960095b32f120c3c2"
"7085f7b64aac344a68d607c0768676ce"
"d4c5b2d057f7601921b453a451e1dea0"
"843ebfef628d9af2784d68e86b730476"
"e136dfe19989de4be30a4e7878efcde5"
"ad2b1254f80c0c5dd3cf111b56572217"
"b9f58fc1dacbf74b59d354a1e62cfa0e"
"bf");
start_time = clock.now();
while (clock.now() - start_time < kTestDuration) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
s.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(),
mac_context.size(), enc_context.data(), enc_context.size()));
count++;
}
delta_time = clock.now() - start_time;
const double derive_keys_time =
delta_time / std::chrono::milliseconds(1) / count;
OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel();
printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n");
printf("PERF:stat, %u, %8.3f, %8.3f, %8.3f\n",
static_cast<unsigned int>(level), provision_time, license_request_time,
derive_keys_time);
}
// Test DeriveKeysFromSessionKey using the maximum size for the HMAC context.
TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
const size_t max_size = GetResourceValue(kLargeMessageSize);
vector<uint8_t> mac_context(max_size);
vector<uint8_t> enc_context(max_size);
// Stripe the data so the two vectors are not identical, and not all zeroes.
for (size_t i = 0; i < max_size; i++) {
mac_context[i] = i % 0x100;
enc_context[i] = (3 * i) % 0x100;
}
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
session_.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
}
TEST_F(OEMCryptoUsesCertificate,
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
session_.FillDefaultContext(&mac_context, &enc_context);
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
&enc_session_key](size_t buffer_length) {
mac_context.resize(buffer_length);
return OEMCrypto_DeriveKeysFromSessionKey(
session_id, enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoUsesCertificate,
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncContext) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
session_.FillDefaultContext(&mac_context, &enc_context);
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
&enc_session_key](size_t buffer_length) {
enc_context.resize(buffer_length);
return OEMCrypto_DeriveKeysFromSessionKey(
session_id, enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoUsesCertificate,
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncSessionKey) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
session_.FillDefaultContext(&mac_context, &enc_context);
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &session_key, &enc_context,
&mac_context,
&enc_session_key](size_t buffer_length) {
session_key.resize(buffer_length);
return OEMCrypto_DeriveKeysFromSessionKey(
session_id, enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
// This test attempts to use alternate algorithms for loaded device certs.
class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
protected:
void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Sign a Message
vector<uint8_t> licenseRequest(size);
GetRandBytes(licenseRequest.data(), licenseRequest.size());
size_t signature_length = 256;
vector<uint8_t> signature(signature_length);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
// Allow OEMCrypto to request a full buffer.
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
ASSERT_NE(static_cast<size_t>(0), signature_length);
signature.assign(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
}
EXPECT_NE(OEMCrypto_SUCCESS, sts)
<< "Signed with forbidden padding scheme=" << (int)scheme
<< ", size=" << (int)size;
const vector<uint8_t> zero(signature.size(), 0);
ASSERT_EQ(zero, signature); // signature should not be computed.
}
void TestSignature(RSA_Padding_Scheme scheme, size_t size) {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> licenseRequest(size);
GetRandBytes(licenseRequest.data(), licenseRequest.size());
size_t signature_length = 0;
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
licenseRequest.size(), nullptr,
&signature_length, scheme);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length);
std::vector<uint8_t> signature(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << size;
signature.resize(signature_length);
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
licenseRequest, signature.data(), signature_length, scheme));
}
void DisallowDeriveKeys() {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
s.GenerateNonce();
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
s.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(),
mac_context.size(), enc_context.data(), enc_context.size()));
}
// If force is true, we assert that the key loads successfully.
void LoadWithAllowedSchemes(uint32_t schemes, bool force) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.set_allowed_schemes(schemes);
provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
OEMCryptoResult sts = provisioning_messages.LoadResponse();
key_loaded_ = (OEMCrypto_SUCCESS == sts);
if (key_loaded_) {
encoded_rsa_key_ = provisioning_messages.encoded_rsa_key();
wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key();
EXPECT_GT(wrapped_rsa_key_.size(), 0u);
EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_));
}
if (force) {
EXPECT_EQ(OEMCrypto_SUCCESS, sts);
}
}
bool key_loaded_ = false;
};
TEST_F(OEMCryptoLoadsCertificateAlternates,
OEMCryptoMemoryGenerateRSASignatureForHugeBuffer) {
OEMCryptoResult sts;
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
// If the device is a cast receiver, then this scheme is required.
if (global_features.cast_receiver) {
ASSERT_TRUE(key_loaded_);
}
if (key_loaded_) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> message_buffer(10);
size_t signature_length = 0;
sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(),
message_buffer.size(), nullptr,
&signature_length, kSign_PKCS1_Block1);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length);
vector<uint8_t> signature(signature_length);
auto oemcrypto_function = [&](size_t buffer_length) {
message_buffer.resize(buffer_length);
return OEMCrypto_GenerateRSASignature(
s.session_id(), message_buffer.data(), message_buffer.size(),
signature.data(), &signature_length, kSign_PKCS1_Block1);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
s.close();
}
}
TEST_F(OEMCryptoLoadsCertificateAlternates,
OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) {
OEMCryptoResult sts;
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
// If the device is a cast receiver, then this scheme is required.
if (global_features.cast_receiver) {
ASSERT_TRUE(key_loaded_);
}
if (key_loaded_) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> message_buffer(50);
vector<uint8_t> signature;
auto oemcrypto_function = [&](size_t signature_length) {
signature.resize(signature_length);
return OEMCrypto_GenerateRSASignature(
s.session_id(), message_buffer.data(), message_buffer.size(),
signature.data(), &signature_length, kSign_PKCS1_Block1);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
s.close();
}
}
// The alternate padding is only required for cast receivers, but all devices
// should forbid the alternate padding for regular certificates.
TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) {
LoadWithAllowedSchemes(kSign_RSASSA_PSS, true); // Use default padding scheme
DisallowForbiddenPadding(kSign_PKCS1_Block1, 50);
}
// The alternate padding is only required for cast receivers, but if a device
// does load an alternate certificate, it should NOT use it for generating
// a license request signature.
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) {
// Try to load an RSA key with alternative padding schemes. This signing
// scheme is used by cast receivers.
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
// If the device is a cast receiver, then this scheme is required.
if (global_features.cast_receiver) {
ASSERT_TRUE(key_loaded_);
}
// If the key loaded with no error, then we will verify that it is not used
// for forbidden padding schemes.
if (key_loaded_) {
// The other padding scheme should fail.
DisallowForbiddenPadding(kSign_RSASSA_PSS, 83);
DisallowDeriveKeys();
if (global_features.cast_receiver) {
// A signature with a valid size should succeed.
TestSignature(kSign_PKCS1_Block1, 83);
TestSignature(kSign_PKCS1_Block1, 50);
}
// A signature with padding that is too big should fail.
DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big.
}
}
// This test verifies RSA signing with the alternate padding scheme used by
// Android cast receivers, PKCS1 Block 1. These tests are not required for
// other devices, and should be filtered out by DeviceFeatures::Initialize for
// those devices.
class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
protected:
vector<uint8_t> encode(uint8_t type, const vector<uint8_t>& substring) {
vector<uint8_t> result;
result.push_back(type);
if (substring.size() < 0x80) {
uint8_t length = substring.size();
result.push_back(length);
} else if (substring.size() < 0x100) {
result.push_back(0x81);
uint8_t length = substring.size();
result.push_back(length);
} else {
result.push_back(0x82);
uint16_t length = substring.size();
result.push_back(length >> 8);
result.push_back(length & 0xFF);
}
result.insert(result.end(), substring.begin(), substring.end());
return result;
}
vector<uint8_t> concat(const vector<uint8_t>& a, const vector<uint8_t>& b) {
vector<uint8_t> result = a;
result.insert(result.end(), b.begin(), b.end());
return result;
}
// This encodes the RSA key used in the PKCS#1 signing tests below.
void BuildRSAKey() {
vector<uint8_t> field_n =
encode(0x02, wvutil::a2b_hex("00"
"df271fd25f8644496b0c81be4bd50297"
"ef099b002a6fd67727eb449cea566ed6"
"a3981a71312a141cabc9815c1209e320"
"a25b32464e9999f18ca13a9fd3892558"
"f9e0adefdd3650dd23a3f036d60fe398"
"843706a40b0b8462c8bee3bce12f1f28"
"60c2444cdc6a44476a75ff4aa24273cc"
"be3bf80248465f8ff8c3a7f3367dfc0d"
"f5b6509a4f82811cedd81cdaaa73c491"
"da412170d544d4ba96b97f0afc806549"
"8d3a49fd910992a1f0725be24f465cfe"
"7e0eabf678996c50bc5e7524abf73f15"
"e5bef7d518394e3138ce4944506aaaaf"
"3f9b236dcab8fc00f87af596fdc3d9d6"
"c75cd508362fae2cbeddcc4c7450b17b"
"776c079ecca1f256351a43b97dbe2153"));
vector<uint8_t> field_e = encode(0x02, wvutil::a2b_hex("010001"));
vector<uint8_t> field_d =
encode(0x02, wvutil::a2b_hex("5bd910257830dce17520b03441a51a8c"
"ab94020ac6ecc252c808f3743c95b7c8"
"3b8c8af1a5014346ebc4242cdfb5d718"
"e30a733e71f291e4d473b61bfba6daca"
"ed0a77bd1f0950ae3c91a8f901118825"
"89e1d62765ee671e7baeea309f64d447"
"bbcfa9ea12dce05e9ea8939bc5fe6108"
"581279c982b308794b3448e7f7b95229"
"2df88c80cb40142c4b5cf5f8ddaa0891"
"678d610e582fcb880f0d707caf47d09a"
"84e14ca65841e5a3abc5e9dba94075a9"
"084341f0edad9b68e3b8e082b80b6e6e"
"8a0547b44fb5061b6a9131603a5537dd"
"abd01d8e863d8922e9aa3e4bfaea0b39"
"d79283ad2cbc8a59cce7a6ecf4e4c81e"
"d4c6591c807defd71ab06866bb5e7745"));
vector<uint8_t> field_p =
encode(0x02, wvutil::a2b_hex("00"
"f44f5e4246391f482b2f5296e3602eb3"
"4aa136427710f7c0416d403fd69d4b29"
"130cfebef34e885abdb1a8a0a5f0e9b5"
"c33e1fc3bfc285b1ae17e40cc67a1913"
"dd563719815ebaf8514c2a7aa0018e63"
"b6c631dc315a46235716423d11ff5803"
"4e610645703606919f5c7ce2660cd148"
"bd9efc123d9c54b6705590d006cfcf3f"));
vector<uint8_t> field_q =
encode(0x02, wvutil::a2b_hex("00"
"e9d49841e0e0a6ad0d517857133e36dc"
"72c1bdd90f9174b52e26570f373640f1"
"c185e7ea8e2ed7f1e4ebb951f70a5802"
"3633b0097aec67c6dcb800fc1a67f9bb"
"0563610f08ebc8746ad129772136eb1d"
"daf46436450d318332a84982fe5d28db"
"e5b3e912407c3e0e03100d87d436ee40"
"9eec1cf85e80aba079b2e6106b97bced"));
vector<uint8_t> field_exp1 =
encode(0x02, wvutil::a2b_hex("00"
"ed102acdb26871534d1c414ecad9a4d7"
"32fe95b10eea370da62f05de2c393b1a"
"633303ea741b6b3269c97f704b352702"
"c9ae79922f7be8d10db67f026a8145de"
"41b30c0a42bf923bac5f7504c248604b"
"9faa57ed6b3246c6ba158e36c644f8b9"
"548fcf4f07e054a56f768674054440bc"
"0dcbbc9b528f64a01706e05b0b91106f"));
vector<uint8_t> field_exp2 =
encode(0x02, wvutil::a2b_hex("6827924a85e88b55ba00f8219128bd37"
"24c6b7d1dfe5629ef197925fecaff5ed"
"b9cdf3a7befd8ea2e8dd3707138b3ff8"
"7c3c39c57f439e562e2aa805a39d7cd7"
"9966d2ece7845f1dbc16bee99999e4d0"
"bf9eeca45fcda8a8500035fe6b5f03bc"
"2f6d1bfc4d4d0a3723961af0cdce4a01"
"eec82d7f5458ec19e71b90eeef7dff61"));
vector<uint8_t> field_invq =
encode(0x02, wvutil::a2b_hex("57b73888d183a99a6307422277551a3d"
"9e18adf06a91e8b55ceffef9077c8496"
"948ecb3b16b78155cb2a3a57c119d379"
"951c010aa635edcf62d84c5a122a8d67"
"ab5fa9e5a4a8772a1e943bafc70ae3a4"
"c1f0f3a4ddffaefd1892c8cb33bb0d0b"
"9590e963a69110fb34db7b906fc4ba28"
"36995aac7e527490ac952a02268a4f18"));
// Header of rsa key is constant.
encoded_rsa_key_ = wvutil::a2b_hex(
// 0x02 0x01 0x00 == integer, size 1 byte, value = 0 (field=version)
"020100"
// 0x30, sequence, size = d = 13 (field=pkeyalg) AlgorithmIdentifier
"300d"
// 0x06 = object identifier. length = 9
// (this should be 1.2.840.113549.1.1.1) (field=algorithm)
"0609"
"2a" // 1*0x40 + 2 = 42 = 0x2a.
"8648" // 840 = 0x348, 0x03 *2 + 0x80 + (0x48>>15) = 0x86.
// 0x48 -> 0x48
"86f70d" // 113549 = 0x1668d -> (110 , 1110111, 0001101)
// -> (0x80+0x06, 0x80+0x77, 0x0d)
"01" // 1
"01" // 1
"01" // 1
"05" // null object. (field=parameter?)
"00" // size of null object
);
vector<uint8_t> pkey = wvutil::a2b_hex("020100"); // integer, version = 0.
pkey = concat(pkey, field_n);
pkey = concat(pkey, field_e);
pkey = concat(pkey, field_d);
pkey = concat(pkey, field_p);
pkey = concat(pkey, field_q);
pkey = concat(pkey, field_exp1);
pkey = concat(pkey, field_exp2);
pkey = concat(pkey, field_invq);
pkey = encode(0x30, pkey);
pkey = encode(0x04, pkey);
encoded_rsa_key_ = concat(encoded_rsa_key_, pkey);
encoded_rsa_key_ = encode(0x30, encoded_rsa_key_); // 0x30=sequence
}
// This is used to test a signature from the file pkcs1v15sign-vectors.txt.
void TestSignature(RSA_Padding_Scheme scheme, const vector<uint8_t>& message,
const vector<uint8_t>& correct_signature) {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// The application will compute the SHA-1 Hash of the message, so this
// test must do that also.
uint8_t hash[SHA_DIGEST_LENGTH];
if (!SHA1(message.data(), message.size(), hash)) {
dump_boringssl_error();
FAIL() << "boringssl error creating SHA1 hash.";
}
// The application will prepend the digest info to the hash.
// SHA-1 digest info prefix = 0x30 0x21 0x30 ...
vector<uint8_t> digest = wvutil::a2b_hex("3021300906052b0e03021a05000414");
digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH);
// OEMCrypto will apply the padding, and encrypt to generate the signature.
size_t signature_length = 0;
sts = OEMCrypto_GenerateRSASignature(s.session_id(), digest.data(),
digest.size(), nullptr,
&signature_length, scheme);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length);
vector<uint8_t> signature(signature_length);
sts = OEMCrypto_GenerateRSASignature(s.session_id(), digest.data(),
digest.size(), signature.data(),
&signature_length, scheme);
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << message.size();
signature.resize(signature_length);
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
// Verify that the signature matches the official test vector.
ASSERT_EQ(correct_signature.size(), signature_length);
signature.resize(signature_length);
ASSERT_EQ(correct_signature, signature);
// Also verify that our verification algorithm agrees. This is not needed
// to test OEMCrypto, but it does verify that this test is valid.
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(),
signature_length, scheme));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
digest, correct_signature.data(), correct_signature.size(), scheme));
}
};
// CAST Receivers should report that they support cast certificates.
TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) {
ASSERT_NE(0u,
OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates());
}
// # PKCS#1 v1.5 Signature Example 15.1
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"f45d55f35551e975d6a8dc7ea9f48859"
"3940cc75694a278f27e578a163d839b3"
"4040841808cf9c58c9b8728bf5f9ce8e"
"e811ea91714f47bab92d0f6d5a26fcfe"
"ea6cd93b910c0a2c963e64eb1823f102"
"753d41f0335910ad3a977104f1aaf6c3"
"742716a9755d11b8eed690477f445c5d"
"27208b2e284330fa3d301423fa7f2d08"
"6e0ad0b892b9db544e456d3f0dab85d9"
"53c12d340aa873eda727c8a649db7fa6"
"3740e25e9af1533b307e61329993110e"
"95194e039399c3824d24c51f22b26bde"
"1024cd395958a2dfeb4816a6e8adedb5"
"0b1f6b56d0b3060ff0f1c4cb0d0e001d"
"d59d73be12");
vector<uint8_t> signature = wvutil::a2b_hex(
"b75a5466b65d0f300ef53833f2175c8a"
"347a3804fc63451dc902f0b71f908345"
"9ed37a5179a3b723a53f1051642d7737"
"4c4c6c8dbb1ca20525f5c9f32db77695"
"3556da31290e22197482ceb69906c46a"
"758fb0e7409ba801077d2a0a20eae7d1"
"d6d392ab4957e86b76f0652d68b83988"
"a78f26e11172ea609bf849fbbd78ad7e"
"dce21de662a081368c040607cee29db0"
"627227f44963ad171d2293b633a392e3"
"31dca54fe3082752f43f63c161b447a4"
"c65a6875670d5f6600fcc860a1caeb0a"
"88f8fdec4e564398a5c46c87f68ce070"
"01f6213abe0ab5625f87d19025f08d81"
"dac7bd4586bc9382191f6d2880f6227e"
"5df3eed21e7792d249480487f3655261");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.2
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"c14b4c6075b2f9aad661def4ecfd3cb9"
"33c623f4e63bf53410d2f016d1ab98e2"
"729eccf8006cd8e08050737d95fdbf29"
"6b66f5b9792a902936c4f7ac69f51453"
"ce4369452dc22d96f037748114662000"
"dd9cd3a5e179f4e0f81fa6a0311ca1ae"
"e6519a0f63cec78d27bb726393fb7f1f"
"88cde7c97f8a66cd66301281dac3f3a4"
"33248c75d6c2dcd708b6a97b0a3f325e"
"0b2964f8a5819e479b");
vector<uint8_t> signature = wvutil::a2b_hex(
"afa7343462bea122cc149fca70abdae7"
"9446677db5373666af7dc313015f4de7"
"86e6e394946fad3cc0e2b02bedba5047"
"fe9e2d7d099705e4a39f28683279cf0a"
"c85c1530412242c0e918953be000e939"
"cf3bf182525e199370fa7907eba69d5d"
"b4631017c0e36df70379b5db8d4c695a"
"979a8e6173224065d7dc15132ef28cd8"
"22795163063b54c651141be86d36e367"
"35bc61f31fca574e5309f3a3bbdf91ef"
"f12b99e9cc1744f1ee9a1bd22c5bad96"
"ad481929251f0343fd36bcf0acde7f11"
"e5ad60977721202796fe061f9ada1fc4"
"c8e00d6022a8357585ffe9fdd59331a2"
"8c4aa3121588fb6cf68396d8ac054659"
"9500c9708500a5972bd54f72cf8db0c8");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.3
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"d02371ad7ee48bbfdb2763de7a843b94"
"08ce5eb5abf847ca3d735986df84e906"
"0bdbcdd3a55ba55dde20d4761e1a21d2"
"25c1a186f4ac4b3019d3adf78fe63346"
"67f56f70c901a0a2700c6f0d56add719"
"592dc88f6d2306c7009f6e7a635b4cb3"
"a502dfe68ddc58d03be10a1170004fe7"
"4dd3e46b82591ff75414f0c4a03e605e"
"20524f2416f12eca589f111b75d639c6"
"1baa80cafd05cf3500244a219ed9ced9"
"f0b10297182b653b526f400f2953ba21"
"4d5bcd47884132872ae90d4d6b1f4215"
"39f9f34662a56dc0e7b4b923b6231e30"
"d2676797817f7c337b5ac824ba93143b"
"3381fa3dce0e6aebd38e67735187b1eb"
"d95c02");
vector<uint8_t> signature = wvutil::a2b_hex(
"3bac63f86e3b70271203106b9c79aabd"
"9f477c56e4ee58a4fce5baf2cab4960f"
"88391c9c23698be75c99aedf9e1abf17"
"05be1dac33140adb48eb31f450bb9efe"
"83b7b90db7f1576d33f40c1cba4b8d6b"
"1d3323564b0f1774114fa7c08e6d1e20"
"dd8fbba9b6ac7ad41e26b4568f4a8aac"
"bfd178a8f8d2c9d5f5b88112935a8bc9"
"ae32cda40b8d20375510735096536818"
"ce2b2db71a9772c9b0dda09ae10152fa"
"11466218d091b53d92543061b7294a55"
"be82ff35d5c32fa233f05aaac7585030"
"7ecf81383c111674397b1a1b9d3bf761"
"2ccbe5bacd2b38f0a98397b24c83658f"
"b6c0b4140ef11970c4630d44344e76ea"
"ed74dcbee811dbf6575941f08a6523b8");
TestSignature(kSign_PKCS1_Block1, message, signature);
};
// # PKCS#1 v1.5 Signature Example 15.4
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"29035584ab7e0226a9ec4b02e8dcf127"
"2dc9a41d73e2820007b0f6e21feccd5b"
"d9dbb9ef88cd6758769ee1f956da7ad1"
"8441de6fab8386dbc693");
vector<uint8_t> signature = wvutil::a2b_hex(
"28d8e3fcd5dddb21ffbd8df1630d7377"
"aa2651e14cad1c0e43ccc52f907f946d"
"66de7254e27a6c190eb022ee89ecf622"
"4b097b71068cd60728a1aed64b80e545"
"7bd3106dd91706c937c9795f2b36367f"
"f153dc2519a8db9bdf2c807430c451de"
"17bbcd0ce782b3e8f1024d90624dea7f"
"1eedc7420b7e7caa6577cef43141a726"
"4206580e44a167df5e41eea0e69a8054"
"54c40eefc13f48e423d7a32d02ed42c0"
"ab03d0a7cf70c5860ac92e03ee005b60"
"ff3503424b98cc894568c7c56a023355"
"1cebe588cf8b0167b7df13adcad82867"
"6810499c704da7ae23414d69e3c0d2db"
"5dcbc2613bc120421f9e3653c5a87672"
"97643c7e0740de016355453d6c95ae72");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.5
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex("bda3a1c79059eae598308d3df609");
vector<uint8_t> signature = wvutil::a2b_hex(
"a156176cb96777c7fb96105dbd913bc4"
"f74054f6807c6008a1a956ea92c1f81c"
"b897dc4b92ef9f4e40668dc7c556901a"
"cb6cf269fe615b0fb72b30a513386923"
"14b0e5878a88c2c7774bd16939b5abd8"
"2b4429d67bd7ac8e5ea7fe924e20a6ec"
"662291f2548d734f6634868b039aa5f9"
"d4d906b2d0cb8585bf428547afc91c6e"
"2052ddcd001c3ef8c8eefc3b6b2a82b6"
"f9c88c56f2e2c3cb0be4b80da95eba37"
"1d8b5f60f92538743ddbb5da2972c71f"
"e7b9f1b790268a0e770fc5eb4d5dd852"
"47d48ae2ec3f26255a3985520206a1f2"
"68e483e9dbb1d5cab190917606de31e7"
"c5182d8f151bf41dfeccaed7cde690b2"
"1647106b490c729d54a8fe2802a6d126");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.6
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"c187915e4e87da81c08ed4356a0cceac"
"1c4fb5c046b45281b387ec28f1abfd56"
"7e546b236b37d01ae71d3b2834365d3d"
"f380b75061b736b0130b070be58ae8a4"
"6d12166361b613dbc47dfaeb4ca74645"
"6c2e888385525cca9dd1c3c7a9ada76d"
"6c");
vector<uint8_t> signature = wvutil::a2b_hex(
"9cab74163608669f7555a333cf196fe3"
"a0e9e5eb1a32d34bb5c85ff689aaab0e"
"3e65668ed3b1153f94eb3d8be379b8ee"
"f007c4a02c7071ce30d8bb341e58c620"
"f73d37b4ecbf48be294f6c9e0ecb5e63"
"fec41f120e5553dfa0ebebbb72640a95"
"37badcb451330229d9f710f62e3ed8ec"
"784e50ee1d9262b42671340011d7d098"
"c6f2557b2131fa9bd0254636597e88ec"
"b35a240ef0fd85957124df8080fee1e1"
"49af939989e86b26c85a5881fae8673d"
"9fd40800dd134eb9bdb6410f420b0aa9"
"7b20efcf2eb0c807faeb83a3ccd9b51d"
"4553e41dfc0df6ca80a1e81dc234bb83"
"89dd195a38b42de4edc49d346478b9f1"
"1f0557205f5b0bd7ffe9c850f396d7c4");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.7
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"abfa2ecb7d29bd5bcb9931ce2bad2f74"
"383e95683cee11022f08e8e7d0b8fa05"
"8bf9eb7eb5f98868b5bb1fb5c31ceda3"
"a64f1a12cdf20fcd0e5a246d7a1773d8"
"dba0e3b277545babe58f2b96e3f4edc1"
"8eabf5cd2a560fca75fe96e07d859def"
"b2564f3a34f16f11e91b3a717b41af53"
"f6605323001aa406c6");
vector<uint8_t> signature = wvutil::a2b_hex(
"c4b437bcf703f352e1faf74eb9622039"
"426b5672caf2a7b381c6c4f0191e7e4a"
"98f0eebcd6f41784c2537ff0f99e7498"
"2c87201bfbc65eae832db71d16dacadb"
"0977e5c504679e40be0f9db06ffd848d"
"d2e5c38a7ec021e7f68c47dfd38cc354"
"493d5339b4595a5bf31e3f8f13816807"
"373df6ad0dc7e731e51ad19eb4754b13"
"4485842fe709d378444d8e36b1724a4f"
"da21cafee653ab80747f7952ee804dea"
"b1039d84139945bbf4be82008753f3c5"
"4c7821a1d241f42179c794ef7042bbf9"
"955656222e45c34369a384697b6ae742"
"e18fa5ca7abad27d9fe71052e3310d0f"
"52c8d12ea33bf053a300f4afc4f098df"
"4e6d886779d64594d369158fdbc1f694");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.8
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"df4044a89a83e9fcbf1262540ae3038b"
"bc90f2b2628bf2a4467ac67722d8546b"
"3a71cb0ea41669d5b4d61859c1b4e47c"
"ecc5933f757ec86db0644e311812d00f"
"b802f03400639c0e364dae5aebc5791b"
"c655762361bc43c53d3c7886768f7968"
"c1c544c6f79f7be820c7e2bd2f9d73e6"
"2ded6d2e937e6a6daef90ee37a1a52a5"
"4f00e31addd64894cf4c02e16099e29f"
"9eb7f1a7bb7f84c47a2b594813be02a1"
"7b7fc43b34c22c91925264126c89f86b"
"b4d87f3ef131296c53a308e0331dac8b"
"af3b63422266ecef2b90781535dbda41"
"cbd0cf22a8cbfb532ec68fc6afb2ac06");
vector<uint8_t> signature = wvutil::a2b_hex(
"1414b38567ae6d973ede4a06842dcc0e"
"0559b19e65a4889bdbabd0fd02806829"
"13bacd5dc2f01b30bb19eb810b7d9ded"
"32b284f147bbe771c930c6052aa73413"
"90a849f81da9cd11e5eccf246dbae95f"
"a95828e9ae0ca3550325326deef9f495"
"30ba441bed4ac29c029c9a2736b1a419"
"0b85084ad150426b46d7f85bd702f48d"
"ac5f71330bc423a766c65cc1dcab20d3"
"d3bba72b63b3ef8244d42f157cb7e3a8"
"ba5c05272c64cc1ad21a13493c3911f6"
"0b4e9f4ecc9900eb056ee59d6fe4b8ff"
"6e8048ccc0f38f2836fd3dfe91bf4a38"
"6e1ecc2c32839f0ca4d1b27a568fa940"
"dd64ad16bd0125d0348e383085f08894"
"861ca18987227d37b42b584a8357cb04");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.9
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"ea941ff06f86c226927fcf0e3b11b087"
"2676170c1bfc33bda8e265c77771f9d0"
"850164a5eecbcc5ce827fbfa07c85214"
"796d8127e8caa81894ea61ceb1449e72"
"fea0a4c943b2da6d9b105fe053b9039a"
"9cc53d420b7539fab2239c6b51d17e69"
"4c957d4b0f0984461879a0759c4401be"
"ecd4c606a0afbd7a076f50a2dfc2807f"
"24f1919baa7746d3a64e268ed3f5f8e6"
"da83a2a5c9152f837cb07812bd5ba7d3"
"a07985de88113c1796e9b466ec299c5a"
"c1059e27f09415");
vector<uint8_t> signature = wvutil::a2b_hex(
"ceeb84ccb4e9099265650721eea0e8ec"
"89ca25bd354d4f64564967be9d4b08b3"
"f1c018539c9d371cf8961f2291fbe0dc"
"2f2f95fea47b639f1e12f4bc381cef0c"
"2b7a7b95c3adf27605b7f63998c3cbad"
"542808c3822e064d4ad14093679e6e01"
"418a6d5c059684cd56e34ed65ab605b8"
"de4fcfa640474a54a8251bbb7326a42d"
"08585cfcfc956769b15b6d7fdf7da84f"
"81976eaa41d692380ff10eaecfe0a579"
"682909b5521fade854d797b8a0345b9a"
"864e0588f6caddbf65f177998e180d1f"
"102443e6dca53a94823caa9c3b35f322"
"583c703af67476159ec7ec93d1769b30"
"0af0e7157dc298c6cd2dee2262f8cddc"
"10f11e01741471bbfd6518a175734575");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.10
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"d8b81645c13cd7ecf5d00ed2c91b9acd"
"46c15568e5303c4a9775ede76b48403d"
"6be56c05b6b1cf77c6e75de096c5cb35"
"51cb6fa964f3c879cf589d28e1da2f9d"
"ec");
vector<uint8_t> signature = wvutil::a2b_hex(
"2745074ca97175d992e2b44791c323c5"
"7167165cdd8da579cdef4686b9bb404b"
"d36a56504eb1fd770f60bfa188a7b24b"
"0c91e881c24e35b04dc4dd4ce38566bc"
"c9ce54f49a175fc9d0b22522d9579047"
"f9ed42eca83f764a10163997947e7d2b"
"52ff08980e7e7c2257937b23f3d279d4"
"cd17d6f495546373d983d536efd7d1b6"
"7181ca2cb50ac616c5c7abfbb9260b91"
"b1a38e47242001ff452f8de10ca6eaea"
"dcaf9edc28956f28a711291fc9a80878"
"b8ba4cfe25b8281cb80bc9cd6d2bd182"
"5246eebe252d9957ef93707352084e6d"
"36d423551bf266a85340fb4a6af37088"
"0aab07153d01f48d086df0bfbec05e7b"
"443b97e71718970e2f4bf62023e95b67");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.11
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"e5739b6c14c92d510d95b826933337ff"
"0d24ef721ac4ef64c2bad264be8b44ef"
"a1516e08a27eb6b611d3301df0062dae"
"fc73a8c0d92e2c521facbc7b26473876"
"7ea6fc97d588a0baf6ce50adf79e600b"
"d29e345fcb1dba71ac5c0289023fe4a8"
"2b46a5407719197d2e958e3531fd54ae"
"f903aabb4355f88318994ed3c3dd62f4"
"20a7");
vector<uint8_t> signature = wvutil::a2b_hex(
"be40a5fb94f113e1b3eff6b6a33986f2"
"02e363f07483b792e68dfa5554df0466"
"cc32150950783b4d968b639a04fd2fb9"
"7f6eb967021f5adccb9fca95acc8f2cd"
"885a380b0a4e82bc760764dbab88c1e6"
"c0255caa94f232199d6f597cc9145b00"
"e3d4ba346b559a8833ad1516ad5163f0"
"16af6a59831c82ea13c8224d84d0765a"
"9d12384da460a8531b4c407e04f4f350"
"709eb9f08f5b220ffb45abf6b75d1579"
"fd3f1eb55fc75b00af8ba3b087827fe9"
"ae9fb4f6c5fa63031fe582852fe2834f"
"9c89bff53e2552216bc7c1d4a3d5dc2b"
"a6955cd9b17d1363e7fee8ed7629753f"
"f3125edd48521ae3b9b03217f4496d0d"
"8ede57acbc5bd4deae74a56f86671de2");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.12
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"7af42835917a88d6b3c6716ba2f5b0d5"
"b20bd4e2e6e574e06af1eef7c81131be"
"22bf8128b9cbc6ec00275ba80294a5d1"
"172d0824a79e8fdd830183e4c00b9678"
"2867b1227fea249aad32ffc5fe007bc5"
"1f21792f728deda8b5708aa99cabab20"
"a4aa783ed86f0f27b5d563f42e07158c"
"ea72d097aa6887ec411dd012912a5e03"
"2bbfa678507144bcc95f39b58be7bfd1"
"759adb9a91fa1d6d8226a8343a8b849d"
"ae76f7b98224d59e28f781f13ece605f"
"84f6c90bae5f8cf378816f4020a7dda1"
"bed90c92a23634d203fac3fcd86d68d3"
"182a7d9ccabe7b0795f5c655e9acc4e3"
"ec185140d10cef053464ab175c83bd83"
"935e3dabaf3462eebe63d15f573d269a");
vector<uint8_t> signature = wvutil::a2b_hex(
"4e78c5902b807914d12fa537ae6871c8"
"6db8021e55d1adb8eb0ccf1b8f36ab7d"
"ad1f682e947a627072f03e627371781d"
"33221d174abe460dbd88560c22f69011"
"6e2fbbe6e964363a3e5283bb5d946ef1"
"c0047eba038c756c40be7923055809b0"
"e9f34a03a58815ebdde767931f018f6f"
"1878f2ef4f47dd374051dd48685ded6e"
"fb3ea8021f44be1d7d149398f98ea9c0"
"8d62888ebb56192d17747b6b8e170954"
"31f125a8a8e9962aa31c285264e08fb2"
"1aac336ce6c38aa375e42bc92ab0ab91"
"038431e1f92c39d2af5ded7e43bc151e"
"6ebea4c3e2583af3437e82c43c5e3b5b"
"07cf0359683d2298e35948ed806c063c"
"606ea178150b1efc15856934c7255cfe");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.13
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"ebaef3f9f23bdfe5fa6b8af4c208c189"
"f2251bf32f5f137b9de4406378686b3f"
"0721f62d24cb8688d6fc41a27cbae21d"
"30e429feacc7111941c277");
vector<uint8_t> signature = wvutil::a2b_hex(
"c48dbef507114f03c95fafbeb4df1bfa"
"88e0184a33cc4f8a9a1035ff7f822a5e"
"38cda18723915ff078244429e0f6081c"
"14fd83331fa65c6ba7bb9a12dbf66223"
"74cd0ca57de3774e2bd7ae823677d061"
"d53ae9c4040d2da7ef7014f3bbdc95a3"
"61a43855c8ce9b97ecabce174d926285"
"142b534a3087f9f4ef74511ec742b0d5"
"685603faf403b5072b985df46adf2d25"
"29a02d40711e2190917052371b79b749"
"b83abf0ae29486c3f2f62477b2bd362b"
"039c013c0c5076ef520dbb405f42cee9"
"5425c373a975e1cdd032c49622c85079"
"b09e88dab2b13969ef7a723973781040"
"459f57d5013638483de2d91cb3c490da"
"81c46de6cd76ea8a0c8f6fe331712d24");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.14
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"c5a2711278761dfcdd4f0c99e6f5619d"
"6c48b5d4c1a80982faa6b4cf1cf7a60f"
"f327abef93c801429efde08640858146"
"1056acc33f3d04f5ada21216cacd5fd1"
"f9ed83203e0e2fe6138e3eae8424e591"
"5a083f3f7ab76052c8be55ae882d6ec1"
"482b1e45c5dae9f41015405327022ec3"
"2f0ea2429763b255043b1958ee3cf6d6"
"3983596eb385844f8528cc9a9865835d"
"c5113c02b80d0fca68aa25e72bcaaeb3"
"cf9d79d84f984fd417");
vector<uint8_t> signature = wvutil::a2b_hex(
"6bd5257aa06611fb4660087cb4bc4a9e"
"449159d31652bd980844daf3b1c7b353"
"f8e56142f7ea9857433b18573b4deede"
"818a93b0290297783f1a2f23cbc72797"
"a672537f01f62484cd4162c3214b9ac6"
"28224c5de01f32bb9b76b27354f2b151"
"d0e8c4213e4615ad0bc71f515e300d6a"
"64c6743411fffde8e5ff190e54923043"
"126ecfc4c4539022668fb675f25c07e2"
"0099ee315b98d6afec4b1a9a93dc3349"
"6a15bd6fde1663a7d49b9f1e639d3866"
"4b37a010b1f35e658682d9cd63e57de0"
"f15e8bdd096558f07ec0caa218a8c06f"
"4788453940287c9d34b6d40a3f09bf77"
"99fe98ae4eb49f3ff41c5040a50cefc9"
"bdf2394b749cf164480df1ab6880273b");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.15
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"9bf8aa253b872ea77a7e23476be26b23"
"29578cf6ac9ea2805b357f6fc3ad130d"
"baeb3d869a13cce7a808bbbbc969857e"
"03945c7bb61df1b5c2589b8e046c2a5d"
"7e4057b1a74f24c711216364288529ec"
"9570f25197213be1f5c2e596f8bf8b2c"
"f3cb38aa56ffe5e31df7395820e94ecf"
"3b1189a965dcf9a9cb4298d3c88b2923"
"c19fc6bc34aacecad4e0931a7c4e5d73"
"dc86dfa798a8476d82463eefaa90a8a9"
"192ab08b23088dd58e1280f7d72e4548"
"396baac112252dd5c5346adb2004a2f7"
"101ccc899cc7fafae8bbe295738896a5"
"b2012285014ef6");
vector<uint8_t> signature = wvutil::a2b_hex(
"27f7f4da9bd610106ef57d32383a448a"
"8a6245c83dc1309c6d770d357ba89e73"
"f2ad0832062eb0fe0ac915575bcd6b8b"
"cadb4e2ba6fa9da73a59175152b2d4fe"
"72b070c9b7379e50000e55e6c269f665"
"8c937972797d3add69f130e34b85bdec"
"9f3a9b392202d6f3e430d09caca82277"
"59ab825f7012d2ff4b5b62c8504dbad8"
"55c05edd5cab5a4cccdc67f01dd6517c"
"7d41c43e2a4957aff19db6f18b17859a"
"f0bc84ab67146ec1a4a60a17d7e05f8b"
"4f9ced6ad10908d8d78f7fc88b76adc8"
"290f87daf2a7be10ae408521395d54ed"
"2556fb7661854a730ce3d82c71a8d493"
"ec49a378ac8a3c74439f7cc555ba13f8"
"59070890ee18ff658fa4d741969d70a5");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.16
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"32474830e2203754c8bf0681dc4f842a"
"fe360930378616c108e833656e5640c8"
"6856885bb05d1eb9438efede679263de"
"07cb39553f6a25e006b0a52311a063ca"
"088266d2564ff6490c46b5609818548f"
"88764dad34a25e3a85d575023f0b9e66"
"5048a03c350579a9d32446c7bb96cc92"
"e065ab94d3c8952e8df68ef0d9fa456b"
"3a06bb80e3bbc4b28e6a94b6d0ff7696"
"a64efe05e735fea025d7bdbc4139f3a3"
"b546075cba7efa947374d3f0ac80a68d"
"765f5df6210bca069a2d88647af7ea04"
"2dac690cb57378ec0777614fb8b65ff4"
"53ca6b7dce6098451a2f8c0da9bfecf1"
"fdf391bbaa4e2a91ca18a1121a7523a2"
"abd42514f489e8");
vector<uint8_t> signature = wvutil::a2b_hex(
"6917437257c22ccb5403290c3dee82d9"
"cf7550b31bd31c51bd57bfd35d452ab4"
"db7c4be6b2e25ac9a59a1d2a7feb627f"
"0afd4976b3003cc9cffd8896505ec382"
"f265104d4cf8c932fa9fe86e00870795"
"9912389da4b2d6b369b36a5e72e29d24"
"c9a98c9d31a3ab44e643e6941266a47a"
"45e3446ce8776abe241a8f5fc6423b24"
"b1ff250dc2c3a8172353561077e850a7"
"69b25f0325dac88965a3b9b472c494e9"
"5f719b4eac332caa7a65c7dfe46d9aa7"
"e6e00f525f303dd63ab7919218901868"
"f9337f8cd26aafe6f33b7fb2c98810af"
"19f7fcb282ba1577912c1d368975fd5d"
"440b86e10c199715fa0b6f4250b53373"
"2d0befe1545150fc47b876de09b00a94");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.17
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"008e59505eafb550aae5e845584cebb0"
"0b6de1733e9f95d42c882a5bbeb5ce1c"
"57e119e7c0d4daca9f1ff7870217f7cf"
"d8a6b373977cac9cab8e71e420");
vector<uint8_t> signature = wvutil::a2b_hex(
"922503b673ee5f3e691e1ca85e9ff417"
"3cf72b05ac2c131da5603593e3bc259c"
"94c1f7d3a06a5b9891bf113fa39e59ff"
"7c1ed6465e908049cb89e4e125cd37d2"
"ffd9227a41b4a0a19c0a44fbbf3de55b"
"ab802087a3bb8d4ff668ee6bbb8ad89e"
"6857a79a9c72781990dfcf92cd519404"
"c950f13d1143c3184f1d250c90e17ac6"
"ce36163b9895627ad6ffec1422441f55"
"e4499dba9be89546ae8bc63cca01dd08"
"463ae7f1fce3d893996938778c1812e6"
"74ad9c309c5acca3fde44e7dd8695993"
"e9c1fa87acda99ece5c8499e468957ad"
"66359bf12a51adbe78d3a213b449bf0b"
"5f8d4d496acf03d3033b7ccd196bc22f"
"68fb7bef4f697c5ea2b35062f48a36dd");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.18
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"6abc54cf8d1dff1f53b17d8160368878"
"a8788cc6d22fa5c2258c88e660b09a89"
"33f9f2c0504ddadc21f6e75e0b833beb"
"555229dee656b9047b92f62e76b8ffcc"
"60dab06b80");
vector<uint8_t> signature = wvutil::a2b_hex(
"0b6daf42f7a862147e417493c2c401ef"
"ae32636ab4cbd44192bbf5f195b50ae0"
"96a475a1614f0a9fa8f7a026cb46c650"
"6e518e33d83e56477a875aca8c7e714c"
"e1bdbd61ef5d535239b33f2bfdd61771"
"bab62776d78171a1423cea8731f82e60"
"766d6454265620b15f5c5a584f55f95b"
"802fe78c574ed5dacfc831f3cf2b0502"
"c0b298f25ccf11f973b31f85e4744219"
"85f3cff702df3946ef0a6605682111b2"
"f55b1f8ab0d2ea3a683c69985ead93ed"
"449ea48f0358ddf70802cb41de2fd83f"
"3c808082d84936948e0c84a131b49278"
"27460527bb5cd24bfab7b48e071b2417"
"1930f99763272f9797bcb76f1d248157"
"5558fcf260b1f0e554ebb3df3cfcb958");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.19
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"af2d78152cf10efe01d274f217b177f6"
"b01b5e749f1567715da324859cd3dd88"
"db848ec79f48dbba7b6f1d33111ef31b"
"64899e7391c2bffd69f49025cf201fc5"
"85dbd1542c1c778a2ce7a7ee108a309f"
"eca26d133a5ffedc4e869dcd7656596a"
"c8427ea3ef6e3fd78fe99d8ddc71d839"
"f6786e0da6e786bd62b3a4f19b891a56"
"157a554ec2a2b39e25a1d7c7d37321c7"
"a1d946cf4fbe758d9276f08563449d67"
"414a2c030f4251cfe2213d04a5410637"
"87");
vector<uint8_t> signature = wvutil::a2b_hex(
"209c61157857387b71e24bf3dd564145"
"50503bec180ff53bdd9bac062a2d4995"
"09bf991281b79527df9136615b7a6d9d"
"b3a103b535e0202a2caca197a7b74e53"
"56f3dd595b49acfd9d30049a98ca88f6"
"25bca1d5f22a392d8a749efb6eed9b78"
"21d3110ac0d244199ecb4aa3d735a83a"
"2e8893c6bf8581383ccaee834635b7fa"
"1faffa45b13d15c1da33af71e89303d6"
"8090ff62ee615fdf5a84d120711da53c"
"2889198ab38317a9734ab27d67924cea"
"74156ff99bef9876bb5c339e93745283"
"e1b34e072226b88045e017e9f05b2a8c"
"416740258e223b2690027491732273f3"
"229d9ef2b1b3807e321018920ad3e53d"
"ae47e6d9395c184b93a374c671faa2ce");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// # PKCS#1 v1.5 Signature Example 15.20
TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) {
BuildRSAKey();
LoadWithAllowedSchemes(kSign_PKCS1_Block1, true);
vector<uint8_t> message = wvutil::a2b_hex(
"40ee992458d6f61486d25676a96dd2cb"
"93a37f04b178482f2b186cf88215270d"
"ba29d786d774b0c5e78c7f6e56a956e7"
"f73950a2b0c0c10a08dbcd67e5b210bb"
"21c58e2767d44f7dd4014e3966143bf7"
"e3d66ff0c09be4c55f93b39994b8518d"
"9c1d76d5b47374dea08f157d57d70634"
"978f3856e0e5b481afbbdb5a3ac48d48"
"4be92c93de229178354c2de526e9c65a"
"31ede1ef68cb6398d7911684fec0babc"
"3a781a66660783506974d0e14825101c"
"3bfaea");
vector<uint8_t> signature = wvutil::a2b_hex(
"927502b824afc42513ca6570de338b8a"
"64c3a85eb828d3193624f27e8b1029c5"
"5c119c9733b18f5849b3500918bcc005"
"51d9a8fdf53a97749fa8dc480d6fe974"
"2a5871f973926528972a1af49e3925b0"
"adf14a842719b4a5a2d89fa9c0b6605d"
"212bed1e6723b93406ad30e86829a5c7"
"19b890b389306dc5506486ee2f36a8df"
"e0a96af678c9cbd6aff397ca200e3edc"
"1e36bd2f08b31d540c0cb282a9559e4a"
"dd4fc9e6492eed0ccbd3a6982e5faa2d"
"dd17be47417c80b4e5452d31f72401a0"
"42325109544d954c01939079d409a5c3"
"78d7512dfc2d2a71efcc3432a765d1c6"
"a52cfce899cd79b15b4fc3723641ef6b"
"d00acc10407e5df58dd1c3c5c559a506");
TestSignature(kSign_PKCS1_Block1, message, signature);
}
// This class is for testing the generic crypto functionality.
class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
protected:
// buffer_size_ must be a multiple of encryption block size, 16. We'll use a
// reasonable number of blocks for most of the tests.
OEMCryptoGenericCryptoTest() : buffer_size_(160) {}
void SetUp() override {
OEMCryptoRefreshTest::SetUp();
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(
license_messages_.CreateResponseWithGenericCryptoKeys());
InitializeClearBuffer();
}
void InitializeClearBuffer() {
clear_buffer_.assign(buffer_size_, 0);
for (size_t i = 0; i < clear_buffer_.size(); i++) {
clear_buffer_[i] = 1 + i % 250;
}
for (size_t i = 0; i < wvoec::KEY_IV_SIZE; i++) {
iv_[i] = i;
}
}
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());
}
// Encrypt the buffer with the specified key made in
// CreateResponseWithGenericCryptoKeys.
void EncryptBuffer(unsigned int key_index, const vector<uint8_t>& in_buffer,
vector<uint8_t>* out_buffer) {
EncryptBufferWithKey(session_.license().keys[key_index].key_data, in_buffer,
out_buffer);
}
// Encrypt the buffer with the specified key.
void EncryptBufferWithKey(const uint8_t* key_data,
const vector<uint8_t>& in_buffer,
vector<uint8_t>* out_buffer) {
AES_KEY aes_key;
ASSERT_EQ(0, AES_set_encrypt_key(key_data, AES_BLOCK_SIZE * 8, &aes_key));
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, iv_, wvoec::KEY_IV_SIZE);
out_buffer->resize(in_buffer.size());
ASSERT_GT(in_buffer.size(), 0u);
ASSERT_EQ(0u, in_buffer.size() % AES_BLOCK_SIZE);
AES_cbc_encrypt(in_buffer.data(), out_buffer->data(), in_buffer.size(),
&aes_key, iv_buffer, AES_ENCRYPT);
}
// Sign the buffer with the specified key made in
// CreateResponseWithGenericCryptoKeys.
void SignBuffer(unsigned int key_index, const vector<uint8_t>& in_buffer,
vector<uint8_t>* signature) {
SignBufferWithKey(session_.license().keys[key_index].key_data, in_buffer,
signature);
}
// Sign the buffer with the specified key.
void SignBufferWithKey(const uint8_t* key_data,
const vector<uint8_t>& in_buffer,
vector<uint8_t>* signature) {
unsigned int md_len = SHA256_DIGEST_LENGTH;
signature->resize(SHA256_DIGEST_LENGTH);
HMAC(EVP_sha256(), key_data, wvoec::MAC_KEY_SIZE, in_buffer.data(),
in_buffer.size(), signature->data(), &md_len);
}
OEMCryptoResult GenericEncrypt(OEMCrypto_SESSION session,
const uint8_t* clear_buffer,
size_t clear_buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_generic_encrypt_fuzz_seed_corpus");
OEMCrypto_Generic_Api_Fuzz fuzzed_structure;
fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC;
fuzzed_structure.algorithm = algorithm;
// Cipher mode and algorithm.
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name, reinterpret_cast<const char*>(iv),
wvoec::KEY_IV_SIZE);
AppendSeparator(file_name);
AppendToFile(file_name, reinterpret_cast<const char*>(clear_buffer),
clear_buffer_length);
}
return OEMCrypto_Generic_Encrypt(session, clear_buffer, clear_buffer_length,
iv, algorithm, out_buffer);
}
OEMCryptoResult GenericDecrypt(OEMCrypto_SESSION session,
const uint8_t* encrypted_buffer,
size_t encrypted_buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_generic_decrypt_fuzz_seed_corpus");
OEMCrypto_Generic_Api_Fuzz fuzzed_structure;
fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC;
fuzzed_structure.algorithm = algorithm;
// Cipher mode and algorithm.
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name, reinterpret_cast<const char*>(iv),
wvoec::KEY_IV_SIZE);
AppendSeparator(file_name);
AppendToFile(file_name, reinterpret_cast<const char*>(encrypted_buffer),
encrypted_buffer_length);
}
return OEMCrypto_Generic_Decrypt(session, encrypted_buffer,
encrypted_buffer_length, iv, algorithm,
out_buffer);
}
OEMCryptoResult GenericVerify(OEMCrypto_SESSION session,
const uint8_t* clear_buffer,
size_t clear_buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length) {
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_generic_verify_fuzz_seed_corpus");
OEMCrypto_Generic_Verify_Fuzz fuzzed_structure;
fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC;
fuzzed_structure.algorithm = algorithm;
fuzzed_structure.signature_length = signature_length;
// Cipher mode and algorithm.
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name, reinterpret_cast<const char*>(clear_buffer),
clear_buffer_length);
}
return OEMCrypto_Generic_Verify(session, clear_buffer, clear_buffer_length,
algorithm, signature, signature_length);
}
OEMCryptoResult GenericSign(OEMCrypto_SESSION session,
const uint8_t* clear_buffer,
size_t clear_buffer_length,
OEMCrypto_Algorithm algorithm, uint8_t* signature,
size_t* signature_length) {
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_generic_sign_fuzz_seed_corpus");
OEMCrypto_Generic_Api_Fuzz fuzzed_structure;
fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC;
fuzzed_structure.algorithm = algorithm;
// Cipher mode and algorithm.
AppendToFile(file_name, reinterpret_cast<const char*>(&fuzzed_structure),
sizeof(fuzzed_structure));
AppendToFile(file_name, reinterpret_cast<const char*>(clear_buffer),
clear_buffer_length);
}
return OEMCrypto_Generic_Sign(session, clear_buffer, clear_buffer_length,
algorithm, signature, signature_length);
}
// This asks OEMCrypto to encrypt with the specified key, and expects a
// failure.
void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t buffer_length) {
OEMCryptoResult sts;
vector<uint8_t> expected_encrypted;
EncryptBuffer(key_index, clear_buffer_, &expected_encrypted);
sts = OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> encrypted(buffer_length);
sts = GenericEncrypt(session_.session_id(), clear_buffer_.data(),
buffer_length, iv_, algorithm, encrypted.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
expected_encrypted.resize(buffer_length);
EXPECT_NE(encrypted, expected_encrypted);
}
// This asks OEMCrypto to decrypt with the specified key, and expects a
// failure.
void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t buffer_length) {
OEMCryptoResult sts;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
sts = OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> resultant(encrypted.size());
sts = GenericDecrypt(session_.session_id(), encrypted.data(), buffer_length,
iv_, algorithm, resultant.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(clear_buffer_, resultant);
}
// This asks OEMCrypto to sign with the specified key, and expects a
// failure.
void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) {
OEMCryptoResult sts;
vector<uint8_t> expected_signature;
SignBuffer(key_index, clear_buffer_, &expected_signature);
sts = OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
size_t signature_length = (size_t)SHA256_DIGEST_LENGTH;
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
sts = GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), algorithm, signature.data(),
&signature_length);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(signature, expected_signature);
}
// This asks OEMCrypto to verify a signature with the specified key, and
// expects a failure.
void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t signature_size, bool alter_data) {
OEMCryptoResult sts;
vector<uint8_t> signature;
SignBuffer(key_index, clear_buffer_, &signature);
if (alter_data) {
signature[0] ^= 42;
}
if (signature.size() < signature_size) {
signature.resize(signature_size);
}
sts = OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
sts = GenericVerify(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), algorithm, signature.data(),
signature_size);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
}
// This must be a multiple of encryption block size.
size_t buffer_size_;
vector<uint8_t> clear_buffer_;
vector<uint8_t> encrypted_buffer_;
uint8_t iv_[wvoec::KEY_IV_SIZE];
};
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); }
// Test that the Generic_Encrypt function works correctly.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) {
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
EncryptBuffer(key_index, clear_buffer_, &expected_encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericEncrypt(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
}
// Test that the Generic_Encrypt function fails when not allowed.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadEncrypt) {
EncryptAndLoadKeys();
BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_);
// The buffer size must be a multiple of 16, so subtracting 10 is bad.
BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10);
BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
}
// Test that the Generic_Encrypt works if the input and output buffers are the
// same.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
EncryptBuffer(key_index, clear_buffer_, &expected_encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
// Input and output are same buffer:
vector<uint8_t> buffer = clear_buffer_;
ASSERT_EQ(
OEMCrypto_SUCCESS,
GenericEncrypt(session_.session_id(), buffer.data(), buffer.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
ASSERT_EQ(expected_encrypted, buffer);
}
TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) {
EncryptAndLoadKeys();
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [session_id](size_t key_id_length) {
vector<uint8_t> key_id(key_id_length);
return OEMCrypto_SelectKey(session_id, key_id.data(), key_id.size(),
OEMCrypto_CipherMode_CENC);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_P(OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeyEncryptForHugeBuffer) {
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
OEMCrypto_SESSION session_id = session_.session_id();
auto& iv = iv_;
auto oemcrypto_function = [&session_id, &iv](size_t buffer_length) mutable {
vector<uint8_t> buffer(buffer_length);
return OEMCrypto_Generic_Encrypt(session_id, buffer.data(), buffer.size(),
iv, OEMCrypto_AES_CBC_128_NO_PADDING,
buffer.data());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 16, kHugeInputBufferLength,
kCheckStatus);
}
TEST_P(
OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeyEncryptForHugeBufferWithBufferLengthNotMultipleOf16) {
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
OEMCrypto_SESSION session_id = session_.session_id();
vector<uint8_t> buffer(17);
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Generic_Encrypt(
session_id, buffer.data(), buffer.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
}
// Test Generic_Decrypt works correctly.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) {
EncryptAndLoadKeys();
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> resultant(encrypted.size());
ASSERT_EQ(
OEMCrypto_SUCCESS,
GenericDecrypt(session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()));
ASSERT_EQ(clear_buffer_, resultant);
}
TEST_P(OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeyDecryptForHugeBuffer) {
EncryptAndLoadKeys();
unsigned int key_index = 1;
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
OEMCrypto_SESSION session_id = session_.session_id();
auto iv = iv_;
auto oemcrypto_function = [&session_id, &iv](size_t buffer_length) {
vector<uint8_t> encrypted(buffer_length);
vector<uint8_t> resultant(encrypted.size());
return OEMCrypto_Generic_Decrypt(
session_id, encrypted.data(), encrypted.size(), iv,
OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data());
};
// API expects length to be multiple of 16. Starting from 16.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 16, kHugeInputBufferLength,
kCheckStatus);
}
// Test that Generic_Decrypt works correctly when the input and output buffers
// are the same.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) {
EncryptAndLoadKeys();
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> buffer = encrypted;
ASSERT_EQ(
OEMCrypto_SUCCESS,
GenericDecrypt(session_.session_id(), buffer.data(), buffer.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
ASSERT_EQ(clear_buffer_, buffer);
}
// Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key
// requires a secure data path.
TEST_P(OEMCryptoGenericCryptoTest, GenericSecureToClear) {
license_messages_.set_control(wvoec::kControlObserveDataPath |
wvoec::kControlDataPathSecure);
license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys();
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> resultant(encrypted.size());
ASSERT_NE(
OEMCrypto_SUCCESS,
GenericDecrypt(session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()));
ASSERT_NE(clear_buffer_, resultant);
}
// Test that the Generic_Decrypt function fails when not allowed.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadDecrypt) {
EncryptAndLoadKeys();
BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_);
// The buffer size must be a multiple of 16, so subtracting 10 is bad.
BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10);
BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_);
}
TEST_P(OEMCryptoGenericCryptoTest, GenericKeySign) {
EncryptAndLoadKeys();
unsigned int key_index = 2;
vector<uint8_t> expected_signature;
SignBuffer(key_index, clear_buffer_, &expected_signature);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
size_t gen_signature_length = 0;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, nullptr,
&gen_signature_length));
ASSERT_EQ(static_cast<size_t>(SHA256_DIGEST_LENGTH), gen_signature_length);
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &gen_signature_length));
ASSERT_EQ(expected_signature, signature);
}
TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeySignForHugeBuffer) {
EncryptAndLoadKeys();
unsigned int key_index = 2;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
size_t signature_length = signature.size();
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &signature,
&signature_length](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
return OEMCrypto_Generic_Sign(session_id, buffer.data(), buffer.size(),
OEMCrypto_HMAC_SHA256, signature.data(),
&signature_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_P(OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeySignForHugeSignatureLength) {
EncryptAndLoadKeys();
unsigned int key_index = 2;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
OEMCrypto_SESSION session_id = session_.session_id();
auto clear_buffer = clear_buffer_;
auto oemcrypto_function = [&session_id,
&clear_buffer](size_t signature_length) {
vector<uint8_t> signature(signature_length);
size_t gen_signature_length = signature_length;
return OEMCrypto_Generic_Sign(session_id, clear_buffer.data(),
clear_buffer.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &gen_signature_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// Test that the Generic_Sign function fails when not allowed.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadSign) {
EncryptAndLoadKeys();
BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key.
BadSign(1, OEMCrypto_HMAC_SHA256); // Can't sign with decrypt key.
BadSign(3, OEMCrypto_HMAC_SHA256); // Can't sign with verify key.
BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm.
}
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) {
EncryptAndLoadKeys();
unsigned int key_index = 3;
vector<uint8_t> signature;
SignBuffer(key_index, clear_buffer_, &signature);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericVerify(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
TEST_P(OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeyVerifyForHugeBuffer) {
EncryptAndLoadKeys();
unsigned int key_index = 3;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
auto oemcrypto_function = [&](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
vector<uint8_t> signature;
SignBuffer(key_index, buffer, &signature);
return GenericVerify(session_.session_id(), buffer.data(), buffer.size(),
OEMCrypto_HMAC_SHA256, signature.data(),
signature.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_P(OEMCryptoGenericCryptoTest,
OEMCryptoMemoryGenericKeyVerifyForHugeSignatureLength) {
EncryptAndLoadKeys();
unsigned int key_index = 3;
vector<uint8_t> signature;
SignBuffer(key_index, clear_buffer_, &signature);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
OEMCrypto_SESSION session_id = session_.session_id();
auto clear_buffer = clear_buffer_;
auto oemcrypto_function = [&session_id, &clear_buffer,
&signature](size_t signature_length) {
return OEMCrypto_Generic_Verify(session_id, clear_buffer.data(),
clear_buffer.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// Test that the Generic_Verify function fails when not allowed.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) {
EncryptAndLoadKeys();
BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false);
BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false);
BadVerify(2, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false);
BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, true);
BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH - 1, false);
BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH + 1, false);
BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false);
}
// Test Generic_Encrypt with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
EncryptBuffer(key_index, clear_buffer_, &expected_encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericEncrypt(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
}
// 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.
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> resultant(encrypted.size());
ASSERT_EQ(
OEMCrypto_SUCCESS,
GenericDecrypt(session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()));
ASSERT_EQ(clear_buffer_, resultant);
}
// Test Generic_Sign with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) {
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 2;
vector<uint8_t> expected_signature;
SignBuffer(key_index, clear_buffer_, &expected_signature);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
size_t gen_signature_length = 0;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, nullptr,
&gen_signature_length));
ASSERT_EQ(static_cast<size_t>(SHA256_DIGEST_LENGTH), gen_signature_length);
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &gen_signature_length));
ASSERT_EQ(expected_signature, signature);
}
// Test Generic_Verify with the maximum buffer size.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
EncryptAndLoadKeys();
unsigned int key_index = 3;
vector<uint8_t> signature;
SignBuffer(key_index, clear_buffer_, &signature);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericVerify(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
// Test Generic_Encrypt when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) {
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys();
vector<uint8_t> expected_encrypted;
EncryptBuffer(0, clear_buffer_, &expected_encrypted);
unsigned int key_index = 0;
vector<uint8_t> encrypted(clear_buffer_.size());
// Should be valid key at the start.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericEncrypt(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
wvutil::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
encrypted.assign(clear_buffer_.size(), 0);
OEMCryptoResult status = OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data());
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status);
ASSERT_NE(encrypted, expected_encrypted);
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index));
}
// Test Generic_Decrypt when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) {
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys();
// Should be valid key at the start.
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> resultant(encrypted.size());
ASSERT_EQ(
OEMCrypto_SUCCESS,
GenericDecrypt(session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()));
ASSERT_EQ(clear_buffer_, resultant);
wvutil::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
resultant.assign(encrypted.size(), 0);
OEMCryptoResult status =
GenericDecrypt(session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data());
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status);
ASSERT_NE(clear_buffer_, resultant);
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index));
}
// Test Generic_Sign when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) {
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys();
unsigned int key_index = 2;
vector<uint8_t> expected_signature;
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
size_t signature_length = signature.size();
SignBuffer(key_index, clear_buffer_, &expected_signature);
// Should be valid key at the start.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericSign(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &signature_length));
ASSERT_EQ(expected_signature, signature);
wvutil::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
signature.assign(SHA256_DIGEST_LENGTH, 0);
OEMCryptoResult status = GenericSign(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
OEMCrypto_HMAC_SHA256, signature.data(), &signature_length);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status);
ASSERT_NE(expected_signature, signature);
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index));
}
// Test Generic_Verify when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) {
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys();
unsigned int key_index = 3;
vector<uint8_t> signature;
SignBuffer(key_index, clear_buffer_, &signature);
// Should be valid key at the start.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(),
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
ASSERT_EQ(OEMCrypto_SUCCESS,
GenericVerify(session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
wvutil::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
OEMCryptoResult status = OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
OEMCrypto_HMAC_SHA256, signature.data(), signature.size());
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status);
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index));
}
const unsigned int kLongKeyId = 2;
// Test that short key ids are allowed.
class OEMCryptoGenericCryptoKeyIdLengthTest
: public OEMCryptoGenericCryptoTest {
protected:
void SetUp() override {
OEMCryptoGenericCryptoTest::SetUp();
license_messages_.set_num_keys(5);
license_messages_.set_control(wvoec::kControlAllowDecrypt);
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes.
// But, we are testing that the key ids do not have to have the same length.
// 12 bytes (common key id length).
license_messages_.SetKeyId(0, "123456789012");
license_messages_.SetKeyId(1, "12345"); // short key id.
// 16 byte key id. (default)
license_messages_.SetKeyId(2, "1234567890123456");
license_messages_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon)
license_messages_.SetKeyId(4, "1"); // very short key id.
ASSERT_EQ(2u, kLongKeyId);
ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings());
}
// Make all four keys have the same length.
void SetUniformKeyIdLength(size_t key_id_length) {
for (size_t i = 0; i < license_messages_.num_keys(); i++) {
string key_id;
key_id.resize(key_id_length, i + 'a');
license_messages_.SetKeyId(i, key_id);
}
ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings());
}
void TestWithKey(unsigned int key_index) {
ASSERT_LT(key_index, license_messages_.num_keys());
EncryptAndLoadKeys();
vector<uint8_t> encrypted;
// To make sure OEMCrypto is not expecting the key_id to be zero padded, we
// will create a buffer that is padded with 'Z'.
// Then, we use fill the buffer with the longer of the three keys. If
// OEMCrypto is paying attention to the key id length, it should pick out
// the correct key.
vector<uint8_t> key_id_buffer(
session_.license().keys[kLongKeyId].key_id_length + 5,
'Z'); // Fill a bigger buffer with letter 'Z'.
memcpy(key_id_buffer.data(), session_.license().keys[kLongKeyId].key_id,
session_.license().keys[kLongKeyId].key_id_length);
EncryptBuffer(key_index, clear_buffer_, &encrypted);
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(session_.session_id(), key_id_buffer.data(),
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC));
vector<uint8_t> resultant(encrypted.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Decrypt(
session_.session_id(), encrypted.data(), encrypted.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()));
ASSERT_EQ(clear_buffer_, resultant);
}
};
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); }
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); }
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); }
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, FourteenByteKeyId) {
TestWithKey(3);
}
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, VeryShortKeyId) {
TestWithKey(4);
}
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, UniformShortKeyId) {
SetUniformKeyIdLength(5);
TestWithKey(2);
}
TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, UniformLongKeyId) {
SetUniformKeyIdLength(kTestKeyIdMaxLength);
TestWithKey(2);
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoGenericCryptoTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
/// @}
/// @addtogroup usage_table
/// @{
// Test usage table functionality.
class LicenseWithUsageEntry {
public:
LicenseWithUsageEntry(const std::string& pst = "my_pst")
: session_(),
license_messages_(&session_),
generic_crypto_(false),
time_license_received_(0),
time_first_decrypt_(0),
time_last_decrypt_(0),
active_(true) {
license_messages_.set_pst(pst);
}
void MakeAndLoadOnline(OEMCryptoSessionTests* test) {
MakeAndLoad(test,
wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired);
}
// If status in not a nullptr, then creating a new entry is allowed to fail,
// and its error code is stored in status.
void MakeOfflineAndClose(OEMCryptoSessionTests* test,
OEMCryptoResult* status = nullptr) {
MakeAndLoad(test, wvoec::kControlNonceOrEntry, status);
if (status != nullptr && *status != OEMCrypto_SUCCESS) {
ASSERT_NO_FATAL_FAILURE(session_.close());
return;
}
ASSERT_NO_FATAL_FAILURE(
session_.UpdateUsageEntry(&(test->encrypted_usage_header_)));
ASSERT_NO_FATAL_FAILURE(GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(session_.close());
}
// If status in not a nullptr, then creating a new entry is allowed to fail,
// and its error code is stored in status.
void MakeAndLoad(SessionUtil* util, uint32_t control,
OEMCryptoResult* status = nullptr) {
license_messages_.set_control(control);
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_));
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
if (generic_crypto_) {
ASSERT_NO_FATAL_FAILURE(
license_messages_.CreateResponseWithGenericCryptoKeys());
} else {
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
}
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry(status));
if (status != nullptr && *status != OEMCrypto_SUCCESS) return;
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
time_license_received_ = wvutil::Clock().GetCurrentTime();
}
void OpenAndReload(SessionUtil* util) {
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());
}
// Test decrypt, and update the decrypt times for the pst report.
void TestDecryptCTR(bool select_key_first = true,
OEMCryptoResult expected_result = OEMCrypto_SUCCESS) {
session_.TestDecryptCTR(select_key_first, expected_result);
time_last_decrypt_ = wvutil::Clock().GetCurrentTime();
if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_;
}
void DeactivateUsageEntry() {
active_ = false;
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_deactivate_usage_entry_fuzz_seed_corpus");
AppendToFile(file_name, pst().c_str(), pst().length());
}
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_DeactivateUsageEntry(
session_.session_id(),
reinterpret_cast<const uint8_t*>(pst().c_str()), pst().length()));
}
void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst()));
Test_PST_Report expected(pst(), status);
ASSERT_NO_FATAL_FAILURE(
session_.VerifyReport(expected, time_license_received_,
time_first_decrypt_, time_last_decrypt_));
// The PST report was signed above. Below we verify that the entire message
// that is sent to the server will be signed by the right mac keys.
RenewalRoundTrip renewal_messages(&license_messages_);
renewal_messages.set_is_release(!active_);
ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest());
}
void ReloadUsageEntry() {
session_.ReloadUsageEntry();
session_.set_mac_keys(license_messages_.response_data().mac_keys);
}
const std::string& pst() const { return license_messages_.pst(); }
void set_pst(const std::string& pst) { license_messages_.set_pst(pst); }
LicenseRoundTrip& license_messages() { return license_messages_; }
Session& session() { return session_; }
void set_generic_crypto(bool generic_crypto) {
generic_crypto_ = generic_crypto;
}
private:
Session session_;
LicenseRoundTrip license_messages_;
bool generic_crypto_;
int64_t time_license_received_;
int64_t time_first_decrypt_;
int64_t time_last_decrypt_;
bool active_;
};
class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest {
public:
void SetUp() override { OEMCryptoGenericCryptoTest::SetUp(); }
virtual void ShutDown() {
ASSERT_NO_FATAL_FAILURE(session_.close());
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate());
}
virtual void Restart() {
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
EnsureTestKeys();
ASSERT_NO_FATAL_FAILURE(session_.open());
}
void PrintDotsWhileSleep(int64_t total_seconds, int64_t interval_seconds) {
int64_t dot_time = interval_seconds;
int64_t elapsed_time = 0;
const int64_t start_time = wvutil::Clock().GetCurrentTime();
do {
wvutil::TestSleep::Sleep(1);
elapsed_time = wvutil::Clock().GetCurrentTime() - start_time;
if (elapsed_time >= dot_time) {
cout << ".";
cout.flush();
dot_time += interval_seconds;
}
} while (elapsed_time < total_seconds);
cout << endl;
}
OEMCryptoResult LoadUsageTableHeader(
const vector<uint8_t>& encrypted_usage_header) {
return OEMCrypto_LoadUsageTableHeader(encrypted_usage_header.data(),
encrypted_usage_header.size());
}
};
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryUpdateUsageEntryForHugeHeaderBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
if (buffer_length < encrypted_usage_header_.size()) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
size_t header_buffer_length = 0;
size_t entry_buffer_length = 0;
// Header buffer length varies as generation_numbers size changes on every
// call. Hence, we need to call update usage entry in every loop to get
// latest value of header_buffer_length.
OEMCrypto_UpdateUsageEntry(s.session_id(), nullptr, &header_buffer_length,
nullptr, &entry_buffer_length);
vector<uint8_t> encrypted_usage_entry(entry_buffer_length);
vector<uint8_t> header_buffer(encrypted_usage_header_);
header_buffer.resize(buffer_length);
return OEMCrypto_UpdateUsageEntry(
s.session_id(), header_buffer.data(), &buffer_length,
encrypted_usage_entry.data(), &entry_buffer_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryUpdateUsageEntryForHugeUsageEntryBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
size_t header_buffer_length = 0;
size_t entry_buffer_length = 0;
// Header buffer length varies as generation_numbers size changes on every
// call. Hence, we need to call update usage entry in every loop to get
// latest value of header_buffer_length.
OEMCrypto_UpdateUsageEntry(s.session_id(), nullptr, &header_buffer_length,
nullptr, &entry_buffer_length);
vector<uint8_t> header_buffer(encrypted_usage_header_);
header_buffer.resize(header_buffer_length);
vector<uint8_t> encrypted_usage_entry(buffer_length);
return OEMCrypto_UpdateUsageEntry(
s.session_id(), header_buffer.data(), &header_buffer_length,
encrypted_usage_entry.data(), &buffer_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryDeactivateUsageEntryForHugePstBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
std::string pst("pst");
pst.resize(buffer_length);
entry.license_messages().set_pst(pst);
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
return OEMCrypto_DeactivateUsageEntry(
s.session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length());
};
// The test setup assertions fails if pst length goes beyond kMaxPSTLength.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, kMaxPSTLength,
kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryLoadUsageTableHeaderForHugeHeader) {
auto oemcrypto_function = [&](size_t buffer_length) {
if (buffer_length < encrypted_usage_header_.size()) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
vector<uint8_t> header_buffer(encrypted_usage_header_);
header_buffer.resize(buffer_length);
return OEMCrypto_LoadUsageTableHeader(header_buffer.data(),
header_buffer.size());
};
// We cannot generate an encrypted usage header of varying length with
// valid signature. Hence irrespective of return status, we call API for
// varying buffer lengths.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function,
encrypted_usage_header_.size(),
kHugeInputBufferLength, !kCheckStatus);
}
TEST_P(
OEMCryptoUsageTableTest,
OEMCryptoMemoryLoadUsageTableHeaderForHugeHeaderStartingHeaderLengthFrom1) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
vector<uint8_t> header_buffer(buffer_length);
return OEMCrypto_LoadUsageTableHeader(header_buffer.data(),
header_buffer.size());
};
// We cannot generate an encrypted usage header of varying length with
// valid signature. Hence irrespective of return status, we call API for
// varying buffer lengths.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryLoadUsageEntryForHugeUsageEntryBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
Session& s = entry.session();
// Make first entry 0.
entry.MakeOfflineAndClose(this);
if (buffer_length < s.encrypted_usage_entry().size()) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
Session s2;
s2.open();
InstallTestRSAKey(&s2);
vector<uint8_t> encrypted_usage_entry(buffer_length);
memcpy(encrypted_usage_entry.data(), s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size());
const uint32_t usage_entry_number = s.usage_entry_number();
return OEMCrypto_LoadUsageEntry(s2.session_id(), usage_entry_number,
encrypted_usage_entry.data(),
encrypted_usage_entry.size());
};
// We cannot generate an encrypted usage enctry of varying length with
// valid signature. Hence irrespective of return status, we call API for
// varying buffer lengths.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryLoadUsageEntryForHugeInvalidUsageEntryNumber) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
// Make first entry 0.
entry.MakeOfflineAndClose(this);
Session s;
s.open();
InstallTestRSAKey(&s);
const uint32_t usage_entry_number = kHugeRandomNumber;
ASSERT_NO_FATAL_FAILURE(OEMCrypto_LoadUsageEntry(
s.session_id(), usage_entry_number, s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
}
TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugeReportBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
s.UpdateUsageEntry(&encrypted_usage_header_);
size_t length = 0;
OEMCrypto_ReportUsage(s.session_id(),
reinterpret_cast<const uint8_t*>(entry.pst().c_str()),
entry.pst().length(), nullptr, &length);
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_report_usage_fuzz_seed_corpus");
AppendToFile(file_name, reinterpret_cast<const char*>(&length),
sizeof(length));
AppendToFile(file_name, entry.pst().c_str(), entry.pst().length());
}
vector<uint8_t> pst_report_buffer(buffer_length);
return OEMCrypto_ReportUsage(
s.session_id(), reinterpret_cast<const uint8_t*>(entry.pst().c_str()),
entry.pst().length(), pst_report_buffer.data(), &buffer_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugePstBuffer) {
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
s.UpdateUsageEntry(&encrypted_usage_header_);
size_t length = 0;
OEMCrypto_ReportUsage(s.session_id(),
reinterpret_cast<const uint8_t*>(entry.pst().c_str()),
entry.pst().length(), nullptr, &length);
vector<uint8_t> pst_report_buffer(length);
vector<uint8_t> pst(buffer_length);
return OEMCrypto_ReportUsage(s.session_id(), pst.data(), pst.size(),
pst_report_buffer.data(), &length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_P(OEMCryptoUsageTableTest,
OEMCryptoMemoryShrinkUsageTableHeaderForHugeHeaderBufferLength) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
auto oemcrypto_function = [&](size_t buffer_length) {
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
size_t header_buffer_length = buffer_length;
encrypted_usage_header_.resize(header_buffer_length);
return OEMCrypto_ShrinkUsageTableHeader(1, encrypted_usage_header_.data(),
&header_buffer_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// Test an online or streaming license with PST. This license requires a valid
// nonce and can only be loaded once.
TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// test repeated report generation
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
// Decrypt should fail.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
}
// Test the usage report when the license is loaded but the keys are never used
// for decryption.
TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// No decrypt. We do not use this license.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
// Decrypt should fail.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
}
// Test that the usage table has been updated and saved before a report can be
// generated.
TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
// Cannot generate a report without first updating the file.
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// Now it's OK.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
// Cannot generate a report without first updating the file.
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
// Decrypt should fail.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// Test an online license with a license renewal.
TEST_P(OEMCryptoUsageTableTest, OnlineLicenseWithRefreshAPI16) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
RenewalRoundTrip renewal_messages(&entry.license_messages());
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
}
// Verify that a streaming license cannot be reloaded.
TEST_P(OEMCryptoUsageTableTest, RepeatOnlineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
Session s2;
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2));
s2.LoadUsageEntry(s); // Use the same entry.
ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s2));
}
// An offline license should not load on the first call if the nonce is bad.
TEST_P(OEMCryptoUsageTableTest, OnlineBadNonce) {
Session s;
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
license_messages.set_control(wvoec::kControlNonceEnabled |
wvoec::kControlNonceRequired);
license_messages.set_pst("my-pst");
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
for (uint32_t i = 0; i < license_messages.num_keys(); i++)
license_messages.response_data().keys[i].control.nonce ^= 42;
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages.LoadResponse());
}
// A license with non-zero replay control bits needs a valid pst.
TEST_P(OEMCryptoUsageTableTest, OnlineEmptyPST) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
license_messages.set_control(wvoec::kControlNonceEnabled |
wvoec::kControlNonceRequired);
// DO NOT SET PST: license_messages.set_pst(pst);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
// A license with non-zero replay control bits needs a valid pst.
TEST_P(OEMCryptoUsageTableTest, OnlineMissingEntry) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
license_messages.set_control(wvoec::kControlNonceEnabled |
wvoec::kControlNonceRequired);
license_messages.set_pst("my-pst");
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
// ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
// Sessions should have at most one entry at a time. This tests different
// orderings of CreateNewUsageEntry and LoadUsageEntry calls.
TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) {
// Entry Count: we start each test with an empty header.
uint32_t usage_entry_number;
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
Session& s = entry.session();
// Make first entry 0.
ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this));
// Load an entry, then try to create a second.
ASSERT_NO_FATAL_FAILURE(s.open());
// Reload entry 0.
ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry());
// Create new entry 1 should fail.
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_CreateNewUsageEntry(entry.session().session_id(),
&usage_entry_number));
ASSERT_NO_FATAL_FAILURE(s.close());
// Create an entry, then try to load a second.
Session s2;
ASSERT_NO_FATAL_FAILURE(s2.open());
// Create entry 1.
ASSERT_NO_FATAL_FAILURE(s2.CreateNewUsageEntry());
// Try to reload entry 0.
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s2.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s2.close());
// Reload an entry and a license, then try to load the same entry again.
// This reloads entry 0.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Create an entry, then try to create a second entry.
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(s2.CreateNewUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_CreateNewUsageEntry(
s2.session_id(), &usage_entry_number));
}
// An entry can be loaded in only one session at a time.
TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) {
// Entry Count: we start each test with an empty header.
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
Session& s = entry.session();
// Make first entry 0.
ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this));
const uint32_t usage_entry_number = s.usage_entry_number();
EXPECT_EQ(usage_entry_number, 0u); // Should be only entry in this test.
// Load an entry, then try to create a second.
ASSERT_NO_FATAL_FAILURE(s.open());
// Reload entry 0.
ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry());
// Create an entry, then try to load a second.
Session s2;
ASSERT_NO_FATAL_FAILURE(s2.open());
// Try to load entry 0 into session 2.
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
OEMCrypto_LoadUsageEntry(s2.session_id(), usage_entry_number,
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
}
// Test generic encrypt when the license uses a PST.
TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.set_generic_crypto(true);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
OEMCryptoResult sts;
unsigned int key_index = 0;
vector<uint8_t> expected_encrypted;
EncryptBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_,
&expected_encrypted);
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id,
s.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> encrypted(clear_buffer_.size());
sts = OEMCrypto_Generic_Encrypt(
s.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EXPECT_EQ(expected_encrypted, encrypted);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
encrypted.assign(clear_buffer_.size(), 0);
sts = OEMCrypto_Generic_Encrypt(
s.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data());
ASSERT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(encrypted, expected_encrypted);
}
// Test generic decrypt when the license uses a PST.
TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.set_generic_crypto(true);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
OEMCryptoResult sts;
unsigned int key_index = 1;
vector<uint8_t> encrypted;
EncryptBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_,
&encrypted);
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id,
s.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> resultant(encrypted.size());
sts = OEMCrypto_Generic_Decrypt(
s.session_id(), encrypted.data(), encrypted.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EXPECT_EQ(clear_buffer_, resultant);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
resultant.assign(encrypted.size(), 0);
sts = OEMCrypto_Generic_Decrypt(
s.session_id(), encrypted.data(), encrypted.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data());
ASSERT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(clear_buffer_, resultant);
}
// Test generic sign when the license uses a PST.
TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.set_generic_crypto(true);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
OEMCryptoResult sts;
unsigned int key_index = 2;
vector<uint8_t> expected_signature;
SignBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_,
&expected_signature);
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id,
s.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
size_t gen_signature_length = 0;
sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
nullptr, &gen_signature_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_EQ(static_cast<size_t>(SHA256_DIGEST_LENGTH), gen_signature_length);
vector<uint8_t> signature(SHA256_DIGEST_LENGTH);
sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(expected_signature, signature);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
signature.assign(SHA256_DIGEST_LENGTH, 0);
gen_signature_length = SHA256_DIGEST_LENGTH;
sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), &gen_signature_length);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_NE(signature, expected_signature);
}
// Test generic verify when the license uses a PST.
TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.set_generic_crypto(true);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
OEMCryptoResult sts;
unsigned int key_index = 3;
vector<uint8_t> signature;
SignBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_,
&signature);
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id,
s.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CENC);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size());
ASSERT_NE(OEMCrypto_SUCCESS, sts);
}
// Test that an offline license can be loaded.
TEST_P(OEMCryptoUsageTableTest, OfflineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this));
}
// Test that an offline license can be loaded and that the license can be
// renewed.
TEST_P(OEMCryptoUsageTableTest, OfflineLicenseRefresh) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoad(this, wvoec::kControlNonceOrEntry);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
// License renewal message is signed by client and verified by the server.
RenewalRoundTrip renewal_messages(&entry.license_messages());
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
}
// Test that an offline license can be reloaded in a new session.
TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
}
// Test that an offline license can be reloaded in a new session, and then
// refreshed.
TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithRefresh) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
RenewalRoundTrip renewal_messages(&entry.license_messages());
MakeRenewalRequest(&renewal_messages);
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
}
// Verify that we can still reload an offline license after OEMCrypto_Terminate
// and Initialize are called. This is as close to a reboot as we can do in a
// unit test.
TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithTerminate) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
ShutDown(); // This calls OEMCrypto_Terminate.
Restart(); // This calls OEMCrypto_Initialize.
ASSERT_EQ(OEMCrypto_SUCCESS, LoadUsageTableHeader(encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
}
// If we attempt to load a second license with the same usage entry as the
// first, but it has different mac keys, then the attempt should fail. This is
// how we verify that we are reloading the same license.
TEST_P(OEMCryptoUsageTableTest, BadReloadOfflineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
// Offline license with new mac keys should fail.
Session s2;
LicenseRoundTrip license_messages2(&s2);
// Copy the response, and then change the mac keys.
license_messages2.response_data() = entry.license_messages().response_data();
license_messages2.core_response() = entry.license_messages().core_response();
license_messages2.response_data().mac_keys[7] ^= 42;
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2));
// This is a valid license: it is correctly signed.
license_messages2.EncryptAndSignResponse();
// Load the usage entry should be OK.
ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s));
ASSERT_NE(OEMCrypto_SUCCESS, license_messages2.LoadResponse());
ASSERT_NO_FATAL_FAILURE(s2.close());
// Now we go back to the original license response. It should load OK.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
}
// An offline license should not load on the first call if the nonce is bad.
TEST_P(OEMCryptoUsageTableTest, OfflineBadNonce) {
Session s;
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
license_messages.set_control(wvoec::kControlNonceOrEntry);
license_messages.set_pst("my-pst");
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
for (size_t i = 0; i < license_messages.num_keys(); i++)
license_messages.response_data().keys[i].control.nonce ^= 42;
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages.LoadResponse());
}
// An offline license needs a valid pst.
TEST_P(OEMCryptoUsageTableTest, OfflineEmptyPST) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_api_version(license_api_version_);
license_messages.set_control(wvoec::kControlNonceOrEntry);
// DO NOT SET PST: license_messages.set_pst(pst);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
// If we try to reload a license with a different PST, the attempt should fail.
TEST_P(OEMCryptoUsageTableTest, ReloadOfflineWrongPST) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
Session s2;
LicenseRoundTrip license_messages2(&s2);
license_messages2.response_data() = entry.license_messages().response_data();
license_messages2.core_response() = entry.license_messages().core_response();
// Change the middle of the pst.
license_messages2.response_data().pst[3] ^= 'Z';
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2));
// This is a valid license: it is correctly signed.
license_messages2.EncryptAndSignResponse();
// Load the usage entry should be OK.
ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s));
ASSERT_NE(OEMCrypto_SUCCESS, license_messages2.LoadResponse());
}
// Once a license has been deactivated, the keys can no longer be used for
// decryption. However, we can still generate a usage report.
TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
// Reload the offline license.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(s.close());
// Offline license can not be reused if it has been deactivated.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s));
s.close();
// But we can still generate a report.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// Sending a release from an offline license that has been deactivate will
// only work if the license server can handle v16 licenses. This is a rare
// condition, so it is OK to break it during the transition months.
entry.license_messages().set_api_version(global_features.api_version);
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
}
// The usage report should indicate that the keys were never used for
// decryption.
TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
// No Decrypt. This license is unused.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
ASSERT_NO_FATAL_FAILURE(s.close());
// Offline license can not be reused if it has been deactivated.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s));
s.close();
// But we can still generate a report.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// Sending a release from an offline license that has been deactivate will
// only work if the license server can handle v16 licenses. This is a rare
// condition, so it is OK to break it during the transition months.
entry.license_messages().set_api_version(global_features.api_version);
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
}
TEST_P(OEMCryptoUsageTableTest, SecureStop) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
// When we generate a secure stop without loading the license first, it
// should assume the server does not support core messages.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
}
// Test update usage table fails when passed a null pointer.
TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
size_t header_buffer_length = encrypted_usage_header_.size();
size_t entry_buffer_length = s.encrypted_usage_entry().size();
vector<uint8_t> buffer(entry_buffer_length);
// Now try to pass in null pointers for the buffers. This should fail.
ASSERT_NE(
OEMCrypto_SUCCESS,
OEMCrypto_UpdateUsageEntry(s.session_id(), nullptr, &header_buffer_length,
buffer.data(), &entry_buffer_length));
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_UpdateUsageEntry(
s.session_id(), encrypted_usage_header_.data(),
&header_buffer_length, nullptr, &entry_buffer_length));
}
// Class used to test usage table defragmentation.
class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
protected:
void ReloadLicense(LicenseWithUsageEntry* entry) {
Session& s = entry->session();
ASSERT_NO_FATAL_FAILURE(entry->OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry->TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry->GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.close());
}
void FailReloadLicense(LicenseWithUsageEntry* entry,
OEMCryptoResult expected_result) {
Session& s = entry->session();
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_EQ(expected_result,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NE(OEMCrypto_SUCCESS, entry->license_messages().LoadResponse());
ASSERT_NO_FATAL_FAILURE(s.close());
}
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 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);
if (sts == OEMCrypto_SUCCESS) {
encrypted_usage_header_.resize(header_buffer_length);
}
}
};
// Verify that usage table entries can be moved around in the table.
TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) {
const size_t ENTRY_COUNT = 10;
vector<LicenseWithUsageEntry> entries(ENTRY_COUNT);
for (size_t i = 0; i < ENTRY_COUNT; i++) {
entries[i].set_pst("pst " + std::to_string(i));
ASSERT_NO_FATAL_FAILURE(entries[i].MakeOfflineAndClose(this))
<< "On license " << i << " pst=" << entries[i].pst();
wvutil::TestSleep::SyncFakeClock();
}
for (size_t i = 0; i < ENTRY_COUNT; i++) {
ASSERT_NO_FATAL_FAILURE(entries[i].OpenAndReload(this))
<< "On license " << i << " pst=" << entries[i].pst();
ASSERT_NO_FATAL_FAILURE(entries[i].session().close())
<< "On license " << i << " pst=" << entries[i].pst();
}
// Move 4 to 1.
ASSERT_NO_FATAL_FAILURE(
entries[4].session().MoveUsageEntry(1, &encrypted_usage_header_));
// Shrink header to 3 entries 0, 1 was 4, 2.
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(3));
ShutDown();
Restart();
ASSERT_EQ(OEMCrypto_SUCCESS, LoadUsageTableHeader(encrypted_usage_header_));
wvutil::TestSleep::SyncFakeClock();
ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[0]));
// Now has index 1.
ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[4]));
ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[2]));
// When 4 was moved to 1, it increased the gen. number in the header.
ASSERT_NO_FATAL_FAILURE(
FailReloadLicense(&entries[1], OEMCrypto_ERROR_GENERATION_SKEW));
// Index 3 is beyond the end of the table.
ASSERT_NO_FATAL_FAILURE(
FailReloadLicense(&entries[3], OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// A usage table entry cannot be moved into an entry where an open session is
// currently using the entry.
TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToOpenSession) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry());
// s0 currently open on index 0. Expect this to fail:
ASSERT_NO_FATAL_FAILURE(entry1.session().MoveUsageEntry(
0, &encrypted_usage_header_, OEMCrypto_ERROR_ENTRY_IN_USE));
}
TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToInvalidHugeEntryIndex) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
entry0.session().open();
entry0.ReloadUsageEntry();
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_MoveEntry(entry0.session().session_id(), kHugeRandomNumber));
}
// The usage table cannot be shrunk if any session is using an entry that would
// be deleted.
TEST_P(OEMCryptoUsageTableDefragTest, ShrinkOverOpenSessions) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry());
entry1.session().open();
ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry());
// Since s0 and s1 are open, we can't shrink.
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_ERROR_ENTRY_IN_USE));
entry1.session().close(); // Can shrink after closing s1, even if s0 is open.
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_SUCCESS));
}
// Verify the usage table size can be increased.
TEST_P(OEMCryptoUsageTableDefragTest, EnlargeHeader) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
// Can only shrink the header -- not make it bigger.
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(4, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// A new header can only be created while no entries are in use.
TEST_P(OEMCryptoUsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry());
const bool kExpectFailure = false;
ASSERT_NO_FATAL_FAILURE(CreateUsageTableHeader(kExpectFailure));
}
// Verify that a usage table entry can only be loaded into the correct index of
// the table.
TEST_P(OEMCryptoUsageTableDefragTest, ReloadUsageEntryWrongIndex) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
entry0.MakeOfflineAndClose(this);
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry1.MakeOfflineAndClose(this);
entry0.session().set_usage_entry_number(1);
ASSERT_NO_FATAL_FAILURE(
FailReloadLicense(&entry0, OEMCrypto_ERROR_INVALID_SESSION));
}
// Verify that a usage table entry cannot be loaded if it has been altered.
TEST_P(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) {
LicenseWithUsageEntry entry;
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
vector<uint8_t> data = s.encrypted_usage_entry();
ASSERT_LT(0UL, data.size());
data[0] ^= 42;
// Error could be signature or verification error.
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
data.data(), data.size()));
}
// This verifies we can actually create the required number of usage table
// entries.
TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) {
// OEMCrypto is required to store at least 300 entries in the usage table
// header, but it is allowed to store more. This test verifies that if we keep
// adding entries, the error indicates a resource limit. It then verifies
// that all of the successful entries are still valid after we throw out the
// last invalid entry.
// After API 16, we require 300 entries in the usage table. Before API 16, we
// required 200.
const size_t required_capacity = RequiredUsageSize();
// We try to make a much large header, and assume there is an error at some
// point.
const size_t attempt_count = required_capacity * 5;
// Count of how many entries we successfully create.
size_t successful_count = 0;
// These entries have licenses tied to them.
std::vector<std::unique_ptr<LicenseWithUsageEntry>> entries;
// Store the status of the last attempt to create an entry.
OEMCryptoResult status = OEMCrypto_SUCCESS;
while (successful_count < attempt_count && status == OEMCrypto_SUCCESS) {
wvutil::TestSleep::SyncFakeClock();
LOGD("Creating license for entry %zu", successful_count);
entries.push_back(
std::unique_ptr<LicenseWithUsageEntry>(new LicenseWithUsageEntry()));
entries.back()->set_pst("pst " + std::to_string(successful_count));
ASSERT_NO_FATAL_FAILURE(entries.back()->MakeOfflineAndClose(this, &status))
<< "Failed creating license for entry " << successful_count;
if (status != OEMCrypto_SUCCESS) {
// Remove the failed session.
entries.resize(entries.size() - 1);
break;
}
EXPECT_EQ(entries.back()->session().usage_entry_number(), successful_count);
successful_count++;
// We don't create a license for each entry. For every license, we'll
// create 10 empty entries.
constexpr size_t filler_count = 10;
for (size_t i = 0; i < filler_count; i++) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry(&status))
<< "Failed creating entry " << successful_count;
if (status != OEMCrypto_SUCCESS) break;
EXPECT_EQ(s.usage_entry_number(), successful_count);
successful_count++;
}
}
LOGD("successful_count = %zu", successful_count);
if (status != OEMCrypto_SUCCESS) {
// If we failed to create this many entries because of limited resources,
// then the error returned should be insufficient resources.
EXPECT_EQ(OEMCrypto_ERROR_INSUFFICIENT_RESOURCES, status)
<< "Failed to create license " << successful_count
<< ", with wrong error code.";
}
EXPECT_GE(successful_count, required_capacity);
wvutil::TestSleep::SyncFakeClock();
// Shrink the table a little.
constexpr size_t small_number = 5;
size_t smaller_size = successful_count - small_number;
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(static_cast<uint32_t>(smaller_size)));
// Throw out the last license if it was in the part of the table that was
// shrunk.
if (entries.back()->session().usage_entry_number() >= smaller_size) {
entries.pop_back();
}
// Create a few more license
for (size_t i = 0; i < small_number; i++) {
wvutil::TestSleep::SyncFakeClock();
entries.push_back(
std::unique_ptr<LicenseWithUsageEntry>(new LicenseWithUsageEntry()));
entries.back()->set_pst("new pst " + std::to_string(smaller_size + i));
entries.back()->MakeOfflineAndClose(this);
}
// Make sure that all of the licenses can be reloaded.
for (size_t i = 0; i < entries.size(); i++) {
wvutil::TestSleep::SyncFakeClock();
Session& s = entries[i]->session();
ASSERT_NO_FATAL_FAILURE(entries[i]->OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kUnused));
ASSERT_NO_FATAL_FAILURE(entries[i]->TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.close());
}
}
// Verify that usage entries can be created in the position of existing entry
// indexes.
TEST_P(OEMCryptoUsageTableDefragTest, ReuseUsageEntryAPI17) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.session().CreateNewUsageEntry());
const uint32_t number = entry0.session().usage_entry_number();
entry0.session().close();
entry1.session().open();
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_ReuseUsageEntry(entry1.session().session_id(), number));
}
// Verify that usage entries cannot replace an entry that is currently in
// use by a session.
TEST_P(OEMCryptoUsageTableDefragTest, ReuseUsageEntryIndexInUseAPI17) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.session().CreateNewUsageEntry());
const uint32_t number = entry0.session().usage_entry_number();
entry1.session().open();
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
OEMCrypto_ReuseUsageEntry(entry1.session().session_id(), number));
}
// Verify that usage entries cannot be created if the usage entry index is
// too large.
TEST_P(OEMCryptoUsageTableDefragTest, ReuseUsageEntryWithInvalidIndexAPI17) {
LicenseWithUsageEntry entry0;
entry0.set_pst("pst 0");
LicenseWithUsageEntry entry1;
entry1.set_pst("pst 1");
entry0.session().open();
ASSERT_NO_FATAL_FAILURE(entry0.session().CreateNewUsageEntry());
const uint32_t number = entry0.session().usage_entry_number();
entry0.session().close();
entry1.session().open();
ASSERT_EQ(
OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_ReuseUsageEntry(entry1.session().session_id(), number + 42));
}
// Verify that usage entries cannot be created if the session already has an
// entry.
TEST_P(OEMCryptoUsageTableDefragTest,
ReuseUsageEntrySessionAlreadyHasEntryAPI17) {
LicenseWithUsageEntry entry;
entry.set_pst("pst 0");
// Create 5 entries in the table.
for (int i = 0; i < 5; i++) {
entry.session().open();
ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry());
entry.session().close();
}
entry.session().open();
ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry());
const uint32_t number = entry.session().usage_entry_number();
ASSERT_EQ(
OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES,
OEMCrypto_ReuseUsageEntry(entry.session().session_id(), number - 3));
}
// This verifies that the usage table header can be loaded if the generation
// number is off by one, but not off by two.
TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
// This also tests a few other error conditions with usage table headers.
LicenseWithUsageEntry entry;
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
// Reload the license, and save the header.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
vector<uint8_t> old_usage_header_2_ = encrypted_usage_header_;
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
vector<uint8_t> old_usage_header_1_ = encrypted_usage_header_;
vector<uint8_t> old_usage_entry_1 = s.encrypted_usage_entry();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
ShutDown();
Restart();
// Null pointer generates error.
ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(
nullptr, old_usage_header_2_.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Modified header generates error.
vector<uint8_t> bad_header = encrypted_usage_header_;
bad_header[3] ^= 42;
ASSERT_NE(OEMCrypto_SUCCESS, LoadUsageTableHeader(bad_header));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 2 generation numbers is error.
ASSERT_EQ(OEMCrypto_ERROR_GENERATION_SKEW,
LoadUsageTableHeader(old_usage_header_2_));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 1 generation numbers is just warning.
ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW,
LoadUsageTableHeader(old_usage_header_1_));
// Everything else should still work. The old entry goes with the old header.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
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());
}
TEST_P(OEMCryptoUsageTableTest, LoadAndReloadEntries) {
constexpr size_t kEntryCount = 10;
std::vector<LicenseWithUsageEntry> entries(kEntryCount);
for (LicenseWithUsageEntry& entry : entries) {
entry.license_messages().set_api_version(license_api_version_);
}
for (size_t i = 0; i < kEntryCount; ++i) {
const std::string create_description =
"Creating entry #" + std::to_string(i);
// Create and update a new entry.
LicenseWithUsageEntry& new_entry = entries[i];
ASSERT_NO_FATAL_FAILURE(new_entry.MakeOfflineAndClose(this))
<< create_description;
// Reload all entries, starting with the most recently created.
for (size_t j = 0; j <= i; ++j) {
const std::string reload_description =
"Reloading entry #" + std::to_string(i - j) +
", after creating entry #" + std::to_string(i);
LicenseWithUsageEntry& old_entry = entries[i - j];
ASSERT_NO_FATAL_FAILURE(old_entry.session().open());
ASSERT_NO_FATAL_FAILURE(old_entry.ReloadUsageEntry())
<< reload_description;
ASSERT_NO_FATAL_FAILURE(
old_entry.session().UpdateUsageEntry(&encrypted_usage_header_))
<< reload_description;
ASSERT_NO_FATAL_FAILURE(old_entry.session().close());
}
}
}
// A usage report with the wrong pst should fail.
TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST));
}
// Test usage table timing.
TEST_P(OEMCryptoUsageTableTest, TimingTest) {
LicenseWithUsageEntry entry1;
entry1.license_messages().set_api_version(license_api_version_);
Session& s1 = entry1.session();
entry1.set_pst("my_pst_1");
ASSERT_NO_FATAL_FAILURE(entry1.MakeOfflineAndClose(this));
LicenseWithUsageEntry entry2;
entry2.license_messages().set_api_version(license_api_version_);
Session& s2 = entry2.session();
entry2.set_pst("my_pst_2");
ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this));
LicenseWithUsageEntry entry3;
entry3.license_messages().set_api_version(license_api_version_);
Session& s3 = entry3.session();
entry3.set_pst("my_pst_3");
ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this));
ASSERT_NO_FATAL_FAILURE(entry1.MakeOfflineAndClose(this));
ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this));
ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this));
wvutil::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
wvutil::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
wvutil::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(entry1.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.close());
ASSERT_NO_FATAL_FAILURE(s2.close());
wvutil::TestSleep::Sleep(kLongSleep);
// This is as close to reboot as we can simulate in code.
ShutDown();
wvutil::TestSleep::Sleep(kShortSleep);
Restart();
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(),
encrypted_usage_header_.size()));
// After a reboot, we should be able to reload keys, and generate reports.
wvutil::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s2.close());
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry3.OpenAndReload(this));
wvutil::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry2.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry3.GenerateVerifyReport(kUnused));
}
// Verify the times in the usage report. For performance reasons, we allow the
// times in the usage report to be off by as much as kUsageTimeTolerance, which
// is 10 seconds. This acceptable error is called slop. This test needs to run
// long enough that the reported values are distinct, even after accounting for
// this slop.
TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
const int64_t kDotIntervalInSeconds = 5;
const int64_t kIdleInSeconds = 20;
const int64_t kPlaybackLoopInSeconds = 2 * 60;
cout << "This test verifies the elapsed time reported in the usage table "
"for a 2 minute simulated playback."
<< endl;
cout << "The total time for this test is about "
<< kPlaybackLoopInSeconds + 2 * kIdleInSeconds << " seconds." << endl;
cout << "Wait " << kIdleInSeconds
<< " seconds to verify usage table time before playback." << endl;
PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
cout << "Start simulated playback..." << endl;
int64_t dot_time = kDotIntervalInSeconds;
int64_t playback_time = 0;
const int64_t start_time = wvutil::Clock().GetCurrentTime();
do {
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
wvutil::TestSleep::Sleep(kShortSleep);
playback_time = wvutil::Clock().GetCurrentTime() - start_time;
ASSERT_LE(0, playback_time);
if (playback_time >= dot_time) {
cout << ".";
cout.flush();
dot_time += kDotIntervalInSeconds;
}
} while (playback_time < kPlaybackLoopInSeconds);
cout << "\nSimulated playback time = " << playback_time << " seconds.\n";
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() -
s.pst_report().seconds_since_last_decrypt(),
playback_time, kUsageTableTimeTolerance);
// We must update the usage entry BEFORE sleeping, not after.
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
cout << "Wait another " << kIdleInSeconds
<< " seconds "
"to verify usage table time since playback ended."
<< endl;
PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds);
// At this point, this is what we expect:
// idle playback loop idle
// |-----|-------------------------|-----|
// |<--->| = seconds_since_last_decrypt
// |<----------------------------->| = seconds_since_first_decrypt
// |<------------------------------------| = seconds_since_license_received
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// This test class is only used to roll back the wall clock. It is used to
// verify that OEMCrypto's system clock is monotonic. It is should only be run
// on a device that allows an application to set the clock.
class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
public:
void SetUp() override { OEMCryptoUsageTableTest::SetUp(); }
void TearDown() override {
wvutil::TestSleep::ResetRollback();
OEMCryptoUsageTableTest::TearDown();
}
};
// NOTE: This test needs root access since clock_settime messes with the system
// time in order to verify that OEMCrypto protects against rollbacks in usage
// entries. Therefore, this test is filtered if not run as root.
// We don't test roll-forward protection or instances where the user rolls back
// the time to the last decrypt call since this requires hardware-secure clocks
// to guarantee.
//
// 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. After the test, it "
<< "rolls the clock back forward." << endl;
constexpr int kRollBackTime = kShortSleep * 2;
constexpr int kPlaybackCount = 3;
constexpr int kTotalTime = kShortSleep * 8;
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));
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));
// Start with three short intervals of playback for entry1.
for (int i = 0; i < kPlaybackCount; i++) {
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
wvutil::TestSleep::Sleep(kShortSleep);
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
}
cout << "Rolling the system time back..." << endl;
ASSERT_TRUE(wvutil::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());
wvutil::TestSleep::Sleep(kShortSleep);
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
}
// One short interval of sleep to push us past the 6 interval duration.
wvutil::TestSleep::Sleep(2 * kShortSleep);
// Should not be able to continue playback in entry1.
ASSERT_NO_FATAL_FAILURE(
entry1.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
// Should not be able to start playback in entry2.
ASSERT_NO_FATAL_FAILURE(
entry2.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED));
// 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()));
wvutil::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()));
wvutil::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.
TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) {
std::string pst(kMaxPSTLength, 'a'); // A large PST.
LicenseWithUsageEntry entry(pst);
entry.MakeOfflineAndClose(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(s.close());
}
// Verify that a usage entry with an invalid session cannot be used.
TEST_P(OEMCryptoUsageTableTest, UsageEntryWithInvalidSession) {
std::string pst("pst");
LicenseWithUsageEntry entry;
entry.license_messages().set_pst(pst);
entry.session().open();
ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry());
entry.session().close();
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
OEMCrypto_DeactivateUsageEntry(
entry.session().session_id(),
reinterpret_cast<const uint8_t*>(pst.c_str()), pst.length()));
entry.session().open();
ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry());
entry.session().close();
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
OEMCrypto_MoveEntry(entry.session().session_id(), 0));
}
// Verify that a usage entry with an invalid session cannot be used.
TEST_P(OEMCryptoUsageTableTest, ReuseUsageEntryWithInvalidSessionAPI17) {
std::string pst("pst");
LicenseWithUsageEntry entry;
entry.license_messages().set_pst(pst);
entry.session().open();
ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry());
entry.session().close();
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
OEMCrypto_ReuseUsageEntry(entry.session().session_id(), 0));
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoUsageTableTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
// These tests only work when the license has a core message.
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableDefragTest,
Values<uint32_t>(kCurrentAPI));
// These tests only work when the license has a core message.
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableTestWallClock,
Values<uint32_t>(kCurrentAPI));
/// @}
} // namespace wvoec