/* 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 #include #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 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 _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 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 _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 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(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_ */