Update OEMCrypto test comments and logs
Merge from Widevine repo of http://go/wvgerrit/121886 This CL merges some changes from branch rvc-dev to sc-dev that prepared it for merge. One change is that the unit tests now say they are part of Android S instead of R. Bug: 180546871 Change-Id: I2ebbd8f7b8586389ebb75f3743a2dc2ad8caa214
This commit is contained in:
125
libwvdrmengine/oemcrypto/test/README.md
Normal file
125
libwvdrmengine/oemcrypto/test/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# OEMCrypto Unit Tests
|
||||
|
||||
## Basic Functionality Tests
|
||||
|
||||
Most unit tests in this category verify that the basic functionality of opening
|
||||
sessions, initializing and terminating the system, and reporting status work
|
||||
correctly.
|
||||
|
||||
## Decrypt Tests
|
||||
|
||||
The decrypt tests verify that encrypted data is correctly decrypted with the
|
||||
desired key. These tests cover a large variety of patterns, sample sizes, and
|
||||
subsample sizes.
|
||||
|
||||
## Secure Buffers
|
||||
|
||||
If OEMCrypto implements the function `OEMCrypto_AllocateSecureBuffer`, then all
|
||||
of the decrypt tests will also run with the output buffer being a secure
|
||||
buffer. If the function `OEMCrypto_SupportsDecryptHash` returns
|
||||
`OEMCrypto_CRC_Clear_Buffer`, then the secure buffer decryption will be verified
|
||||
with the CRC32 hash of the input data.
|
||||
|
||||
## Usage Table Tests
|
||||
|
||||
Usage table tests verify that the usage table is correctly procesed. The usage
|
||||
table is used to control reloading keys for offline playback, and for reporting
|
||||
secure stops for online playback.
|
||||
|
||||
## Duration Tests
|
||||
|
||||
Duration tests verify that license durations are enforced correctly. Most of
|
||||
this functionality can be met by keeping an accurate system time, and calling
|
||||
the ODK functions as described in the document "License Duration and Renewal".
|
||||
|
||||
## OEMCrypto Memory Unit Tests
|
||||
|
||||
### Objective
|
||||
|
||||
* Add OEMCrypto buffer overflow unit tests (indirect way of fuzzing) to verify
|
||||
OEMCrypto API behavior when the parameters passed to the API are out of
|
||||
range or not reasonable. The API can return an error code, but shouldn't
|
||||
crash.
|
||||
|
||||
* A lot of OEMCrypto APIs take buffers and their length as inputs to the APIs
|
||||
and we have added unit tests with buffers of varying lengths (small
|
||||
to huge) to verify API behavior which is an indirect and simplest way of
|
||||
fuzz testing to detect buffer overflows.
|
||||
|
||||
* Add the tests for OEMCrypto APIs with prefix `OEMCryptoMemory` in the
|
||||
following format. Huge length is set at 100 MB as of now.
|
||||
|
||||
```cpp
|
||||
for (size_t length=small_length; length<huge_length; length=length * 2) {
|
||||
Create buffer of size length.
|
||||
Call api expecting it not to crash or segfault.
|
||||
}
|
||||
```
|
||||
|
||||
* Add tests for OEMCrypto APIs with out of range values for length and offsets
|
||||
of OEMCryptoSubstring struct. This length and offset fields are used to read
|
||||
values from an input buffer in most of the APIs. This can cause buffer
|
||||
overflows if the length and offset fields are not validated against the
|
||||
input buffer.
|
||||
|
||||
### Background
|
||||
|
||||
* Security is the top priority for Widevine. We came up with a simple approach
|
||||
to catch most common issues with widevine's implementations. A simplest
|
||||
approach is to add OEMCrypto unit tests to verify OEMCrypto API behavior
|
||||
when the parameters are out of range, meaning for an unreasonable length
|
||||
which can cause buffer overflows. Most of the implementation either does not
|
||||
validate input length parameters or copies data to secure buffers out of TA
|
||||
space causing memory corruptions, buffer overflows. Partners who implement
|
||||
OEMCrypto implementations will run OEMCrypto unit tests as part of the
|
||||
process.
|
||||
|
||||
* We have added unit tests with parameters that can cause buffer overflows if
|
||||
the parameters are not validated. This way partners can catch issues
|
||||
earlier in the process when they run OEMCrypto unit tests. All the unit
|
||||
tests with prefix `OEMCryptoMemory` are added to test the above scenario.
|
||||
|
||||
### What to expect from these tests
|
||||
|
||||
* `OEMCryptoMemory*` tests are designed to fail if API doesn't have enough
|
||||
validations around input buffer lengths, parameters or OEMCryptoSubstring
|
||||
struct. If the API doesn't have validations which might lead to a crash, the
|
||||
test fails with a segfault or an appropriate crash message based on the API
|
||||
implementation.
|
||||
|
||||
* Find out for what buffer length, the API is crashing and then debugging the
|
||||
test against the OEMCrypto implementation should be able to provide
|
||||
information about the error.
|
||||
|
||||
* Another way to debug would be to compile the tests with sanitizer flags,
|
||||
which will be able to provide detailed information about the crash.
|
||||
|
||||
* Partners are expected to fix issues with the API so that the tests don't
|
||||
fail.
|
||||
|
||||
* As these tests run for varying lengths from small to huge buffer lengths,
|
||||
some of the tests might take longer to run(~3 minutes).
|
||||
|
||||
* `OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer*` tests which tries to
|
||||
call install keybox API with varying buffer lengths. This test by default
|
||||
is not compiled as it overwrites the keybox on the device. Uncomment,
|
||||
compile and run the tests only if you have ability to recover the keybox
|
||||
on device where the test is ran.
|
||||
|
||||
## Filtering out tests
|
||||
|
||||
The source code will check for functionality of OEMCrypto and filter out tests
|
||||
that are not required. For example, if a device uses a keybox, then Provisioning
|
||||
3.0 tests are skipped, and vice versa.
|
||||
|
||||
If you wish to skip slow tests because you only want to verify basic
|
||||
functionality, then you can set the environment variable `GTEST_FILTER`,
|
||||
as documented
|
||||
[here](https://github.com/google/googletest/blob/master/docs/advanced.md#running-a-subset-of-the-tests).
|
||||
|
||||
For example, to skip the duration tests, buffer overflow tests and long running
|
||||
stress tests, you would set
|
||||
|
||||
```
|
||||
GTEST_FILTER="*-*Duration*:*TimingTest*:*Memory*:*Huge*:*NonceFlood*:*ManyUsageEntries*:*Defrag*"
|
||||
```
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "log.h"
|
||||
#include "oemcrypto_fuzz_helper.h"
|
||||
#include "oemcrypto_fuzz_structs.h"
|
||||
#include "oemcrypto_overflow.h"
|
||||
|
||||
namespace wvoec {
|
||||
const size_t MAX_FUZZ_SAMPLE_SIZE = 5 * MB;
|
||||
@@ -137,13 +138,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
|
||||
// Copy sub sample data.
|
||||
sample_descriptions[i].subsamples = &subsamples[input_subsample_index];
|
||||
input_subsample_index += sample_descriptions[i].subsamples_length;
|
||||
if (AddOverflowUX(input_subsample_index,
|
||||
sample_descriptions[i].subsamples_length,
|
||||
&input_subsample_index)) {
|
||||
return 0;
|
||||
}
|
||||
if (input_subsample_index > subsamples.size()) return 0;
|
||||
} // Sample loop.
|
||||
|
||||
// Allocate input/output buffers for each sample description.
|
||||
vector<OEMCrypto_SharedMemory> input_buffer(total_input_data_length);
|
||||
RAND_bytes(input_buffer.data(), total_input_data_length);
|
||||
size_t input_buffer_index = 0;
|
||||
for (size_t i = 0; i < samples_length; i++) {
|
||||
sample_descriptions[i].buffers.input_data =
|
||||
|
||||
@@ -36,9 +36,7 @@ class OEMCryptoLicenseAPIFuzz : public InitializeFuzz {
|
||||
session_.GenerateNonce();
|
||||
}
|
||||
|
||||
~OEMCryptoLicenseAPIFuzz() {
|
||||
session_.close();
|
||||
}
|
||||
~OEMCryptoLicenseAPIFuzz() { session_.close(); }
|
||||
|
||||
LicenseRoundTrip& license_messages() { return license_messages_; }
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dispatcher.h"
|
||||
#include "marshaller_base.h"
|
||||
#include "transport_interface.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
void InitializeODKMessage(ODK_Message* message, uint8_t* data, size_t size) {
|
||||
ODK_Message_Impl* impl = (ODK_Message_Impl*)message;
|
||||
impl->base = data;
|
||||
impl->size = size;
|
||||
impl->capacity = size;
|
||||
impl->read_offset = 0;
|
||||
impl->status = MESSAGE_STATUS_OK;
|
||||
}
|
||||
|
||||
void OpenOEMCryptoTASession() {
|
||||
ODK_Message request;
|
||||
ODK_Message* response = NULL;
|
||||
uint8_t response_buffer[0x1000];
|
||||
uint8_t request_body[] = {
|
||||
0x06, // TAG_UINT32
|
||||
0x09, 0x00, 0x00, 0x00, // API value (0x09)
|
||||
0x01, // TAG_BOOL
|
||||
0x00, // value (false)
|
||||
0x0a // TAG_EOM
|
||||
};
|
||||
|
||||
InitializeODKMessage(&request, request_body, sizeof(request_body));
|
||||
|
||||
ODK_DispatchMessage(&request, &response);
|
||||
if (response != NULL) ODK_Transport_DeallocateMessage(response);
|
||||
}
|
||||
|
||||
void InitializeOEMCryptoTA() {
|
||||
ODK_Message init_request;
|
||||
ODK_Message* init_response = NULL;
|
||||
uint8_t response_buffer[0x1000];
|
||||
uint8_t init_request_body[] = {
|
||||
0x06, // TAG_UINT32
|
||||
0x01, 0x00, 0x00, 0x00, // API value(0x01)
|
||||
0x0a // TAG_EOM
|
||||
};
|
||||
|
||||
InitializeODKMessage(&init_request, init_request_body,
|
||||
sizeof(init_request_body));
|
||||
|
||||
ODK_DispatchMessage(&init_request, &init_response);
|
||||
if (init_response != NULL) ODK_Transport_DeallocateMessage(init_response);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
|
||||
ODK_InitializeDispatcher();
|
||||
InitializeOEMCryptoTA();
|
||||
OpenOEMCryptoTASession();
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
ODK_Message request;
|
||||
ODK_Message* response = NULL;
|
||||
unsigned char response_buffer[0x1000];
|
||||
|
||||
uint8_t* input = new uint8_t[size];
|
||||
memcpy(input, data, size);
|
||||
|
||||
InitializeODKMessage(&request, input, size);
|
||||
|
||||
ODK_DispatchMessage(&request, &response);
|
||||
if (response != NULL) ODK_Transport_DeallocateMessage(response);
|
||||
|
||||
delete[] input;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
@@ -129,6 +129,7 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*");
|
||||
if (!cast_receiver) FilterOut(&filter, "*CastReceiver*");
|
||||
if (!usage_table) FilterOut(&filter, "*UsageTable*");
|
||||
if (!usage_table) FilterOut(&filter, "*BadRange_pst*");
|
||||
if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*");
|
||||
if (provisioning_method
|
||||
!= OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*");
|
||||
|
||||
@@ -154,8 +154,7 @@ RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse, ResponseData>::
|
||||
constexpr size_t small_size = 42; // arbitrary.
|
||||
uint32_t session_id = session()->session_id();
|
||||
GetDefaultRequestSignatureAndCoreMessageLengths<PrepAndSignRequest>(
|
||||
session_id, required_message_size_, small_size, &gen_signature_length,
|
||||
&core_message_length);
|
||||
session_id, small_size, &gen_signature_length, &core_message_length);
|
||||
// Used to test request APIs with varying lengths of core message.
|
||||
core_message_length =
|
||||
std::max(core_message_length, required_core_message_size_);
|
||||
@@ -192,9 +191,8 @@ RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse, ResponseData>::
|
||||
|
||||
template <PrepAndSignRequest_t PrepAndSignRequest>
|
||||
void GetDefaultRequestSignatureAndCoreMessageLengths(
|
||||
uint32_t& session_id, size_t& required_message_size,
|
||||
const size_t& small_size, size_t* gen_signature_length,
|
||||
size_t* core_message_length) {
|
||||
uint32_t& session_id, const size_t& small_size,
|
||||
size_t* gen_signature_length, size_t* core_message_length) {
|
||||
vector<uint8_t> data(small_size);
|
||||
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
||||
ASSERT_EQ(
|
||||
|
||||
@@ -669,9 +669,8 @@ void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
|
||||
vector<uint8_t>& data);
|
||||
template <PrepAndSignRequest_t PrepAndSignRequest>
|
||||
void GetDefaultRequestSignatureAndCoreMessageLengths(
|
||||
uint32_t& session_id, size_t& required_message_size,
|
||||
const size_t& small_size, size_t* gen_signature_length,
|
||||
size_t* core_message_length);
|
||||
uint32_t& session_id, const size_t& small_size,
|
||||
size_t* gen_signature_length, size_t* core_message_length);
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEC_SESSION_UTIL_H_
|
||||
|
||||
@@ -137,6 +137,11 @@ void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
|
||||
!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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +175,17 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
|
||||
// const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576};
|
||||
// clang-format on
|
||||
|
||||
// Return a printable string from data. If all the characters are printable,
|
||||
// then just use the string. Otherwise, convert to hex.
|
||||
std::string MaybeHex(const uint8_t* data, size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (!isprint(data[i])) return "0x" + wvcdm::HexEncode(data, length);
|
||||
}
|
||||
return std::string(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
std::string MaybeHex(const std::vector<uint8_t>& data) {
|
||||
return MaybeHex(data.data(), data.size());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
@@ -202,6 +218,17 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
|
||||
Session s;
|
||||
s.open();
|
||||
OEMCrypto_DestBufferDesc output_descriptor;
|
||||
int secure_fd = kHugeRandomNumber;
|
||||
ASSERT_NE(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_FreeSecureBuffer(s.session_id(), &output_descriptor,
|
||||
secure_fd));
|
||||
s.close();
|
||||
}
|
||||
|
||||
/// @addtogroup basic
|
||||
/// @{
|
||||
|
||||
@@ -212,15 +239,20 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
*/
|
||||
TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
const std::string log_message =
|
||||
"OEMCrypto unit tests for API 16.4. Tests last updated 2020-10-07";
|
||||
"OEMCrypto unit tests for API 16.3 or 4. Tests last updated 2021-02-22";
|
||||
cout << " " << log_message << "\n";
|
||||
cout << " "
|
||||
<< "These tests are part of Android S."
|
||||
<< "\n";
|
||||
LOGI("%s", log_message.c_str());
|
||||
// If any of the following fail, then it is time to update the log message
|
||||
// above.
|
||||
EXPECT_EQ(ODK_MAJOR_VERSION, 16);
|
||||
// Note on minor versions. Widevine requires version 16.3 or greater for CE
|
||||
// CDM and Android devices. For CE CDM devices that do not support usage
|
||||
// tables, we strongly recommend 16.4.
|
||||
// tables, we strongly recommend 16.4. Note: This is the version of the ODK
|
||||
// library built into the tests, which might be different from the version
|
||||
// that is pre-compiled into liboemcrypto.so.
|
||||
EXPECT_GE(ODK_MINOR_VERSION, 3);
|
||||
EXPECT_LE(ODK_MINOR_VERSION, 4);
|
||||
EXPECT_EQ(kCurrentAPI, 16u);
|
||||
@@ -229,7 +261,9 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
ASSERT_EQ('L', level[0]);
|
||||
cout << " OEMCrypto Security Level is " << level << endl;
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
cout << " OEMCrypto API version is " << version << endl;
|
||||
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 {
|
||||
@@ -661,8 +695,8 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) {
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
|
||||
dest_buffer_descriptor.buffer.clear.address = output_buffer.data();
|
||||
dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size() - 1;
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
ASSERT_NE(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(),
|
||||
input_buffer.size(), &dest_buffer_descriptor,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
|
||||
@@ -896,8 +930,8 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
|
||||
uint8_t dev_id[128] = {0};
|
||||
size_t dev_id_len = 128;
|
||||
sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len);
|
||||
cout << " NormalGetDeviceId: dev_id = " << dev_id
|
||||
<< " len = " << dev_id_len << endl;
|
||||
cout << " NormalGetDeviceId: dev_id = "
|
||||
<< MaybeHex(dev_id, dev_id_len) << " len = " << dev_id_len << endl;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
@@ -1039,7 +1073,7 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
|
||||
}
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
dev_id.resize(dev_id_len);
|
||||
cout << " NormalGetDeviceId: dev_id = " << dev_id.data()
|
||||
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
|
||||
<< " len = " << dev_id_len << endl;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
@@ -2355,6 +2389,39 @@ TEST_P(OEMCryptoLicenseTest,
|
||||
!kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoLicenseTest,
|
||||
DecryptCENCForNumBytesClearPlusEncryptedOverflowsSize) {
|
||||
LoadLicense();
|
||||
OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[0].key_id,
|
||||
session_.license().keys[0].key_id_length,
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
|
||||
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 = 0xFFFFFFFFFFFFFFFF;
|
||||
|
||||
// Create the pattern description (always 0,0 for CTR)
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
||||
// Try to decrypt the data
|
||||
ASSERT_NE(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
|
||||
&pattern));
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoLicenseTest,
|
||||
OEMCryptoMemoryDecryptCENCForHugeNumBytesEncryptedAndBuffers) {
|
||||
TestDecryptCENCForHugeBufferLengths(
|
||||
@@ -3850,50 +3917,17 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
OEMCrypto_SUCCESS);
|
||||
}
|
||||
|
||||
void TestDecryptCENC() {
|
||||
OEMCryptoResult sts;
|
||||
void TestDecryptCENC() { ASSERT_EQ(DecryptCENC(), OEMCrypto_SUCCESS); }
|
||||
|
||||
// OEMCrypto only supports providing a decrypt hash for one sample.
|
||||
if (samples_.size() > 1) verify_crc_ = false;
|
||||
|
||||
// If supported, check the decrypt hashes.
|
||||
if (verify_crc_) {
|
||||
const TestSample& sample = samples_[0];
|
||||
|
||||
uint32_t hash =
|
||||
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
|
||||
ASSERT_EQ(OEMCrypto_SetDecryptHash(
|
||||
session_.session_id(), 1,
|
||||
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)),
|
||||
OEMCrypto_SUCCESS);
|
||||
}
|
||||
|
||||
// Build an array of just the sample descriptions.
|
||||
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
|
||||
sample_descriptions.reserve(samples_.size());
|
||||
for (TestSample& sample : samples_) {
|
||||
// This must be deferred until this point in case the test modifies the
|
||||
// buffer before testing decrypt.
|
||||
sample.description.buffers.input_data = sample.encrypted_buffer.data();
|
||||
// Append to the description array.
|
||||
sample_descriptions.push_back(sample.description);
|
||||
}
|
||||
|
||||
// Perform decryption using the test data that was previously set up.
|
||||
sts = DecryptFallbackChain::Decrypt(
|
||||
session_.session_id(), sample_descriptions.data(),
|
||||
sample_descriptions.size(), cipher_mode_, &pattern_);
|
||||
ASSERT_EQ(sts, OEMCrypto_SUCCESS);
|
||||
|
||||
// Validate the decrypted data.
|
||||
void ValidateDecryptedData() {
|
||||
for (TestSample& sample : samples_) {
|
||||
if (sample.description.buffers.output_descriptor.type ==
|
||||
OEMCrypto_BufferType_Clear) {
|
||||
const size_t total_size = sample.description.buffers.input_data_length;
|
||||
// To verify there is no buffer overrun after decrypting, look at the
|
||||
// padded bytes just after the data buffer that was written. It should
|
||||
// not have changed from the original 0xaa that we set in MakeBuffer
|
||||
// function.
|
||||
// padded bytes just after the data buffer that was written. It
|
||||
// should not have changed from the original 0xaa that we set in
|
||||
// MakeBuffer function.
|
||||
if (decrypt_inplace_) {
|
||||
EXPECT_EQ(std::count(sample.encrypted_buffer.begin() + total_size,
|
||||
sample.encrypted_buffer.end(), 0xaa),
|
||||
@@ -3919,6 +3953,41 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
}
|
||||
}
|
||||
|
||||
OEMCryptoResult DecryptCENC() {
|
||||
// OEMCrypto only supports providing a decrypt hash for one sample.
|
||||
if (samples_.size() > 1) verify_crc_ = false;
|
||||
|
||||
// If supported, check the decrypt hashes.
|
||||
if (verify_crc_) {
|
||||
const TestSample& sample = samples_[0];
|
||||
|
||||
uint32_t hash =
|
||||
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
|
||||
OEMCrypto_SetDecryptHash(session_.session_id(), 1,
|
||||
reinterpret_cast<const uint8_t*>(&hash),
|
||||
sizeof(hash));
|
||||
}
|
||||
|
||||
// Build an array of just the sample descriptions.
|
||||
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
|
||||
sample_descriptions.reserve(samples_.size());
|
||||
for (TestSample& sample : samples_) {
|
||||
// This must be deferred until this point in case the test modifies the
|
||||
// buffer before testing decrypt.
|
||||
sample.description.buffers.input_data = sample.encrypted_buffer.data();
|
||||
// Append to the description array.
|
||||
sample_descriptions.push_back(sample.description);
|
||||
}
|
||||
|
||||
// Perform decryption using the test data that was previously set up.
|
||||
OEMCryptoResult result = DecryptFallbackChain::Decrypt(
|
||||
session_.session_id(), sample_descriptions.data(),
|
||||
sample_descriptions.size(), cipher_mode_, &pattern_);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
ValidateDecryptedData();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parameters of test case
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern_;
|
||||
OEMCryptoCipherMode cipher_mode_;
|
||||
@@ -4094,7 +4163,6 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
|
||||
// max_sample_size.
|
||||
const size_t subsample_size =
|
||||
std::min(max_sample_size / max_num_subsamples + 1, max_subsample_size);
|
||||
|
||||
size_t bytes_remaining = max_sample_size;
|
||||
std::vector<SubsampleSize> subsample_sizes;
|
||||
while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) {
|
||||
@@ -4113,6 +4181,103 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
OEMCryptoMemoryDecryptCENCForHugeNumberOfSubSamples) {
|
||||
auto oemcrypto_function = [&](size_t number_of_subsamples) {
|
||||
std::vector<SubsampleSize> subsample_sizes;
|
||||
while (number_of_subsamples-- > 0) {
|
||||
subsample_sizes.push_back({1, 1});
|
||||
}
|
||||
SetSubsampleSizes(subsample_sizes);
|
||||
LoadLicense();
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
OEMCryptoResult result = DecryptCENC();
|
||||
// Closing the session and opening it for next iteration.
|
||||
// If it is last iteration, session will be closed in teardown method of
|
||||
// class.
|
||||
session_.close();
|
||||
session_.open();
|
||||
InstallTestRSAKey(&session_);
|
||||
return result;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
OEMCryptoMemoryCheckDecryptCENCStatusForHugeNumberOfSubSamples) {
|
||||
size_t number_of_subsamples = 10000;
|
||||
std::vector<SubsampleSize> subsample_sizes;
|
||||
while (number_of_subsamples-- > 0) {
|
||||
subsample_sizes.push_back({100, 100});
|
||||
}
|
||||
SetSubsampleSizes(subsample_sizes);
|
||||
LoadLicense();
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
// Build an array of just the sample descriptions.
|
||||
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
|
||||
sample_descriptions.reserve(samples_.size());
|
||||
for (TestSample& sample : samples_) {
|
||||
// This must be deferred until this point in case the test modifies the
|
||||
// buffer before testing decrypt.
|
||||
sample.description.buffers.input_data = sample.encrypted_buffer.data();
|
||||
// Append to the description array.
|
||||
sample_descriptions.push_back(sample.description);
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_DecryptCENC(
|
||||
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
|
||||
LOGD("Large number of subsamples test has return code %d", result);
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
OEMCryptoMemoryCheckDecryptCENCStatusForHugeSubSample) {
|
||||
std::vector<SubsampleSize> subsample_sizes;
|
||||
subsample_sizes.push_back({100000, 100000});
|
||||
SetSubsampleSizes(subsample_sizes);
|
||||
LoadLicense();
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
// Build an array of just the sample descriptions.
|
||||
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
|
||||
sample_descriptions.reserve(samples_.size());
|
||||
for (TestSample& sample : samples_) {
|
||||
// This must be deferred until this point in case the test modifies the
|
||||
// buffer before testing decrypt.
|
||||
sample.description.buffers.input_data = sample.encrypted_buffer.data();
|
||||
// Append to the description array.
|
||||
sample_descriptions.push_back(sample.description);
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_DecryptCENC(
|
||||
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
|
||||
LOGD("Large subsample test has return code %d", result);
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests,
|
||||
OEMCryptoMemoryDecryptCENCForHugeNumberOfSamples) {
|
||||
auto oemcrypto_function = [&](size_t number_of_samples) {
|
||||
std::vector<std::vector<SubsampleSize>> samples;
|
||||
std::vector<SubsampleSize> subsample_sizes;
|
||||
subsample_sizes.push_back({1, 1});
|
||||
while (number_of_samples-- > 0) {
|
||||
samples.push_back(subsample_sizes);
|
||||
}
|
||||
SetSampleSizes(samples);
|
||||
LoadLicense();
|
||||
MakeBuffers();
|
||||
EncryptData();
|
||||
OEMCryptoResult result = DecryptCENC();
|
||||
// Closing the session and opening it for next iteration.
|
||||
// If it is last iteration, session will be closed in teardown method of
|
||||
// class.
|
||||
session_.close();
|
||||
session_.open();
|
||||
InstallTestRSAKey(&session_);
|
||||
return result;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
|
||||
}
|
||||
|
||||
// Based on the resource rating, OEMCrypto should be able to handle the maximum
|
||||
// subsample size.
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
|
||||
|
||||
Reference in New Issue
Block a user