Source release 16.4.0

This commit is contained in:
John W. Bruce
2020-10-09 16:08:56 -07:00
parent 160df9f57a
commit 9d17a531ee
562 changed files with 52913 additions and 37426 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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_

View File

@@ -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.

View 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

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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',
],
},
],
}

View File

@@ -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',
],

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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_

View File

@@ -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*");

View File

@@ -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,

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);

View File

@@ -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(&current, &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