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
10211 lines
430 KiB
C++
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(¤t, &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(¤t, &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(¤t, &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
|