Widevine MediaCas client code that works with Android R
This commit is contained in:
168
oemcrypto/odk/test/fuzzing/Android.bp
Normal file
168
oemcrypto/odk/test/fuzzing/Android.bp
Normal file
@@ -0,0 +1,168 @@
|
||||
// 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "odk_fuzz_library_defaults",
|
||||
srcs: [
|
||||
"odk_fuzz_helper.cpp",
|
||||
],
|
||||
include_dirs: [
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/odk/test",
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/odk/include",
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/odk/src",
|
||||
],
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_license_request_fuzz",
|
||||
srcs: [
|
||||
"odk_license_request_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/license_request_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_renewal_request_fuzz",
|
||||
srcs: [
|
||||
"odk_renewal_request_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/renewal_request_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_provisioning_request_fuzz",
|
||||
srcs: [
|
||||
"odk_provisioning_request_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/provisioning_request_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_license_response_fuzz",
|
||||
srcs: [
|
||||
"odk_license_response_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_renewal_response_fuzz",
|
||||
srcs: [
|
||||
"odk_renewal_response_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_provisioning_response_fuzz",
|
||||
srcs: [
|
||||
"odk_provisioning_response_fuzz.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_license_response_fuzz_with_mutator",
|
||||
srcs: [
|
||||
"odk_license_response_fuzz_with_mutator.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_renewal_response_fuzz_with_mutator",
|
||||
srcs: [
|
||||
"odk_renewal_response_fuzz_with_mutator.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "odk_provisioning_response_fuzz_with_mutator",
|
||||
srcs: [
|
||||
"odk_provisioning_response_fuzz_with_mutator.cpp",
|
||||
],
|
||||
fuzz_config: {
|
||||
componentid: 611718,
|
||||
},
|
||||
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
|
||||
static_libs: [
|
||||
"libwv_kdo",
|
||||
"libwv_odk",
|
||||
],
|
||||
defaults: ["odk_fuzz_library_defaults"],
|
||||
proprietary: true,
|
||||
}
|
||||
19
oemcrypto/odk/test/fuzzing/README.md
Normal file
19
oemcrypto/odk/test/fuzzing/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# ODK Fuzzing
|
||||
|
||||
## Objective
|
||||
|
||||
* Run fuzzing on ODK and KDO serialize and deserialize APIs using google
|
||||
supported fuzzer engines to find security vulnerabilities. Any issues found
|
||||
by clusterfuzz will be reported to
|
||||
[odk fuzz buganizer](https://b.corp.google.com/issues?q=componentid:425099%20status:open%20reporter:cluster-fuzz-googleplex@google.com).
|
||||
|
||||
## Run fuzz target on local machine
|
||||
|
||||
* In order to run fuzz target locally and see code coverage, save binary input
|
||||
to be tested against fuzz target into a temporary corpus directory and
|
||||
execute following commands
|
||||
|
||||
```shell
|
||||
$ blaze build --config=asan-fuzzer //your:target
|
||||
$ blaze-bin/your/target FULL_CORPUS_DIR
|
||||
```
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"componentid":425099}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27
oemcrypto/odk/test/fuzzing/corpus_generator/Android.bp
Normal file
27
oemcrypto/odk/test/fuzzing/corpus_generator/Android.bp
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.
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Builds odk_corpus_generator shared library, which can be used with
|
||||
// LD_PRELOAD command to generate corpus by intercepting oemcrypto
|
||||
// unit tests.
|
||||
// ----------------------------------------------------------------
|
||||
// Builds libwv_odk.so, The ODK shared Library (libwv_odk) is used
|
||||
// by the OEMCrypto unit tests to generate corpus for ODK fuzz scrips.
|
||||
cc_library_shared {
|
||||
name: "libwv_odk_corpus_generator",
|
||||
include_dirs: [
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/include",
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/odk/include",
|
||||
"vendor/widevine/libwvmediacas/oemcrypto/odk/test",
|
||||
],
|
||||
host_ldlibs: ["-ldl"],
|
||||
srcs: [
|
||||
"odk_corpus_generator.c",
|
||||
"odk_corpus_generator_helper.c",
|
||||
],
|
||||
proprietary: true,
|
||||
|
||||
owner: "widevine",
|
||||
}
|
||||
79
oemcrypto/odk/test/fuzzing/corpus_generator/README.md
Normal file
79
oemcrypto/odk/test/fuzzing/corpus_generator/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Objective
|
||||
|
||||
The Idea behind the corpus generator code is to intercept OEMCrypto unit test
|
||||
calls to odk APIs using LD_PRELOAD and read the data into corpus files which can
|
||||
be fed as corpus to fuzzer scripts.
|
||||
|
||||
LD_PRELOAD command needs to be run from cdm repository while running oemcrypto
|
||||
unit tests.
|
||||
|
||||
## Get OEMCrypto and Build OEMCrypto unit tests:
|
||||
|
||||
* 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
|
||||
```
|
||||
|
||||
* We need to run odk as a dynamic library in order to use LD_PRELOAD, apply
|
||||
patch from go/wvgerrit/95090 to locally cloned repo which has changes to run
|
||||
odk as dynamic library:
|
||||
|
||||
```shell
|
||||
$ cd /path/to/cdm/repo
|
||||
$ git fetch origin 209721cc901745999e08e35466e74f708321267e
|
||||
$ git cherry-pick FETCH_HEAD
|
||||
```
|
||||
|
||||
* Build OEMCrypto unit tests:
|
||||
|
||||
```shell
|
||||
$ cd /path/to/cdm/repo
|
||||
$ export PATH_TO_CDM_DIR=..
|
||||
$ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp
|
||||
$ ninja -C out/Default/
|
||||
```
|
||||
|
||||
## Capture corpus for odk fuzzer by intercepting OEMCrypto unit tests:
|
||||
|
||||
When we run LD_PRELOAD command odk_corpus_generator.so gets preloaded before
|
||||
oemcrypto_unittests and odk_corpus_generator has functions to intercept calls to
|
||||
ODK request and response APIs. Each call to odk API from oemcrypto_unittests
|
||||
gets intercepted and input to ODK de serialize response APIs and output from ODK
|
||||
serialize request APIs is captured in binary format and stored into corpus files
|
||||
|
||||
In order to run LD_PRELOAD command, we need to compile corpus generator shared
|
||||
library and need to preload that before OEMCrypto unit tests run
|
||||
|
||||
* Compile shared library
|
||||
|
||||
```shell
|
||||
$ cd /path/to/cdm/repo
|
||||
$ gyp --format=ninja --depth=$(pwd) oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp
|
||||
$ ninja -C out/Default/
|
||||
```
|
||||
|
||||
* Preload the shared library before running OEMCrypto unit tests
|
||||
|
||||
```shell
|
||||
$ cd oemcrypto/odk/test/fuzzing/corpus
|
||||
$ mkdir license_request_corpus license_response_corpus renewal_request_corpus renewal_response_corpus provisioning_request_corpus provisioning_response_corpus
|
||||
$ cd /path/to/cdm/repo
|
||||
$ LD_PRELOAD=out/Default/lib/libodk_corpus_generator.so ./out/Default/oemcrypto_unittests
|
||||
```
|
||||
|
||||
LD_PRELOAD command runs oemcrypto_unittests with odk_corpus_generator as
|
||||
interceptor. We should see unit tests being executed. The corpus files in binary
|
||||
format will be captured into `oemcrypto/odk/test/fuzzing/corpus` path. These
|
||||
files can be used as input corpus for ODK request and response fuzzer scripts.
|
||||
|
||||
The generated corpus files can be minimized using go/testcorpus#minimize and
|
||||
uploaded into google3 repository under following directory under respective
|
||||
corpus types
|
||||
`fuzzing/corpus`
|
||||
@@ -0,0 +1,160 @@
|
||||
/* 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. */
|
||||
|
||||
/* We must define this macro to get RTLD_NEXT definition from <dlfcn.h> */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "fuzzing/corpus_generator/odk_corpus_generator_helper.h"
|
||||
#include "fuzzing/odk_fuzz_structs.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*,
|
||||
const ODK_NonceValues*);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreLicenseRequest");
|
||||
OEMCryptoResult oem_crypto_result = (*original_function)(
|
||||
message, message_length, core_message_length, nonce_values);
|
||||
char* file_name = GetFileName("license_request_corpus");
|
||||
|
||||
/* License Request format expected by fuzzer - [Core License Request] */
|
||||
AppendToFile(file_name, (const char*)message, *core_message_length);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ParseLicense(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
bool initial_license_load, bool usage_entry_present,
|
||||
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_license) {
|
||||
struct ODK_ParseLicense_Args parse_license_args;
|
||||
parse_license_args.nonce_values = *nonce_values;
|
||||
memcpy(parse_license_args.request_hash, request_hash, ODK_SHA256_HASH_SIZE);
|
||||
parse_license_args.timer_limits = *timer_limits;
|
||||
parse_license_args.clock_values = *clock_values;
|
||||
parse_license_args.usage_entry_present = usage_entry_present;
|
||||
parse_license_args.initial_license_load = initial_license_load;
|
||||
OEMCryptoResult (*original_function)(
|
||||
const uint8_t*, size_t, size_t, bool, bool, const uint8_t*,
|
||||
ODK_TimerLimits*, ODK_ClockValues*, ODK_NonceValues*, ODK_ParsedLicense*);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_ParseLicense");
|
||||
OEMCryptoResult oem_crypto_result = (*original_function)(
|
||||
message, message_length, core_message_length, initial_license_load,
|
||||
usage_entry_present, request_hash, timer_limits, clock_values,
|
||||
nonce_values, parsed_license);
|
||||
char* file_name = GetFileName("license_response_corpus");
|
||||
|
||||
/* License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core
|
||||
*/
|
||||
/* License Response] */
|
||||
AppendToFile(file_name, (const char*)&parse_license_args,
|
||||
sizeof(struct ODK_ParseLicense_Args));
|
||||
AppendToFile(file_name, (const char*)message, core_message_length);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t* core_message_size,
|
||||
ODK_NonceValues* nonce_values,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds) {
|
||||
OEMCryptoResult (*original_function)(
|
||||
uint8_t*, size_t, size_t*, ODK_NonceValues*, ODK_ClockValues*, uint64_t);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreRenewalRequest");
|
||||
OEMCryptoResult oem_crypto_result =
|
||||
(*original_function)(message, message_length, core_message_size,
|
||||
nonce_values, clock_values, system_time_seconds);
|
||||
char* file_name = GetFileName("renewal_request_corpus");
|
||||
|
||||
/* License Request format expected by fuzzer - [ODK_ClockValues][Core */
|
||||
/* License Request] */
|
||||
AppendToFile(file_name, (const char*)clock_values, sizeof(ODK_ClockValues));
|
||||
AppendToFile(file_name, (const char*)message, *core_message_size);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
size_t core_message_length,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
uint64_t system_time,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
struct ODK_ParseRenewal_Args parse_renewal_args;
|
||||
parse_renewal_args.nonce_values = *nonce_values;
|
||||
parse_renewal_args.clock_values = *clock_values;
|
||||
parse_renewal_args.timer_limits = *timer_limits;
|
||||
parse_renewal_args.system_time = system_time;
|
||||
OEMCryptoResult (*original_function)(
|
||||
const uint8_t*, size_t, size_t, const ODK_NonceValues*, uint64_t,
|
||||
const ODK_TimerLimits*, ODK_ClockValues*, uint64_t*);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_ParseRenewal");
|
||||
OEMCryptoResult oem_crypto_result = (*original_function)(
|
||||
message, message_length, core_message_length, nonce_values, system_time,
|
||||
timer_limits, clock_values, timer_value);
|
||||
char* file_name = GetFileName("renewal_response_corpus");
|
||||
|
||||
/* Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core
|
||||
*/
|
||||
/* Renewal Response] */
|
||||
AppendToFile(file_name, (const char*)&parse_renewal_args,
|
||||
sizeof(struct ODK_ParseRenewal_Args));
|
||||
AppendToFile(file_name, (const char*)message, core_message_length);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length) {
|
||||
OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*,
|
||||
const ODK_NonceValues*, const uint8_t*,
|
||||
size_t);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreProvisioningRequest");
|
||||
OEMCryptoResult oem_crypto_result =
|
||||
(*original_function)(message, message_length, core_message_length,
|
||||
nonce_values, device_id, device_id_length);
|
||||
char* file_name = GetFileName("provisioning_request_corpus");
|
||||
|
||||
/* Provisioning Request format expected by fuzzer - [Core Provisioning */
|
||||
/* Request] */
|
||||
AppendToFile(file_name, (const char*)message, *core_message_length);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ParseProvisioning(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
|
||||
struct ODK_ParseProvisioning_Args parse_provisioning_args;
|
||||
parse_provisioning_args.nonce_values = *nonce_values;
|
||||
memcpy(parse_provisioning_args.device_id, device_id, device_id_length);
|
||||
parse_provisioning_args.device_id_length = device_id_length;
|
||||
OEMCryptoResult (*original_function)(const uint8_t*, size_t, size_t,
|
||||
const ODK_NonceValues*, const uint8_t*,
|
||||
size_t, ODK_ParsedProvisioning*);
|
||||
original_function = dlsym(RTLD_NEXT, "ODK_ParseProvisioning");
|
||||
OEMCryptoResult oem_crypto_result = (*original_function)(
|
||||
message, message_length, core_message_length, nonce_values, device_id,
|
||||
device_id_length, parsed_response);
|
||||
char* file_name = GetFileName("provisioning_response_corpus");
|
||||
|
||||
/* Provisioning Response format expected by fuzzer - */
|
||||
/* [ODK_ParseProvisioning_Args][Core Provisioning Response] */
|
||||
AppendToFile(file_name, (const char*)&parse_provisioning_args,
|
||||
sizeof(struct ODK_ParseProvisioning_Args));
|
||||
AppendToFile(file_name, (const char*)message, core_message_length);
|
||||
free(file_name);
|
||||
return oem_crypto_result;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* 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 "fuzzing/corpus_generator/odk_corpus_generator_helper.h"
|
||||
|
||||
void AppendToFile(const char* file_name, const char* message,
|
||||
const size_t message_size) {
|
||||
FILE* fptr;
|
||||
if ((fptr = fopen(file_name, "ab")) == NULL) {
|
||||
printf("Error! opening file %s", file_name);
|
||||
return;
|
||||
}
|
||||
fwrite(message, message_size, 1, fptr);
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
char* GetFileName(const char* directory) {
|
||||
char* file_name;
|
||||
file_name = malloc(150);
|
||||
sprintf(file_name, "%s%s/%d", PATH_TO_CORPUS, directory, rand());
|
||||
return file_name;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* 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 WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
|
||||
#define WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
|
||||
|
||||
#define PATH_TO_CORPUS "./oemcrypto/odk/test/fuzzing/corpus/"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void AppendToFile(const char* file_name, const char* message,
|
||||
const size_t message_size);
|
||||
|
||||
char* GetFileName(const char* directory);
|
||||
|
||||
#endif /* WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ \
|
||||
*/
|
||||
159
oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp
Normal file
159
oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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 "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
bool convert_byte_to_valid_boolean(const bool* in) {
|
||||
const char* buf = reinterpret_cast<const char*>(in);
|
||||
for (int i = 0; i < sizeof(bool); i++) {
|
||||
if (buf[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConvertDataToValidBools(ODK_ParsedLicense* t) {
|
||||
// Convert boolean flags in parsed_license to valid bytes to
|
||||
// avoid errors from msan
|
||||
t->nonce_required = convert_byte_to_valid_boolean(&t->nonce_required);
|
||||
t->timer_limits.soft_enforce_playback_duration =
|
||||
convert_byte_to_valid_boolean(
|
||||
&t->timer_limits.soft_enforce_playback_duration);
|
||||
t->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
|
||||
&t->timer_limits.soft_enforce_rental_duration);
|
||||
}
|
||||
|
||||
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t) {}
|
||||
|
||||
void ConvertDataToValidBools(ODK_ParsedProvisioning* t) {}
|
||||
|
||||
OEMCryptoResult odk_serialize_LicenseRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_LicenseRequest& core_license_request,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
|
||||
}
|
||||
|
||||
OEMCryptoResult odk_serialize_RenewalRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
|
||||
ODK_ClockValues clock{};
|
||||
memcpy(&clock, in, sizeof(ODK_ClockValues));
|
||||
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
|
||||
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
|
||||
&clock, system_time_seconds);
|
||||
}
|
||||
|
||||
OEMCryptoResult odk_serialize_ProvisioningRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_ProvisioningRequest& core_provisioning,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
const std::string& device_id = core_provisioning.device_id;
|
||||
return ODK_PrepareCoreProvisioningRequest(
|
||||
out, SIZE_MAX, size, nonce_values,
|
||||
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
|
||||
}
|
||||
|
||||
OEMCryptoResult odk_deserialize_LicenseResponse(const uint8_t* message,
|
||||
size_t core_message_length,
|
||||
ODK_ParseLicense_Args* a,
|
||||
ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_lic) {
|
||||
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
|
||||
static_cast<bool>(a->initial_license_load),
|
||||
static_cast<bool>(a->usage_entry_present),
|
||||
a->request_hash, &a->timer_limits, &a->clock_values,
|
||||
nonce_values, parsed_lic);
|
||||
}
|
||||
|
||||
OEMCryptoResult odk_deserialize_RenewalResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
|
||||
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
|
||||
* variables. Input from fuzzer can be parsed and any random bytes can be
|
||||
* assigned to boolean variables. Using the workaround to mitigate sanitizer
|
||||
* errors in fuzzer code and converting random bytes to 0 OR 1.
|
||||
* This has no negative security impact*/
|
||||
a->timer_limits.soft_enforce_playback_duration =
|
||||
convert_byte_to_valid_boolean(
|
||||
&a->timer_limits.soft_enforce_playback_duration);
|
||||
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
|
||||
&a->timer_limits.soft_enforce_rental_duration);
|
||||
uint64_t timer_value = 0;
|
||||
OEMCryptoResult err =
|
||||
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
|
||||
&a->timer_limits, &a->clock_values, &timer_value);
|
||||
const bool is_parse_renewal_response_successful =
|
||||
err == ODK_SET_TIMER || err == ODK_DISABLE_TIMER ||
|
||||
err == ODK_TIMER_EXPIRED || err == ODK_STALE_RENEWAL;
|
||||
if (!is_parse_renewal_response_successful) {
|
||||
return err;
|
||||
}
|
||||
// In order to capture playback_time information which is part of
|
||||
// renewal_msg and will be later used in kdo_serialize_RenewalResponse in
|
||||
// odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method.
|
||||
// playback_time cannot be captured from publicly exposed API
|
||||
// ODK_ParseRenewal.
|
||||
Message* msg = nullptr;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, const_cast<uint8_t*>(buf), len);
|
||||
SetSize(msg, len);
|
||||
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult odk_deserialize_ProvisioningResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
|
||||
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
|
||||
a->device_id_length, parsed_prov);
|
||||
}
|
||||
|
||||
bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
|
||||
const ODK_ParsedLicense& parsed_lic,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
|
||||
nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id};
|
||||
std::string core_request_sha_256(
|
||||
reinterpret_cast<const char*>(args->request_hash), ODK_SHA256_HASH_SIZE);
|
||||
return serialize::CreateCoreLicenseResponse(
|
||||
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
|
||||
}
|
||||
|
||||
bool kdo_serialize_RenewalResponse(
|
||||
const ODK_ParseRenewal_Args* args,
|
||||
const ODK_PreparedRenewalRequest& renewal_msg,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
ODK_RenewalRequest core_request{
|
||||
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
|
||||
return serialize::CreateCoreRenewalResponse(
|
||||
core_request, args->timer_limits.initial_renewal_duration_seconds,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
bool kdo_serialize_ProvisioningResponse(
|
||||
const ODK_ParseProvisioning_Args* args,
|
||||
const ODK_ParsedProvisioning& parsed_prov,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
if (args->device_id_length > sizeof(args->device_id)) {
|
||||
return false;
|
||||
}
|
||||
ODK_ProvisioningRequest core_request{
|
||||
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id,
|
||||
std::string(reinterpret_cast<const char*>(args->device_id),
|
||||
args->device_id_length)};
|
||||
return serialize::CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
206
oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h
Normal file
206
oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/* 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 WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
|
||||
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "core_message_serialize.h"
|
||||
#include "fuzzing/odk_fuzz_structs.h"
|
||||
#include "odk_serialize.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
bool convert_byte_to_valid_boolean(const bool* in);
|
||||
|
||||
OEMCryptoResult odk_serialize_LicenseRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_LicenseRequest& core_license_request,
|
||||
const ODK_NonceValues* nonce_values);
|
||||
|
||||
OEMCryptoResult odk_serialize_RenewalRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values);
|
||||
|
||||
OEMCryptoResult odk_serialize_ProvisioningRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_ProvisioningRequest& core_provisioning,
|
||||
const ODK_NonceValues* nonce_values);
|
||||
|
||||
OEMCryptoResult odk_deserialize_LicenseResponse(const uint8_t* message,
|
||||
size_t core_message_length,
|
||||
ODK_ParseLicense_Args* a,
|
||||
ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_lic);
|
||||
|
||||
OEMCryptoResult odk_deserialize_RenewalResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg);
|
||||
|
||||
OEMCryptoResult odk_deserialize_ProvisioningResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov);
|
||||
|
||||
bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
|
||||
const ODK_ParsedLicense& parsed_lic,
|
||||
std::string* oemcrypto_core_message);
|
||||
|
||||
bool kdo_serialize_RenewalResponse(
|
||||
const ODK_ParseRenewal_Args* args,
|
||||
const ODK_PreparedRenewalRequest& renewal_msg,
|
||||
std::string* oemcrypto_core_message);
|
||||
|
||||
bool kdo_serialize_ProvisioningResponse(
|
||||
const ODK_ParseProvisioning_Args* args,
|
||||
const ODK_ParsedProvisioning& parsed_prov,
|
||||
std::string* oemcrypto_core_message);
|
||||
|
||||
/* Idea behind having three different functions is: */
|
||||
/* Only ODK_ParseLicense structure had fields which needed additional */
|
||||
/* procession. Having a single function with templated parameter T was */
|
||||
/* failing during compile time because other two structures doesn't have */
|
||||
/* fields that need additional processing. Hence to reduce code redundance and
|
||||
*/
|
||||
/* make us of common FuzzerMutateResponse across three response fuzzers, */
|
||||
/* three independent functions were defined and renewal and provisioning */
|
||||
/* functions would be empty as no additional processing is needed for them. */
|
||||
void ConvertDataToValidBools(ODK_ParsedLicense* t);
|
||||
|
||||
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t);
|
||||
|
||||
void ConvertDataToValidBools(ODK_ParsedProvisioning* t);
|
||||
|
||||
/* Forward-declare the libFuzzer's mutator callback. Mark it weak so that */
|
||||
/* the program links successfully even outside of --config=asan-fuzzer */
|
||||
/* (apparently the only config in which LLVM uses our custom mutator). */
|
||||
extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize)
|
||||
__attribute__((weak));
|
||||
|
||||
template <typename A, typename T, typename F, typename G>
|
||||
size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
|
||||
const F& odk_deserialize_fun,
|
||||
const G& kdo_serialize_fun) {
|
||||
const size_t kArgsSize = sizeof(A);
|
||||
const size_t kCoreResponseSize = sizeof(T);
|
||||
const size_t kTotalResponseSize = kArgsSize + kCoreResponseSize;
|
||||
|
||||
/* Deserializing data in order to make sure it deserializes properly. */
|
||||
/* Input byte array format: [function arguments][data to parse]. */
|
||||
std::shared_ptr<A> _args(new A());
|
||||
A* args = _args.get();
|
||||
memcpy(args, data, kArgsSize);
|
||||
ODK_NonceValues nonce_values = args->nonce_values;
|
||||
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||
const uint8_t* buf = data + kArgsSize;
|
||||
T t = {};
|
||||
OEMCryptoResult result =
|
||||
odk_deserialize_fun(buf, size - kArgsSize, args, &nonce_values, &t);
|
||||
|
||||
/* If data doesn't deserialize successfully, We copy random bytes into */
|
||||
/* T and serialize using kdo function */
|
||||
/* which will create a valid oemcrypto core message using */
|
||||
/* nonce and request hash from function args. OEMCrypto core message acts as
|
||||
*/
|
||||
/* input to odk_kdo. */
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
if (max_size < kTotalResponseSize) {
|
||||
return 0;
|
||||
}
|
||||
/* Initialize remaining bytes needed in data to zero. */
|
||||
if (size < kTotalResponseSize) {
|
||||
memset(data + size, 0, kTotalResponseSize - size);
|
||||
}
|
||||
t = {};
|
||||
memcpy(&t, buf, kCoreResponseSize);
|
||||
}
|
||||
|
||||
/* Ask LLVM to run its usual mutations, hopefully giving us interesting */
|
||||
/* inputs. We copy deserialized data into pointer data, run mutations */
|
||||
/* and copy back the mutated data to args and t */
|
||||
memcpy(data + kArgsSize, &t, kCoreResponseSize);
|
||||
LLVMFuzzerMutate(data, kTotalResponseSize, kTotalResponseSize);
|
||||
memcpy(args, data, kArgsSize);
|
||||
memcpy(&t, data + kArgsSize, kCoreResponseSize);
|
||||
/* Convert boolean flags in parsed message to valid bytes to */
|
||||
/* avoid errors from msan. Only needed for parsed license. */
|
||||
ConvertDataToValidBools(&t);
|
||||
/* Serialize the data after mutation. */
|
||||
std::string oemcrypto_core_message;
|
||||
if (!kdo_serialize_fun(args, t, &oemcrypto_core_message)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy mutated and serialized oemcrypto_core_message to data */
|
||||
/* so that it acts as input to odk_kdo function. */
|
||||
memcpy(data + kArgsSize, oemcrypto_core_message.data(),
|
||||
oemcrypto_core_message.size());
|
||||
return kArgsSize + oemcrypto_core_message.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Template arguments:
|
||||
* A: struct holding function arguments
|
||||
* T: odk deserialize output/kdo serialize input structure
|
||||
* F: odk deserialize function
|
||||
* G: kdo serialize function
|
||||
*
|
||||
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||
*/
|
||||
template <typename A, typename T, typename F, typename G>
|
||||
void odk_kdo(const F& odk_fun, const G& kdo_fun, const uint8_t* in,
|
||||
const size_t size, const size_t args_size, uint8_t* out) {
|
||||
T t = {};
|
||||
/* Input byte array format: [function arguments][data to parse] */
|
||||
if (size < args_size) {
|
||||
return;
|
||||
}
|
||||
const uint8_t* buf = in + args_size;
|
||||
std::shared_ptr<A> _args(new A());
|
||||
A* args = _args.get();
|
||||
memcpy(args, in, args_size);
|
||||
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||
ODK_NonceValues nonce_values = args->nonce_values;
|
||||
|
||||
OEMCryptoResult result =
|
||||
odk_fun(buf, size - args_size, args, &nonce_values, &t);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
std::string oemcrypto_core_message;
|
||||
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template arguments:
|
||||
* T: kdo deserialize output/odk serialize input structure
|
||||
* F: kdo deserialize function
|
||||
* G: odk serialize function
|
||||
*
|
||||
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||
*/
|
||||
template <typename T, typename F, typename G>
|
||||
static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in,
|
||||
size_t size, const size_t clock_value_size, uint8_t* out) {
|
||||
if (size <= clock_value_size) {
|
||||
return;
|
||||
}
|
||||
/* Input byte array format: [Clock Values][data to parse]. */
|
||||
/* Only Renewal Request expects clock values to be present. */
|
||||
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
|
||||
size - clock_value_size);
|
||||
T t = {};
|
||||
if (!kdo_fun(input, &t)) {
|
||||
return;
|
||||
}
|
||||
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
|
||||
t.nonce, t.session_id};
|
||||
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
|
||||
if (OEMCrypto_SUCCESS != err) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} /* namespace oemcrypto_core_message */
|
||||
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ */
|
||||
28
oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h
Normal file
28
oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h
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. */
|
||||
#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
|
||||
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
|
||||
|
||||
#include "odk_structs.h"
|
||||
|
||||
struct ODK_ParseLicense_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
uint8_t initial_license_load;
|
||||
uint8_t usage_entry_present;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
};
|
||||
struct ODK_ParseRenewal_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
uint64_t system_time;
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
};
|
||||
struct ODK_ParseProvisioning_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
size_t device_id_length;
|
||||
uint8_t device_id[64];
|
||||
};
|
||||
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ */
|
||||
22
oemcrypto/odk/test/fuzzing/odk_license_request_fuzz.cpp
Normal file
22
oemcrypto/odk/test/fuzzing/odk_license_request_fuzz.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> out(size);
|
||||
const size_t kClockValueSize = 0;
|
||||
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
|
||||
odk_serialize_LicenseRequest, data, size,
|
||||
kClockValueSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
20
oemcrypto/odk/test/fuzzing/odk_license_response_fuzz.cpp
Normal file
20
oemcrypto/odk/test/fuzzing/odk_license_response_fuzz.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
||||
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data,
|
||||
size, kLicenseResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
@@ -0,0 +1,35 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
// The custom mutator: Ensure that each input can be deserialized properly
|
||||
// by ODK function after mutation.
|
||||
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
|
||||
size_t max_size, unsigned int seed) {
|
||||
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
|
||||
if (size < kLicenseResponseArgsSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mutate input data and return mutated input size.
|
||||
return FuzzerMutateResponse<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
||||
data, size, max_size, odk_deserialize_LicenseResponse,
|
||||
kdo_serialize_LicenseResponse);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
||||
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data,
|
||||
size, kLicenseResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
22
oemcrypto/odk/test/fuzzing/odk_provisioning_request_fuzz.cpp
Normal file
22
oemcrypto/odk/test/fuzzing/odk_provisioning_request_fuzz.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> out(size);
|
||||
const size_t kClockValueSize = 0;
|
||||
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
|
||||
odk_serialize_ProvisioningRequest, data,
|
||||
size, kClockValueSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
@@ -0,0 +1,21 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kProvisioningResponseArgsSize =
|
||||
sizeof(ODK_ParseProvisioning_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
|
||||
odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse,
|
||||
data, size, kProvisioningResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
@@ -0,0 +1,38 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
// The custom mutator: Ensure that each input can be deserialized properly
|
||||
// by ODK function after mutation.
|
||||
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
|
||||
size_t max_size, unsigned int seed) {
|
||||
const size_t kProvisioningResponseArgsSize =
|
||||
sizeof(ODK_ParseProvisioning_Args);
|
||||
if (size < kProvisioningResponseArgsSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mutate input data and return mutated input size.
|
||||
return FuzzerMutateResponse<ODK_ParseProvisioning_Args,
|
||||
ODK_ParsedProvisioning>(
|
||||
data, size, max_size, odk_deserialize_ProvisioningResponse,
|
||||
kdo_serialize_ProvisioningResponse);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kProvisioningResponseArgsSize =
|
||||
sizeof(ODK_ParseProvisioning_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
|
||||
odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse,
|
||||
data, size, kProvisioningResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
22
oemcrypto/odk/test/fuzzing/odk_renewal_request_fuzz.cpp
Normal file
22
oemcrypto/odk/test/fuzzing/odk_renewal_request_fuzz.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> out(size);
|
||||
const size_t kClockValueSize = sizeof(ODK_ClockValues);
|
||||
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
|
||||
odk_serialize_RenewalRequest, data, size,
|
||||
kClockValueSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
20
oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz.cpp
Normal file
20
oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
|
||||
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data,
|
||||
size, kRenewalResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
@@ -0,0 +1,36 @@
|
||||
/* 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 <vector>
|
||||
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
// The custom mutator: Ensure that each input can be deserialized properly
|
||||
// by ODK function after mutation.
|
||||
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
|
||||
size_t max_size, unsigned int seed) {
|
||||
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
|
||||
if (size < kRenewalResponseArgsSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mutate input data and return mutated input size.
|
||||
return FuzzerMutateResponse<ODK_ParseRenewal_Args,
|
||||
ODK_PreparedRenewalRequest>(
|
||||
data, size, max_size, odk_deserialize_RenewalResponse,
|
||||
kdo_serialize_RenewalResponse);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
|
||||
std::vector<uint8_t> out(size);
|
||||
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
|
||||
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data,
|
||||
size, kRenewalResponseArgsSize, out.data());
|
||||
return 0;
|
||||
}
|
||||
} // namespace oemcrypto_core_message
|
||||
37
oemcrypto/odk/test/odk_core_message_test.cpp
Normal file
37
oemcrypto/odk/test/odk_core_message_test.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 "OEMCryptoCENCCommon.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "odk.h"
|
||||
#include "third_party/absl/strings/escaping.h"
|
||||
|
||||
namespace wvodk_test {
|
||||
TEST(CoreMessageTest, RenwalRequest) {
|
||||
std::string oem =
|
||||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst"
|
||||
"uvwxyzabcdefghijklmnopqrstuvwxyz";
|
||||
const uint8_t* buf = reinterpret_cast<const uint8_t*>(oem.c_str());
|
||||
uint8_t* message = const_cast<uint8_t*>(buf);
|
||||
size_t message_length = 88;
|
||||
size_t core_message_length = 88;
|
||||
uint16_t api_minor_version = 16;
|
||||
uint16_t api_major_version = 16;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t timer_status = 2;
|
||||
uint64_t time = 10;
|
||||
enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce};
|
||||
ODK_ClockValues clock_values{time, time, time, time,
|
||||
time, timer_status, status};
|
||||
uint64_t system_time_seconds = 100;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewalRequest(message, message_length,
|
||||
&core_message_length, &nonce_values,
|
||||
&clock_values, system_time_seconds));
|
||||
// All messages have at least a five 4-byte fields.
|
||||
char* m = reinterpret_cast<char*>(message);
|
||||
VLOG(0) << absl::BytesToHexString(std::string(m, core_message_length));
|
||||
}
|
||||
} // namespace wvodk_test
|
||||
729
oemcrypto/odk/test/odk_test.cpp
Normal file
729
oemcrypto/odk/test/odk_test.cpp
Normal file
@@ -0,0 +1,729 @@
|
||||
// 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.
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
#include <endian.h> // TODO(b/147944591): use this one? Or odk_endian.h?
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_serialize.h"
|
||||
#include "core_message_types.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "odk_test_helper.h"
|
||||
|
||||
namespace wvodk_test {
|
||||
|
||||
namespace {
|
||||
|
||||
using oemcrypto_core_message::ODK_LicenseRequest;
|
||||
using oemcrypto_core_message::ODK_ProvisioningRequest;
|
||||
using oemcrypto_core_message::ODK_RenewalRequest;
|
||||
|
||||
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||
|
||||
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
|
||||
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
|
||||
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
||||
|
||||
constexpr uint32_t kExtraPayloadSize = 128u;
|
||||
|
||||
template <typename T, typename F, typename G>
|
||||
void ValidateRequest(uint32_t message_type,
|
||||
const std::vector<ODK_Field>& extra_fields,
|
||||
const F& odk_prepare_func, const G& kdo_parse_func) {
|
||||
uint32_t message_size = 0;
|
||||
uint16_t api_major_version = ODK_MAJOR_VERSION;
|
||||
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
||||
uint32_t nonce = 0xdeadbeef;
|
||||
uint32_t session_id = 0xcafebabe;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
std::vector<ODK_Field> total_fields = {
|
||||
{ODK_UINT32, &message_type, "message_type"},
|
||||
{ODK_UINT32, &message_size, "message_size"},
|
||||
{ODK_UINT16, &api_minor_version, "api_minor_version"},
|
||||
{ODK_UINT16, &api_major_version, "api_major_version"},
|
||||
{ODK_UINT32, &nonce, "nonce"},
|
||||
{ODK_UINT32, &session_id, "session_id"},
|
||||
};
|
||||
|
||||
total_fields.insert(total_fields.end(), extra_fields.begin(),
|
||||
extra_fields.end());
|
||||
for (auto& field : total_fields) {
|
||||
message_size += ODK_FieldLength(field.type);
|
||||
}
|
||||
|
||||
// empty buf, expect core message length to be set correctly
|
||||
size_t core_message_length = 0;
|
||||
uint8_t* buf_empty = nullptr;
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
odk_prepare_func(buf_empty, &core_message_length, &nonce_values));
|
||||
EXPECT_EQ(core_message_length, message_size);
|
||||
|
||||
// non-empty buf, expect core message length to be set correctly, and buf is
|
||||
// filled with ODK_Field values appropriately
|
||||
uint8_t* buf = new uint8_t[message_size]{};
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
odk_prepare_func(buf, &core_message_length, &nonce_values));
|
||||
EXPECT_EQ(core_message_length, message_size);
|
||||
|
||||
uint8_t* buf_expected = new uint8_t[message_size]{};
|
||||
size_t buf_len_expected = 0;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf_expected, SIZE_MAX,
|
||||
&buf_len_expected, total_fields));
|
||||
EXPECT_EQ(buf_len_expected, message_size);
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ODK_ExpectEqualBuf(buf_expected, buf, message_size, total_fields));
|
||||
|
||||
// odk kdo round-trip: deserialize from buf, then serialize it to buf2
|
||||
// expect them to be identical
|
||||
T t = {};
|
||||
std::string oemcrypto_core_message(reinterpret_cast<char*>(buf),
|
||||
message_size);
|
||||
EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t));
|
||||
nonce_values.api_minor_version = t.api_minor_version;
|
||||
nonce_values.api_major_version = t.api_major_version;
|
||||
nonce_values.nonce = t.nonce;
|
||||
nonce_values.session_id = t.session_id;
|
||||
uint8_t* buf2 = new uint8_t[message_size]{};
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
odk_prepare_func(buf2, &core_message_length, &nonce_values));
|
||||
EXPECT_EQ(core_message_length, message_size);
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ODK_ExpectEqualBuf(buf, buf2, message_size, total_fields));
|
||||
|
||||
delete[] buf;
|
||||
delete[] buf_expected;
|
||||
delete[] buf2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template arguments:
|
||||
* T: kdo input struct
|
||||
* F: odk deserializer
|
||||
* G: kdo serializer
|
||||
*/
|
||||
template <typename T, typename F, typename G>
|
||||
void ValidateResponse(ODK_CoreMessage* core_message,
|
||||
const std::vector<ODK_Field>& extra_fields,
|
||||
const F& odk_parse_func, const G& kdo_prepare_func) {
|
||||
T t = {};
|
||||
t.api_minor_version = core_message->nonce_values.api_minor_version;
|
||||
t.api_major_version = core_message->nonce_values.api_major_version;
|
||||
t.nonce = core_message->nonce_values.nonce;
|
||||
t.session_id = core_message->nonce_values.session_id;
|
||||
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(core_message, extra_fields, &buf, &buf_size);
|
||||
|
||||
uint8_t* zero = new uint8_t[buf_size]{};
|
||||
size_t bytes_read = 0;
|
||||
// zero-out input
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, buf_size,
|
||||
&bytes_read, extra_fields));
|
||||
|
||||
// parse buf with odk
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, odk_parse_func(buf, buf_size));
|
||||
|
||||
size_t size_out = 0;
|
||||
ODK_IterFields(ODK_FieldMode::ODK_DUMP, buf, buf_size, &size_out,
|
||||
extra_fields);
|
||||
|
||||
// serialize odk output to oemcrypto_core_message
|
||||
std::string oemcrypto_core_message;
|
||||
EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message));
|
||||
|
||||
// verify round-trip works
|
||||
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, oemcrypto_core_message.data(),
|
||||
buf_size, extra_fields));
|
||||
delete[] buf;
|
||||
delete[] zero;
|
||||
}
|
||||
|
||||
TEST(OdkTest, SerializeFields) {
|
||||
uint32_t x[] = {0, 1, 2};
|
||||
uint64_t y[] = {3LL << 32, 4LL << 32, 5LL << 32};
|
||||
OEMCrypto_Substring s = {.offset = 6, .length = 7};
|
||||
std::vector<ODK_Field> fields = {
|
||||
{ODK_UINT32, &x[0], "x[0]"}, {ODK_UINT32, &x[1], "x[1]"},
|
||||
{ODK_UINT32, &x[2], "x[2]"}, {ODK_UINT64, &y[0], "y[0]"},
|
||||
{ODK_UINT64, &y[1], "y[1]"}, {ODK_UINT64, &y[2], "y[2]"},
|
||||
{ODK_SUBSTRING, &s, "s"},
|
||||
};
|
||||
uint8_t buf[1024] = {0};
|
||||
uint8_t buf2[1024] = {0};
|
||||
size_t bytes_read = 0, bytes_written = 0;
|
||||
ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_read, fields);
|
||||
std::vector<ODK_Field> fields2(fields.size());
|
||||
ODK_ResetOdkFields(&fields);
|
||||
ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields);
|
||||
ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields);
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, bytes_read, fields));
|
||||
}
|
||||
|
||||
TEST(OdkTest, SerializeFieldsStress) {
|
||||
const int n = 1024;
|
||||
std::vector<ODK_Field> fields(n);
|
||||
std::srand(0);
|
||||
size_t total_size = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
fields[i].type = static_cast<ODK_FieldType>(std::rand() %
|
||||
static_cast<int>(ODK_NUMTYPES));
|
||||
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
|
||||
fields[i].name = "stress";
|
||||
total_size += ODK_FieldLength(fields[i].type);
|
||||
}
|
||||
|
||||
uint8_t* buf = new uint8_t[total_size]{};
|
||||
for (int i = 0; i < total_size; i++) {
|
||||
buf[i] = std::rand() & 0xff;
|
||||
}
|
||||
|
||||
size_t bytes_read = 0, bytes_written = 0;
|
||||
uint8_t* buf2 = new uint8_t[total_size]{};
|
||||
ODK_IterFields(ODK_READ, buf, total_size, &bytes_read, fields);
|
||||
EXPECT_EQ(bytes_read, total_size);
|
||||
ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields);
|
||||
EXPECT_EQ(bytes_written, total_size);
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, total_size, fields));
|
||||
|
||||
// cleanup
|
||||
for (int i = 0; i < n; i++) {
|
||||
free(fields[i].value);
|
||||
}
|
||||
delete[] buf;
|
||||
delete[] buf2;
|
||||
}
|
||||
|
||||
TEST(OdkTest, NullRequestTest) {
|
||||
size_t core_message_length = 0;
|
||||
ODK_NonceValues nonce_values{0};
|
||||
ODK_ClockValues clock_values{0};
|
||||
|
||||
// Assert that nullptr does not cause a core dump.
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreLicenseRequest(
|
||||
nullptr, 0uL, nullptr, &nonce_values));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreLicenseRequest(nullptr, 0uL, &core_message_length,
|
||||
nullptr));
|
||||
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, nullptr, &nonce_values,
|
||||
&clock_values, 0uL));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length,
|
||||
nullptr, &clock_values, 0uL));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length,
|
||||
&nonce_values, nullptr, 0uL));
|
||||
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
nullptr, 0uL, &core_message_length, nullptr, nullptr, 0uL));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreProvisioningRequest(nullptr, 0uL, nullptr,
|
||||
&nonce_values, nullptr, 0uL));
|
||||
|
||||
// Null device id in provisioning request is ok
|
||||
uint8_t message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
core_message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length,
|
||||
&nonce_values, nullptr, 0uL));
|
||||
}
|
||||
|
||||
TEST(OdkTest, NullResponseTest) {
|
||||
constexpr size_t message_size = 64;
|
||||
uint8_t message[message_size] = {0};
|
||||
size_t core_message_length = message_size;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {0};
|
||||
ODK_TimerLimits timer_limits{0};
|
||||
ODK_ParsedLicense parsed_license;
|
||||
ODK_NonceValues nonce_values{0};
|
||||
ODK_ClockValues clock_values{0};
|
||||
|
||||
// Assert that nullptr does not cause a core dump.
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(message, message_size, core_message_length, true,
|
||||
true, request_hash, &timer_limits, &clock_values,
|
||||
&nonce_values, nullptr));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(message, message_size, core_message_length, true,
|
||||
true, request_hash, &timer_limits, &clock_values,
|
||||
nullptr, &parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(message, message_size, core_message_length, true,
|
||||
true, request_hash, &timer_limits, nullptr,
|
||||
&nonce_values, &parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(message, message_size, core_message_length, true,
|
||||
true, request_hash, nullptr, &clock_values,
|
||||
&nonce_values, &parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(message, message_size, core_message_length, true,
|
||||
true, nullptr, &timer_limits, &clock_values,
|
||||
&nonce_values, &parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseLicense(nullptr, message_size, core_message_length, true,
|
||||
true, request_hash, &timer_limits, &clock_values,
|
||||
&nonce_values, &parsed_license));
|
||||
|
||||
constexpr uint64_t system_time = 0;
|
||||
uint64_t timer_value = 0;
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseRenewal(message, message_size, core_message_length,
|
||||
&nonce_values, system_time, &timer_limits, nullptr,
|
||||
&timer_value));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseRenewal(message, message_size, core_message_length,
|
||||
&nonce_values, system_time, nullptr, &clock_values,
|
||||
&timer_value));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseRenewal(message, message_size, core_message_length,
|
||||
nullptr, system_time, &timer_limits, &clock_values,
|
||||
&timer_value));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseRenewal(nullptr, message_size, core_message_length,
|
||||
&nonce_values, system_time, &timer_limits,
|
||||
&clock_values, &timer_value));
|
||||
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
ODK_ParsedProvisioning parsed_response;
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseProvisioning(message, message_size, core_message_length,
|
||||
&nonce_values, device_id,
|
||||
ODK_DEVICE_ID_LEN_MAX, nullptr));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseProvisioning(message, message_size, core_message_length,
|
||||
&nonce_values, nullptr, 0, &parsed_response));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseProvisioning(message, message_size, core_message_length,
|
||||
nullptr, device_id, ODK_DEVICE_ID_LEN_MAX,
|
||||
&parsed_response));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_ParseProvisioning(nullptr, message_size, core_message_length,
|
||||
&nonce_values, device_id,
|
||||
ODK_DEVICE_ID_LEN_MAX, &parsed_response));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreLicenseRequest) {
|
||||
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(license_message);
|
||||
ODK_NonceValues nonce_values{0};
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreLicenseRequest(
|
||||
license_message, sizeof(license_message),
|
||||
&core_message_length, &nonce_values));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreLicenseRequestSize) {
|
||||
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(license_message);
|
||||
ODK_NonceValues nonce_values{0};
|
||||
// message length smaller than core message length
|
||||
size_t core_message_length_invalid = core_message_length + 1;
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreLicenseRequest(
|
||||
license_message, sizeof(license_message),
|
||||
&core_message_length_invalid, &nonce_values));
|
||||
// message length larger than core message length
|
||||
uint8_t license_message_large[ODK_LICENSE_REQUEST_SIZE * 2] = {0};
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreLicenseRequest(license_message_large,
|
||||
sizeof(license_message_large),
|
||||
&core_message_length, &nonce_values));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreRenewalRequest) {
|
||||
uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(renewal_message);
|
||||
ODK_NonceValues nonce_values{0};
|
||||
ODK_ClockValues clock_values{0};
|
||||
constexpr uint64_t system_time_seconds = 10;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewalRequest(
|
||||
renewal_message, sizeof(renewal_message), &core_message_length,
|
||||
&nonce_values, &clock_values, system_time_seconds));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreRenewalRequestTimer) {
|
||||
uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(renewal_message);
|
||||
ODK_NonceValues nonce_values{2, 16, 0, 0};
|
||||
constexpr uint64_t system_time_seconds = 10;
|
||||
ODK_ClockValues clock_values_updated{0};
|
||||
// system time smaller than first decrypt time
|
||||
clock_values_updated.time_of_first_decrypt = system_time_seconds + 1;
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewalRequest(
|
||||
renewal_message, sizeof(renewal_message), &core_message_length,
|
||||
&nonce_values, &clock_values_updated, system_time_seconds));
|
||||
clock_values_updated.time_of_first_decrypt = system_time_seconds - 1;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewalRequest(
|
||||
renewal_message, sizeof(renewal_message), &core_message_length,
|
||||
&nonce_values, &clock_values_updated, system_time_seconds));
|
||||
// clock_values.time_of_renewal_request should get updated
|
||||
EXPECT_EQ(system_time_seconds - clock_values_updated.time_of_first_decrypt,
|
||||
clock_values_updated.time_of_renewal_request);
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreProvisioningRequest) {
|
||||
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
ODK_NonceValues nonce_values{0};
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
EXPECT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
provisioning_message, sizeof(provisioning_message),
|
||||
&core_message_length, &nonce_values, device_id, sizeof(device_id)));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) {
|
||||
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
ODK_NonceValues nonce_values{0};
|
||||
uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0};
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
provisioning_message, sizeof(provisioning_message),
|
||||
&core_message_length, &nonce_values, device_id_invalid,
|
||||
sizeof(device_id_invalid)));
|
||||
}
|
||||
|
||||
// Serialize and de-serialize license request
|
||||
TEST(OdkTest, LicenseRequestRoundtrip) {
|
||||
std::vector<ODK_Field> empty;
|
||||
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
||||
ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, nonce_values);
|
||||
};
|
||||
auto kdo_parse_func = CoreLicenseRequestFromMessage;
|
||||
ValidateRequest<ODK_LicenseRequest>(ODK_License_Request_Type, empty,
|
||||
odk_prepare_func, kdo_parse_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, RenewalRequestRoundtrip) {
|
||||
constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
|
||||
uint64_t playback_time = 0xCAFE00000000;
|
||||
const uint64_t playback_start = system_time_seconds - playback_time;
|
||||
const std::vector<ODK_Field> extra_fields = {
|
||||
{ODK_UINT64, &playback_time, "playback_time"},
|
||||
};
|
||||
ODK_ClockValues clock_values = {0};
|
||||
clock_values.time_of_first_decrypt = playback_start;
|
||||
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
||||
ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
|
||||
&clock_values, system_time_seconds);
|
||||
};
|
||||
auto kdo_parse_func = [&](const std::string& oemcrypto_core_message,
|
||||
ODK_RenewalRequest* core_renewal_request) {
|
||||
bool ok = CoreRenewalRequestFromMessage(oemcrypto_core_message,
|
||||
core_renewal_request);
|
||||
return ok;
|
||||
};
|
||||
ValidateRequest<ODK_RenewalRequest>(ODK_Renewal_Request_Type, extra_fields,
|
||||
odk_prepare_func, kdo_parse_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, ProvisionRequestRoundtrip) {
|
||||
uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
memset(device_id, 0xff, device_id_length);
|
||||
std::vector<ODK_Field> extra_fields = {
|
||||
{ODK_UINT32, &device_id_length, "device_id_length"},
|
||||
{ODK_DEVICEID, device_id, "device_id"},
|
||||
};
|
||||
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreProvisioningRequest(buf, SIZE_MAX, size, nonce_values,
|
||||
device_id, device_id_length);
|
||||
};
|
||||
auto kdo_parse_func =
|
||||
[&](const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
bool ok = CoreProvisioningRequestFromMessage(oemcrypto_core_message,
|
||||
core_provisioning_request);
|
||||
return ok;
|
||||
};
|
||||
ValidateRequest<ODK_ProvisioningRequest>(ODK_Provisioning_Request_Type,
|
||||
extra_fields, odk_prepare_func,
|
||||
kdo_parse_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParseLicenseErrorNonce) {
|
||||
ODK_LicenseResponseParams params;
|
||||
ODK_SetDefaultLicenseResponseParams(¶ms);
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
||||
&buf_size);
|
||||
// temporarily mess up with nonce
|
||||
params.core_message.nonce_values.nonce = 0;
|
||||
OEMCryptoResult err = ODK_ParseLicense(
|
||||
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
||||
params.usage_entry_present, params.request_hash, &(params.timer_limits),
|
||||
&(params.clock_values), &(params.core_message.nonce_values),
|
||||
&(params.parsed_license));
|
||||
EXPECT_EQ(OEMCrypto_ERROR_INVALID_NONCE, err);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParseLicenseErrorUsageEntry) {
|
||||
ODK_LicenseResponseParams params;
|
||||
ODK_SetDefaultLicenseResponseParams(¶ms);
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
||||
&buf_size);
|
||||
params.usage_entry_present = false;
|
||||
OEMCryptoResult err = ODK_ParseLicense(
|
||||
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
||||
params.usage_entry_present, params.request_hash, &(params.timer_limits),
|
||||
&(params.clock_values), &(params.core_message.nonce_values),
|
||||
&(params.parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParseLicenseErrorRequestHash) {
|
||||
ODK_LicenseResponseParams params;
|
||||
ODK_SetDefaultLicenseResponseParams(¶ms);
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
||||
&buf_size);
|
||||
// temporarily mess up with request hash
|
||||
params.request_hash[0] = 0xff;
|
||||
OEMCryptoResult err = ODK_ParseLicense(
|
||||
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
||||
params.usage_entry_present, params.request_hash, &(params.timer_limits),
|
||||
&(params.clock_values), &(params.core_message.nonce_values),
|
||||
&(params.parsed_license));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParseRenewalErrorTimer) {
|
||||
ODK_RenewalResponseParams params;
|
||||
ODK_SetDefaultRenewalResponseParams(¶ms);
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
||||
&buf_size);
|
||||
params.clock_values.time_of_renewal_request = 0;
|
||||
OEMCryptoResult err = ODK_ParseRenewal(
|
||||
buf, buf_size, buf_size, &(params.core_message.nonce_values),
|
||||
params.system_time, &(params.timer_limits), &(params.clock_values),
|
||||
&(params.playback_timer));
|
||||
EXPECT_EQ(ODK_STALE_RENEWAL, err);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParsePrivisioningErrorDeviceId) {
|
||||
ODK_ProvisioningResponseParams params;
|
||||
ODK_SetDefaultProvisioningResponseParams(¶ms);
|
||||
uint8_t* buf = nullptr;
|
||||
uint32_t buf_size = 0;
|
||||
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
||||
&buf_size);
|
||||
// temporarily mess up with device_id
|
||||
params.device_id[0] = 0;
|
||||
OEMCryptoResult err = ODK_ParseProvisioning(
|
||||
buf, buf_size + 16, buf_size, &(params.core_message.nonce_values),
|
||||
params.device_id, params.device_id_length, &(params.parsed_provisioning));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
// Serialize and de-serialize license response
|
||||
TEST(OdkTest, LicenseResponseRoundtrip) {
|
||||
ODK_LicenseResponseParams params;
|
||||
ODK_SetDefaultLicenseResponseParams(¶ms);
|
||||
// save a copy of params.request_hash as it will be zero out during the test
|
||||
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
|
||||
memcpy(request_hash_read, params.request_hash, sizeof(request_hash_read));
|
||||
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
|
||||
return ODK_ParseLicense(
|
||||
buf, size + kExtraPayloadSize, size, params.initial_license_load,
|
||||
params.usage_entry_present, request_hash_read, &(params.timer_limits),
|
||||
&(params.clock_values), &(params.core_message.nonce_values),
|
||||
&(params.parsed_license));
|
||||
};
|
||||
const std::string request_hash_string(
|
||||
reinterpret_cast<const char*>(request_hash_read),
|
||||
sizeof(request_hash_read));
|
||||
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
return CreateCoreLicenseResponse(params.parsed_license, core_request,
|
||||
request_hash_string,
|
||||
oemcrypto_core_message);
|
||||
};
|
||||
ValidateResponse<ODK_LicenseRequest>(&(params.core_message),
|
||||
params.extra_fields, odk_parse_func,
|
||||
kdo_prepare_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, RenewalResponseRoundtrip) {
|
||||
ODK_RenewalResponseParams params;
|
||||
ODK_SetDefaultRenewalResponseParams(¶ms);
|
||||
const uint64_t playback_clock = params.playback_clock;
|
||||
const uint64_t renewal_duration = params.renewal_duration;
|
||||
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
|
||||
OEMCryptoResult err =
|
||||
ODK_ParseRenewal(buf, size, size, &(params.core_message.nonce_values),
|
||||
params.system_time, &(params.timer_limits),
|
||||
&(params.clock_values), &(params.playback_timer));
|
||||
|
||||
EXPECT_EQ(ODK_SET_TIMER, err);
|
||||
EXPECT_EQ(renewal_duration, params.playback_timer);
|
||||
EXPECT_EQ(params.clock_values.time_when_timer_expires,
|
||||
params.system_time + params.playback_timer);
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
};
|
||||
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
core_request.playback_time_seconds = playback_clock;
|
||||
return CreateCoreRenewalResponse(core_request, renewal_duration,
|
||||
oemcrypto_core_message);
|
||||
};
|
||||
ValidateResponse<ODK_RenewalRequest>(&(params.core_message),
|
||||
params.extra_fields, odk_parse_func,
|
||||
kdo_prepare_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, ProvisionResponseRoundtrip) {
|
||||
ODK_ProvisioningResponseParams params;
|
||||
ODK_SetDefaultProvisioningResponseParams(¶ms);
|
||||
// save a copy of params.device_id as it will be zero out during the test
|
||||
const uint32_t device_id_length = params.device_id_length;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
memcpy(device_id, params.device_id, device_id_length);
|
||||
|
||||
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
|
||||
OEMCryptoResult err = ODK_ParseProvisioning(
|
||||
buf, size + 16, size, &(params.core_message.nonce_values), device_id,
|
||||
device_id_length, &(params.parsed_provisioning));
|
||||
return err;
|
||||
};
|
||||
auto kdo_prepare_func = [&](ODK_ProvisioningRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
core_request.device_id.assign(reinterpret_cast<char*>(device_id),
|
||||
device_id_length);
|
||||
return CreateCoreProvisioningResponse(params.parsed_provisioning,
|
||||
core_request, oemcrypto_core_message);
|
||||
};
|
||||
ValidateResponse<ODK_ProvisioningRequest>(&(params.core_message),
|
||||
params.extra_fields, odk_parse_func,
|
||||
kdo_prepare_func);
|
||||
}
|
||||
|
||||
TEST(OdkSizeTest, LicenseRequest) {
|
||||
uint8_t* message = nullptr;
|
||||
size_t message_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
||||
uint16_t api_major_version = 0;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t session_id = 0;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
ODK_PrepareCoreLicenseRequest(message, message_length,
|
||||
&core_message_length, &nonce_values));
|
||||
// the core_message_length should be appropriately set
|
||||
EXPECT_EQ(ODK_LICENSE_REQUEST_SIZE, core_message_length);
|
||||
}
|
||||
|
||||
TEST(OdkSizeTest, RenewalRequest) {
|
||||
uint8_t* message = nullptr;
|
||||
size_t message_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
||||
uint16_t api_major_version = ODK_MAJOR_VERSION;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t session_id = 0;
|
||||
ODK_ClockValues clock_values = {};
|
||||
clock_values.time_of_first_decrypt = 10;
|
||||
clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||
uint64_t system_time_seconds = 15;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
ODK_PrepareCoreRenewalRequest(message, message_length,
|
||||
&core_message_length, &nonce_values,
|
||||
&clock_values, system_time_seconds));
|
||||
// the core_message_length should be appropriately set
|
||||
EXPECT_EQ(ODK_RENEWAL_REQUEST_SIZE, core_message_length);
|
||||
}
|
||||
|
||||
TEST(OdkSizeTest, ReleaseRequest) {
|
||||
uint8_t* message = nullptr;
|
||||
size_t message_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
||||
uint16_t api_major_version = 0;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t session_id = 0;
|
||||
ODK_ClockValues clock_values = {};
|
||||
clock_values.time_of_first_decrypt = 10;
|
||||
clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||
uint64_t system_time_seconds = 15;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewalRequest(message, message_length,
|
||||
&core_message_length, &nonce_values,
|
||||
&clock_values, system_time_seconds));
|
||||
// Release requests do not have a core message.
|
||||
EXPECT_GE(core_message_length, 0);
|
||||
}
|
||||
|
||||
TEST(OdkSizeTest, ProvisioningRequest) {
|
||||
uint8_t* message = nullptr;
|
||||
size_t message_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
||||
uint16_t api_major_version = 0;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t session_id = 0;
|
||||
uint32_t device_id_length = 0;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
message, message_length, &core_message_length, &nonce_values,
|
||||
nullptr, device_id_length));
|
||||
// the core_message_length should be appropriately set
|
||||
EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length);
|
||||
}
|
||||
|
||||
// Verify the version string contains the right version numbers.
|
||||
TEST(OdkTest, CheckReleaseVersion) {
|
||||
// Here are the version numbers.
|
||||
std::string expected_version = std::to_string(ODK_MAJOR_VERSION) + "." +
|
||||
std::to_string(ODK_MINOR_VERSION);
|
||||
// Here is the version string.
|
||||
EXPECT_NE(std::string(ODK_RELEASE_DATE).find(expected_version),
|
||||
std::string::npos)
|
||||
<< "Version mismatch in odk_structs.h";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace wvodk_test
|
||||
488
oemcrypto/odk/test/odk_test_helper.cpp
Normal file
488
oemcrypto/odk/test/odk_test_helper.cpp
Normal file
@@ -0,0 +1,488 @@
|
||||
// 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.
|
||||
|
||||
#include "odk_test_helper.h"
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
|
||||
namespace wvodk_test {
|
||||
|
||||
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
|
||||
uint32_t message_type) {
|
||||
ASSERT_TRUE(core_message != nullptr);
|
||||
core_message->message_type = message_type;
|
||||
core_message->message_length = 0;
|
||||
core_message->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
||||
core_message->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||
core_message->nonce_values.nonce = 0xdeadbeef;
|
||||
core_message->nonce_values.session_id = 0xcafebabe;
|
||||
}
|
||||
|
||||
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params) {
|
||||
ODK_SetDefaultCoreFields(&(params->core_message), ODK_License_Response_Type);
|
||||
params->initial_license_load = true;
|
||||
params->usage_entry_present = true;
|
||||
params->parsed_license = {
|
||||
.enc_mac_keys_iv = {.offset = 0, .length = 1},
|
||||
.enc_mac_keys = {.offset = 2, .length = 3},
|
||||
.pst = {.offset = 4, .length = 5},
|
||||
.srm_restriction_data = {.offset = 6, .length = 7},
|
||||
.license_type = OEMCrypto_EntitlementLicense,
|
||||
.nonce_required = true,
|
||||
.timer_limits =
|
||||
{
|
||||
.soft_enforce_rental_duration = true,
|
||||
.soft_enforce_playback_duration = false,
|
||||
.earliest_playback_start_seconds = 10,
|
||||
.rental_duration_seconds = 11,
|
||||
.total_playback_duration_seconds = 12,
|
||||
.initial_renewal_duration_seconds = 13,
|
||||
},
|
||||
.key_array_length = 3,
|
||||
.key_array =
|
||||
{
|
||||
{
|
||||
.key_id = {.offset = 15, .length = 16},
|
||||
.key_data_iv = {.offset = 17, .length = 18},
|
||||
.key_data = {.offset = 19, .length = 20},
|
||||
.key_control_iv = {.offset = 21, .length = 22},
|
||||
.key_control = {.offset = 23, .length = 24},
|
||||
},
|
||||
{
|
||||
.key_id = {.offset = 25, .length = 26},
|
||||
.key_data_iv = {.offset = 27, .length = 28},
|
||||
.key_data = {.offset = 29, .length = 30},
|
||||
.key_control_iv = {.offset = 31, .length = 32},
|
||||
.key_control = {.offset = 33, .length = 34},
|
||||
},
|
||||
{
|
||||
.key_id = {.offset = 35, .length = 36},
|
||||
.key_data_iv = {.offset = 37, .length = 38},
|
||||
.key_data = {.offset = 39, .length = 40},
|
||||
.key_control_iv = {.offset = 41, .length = 42},
|
||||
.key_control = {.offset = 43, .length = 44},
|
||||
},
|
||||
},
|
||||
};
|
||||
memset(params->request_hash, 0xaa, sizeof(params->request_hash));
|
||||
params->extra_fields = {
|
||||
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys_iv),
|
||||
".enc_mac_keys_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys), ".enc_mac_keys"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.pst), ".pst"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.srm_restriction_data),
|
||||
".srm_restriction_data"},
|
||||
{ODK_UINT32, &(params->parsed_license.license_type), ".license_type"},
|
||||
{ODK_UINT32, &(params->parsed_license.nonce_required), ".nonce_required"},
|
||||
{ODK_UINT32,
|
||||
&(params->parsed_license.timer_limits.soft_enforce_rental_duration),
|
||||
".soft_enforce_rental_duration"},
|
||||
{ODK_UINT32,
|
||||
&(params->parsed_license.timer_limits.soft_enforce_playback_duration),
|
||||
".soft_enforce_playback_duration"},
|
||||
{ODK_UINT64,
|
||||
&(params->parsed_license.timer_limits.earliest_playback_start_seconds),
|
||||
".earliest_playback_start_seconds"},
|
||||
{ODK_UINT64,
|
||||
&(params->parsed_license.timer_limits.rental_duration_seconds),
|
||||
".rental_duration_seconds"},
|
||||
{ODK_UINT64,
|
||||
&(params->parsed_license.timer_limits.total_playback_duration_seconds),
|
||||
".total_playback_duration_seconds"},
|
||||
{ODK_UINT64,
|
||||
&(params->parsed_license.timer_limits.initial_renewal_duration_seconds),
|
||||
".initial_renewal_duration_seconds"},
|
||||
{ODK_UINT32, &(params->parsed_license.key_array_length),
|
||||
".key_array_length"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_id), ".key_id"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data_iv),
|
||||
".key_data_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data),
|
||||
".key_data"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control_iv),
|
||||
".key_control_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control),
|
||||
".key_control"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_id), ".key_id"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data_iv),
|
||||
".key_data_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data),
|
||||
".key_data"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control_iv),
|
||||
".key_control_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control),
|
||||
".key_control"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_id), ".key_id"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data_iv),
|
||||
".key_data_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data),
|
||||
".key_data"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control_iv),
|
||||
".key_control_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control),
|
||||
".key_control"},
|
||||
{ODK_HASH, params->request_hash, ".request_hash"},
|
||||
};
|
||||
}
|
||||
|
||||
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params) {
|
||||
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Renewal_Response_Type);
|
||||
params->system_time = 0xfaceb00c;
|
||||
params->playback_clock = 10;
|
||||
params->playback_timer = 20;
|
||||
params->renewal_duration = 300;
|
||||
params->extra_fields = {
|
||||
{ODK_UINT64, &(params->playback_clock), "playback_clock"},
|
||||
{ODK_UINT64, &(params->renewal_duration), "renewal_duration"},
|
||||
};
|
||||
params->timer_limits = {
|
||||
.soft_enforce_rental_duration = false,
|
||||
.soft_enforce_playback_duration = false,
|
||||
.earliest_playback_start_seconds = 0,
|
||||
.rental_duration_seconds = 1000,
|
||||
.total_playback_duration_seconds = 2000,
|
||||
.initial_renewal_duration_seconds = 300,
|
||||
};
|
||||
params->clock_values = {
|
||||
.time_of_license_signed =
|
||||
params->system_time - params->playback_clock - 42,
|
||||
.time_of_first_decrypt = params->system_time - params->playback_clock,
|
||||
.time_of_last_decrypt = params->system_time - params->playback_clock,
|
||||
.time_of_renewal_request = params->playback_clock,
|
||||
.time_when_timer_expires = params->system_time + params->playback_timer,
|
||||
.timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE,
|
||||
.status = kActive,
|
||||
};
|
||||
}
|
||||
|
||||
void ODK_SetDefaultProvisioningResponseParams(
|
||||
ODK_ProvisioningResponseParams* params) {
|
||||
ODK_SetDefaultCoreFields(&(params->core_message),
|
||||
ODK_Provisioning_Response_Type);
|
||||
params->device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
||||
memset(params->device_id, 0xff, params->device_id_length);
|
||||
memset(params->device_id + params->device_id_length, 0,
|
||||
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
|
||||
params->parsed_provisioning = {
|
||||
.enc_private_key = {.offset = 0, .length = 1},
|
||||
.enc_private_key_iv = {.offset = 2, .length = 3},
|
||||
.encrypted_message_key = {.offset = 4, .length = 5},
|
||||
};
|
||||
params->extra_fields = {
|
||||
{ODK_UINT32, &(params->device_id_length), "device_id_length"},
|
||||
{ODK_DEVICEID, params->device_id, "device_id"},
|
||||
{ODK_UINT32, &(params->parsed_provisioning).key_type, "key_type"},
|
||||
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key,
|
||||
"enc_private_key"},
|
||||
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key_iv,
|
||||
"enc_private_key_iv"},
|
||||
{ODK_SUBSTRING, &(params->parsed_provisioning).encrypted_message_key,
|
||||
"encrypted_message_key"},
|
||||
};
|
||||
}
|
||||
|
||||
size_t ODK_FieldLength(ODK_FieldType type) {
|
||||
switch (type) {
|
||||
case ODK_UINT16:
|
||||
return sizeof(uint16_t);
|
||||
case ODK_UINT32:
|
||||
return sizeof(uint32_t);
|
||||
case ODK_UINT64:
|
||||
return sizeof(uint64_t);
|
||||
case ODK_SUBSTRING:
|
||||
return sizeof(uint32_t) + sizeof(uint32_t);
|
||||
case ODK_DEVICEID:
|
||||
return ODK_DEVICE_ID_LEN_MAX;
|
||||
case ODK_HASH:
|
||||
return ODK_SHA256_HASH_SIZE;
|
||||
default:
|
||||
return SIZE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ODK_AllocSize(ODK_FieldType type) {
|
||||
if (type == ODK_SUBSTRING) {
|
||||
return sizeof(OEMCrypto_Substring);
|
||||
}
|
||||
return ODK_FieldLength(type);
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
|
||||
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
switch (field->type) {
|
||||
case ODK_UINT16: {
|
||||
const uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
|
||||
memcpy(buf, &u16, sizeof(u16));
|
||||
break;
|
||||
}
|
||||
case ODK_UINT32: {
|
||||
const uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
|
||||
memcpy(buf, &u32, sizeof(u32));
|
||||
break;
|
||||
}
|
||||
case ODK_UINT64: {
|
||||
const uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
|
||||
memcpy(buf, &u64, sizeof(u64));
|
||||
break;
|
||||
}
|
||||
case ODK_SUBSTRING: {
|
||||
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
||||
const uint32_t off = htobe32(s->offset);
|
||||
const uint32_t len = htobe32(s->length);
|
||||
memcpy(buf, &off, sizeof(off));
|
||||
memcpy(buf + sizeof(off), &len, sizeof(len));
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
const uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||
memcpy(buf, id, field_len);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
|
||||
const ODK_Field* field) {
|
||||
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
switch (field->type) {
|
||||
case ODK_UINT16: {
|
||||
memcpy(field->value, buf, sizeof(uint16_t));
|
||||
uint16_t* u16p = static_cast<uint16_t*>(field->value);
|
||||
*u16p = be16toh(*u16p);
|
||||
break;
|
||||
}
|
||||
case ODK_UINT32: {
|
||||
memcpy(field->value, buf, sizeof(uint32_t));
|
||||
uint32_t* u32p = static_cast<uint32_t*>(field->value);
|
||||
*u32p = be32toh(*u32p);
|
||||
break;
|
||||
}
|
||||
case ODK_UINT64: {
|
||||
memcpy(field->value, buf, sizeof(uint64_t));
|
||||
uint64_t* u64p = static_cast<uint64_t*>(field->value);
|
||||
*u64p = be64toh(*u64p);
|
||||
break;
|
||||
}
|
||||
case ODK_SUBSTRING: {
|
||||
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
||||
uint32_t off = 0;
|
||||
uint32_t len = 0;
|
||||
memcpy(&off, buf, sizeof(off));
|
||||
memcpy(&len, buf + sizeof(off), sizeof(len));
|
||||
s->offset = be32toh(off);
|
||||
s->length = be32toh(len);
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||
memcpy(id, buf, field_len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
|
||||
const ODK_Field* field) {
|
||||
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
switch (field->type) {
|
||||
case ODK_UINT16: {
|
||||
uint16_t val;
|
||||
memcpy(&val, buf, sizeof(uint16_t));
|
||||
val = be16toh(val);
|
||||
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
case ODK_UINT32: {
|
||||
uint32_t val;
|
||||
memcpy(&val, buf, sizeof(uint32_t));
|
||||
val = be32toh(val);
|
||||
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
case ODK_UINT64: {
|
||||
uint64_t val;
|
||||
memcpy(&val, buf, sizeof(uint64_t));
|
||||
val = be64toh(val);
|
||||
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
case ODK_SUBSTRING: {
|
||||
uint32_t off = 0;
|
||||
uint32_t len = 0;
|
||||
memcpy(&off, buf, sizeof(off));
|
||||
memcpy(&len, buf + sizeof(off), sizeof(len));
|
||||
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
std::cerr << field->name << ": ";
|
||||
for (size_t i = 0; i < field_len; i++) {
|
||||
std::cerr << std::hex << std::setfill('0') << std::setw(2)
|
||||
<< static_cast<unsigned int>(buf[i]);
|
||||
}
|
||||
std::cerr << "\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
std::cerr << std::dec; // Return to normal.
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parameters:
|
||||
* [in] size_in: buffer size
|
||||
* [out] size_out: bytes processed
|
||||
*/
|
||||
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
|
||||
const size_t size_in, size_t* size_out,
|
||||
const std::vector<ODK_Field>& fields) {
|
||||
if (buf == nullptr || size_out == nullptr) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
size_t off = 0, off2 = 0;
|
||||
for (size_t i = 0; i < fields.size(); i++) {
|
||||
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
|
||||
off2 > size_in) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
|
||||
if (__builtin_add_overflow(base, off, &base)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
uint8_t* const buf_off = buf + off;
|
||||
switch (mode) {
|
||||
case ODK_WRITE:
|
||||
ODK_WriteSingleField(buf_off, &fields[i]);
|
||||
break;
|
||||
case ODK_READ:
|
||||
ODK_ReadSingleField(buf_off, &fields[i]);
|
||||
break;
|
||||
case ODK_DUMP:
|
||||
ODK_DumpSingleField(buf_off, &fields[i]);
|
||||
break;
|
||||
default:
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
off = off2;
|
||||
}
|
||||
*size_out = off;
|
||||
if (*size_out > size_in) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
|
||||
const std::vector<ODK_Field>& fields) {
|
||||
if (memcmp(s1, s2, n) != 0) {
|
||||
const void* buffers[] = {s1, s2};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
char _tmp[] = "/tmp/fileXXXXXX";
|
||||
const int temp_fd = mkstemp(_tmp);
|
||||
if (temp_fd >= 0) {
|
||||
close(temp_fd);
|
||||
} else {
|
||||
std::cerr << "Failed to open temp file." << std::endl;
|
||||
break;
|
||||
}
|
||||
std::string tmp(_tmp);
|
||||
std::fstream out(tmp, std::ios::out | std::ios::binary);
|
||||
out.write(static_cast<const char*>(buffers[i]), n);
|
||||
out.close();
|
||||
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
|
||||
size_t bytes_written;
|
||||
uint8_t* buf =
|
||||
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
|
||||
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields);
|
||||
}
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields) {
|
||||
if (fields == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto& field : *fields) {
|
||||
if (field.value != nullptr) {
|
||||
const size_t size = ODK_AllocSize(field.type);
|
||||
memset(field.value, 0, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
|
||||
const std::vector<ODK_Field>& extra_fields,
|
||||
uint8_t** buf, uint32_t* buf_size) {
|
||||
ASSERT_TRUE(core_message != nullptr);
|
||||
ASSERT_TRUE(buf_size != nullptr);
|
||||
std::vector<ODK_Field> total_fields = {
|
||||
{ODK_UINT32, &(core_message->message_type), "message_type"},
|
||||
{ODK_UINT32, &(core_message->message_length), "message_size"},
|
||||
{ODK_UINT16, &(core_message->nonce_values.api_minor_version),
|
||||
"api_minor_version"},
|
||||
{ODK_UINT16, &(core_message->nonce_values.api_major_version),
|
||||
"api_major_version"},
|
||||
{ODK_UINT32, &(core_message->nonce_values.nonce), "nonce"},
|
||||
{ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"},
|
||||
};
|
||||
|
||||
uint32_t header_size = 0;
|
||||
for (auto& field : total_fields) {
|
||||
header_size += ODK_FieldLength(field.type);
|
||||
}
|
||||
|
||||
total_fields.insert(total_fields.end(), extra_fields.begin(),
|
||||
extra_fields.end());
|
||||
for (auto& field : total_fields) {
|
||||
*buf_size += ODK_FieldLength(field.type);
|
||||
}
|
||||
// update message_size
|
||||
*(reinterpret_cast<uint32_t*>(total_fields[1].value)) = *buf_size;
|
||||
|
||||
*buf = new uint8_t[*buf_size]{};
|
||||
size_t bytes_written = 0;
|
||||
// serialize ODK fields to message buffer
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, *buf, SIZE_MAX,
|
||||
&bytes_written, total_fields));
|
||||
EXPECT_EQ(bytes_written, *buf_size);
|
||||
}
|
||||
|
||||
} // namespace wvodk_test
|
||||
99
oemcrypto/odk/test/odk_test_helper.h
Normal file
99
oemcrypto/odk/test/odk_test_helper.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 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. */
|
||||
|
||||
#ifndef WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
|
||||
#define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
|
||||
namespace wvodk_test {
|
||||
|
||||
enum ODK_FieldType {
|
||||
ODK_UINT16,
|
||||
ODK_UINT32,
|
||||
ODK_UINT64,
|
||||
ODK_SUBSTRING,
|
||||
ODK_DEVICEID,
|
||||
ODK_HASH,
|
||||
ODK_NUMTYPES,
|
||||
};
|
||||
|
||||
enum ODK_FieldMode {
|
||||
ODK_READ,
|
||||
ODK_WRITE,
|
||||
ODK_DUMP,
|
||||
};
|
||||
|
||||
struct ODK_Field {
|
||||
ODK_FieldType type;
|
||||
void* value;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct ODK_LicenseResponseParams {
|
||||
ODK_CoreMessage core_message;
|
||||
bool initial_license_load;
|
||||
bool usage_entry_present;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
ODK_ParsedLicense parsed_license;
|
||||
std::vector<ODK_Field> extra_fields;
|
||||
};
|
||||
|
||||
struct ODK_RenewalResponseParams {
|
||||
ODK_CoreMessage core_message;
|
||||
uint64_t system_time;
|
||||
uint64_t playback_clock;
|
||||
uint64_t renewal_duration;
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
uint64_t playback_timer;
|
||||
std::vector<ODK_Field> extra_fields;
|
||||
};
|
||||
|
||||
struct ODK_ProvisioningResponseParams {
|
||||
ODK_CoreMessage core_message;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
uint32_t device_id_length;
|
||||
ODK_ParsedProvisioning parsed_provisioning;
|
||||
std::vector<ODK_Field> extra_fields;
|
||||
};
|
||||
|
||||
/* Default values in core_message for testing */
|
||||
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
|
||||
uint32_t message_type);
|
||||
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params);
|
||||
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params);
|
||||
void ODK_SetDefaultProvisioningResponseParams(
|
||||
ODK_ProvisioningResponseParams* params);
|
||||
|
||||
size_t ODK_FieldLength(ODK_FieldType type);
|
||||
size_t ODK_AllocSize(ODK_FieldType type);
|
||||
|
||||
/* Copy ODK_Field to buf */
|
||||
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field);
|
||||
/* Load buf to ODK_Field */
|
||||
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, const ODK_Field* field);
|
||||
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, const ODK_Field* field);
|
||||
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
|
||||
const size_t size_in, size_t* size_out,
|
||||
const std::vector<ODK_Field>& fields);
|
||||
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
|
||||
const std::vector<ODK_Field>& fields);
|
||||
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields);
|
||||
|
||||
/* Serialize core_message and extra_fields into buf */
|
||||
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
|
||||
const std::vector<ODK_Field>& extra_fields,
|
||||
uint8_t** buf, uint32_t* buf_size);
|
||||
|
||||
} /* namespace wvodk_test */
|
||||
|
||||
#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */
|
||||
1236
oemcrypto/odk/test/odk_timer_test.cpp
Normal file
1236
oemcrypto/odk/test/odk_timer_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user