Source release 16.4.0
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
|
||||
/* source code may only be used and distributed under the Widevine Master */
|
||||
/* License Agreement. */
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
/*********************************************************************
|
||||
* OEMCryptoCENCCommon.h
|
||||
@@ -20,7 +20,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @addtogroup common_types
|
||||
/// @{
|
||||
|
||||
/* clang-format off */
|
||||
/** Error and result codes returned by OEMCrypto functions. */
|
||||
typedef enum OEMCryptoResult {
|
||||
OEMCrypto_SUCCESS = 0,
|
||||
OEMCrypto_ERROR_INIT_FAILED = 1,
|
||||
@@ -94,8 +98,7 @@ typedef enum OEMCryptoResult {
|
||||
} OEMCryptoResult;
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* OEMCrypto_Usage_Entry_Status.
|
||||
/**
|
||||
* Valid values for status in the usage table.
|
||||
*/
|
||||
typedef enum OEMCrypto_Usage_Entry_Status {
|
||||
@@ -106,7 +109,7 @@ typedef enum OEMCrypto_Usage_Entry_Status {
|
||||
kInactiveUnused = 4,
|
||||
} OEMCrypto_Usage_Entry_Status;
|
||||
|
||||
/*
|
||||
/**
|
||||
* OEMCrypto_LicenseType is used in the license message to indicate if the key
|
||||
* objects are for content keys, or for entitlement keys.
|
||||
*/
|
||||
@@ -122,9 +125,7 @@ typedef enum OEMCrypto_PrivateKeyType {
|
||||
OEMCrypto_ECC_Private_Key = 1,
|
||||
} OEMCrypto_PrivateKeyType;
|
||||
|
||||
/*
|
||||
* OEMCrypto_Substring
|
||||
*
|
||||
/**
|
||||
* Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and
|
||||
* other functions which must verify that a parameter is contained within a
|
||||
* signed message.
|
||||
@@ -134,23 +135,22 @@ typedef struct {
|
||||
size_t length;
|
||||
} OEMCrypto_Substring;
|
||||
|
||||
/*
|
||||
* OEMCrypto_KeyObject
|
||||
/**
|
||||
* Points to the relevant fields for a content key. The fields are extracted
|
||||
* from the License Response message offered to OEMCrypto_LoadKeys(). Each
|
||||
* field points to one of the components of the key. Key data, key control,
|
||||
* and both IV fields are 128 bits (16 bytes):
|
||||
* key_id - the unique id of this key.
|
||||
* key_id_length - the size of key_id. OEMCrypto may assume this is at
|
||||
* @param key_id: the unique id of this key.
|
||||
* @param key_id_length: the size of key_id. OEMCrypto may assume this is at
|
||||
* most 16. However, OEMCrypto shall correctly handle key id lengths
|
||||
* from 1 to 16 bytes.
|
||||
* key_data_iv - the IV for performing AES-128-CBC decryption of the
|
||||
* @param key_data_iv: the IV for performing AES-128-CBC decryption of the
|
||||
* key_data field.
|
||||
* key_data - the key data. It is encrypted (AES-128-CBC) with the
|
||||
* @param key_data - the key data. It is encrypted (AES-128-CBC) with the
|
||||
* session's derived encrypt key and the key_data_iv.
|
||||
* key_control_iv - the IV for performing AES-128-CBC decryption of the
|
||||
* @param key_control_iv: the IV for performing AES-128-CBC decryption of the
|
||||
* key_control field.
|
||||
* key_control - the key control block. It is encrypted (AES-128-CBC) with
|
||||
* @param key_control: the key control block. It is encrypted (AES-128-CBC) with
|
||||
* the content key from the key_data field.
|
||||
*
|
||||
* The memory for the OEMCrypto_KeyObject fields is allocated and freed
|
||||
@@ -164,8 +164,10 @@ typedef struct {
|
||||
OEMCrypto_Substring key_control;
|
||||
} OEMCrypto_KeyObject;
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */
|
||||
#endif // WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||
|
||||
@@ -139,9 +139,76 @@
|
||||
```
|
||||
*Note*: Make sure LLVMFuzzerTestOneInput calls the function you want to fuzz.
|
||||
|
||||
* Add a new target to oemcrypto_fuzztests.gyp file and follow instructions in
|
||||
* Add a new target to oemcrypto_fuzztests.gyp file and follow instructions in
|
||||
[testing fuzzer locally](#testing-fuzzer-locally) to build and test locally.
|
||||
|
||||
## Building OEMCrypto fuzz scripts and uploading them to Google Cloud Storage:
|
||||
|
||||
* We are using Google Cloud Buid (GCB) in order to setup continuous
|
||||
integration which uploads OEMCrypto fuzz binaries to Google Cloud Storage.
|
||||
GCB expects build script in form of a docker image that is uploaded to
|
||||
Google Container Registry(GCR).
|
||||
|
||||
The cloud build scripts (docker images) for widevine projects are
|
||||
[here](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker/README.md)
|
||||
|
||||
Refer to README of the project to setup a new docker image and uploading
|
||||
the image to GCR.
|
||||
|
||||
* Git on borg repository needs to be integrated with GCB and a git trigger
|
||||
needs to be set up in order to achieve continuous integration. Git trigger
|
||||
will mention which docker image the GCB needs to use in order to build fuzz
|
||||
binaries. GCB searches for docker images from GCR.
|
||||
|
||||
Design document lists the steps to create a git trigger.
|
||||
|
||||
### Adding a new fuzz script to the build script:
|
||||
|
||||
* In order to update build script such as adding a new fuzzer to build script,
|
||||
we need to update the build script in docker image from cloud repository.
|
||||
[Build script.](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker
|
||||
/cloud_build/oemcrypto/release/ubuntu/fuzz/build.sh)
|
||||
|
||||
Add the new fuzz script name to fuzzers variable and follow steps in README
|
||||
to upload new docker image. Make sure you update the tag to be higher than
|
||||
latest version in GCR.
|
||||
|
||||
Run the following command from your machine to update the docker image tag
|
||||
in the git trigger.
|
||||
|
||||
```shell
|
||||
stubby call --rpc_creds_file=/tmp/mint.txt \
|
||||
blade:alphasource-ci-proctor-metadata-service-prod \
|
||||
ProctorMetadataService.UpdateTrigger --proto2 <<EOF
|
||||
trigger {
|
||||
cloud_project_number: 257246079067
|
||||
name: "cdm-git-trigger"
|
||||
id: "e8939c9a-d971-4c05-91b5-e0544abf872b"
|
||||
state: LIVE
|
||||
git_trigger {
|
||||
url: "https://widevine-internal.googlesource.com/cdm"
|
||||
branch_name: "master"
|
||||
}
|
||||
build_configs {
|
||||
build {
|
||||
steps {
|
||||
name: "gcr.io/google.com/blockbuster-1154/
|
||||
cloud-build-oemcrypto-release-ubuntu-fuzz:LATEST_TAG_VERSION"
|
||||
}
|
||||
}
|
||||
}
|
||||
result_config {
|
||||
email_config {
|
||||
notify_condition {
|
||||
condition: ON_FAILURE
|
||||
}
|
||||
to_address: "wideving-engprod@google.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Generate code coverage reports locally
|
||||
|
||||
* Code coverage is a means of measuring fuzzer performance. We want to make
|
||||
@@ -152,36 +219,29 @@
|
||||
generated manually. Future plan is to build a dashboard for git on borg
|
||||
coverage reports.
|
||||
|
||||
* In order to generate coverage reports, we need to compile fuzzer binary with
|
||||
flags to enable coverage. We can remove
|
||||
`-fsanitize=fuzzer,address,undefined` from oemcrypto_fuzztests.gypi file as
|
||||
that is needed only while fuzzing. Add following flags to both cflags_cc and
|
||||
ldflags of oemcrypto_fuzztests.gypi and build fuzz binaries as mentioned in
|
||||
`Testing fuzzer locally` section.
|
||||
### Generate code coverage reports using script from Google cloud build
|
||||
* A docker image with script to generate code coverage reports for oemcrypto
|
||||
fuzz scripts is linked with a GCB trigger
|
||||
`oemcrypto-fuzzing-code-coverage-git-trigger`. More information about clang
|
||||
source based coverage can be found
|
||||
[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
|
||||
|
||||
```
|
||||
'-fprofile-instr-generate',
|
||||
'-fcoverage-mapping',
|
||||
```
|
||||
* This trigger when invoked compiles oemcrypto fuzz scripts with clang source
|
||||
based code coverage enabled, downloads latest corpus from cluster fuzz
|
||||
for the respective fuzzer, generates and uploads code coverage html reports
|
||||
to [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=).
|
||||
|
||||
* We need to run fuzzer binary against the corpus downloaded from
|
||||
[clusterfuzz](https://clusterfuzz.corp.google.com/fuzzer-stats). Clock on
|
||||
download link from corpus_backup column. Use gsutil command to download the
|
||||
entire corpus for the fuzz binary.
|
||||
* The trigger can be invoked manually using cloud scheduler
|
||||
`oemcrypto_fuzzing_code_coverage_reports`.
|
||||
|
||||
* Use the following commands to generate raw profile data file with coverage
|
||||
information and generate a html coverage report for a single fuzzer. More
|
||||
information about clang source based coverage can be found
|
||||
[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html). Follow
|
||||
[this](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) for steps
|
||||
to combine code coverage reports of multiple fuzzers.
|
||||
* In order to generate latest code coverage reports from master branch,
|
||||
go to pantheon->cloud scheduler->oemcrypto_fuzzing_code_coverage_reports and
|
||||
click on `RUN NOW` button.
|
||||
|
||||
```shell
|
||||
# Run fuzz binary against corpus backup to generate default.profraw file.
|
||||
$ ./out/Default/fuzz_binary path/to/corpus/backup -runs=0
|
||||
# Index raw profile files to generate coverage reports.
|
||||
$ llvm-profdata merge -sparse default.profraw -o default.profdata
|
||||
# Generate html coverage file.
|
||||
$ llvm-cov show ./out/Default/fuzz_binary -format=html \
|
||||
-instr-profile=default.profdata -o default.html
|
||||
```
|
||||
* The above step should invoke a google cloud build. Go to cloud build console
|
||||
and find latest build job with Trigger Name
|
||||
`oemcrypto-fuzzing-code-coverage-git-trigger`.
|
||||
|
||||
* Once the build job is successful, latest code coverage reports can be
|
||||
downloaded from [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=).
|
||||
The coverage report folder uploaded to GCS is appended with timestamp.
|
||||
182
oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc
Normal file
182
oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "FuzzedDataProvider.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oemcrypto_fuzz_helper.h"
|
||||
#include "oemcrypto_fuzz_structs.h"
|
||||
|
||||
namespace wvoec {
|
||||
const size_t MAX_FUZZ_SAMPLE_SIZE = 5 * MB;
|
||||
// Free dynamic memory allocated by fuzzer script.
|
||||
void FreeOutputBuffers(OEMCrypto_SESSION session_id,
|
||||
OEMCrypto_SampleDescription* sample_description,
|
||||
size_t sample_index, int* secure_fd_array) {
|
||||
for (size_t i = 0; i < sample_index; i++) {
|
||||
OEMCrypto_DestBufferDesc fuzzed_output_descriptor =
|
||||
sample_description[i].buffers.output_descriptor;
|
||||
switch (fuzzed_output_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear: {
|
||||
delete[] fuzzed_output_descriptor.buffer.clear.address;
|
||||
break;
|
||||
}
|
||||
case OEMCrypto_BufferType_Secure: {
|
||||
OEMCrypto_FreeSecureBuffer(session_id, &fuzzed_output_descriptor,
|
||||
secure_fd_array[i]);
|
||||
break;
|
||||
}
|
||||
case OEMCrypto_BufferType_Direct: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to initialize output buffer pointers by allocating memory.
|
||||
// Limiting output buffer size to 5 MB as 4 MB is maximum size specified
|
||||
// by resource rating tier documentation.
|
||||
bool InitializeOutputBuffers(OEMCrypto_SESSION session_id,
|
||||
OEMCrypto_DestBufferDesc& output_descriptor,
|
||||
size_t sample_index,
|
||||
vector<int>& secure_fd_array) {
|
||||
switch (output_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear: {
|
||||
output_descriptor.buffer.clear
|
||||
.address = new OEMCrypto_SharedMemory[std::min(
|
||||
MAX_FUZZ_SAMPLE_SIZE, output_descriptor.buffer.clear.address_length)];
|
||||
return true;
|
||||
}
|
||||
case OEMCrypto_BufferType_Secure: {
|
||||
int* secure_fd;
|
||||
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
|
||||
session_id,
|
||||
std::min(MAX_FUZZ_SAMPLE_SIZE,
|
||||
output_descriptor.buffer.secure.handle_length),
|
||||
&output_descriptor, secure_fd);
|
||||
if (sts == OEMCrypto_SUCCESS) secure_fd_array[sample_index] = *secure_fd;
|
||||
return sts == OEMCrypto_SUCCESS;
|
||||
}
|
||||
case OEMCrypto_BufferType_Direct: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
// Redirect printf and log statements from oemcrypto functions to a file to
|
||||
// reduce noise
|
||||
RedirectStdoutToFile();
|
||||
size_t samples_length;
|
||||
|
||||
// Split data using separator.
|
||||
auto inputs = SplitInput(data, size);
|
||||
if (inputs.size() < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OEMCrypto_Decrypt_Cenc_Fuzz fuzzed_structure;
|
||||
if (inputs[0].size() < sizeof(fuzzed_structure)) {
|
||||
return 0;
|
||||
}
|
||||
// Copy OEMCrypto_Decrypt_Cenc_Fuzz from input data.
|
||||
memcpy(&fuzzed_structure, data, sizeof(fuzzed_structure));
|
||||
ConvertDataToValidEnum(OEMCrypto_CipherMode_MaxValue,
|
||||
&fuzzed_structure.cipher_mode);
|
||||
|
||||
size_t remaining_size_for_samples =
|
||||
inputs[0].size() - sizeof(fuzzed_structure);
|
||||
// Initialize FDP structures to read data using inbuilt functions.
|
||||
FuzzedDataProvider fuzzed_sample_data(data + sizeof(fuzzed_structure),
|
||||
remaining_size_for_samples);
|
||||
FuzzedDataProvider fuzzed_subsample_data(inputs[1].data(), inputs[1].size());
|
||||
|
||||
// Read subsamples from fuzzed data.
|
||||
vector<OEMCrypto_SubSampleDescription> subsamples;
|
||||
while (fuzzed_subsample_data.remaining_bytes() >
|
||||
sizeof(OEMCrypto_SubSampleDescription)) {
|
||||
OEMCrypto_SubSampleDescription subsample;
|
||||
fuzzed_subsample_data.ConsumeData(&subsample,
|
||||
sizeof(OEMCrypto_SubSampleDescription));
|
||||
subsamples.push_back(subsample);
|
||||
}
|
||||
if (subsamples.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Infer samples_length from fuzzed data.
|
||||
size_t sample_description_size = sizeof(OEMCrypto_SampleDescription);
|
||||
samples_length =
|
||||
fuzzed_sample_data.remaining_bytes() / sample_description_size;
|
||||
if (samples_length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize sample_descriptions array.
|
||||
vector<OEMCrypto_SampleDescription> sample_descriptions(samples_length);
|
||||
// Create array to maintain secure_fd buffer values for secure buffers.
|
||||
vector<int> secure_fd_array(samples_length);
|
||||
|
||||
OEMCryptoLicenseAPIFuzz license_api_fuzz;
|
||||
Session* session = license_api_fuzz.session();
|
||||
// Copy samples from fuzzed data.
|
||||
size_t input_subsample_index = 0;
|
||||
size_t total_input_data_length = 0;
|
||||
for (size_t i = 0; i < samples_length; i++) {
|
||||
fuzzed_sample_data.ConsumeData(&sample_descriptions[i],
|
||||
sample_description_size);
|
||||
ConvertDataToValidEnum(
|
||||
OEMCrypto_BufferType_MaxValue,
|
||||
&sample_descriptions[i].buffers.output_descriptor.type);
|
||||
|
||||
// Copy random data into input sample data. Cap input data length at 5 MB,
|
||||
// 1 MB higher than that described by resource rating tier.
|
||||
total_input_data_length += std::min(
|
||||
MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length);
|
||||
|
||||
// Copy sub sample data.
|
||||
sample_descriptions[i].subsamples = &subsamples[input_subsample_index];
|
||||
input_subsample_index += sample_descriptions[i].subsamples_length;
|
||||
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 =
|
||||
&input_buffer[input_buffer_index];
|
||||
input_buffer_index += std::min(
|
||||
MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length);
|
||||
|
||||
// Create output buffer pointers. If secure buffer is not supported, we
|
||||
// explicitly convert to clear buffer and fuzz.
|
||||
if (!InitializeOutputBuffers(
|
||||
session->session_id(),
|
||||
sample_descriptions[i].buffers.output_descriptor, i,
|
||||
secure_fd_array)) {
|
||||
LOGI(
|
||||
"[OEMCrypto decrypt CENC fuzz] Secure buffers are not supported. Use "
|
||||
"clear buffer instead.");
|
||||
sample_descriptions[i].buffers.output_descriptor.type =
|
||||
OEMCrypto_BufferType_Clear;
|
||||
InitializeOutputBuffers(session->session_id(),
|
||||
sample_descriptions[i].buffers.output_descriptor,
|
||||
i, secure_fd_array);
|
||||
}
|
||||
}
|
||||
|
||||
// Load license and call decrypt_cenc API.
|
||||
license_api_fuzz.LoadLicense();
|
||||
OEMCrypto_SelectKey(session->session_id(), session->license().keys[0].key_id,
|
||||
session->license().keys[0].key_id_length,
|
||||
fuzzed_structure.cipher_mode);
|
||||
OEMCrypto_DecryptCENC(session->session_id(), sample_descriptions.data(),
|
||||
samples_length, &fuzzed_structure.pattern);
|
||||
FreeOutputBuffers(session->session_id(), sample_descriptions.data(),
|
||||
samples_length, secure_fd_array.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -5,4 +5,21 @@
|
||||
|
||||
namespace wvoec {
|
||||
void RedirectStdoutToFile() { freopen("log.txt", "a", stdout); }
|
||||
|
||||
std::vector<std::vector<uint8_t>> SplitInput(const uint8_t* data, size_t size) {
|
||||
std::vector<std::vector<uint8_t>> result;
|
||||
auto current_pos = data;
|
||||
auto end = data + size;
|
||||
// Using memmem to find separator
|
||||
while (const uint8_t* pos = reinterpret_cast<const uint8_t*>(
|
||||
memmem(current_pos, end - current_pos, kFuzzDataSeparator,
|
||||
sizeof(kFuzzDataSeparator)))) {
|
||||
result.push_back({current_pos, pos});
|
||||
current_pos = pos + sizeof(kFuzzDataSeparator);
|
||||
}
|
||||
if (current_pos < end) {
|
||||
result.push_back({current_pos, end});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "FuzzedDataProvider.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oemcrypto_corpus_generator_helper.h"
|
||||
#include "oemcrypto_session_tests_helper.h"
|
||||
|
||||
namespace wvoec {
|
||||
// Initial setup to create a valid OEMCrypto state such as initializing crypto
|
||||
// firmware/hardware, installing golden key box etc. in order to fuzz
|
||||
@@ -42,6 +42,15 @@ class OEMCryptoLicenseAPIFuzz : public InitializeFuzz {
|
||||
|
||||
LicenseRoundTrip& license_messages() { return license_messages_; }
|
||||
|
||||
Session* session() { return &session_; }
|
||||
|
||||
void LoadLicense() {
|
||||
license_messages_.SignAndVerifyRequest();
|
||||
license_messages_.CreateDefaultResponse();
|
||||
license_messages_.EncryptAndSignResponse();
|
||||
license_messages_.LoadResponse();
|
||||
}
|
||||
|
||||
private:
|
||||
Session session_;
|
||||
LicenseRoundTrip license_messages_;
|
||||
@@ -89,6 +98,9 @@ void ConvertDataToValidEnum(T max_enum_value, T* t) {
|
||||
// Redirect printf and log statements from oemcrypto functions to a file to
|
||||
// reduce noise
|
||||
void RedirectStdoutToFile();
|
||||
|
||||
// Function to split fuzzer input using delimiter "-_^_".
|
||||
std::vector<std::vector<uint8_t>> SplitInput(const uint8_t* data, size_t size);
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // OEMCRYPTO_FUZZ_HELPER_H_
|
||||
|
||||
@@ -25,6 +25,17 @@ struct OEMCrypto_Request_Fuzz {
|
||||
// that ODK parses and actual message buffer to the request APIs.
|
||||
size_t signature_length;
|
||||
size_t core_message_length;
|
||||
// Request message is of variable length and not included in this structure.
|
||||
};
|
||||
|
||||
struct OEMCrypto_Decrypt_Cenc_Fuzz {
|
||||
// Corpus format is as below, let | be separator.
|
||||
// cipher_mode + pattern + sample_data for all samples |
|
||||
// subsample_data for all samples
|
||||
OEMCryptoCipherMode cipher_mode;
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
||||
// Sample data and subsample data are of variable length and not included in
|
||||
// this structure.
|
||||
};
|
||||
} // namespace wvoec
|
||||
|
||||
|
||||
@@ -48,5 +48,17 @@
|
||||
'oemcrypto_renewal_request_fuzz.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_decrypt_cenc_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_decrypt_cenc_fuzz.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_load_entitled_content_keys_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_load_entitled_content_keys_fuzz.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@
|
||||
# Need -g flag to include source line numbers in error stack trace.
|
||||
'-g',
|
||||
],
|
||||
'ldflags': [
|
||||
'-fPIC',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
],
|
||||
}],
|
||||
['generate_code_coverage_report=="true"', {
|
||||
# Include flags to build fuzzer binaries to generate source based code coverage reports.
|
||||
@@ -70,12 +74,14 @@
|
||||
'-fprofile-instr-generate',
|
||||
'-fcoverage-mapping',
|
||||
],
|
||||
'ldflags': [
|
||||
'-fPIC',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
'-fprofile-instr-generate',
|
||||
'-fcoverage-mapping',
|
||||
],
|
||||
}],
|
||||
], # conditions
|
||||
'ldflags': [
|
||||
'-fPIC',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
],
|
||||
'libraries': [
|
||||
'-lpthread',
|
||||
],
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "FuzzedDataProvider.h"
|
||||
#include "oemcrypto_fuzz_helper.h"
|
||||
#include "oemcrypto_fuzz_structs.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
// Redirect printf and log statements from oemcrypto functions to a file to
|
||||
// reduce noise
|
||||
RedirectStdoutToFile();
|
||||
|
||||
// Corpus format is as below, let | be separator.
|
||||
// message buffer with key data | entitled content key object array with
|
||||
// offsets and lengths to read key data from message buffer.
|
||||
// Split data using separator.
|
||||
auto inputs = SplitInput(data, size);
|
||||
if (inputs.size() < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FuzzedDataProvider fuzzed_entitled_content_key_array(inputs[1].data(),
|
||||
inputs[1].size());
|
||||
|
||||
// Message to be verified. Return 0 if key data buffer is empty.
|
||||
if (inputs[0].size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy data to OEMCrypto_EntitledContentKeyObject array.
|
||||
size_t entitled_content_key_object_size =
|
||||
sizeof(OEMCrypto_EntitledContentKeyObject);
|
||||
size_t entitled_content_key_array_length =
|
||||
fuzzed_entitled_content_key_array.remaining_bytes() /
|
||||
entitled_content_key_object_size;
|
||||
if (entitled_content_key_array_length == 0) {
|
||||
return 0;
|
||||
}
|
||||
OEMCrypto_EntitledContentKeyObject* entitled_content_key_array =
|
||||
new OEMCrypto_EntitledContentKeyObject[entitled_content_key_array_length];
|
||||
|
||||
for (size_t i = 0; i < entitled_content_key_array_length; i++) {
|
||||
fuzzed_entitled_content_key_array.ConsumeData(
|
||||
&entitled_content_key_array[i], entitled_content_key_object_size);
|
||||
}
|
||||
|
||||
OEMCryptoLicenseAPIFuzz license_api_fuzz;
|
||||
// Setting up state. Load default entitlement license to load entitlement
|
||||
// keys into sessions key table.
|
||||
license_api_fuzz.license_messages().set_license_type(
|
||||
OEMCrypto_EntitlementLicense);
|
||||
license_api_fuzz.LoadLicense();
|
||||
// Call OEMCrypto_LoadEntitledContentKeys with fuzzed buffers.
|
||||
Session* session = license_api_fuzz.session();
|
||||
uint8_t* fuzzed_key_data = inputs[0].data();
|
||||
size_t fuzzed_key_data_size = inputs[0].size();
|
||||
OEMCrypto_LoadEntitledContentKeys(
|
||||
session->session_id(), fuzzed_key_data, fuzzed_key_data_size,
|
||||
entitled_content_key_array_length, entitled_content_key_array);
|
||||
delete[] entitled_content_key_array;
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -23,16 +23,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t renewal_response_size =
|
||||
size - sizeof(OEMCrypto_Renewal_Response_Fuzz);
|
||||
|
||||
OEMCryptoLicenseAPIFuzz license_api_fuzz;
|
||||
license_api_fuzz.license_messages().SignAndVerifyRequest();
|
||||
license_api_fuzz.license_messages().CreateDefaultResponse();
|
||||
OEMCryptoRenewalAPIFuzz renewal_response_fuzz;
|
||||
renewal_response_fuzz.license_messages().SignAndVerifyRequest();
|
||||
renewal_response_fuzz.license_messages().CreateDefaultResponse();
|
||||
// Inject timer limits from fuzzed input to timer_limits field from
|
||||
// core license response.
|
||||
license_api_fuzz.license_messages().InjectFuzzedTimerLimits(fuzzed_data);
|
||||
license_api_fuzz.license_messages().EncryptAndSignResponse();
|
||||
license_api_fuzz.license_messages().LoadResponse();
|
||||
renewal_response_fuzz.license_messages().InjectFuzzedTimerLimits(fuzzed_data);
|
||||
renewal_response_fuzz.license_messages().EncryptAndSignResponse();
|
||||
renewal_response_fuzz.license_messages().LoadResponse();
|
||||
|
||||
OEMCryptoRenewalAPIFuzz renewal_response_fuzz;
|
||||
// Call renewal response API using fuzzed data.
|
||||
renewal_response_fuzz.renewal_messages().SignAndVerifyRequest();
|
||||
renewal_response_fuzz.renewal_messages().InjectFuzzedResponseData(
|
||||
fuzzed_data, renewal_response, renewal_response_size);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "oemcrypto_corpus_generator_helper.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
@@ -55,7 +56,12 @@ OEMCryptoResult DecryptFallbackChain::Decrypt(
|
||||
OEMCrypto_DecryptCENC(session_id, samples, samples_length, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteDecryptCencCorpus(cipher_mode, samples, pattern, samples_length);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Fall back to decrypting individual samples.
|
||||
for (size_t i = 0; i < samples_length; ++i) {
|
||||
@@ -75,7 +81,12 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Fall back to decrypting individual subsamples.
|
||||
OEMCrypto_SampleDescription fake_sample = sample;
|
||||
@@ -88,7 +99,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||
fake_sample.subsamples = &subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
sts = DecryptSubsample(session_id, fake_sample, pattern);
|
||||
sts = DecryptSubsample(session_id, fake_sample, pattern, cipher_mode);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
|
||||
fake_sample.buffers.input_data += length;
|
||||
@@ -106,11 +117,17 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||
// OEMCrypto implementation does not accept full subsamples.
|
||||
OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
OEMCryptoCipherMode cipher_mode) {
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Fall back to decrypting individual subsample halves.
|
||||
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
|
||||
@@ -132,7 +149,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||
subsample.num_bytes_encrypted == 0)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
|
||||
// Advance the buffers for the other half, in case they're needed.
|
||||
@@ -154,7 +171,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||
if (subsample.subsample_flags & OEMCrypto_LastSubsample)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
}
|
||||
|
||||
@@ -166,7 +183,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||
// caller.
|
||||
OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
OEMCryptoCipherMode cipher_mode) {
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
|
||||
}
|
||||
return OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
// In a real CDM, you would want some fallback here to handle the case where
|
||||
// the buffer is too big for the OEMCrypto implementation. But in the case of
|
||||
@@ -175,4 +196,40 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
|
||||
// here.
|
||||
}
|
||||
|
||||
// Used for OEMCrypto Fuzzing: Corpus format is as below, let | be separator.
|
||||
// cipher_mode + pattern + sample_data for all samples |
|
||||
// subsample_data for all samples
|
||||
void WriteDecryptCencCorpus(
|
||||
OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_SampleDescription* samples_description,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern, size_t samples_length) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_decrypt_cenc_fuzz_seed_corpus");
|
||||
// Cipher mode.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&cipher_mode),
|
||||
sizeof(OEMCryptoCipherMode));
|
||||
|
||||
// Pattern.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(pattern),
|
||||
sizeof(OEMCrypto_CENCEncryptPatternDesc));
|
||||
|
||||
// Sample data for all samples.
|
||||
for (size_t i = 0; i < samples_length; i++) {
|
||||
AppendToFile(file_name,
|
||||
reinterpret_cast<const char*>(&samples_description[i]),
|
||||
sizeof(OEMCrypto_SampleDescription));
|
||||
}
|
||||
AppendSeparator(file_name);
|
||||
|
||||
// Subsample data for all samples.
|
||||
for (size_t i = 0; i < samples_length; i++) {
|
||||
for (size_t j = 0; j < samples_description[i].subsamples_length; j++) {
|
||||
AppendToFile(
|
||||
file_name,
|
||||
reinterpret_cast<const char*>(&samples_description[i].subsamples[j]),
|
||||
sizeof(OEMCrypto_SubSampleDescription));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -43,17 +43,24 @@ class DecryptFallbackChain {
|
||||
|
||||
static OEMCryptoResult DecryptSubsample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
OEMCryptoCipherMode cipher_mode);
|
||||
|
||||
static OEMCryptoResult DecryptSubsampleHalf(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
OEMCryptoCipherMode cipher_mode);
|
||||
|
||||
// There is no reason to have an instance of this class.
|
||||
DecryptFallbackChain() = delete;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(DecryptFallbackChain);
|
||||
};
|
||||
|
||||
void WriteDecryptCencCorpus(OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_SampleDescription* samples,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
size_t samples_length);
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||
|
||||
@@ -64,7 +64,7 @@ void DeviceFeatures::Initialize() {
|
||||
printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false");
|
||||
OEMCrypto_CloseSession(session);
|
||||
api_version = OEMCrypto_APIVersion();
|
||||
printf("api_version = %d.\n", api_version);
|
||||
printf("api_version = %u.\n", api_version);
|
||||
// These unit tests only work with new usage tables. We do not test v12
|
||||
// usage tables.
|
||||
if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable();
|
||||
@@ -81,7 +81,7 @@ void DeviceFeatures::Initialize() {
|
||||
}
|
||||
printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false");
|
||||
resource_rating = OEMCrypto_ResourceRatingTier();
|
||||
printf("resource_rating = %d, security level %s.\n", resource_rating,
|
||||
printf("resource_rating = %u, security level %s.\n", resource_rating,
|
||||
OEMCrypto_SecurityLevel());
|
||||
uint32_t decrypt_hash_type = OEMCrypto_SupportsDecryptHash();
|
||||
supports_crc = (decrypt_hash_type == OEMCrypto_CRC_Clear_Buffer);
|
||||
@@ -115,8 +115,7 @@ void DeviceFeatures::Initialize() {
|
||||
std::string security_level = OEMCrypto_SecurityLevel();
|
||||
supports_level_1 = (security_level == "L1");
|
||||
printf("SecurityLevel is %s (%s)\n",
|
||||
supports_level_1 ? "Level 1" : "Not Level 1",
|
||||
security_level.c_str());
|
||||
supports_level_1 ? "Level 1" : "Not Level 1", security_level.c_str());
|
||||
CheckSecureBuffers();
|
||||
OEMCrypto_Terminate();
|
||||
initialized_ = true;
|
||||
@@ -130,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*");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "core_message_serialize.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "log.h"
|
||||
#include "odk_attributes.h"
|
||||
#include "odk_structs.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_test_data.h"
|
||||
@@ -337,7 +338,7 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
|
||||
}
|
||||
|
||||
void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
||||
size_t size) {
|
||||
size_t size UNUSED) {
|
||||
// Interpreting fuzz data as unencrypted core_response + message_data
|
||||
const size_t core_response_size = sizeof(ODK_ParsedProvisioning);
|
||||
// Copy core_response from data and serialize.
|
||||
@@ -544,7 +545,7 @@ void LicenseRoundTrip::InjectFuzzedTimerLimits(
|
||||
}
|
||||
|
||||
void LicenseRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
||||
size_t size) {
|
||||
size_t size UNUSED) {
|
||||
// Interpreting fuzz data as unencrypted core_response + message_data
|
||||
const size_t core_response_size = sizeof(ODK_ParsedLicense);
|
||||
// Copy core_response from data.
|
||||
@@ -900,6 +901,17 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
|
||||
key_data->encrypted_content_key_data, KEY_SIZE, &aes_key,
|
||||
iv, AES_ENCRYPT);
|
||||
}
|
||||
if (ShouldGenerateCorpus()) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_load_entitled_content_keys_fuzz_seed_corpus");
|
||||
// Corpus for load entitled keys fuzzer should be in the format:
|
||||
// message buffer to be verified | entitled content key object array.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_data_),
|
||||
sizeof(entitled_key_data_));
|
||||
AppendSeparator(file_name);
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_array_),
|
||||
num_keys_);
|
||||
}
|
||||
ASSERT_EQ(expected_sts,
|
||||
OEMCrypto_LoadEntitledContentKeys(
|
||||
license_messages_->session()->session_id(),
|
||||
@@ -1311,10 +1323,9 @@ void Session::TestDecryptResult(OEMCryptoResult expected_result,
|
||||
|
||||
void Session::TestSelectExpired(unsigned int key_index) {
|
||||
if (global_features.api_version >= 13) {
|
||||
OEMCryptoResult status =
|
||||
OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id,
|
||||
license().keys[key_index].key_id_length,
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
OEMCryptoResult status = OEMCrypto_SelectKey(
|
||||
session_id(), license().keys[key_index].key_id,
|
||||
license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR);
|
||||
// It is OK for SelectKey to succeed with an expired key, but if there is
|
||||
// an error, it must be OEMCrypto_ERROR_KEY_EXIRED.
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
@@ -1382,9 +1393,9 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
// TODO(fredgc): Verify cert is signed by Google.
|
||||
|
||||
int result = X509_verify_cert(store_ctx.get());
|
||||
ASSERT_GE(0, result) << " OEM Cert not valid. " <<
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get()));
|
||||
ASSERT_GE(0, result) << " OEM Cert not valid. "
|
||||
<< X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get()));
|
||||
if (result == 0) {
|
||||
printf("Cert not verified: %s.\n",
|
||||
X509_verify_cert_error_string(
|
||||
@@ -1440,7 +1451,7 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pkey_ctx,
|
||||
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
|
||||
const_cast<EVP_MD*>(EVP_sha1())) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
@@ -1489,18 +1500,17 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
|
||||
|
||||
const bool ok = VerifyPSSSignature(
|
||||
pkey.get(), message.data(), message.size(), signature,
|
||||
signature_length);
|
||||
const bool ok =
|
||||
VerifyPSSSignature(pkey.get(), message.data(), message.size(),
|
||||
signature, signature_length);
|
||||
EXPECT_TRUE(ok) << "PSS signature check failed.";
|
||||
} else if (padding_scheme == kSign_PKCS1_Block1) {
|
||||
vector<uint8_t> padded_digest(signature_length);
|
||||
int size;
|
||||
// RSA_public_decrypt decrypts the signature, and then verifies that
|
||||
// it was padded with RSA PKCS1 padding.
|
||||
size = RSA_public_decrypt(
|
||||
signature_length, signature, padded_digest.data(), public_rsa_,
|
||||
RSA_PKCS1_PADDING);
|
||||
size = RSA_public_decrypt(signature_length, signature, padded_digest.data(),
|
||||
public_rsa_, RSA_PKCS1_PADDING);
|
||||
EXPECT_GT(size, 0);
|
||||
padded_digest.resize(size);
|
||||
EXPECT_EQ(message, padded_digest);
|
||||
@@ -1566,16 +1576,14 @@ void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
|
||||
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
|
||||
usage_entry_number_ = index;
|
||||
encrypted_usage_entry_ = buffer;
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadUsageEntry(
|
||||
session_id(), index, buffer.data(), buffer.size()));
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadUsageEntry(session_id(), index, buffer.data(),
|
||||
buffer.size()));
|
||||
}
|
||||
|
||||
void Session::MoveUsageEntry(uint32_t new_index,
|
||||
std::vector<uint8_t>* header_buffer,
|
||||
OEMCryptoResult expect_result) {
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(open());
|
||||
ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry());
|
||||
ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index));
|
||||
@@ -1587,8 +1595,7 @@ void Session::MoveUsageEntry(uint32_t new_index,
|
||||
}
|
||||
|
||||
void Session::GenerateReport(const std::string& pst,
|
||||
OEMCryptoResult expected_result,
|
||||
Session* other) {
|
||||
OEMCryptoResult expected_result, Session* other) {
|
||||
ASSERT_TRUE(open_);
|
||||
if (other) { // If other is specified, copy mac keys.
|
||||
key_deriver_ = other->key_deriver_;
|
||||
@@ -1624,14 +1631,13 @@ void Session::GenerateReport(const std::string& pst,
|
||||
void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
wvcdm::Unpacked_PST_Report computed = pst_report();
|
||||
EXPECT_EQ(expected.status, computed.status());
|
||||
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
|
||||
char* pst_ptr = reinterpret_cast<char*>(computed.pst());
|
||||
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
|
||||
ASSERT_EQ(expected.pst, computed_pst);
|
||||
int64_t now = wvcdm::Clock().GetCurrentTime();
|
||||
int64_t age = now - expected.time_created; // How old is this report.
|
||||
EXPECT_NEAR(expected.seconds_since_license_received + age,
|
||||
computed.seconds_since_license_received(),
|
||||
kTimeTolerance);
|
||||
computed.seconds_since_license_received(), kTimeTolerance);
|
||||
// Decrypt times only valid on licenses that have been active.
|
||||
if (expected.status == kActive || expected.status == kInactiveUsed) {
|
||||
EXPECT_NEAR(expected.seconds_since_first_decrypt + age,
|
||||
@@ -1643,8 +1649,8 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
}
|
||||
std::vector<uint8_t> signature(SHA_DIGEST_LENGTH);
|
||||
key_deriver_.ClientSignPstReport(pst_report_buffer_, &signature);
|
||||
EXPECT_EQ(0, memcmp(computed.signature(), signature.data(),
|
||||
SHA_DIGEST_LENGTH));
|
||||
EXPECT_EQ(0,
|
||||
memcmp(computed.signature(), signature.data(), SHA_DIGEST_LENGTH));
|
||||
}
|
||||
|
||||
void Session::VerifyReport(Test_PST_Report expected,
|
||||
|
||||
@@ -583,15 +583,14 @@ class Session {
|
||||
// Verify the Usage Report. If any time is greater than 10 minutes, it is
|
||||
// assumed to be an absolute time, and time_since will be computed relative to
|
||||
// now.
|
||||
void VerifyReport(Test_PST_Report report,
|
||||
int64_t time_license_received = 0,
|
||||
void VerifyReport(Test_PST_Report report, int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Create an entry in the old usage table based on the given report.
|
||||
void CreateOldEntry(const Test_PST_Report &report);
|
||||
void CreateOldEntry(const Test_PST_Report& report);
|
||||
// Create a new entry and copy the old entry into it. Then very the report
|
||||
// is right.
|
||||
void CopyAndVerifyOldEntry(const Test_PST_Report &report,
|
||||
void CopyAndVerifyOldEntry(const Test_PST_Report& report,
|
||||
std::vector<uint8_t>* header_buffer);
|
||||
|
||||
// The unencrypted license response or license renewal response.
|
||||
|
||||
@@ -19,6 +19,16 @@ void AppendToFile(const std::string& file_name, const char* message,
|
||||
filebuf.close();
|
||||
}
|
||||
|
||||
void AppendSeparator(const std::string& file_name) {
|
||||
std::ofstream filebuf(file_name.c_str(), std::ios::app | std::ios::binary);
|
||||
if (!filebuf) {
|
||||
std::cout << "Cannot open file " << file_name.c_str() << std::endl;
|
||||
}
|
||||
filebuf.write(reinterpret_cast<const char*>(&kFuzzDataSeparator),
|
||||
sizeof(kFuzzDataSeparator));
|
||||
filebuf.close();
|
||||
}
|
||||
|
||||
std::string GetFileName(const char* directory) {
|
||||
std::string file_name(PATH_TO_CORPUS);
|
||||
file_name += directory;
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
#include <string>
|
||||
|
||||
namespace wvoec {
|
||||
const uint8_t kFuzzDataSeparator[] = {'-', '_', '^', '_'};
|
||||
|
||||
void AppendToFile(const std::string& file_name, const char* message,
|
||||
const size_t message_size);
|
||||
|
||||
// Function to append separator "-_^_" between contents of corpus file.
|
||||
void AppendSeparator(const std::string& file_name);
|
||||
|
||||
std::string GetFileName(const char* directory);
|
||||
|
||||
void SetGenerateCorpus(bool should_generate_corpus);
|
||||
|
||||
@@ -2,8 +2,29 @@
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
//
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
|
||||
/**
|
||||
* @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 <openssl/aes.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -17,6 +38,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -82,6 +104,9 @@ 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;
|
||||
// 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
|
||||
@@ -93,6 +118,32 @@ T GetResourceValue(T (&resource_values)[N]) {
|
||||
return resource_values[global_features.resource_rating - 1];
|
||||
}
|
||||
|
||||
// Used for testing oemcrypto APIs with huge buffers.
|
||||
typedef const std::function<OEMCryptoResult(size_t)> oemcrypto_function;
|
||||
// Function to test APIs that expect a buffer length as input
|
||||
// by passing huge buffer lengths upto end_buffer_length and test that the API
|
||||
// doesn't crash.
|
||||
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
|
||||
size_t start_buffer_length,
|
||||
size_t end_buffer_length,
|
||||
bool check_status) {
|
||||
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
||||
for (size_t buffer_length = start_buffer_length;
|
||||
buffer_length < end_buffer_length &&
|
||||
(sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER ||
|
||||
!check_status);
|
||||
buffer_length *= 2) {
|
||||
sts = f(buffer_length);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to test APIs that expect a buffer length as input
|
||||
// by passing huge buffer lengths upto kHugeInputBufferLength and test that
|
||||
// the API doesn't crash.
|
||||
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) {
|
||||
TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status);
|
||||
}
|
||||
|
||||
// After API 16, we require 300 entries in the usage table. Before API 16, we
|
||||
// required 200.
|
||||
size_t RequiredUsageSize() {
|
||||
@@ -148,19 +199,27 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// General tests.
|
||||
// This test is first, becuase it might give an idea why other
|
||||
// tests are failing when the device has the wrong keybox installed.
|
||||
/// @addtogroup basic
|
||||
/// @{
|
||||
|
||||
/**
|
||||
* Verifies initialization and logs version information.
|
||||
* This test is first, because it might give an idea why other
|
||||
* tests are failing when the device has the wrong keybox installed.
|
||||
*/
|
||||
TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
const std::string log_message =
|
||||
"OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01";
|
||||
"OEMCrypto unit tests for API 16.4. Tests last updated 2020-10-07";
|
||||
cout << " " << log_message << "\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);
|
||||
EXPECT_EQ(ODK_MINOR_VERSION, 3);
|
||||
// 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.
|
||||
EXPECT_GE(ODK_MINOR_VERSION, 3);
|
||||
EXPECT_LE(ODK_MINOR_VERSION, 4);
|
||||
EXPECT_EQ(kCurrentAPI, 16u);
|
||||
const char* level = OEMCrypto_SecurityLevel();
|
||||
ASSERT_NE(nullptr, level);
|
||||
@@ -186,14 +245,18 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
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.
|
||||
/**
|
||||
* 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.
|
||||
/**
|
||||
* OEMCrypto must declare what type of provisioning scheme it uses.
|
||||
*/
|
||||
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
|
||||
OEMCrypto_ProvisioningMethod provisioning_method =
|
||||
OEMCrypto_GetProvisioningMethod();
|
||||
@@ -228,10 +291,10 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) {
|
||||
OEMCrypto_HDCP_Capability current, maximum;
|
||||
sts = OEMCrypto_GetHDCPCapability(¤t, &maximum);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
printf(" Current HDCP Capability: 0x%02x = %s.\n", current,
|
||||
HDCPCapabilityAsString(current));
|
||||
printf(" Maximum HDCP Capability: 0x%02x = %s.\n", maximum,
|
||||
HDCPCapabilityAsString(maximum));
|
||||
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) {
|
||||
@@ -255,6 +318,14 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
|
||||
OEMCrypto_LoadSRM(bad_srm.data(), bad_srm.size()));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryLoadSrmForLargeSrm) {
|
||||
auto oemcrypto_function = [](size_t buffer_length) {
|
||||
vector<uint8_t> srm_buffer(buffer_length);
|
||||
return OEMCrypto_LoadSRM(srm_buffer.data(), srm_buffer.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
|
||||
size_t sessions_count;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
@@ -285,6 +356,15 @@ TEST_F(OEMCryptoClientTest, NormalInitTermination) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
|
||||
}
|
||||
|
||||
// Test that set sandbox doesn't crash for a large sandbox id leangth.
|
||||
TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForLargeSandboxIdLength) {
|
||||
auto oemcrypto_function = [](size_t buffer_length) {
|
||||
vector<uint8_t> buffer(buffer_length);
|
||||
return OEMCrypto_SetSandbox(buffer.data(), buffer.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
//
|
||||
// Session Tests
|
||||
//
|
||||
@@ -375,6 +455,17 @@ TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) {
|
||||
ASSERT_LE(count, 3); // P(count > 3) = 1/256^3 = 6e-8.
|
||||
}
|
||||
|
||||
// Verify that GetRandom doesn't crash for large input lengths.
|
||||
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryGetRandomForLargeBuffer) {
|
||||
auto oemcrypto_function = [](size_t buffer_length) {
|
||||
vector<uint8_t> buffer(buffer_length);
|
||||
// TODO(ellurubharath, fredgc): Need to re-evaluate this on a real device
|
||||
// as GetRandom can be slow.
|
||||
return OEMCrypto_GetRandom(buffer.data(), buffer.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, GenerateNonce) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
@@ -509,6 +600,131 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) {
|
||||
ASSERT_EQ(input_buffer, output_buffer);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForHugeBufferLengths) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> input_buffer;
|
||||
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
|
||||
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
|
||||
|
||||
auto oemcrypto_function = [&s, &dest_buffer_descriptor,
|
||||
&input_buffer](size_t buffer_length) {
|
||||
input_buffer.resize(buffer_length);
|
||||
int secure_fd;
|
||||
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
|
||||
s.session_id(), buffer_length, &dest_buffer_descriptor, &secure_fd);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
LOGI("Secure buffers are not supported.");
|
||||
return sts;
|
||||
}
|
||||
|
||||
dest_buffer_descriptor.buffer.secure.handle_length = buffer_length;
|
||||
OEMCryptoResult status = OEMCrypto_CopyBuffer(
|
||||
s.session_id(), input_buffer.data(), buffer_length,
|
||||
&dest_buffer_descriptor,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor,
|
||||
secure_fd);
|
||||
return status;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest,
|
||||
OEMCryptoMemoryCopyBufferDirectForHugeBufferLengths) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> input_buffer;
|
||||
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
|
||||
dest_buffer_descriptor.type = OEMCrypto_BufferType_Direct;
|
||||
|
||||
auto oemcrypto_function = [&s, &dest_buffer_descriptor,
|
||||
&input_buffer](size_t buffer_length) {
|
||||
input_buffer.resize(buffer_length);
|
||||
OEMCryptoResult status = OEMCrypto_CopyBuffer(
|
||||
s.session_id(), input_buffer.data(), buffer_length,
|
||||
&dest_buffer_descriptor,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
return status;
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForOutOfRangeOffset) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> input_buffer;
|
||||
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
|
||||
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
|
||||
|
||||
size_t buffer_length = KiB;
|
||||
input_buffer.resize(buffer_length);
|
||||
int secure_fd;
|
||||
if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length,
|
||||
&dest_buffer_descriptor,
|
||||
&secure_fd) != OEMCrypto_SUCCESS) {
|
||||
LOGI("Secure buffers are not supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
dest_buffer_descriptor.buffer.secure.handle_length = buffer_length;
|
||||
auto oemcrypto_function = [&s, &dest_buffer_descriptor, &input_buffer,
|
||||
&buffer_length](size_t offset) {
|
||||
dest_buffer_descriptor.buffer.secure.offset = offset;
|
||||
return OEMCrypto_CopyBuffer(
|
||||
s.session_id(), input_buffer.data(), buffer_length,
|
||||
&dest_buffer_descriptor,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor,
|
||||
secure_fd);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest,
|
||||
OEMCryptoMemoryCopyBufferForOutOfRangeHandleLength) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> input_buffer;
|
||||
OEMCrypto_DestBufferDesc dest_buffer_descriptor;
|
||||
dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure;
|
||||
|
||||
size_t buffer_length = KiB;
|
||||
input_buffer.resize(buffer_length);
|
||||
int secure_fd;
|
||||
if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length,
|
||||
&dest_buffer_descriptor,
|
||||
&secure_fd) != OEMCrypto_SUCCESS) {
|
||||
LOGI("Secure buffers are not supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
dest_buffer_descriptor.buffer.secure.handle_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.address = output_buffer.data();
|
||||
dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size();
|
||||
ASSERT_NO_FATAL_FAILURE(OEMCrypto_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.";
|
||||
@@ -532,6 +748,33 @@ class OEMCryptoKeyboxTest : public OEMCryptoClientTest {
|
||||
}
|
||||
};
|
||||
|
||||
// Test that OEMCrypto_InstallKeyboxOrOEMCert doesn't crash for large keybox.
|
||||
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer) {
|
||||
auto f = [](size_t keybox_length) {
|
||||
vector<uint8_t> keybox(keybox_length);
|
||||
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
|
||||
return OEMCrypto_InstallKeyboxOrOEMCert(keybox.data(), keybox.size());
|
||||
};
|
||||
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
|
||||
// at beginning of generated buffers.
|
||||
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
|
||||
kCheckStatus);
|
||||
}
|
||||
|
||||
// This test verifies that load test key box doesn't crash for large
|
||||
// buffer length.
|
||||
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) {
|
||||
auto f = [](size_t keybox_length) {
|
||||
vector<uint8_t> keybox(keybox_length);
|
||||
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
|
||||
return OEMCrypto_LoadTestKeybox(keybox.data(), keybox.size());
|
||||
};
|
||||
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
|
||||
// at beginning of generated buffers.
|
||||
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
|
||||
kCheckStatus);
|
||||
}
|
||||
|
||||
// This test is used to print the device ID to stdout.
|
||||
TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
|
||||
OEMCryptoResult sts;
|
||||
@@ -543,6 +786,15 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) {
|
||||
auto oemcrypto_function = [](size_t input_length) {
|
||||
size_t device_id_length = input_length;
|
||||
vector<uint8_t> device_id(device_id_length);
|
||||
return OEMCrypto_GetDeviceID(device_id.data(), &device_id_length);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) {
|
||||
OEMCryptoResult sts;
|
||||
uint8_t dev_id[128];
|
||||
@@ -570,11 +822,20 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) {
|
||||
sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
|
||||
|
||||
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
|
||||
printf(" NormalGetKeyData: system_id = %d = 0x%04X, version=%d\n",
|
||||
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, OEMCryptoMemoryGetKeyIdForLargeIdLength) {
|
||||
auto oemcrypto_function = [](size_t input_length) {
|
||||
size_t key_data_length = input_length;
|
||||
vector<uint8_t> key_data(key_data_length);
|
||||
return OEMCrypto_GetKeyData(key_data.data(), &key_data_length);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) {
|
||||
OEMCryptoResult sts;
|
||||
uint8_t key_data[256];
|
||||
@@ -606,6 +867,42 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
|
||||
enc_context.data(), enc_context.size()));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest,
|
||||
OEMCryptoMemoryGenerateDerivedKeysForLargeMacContextLength) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
s.FillDefaultContext(&mac_context, &enc_context);
|
||||
|
||||
auto oemcrypto_function = [&s, &mac_context,
|
||||
&enc_context](size_t buffer_length) {
|
||||
mac_context.resize(buffer_length);
|
||||
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
|
||||
mac_context.size(), enc_context.data(),
|
||||
enc_context.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest,
|
||||
OEMCryptoMemoryGenerateDerivedKeysForLargeEncContextLength) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
s.FillDefaultContext(&mac_context, &enc_context);
|
||||
|
||||
auto oemcrypto_function = [&s, &mac_context,
|
||||
&enc_context](size_t buffer_length) {
|
||||
enc_context.resize(buffer_length);
|
||||
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
|
||||
mac_context.size(), enc_context.data(),
|
||||
enc_context.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
// This class is for tests that have an OEM Certificate instead of a keybox.
|
||||
class OEMCryptoProv30Test : public OEMCryptoClientTest {};
|
||||
|
||||
@@ -644,6 +941,11 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify.
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @addtogroup license
|
||||
/// @{
|
||||
|
||||
// This verifies that the OEM Certificate cannot be used for other RSA padding
|
||||
// schemes. Those schemes should only be used by cast receiver certificates.
|
||||
TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) {
|
||||
@@ -711,6 +1013,27 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForLargeCertLength) {
|
||||
if (wrapped_rsa_key_.size() == 0) {
|
||||
// If we don't have a wrapped key yet, create one.
|
||||
// This wrapped key will be shared by all sessions in the test.
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
}
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
// Install the DRM Cert's RSA key.
|
||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey());
|
||||
|
||||
auto oemcrypto_function = [](size_t input_length) {
|
||||
size_t public_cert_length = input_length;
|
||||
vector<uint8_t> public_cert(public_cert_length);
|
||||
return OEMCrypto_GetOEMPublicCertificate(public_cert.data(),
|
||||
&public_cert_length);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
//
|
||||
// AddKey Tests
|
||||
//
|
||||
@@ -836,6 +1159,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
|
||||
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
|
||||
@@ -849,6 +1173,8 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
|
||||
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 = ODK_MAJOR_VERSION;
|
||||
license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION;
|
||||
@@ -861,6 +1187,34 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
|
||||
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
|
||||
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
||||
}
|
||||
|
||||
// Verify that a license may be reloaded without a nonce, but with a nonzero
|
||||
// rental duration. In order to start the rental clock, we sign a dummy license
|
||||
// instead.
|
||||
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) {
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
||||
license_messages_.set_control(0);
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||
// It is not recommended for a license without a nonce to have a nonzero
|
||||
// rental duration. But there are content providers that have licenses with
|
||||
// this policy.
|
||||
license_messages_.core_response().timer_limits.rental_duration_seconds =
|
||||
kDuration;
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
||||
|
||||
// Load license in a different session, which did not create the request.
|
||||
Session session2;
|
||||
ASSERT_NO_FATAL_FAILURE(session2.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
|
||||
// However, in order to start the rental clock, we have to sign something. So
|
||||
// we will sign a dummy license request.
|
||||
LicenseRoundTrip dummy_license(&session2);
|
||||
ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest());
|
||||
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
|
||||
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
||||
}
|
||||
|
||||
// Verify that a license may be loaded with a nonce.
|
||||
@@ -870,6 +1224,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
|
||||
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.
|
||||
@@ -1336,6 +1691,10 @@ TEST_F(OEMCryptoLicenseTestAPI16, BadCoreHashAPI16) {
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||
}
|
||||
/// @}
|
||||
|
||||
/// @addtogroup decrypt
|
||||
/// @{
|
||||
|
||||
// LoadKeys should fail if we try to load keys with no keys.
|
||||
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) {
|
||||
@@ -1520,6 +1879,58 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) {
|
||||
strlen(key_id), reinterpret_cast<uint8_t*>(&block), &size));
|
||||
}
|
||||
|
||||
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_id_length.
|
||||
TEST_F(OEMCryptoLicenseTestAPI16,
|
||||
OEMCryptoMemoryQueryKeyControlForHugeKeyIdLength) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||
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());
|
||||
|
||||
OEMCrypto_SESSION session_id = session_.session_id();
|
||||
vector<uint8_t> valid_key_id(
|
||||
license_messages_.response_data().keys[0].key_id,
|
||||
license_messages_.response_data().keys[0].key_id + kTestKeyIdMaxLength);
|
||||
auto oemcrypto_function = [&session_id,
|
||||
&valid_key_id](size_t additional_key_id_length) {
|
||||
vector<uint8_t> key_id(valid_key_id);
|
||||
key_id.resize(valid_key_id.size() + additional_key_id_length);
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
return OEMCrypto_QueryKeyControl(session_id, key_id.data(), key_id.size(),
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
};
|
||||
// We do not want to stop as soon as API results in an error as it would
|
||||
// return error on first iteration as key_id is invalid.
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block
|
||||
// length.
|
||||
TEST_F(OEMCryptoLicenseTestAPI16,
|
||||
OEMCryptoMemoryQueryKeyControlForHugeKeyControlBlockLength) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||
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());
|
||||
|
||||
OEMCrypto_SESSION session_id = session_.session_id();
|
||||
uint8_t* key_id = license_messages_.response_data().keys[0].key_id;
|
||||
size_t key_id_length =
|
||||
license_messages_.response_data().keys[0].key_id_length;
|
||||
auto oemcrypto_function = [&session_id, &key_id,
|
||||
&key_id_length](size_t buffer_length) {
|
||||
size_t key_control_block_length = buffer_length;
|
||||
vector<uint8_t> key_control_block(key_control_block_length);
|
||||
return OEMCrypto_QueryKeyControl(session_id, key_id, key_id_length,
|
||||
key_control_block.data(),
|
||||
&key_control_block_length);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
// If the device says it supports anti-rollback in the hardware, then it should
|
||||
// accept a key control block with the anti-rollback hardware bit set.
|
||||
// Otherwise, it should reject that key control block.
|
||||
@@ -1718,6 +2129,11 @@ TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) {
|
||||
INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP,
|
||||
Range(1, 6));
|
||||
|
||||
/// @}
|
||||
|
||||
/// @addtogroup renewal
|
||||
/// @{
|
||||
|
||||
//
|
||||
// Load, Refresh Keys Test
|
||||
//
|
||||
@@ -1903,6 +2319,30 @@ TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) {
|
||||
OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number));
|
||||
}
|
||||
|
||||
// This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very
|
||||
// large hash buffer.
|
||||
TEST_F(OEMCryptoLicenseTestAPI16,
|
||||
OEMCryptoMemoryDecryptHashForLargeHashBuffer) {
|
||||
uint32_t session_id = session_.session_id();
|
||||
auto f = [session_id](size_t hash_length) {
|
||||
uint32_t frame_number = 1;
|
||||
vector<uint8_t> hash_buffer(hash_length);
|
||||
return OEMCrypto_SetDecryptHash(session_id, frame_number,
|
||||
hash_buffer.data(), hash_buffer.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(f, sizeof(uint32_t), kHugeInputBufferLength,
|
||||
kCheckStatus);
|
||||
}
|
||||
|
||||
// This test verifies OEMCrypto_SetDecryptHash for out of range frame number.
|
||||
TEST_P(OEMCryptoLicenseTest, DecryptHashForOutOfRangeFrameNumber) {
|
||||
uint32_t frame_number = 40;
|
||||
uint32_t hash = 42;
|
||||
ASSERT_NO_FATAL_FAILURE(OEMCrypto_SetDecryptHash(
|
||||
session_.session_id(), frame_number,
|
||||
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
|
||||
}
|
||||
|
||||
//
|
||||
// Decrypt Tests -- these test Decrypt CTR mode only.
|
||||
//
|
||||
@@ -3075,6 +3515,50 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
|
||||
enc_context.data(), enc_context.size()));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoUsesCertificate,
|
||||
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(),
|
||||
encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
session_.FillDefaultContext(&mac_context, &enc_context);
|
||||
OEMCrypto_SESSION session_id = session_.session_id();
|
||||
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
|
||||
&enc_session_key](size_t buffer_length) {
|
||||
mac_context.resize(buffer_length);
|
||||
return OEMCrypto_DeriveKeysFromSessionKey(
|
||||
session_id, enc_session_key.data(), enc_session_key.size(),
|
||||
mac_context.data(), mac_context.size(), enc_context.data(),
|
||||
enc_context.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoUsesCertificate,
|
||||
OEMCryptoMemoryDeriveKeysFromSessionKeyForLargeEncContext) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(),
|
||||
encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
session_.FillDefaultContext(&mac_context, &enc_context);
|
||||
OEMCrypto_SESSION session_id = session_.session_id();
|
||||
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
|
||||
&enc_session_key](size_t buffer_length) {
|
||||
enc_context.resize(buffer_length);
|
||||
return OEMCrypto_DeriveKeysFromSessionKey(
|
||||
session_id, enc_session_key.data(), enc_session_key.size(),
|
||||
mac_context.data(), mac_context.size(), enc_context.data(),
|
||||
enc_context.size());
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
}
|
||||
|
||||
// This test attempts to use alternate algorithms for loaded device certs.
|
||||
class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
||||
protected:
|
||||
@@ -3178,19 +3662,82 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
|
||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
|
||||
OEMCryptoResult sts = provisioning_messages.LoadResponse();
|
||||
encoded_rsa_key_ = provisioning_messages.encoded_rsa_key();
|
||||
wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key();
|
||||
key_loaded_ = (wrapped_rsa_key_.size() > 0);
|
||||
key_loaded_ = (OEMCrypto_SUCCESS == sts);
|
||||
if (key_loaded_) {
|
||||
encoded_rsa_key_ = provisioning_messages.encoded_rsa_key();
|
||||
wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key();
|
||||
EXPECT_GT(wrapped_rsa_key_.size(), 0u);
|
||||
EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_));
|
||||
}
|
||||
if (force) {
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_));
|
||||
ASSERT_TRUE(key_loaded_);
|
||||
}
|
||||
}
|
||||
|
||||
bool key_loaded_;
|
||||
};
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
OEMCryptoMemoryGenerateRSASignatureForLargeBuffer) {
|
||||
OEMCryptoResult sts;
|
||||
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
|
||||
// If the device is a cast receiver, then this scheme is required.
|
||||
if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_);
|
||||
if (key_loaded_) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
vector<uint8_t> message_buffer(10);
|
||||
size_t signature_length = 0;
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(),
|
||||
message_buffer.size(), nullptr,
|
||||
&signature_length, kSign_PKCS1_Block1);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
||||
vector<uint8_t> signature(signature_length);
|
||||
|
||||
auto oemcrypto_function = [&](size_t buffer_length) {
|
||||
message_buffer.resize(buffer_length);
|
||||
return OEMCrypto_GenerateRSASignature(
|
||||
s.session_id(), message_buffer.data(), message_buffer.size(),
|
||||
signature.data(), &signature_length, kSign_PKCS1_Block1);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
OEMCryptoMemoryGenerateRSASignatureForLargeSignatureLength) {
|
||||
OEMCryptoResult sts;
|
||||
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
|
||||
// If the device is a cast receiver, then this scheme is required.
|
||||
if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_);
|
||||
if (key_loaded_) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
vector<uint8_t> message_buffer(50);
|
||||
vector<uint8_t> signature;
|
||||
auto oemcrypto_function = [&](size_t signature_length) {
|
||||
signature.resize(signature_length);
|
||||
return OEMCrypto_GenerateRSASignature(
|
||||
s.session_id(), message_buffer.data(), message_buffer.size(),
|
||||
signature.data(), &signature_length, kSign_PKCS1_Block1);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
// The alternate padding is only required for cast receivers, but all devices
|
||||
// should forbid the alternate padding for regular certificates.
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) {
|
||||
@@ -3201,7 +3748,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) {
|
||||
// The alternate padding is only required for cast receivers, but if a device
|
||||
// does load an alternate certificate, it should NOT use it for generating
|
||||
// a license request signature.
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1_API16) {
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) {
|
||||
// Try to load an RSA key with alternative padding schemes. This signing
|
||||
// scheme is used by cast receivers.
|
||||
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
|
||||
@@ -4282,6 +4829,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
|
||||
if (alter_data) {
|
||||
signature[0] ^= 42;
|
||||
}
|
||||
if (signature.size() < signature_size) {
|
||||
signature.resize(signature_size);
|
||||
}
|
||||
|
||||
sts = OEMCrypto_SelectKey(session_.session_id(),
|
||||
session_.license().keys[key_index].key_id,
|
||||
@@ -4357,6 +4907,17 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
|
||||
ASSERT_EQ(expected_encrypted, buffer);
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) {
|
||||
EncryptAndLoadKeys();
|
||||
OEMCrypto_SESSION session_id = session_.session_id();
|
||||
auto oemcrypto_function = [session_id](size_t key_id_length) {
|
||||
vector<uint8_t> key_id(key_id_length);
|
||||
return OEMCrypto_SelectKey(session_id, key_id.data(), key_id.size(),
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
};
|
||||
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
|
||||
}
|
||||
|
||||
// Test Generic_Decrypt works correctly.
|
||||
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) {
|
||||
EncryptAndLoadKeys();
|
||||
@@ -4821,6 +5382,11 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoTest,
|
||||
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest,
|
||||
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
|
||||
|
||||
/// @}
|
||||
|
||||
/// @addtogroup usage_table
|
||||
/// @{
|
||||
|
||||
// Test usage table functionality.
|
||||
class LicenseWithUsageEntry {
|
||||
public:
|
||||
@@ -5871,7 +6437,7 @@ TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) {
|
||||
OEMCryptoResult status = OEMCrypto_SUCCESS;
|
||||
while (successful_count < attempt_count && status == OEMCrypto_SUCCESS) {
|
||||
wvcdm::TestSleep::SyncFakeClock();
|
||||
LOGD("Creating license for entry %zd", successful_count);
|
||||
LOGD("Creating license for entry %zu", successful_count);
|
||||
entries.push_back(
|
||||
std::unique_ptr<LicenseWithUsageEntry>(new LicenseWithUsageEntry()));
|
||||
entries.back()->set_pst("pst " + std::to_string(successful_count));
|
||||
@@ -5897,7 +6463,7 @@ TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) {
|
||||
successful_count++;
|
||||
}
|
||||
}
|
||||
LOGD("successful_count = %d", successful_count);
|
||||
LOGD("successful_count = %zu", successful_count);
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
// If we failed to create this many entries because of limited resources,
|
||||
// then the error returned should be insufficient resources.
|
||||
@@ -6009,6 +6575,38 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
|
||||
}
|
||||
|
||||
TEST_P(OEMCryptoUsageTableTest, LoadAndReloadEntries) {
|
||||
constexpr size_t kEntryCount = 10;
|
||||
std::vector<LicenseWithUsageEntry> entries(kEntryCount);
|
||||
|
||||
for (LicenseWithUsageEntry& entry : entries) {
|
||||
entry.license_messages().set_api_version(license_api_version_);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < kEntryCount; ++i) {
|
||||
const std::string create_description =
|
||||
"Creating entry #" + std::to_string(i);
|
||||
// Create and update a new entry.
|
||||
LicenseWithUsageEntry& new_entry = entries[i];
|
||||
ASSERT_NO_FATAL_FAILURE(new_entry.MakeOfflineAndClose(this))
|
||||
<< create_description;
|
||||
// Reload all entries, starting with the most recently created.
|
||||
for (size_t j = 0; j <= i; ++j) {
|
||||
const std::string reload_description =
|
||||
"Reloading entry #" + std::to_string(i - j) +
|
||||
", after creating entry #" + std::to_string(i);
|
||||
LicenseWithUsageEntry& old_entry = entries[i - j];
|
||||
ASSERT_NO_FATAL_FAILURE(old_entry.session().open());
|
||||
ASSERT_NO_FATAL_FAILURE(old_entry.ReloadUsageEntry())
|
||||
<< reload_description;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
old_entry.session().UpdateUsageEntry(&encrypted_usage_header_))
|
||||
<< reload_description;
|
||||
ASSERT_NO_FATAL_FAILURE(old_entry.session().close());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A usage report with the wrong pst should fail.
|
||||
TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) {
|
||||
LicenseWithUsageEntry entry;
|
||||
@@ -6174,9 +6772,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
|
||||
// on a device that allows an application to set the clock.
|
||||
class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
OEMCryptoUsageTableTest::SetUp();
|
||||
}
|
||||
void SetUp() override { OEMCryptoUsageTableTest::SetUp(); }
|
||||
|
||||
void TearDown() override {
|
||||
wvcdm::TestSleep::ResetRollback();
|
||||
@@ -6320,4 +6916,5 @@ INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableDefragTest,
|
||||
INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableTestWallClock,
|
||||
Values<uint32_t>(kCurrentAPI));
|
||||
|
||||
/// @}
|
||||
} // namespace wvoec
|
||||
|
||||
Reference in New Issue
Block a user