/* 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 #include #include #include #include #include #include #include "OEMCryptoCENCCommon.h" #include "core_message_deserialize.h" #include "core_message_serialize.h" #include "core_message_types.h" #include "odk.h" #include "odk_serialize.h" #include "odk_structs.h" #include "odk_structs_priv.h" typedef std::function roundtrip_fun; 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; // @ kdo deserialize; odk serialize static 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); } static 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); } static 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(device_id.data()), device_id.size()); } /** * 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 roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, size_t clock_value_size) -> void { if (size <= clock_value_size) { return; } // Input byte array format: [Clock Values][data to parse] 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; } assert(0 == memcmp(in + clock_value_size, out, size)); }; return roundtrip; } // @ odk deserialize; kdo serialize namespace { struct ODK_ParseLicense_Args { ODK_NonceValues nonce_values; uint8_t initial_license_load; uint8_t usage_entry_present; uint8_t request_hash[32]; 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]; }; } // namespace bool convert_byte_to_valid_boolean(const bool* in) { const int value = *reinterpret_cast(in); return value != 0; } static 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(a->initial_license_load), static_cast(a->usage_entry_present), a->request_hash, &a->timer_limits, &a->clock_values, nonce_values, parsed_lic); } static 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(args->request_hash), 32); return CreateCoreLicenseResponse( parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message); } static 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); if (OEMCrypto_SUCCESS == err) { Message* msg = nullptr; AllocateMessage(&msg, message_block); InitMessage(msg, const_cast(buf), len); SetSize(msg, len); Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); assert(ValidMessage(msg)); } return err; } static 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 CreateCoreRenewalResponse( core_request, args->timer_limits.initial_renewal_duration_seconds, oemcrypto_core_message); } static 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); } static 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(args->device_id), args->device_id_length)}; return CreateCoreProvisioningResponse(parsed_prov, core_request, oemcrypto_core_message); } /** * 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 static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, size_t args_size) -> void { // Input byte array format: [function arguments][data to parse] if (args_size > size) { return; } T t = {}; 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; args->nonce_values.api_minor_version = ODK_MINOR_VERSION; /* * Input random bytes from autofuzz are interpreted by this script as * [function args][data to parse]. Odk deserialize functions * expect the nonce values in function args to match with those * in data to parse which is not possible with random bytes. * We follow two pass approach. * * 1st pass - We copy random bytes into struct t and call kdo serialize * with function args which will create oemcrypto core message using nonce * from function args. Now we have a valid oemcrypto core message which is * formed using nonce_values from function args which acts as input bytes * for 2nd pass * * 2nd pass - oemcrypto core message from 1st pass guarantees that * nonce_values in [function args] and core message match. we call * odk_deserialize using nonce from function args and oemcrypto core message * from 1st pass. Then we call kdo function which generates oemcrypto core * message2, which should be equal to oemcrypto_core_message which was input * to 2nd pass */ // TODO(ellurubharath): Use structure aware fuzzing // 1st pass memcpy(&t, buf, sizeof(t)); std::string oemcrypto_core_message; if (!kdo_fun(args, t, &oemcrypto_core_message)) { return; } assert(oemcrypto_core_message.size() <= size); // 2nd pass ODK_NonceValues nonce_values = args->nonce_values; OEMCryptoResult result = odk_fun(reinterpret_cast(oemcrypto_core_message.data()), oemcrypto_core_message.size(), args, &nonce_values, &t); if (result != OEMCrypto_SUCCESS) { return; } std::string oemcrypto_core_message2; if (!kdo_fun(args, t, &oemcrypto_core_message2)) { return; } assert(oemcrypto_core_message == oemcrypto_core_message2); }; return roundtrip; } // @ fuzz raw -> parsed -> raw static void verify_roundtrip(const uint8_t* in, size_t size, roundtrip_fun roundtrip, size_t args_size) { std::vector _out(size); auto out = _out.data(); roundtrip(in, out, size, args_size); } // Entry point for fuzzer, data is random bytes program gets from autofuzzer extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { verify_roundtrip(data, size, kdo_odk(CoreLicenseRequestFromMessage, odk_serialize_LicenseRequest), 0); verify_roundtrip(data, size, kdo_odk(CoreRenewalRequestFromMessage, odk_serialize_RenewalRequest), sizeof(ODK_ClockValues)); verify_roundtrip( data, size, kdo_odk(CoreProvisioningRequestFromMessage, odk_serialize_ProvisioningRequest), 0); verify_roundtrip( data, size, odk_kdo( odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse), sizeof(ODK_ParseLicense_Args)); verify_roundtrip( data, size, odk_kdo( odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse), sizeof(ODK_ParseRenewal_Args)); verify_roundtrip(data, size, odk_kdo( odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse), sizeof(ODK_ParseProvisioning_Args)); return 0; }