Sync oemcrypto files from cdm udc-dev to Android
Changes included in this CL: 166806: Update OEMCrypto_GetDeviceInformation() | https://widevine-internal-review.googlesource.com/c/cdm/+/166806 166808: Update Android L3 after OEMCrypto_GetDeviceInformation() signature changes | https://widevine-internal-review.googlesource.com/c/cdm/+/166808 166809: Decode device info and write it to CSR payload | https://widevine-internal-review.googlesource.com/c/cdm/+/166809 167158: Fix Android include path and copy_files | https://widevine-internal-review.googlesource.com/c/cdm/+/167158 167159: Fix common typos and use inclusive language suggested by Android linter | https://widevine-internal-review.googlesource.com/c/cdm/+/167159 165618: Explicitly state python3 where needed. | https://widevine-internal-review.googlesource.com/c/cdm/+/165618 166757: Update Android.bp for Android | https://widevine-internal-review.googlesource.com/c/cdm/+/166757 164993: Refactor basic oemcrypto unit tests | https://widevine-internal-review.googlesource.com/c/cdm/+/164993 164978: Update OEMCrypto Unit Test Docs | https://widevine-internal-review.googlesource.com/c/cdm/+/164978 166941: Update make files for OEMCrypto | https://widevine-internal-review.googlesource.com/c/cdm/+/166941 165279: Refactor license unit tests | https://widevine-internal-review.googlesource.com/c/cdm/+/165279 165318: Refactor provisioning unit tests | https://widevine-internal-review.googlesource.com/c/cdm/+/165318 164800: Add extra check for renew on license load unit test | https://widevine-internal-review.googlesource.com/c/cdm/+/164800 165860: Remove duplicate definition of MaybeHex() | https://widevine-internal-review.googlesource.com/c/cdm/+/165860 164889: Updated CoreCommonRequestFromMessage and fix test | https://widevine-internal-review.googlesource.com/c/cdm/+/164889 164967: Add OPK pre-hook and post-hook error codes | https://widevine-internal-review.googlesource.com/c/cdm/+/164967 165140: Add hidden device_id_length to v18 provisioning message | https://widevine-internal-review.googlesource.com/c/cdm/+/165140 165204: Fix memory leak in oemcrypto test | https://widevine-internal-review.googlesource.com/c/cdm/+/165204 165958: Fix oemcrypto_generic_verify_fuzz mutator signature offset | https://widevine-internal-review.googlesource.com/c/cdm/+/165958 166037: Support SHA-256 in OEMCrypto Session Util | https://widevine-internal-review.googlesource.com/c/cdm/+/166037 Test: Run GtsMediaTests on Pixel 7 Bug: 270612144 Change-Id: Iff0820a2de7d043a820470a130af65b0dcadb759
This commit is contained in:
@@ -6,13 +6,15 @@ LOCAL_C_INCLUDES := \
|
||||
vendor/widevine/libwvdrmengine/cdm/util/include \
|
||||
|
||||
LOCAL_MODULE:=oemcrypto_test
|
||||
LOCAL_LICENSE_KINDS:=legacy_by_exception_only legacy_proprietary
|
||||
LOCAL_LICENSE_CONDITIONS:=by_exception_only proprietary by_exception_only
|
||||
LOCAL_LICENSE_KINDS:=legacy_by_exception_only
|
||||
LOCAL_LICENSE_CONDITIONS:=by_exception_only
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_MODULE_OWNER := widevine
|
||||
LOCAL_PROPRIETARY_MODULE := true
|
||||
|
||||
LOCAL_C_INCLUDES += external/googletest/googlemock/include \
|
||||
|
||||
# When built, explicitly put it in the DATA/nativetest directory.
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ LOCAL_SRC_FILES:= \
|
||||
oec_session_util.cpp \
|
||||
oemcrypto_corpus_generator_helper.cpp \
|
||||
oemcrypto_session_tests_helper.cpp \
|
||||
oemcrypto_basic_test.cpp \
|
||||
oemcrypto_license_test.cpp \
|
||||
oemcrypto_provisioning_test.cpp \
|
||||
oemcrypto_test.cpp \
|
||||
oemcrypto_test_android.cpp \
|
||||
oemcrypto_test_main.cpp \
|
||||
@@ -38,6 +41,8 @@ LOCAL_C_INCLUDES += \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
libjsmn \
|
||||
libgmock \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libwvlevel3 \
|
||||
|
||||
@@ -23,5 +23,6 @@ export PYTHONPATH="$PYTHONPATH:$CDM_DIR/third_party"
|
||||
python3 $CDM_DIR/third_party/gyp/__init__.py --format=ninja \
|
||||
--depth=$(pwd) \
|
||||
--include=oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gypi \
|
||||
-Dopk_config_dir=$CDM_DIR/oemcrypto/opk/ports/linux/ta/common \
|
||||
oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp
|
||||
ninja -C out/Default
|
||||
|
||||
@@ -93,7 +93,8 @@ extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
|
||||
fuzzed_properties.value.signature.data(),
|
||||
&signature_length);
|
||||
const size_t signature_offset = sizeof(fuzzed_properties.value.structure) +
|
||||
fuzzed_properties.value.buffer.size();
|
||||
fuzzed_properties.value.buffer.size() +
|
||||
sizeof(kFuzzDataSeparator);
|
||||
size = signature_offset + signature_length;
|
||||
if (size > max_size) {
|
||||
return 0;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "oec_session_util.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bio.h>
|
||||
@@ -44,6 +45,8 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
using testing::AnyOf;
|
||||
|
||||
// GTest requires PrintTo to be in the same namespace as the thing it prints,
|
||||
// which is std::vector in this case.
|
||||
namespace std {
|
||||
@@ -1811,8 +1814,15 @@ void Session::VerifyRsaSignature(const vector<uint8_t>& message,
|
||||
const util::RsaSignatureAlgorithm algorithm =
|
||||
padding_scheme == kSign_RSASSA_PSS ? util::kRsaPssDefault
|
||||
: util::kRsaPkcs1Cast;
|
||||
const OEMCryptoResult result = public_rsa_->VerifySignature(
|
||||
message.data(), message.size(), signature, signature_length, algorithm);
|
||||
OEMCrypto_SignatureHashAlgorithm hash_algorithm = OEMCrypto_SHA1;
|
||||
if (algorithm == util::kRsaPssDefault) {
|
||||
ASSERT_THAT(
|
||||
OEMCrypto_GetSignatureHashAlgorithm(session_id(), &hash_algorithm),
|
||||
AnyOf(OEMCrypto_SUCCESS, OEMCrypto_ERROR_NOT_IMPLEMENTED));
|
||||
}
|
||||
const OEMCryptoResult result =
|
||||
public_rsa_->VerifySignature(message.data(), message.size(), signature,
|
||||
signature_length, algorithm, hash_algorithm);
|
||||
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "RSA signature check failed";
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace wvoec {
|
||||
// OEMCrypto Fuzzing: Set max signture length to 1mb.
|
||||
const size_t MB = 1024 * 1024;
|
||||
|
||||
// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp
|
||||
// Make sure this is larger than kMaxKeysPerSession.
|
||||
constexpr size_t kMaxNumKeys = 30;
|
||||
|
||||
namespace {
|
||||
|
||||
649
libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp
Normal file
649
libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp
Normal file
@@ -0,0 +1,649 @@
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
|
||||
#include "oemcrypto_basic_test.h"
|
||||
|
||||
#include "clock.h"
|
||||
#include "jsmn.h"
|
||||
#include "log.h"
|
||||
#include "oemcrypto_corpus_generator_helper.h"
|
||||
#include "oemcrypto_resource_test.h"
|
||||
#include "test_sleep.h"
|
||||
|
||||
namespace wvoec {
|
||||
void OEMCryptoClientTest::SetUp() {
|
||||
::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());
|
||||
const OEMCryptoResult api_status = OEMCrypto_SetMaxAPIVersion(kCurrentAPI);
|
||||
OEMCrypto_EnterTestMode();
|
||||
if (api_status != OEMCrypto_SUCCESS &&
|
||||
api_status != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
// Log error, but continue assuming no error.
|
||||
LOGE("OEMCrypto_SetMaxAPIVersion returned %d", api_status);
|
||||
}
|
||||
}
|
||||
|
||||
void OEMCryptoClientTest::TearDown() {
|
||||
OEMCrypto_Terminate();
|
||||
::testing::Test::TearDown();
|
||||
}
|
||||
|
||||
const uint8_t* OEMCryptoClientTest::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 OEMCryptoClientTest::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) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_copy_buffer_fuzz_seed_corpus");
|
||||
|
||||
OEMCrypto_Copy_Buffer_Fuzz fuzzed_structure;
|
||||
fuzzed_structure.dest_buffer_desc.type = dest_buffer_descriptor->type;
|
||||
switch (fuzzed_structure.dest_buffer_desc.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
fuzzed_structure.dest_buffer_desc.buffer_config =
|
||||
dest_buffer_descriptor->buffer.clear.clear_buffer_length;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
fuzzed_structure.dest_buffer_desc.buffer_config =
|
||||
dest_buffer_descriptor->buffer.secure.secure_buffer_length;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
fuzzed_structure.dest_buffer_desc.buffer_config =
|
||||
dest_buffer_descriptor->buffer.direct.is_video;
|
||||
break;
|
||||
}
|
||||
fuzzed_structure.subsample_flags = subsample_flags;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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>";
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
/// @addtogroup basic
|
||||
/// @{
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 18.0. Tests last updated 2022-08-08";
|
||||
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, 18);
|
||||
EXPECT_EQ(ODK_MINOR_VERSION, 1);
|
||||
EXPECT_EQ(kCurrentAPI, 18u);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
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, CheckJsonBuildInformationAPI18) {
|
||||
std::string build_info;
|
||||
OEMCryptoResult sts = OEMCrypto_BuildInformation(&build_info[0], nullptr);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
|
||||
size_t buf_length = 0;
|
||||
// OEMCrypto must allow |buffer| to be null so long as |buffer_length|
|
||||
// is provided and initially set to zero.
|
||||
sts = OEMCrypto_BuildInformation(nullptr, &buf_length);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
build_info.resize(buf_length);
|
||||
const size_t max_final_size = buf_length;
|
||||
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_LE(buf_length, max_final_size);
|
||||
build_info.resize(buf_length);
|
||||
|
||||
jsmn_parser p;
|
||||
jsmn_init(&p);
|
||||
std::vector<jsmntok_t> tokens;
|
||||
int32_t num_tokens =
|
||||
jsmn_parse(&p, build_info.c_str(), build_info.size(), nullptr, 0);
|
||||
EXPECT_GT(num_tokens, 0)
|
||||
<< "Failed to parse BuildInformation as JSON, parse returned "
|
||||
<< num_tokens << "for following build info: " << build_info;
|
||||
|
||||
tokens.resize(num_tokens);
|
||||
jsmn_init(&p);
|
||||
int32_t jsmn_result = jsmn_parse(&p, build_info.c_str(), build_info.size(),
|
||||
tokens.data(), num_tokens);
|
||||
EXPECT_GE(jsmn_result, 0)
|
||||
<< "Failed to parse BuildInformation as JSON, parse returned "
|
||||
<< jsmn_result << "for following build info: " << build_info;
|
||||
|
||||
std::map<std::string, jsmntype_t> expected;
|
||||
expected["soc_vendor"] = JSMN_STRING;
|
||||
expected["soc_model"] = JSMN_STRING;
|
||||
expected["ta_ver"] = JSMN_STRING;
|
||||
expected["uses_opk"] = JSMN_PRIMITIVE;
|
||||
expected["tee_os"] = JSMN_STRING;
|
||||
expected["tee_os_ver"] = JSMN_STRING;
|
||||
|
||||
// for values in token
|
||||
// build string from start,end
|
||||
// check for existence in map
|
||||
// check if value matches expectation
|
||||
// remove from map
|
||||
for (int i = 0; i < jsmn_result; i++) {
|
||||
jsmntok_t token = tokens[i];
|
||||
std::string key = build_info.substr(token.start, token.end - token.start);
|
||||
if (expected.find(key) != expected.end()) {
|
||||
EXPECT_EQ(expected.find(key)->second, tokens[i + 1].type)
|
||||
<< "Type is incorrect for key " << key;
|
||||
expected.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
// if map is not empty, return false
|
||||
if (expected.size() > 0) {
|
||||
std::string missing;
|
||||
for (auto e : expected) {
|
||||
missing.append(e.first);
|
||||
missing.append(" ");
|
||||
}
|
||||
FAIL() << "JSON does not contain all required keys. Missing keys: ["
|
||||
<< missing << "] in string " << build_info;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
(void)OEMCrypto_SetMaxAPIVersion(kCurrentAPI);
|
||||
(void)OEMCrypto_EnterTestMode();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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.";
|
||||
}
|
||||
|
||||
/// @}
|
||||
} // namespace wvoec
|
||||
41
libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.h
Normal file
41
libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// Test data for OEMCrypto unit tests.
|
||||
//
|
||||
#ifndef CDM_OEMCRYPTO_BASIC_TEST_
|
||||
#define CDM_OEMCRYPTO_BASIC_TEST_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
namespace wvoec {
|
||||
|
||||
const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value);
|
||||
|
||||
std::string MaybeHex(const uint8_t* data, size_t length);
|
||||
std::string MaybeHex(const std::vector<uint8_t>& data);
|
||||
|
||||
/** Tests for just basic client functionality. */
|
||||
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
protected:
|
||||
OEMCryptoClientTest() {}
|
||||
|
||||
void SetUp() override;
|
||||
void TearDown() override;
|
||||
const uint8_t* find(const std::vector<uint8_t>& message,
|
||||
const std::vector<uint8_t>& substring);
|
||||
OEMCryptoResult CopyBuffer(
|
||||
OEMCrypto_SESSION session, OEMCrypto_SharedMemory* input_buffer,
|
||||
size_t input_buffer_size,
|
||||
const OEMCrypto_DestBufferDesc* dest_buffer_descriptor,
|
||||
uint8_t subsample_flags);
|
||||
};
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEMCRYPTO_BASIC_TEST_
|
||||
807
libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp
Normal file
807
libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp
Normal file
@@ -0,0 +1,807 @@
|
||||
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
|
||||
#include "oemcrypto_license_test.h"
|
||||
#include "log.h"
|
||||
#include "oemcrypto_basic_test.h"
|
||||
#include "oemcrypto_resource_test.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// Function to test APIs that expect a buffer length as input
|
||||
// by passing huge buffer lengths up to 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 up to kHugeInputBufferLength and test that
|
||||
// the API doesn't crash.
|
||||
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) {
|
||||
TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status);
|
||||
}
|
||||
|
||||
// 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->InstallTestDrmKey(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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(InstallTestDrmKey(&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 placeholder
|
||||
// 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(InstallTestDrmKey(&session2));
|
||||
// However, in order to start the rental clock, we have to sign something. So
|
||||
// we will sign a placeholder 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());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
//---------------------------------------------------------------------------//
|
||||
// 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());
|
||||
}
|
||||
//---------------------------------------------------------------------------//
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
// 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(InstallTestDrmKey(&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 possibility 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(InstallTestDrmKey(&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(InstallTestDrmKey(&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];
|
||||
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(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());
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
/// @}
|
||||
|
||||
/// @addtogroup security
|
||||
/// @{
|
||||
|
||||
// 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();
|
||||
vector<uint8_t> key_handle;
|
||||
GetKeyHandleIntoVector(session_.session_id(),
|
||||
session_.license().keys[0].key_id,
|
||||
session_.license().keys[0].key_id_length,
|
||||
OEMCrypto_CipherMode_CENC, key_handle);
|
||||
|
||||
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(key_handle.data(), key_handle.size(),
|
||||
&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,
|
||||
OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesEncryptedAPI16) {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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, ClearKcbAPI17) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&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);
|
||||
}
|
||||
|
||||
// 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. 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(InstallTestDrmKey(&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(InstallTestDrmKey(&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(InstallTestDrmKey(&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());
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
} // namespace wvoec
|
||||
242
libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h
Normal file
242
libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h
Normal file
@@ -0,0 +1,242 @@
|
||||
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// Test data for OEMCrypto unit tests.
|
||||
//
|
||||
#ifndef CDM_OEMCRYPTO_LICENSE_TEST_
|
||||
#define CDM_OEMCRYPTO_LICENSE_TEST_
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oemcrypto_basic_test.h"
|
||||
#include "oemcrypto_resource_test.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
|
||||
using ::testing::WithParamInterface;
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// Used for testing oemcrypto APIs with huge buffers.
|
||||
typedef const std::function<OEMCryptoResult(size_t)> oemcrypto_function;
|
||||
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
|
||||
size_t start_buffer_length,
|
||||
size_t end_buffer_length, bool check_status);
|
||||
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status);
|
||||
|
||||
void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session);
|
||||
|
||||
class OEMCryptoSessionTests : public OEMCryptoClientTest {
|
||||
public:
|
||||
vector<uint8_t> encrypted_usage_header_;
|
||||
|
||||
protected:
|
||||
OEMCryptoSessionTests() {}
|
||||
|
||||
void SetUp() override {
|
||||
OEMCryptoClientTest::SetUp();
|
||||
EnsureTestROT();
|
||||
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();
|
||||
InstallTestDrmKey(&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) {
|
||||
InstallTestDrmKey(&s);
|
||||
license_messages.SignAndVerifyRequest();
|
||||
license_messages.CreateDefaultResponse();
|
||||
license_messages.EncryptAndSignResponse();
|
||||
return license_messages.LoadResponse();
|
||||
}
|
||||
};
|
||||
|
||||
class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {};
|
||||
|
||||
// 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(InstallTestDrmKey(&session_));
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.close());
|
||||
OEMCryptoSessionTests::TearDown();
|
||||
}
|
||||
|
||||
protected:
|
||||
Session session_;
|
||||
uint32_t license_api_version_;
|
||||
LicenseRoundTrip license_messages_;
|
||||
};
|
||||
|
||||
// This class is used to test a license that is from a server with the specified
|
||||
// version parameter. Up to two versions 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) {
|
||||
vector<uint8_t> key_handle;
|
||||
GetKeyHandleIntoVector(session_.session_id(),
|
||||
session_.license().keys[0].key_id,
|
||||
session_.license().keys[0].key_id_length,
|
||||
OEMCrypto_CipherMode_CENC, key_handle);
|
||||
|
||||
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(key_handle.data(), key_handle.size(),
|
||||
&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();
|
||||
vector<uint8_t> key_handle;
|
||||
GetKeyHandleIntoVector(session_.session_id(),
|
||||
session_.license().keys[0].key_id,
|
||||
session_.license().keys[0].key_id_length,
|
||||
OEMCrypto_CipherMode_CENC, key_handle);
|
||||
|
||||
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(
|
||||
key_handle.data(), key_handle.size(), &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);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEMCRYPTO_LICENSE_TEST_
|
||||
627
libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp
Normal file
627
libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp
Normal file
@@ -0,0 +1,627 @@
|
||||
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
|
||||
#include "oemcrypto_provisioning_test.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "oemcrypto_basic_test.h"
|
||||
#include "oemcrypto_resource_test.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
/// @addtogroup provision
|
||||
/// @{
|
||||
|
||||
// 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, 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, 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()));
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
// 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_drm_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(CreateWrappedDRMKey());
|
||||
}
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
// Install the DRM Cert's RSA key.
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetTestRsaPublicKey());
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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.
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetPublicKeyFromSubjectPublicKey(
|
||||
key_type, public_key.data(), public_key_size));
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv40Test, GetDeviceInformationAPI18) {
|
||||
std::vector<uint8_t> device_info;
|
||||
size_t device_info_length = 0;
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetDeviceInformation(device_info.data(), &device_info_length);
|
||||
ASSERT_EQ(sts, OEMCrypto_ERROR_SHORT_BUFFER);
|
||||
ASSERT_NE(device_info_length, 0uL);
|
||||
device_info.resize(device_info_length);
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_GetDeviceInformation(device_info.data(), &device_info_length),
|
||||
OEMCrypto_SUCCESS);
|
||||
EXPECT_NE(device_info_length, 0uL);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv40Test, GetDeviceSignedCsrPayloadAPI18) {
|
||||
std::vector<uint8_t> challenge(64, 0xaa);
|
||||
// TODO: add cppbor support for oemcrypto tests for all targets. Before that,
|
||||
// use hex values which are equivalent of the commented cppbor statement.
|
||||
// std::vector<uint8_t> device_info = cppbor::Map()
|
||||
// .add("manufacturer", "google")
|
||||
// .add("fused", 0)
|
||||
// .add("other", "ignored")
|
||||
// .canonicalize()
|
||||
// .encode();
|
||||
//
|
||||
std::vector<uint8_t> device_info = {
|
||||
0xa3, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x0, 0x65, 0x6f, 0x74,
|
||||
0x68, 0x65, 0x72, 0x67, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64,
|
||||
0x6c, 0x6d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72,
|
||||
0x65, 0x72, 0x66, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65};
|
||||
std::vector<uint8_t> signed_csr_payload;
|
||||
size_t signed_csr_payload_length = 0;
|
||||
OEMCryptoResult sts = OEMCrypto_GetDeviceSignedCsrPayload(
|
||||
challenge.data(), challenge.size(), device_info.data(),
|
||||
device_info.size(), signed_csr_payload.data(),
|
||||
&signed_csr_payload_length);
|
||||
ASSERT_EQ(sts, OEMCrypto_ERROR_SHORT_BUFFER);
|
||||
ASSERT_NE(signed_csr_payload_length, 0uL);
|
||||
signed_csr_payload.resize(signed_csr_payload_length);
|
||||
ASSERT_EQ(OEMCrypto_GetDeviceSignedCsrPayload(
|
||||
challenge.data(), challenge.size(), device_info.data(),
|
||||
device_info.size(), signed_csr_payload.data(),
|
||||
&signed_csr_payload_length),
|
||||
OEMCrypto_SUCCESS);
|
||||
EXPECT_NE(signed_csr_payload_length, 0uL);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv40Test, GetDeviceSignedCsrPayloadInvalid) {
|
||||
std::vector<uint8_t> signed_csr_payload;
|
||||
size_t signed_csr_payload_length = 0;
|
||||
std::vector<uint8_t> challenge(64, 0xaa);
|
||||
std::vector<uint8_t> device_info = {
|
||||
0xa3, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x0, 0x65, 0x6f, 0x74,
|
||||
0x68, 0x65, 0x72, 0x67, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64,
|
||||
0x6c, 0x6d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72,
|
||||
0x65, 0x72, 0x66, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65};
|
||||
std::vector<uint8_t> challenge_empty;
|
||||
OEMCryptoResult sts = OEMCrypto_GetDeviceSignedCsrPayload(
|
||||
challenge_empty.data(), challenge_empty.size(), device_info.data(),
|
||||
device_info.size(), signed_csr_payload.data(),
|
||||
&signed_csr_payload_length);
|
||||
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) return;
|
||||
ASSERT_EQ(sts, OEMCrypto_ERROR_INVALID_CONTEXT);
|
||||
|
||||
// Oversized challenge
|
||||
std::vector<uint8_t> challenge_long(65, 0xaa);
|
||||
sts = OEMCrypto_GetDeviceSignedCsrPayload(
|
||||
challenge_long.data(), challenge_long.size(), device_info.data(),
|
||||
device_info.size(), signed_csr_payload.data(),
|
||||
&signed_csr_payload_length);
|
||||
ASSERT_EQ(sts, OEMCrypto_ERROR_INVALID_CONTEXT);
|
||||
|
||||
std::vector<uint8_t> device_empty;
|
||||
sts = OEMCrypto_GetDeviceSignedCsrPayload(
|
||||
challenge.data(), challenge.size(), device_empty.data(),
|
||||
device_empty.size(), signed_csr_payload.data(),
|
||||
&signed_csr_payload_length);
|
||||
ASSERT_EQ(sts, OEMCrypto_ERROR_INVALID_CONTEXT);
|
||||
}
|
||||
|
||||
// Verifies that 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 that 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.
|
||||
if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromSubjectPublicKey(
|
||||
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));
|
||||
} else if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetEccPublicKeyFromSubjectPublicKey(
|
||||
public_key1.data(), public_key1.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2,
|
||||
public_key_signature2.data(),
|
||||
public_key_signature2.size()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Verify that the private key from an OEM Cert cannot be loaded as a DRM
|
||||
* cert.
|
||||
*/
|
||||
TEST_F(OEMCryptoProv40Test, OEMPrivateKeyCannotBeDRMKey) {
|
||||
// Create an OEM Cert and save it for alter.
|
||||
Session s1;
|
||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||
ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1));
|
||||
ASSERT_EQ(s1.IsPublicKeySet(), true);
|
||||
s1.close();
|
||||
const std::vector<uint8_t> wrapped_oem_key1 = wrapped_oem_key_;
|
||||
// Now create a new OEM cert, load the second key, and try to load key1
|
||||
// as the DRM key.
|
||||
Session s2;
|
||||
ASSERT_NO_FATAL_FAILURE(s2.open());
|
||||
ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s2));
|
||||
s2.close();
|
||||
// Load the current key as the OEM key in session 3.
|
||||
Session s3;
|
||||
ASSERT_NO_FATAL_FAILURE(s3.open());
|
||||
// Now try to load key 1 as a DRM key. That should fail.
|
||||
ASSERT_EQ(OEMCrypto_ERROR_INVALID_KEY,
|
||||
OEMCrypto_LoadDRMPrivateKey(s3.session_id(), oem_key_type_,
|
||||
wrapped_oem_key1.data(),
|
||||
wrapped_oem_key1.size()));
|
||||
}
|
||||
|
||||
/** The private key for a DRM Cert cannot be loaded as an OEM Certificate. */
|
||||
TEST_F(OEMCryptoProv40Test, DRMPrivateKeyCannotBeOEMKey) {
|
||||
// Create a DRM cert and save it for later.
|
||||
Session s1;
|
||||
// Make sure the drm private key exists.
|
||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s1));
|
||||
ASSERT_NE(wrapped_drm_key_.size(), 0u);
|
||||
// Now try to load the drm private key as an OEM key.
|
||||
Session s2;
|
||||
ASSERT_NO_FATAL_FAILURE(s2.open());
|
||||
ASSERT_EQ(OEMCrypto_ERROR_INVALID_KEY,
|
||||
OEMCrypto_InstallOemPrivateKey(
|
||||
s2.session_id(), drm_key_type_,
|
||||
reinterpret_cast<const uint8_t*>(wrapped_drm_key_.data()),
|
||||
wrapped_drm_key_.size()));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv40Test, GetDeviceId) {
|
||||
OEMCryptoResult sts;
|
||||
std::vector<uint8_t> dev_id;
|
||||
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;
|
||||
// Device id should be stable. Query again.
|
||||
std::vector<uint8_t> dev_id2(dev_id_len);
|
||||
sts = OEMCrypto_GetDeviceID(dev_id2.data(), &dev_id_len);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(dev_id2, dev_id);
|
||||
}
|
||||
|
||||
// Verifies provisioning stage 1 OEM cert provisioning round trip works
|
||||
TEST_F(OEMCryptoProv40Test, ProvisionOemCert) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s));
|
||||
ASSERT_EQ(s.IsPublicKeySet(), true);
|
||||
}
|
||||
|
||||
// Verifies both provisioning stages OEM and DRM cert provisioning round trip
|
||||
// works
|
||||
TEST_F(OEMCryptoProv40Test, ProvisionDrmCert) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s));
|
||||
ASSERT_EQ(s.IsPublicKeySet(), true);
|
||||
}
|
||||
|
||||
/// @}
|
||||
} // namespace wvoec
|
||||
44
libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h
Normal file
44
libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// Test data for OEMCrypto unit tests.
|
||||
//
|
||||
#ifndef CDM_OEMCRYPTO_PROVISIONING_TEST_
|
||||
#define CDM_OEMCRYPTO_PROVISIONING_TEST_
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "oemcrypto_basic_test.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
namespace wvoec {
|
||||
|
||||
// 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)));
|
||||
}
|
||||
};
|
||||
|
||||
// This class is for tests that have an OEM Certificate instead of a keybox.
|
||||
class OEMCryptoProv30Test : public OEMCryptoClientTest {};
|
||||
|
||||
// This class is for tests that have boot certificate chain instead of a keybox.
|
||||
class OEMCryptoProv40Test : public OEMCryptoClientTest {};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEMCRYPTO_PROVISIONING_TEST_
|
||||
62
libwvdrmengine/oemcrypto/test/oemcrypto_resource_test.h
Normal file
62
libwvdrmengine/oemcrypto/test/oemcrypto_resource_test.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// Test data for OEMCrypto unit tests.
|
||||
//
|
||||
#ifndef CDM_OEMCRYPTO_RESOURCE_TEST_
|
||||
#define CDM_OEMCRYPTO_RESOURCE_TEST_
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_types.h"
|
||||
|
||||
namespace wvoec {
|
||||
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];
|
||||
}
|
||||
|
||||
// After API 16, we require 300 entries in the usage table. Before API 16, we
|
||||
// required 200.
|
||||
inline 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
|
||||
} // namespace wvoec
|
||||
#endif // CDM_OEMCRYPTO_RESOURCE_TEST_
|
||||
@@ -3,28 +3,6 @@
|
||||
// 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>
|
||||
@@ -58,6 +36,9 @@ using namespace std;
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
/// @addtogroup security
|
||||
/// @{
|
||||
|
||||
TEST_F(OEMCryptoClientTest,
|
||||
OEMCryptoMemoryAllocateSecureBufferForHugeBufferSize) {
|
||||
Session s;
|
||||
@@ -579,6 +560,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
OEMCryptoResult result = DecryptCENC();
|
||||
FreeSecureBuffers();
|
||||
// Closing the session and opening it for next iteration.
|
||||
// If it is last iteration, session will be closed in teardown method of
|
||||
// class.
|
||||
@@ -588,6 +570,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
return result;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
|
||||
|
||||
// Avoid double free when test teardown calls FreeSecureBuffers()
|
||||
MakeBuffers();
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
@@ -604,6 +589,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
OEMCryptoResult result = DecryptCENC();
|
||||
FreeSecureBuffers();
|
||||
// Closing the session and opening it for next iteration.
|
||||
// If it is last iteration, session will be closed in teardown method of
|
||||
// class.
|
||||
@@ -613,6 +599,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
return result;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
|
||||
|
||||
// Avoid double free when test teardown calls FreeSecureBuffers()
|
||||
MakeBuffers();
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificate,
|
||||
@@ -1190,5 +1179,6 @@ TEST_P(OEMCryptoUsageTableTest,
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
/// @}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef CDM_OEMCRYPTO_SESSION_TESTS_HELPER_
|
||||
#define CDM_OEMCRYPTO_SESSION_TESTS_HELPER_
|
||||
|
||||
#include <assert.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rand.h>
|
||||
@@ -55,3 +58,4 @@ class SessionUtil {
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
#endif // CDM_OEMCRYPTO_SESSION_TESTS_HELPER_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,9 @@
|
||||
// On Android, these features are not optional. This set of unit tests
|
||||
// verify that these features are implemented.
|
||||
//
|
||||
// In the file oemcrypto_test.cpp, the unit tests only verify correct
|
||||
// In the other oemcrypto test files, the unit tests only verify correct
|
||||
// functionality for functions that are implemented. Android devices must pass
|
||||
// unit tests in both files.
|
||||
// unit tests in this file also.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "oec_key_deriver.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "oemcrypto_basic_test.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "platform.h"
|
||||
@@ -140,19 +141,6 @@ constexpr size_t kInitialOtaKeyboxRequestSize = 8 * 1024; // 8 kB.
|
||||
// |use_test_key| parameter of OEMCrypto_GenerateOTARequest() is true.
|
||||
constexpr uint32_t kUseTestKey = 1;
|
||||
|
||||
// TODO(fredgc): duplicate code. Move to common util package.
|
||||
// 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());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetModelKey(const std::vector<uint8_t>& device_id) {
|
||||
std::vector<uint8_t> keymint_key(
|
||||
TestKeyPKCS8, TestKeyPKCS8 + wvutil::ArraySize(TestKeyPKCS8));
|
||||
|
||||
Reference in New Issue
Block a user