Source release 16.3.0
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*
|
||||
* See the document "WV Modular DRM Security Integration Guide for Common
|
||||
* Encryption (CENC) -- version 16.2" for a description of this API. You
|
||||
* Encryption (CENC) -- version 16.3" for a description of this API. You
|
||||
* can find this document in the widevine repository as
|
||||
* docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf
|
||||
* Changes between different versions of this API are documented in the files
|
||||
|
||||
@@ -112,7 +112,8 @@ typedef enum OEMCrypto_Usage_Entry_Status {
|
||||
*/
|
||||
typedef enum OEMCrypto_LicenseType {
|
||||
OEMCrypto_ContentLicense = 0,
|
||||
OEMCrypto_EntitlementLicense = 1
|
||||
OEMCrypto_EntitlementLicense = 1,
|
||||
OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense,
|
||||
} OEMCrypto_LicenseType;
|
||||
|
||||
/* Private key type used in the provisioning response. */
|
||||
|
||||
187
oemcrypto/test/fuzz_tests/README.md
Normal file
187
oemcrypto/test/fuzz_tests/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# OEMCRYPTO Fuzzing
|
||||
|
||||
## Objective
|
||||
|
||||
* Run fuzzing on OEMCrypto public APIs on linux using google supported
|
||||
clusterfuzz infrastructure to find security vulnerabilities.
|
||||
|
||||
Design Document - https://docs.google.com/document/d/1mdSV2irJZz5Y9uYb5DmSIddBjrAIZU9q8G5Q_BGpA4I/edit?usp=sharing
|
||||
|
||||
Fuzzing at google -
|
||||
[go/fuzzing](https://g3doc.corp.google.com/security/fuzzing/g3doc/fuzzing_resources.md?cl=head)
|
||||
## Monitoring
|
||||
### Cluster fuzz statistics
|
||||
|
||||
* Performance of OEMCrypto fuzz binaries running continuously using cluster
|
||||
fuzz infrastructure can be monitored
|
||||
[here](https://clusterfuzz.corp.google.com/fuzzer-stats).
|
||||
|
||||
The options to select are `Job type: libfuzzer_asan_oemcrypto` and `Fuzzer:
|
||||
fuzzer name you are looking for`
|
||||
|
||||
Example: [load_license_fuzz](https://clusterfuzz.corp.google.com/fuzzer-stats?group_by=by-day&date_start=2020-07-11&date_end=2020-07-17&fuzzer=libFuzzer_oemcrypto_load_license_fuzz&job=libfuzzer_asan_oemcrypto)
|
||||
|
||||
### Issues filed by clusterfuzz - Fixing those issues
|
||||
|
||||
* Any issues found with the fuzz target under test are reported by clusterfuzz
|
||||
[here](https://b.corp.google.com/hotlists/2442954).
|
||||
|
||||
* The bug will have a link to the test case that generated the bug. Download
|
||||
the test case and follow the steps from
|
||||
[testing fuzzer locally](#testing-fuzzer-locally) section to run the fuzzer
|
||||
locally using the test case that caused the crash.
|
||||
|
||||
* Once the issue is fixed, consider adding the test case that caused the crash
|
||||
to the seed corpus zip file. Details about seed corpus and their location
|
||||
are mentioned in
|
||||
[this section](#build-oemcrypto-unit-tests-to-generate-corpus).
|
||||
|
||||
## Corpus
|
||||
|
||||
* Once the fuzzer scripts are ready and running continuously using clusterfuzz
|
||||
or android infrastructure, we can measure the efficiency of fuzzers by
|
||||
looking at code coverage and number of new features that have been
|
||||
discovered by fuzzer scripts here Fuzz script statistics.
|
||||
|
||||
A fuzzer which tries to start from random inputs and figure out intelligent
|
||||
inputs to crash the libraries can be time consuming and not effective. A way
|
||||
to make fuzzers more effective is by providing a set of valid and invalid
|
||||
inputs of the library so that fuzzer can use those as a starting point.
|
||||
These sets of valid and invalid inputs are called corpus.
|
||||
|
||||
The idea is to run OEMCrypto unit tests and read required data into binary
|
||||
corpus files before calling into respective OEMCrypto APIs under test.
|
||||
Writing corpus data to binary files is controlled by --generate_corpus flag.
|
||||
|
||||
### Build OEMCrypto unit tests to generate corpus
|
||||
|
||||
* Install Pre-requisites
|
||||
|
||||
```shell
|
||||
$ sudo apt-get install gyp ninja-build
|
||||
```
|
||||
|
||||
* download cdm source code (including ODK & OEMCrypto unit tests):
|
||||
|
||||
```shell
|
||||
$ git clone sso://widevine-internal/cdm
|
||||
```
|
||||
|
||||
* Build OEMCrypto unit tests and run with --generate_corpus flag to generate
|
||||
corpus files:
|
||||
|
||||
```shell
|
||||
$ cd /path/to/cdm/repo
|
||||
$ export CDM_DIR=/path/to/cdm/repo
|
||||
$ export PATH_TO_CDM_DIR=..
|
||||
$ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp
|
||||
$ ninja -C out/Default/
|
||||
$ ./out/Default/oemcrypto_unittests --generate_corpus
|
||||
```
|
||||
|
||||
* To avoid uploading huge binary files to git repository, the corpus files
|
||||
will be saved in fuzzername_seed_corpus.zip format in blockbuster project's
|
||||
oemcrypto_fuzzing_corpus GCS bucket using gsutil. If you need permissions
|
||||
for blockbuster project, contact widevine-engprod@google.com.
|
||||
|
||||
```shell
|
||||
$ gsutil cp gs://oemcrypto_fuzzing_corpus/<fuzzername_seed_corpus.zip> \
|
||||
<destination_path>
|
||||
```
|
||||
|
||||
## Testing fuzzer locally
|
||||
|
||||
* Corpus needed to run fuzz tests locally are available in blockbuster
|
||||
project's oemcrypto_fuzzing_corpus GCS bucket. If you need permissions for
|
||||
this project, contact widevine-engprod@google.com. Download corpus.
|
||||
|
||||
```shell
|
||||
$ gsutil cp gs://oemcrypto_fuzzing_corpus/<fuzzername_seed_corpus.zip> \
|
||||
<destination_path>
|
||||
```
|
||||
|
||||
* Add flags to generate additional debugging information. Add '-g3' flag to
|
||||
oemcrypto_fuzztests.gypi cflags_cc in order to generate additional debug
|
||||
information locally.
|
||||
|
||||
* Build and test fuzz scripts locally using:
|
||||
|
||||
```shell
|
||||
$ export CXX=clang++
|
||||
$ export CC=clang
|
||||
$ export GYP_DEFINES="clang=1"
|
||||
$ cd /path/to/cdm/repo
|
||||
$ export PATH_TO_CDM_DIR=.
|
||||
$ gyp --format=ninja --depth=$(pwd) \
|
||||
oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
|
||||
$ ninja -C out/Default/
|
||||
$ mkdir /tmp/new_interesting_corpus
|
||||
$ ./out/Default/fuzzer_binary /tmp/new_interesting_corpus \
|
||||
/path/to/fuzz/seed/corpus/folder
|
||||
```
|
||||
|
||||
* In order to run fuzz script against a crash input, follow the above steps
|
||||
and run the fuzz binary against crash input rather than seed corpus.
|
||||
|
||||
```shell
|
||||
$ ./out/Default/fuzzer_binary crash_input_file
|
||||
```
|
||||
## Adding a new OEMCrypto fuzz script
|
||||
* In order to fuzz a new OEMCrypto API in future, a fuzz script can be added to
|
||||
oemcrypto/test/fuzz_tests folder which ends with _fuzz.cc.
|
||||
|
||||
* In the program, define the function LLVMFuzzerTestOneInput with the following signature:
|
||||
```
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
<your test code goes here>
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
*Note*: Make sure LLVMFuzzerTestOneInput calls the function you want to fuzz.
|
||||
|
||||
* Add a new target to oemcrypto_fuzztests.gyp file and follow instructions in
|
||||
[testing fuzzer locally](#testing-fuzzer-locally) to build and test locally.
|
||||
|
||||
## Generate code coverage reports locally
|
||||
|
||||
* Code coverage is a means of measuring fuzzer performance. We want to make
|
||||
sure that our fuzzer covers all the paths in our code and make any tweeks to
|
||||
fuzzer logic so we can maximize coverage to get better results.
|
||||
|
||||
Coverage reports for git on borg project is not automated and needs to be
|
||||
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.
|
||||
|
||||
```
|
||||
'-fprofile-instr-generate',
|
||||
'-fcoverage-mapping',
|
||||
```
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
```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
|
||||
```
|
||||
8
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc
Normal file
8
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc
Normal file
@@ -0,0 +1,8 @@
|
||||
// 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 "oemcrypto_fuzz_helper.h"
|
||||
|
||||
namespace wvoec {
|
||||
void RedirectStdoutToFile() { freopen("log.txt", "a", stdout); }
|
||||
} // namespace wvoec
|
||||
94
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h
Normal file
94
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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.
|
||||
#ifndef OEMCRYPTO_FUZZ_HELPER_H_
|
||||
#define OEMCRYPTO_FUZZ_HELPER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
// OEMCrypto APIs.
|
||||
class InitializeFuzz : public SessionUtil {
|
||||
public:
|
||||
InitializeFuzz() {
|
||||
wvoec::global_features.Initialize();
|
||||
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
|
||||
OEMCrypto_Initialize();
|
||||
EnsureTestKeys();
|
||||
}
|
||||
|
||||
~InitializeFuzz() { OEMCrypto_Terminate(); }
|
||||
};
|
||||
|
||||
class OEMCryptoLicenseAPIFuzz : public InitializeFuzz {
|
||||
public:
|
||||
OEMCryptoLicenseAPIFuzz() : license_messages_(&session_) {
|
||||
session_.open();
|
||||
InstallTestRSAKey(&session_);
|
||||
session_.GenerateNonce();
|
||||
}
|
||||
|
||||
~OEMCryptoLicenseAPIFuzz() {
|
||||
session_.close();
|
||||
}
|
||||
|
||||
LicenseRoundTrip& license_messages() { return license_messages_; }
|
||||
|
||||
private:
|
||||
Session session_;
|
||||
LicenseRoundTrip license_messages_;
|
||||
};
|
||||
|
||||
class OEMCryptoProvisioningAPIFuzz : public InitializeFuzz {
|
||||
public:
|
||||
OEMCryptoProvisioningAPIFuzz()
|
||||
: provisioning_messages_(&session_, encoded_rsa_key_) {
|
||||
// Opens a session and Generates Nonce.
|
||||
provisioning_messages_.PrepareSession(keybox_);
|
||||
}
|
||||
|
||||
~OEMCryptoProvisioningAPIFuzz() { session_.close(); }
|
||||
|
||||
ProvisioningRoundTrip& provisioning_messages() {
|
||||
return provisioning_messages_;
|
||||
}
|
||||
|
||||
private:
|
||||
Session session_;
|
||||
ProvisioningRoundTrip provisioning_messages_;
|
||||
};
|
||||
|
||||
// Initial setup to create a valid state such as creating session, installing
|
||||
// golden key box etc. in order to fuzz Load Renewal API.
|
||||
class OEMCryptoRenewalAPIFuzz : public OEMCryptoLicenseAPIFuzz {
|
||||
public:
|
||||
OEMCryptoRenewalAPIFuzz() : renewal_messages_(&license_messages()) {}
|
||||
|
||||
RenewalRoundTrip& renewal_messages() { return renewal_messages_; }
|
||||
|
||||
private:
|
||||
RenewalRoundTrip renewal_messages_;
|
||||
};
|
||||
|
||||
// Convert data to valid enum value.
|
||||
template <typename T>
|
||||
void ConvertDataToValidEnum(T max_enum_value, T* t) {
|
||||
FuzzedDataProvider fuzzed_enum_data(reinterpret_cast<uint8_t*>(t), sizeof(T));
|
||||
*t = static_cast<T>(fuzzed_enum_data.ConsumeIntegralInRange<uint32_t>(
|
||||
0, static_cast<uint32_t>(max_enum_value)));
|
||||
}
|
||||
|
||||
// Redirect printf and log statements from oemcrypto functions to a file to
|
||||
// reduce noise
|
||||
void RedirectStdoutToFile();
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // OEMCRYPTO_FUZZ_HELPER_H_
|
||||
31
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h
Normal file
31
oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
#ifndef OEMCRYPTO_FUZZ_STRUCTS_H_
|
||||
#define OEMCRYPTO_FUZZ_STRUCTS_H_
|
||||
|
||||
namespace wvoec {
|
||||
struct OEMCrypto_Renewal_Response_Fuzz {
|
||||
// Timer limits in core license response needs to be fuzzed as load renewal
|
||||
// depends on timer limits loaded from license response.
|
||||
ODK_TimerLimits timer_limits;
|
||||
// message(core_response + license_renewal_response) which mimics
|
||||
// response from license renewal server needs to be fuzzed. core_request
|
||||
// will be used to generate serialized core response.
|
||||
oemcrypto_core_message::ODK_RenewalRequest core_request;
|
||||
// Renewal duration seconds needs to be fuzzed which is part of serialized
|
||||
// core message from license renewal server.
|
||||
uint64_t renewal_duration_seconds;
|
||||
// license_renewal_response is of variable length and not included in this
|
||||
// structure.
|
||||
};
|
||||
|
||||
struct OEMCrypto_Request_Fuzz {
|
||||
// We would like to fuzz computed signature_length, input core_message_length
|
||||
// that ODK parses and actual message buffer to the request APIs.
|
||||
size_t signature_length;
|
||||
size_t core_message_length;
|
||||
};
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // OEMCRYPTO_FUZZ_STRUCTS_H_
|
||||
@@ -5,20 +5,48 @@
|
||||
# Builds under the CDM ./build.py (target platform) build system
|
||||
# Refer to the distribution package's README for details.
|
||||
{
|
||||
'variables': {
|
||||
'oemcrypto_lib%': '',
|
||||
'target_defaults': {
|
||||
'type': 'executable',
|
||||
'includes': [
|
||||
'oemcrypto_fuzztests.gypi',
|
||||
],
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'wv_ce_cdm_oemcrypto_generate_signature_fuzz_test',
|
||||
'type': 'executable',
|
||||
'target_name': 'oemcrypto_load_license_fuzz',
|
||||
'sources': [
|
||||
# The test runner and the testing device certificate.
|
||||
'oemcrypto_generate_signature.cc',
|
||||
'oemcrypto_load_license_fuzz.cc',
|
||||
],
|
||||
'includes': [
|
||||
'oemcrypto_fuzztests.gypi',
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_load_provisioning_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_load_provisioning_fuzz.cc',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_load_renewal_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_load_renewal_fuzz.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_license_request_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_license_request_fuzz.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_provisioning_request_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_provisioning_request_fuzz.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'oemcrypto_renewal_request_fuzz',
|
||||
'sources': [
|
||||
'oemcrypto_renewal_request_fuzz.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,48 +1,82 @@
|
||||
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
#source code may only be used and distributed under the Widevine Master License
|
||||
#Agreement.
|
||||
#
|
||||
# Include this in any custom unit test targets.
|
||||
# Does not include the test runner main.
|
||||
|
||||
{
|
||||
'variables': {
|
||||
'boringssl_libcrypto_path%': '../../../third_party/boringssl/boringssl.gyp:crypto',
|
||||
'boringssl_libssl_path%': '../../../third_party/boringssl/boringssl.gyp:ssl',
|
||||
'oemcrypto_dir': '../..',
|
||||
'platform_specific_dir': '../../../linux/src',
|
||||
'privacy_crypto_impl%': 'boringssl',
|
||||
# Flag used to generate source based code coverage reports.
|
||||
'generate_code_coverage_report%': 'false',
|
||||
'util_dir': '../../../util',
|
||||
},
|
||||
'sources': [
|
||||
'../../odk/src/core_message_deserialize.cpp',
|
||||
'../../odk/src/core_message_serialize.cpp',
|
||||
'../oec_device_features.cpp',
|
||||
'../oec_key_deriver.cpp',
|
||||
'../oemcrypto_corpus_generator_helper.cpp',
|
||||
'../oec_session_util.cpp',
|
||||
'../oemcrypto_corpus_generator_helper.cpp',
|
||||
'oemcrypto_fuzz_helper.cc',
|
||||
'../oemcrypto_session_tests_helper.cpp',
|
||||
'../oemcrypto_session_tests_helper.h',
|
||||
'../../../cdm/test/device_cert.cpp',
|
||||
'../../../cdm/test/device_cert.h',
|
||||
'<(platform_specific_dir)/file_store.cpp',
|
||||
'<(platform_specific_dir)/log.cpp',
|
||||
'<(util_dir)/src/platform.cpp',
|
||||
'<(util_dir)/src/rw_lock.cpp',
|
||||
'<(util_dir)/src/string_conversions.cpp',
|
||||
'<(util_dir)/test/test_sleep.cpp',
|
||||
'<(util_dir)/test/test_clock.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../../../core/include', # log.h
|
||||
'../../include',
|
||||
'../../ref/src', # oemcrypto_key_ref.h
|
||||
'../',
|
||||
'../../../cdm/test',
|
||||
'../../../third_party/fuzz',
|
||||
'<(util_dir)/include',
|
||||
'<(util_dir)/test',
|
||||
'<(oemcrypto_dir)/include',
|
||||
'<(oemcrypto_dir)/ref/src',
|
||||
'<(oemcrypto_dir)/test',
|
||||
'<(oemcrypto_dir)/test/fuzz_tests',
|
||||
'<(oemcrypto_dir)/odk/include',
|
||||
'<(oemcrypto_dir)/odk/src',
|
||||
],
|
||||
'defines': [
|
||||
'OEMCRYPTO_TESTS',
|
||||
'OEMCRYPTO_FUZZ_TESTS',
|
||||
],
|
||||
'libraries': [
|
||||
'../../../third_party/fuzz/platforms/x86-64/libFuzzer.a',
|
||||
'includes': [
|
||||
'../../../util/libssl_dependency.gypi',
|
||||
'../../ref/oec_ref.gypi',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../cdm/cdm.gyp:widevine_ce_cdm_shared',
|
||||
'../../../third_party/gmock.gyp:gmock',
|
||||
'../../../third_party/gmock.gyp:gtest',
|
||||
'../../../third_party/gmock.gyp:gmock',
|
||||
],
|
||||
'defines': [
|
||||
'OEMCRYPTO_FUZZ_TESTS',
|
||||
],
|
||||
'conditions': [
|
||||
['oemcrypto_lib==""', {
|
||||
'includes': [
|
||||
'../../ref/oec_ref.gypi',
|
||||
],
|
||||
}, {
|
||||
'libraries': [
|
||||
'../../../third_party/fuzz/platforms/x86-64/libFuzzer.a',
|
||||
'<(oemcrypto_lib)',
|
||||
],
|
||||
['generate_code_coverage_report=="false"', {
|
||||
# Include flags to build fuzzer binaries for cluster fuzz.
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
# Need -g flag to include source line numbers in error stack trace.
|
||||
'-g',
|
||||
],
|
||||
}],
|
||||
['generate_code_coverage_report=="true"', {
|
||||
# Include flags to build fuzzer binaries to generate source based code coverage reports.
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
'-fprofile-instr-generate',
|
||||
'-fcoverage-mapping',
|
||||
],
|
||||
}],
|
||||
], # conditions
|
||||
'ldflags': [
|
||||
'-fPIC',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
],
|
||||
'libraries': [
|
||||
'-lpthread',
|
||||
],
|
||||
}
|
||||
|
||||
28
oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc
Normal file
28
oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 "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();
|
||||
// Reject the input if it is less than fuzz data structure size.
|
||||
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
|
||||
return 0;
|
||||
}
|
||||
// Input for license request API will be modified by OEMCrypto, hence it
|
||||
// cannot be a const. Fuzzer complains if const identifier is removed of data,
|
||||
// hence copying data into a non const pointer.
|
||||
uint8_t* input = new uint8_t[size];
|
||||
memcpy(input, data, size);
|
||||
OEMCryptoLicenseAPIFuzz license_api_fuzz;
|
||||
license_api_fuzz.license_messages().InjectFuzzedRequestData(input, size);
|
||||
delete[] input;
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
30
oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc
Normal file
30
oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 "oemcrypto_fuzz_helper.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();
|
||||
if (size < sizeof(ODK_ParsedLicense) + sizeof(MessageData)) {
|
||||
return 0;
|
||||
}
|
||||
OEMCryptoLicenseAPIFuzz license_api_fuzz;
|
||||
license_api_fuzz.license_messages().SignAndVerifyRequest();
|
||||
// Interpreting input fuzz data as unencrypted (core_response + license
|
||||
// message data) from license server.
|
||||
license_api_fuzz.license_messages().InjectFuzzedResponseData(data, size);
|
||||
|
||||
// Convert OEMCrypto_LicenseType in core_response to a valid enum value.
|
||||
ConvertDataToValidEnum(
|
||||
OEMCrypto_LicenstType_MaxValue,
|
||||
&license_api_fuzz.license_messages().core_response().license_type);
|
||||
|
||||
license_api_fuzz.license_messages().EncryptAndSignResponse();
|
||||
license_api_fuzz.license_messages().LoadResponse();
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 "oemcrypto_fuzz_helper.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();
|
||||
if (size < sizeof(ODK_ParsedProvisioning) + sizeof(RSAPrivateKeyMessage)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz;
|
||||
provisioning_api_fuzz.provisioning_messages().SignAndVerifyRequest();
|
||||
// Interpreting input fuzz data as unencrypted(core_response + provisioning
|
||||
// message data) from provisioning server.
|
||||
provisioning_api_fuzz.provisioning_messages().InjectFuzzedResponseData(data,
|
||||
size);
|
||||
provisioning_api_fuzz.provisioning_messages().EncryptAndSignResponse();
|
||||
provisioning_api_fuzz.provisioning_messages().LoadResponse();
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
42
oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc
Normal file
42
oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 "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();
|
||||
if (size < sizeof(OEMCrypto_Renewal_Response_Fuzz)) {
|
||||
return 0;
|
||||
}
|
||||
// Copy input data to OEMCrypto_Renewal_Response_Fuzz and rest of message
|
||||
// into encrypted license_renewal_response.
|
||||
OEMCrypto_Renewal_Response_Fuzz fuzzed_data;
|
||||
memcpy(&fuzzed_data, data, sizeof(fuzzed_data));
|
||||
const uint8_t* renewal_response =
|
||||
data + sizeof(OEMCrypto_Renewal_Response_Fuzz);
|
||||
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();
|
||||
// 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();
|
||||
|
||||
OEMCryptoRenewalAPIFuzz renewal_response_fuzz;
|
||||
renewal_response_fuzz.renewal_messages().SignAndVerifyRequest();
|
||||
renewal_response_fuzz.renewal_messages().InjectFuzzedResponseData(
|
||||
fuzzed_data, renewal_response, renewal_response_size);
|
||||
renewal_response_fuzz.renewal_messages().LoadResponse();
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 "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();
|
||||
// If input size is less than fuzz data structure size, reject the input.
|
||||
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
|
||||
return 0;
|
||||
}
|
||||
// Input for provisioning request API will be modified by OEMCrypto, hence it
|
||||
// cannot be a const. Fuzzer complains if const identifier is removed of data,
|
||||
// hence copying data into a non const pointer.
|
||||
uint8_t* input = new uint8_t[size];
|
||||
memcpy(input, data, size);
|
||||
OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz;
|
||||
provisioning_api_fuzz.provisioning_messages().InjectFuzzedRequestData(input,
|
||||
size);
|
||||
delete[] input;
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
27
oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc
Normal file
27
oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 "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();
|
||||
// If input size is less than fuzz data structure, reject the input.
|
||||
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
|
||||
return 0;
|
||||
}
|
||||
// Input for renewal request API will be modified by OEMCrypto, hence it
|
||||
// cannot be a const. Fuzzer complains if const identifier is removed of data,
|
||||
// hence copying data into a non const pointer.
|
||||
uint8_t* input = new uint8_t[size];
|
||||
memcpy(input, data, size);
|
||||
OEMCryptoRenewalAPIFuzz renewal_api_fuzz;
|
||||
renewal_api_fuzz.renewal_messages().InjectFuzzedRequestData(input, size);
|
||||
delete[] input;
|
||||
return 0;
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -8,58 +8,15 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "oec_test_data.h"
|
||||
#include "test_sleep.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
DeviceFeatures global_features;
|
||||
|
||||
bool CanChangeTime() {
|
||||
#ifdef _WIN32
|
||||
LUID desired_id;
|
||||
if (!LookupPrivilegeValue(nullptr, SE_SYSTEMTIME_NAME, &desired_id))
|
||||
return false;
|
||||
HANDLE token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token))
|
||||
return false;
|
||||
std::unique_ptr<void, decltype(&CloseHandle)> safe_token(token, &CloseHandle);
|
||||
|
||||
// This queries all the permissions given to the token to determine if we can
|
||||
// change the system time. Note this is subtly different from PrivilegeCheck
|
||||
// as that only checks "enabled" privileges; even with admin rights, the
|
||||
// privilege is default disabled, even when granted.
|
||||
|
||||
DWORD size = 0;
|
||||
// Determine how big we need to allocate first.
|
||||
GetTokenInformation(token, TokenPrivileges, nullptr, 0, &size);
|
||||
// Since TOKEN_PRIVILEGES uses a variable-length array, we need to use malloc
|
||||
std::unique_ptr<TOKEN_PRIVILEGES, decltype(&free)> privileges(
|
||||
(TOKEN_PRIVILEGES*)malloc(size), &free);
|
||||
if (privileges && GetTokenInformation(token, TokenPrivileges,
|
||||
privileges.get(), size, &size)) {
|
||||
for (int i = 0; i < privileges->PrivilegeCount; i++) {
|
||||
if (privileges->Privileges[i].Luid.HighPart == desired_id.HighPart &&
|
||||
privileges->Privileges[i].Luid.LowPart == desired_id.LowPart) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return getuid() == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceFeatures::Initialize() {
|
||||
if (initialized_) return;
|
||||
uses_keybox = false;
|
||||
@@ -188,8 +145,11 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
// clang-format on
|
||||
// Some tests may require root access. If user is not root, filter these tests
|
||||
// out.
|
||||
if (!CanChangeTime()) {
|
||||
if (!wvcdm::TestSleep::CanChangeSystemTime()) {
|
||||
printf("Filtering out TimeRollbackPrevention.\n");
|
||||
FilterOut(&filter, "*TimeRollbackPrevention*");
|
||||
} else {
|
||||
printf("Can change time. I will run TimeRollbackPrevention.\n");
|
||||
}
|
||||
// Performance tests take a long time. Filter them out if they are not
|
||||
// specifically requested.
|
||||
|
||||
@@ -61,6 +61,12 @@ void Encryptor::PadAndEncryptProvisioningMessage(
|
||||
EXPECT_EQ(1, GetRandBytes(data->rsa_key_iv, KEY_IV_SIZE));
|
||||
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
|
||||
*encrypted = *data;
|
||||
if (data->rsa_key_length > sizeof(data->rsa_key)) {
|
||||
// OEMCrypto Fuzzing: fuzzed |rsa_key_length| overflows the allocated
|
||||
// buffer. Skip encryption in that case.
|
||||
return;
|
||||
}
|
||||
|
||||
size_t padding = AES_BLOCK_SIZE - (data->rsa_key_length % AES_BLOCK_SIZE);
|
||||
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
|
||||
padding);
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#include "core_message_serialize.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "log.h"
|
||||
#include "odk_structs.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "oemcrypto_corpus_generator_helper.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "platform.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -162,6 +164,10 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
std::max(required_message_size_, core_message_length + small_size);
|
||||
data.resize(message_size);
|
||||
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteRequestApiCorpus<CoreRequest>(gen_signature_length,
|
||||
core_message_length, data);
|
||||
}
|
||||
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
|
||||
@@ -177,6 +183,27 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
VerifyRequestSignature(data, gen_signature, core_message_length);
|
||||
}
|
||||
|
||||
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
||||
class CoreResponse, class ResponseData>
|
||||
void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
ResponseData>::InjectFuzzedRequestData(uint8_t* data,
|
||||
size_t size) {
|
||||
OEMCrypto_Request_Fuzz fuzz_structure;
|
||||
// Copy data into fuzz structure, cap signature length at 1mb as it will be
|
||||
// used to initialize signature vector.
|
||||
memcpy(&fuzz_structure, data, sizeof(fuzz_structure));
|
||||
fuzz_structure.signature_length =
|
||||
std::min(fuzz_structure.signature_length, MB);
|
||||
vector<uint8_t> signature(fuzz_structure.signature_length);
|
||||
|
||||
// Interpret rest of data as actual message buffer to request APIs.
|
||||
uint8_t* message_ptr = data + sizeof(fuzz_structure);
|
||||
size_t message_size = size - sizeof(fuzz_structure);
|
||||
PrepAndSignRequest(session()->session_id(), message_ptr, message_size,
|
||||
&fuzz_structure.core_message_length, signature.data(),
|
||||
&fuzz_structure.signature_length);
|
||||
}
|
||||
|
||||
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
||||
class CoreResponse, class ResponseData>
|
||||
OEMCrypto_Substring RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
@@ -309,8 +336,35 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
|
||||
&response_signature_);
|
||||
}
|
||||
|
||||
void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
||||
size_t size) {
|
||||
// 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.
|
||||
memcpy(&core_response_, data, core_response_size);
|
||||
|
||||
// Copy provisioning message data into response_data.
|
||||
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
|
||||
// Set nonce to one from session to pass nonce checks.
|
||||
response_data_.nonce = session()->nonce();
|
||||
}
|
||||
|
||||
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
|
||||
EXPECT_NE(session, nullptr);
|
||||
// Write corpus for oemcrypto_load_provisioning_fuzz. Fuzz script expects
|
||||
// unencrypted response from provisioning server as input corpus data.
|
||||
// Data will be encrypted and signed again explicitly by fuzzer script after
|
||||
// mutations.
|
||||
if (ShouldGenerateCorpus()) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_load_provisioning_fuzz_seed_corpus");
|
||||
// Corpus for license response fuzzer should be in the format:
|
||||
// unencrypted (core_response + response_data).
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
|
||||
sizeof(ODK_ParsedProvisioning));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
|
||||
sizeof(response_data_));
|
||||
}
|
||||
size_t wrapped_key_length = 0;
|
||||
const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
|
||||
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
|
||||
@@ -472,6 +526,69 @@ void LicenseRoundTrip::CreateDefaultResponse() {
|
||||
FillCoreResponseSubstrings();
|
||||
}
|
||||
|
||||
void LicenseRoundTrip::ConvertDataToValidBools(ODK_ParsedLicense* t) {
|
||||
t->nonce_required = ConvertByteToValidBoolean(&t->nonce_required);
|
||||
t->timer_limits.soft_enforce_playback_duration = ConvertByteToValidBoolean(
|
||||
&t->timer_limits.soft_enforce_playback_duration);
|
||||
t->timer_limits.soft_enforce_rental_duration =
|
||||
ConvertByteToValidBoolean(&t->timer_limits.soft_enforce_rental_duration);
|
||||
}
|
||||
|
||||
void LicenseRoundTrip::InjectFuzzedTimerLimits(
|
||||
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data) {
|
||||
// Interpreting fuzz data as timer limits.
|
||||
// Copy timer limits from data.
|
||||
memcpy(&core_response_.timer_limits, &fuzzed_data.timer_limits,
|
||||
sizeof(fuzzed_data.timer_limits));
|
||||
ConvertDataToValidBools(&core_response_);
|
||||
}
|
||||
|
||||
void LicenseRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
||||
size_t size) {
|
||||
// Interpreting fuzz data as unencrypted core_response + message_data
|
||||
const size_t core_response_size = sizeof(ODK_ParsedLicense);
|
||||
// Copy core_response from data.
|
||||
memcpy(&core_response_, data, core_response_size);
|
||||
// Maximum number of keys could be kMaxNumKeys(30). key_array_length can be
|
||||
// any random value as it is read from fuzz data.
|
||||
// Key data array(MessageKeyData keys[kMaxNumKeys]) will be looped over
|
||||
// key_array_length number of times during LoadLicense. If key_array_length is
|
||||
// more than kMaxNumKeys, setting it to max value of kMaxNumKeys as we should
|
||||
// not go out of bounds of this array length. For corpus, this value is
|
||||
// already hard coded to 4.
|
||||
if (core_response_.key_array_length > kMaxNumKeys) {
|
||||
core_response_.key_array_length = kMaxNumKeys;
|
||||
}
|
||||
// For corpus data, this value gets set to 4, but we need to test other
|
||||
// scenarios too, hence reading key_array_length value.
|
||||
set_num_keys(core_response_.key_array_length);
|
||||
ConvertDataToValidBools(&core_response_);
|
||||
// TODO(b/157520981): Once assertion bug is fixed, for loop can be removed.
|
||||
// Workaround for the above bug: key_data.length and key_id.length are being
|
||||
// used in AES decryption process and are expected to be a multiple of 16. An
|
||||
// assertion in AES decryption fails if this condition is not met which will
|
||||
// crash fuzzer.
|
||||
for (uint32_t i = 0; i < num_keys_; ++i) {
|
||||
size_t key_data_length = core_response_.key_array[i].key_data.length;
|
||||
size_t key_id_length = core_response_.key_array[i].key_id.length;
|
||||
if (key_data_length % 16 != 0) {
|
||||
core_response_.key_array[i].key_data.length =
|
||||
key_data_length - (key_data_length % 16);
|
||||
}
|
||||
if (key_id_length % 16 != 0) {
|
||||
core_response_.key_array[i].key_id.length =
|
||||
key_id_length - (key_id_length % 16);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy response_data from data and set nonce to match one in request to pass
|
||||
// nonce validations.
|
||||
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
|
||||
for (uint32_t i = 0; i < num_keys_; ++i) {
|
||||
response_data_.keys[i].control.nonce = session()->nonce();
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseRoundTrip::CreateResponseWithGenericCryptoKeys() {
|
||||
CreateDefaultResponse();
|
||||
response_data_.keys[0].control.control_bits |=
|
||||
@@ -540,17 +657,28 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
2 * MAC_KEY_SIZE, response_data_.mac_key_iv);
|
||||
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(
|
||||
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
|
||||
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
|
||||
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
session_->key_deriver().CBCEncrypt(
|
||||
&response_data_.keys[i].key_data[0],
|
||||
&encrypted_response_data_.keys[i].key_data[0],
|
||||
response_data_.keys[i].key_data_length, response_data_.keys[i].key_iv);
|
||||
// OEMCrypto Fuzzing skip encryption: key_data_length can be any number when
|
||||
// called from fuzzer. We want to skip encryption if that happens and let
|
||||
// LoadLicense be called with unencrypted data for that key. OEMCrypto
|
||||
// Fuzzing skip encryption: key_data_length being a random value will
|
||||
// encrypt data which is not expected to, there by leading to inefficient
|
||||
// fuzzing.
|
||||
if (response_data_.keys[i].key_data_length <=
|
||||
sizeof(response_data_.keys[i].key_data) &&
|
||||
response_data_.keys[i].key_data_length % 16 == 0) {
|
||||
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(
|
||||
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
|
||||
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
|
||||
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
session_->key_deriver().CBCEncrypt(
|
||||
&response_data_.keys[i].key_data[0],
|
||||
&encrypted_response_data_.keys[i].key_data[0],
|
||||
response_data_.keys[i].key_data_length,
|
||||
response_data_.keys[i].key_iv);
|
||||
}
|
||||
}
|
||||
if (api_version_ < kCoreMessagesAPI) {
|
||||
serialized_core_message_.resize(0);
|
||||
@@ -588,6 +716,21 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
|
||||
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
|
||||
EXPECT_NE(session, nullptr);
|
||||
// Write corpus for oemcrypto_load_license_fuzz. Fuzz script expects
|
||||
// unecnrypted response from license server as input corpus data.
|
||||
// Data will be encrypted and signed again explicitly by fuzzer script
|
||||
// after mutations.
|
||||
if (ShouldGenerateCorpus()) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_load_license_fuzz_seed_corpus");
|
||||
// Corpus for license response fuzzer should be in the format:
|
||||
// core_response + response_data.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
|
||||
sizeof(ODK_ParsedLicense));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
|
||||
sizeof(response_data_));
|
||||
}
|
||||
|
||||
// Some tests adjust the offset to be beyond the length of the message. Here,
|
||||
// we create a duplicate of the main message buffer so that these offsets do
|
||||
// not point to garbage data. The goal is to make sure OEMCrypto is verifying
|
||||
@@ -630,7 +773,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
|
||||
|
||||
// Note: we verify content licenses here. For entitlement license, we verify
|
||||
// the key control blocks after loading entitled content keys.
|
||||
if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys();
|
||||
if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys(session);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -644,12 +787,12 @@ OEMCryptoResult LicenseRoundTrip::ReloadResponse(Session* session) {
|
||||
// with the truth key control block. Failures in this function probably
|
||||
// indicate the OEMCrypto_LoadLicense/LoadKeys did not correctly process the key
|
||||
// control block.
|
||||
void LicenseRoundTrip::VerifyTestKeys() {
|
||||
void LicenseRoundTrip::VerifyTestKeys(Session* session) {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
||||
session_->session_id(), response_data_.keys[i].key_id,
|
||||
session->session_id(), response_data_.keys[i].key_id,
|
||||
response_data_.keys[i].key_id_length,
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
@@ -906,7 +1049,46 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
|
||||
&response_signature_);
|
||||
}
|
||||
|
||||
void RenewalRoundTrip::InjectFuzzedResponseData(
|
||||
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
|
||||
const uint8_t* renewal_response, const size_t renewal_response_size) {
|
||||
// Serializing core message.
|
||||
// This call also sets nonce in core response to match with session nonce.
|
||||
oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
|
||||
fuzzed_data.core_request, fuzzed_data.renewal_duration_seconds,
|
||||
&serialized_core_message_);
|
||||
|
||||
// Copy serialized core message and encrypted response from data and
|
||||
// calculate signature. Now we will have a valid signature for data generated
|
||||
// by fuzzer.
|
||||
encrypted_response_.assign(serialized_core_message_.begin(),
|
||||
serialized_core_message_.end());
|
||||
encrypted_response_.insert(encrypted_response_.end(), renewal_response,
|
||||
renewal_response + renewal_response_size);
|
||||
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
|
||||
encrypted_response_.size(),
|
||||
&response_signature_);
|
||||
}
|
||||
|
||||
OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
|
||||
// Write corpus for oemcrypto_load_renewal_fuzz. Fuzz script expects
|
||||
// encrypted response from Renewal server as input corpus data.
|
||||
// Data will be signed again explicitly by fuzzer script after mutations.
|
||||
if (ShouldGenerateCorpus()) {
|
||||
const std::string file_name =
|
||||
GetFileName("oemcrypto_load_renewal_fuzz_seed_corpus");
|
||||
// Corpus for renewal response fuzzer should be in the format:
|
||||
// OEMCrypto_Renewal_Response_Fuzz + license_renewal_response.
|
||||
OEMCrypto_Renewal_Response_Fuzz renewal_response_fuzz;
|
||||
renewal_response_fuzz.core_request = core_request_;
|
||||
renewal_response_fuzz.renewal_duration_seconds = renewal_duration_seconds_;
|
||||
AppendToFile(file_name,
|
||||
reinterpret_cast<const char*>(&renewal_response_fuzz),
|
||||
sizeof(renewal_response_fuzz));
|
||||
AppendToFile(file_name,
|
||||
reinterpret_cast<const char*>(&encrypted_response_data_),
|
||||
sizeof(encrypted_response_data_));
|
||||
}
|
||||
if (license_messages_->api_version() < kCoreMessagesAPI) {
|
||||
return OEMCrypto_RefreshKeys(
|
||||
session->session_id(), encrypted_response_.data(),
|
||||
@@ -1009,7 +1191,7 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
// Uses test certificate.
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
if (public_rsa_ == nullptr) PreparePublicKey();
|
||||
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
||||
// A failure here probably indicates that there is something wrong with the
|
||||
// test program and its dependency on BoringSSL.
|
||||
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
@@ -1297,12 +1479,11 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
EXPECT_TRUE(nullptr != public_rsa_)
|
||||
<< "No public RSA key loaded in test code.\n";
|
||||
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
||||
|
||||
EXPECT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
ASSERT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
<< "Signature size is wrong. " << signature_length << ", should be "
|
||||
<< RSA_size(public_rsa_) << "\n";
|
||||
<< RSA_size(public_rsa_);
|
||||
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
@@ -1471,7 +1652,10 @@ void Session::VerifyReport(Test_PST_Report expected,
|
||||
int64_t time_first_decrypt,
|
||||
int64_t time_last_decrypt) {
|
||||
const int64_t now = wvcdm::Clock().GetCurrentTime();
|
||||
expected.seconds_since_license_received = now - time_license_received;
|
||||
expected.seconds_since_license_received =
|
||||
(time_license_received > 0 && time_license_received < now)
|
||||
? now - time_license_received
|
||||
: 0;
|
||||
expected.seconds_since_first_decrypt =
|
||||
(time_first_decrypt > 0 && time_first_decrypt < now)
|
||||
? now - time_first_decrypt
|
||||
@@ -1482,4 +1666,41 @@ void Session::VerifyReport(Test_PST_Report expected,
|
||||
: 0;
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
|
||||
}
|
||||
|
||||
bool ConvertByteToValidBoolean(const bool* in) {
|
||||
const char* buf = reinterpret_cast<const char*>(in);
|
||||
for (size_t i = 0; i < sizeof(bool); i++) {
|
||||
if (buf[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class CoreRequest>
|
||||
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
|
||||
vector<uint8_t>& data) {
|
||||
std::string file_name;
|
||||
if (std::is_same<CoreRequest,
|
||||
oemcrypto_core_message::ODK_LicenseRequest>::value) {
|
||||
file_name = GetFileName("oemcrypto_license_request_fuzz_seed_corpus");
|
||||
} else if (std::is_same<
|
||||
CoreRequest,
|
||||
oemcrypto_core_message::ODK_ProvisioningRequest>::value) {
|
||||
file_name = GetFileName("oemcrypto_provisioning_request_fuzz_seed_corpus");
|
||||
} else if (std::is_same<CoreRequest,
|
||||
oemcrypto_core_message::ODK_RenewalRequest>::value) {
|
||||
file_name = GetFileName("oemcrypto_renewal_request_fuzz_seed_corpus");
|
||||
} else {
|
||||
LOGE("Invalid CoreRequest type while writing request api corups.");
|
||||
}
|
||||
// Corpus for request APIs should be signature_length + core_message_length +
|
||||
// data pointer.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&signature_length),
|
||||
sizeof(signature_length));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&core_message_length),
|
||||
sizeof(core_message_length));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(data.data()),
|
||||
data.size());
|
||||
}
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "odk.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_key_deriver.h"
|
||||
#include "oemcrypto_fuzz_structs.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "pst_report.h"
|
||||
|
||||
@@ -32,6 +33,8 @@ void PrintTo(const vector<uint8_t>& value, ostream* os);
|
||||
} // namespace std
|
||||
|
||||
namespace wvoec {
|
||||
// OEMCrypto Fuzzing: Set max signture length to 1mb.
|
||||
const size_t MB = 1024 * 1024;
|
||||
|
||||
// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp
|
||||
constexpr size_t kMaxNumKeys = 30;
|
||||
@@ -158,6 +161,9 @@ class RoundTrip {
|
||||
// Have OEMCrypto sign a request message and then verify the signature and the
|
||||
// core message.
|
||||
virtual void SignAndVerifyRequest();
|
||||
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
||||
// License/Provisioning/Renwal request data that can be serialized.
|
||||
virtual void InjectFuzzedRequestData(uint8_t* data, size_t size);
|
||||
// Create a default |response_data| and |core_response|.
|
||||
virtual void CreateDefaultResponse() = 0;
|
||||
// Copy fields from |response_data| to |padded_response_data|, encrypting
|
||||
@@ -241,6 +247,11 @@ class ProvisioningRoundTrip
|
||||
void set_allowed_schemes(uint32_t allowed_schemes) {
|
||||
allowed_schemes_ = allowed_schemes;
|
||||
}
|
||||
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
||||
// provisioning response data that can be parsed. Calculates signature for
|
||||
// data generated by fuzzer, so that signature validation passes when parsing
|
||||
// provisioning response.
|
||||
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
|
||||
|
||||
protected:
|
||||
void VerifyRequestSignature(const vector<uint8_t>& data,
|
||||
@@ -286,6 +297,18 @@ class LicenseRoundTrip
|
||||
license_type_(OEMCrypto_ContentLicense),
|
||||
request_hash_() {}
|
||||
void CreateDefaultResponse() override;
|
||||
// Used for OEMCrypto Fuzzing: Function to inject fuzzed timer limits
|
||||
// into timer_limits field from core_response. We need to fuzz timer
|
||||
// limits in order to efficiently fuzz load renewal response API.
|
||||
void InjectFuzzedTimerLimits(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data);
|
||||
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
||||
// License response data that can be parsed. Calculates signature for data
|
||||
// generated by fuzzer, so that signature validation passes when parsing
|
||||
// license response.
|
||||
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
|
||||
// Used for OEMCrypto Fuzzing: Convert boolean flags in parsed_license to
|
||||
// valid bytes to avoid errors from msan.
|
||||
void ConvertDataToValidBools(ODK_ParsedLicense* t);
|
||||
// Create a license with four keys. Each key is responsible for one of generic
|
||||
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
|
||||
// is allowed only one type of operation.
|
||||
@@ -298,7 +321,7 @@ class LicenseRoundTrip
|
||||
// Reload an offline license into a different session. This derives new mac
|
||||
// keys and then calls LoadResponse.
|
||||
OEMCryptoResult ReloadResponse(Session* session);
|
||||
void VerifyTestKeys();
|
||||
void VerifyTestKeys(Session* session);
|
||||
// Set the default key control block for all keys. This is used in
|
||||
// CreateDefaultResponse. The key control block determines the restrictions
|
||||
// that OEMCrypto should place on a key's use. For example, it specifies the
|
||||
@@ -386,6 +409,9 @@ class RenewalRoundTrip
|
||||
is_release_(false) {}
|
||||
void CreateDefaultResponse() override;
|
||||
void EncryptAndSignResponse() override;
|
||||
void InjectFuzzedResponseData(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
|
||||
const uint8_t* renewal_response,
|
||||
const size_t renewal_response_size);
|
||||
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
||||
OEMCryptoResult LoadResponse(Session* session) override;
|
||||
uint64_t renewal_duration_seconds() const {
|
||||
@@ -599,6 +625,13 @@ class Session {
|
||||
string pst_;
|
||||
};
|
||||
|
||||
// Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors
|
||||
// generated by msan.
|
||||
bool ConvertByteToValidBoolean(const bool* in);
|
||||
// Used for OEMCrypto Fuzzing: Generates corpus for request APIs.
|
||||
template <class CoreRequest>
|
||||
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
|
||||
vector<uint8_t>& data);
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEC_SESSION_UTIL_H_
|
||||
|
||||
34
oemcrypto/test/oemcrypto_corpus_generator_helper.cpp
Normal file
34
oemcrypto/test/oemcrypto_corpus_generator_helper.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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 "oemcrypto_corpus_generator_helper.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace wvoec {
|
||||
bool g_generate_corpus;
|
||||
|
||||
void AppendToFile(const std::string& file_name, const char* message,
|
||||
const size_t message_size) {
|
||||
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(message, message_size);
|
||||
filebuf.close();
|
||||
}
|
||||
|
||||
std::string GetFileName(const char* directory) {
|
||||
std::string file_name(PATH_TO_CORPUS);
|
||||
file_name += directory;
|
||||
file_name += "/";
|
||||
file_name += std::to_string(rand());
|
||||
return file_name;
|
||||
}
|
||||
|
||||
void SetGenerateCorpus(bool should_generate_corpus) {
|
||||
g_generate_corpus = should_generate_corpus;
|
||||
}
|
||||
bool ShouldGenerateCorpus() { return g_generate_corpus; }
|
||||
} // namespace wvoec
|
||||
25
oemcrypto/test/oemcrypto_corpus_generator_helper.h
Normal file
25
oemcrypto/test/oemcrypto_corpus_generator_helper.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* 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. */
|
||||
#ifndef CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_
|
||||
#define CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_
|
||||
|
||||
#define PATH_TO_CORPUS "./oemcrypto/test/fuzz_tests/corpus/"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
namespace wvoec {
|
||||
void AppendToFile(const std::string& file_name, const char* message,
|
||||
const size_t message_size);
|
||||
|
||||
std::string GetFileName(const char* directory);
|
||||
|
||||
void SetGenerateCorpus(bool should_generate_corpus);
|
||||
// Output of this function decides if binary data needs to be written
|
||||
// to corpus files or not. Controlled by --generate_corpus flag.
|
||||
bool ShouldGenerateCorpus();
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_
|
||||
@@ -81,6 +81,6 @@ void SessionUtil::InstallTestRSAKey(Session* s) {
|
||||
ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
}
|
||||
// Test RSA key should be loaded.
|
||||
ASSERT_NO_FATAL_FAILURE(s->GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_NO_FATAL_FAILURE(s->PreparePublicKey());
|
||||
}
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -14,12 +14,6 @@
|
||||
#include <openssl/x509.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -122,37 +116,6 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
|
||||
// const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576};
|
||||
// clang-format on
|
||||
|
||||
/** @return The Unix time of the given time point. */
|
||||
template <typename Duration>
|
||||
uint64_t UnixTime(
|
||||
const std::chrono::time_point<std::chrono::system_clock, Duration>& point) {
|
||||
return point.time_since_epoch() / std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
using NativeTime = SYSTEMTIME;
|
||||
#else
|
||||
using NativeTime = timeval;
|
||||
#endif
|
||||
|
||||
void AddNativeTime(int64_t delta_seconds, NativeTime* time) {
|
||||
#ifdef _WIN32
|
||||
// See remarks from this for why this series is used.
|
||||
// https://msdn.microsoft.com/en-us/f77cdf86-0f97-4a89-b565-95b46fa7d65b
|
||||
FILETIME file_time;
|
||||
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
|
||||
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
|
||||
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
|
||||
long_time +=
|
||||
delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
|
||||
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
|
||||
file_time.dwHighDateTime = long_time >> 32;
|
||||
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
|
||||
#else
|
||||
time->tv_sec += delta_seconds;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
@@ -191,13 +154,13 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
// 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.2. Tests last updated 2020-03-27";
|
||||
"OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01";
|
||||
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, 2);
|
||||
EXPECT_EQ(ODK_MINOR_VERSION, 3);
|
||||
EXPECT_EQ(kCurrentAPI, 16u);
|
||||
const char* level = OEMCrypto_SecurityLevel();
|
||||
ASSERT_NE(nullptr, level);
|
||||
@@ -725,6 +688,7 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
|
||||
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());
|
||||
// Request the OEM Cert. -- This should NOT load the OEM Private key.
|
||||
vector<uint8_t> public_cert;
|
||||
size_t public_cert_length = 0;
|
||||
@@ -890,7 +854,13 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
|
||||
license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION;
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||
|
||||
// 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));
|
||||
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
|
||||
}
|
||||
|
||||
// Verify that a license may be loaded with a nonce.
|
||||
@@ -1865,7 +1835,7 @@ TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) {
|
||||
LoadLicense();
|
||||
RenewalRoundTrip renewal_messages(&license_messages_);
|
||||
const size_t max_size = GetResourceValue(kLargeMessageSize);
|
||||
license_messages_.set_message_size(max_size);
|
||||
renewal_messages.set_message_size(max_size);
|
||||
MakeRenewalRequest(&renewal_messages);
|
||||
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
||||
}
|
||||
@@ -2233,10 +2203,11 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
void TestDecryptCENC() {
|
||||
OEMCryptoResult sts;
|
||||
|
||||
// OEMCrypto only supports providing a decrypt hash for one sample.
|
||||
if (samples_.size() > 1) verify_crc_ = false;
|
||||
|
||||
// If supported, check the decrypt hashes.
|
||||
if (verify_crc_) {
|
||||
// OEMCrypto only supports providing a decrypt hash for the first sample
|
||||
// in the sample array.
|
||||
const TestSample& sample = samples_[0];
|
||||
|
||||
uint32_t hash =
|
||||
@@ -2291,7 +2262,7 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
}
|
||||
}
|
||||
}
|
||||
if (global_features.supports_crc) {
|
||||
if (verify_crc_) {
|
||||
uint32_t frame;
|
||||
ASSERT_EQ(OEMCrypto_GetHashErrorCode(session_.session_id(), &frame),
|
||||
OEMCrypto_SUCCESS);
|
||||
@@ -2611,18 +2582,6 @@ TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) {
|
||||
session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||
}
|
||||
|
||||
// If analog is forbidden, then decrypt to a clear buffer should be forbidden.
|
||||
TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
||||
license_messages_.set_control(wvoec::kControlDisableAnalogOutput);
|
||||
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_ERROR_ANALOG_OUTPUT));
|
||||
}
|
||||
|
||||
// Test that key duration is honored.
|
||||
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||
@@ -4201,6 +4160,11 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeBuffer(size_t new_size) {
|
||||
buffer_size_ = new_size;
|
||||
InitializeClearBuffer(); // Re-initialize the clear buffer.
|
||||
}
|
||||
|
||||
void EncryptAndLoadKeys() {
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||
@@ -4536,7 +4500,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) {
|
||||
|
||||
// Test Generic_Encrypt with the maximum buffer size.
|
||||
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
|
||||
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
|
||||
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
|
||||
EncryptAndLoadKeys();
|
||||
unsigned int key_index = 0;
|
||||
vector<uint8_t> expected_encrypted;
|
||||
@@ -4559,7 +4523,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
|
||||
// Test Generic_Decrypt with the maximum buffer size.
|
||||
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) {
|
||||
// Some applications are known to pass in a block that is almost 400k.
|
||||
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
|
||||
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
|
||||
EncryptAndLoadKeys();
|
||||
unsigned int key_index = 1;
|
||||
vector<uint8_t> encrypted;
|
||||
@@ -4580,7 +4544,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) {
|
||||
|
||||
// Test Generic_Sign with the maximum buffer size.
|
||||
TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) {
|
||||
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
|
||||
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
|
||||
EncryptAndLoadKeys();
|
||||
unsigned int key_index = 2;
|
||||
vector<uint8_t> expected_signature;
|
||||
@@ -4608,7 +4572,7 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) {
|
||||
|
||||
// Test Generic_Verify with the maximum buffer size.
|
||||
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
|
||||
buffer_size_ = GetResourceValue(kMaxGenericBuffer);
|
||||
ResizeBuffer(GetResourceValue(kMaxGenericBuffer));
|
||||
EncryptAndLoadKeys();
|
||||
unsigned int key_index = 3;
|
||||
vector<uint8_t> signature;
|
||||
@@ -4917,6 +4881,7 @@ class LicenseWithUsageEntry {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||
ASSERT_NO_FATAL_FAILURE(session_.ReloadUsageEntry());
|
||||
ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_));
|
||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||
}
|
||||
|
||||
@@ -5719,19 +5684,29 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
|
||||
|
||||
void ShrinkHeader(uint32_t new_size,
|
||||
OEMCryptoResult expected_result = OEMCrypto_SUCCESS) {
|
||||
// We call OEMCrypto_ShrinkUsageTableHeader once with a zero length buffer,
|
||||
// so that OEMCrypto can tell us how big the buffer should be.
|
||||
size_t header_buffer_length = 0;
|
||||
OEMCryptoResult sts = OEMCrypto_ShrinkUsageTableHeader(
|
||||
new_size, nullptr, &header_buffer_length);
|
||||
// If we are expecting success, then the first call shall return
|
||||
// SHORT_BUFFER. However, if we are not expecting success, this first call
|
||||
// may return either SHORT_BUFFER or the expect error.
|
||||
if (expected_result == OEMCrypto_SUCCESS) {
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
} else {
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return;
|
||||
} else if (sts != OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||
// If we got any thing from the first call, it should be the expected
|
||||
// error, and we don't need to call a second time.
|
||||
ASSERT_EQ(expected_result, sts);
|
||||
return;
|
||||
}
|
||||
// If the first call resulted in SHORT_BUFFER, we should resize the buffer
|
||||
// and try again.
|
||||
ASSERT_LT(0u, header_buffer_length);
|
||||
encrypted_usage_header_.resize(header_buffer_length);
|
||||
sts = OEMCrypto_ShrinkUsageTableHeader(
|
||||
new_size, encrypted_usage_header_.data(), &header_buffer_length);
|
||||
// For the second call, we always demand the expected result.
|
||||
ASSERT_EQ(expected_result, sts);
|
||||
}
|
||||
};
|
||||
@@ -6030,6 +6005,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
|
||||
old_usage_entry_1.data(),
|
||||
old_usage_entry_1.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
|
||||
}
|
||||
|
||||
@@ -6200,44 +6176,12 @@ class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
OEMCryptoUsageTableTest::SetUp();
|
||||
did_change_system_time_ = false;
|
||||
test_start_steady_ = steady_clock_.now();
|
||||
#ifdef _WIN32
|
||||
GetSystemTime(&test_start_wall_);
|
||||
#else
|
||||
ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (did_change_system_time_) {
|
||||
const auto delta = steady_clock_.now() - test_start_steady_;
|
||||
const int64_t delta_sec = delta / std::chrono::seconds(1);
|
||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec));
|
||||
}
|
||||
wvcdm::TestSleep::ResetRollback();
|
||||
OEMCryptoUsageTableTest::TearDown();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets the current wall-clock time to a delta based on the start of the
|
||||
* test.
|
||||
*/
|
||||
void SetWallTimeDelta(int64_t delta_seconds) {
|
||||
did_change_system_time_ = true;
|
||||
NativeTime time = test_start_wall_;
|
||||
ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time));
|
||||
#ifdef _WIN32
|
||||
ASSERT_TRUE(SetSystemTime(&time));
|
||||
#else
|
||||
ASSERT_EQ(0, settimeofday(&time, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::chrono::steady_clock steady_clock_;
|
||||
bool did_change_system_time_;
|
||||
NativeTime test_start_wall_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> test_start_steady_;
|
||||
};
|
||||
|
||||
// NOTE: This test needs root access since clock_settime messes with the system
|
||||
@@ -6246,61 +6190,104 @@ class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
|
||||
// We don't test roll-forward protection or instances where the user rolls back
|
||||
// the time to the last decrypt call since this requires hardware-secure clocks
|
||||
// to guarantee.
|
||||
//
|
||||
// This test overlaps two tests in parallel because they each have several
|
||||
// seconds of sleeping, then we roll the system clock back, and then we sleep
|
||||
// some more.
|
||||
// For the first test, we use entry1. The playback duration is 6 short
|
||||
// intervals. We play for 3, roll the clock back 2, and then play for 3 more.
|
||||
// We then sleep until after the allowed playback duration and try to play. If
|
||||
// OEMCrypto allows the rollback, then there is only 5 intervals, which is
|
||||
// legal. But if OEMCrypto forbids the rollback, then there is 8 intervals of
|
||||
// playback, which is not legal.
|
||||
//
|
||||
// For the second test, we use entry2. The rental duration is 6 short
|
||||
// intervals. The times are the same as for entry1, except we do not start
|
||||
// playback for entry2 until the end.
|
||||
|
||||
// clang-format off
|
||||
// [--][--][--][--][--][--][--] -- playback or rental limit.
|
||||
//
|
||||
// Here's what the system clock sees with rollback:
|
||||
// [--][--][--] 3 short intervals of playback or sleep
|
||||
// <------> Rollback 2 short intervals.
|
||||
// [--][--][--] 3 short intervals of playback or sleep
|
||||
// [--] 1 short intervals of sleep.
|
||||
//
|
||||
// Here's what the system clock sees without rollback:
|
||||
// [--][--][--] 3 short intervals of playback or sleep
|
||||
// [--][--][--] 3 short intervals of playback or sleep
|
||||
// [--][--]X 2 short intervals of sleep.
|
||||
//
|
||||
// |<---------------------------->| 8 short intervals from license received
|
||||
// until pst reports generated.
|
||||
// clang-format on
|
||||
|
||||
TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
|
||||
cout << "This test temporarily rolls back the system time in order to verify "
|
||||
<< "that the usage report accounts for the change. It then rolls "
|
||||
<< "the time back forward to the absolute time." << endl;
|
||||
LicenseWithUsageEntry entry;
|
||||
entry.MakeOfflineAndClose(this);
|
||||
Session& s = entry.session();
|
||||
std::chrono::system_clock wall_clock;
|
||||
std::chrono::steady_clock monotonic_clock;
|
||||
const auto loaded = wall_clock.now();
|
||||
<< "that the usage report accounts for the change. After the test, it "
|
||||
<< "rolls the clock back forward." << endl;
|
||||
constexpr int kRollBackTime = kShortSleep * 2;
|
||||
constexpr int kPlaybackCount = 3;
|
||||
constexpr int kTotalTime = kShortSleep * 8;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||
const auto first_decrypt = wall_clock.now();
|
||||
// Monotonic clock can't be changed. We use this since system clock will be
|
||||
// unreliable.
|
||||
const auto first_decrypt_monotonic = monotonic_clock.now();
|
||||
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
|
||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
LicenseWithUsageEntry entry1;
|
||||
entry1.license_messages()
|
||||
.core_response()
|
||||
.timer_limits.total_playback_duration_seconds = 7 * kShortSleep;
|
||||
entry1.MakeOfflineAndClose(this);
|
||||
Session& s1 = entry1.session();
|
||||
ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this));
|
||||
|
||||
// Imitate playback.
|
||||
wvcdm::TestSleep::Sleep(kLongDuration * 2);
|
||||
LicenseWithUsageEntry entry2;
|
||||
entry2.license_messages()
|
||||
.core_response()
|
||||
.timer_limits.rental_duration_seconds = 7 * kShortSleep;
|
||||
entry2.MakeOfflineAndClose(this);
|
||||
Session& s2 = entry2.session();
|
||||
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
|
||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
// Start with three short intervals of playback for entry1.
|
||||
for (int i = 0; i < kPlaybackCount; i++) {
|
||||
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
|
||||
wvcdm::TestSleep::Sleep(kShortSleep);
|
||||
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
|
||||
}
|
||||
|
||||
// Rollback the wall clock time.
|
||||
cout << "Rolling the system time back..." << endl;
|
||||
ASSERT_TRUE(wvcdm::TestSleep::RollbackSystemTime(kRollBackTime));
|
||||
|
||||
// Three more short intervals of playback after the rollback.
|
||||
for (int i = 0; i < kPlaybackCount; i++) {
|
||||
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
|
||||
wvcdm::TestSleep::Sleep(kShortSleep);
|
||||
ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR());
|
||||
}
|
||||
|
||||
// One short interval of sleep to push us past the 6 interval duration.
|
||||
wvcdm::TestSleep::Sleep(2 * kShortSleep);
|
||||
|
||||
// Should not be able to continue playback in entry1.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetWallTimeDelta(-static_cast<int64_t>(kLongDuration) * 10));
|
||||
|
||||
// Try to playback again.
|
||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||
const auto third_decrypt_monotonic = monotonic_clock.now();
|
||||
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
|
||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
Test_PST_Report expected(entry.pst(), kActive);
|
||||
|
||||
// Restore wall clock to its original position to verify that OEMCrypto does
|
||||
// not report negative times.
|
||||
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
|
||||
cout << "Rolling the system time forward to the absolute time..." << endl;
|
||||
entry1.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
|
||||
// Should not be able to start playback in entry2.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetWallTimeDelta(test_duration / std::chrono::seconds(1)));
|
||||
// Need to update time created since the verification checks the time of PST
|
||||
// report creation.
|
||||
expected.time_created = UnixTime(wall_clock.now());
|
||||
entry2.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED));
|
||||
|
||||
const auto end_time = first_decrypt + test_duration;
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyReport(
|
||||
expected, UnixTime(loaded), UnixTime(first_decrypt), UnixTime(end_time)));
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
// Now we look at the usage reports:
|
||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(entry1.pst()));
|
||||
wvcdm::Unpacked_PST_Report report1 = s1.pst_report();
|
||||
EXPECT_EQ(report1.status(), kActive);
|
||||
EXPECT_GE(report1.seconds_since_license_received(), kTotalTime);
|
||||
EXPECT_GE(report1.seconds_since_first_decrypt(), kTotalTime);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(entry2.pst()));
|
||||
wvcdm::Unpacked_PST_Report report2 = s2.pst_report();
|
||||
EXPECT_EQ(report2.status(), kUnused);
|
||||
EXPECT_GE(report2.seconds_since_license_received(), kTotalTime);
|
||||
}
|
||||
|
||||
// Verify that a large PST can be used with usage table entries.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oemcrypto_corpus_generator_helper.h"
|
||||
#include "test_sleep.h"
|
||||
|
||||
static void acknowledge_cast() {
|
||||
@@ -23,6 +24,9 @@ int main(int argc, char** argv) {
|
||||
// Skip the first element, which is the program name.
|
||||
const std::vector<std::string> args(argv + 1, argv + argc);
|
||||
for (const std::string& arg : args) {
|
||||
if (arg == "--generate_corpus") {
|
||||
wvoec::SetGenerateCorpus(true);
|
||||
}
|
||||
if (arg == "--verbose" || arg == "-v") {
|
||||
++verbosity;
|
||||
} else if (arg == "--cast") {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
'oec_decrypt_fallback_chain.cpp',
|
||||
'oec_key_deriver.cpp',
|
||||
'oec_session_util.cpp',
|
||||
'oemcrypto_corpus_generator_helper.cpp',
|
||||
'oemcrypto_session_tests_helper.cpp',
|
||||
'oemcrypto_test.cpp',
|
||||
'wvcrc.cpp',
|
||||
@@ -20,6 +21,7 @@
|
||||
'<(oemcrypto_dir)/include',
|
||||
'<(oemcrypto_dir)/ref/src',
|
||||
'<(oemcrypto_dir)/test',
|
||||
'<(oemcrypto_dir)/test/fuzz_tests',
|
||||
'<(oemcrypto_dir)/odk/include',
|
||||
],
|
||||
'defines': [
|
||||
|
||||
Reference in New Issue
Block a user