[ Cherry-pick of v19 http://go/wvgerrit/219592 ] [ Merge of http://go/wvgerrit/219554 ] The original CheckBuildInformation_OutputLengthAPI17 test was written with the assumption that the estimated length would not be too much larger than the real length of the build info; however this is not true for some vendors. This CL changes the short-buffer length to be based on a real build info length from a successful call to OEMCrypto. Bug: 411308060 Change-Id: I6504288ca59d7d41facaadc45adc76a5236826d9
1039 lines
40 KiB
C++
1039 lines
40 KiB
C++
// 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 <ctype.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <jsmn.h>
|
|
|
|
#include "OEMCryptoCENC.h"
|
|
#include "clock.h"
|
|
#include "log.h"
|
|
#include "oemcrypto_corpus_generator_helper.h"
|
|
#include "oemcrypto_resource_test.h"
|
|
#include "test_sleep.h"
|
|
|
|
void PrintTo(const jsmntype_t& type, std::ostream* out) {
|
|
switch (type) {
|
|
case JSMN_UNDEFINED:
|
|
*out << "Undefined";
|
|
return;
|
|
case JSMN_OBJECT:
|
|
*out << "Object";
|
|
return;
|
|
case JSMN_ARRAY:
|
|
*out << "Array";
|
|
return;
|
|
case JSMN_STRING:
|
|
*out << "String";
|
|
return;
|
|
case JSMN_PRIMITIVE:
|
|
*out << "Primitive";
|
|
return;
|
|
}
|
|
*out << "Unknown(" << static_cast<int>(type) << ')';
|
|
}
|
|
|
|
namespace wvoec {
|
|
namespace {
|
|
// Counts the number of ancestor tokens of the provided |root_index| token.
|
|
// The result does not count the root itself.
|
|
//
|
|
// JSMN tokens specify the count of immediate ancessor tokens, but
|
|
// not the total.
|
|
// - Primitives never have children
|
|
// - Strings have 0 if they are a value, and 1 if they are the
|
|
// name of an object member
|
|
// - Objects have the count of members (each key-value pair is 1,
|
|
// regardless of the value's children elements)
|
|
// - Arrays have the count of elements (regardless of the values members)
|
|
//
|
|
int32_t JsmnAncestorCount(const std::vector<jsmntok_t>& tokens,
|
|
int32_t root_index) {
|
|
if (root_index >= static_cast<int32_t>(tokens.size())) return 0;
|
|
int32_t count = 0;
|
|
int32_t iter = root_index;
|
|
int32_t remainder = 1;
|
|
while (remainder > 0 && iter < static_cast<int32_t>(tokens.size())) {
|
|
const int32_t child_count = tokens[iter].size;
|
|
remainder += child_count;
|
|
count += child_count;
|
|
iter++;
|
|
remainder--;
|
|
}
|
|
return count;
|
|
}
|
|
} // namespace
|
|
|
|
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) {
|
|
// Check for a early null termination. This is common for the device
|
|
// id in a keybox, which is padded with 0s.
|
|
const size_t c_len = strnlen(reinterpret_cast<const char*>(data), length);
|
|
// If there is any nonzero after the first zero, then just use hex.
|
|
for (size_t i = c_len; i < length; i++) {
|
|
if (data[i] != 0) return "0x" + wvutil::HexEncode(data, length);
|
|
}
|
|
for (size_t i = 0; i < c_len; i++) {
|
|
if (!isprint(data[i])) return "0x" + wvutil::HexEncode(data, length);
|
|
}
|
|
return std::string(reinterpret_cast<const char*>(data), c_len);
|
|
}
|
|
|
|
std::string MaybeHex(const std::vector<uint8_t>& data) {
|
|
return MaybeHex(data.data(), data.size());
|
|
}
|
|
|
|
// Get the Device's ID and return it in a printable format.
|
|
std::string 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) {
|
|
if (dev_id_len <= 0) return "NO DEVICE ID";
|
|
dev_id.resize(dev_id_len);
|
|
sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len);
|
|
}
|
|
if (sts != OEMCrypto_SUCCESS) return "NO DEVICE ID";
|
|
dev_id.resize(dev_id_len);
|
|
return MaybeHex(dev_id);
|
|
}
|
|
|
|
/// @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.
|
|
*
|
|
* The log message should be updated by Widevine with every release so that it
|
|
* is easier to verify which version of the tests a partner is running. Widevine
|
|
* should change the API version number when the API changes, but the unit tests
|
|
* might be updated more frequently, and are only tracked by the date of the
|
|
* last change.
|
|
*/
|
|
TEST_F(OEMCryptoClientTest, VersionNumber) {
|
|
const std::string log_message =
|
|
"OEMCrypto unit tests for API 19.1. Tests last updated 2024-03-25";
|
|
cout << " " << log_message << "\n";
|
|
cout << " "
|
|
<< "These tests are part of Android U."
|
|
<< "\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, 19);
|
|
EXPECT_EQ(ODK_MINOR_VERSION, 1);
|
|
EXPECT_EQ(kCurrentAPI, static_cast<unsigned>(ODK_MAJOR_VERSION));
|
|
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;
|
|
cout << " OEMCrypto Device ID is '" << GetDeviceId() << "'"
|
|
<< 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);
|
|
}
|
|
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);
|
|
}
|
|
const std::string comma = ",";
|
|
const std::string pretty_comma = ",\n ";
|
|
std::string::size_type pos = 0;
|
|
while ((pos = build_info.find(comma, pos)) != std::string::npos) {
|
|
build_info.replace(pos, comma.size(), pretty_comma);
|
|
pos += pretty_comma.size();
|
|
}
|
|
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) {
|
|
if (wvoec::global_features.api_version < 17) {
|
|
GTEST_SKIP() << "Test for versions 17 and up only.";
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Verifies that OEMCrypto_BuildInformation() is behaving as expected
|
|
// by assigning appropriate values to the build info size.
|
|
TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputLengthAPI17) {
|
|
if (wvoec::global_features.api_version < 17) {
|
|
GTEST_SKIP() << "Test for versions 17 and up only.";
|
|
}
|
|
|
|
constexpr size_t kZero = 0;
|
|
constexpr char kNullChar = '\0';
|
|
|
|
// Allocating single byte to avoid potential null dereference.
|
|
std::string build_info(1, kNullChar);
|
|
size_t build_info_length = 0;
|
|
|
|
OEMCryptoResult result =
|
|
OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
|
|
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER);
|
|
ASSERT_GT(build_info_length, kZero)
|
|
<< "Signaling ERROR_SHORT_BUFFER should have assigned a length";
|
|
|
|
// Try again using the size they provided, ensuring that it
|
|
// is successful.
|
|
const size_t initial_estimate_length = build_info_length;
|
|
build_info.assign(build_info_length, kNullChar);
|
|
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
ASSERT_EQ(result, OEMCrypto_SUCCESS)
|
|
<< "initial_estimate_length = " << initial_estimate_length
|
|
<< ", build_info_length (output) = " << build_info_length;
|
|
ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty";
|
|
// Ensure the real length is within the size originally specified.
|
|
// OK if final length is smaller than estimated length.
|
|
ASSERT_LE(build_info_length, initial_estimate_length);
|
|
const size_t expected_length = build_info_length;
|
|
|
|
// Force a ERROR_SHORT_BUFFER using a non-zero value.
|
|
// Note: It is assumed that vendors will provide more than a single
|
|
// character of info.
|
|
const size_t short_length = (expected_length >= 2) ? expected_length / 2 : 1;
|
|
build_info.assign(short_length, kNullChar);
|
|
build_info_length = build_info.size();
|
|
|
|
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER)
|
|
<< "short_length = " << short_length
|
|
<< ", expected_length = " << expected_length << ", build_info_length"
|
|
<< build_info_length;
|
|
// OEM specified build info length should be larger than the
|
|
// original length if returning ERROR_SHORT_BUFFER.
|
|
ASSERT_GT(build_info_length, short_length);
|
|
|
|
// Final attempt with a buffer large enough buffer, padding to
|
|
// ensure the caller truncates.
|
|
constexpr size_t kBufferPadSize = 42;
|
|
const size_t oversize_length = expected_length + kBufferPadSize;
|
|
build_info.assign(oversize_length, kNullChar);
|
|
build_info_length = build_info.size();
|
|
|
|
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
|
|
ASSERT_EQ(result, OEMCrypto_SUCCESS)
|
|
<< "oversize_length = " << oversize_length
|
|
<< ", expected_length = " << expected_length
|
|
<< ", build_info_length (output) = " << build_info_length;
|
|
// Ensure not empty.
|
|
ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty";
|
|
// Ensure it was truncated down from the padded length.
|
|
ASSERT_LT(build_info_length, oversize_length)
|
|
<< "Should have truncated from oversized buffer: expected_length = "
|
|
<< expected_length;
|
|
// Ensure that length is equal to the length of the previous
|
|
// successful call.
|
|
ASSERT_EQ(build_info_length, expected_length);
|
|
}
|
|
|
|
// Verifies that OEMCrypto_BuildInformation() is behaving as expected
|
|
// by checking the resulting contents.
|
|
// Does not validate whether output if valid JSON for v18.
|
|
TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputContentAPI17) {
|
|
if (wvoec::global_features.api_version < 17) {
|
|
GTEST_SKIP() << "Test for versions 17 and up only.";
|
|
}
|
|
|
|
constexpr size_t kZero = 0;
|
|
constexpr char kNullChar = '\0';
|
|
|
|
// Allocating single byte to avoid potential null dereference.
|
|
std::string build_info(1, kNullChar);
|
|
size_t build_info_length = 0;
|
|
OEMCryptoResult result =
|
|
OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER);
|
|
ASSERT_GT(build_info_length, kZero)
|
|
<< "Signaling ERROR_SHORT_BUFFER should have assigned a length";
|
|
|
|
// Expect successful acquisition of build information.
|
|
const size_t expected_length = build_info_length;
|
|
build_info.assign(expected_length, kNullChar);
|
|
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
|
|
ASSERT_EQ(result, OEMCrypto_SUCCESS)
|
|
<< "expected_length = " << expected_length
|
|
<< ", build_info_length = " << build_info_length;
|
|
// Ensure not empty.
|
|
ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty";
|
|
// Ensure the real length is within the size originally specified.
|
|
ASSERT_LE(build_info_length, expected_length)
|
|
<< "Cannot specify success if buffer was too small";
|
|
build_info.resize(build_info_length);
|
|
|
|
// Ensure there isn't a trailing null byte.
|
|
ASSERT_NE(build_info.back(), kNullChar)
|
|
<< "Build info must not contain trailing null byte";
|
|
|
|
// Ensure all build info characters are printable, or a limited
|
|
// set of white space characters (case of JSON build info).
|
|
const auto is_valid_build_info_white_space = [](const char& ch) -> bool {
|
|
constexpr char kSpace = ' ';
|
|
constexpr char kLineFeed = '\n';
|
|
constexpr char kTab = '\t';
|
|
return ch == kLineFeed || ch == kTab || ch == kSpace;
|
|
};
|
|
const auto is_valid_build_info_char = [&](const char& ch) -> bool {
|
|
return ::isprint(ch) || is_valid_build_info_white_space(ch);
|
|
};
|
|
ASSERT_TRUE(std::all_of(build_info.begin(), build_info.end(),
|
|
is_valid_build_info_char))
|
|
<< "Build info is not printable: " << wvutil::b2a_hex(build_info);
|
|
|
|
// Ensure build info isn't just white space.
|
|
ASSERT_FALSE(std::all_of(build_info.begin(), build_info.end(),
|
|
is_valid_build_info_white_space))
|
|
<< "Build info is just white space: " << wvutil::b2a_hex(build_info);
|
|
}
|
|
|
|
TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
|
|
if (wvoec::global_features.api_version < 18) {
|
|
GTEST_SKIP() << "Test for versions 18 and up only.";
|
|
}
|
|
constexpr char kNullChar = '\0';
|
|
constexpr size_t kZero = 0;
|
|
|
|
// Step 1: Get Build Info
|
|
size_t buffer_length = 0;
|
|
// OEMCrypto must allow |buffer| to be null so long as |buffer_length|
|
|
// is provided and initially set to zero.
|
|
OEMCryptoResult result = OEMCrypto_BuildInformation(nullptr, &buffer_length);
|
|
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
|
|
ASSERT_GT(buffer_length, kZero);
|
|
|
|
std::string build_info(buffer_length, kNullChar);
|
|
const size_t max_final_size = buffer_length;
|
|
result = OEMCrypto_BuildInformation(&build_info[0], &buffer_length);
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
|
ASSERT_LE(buffer_length, max_final_size);
|
|
build_info.resize(buffer_length);
|
|
|
|
// Step 2: Parse as JSON
|
|
jsmn_parser p;
|
|
jsmn_init(&p);
|
|
std::vector<jsmntok_t> tokens;
|
|
const 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);
|
|
const 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;
|
|
|
|
// Step 3a: Ensure info is a single JSON object.
|
|
const jsmntok_t& object_token = tokens[0];
|
|
ASSERT_EQ(object_token.type, JSMN_OBJECT)
|
|
<< "Build info is not a JSON object: " << build_info;
|
|
|
|
// Step 3b: Verify schema of defined fields.
|
|
|
|
// Required fields must be present in the build information,
|
|
// and be of the correct type.
|
|
const std::map<std::string, jsmntype_t> kRequiredFields = {
|
|
// SOC manufacturer name
|
|
{"soc_vendor", JSMN_STRING},
|
|
// SOC model name
|
|
{"soc_model", JSMN_STRING},
|
|
// TA version in string format eg "1.12.3+tag", "2.0"
|
|
{"ta_ver", JSMN_STRING},
|
|
// [bool] Whether TA was built with Widevine's OPK
|
|
{"uses_opk", JSMN_PRIMITIVE},
|
|
// Trusted OS intended to run the TA, eg "Trusty", "QSEE", "OP-TEE"
|
|
{"tee_os", JSMN_STRING},
|
|
// Version of Trusted OS intended to run the TA
|
|
{"tee_os_ver", JSMN_STRING},
|
|
// [bool] Whether this is a debug build of the TA
|
|
// Not forcing behavior until implementations fix
|
|
// them self
|
|
// {"is_debug", JSMN_PRIMITIVE},
|
|
};
|
|
|
|
const std::string kSpecialCaseReeKey = "ree";
|
|
|
|
// Optional fields may be present in the build information;
|
|
// if they are, then the must be the correct type.
|
|
const std::map<std::string, jsmntype_t> kOptionalFields = {
|
|
// Name of company or entity that provides OEMCrypto.
|
|
{"implementor", JSMN_STRING},
|
|
// Git commit hash of the code repository.
|
|
{"git_commit", JSMN_STRING},
|
|
// ISO 8601 formatted timestamp of the time the TA was compiled
|
|
{"build_timestamp", JSMN_STRING},
|
|
// Whether this was built with FACTORY_MODE_ONLY defined
|
|
{"is_factory_mode", JSMN_PRIMITIVE},
|
|
// ... provide information about liboemcrypto.so
|
|
// Special case, see kOptionalReeFields for details.
|
|
{kSpecialCaseReeKey, JSMN_OBJECT},
|
|
// Technically required, but several implementations
|
|
// do not implement this fields.
|
|
{"is_debug", JSMN_PRIMITIVE},
|
|
};
|
|
|
|
// A set of the required fields found when examining the
|
|
// build information, use to verify all fields are present.
|
|
std::set<std::string> found_required_fields;
|
|
// Stores the tokens of the "ree" field, if set, used to
|
|
// validate its content.
|
|
std::vector<jsmntok_t> ree_tokens;
|
|
bool has_ree_info = false;
|
|
|
|
// Start: first object key token
|
|
// Condition: key-value pair (2 tokens)
|
|
// Iter: next key-value pair (2 tokens)
|
|
for (int32_t i = 1; (i + 1) < jsmn_result; i += 2) {
|
|
// JSMN objects consist of pairs of key-value pairs (keys are always
|
|
// JSMN_STRING).
|
|
const jsmntok_t& key_token = tokens[i];
|
|
ASSERT_EQ(key_token.type, JSMN_STRING)
|
|
<< "Bad object key: i = " << i << ", build_info = " << build_info;
|
|
const jsmntok_t& value_token = tokens[i + 1];
|
|
|
|
const std::string key =
|
|
build_info.substr(key_token.start, key_token.end - key_token.start);
|
|
if (kRequiredFields.find(key) != kRequiredFields.end()) {
|
|
ASSERT_EQ(value_token.type, kRequiredFields.at(key))
|
|
<< "Unexpected required field type: field = " << key
|
|
<< ", build_info = " << build_info;
|
|
found_required_fields.insert(key);
|
|
} else if (kOptionalFields.find(key) != kOptionalFields.end()) {
|
|
ASSERT_EQ(value_token.type, kOptionalFields.at(key))
|
|
<< "Unexpected optional field type: field = " << key
|
|
<< ", build_info = " << build_info;
|
|
} // Do not validate vendor fields.
|
|
|
|
if (key == kSpecialCaseReeKey) {
|
|
// Store the tokens of the "ree" field for additional validation.
|
|
const int32_t first_ree_field_index = i + 2;
|
|
const int32_t ree_token_count = JsmnAncestorCount(tokens, i + 1);
|
|
const auto first_ree_field_iter = tokens.begin() + first_ree_field_index;
|
|
ree_tokens.assign(first_ree_field_iter,
|
|
first_ree_field_iter + ree_token_count);
|
|
has_ree_info = true;
|
|
}
|
|
|
|
// Skip potential nested tokens.
|
|
i += JsmnAncestorCount(tokens, i + 1);
|
|
}
|
|
|
|
// Step 3c: Ensure all required fields were found.
|
|
if (found_required_fields.size() != kRequiredFields.size()) {
|
|
// Generate a list of all the missing fields.
|
|
std::string missing_fields;
|
|
for (const auto& required_field : kRequiredFields) {
|
|
if (found_required_fields.find(required_field.first) !=
|
|
found_required_fields.end())
|
|
continue;
|
|
if (!missing_fields.empty()) {
|
|
missing_fields.append(", ");
|
|
}
|
|
missing_fields.push_back('"');
|
|
missing_fields.append(required_field.first);
|
|
missing_fields.push_back('"');
|
|
}
|
|
|
|
FAIL() << "Build info JSON object does not contain all required keys; "
|
|
<< "missing_fields = [" << missing_fields
|
|
<< "], build_info = " << build_info;
|
|
return;
|
|
}
|
|
|
|
// If no "ree" field tokens, then end here.
|
|
if (!has_ree_info) return;
|
|
// Step 4a: Verify "ree" object scheme.
|
|
ASSERT_FALSE(ree_tokens.empty())
|
|
<< "REE field was specified, but contents were empty: build_info = "
|
|
<< build_info;
|
|
|
|
// The optional field "ree", if present, must follow the required
|
|
// format.
|
|
const std::map<std::string, jsmntype_t> kReeRequiredFields = {
|
|
// liboemcrypto.so version in string format eg "2.15.0+tag"
|
|
{"liboemcrypto_ver", JSMN_STRING},
|
|
// git hash of code that compiled liboemcrypto.so
|
|
{"git_commit", JSMN_STRING},
|
|
// ISO 8601 timestamp for when liboemcrypto.so was built
|
|
{"build_timestamp", JSMN_STRING}};
|
|
|
|
found_required_fields.clear();
|
|
for (int32_t i = 0; (i + 1) < static_cast<int32_t>(ree_tokens.size());
|
|
i += 2) {
|
|
const jsmntok_t& key_token = ree_tokens[i];
|
|
ASSERT_EQ(key_token.type, JSMN_STRING)
|
|
<< "Bad REE object key: i = " << i << ", build_info = " << build_info;
|
|
const jsmntok_t& value_token = ree_tokens[i + 1];
|
|
|
|
const std::string key =
|
|
build_info.substr(key_token.start, key_token.end - key_token.start);
|
|
if (kReeRequiredFields.find(key) != kReeRequiredFields.end()) {
|
|
ASSERT_EQ(value_token.type, kReeRequiredFields.at(key))
|
|
<< "Unexpected optional REE field type: ree_field = " << key
|
|
<< ", build_info = " << build_info;
|
|
found_required_fields.insert(key);
|
|
} // Do not validate vendor fields.
|
|
|
|
// Skip potential nested tokens.
|
|
i += JsmnAncestorCount(ree_tokens, i + 1);
|
|
}
|
|
|
|
// Step 4b: Ensure all required fields of the "ree" object were found.
|
|
if (found_required_fields.size() == kReeRequiredFields.size()) return;
|
|
// Generate a list of all the missing REE fields.
|
|
std::string missing_ree_fields;
|
|
for (const auto& required_field : kReeRequiredFields) {
|
|
if (found_required_fields.find(required_field.first) !=
|
|
found_required_fields.end())
|
|
continue;
|
|
if (!missing_ree_fields.empty()) {
|
|
missing_ree_fields.append(", ");
|
|
}
|
|
missing_ree_fields.push_back('"');
|
|
missing_ree_fields.append(required_field.first);
|
|
missing_ree_fields.push_back('"');
|
|
}
|
|
|
|
FAIL() << "REE info JSON object does not contain all required keys; "
|
|
<< "missing_ree_fields = [" << missing_ree_fields
|
|
<< "], build_info = " << 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) {
|
|
if (wvoec::global_features.api_version < 17) {
|
|
GTEST_SKIP() << "Test for versions 17 and up only.";
|
|
}
|
|
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
|