/* 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) -> size_t { if (size <= clock_value_size) { return 0; } // 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 0; } 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); return OEMCrypto_SUCCESS == err ? size : 0; }; return roundtrip; } // @ odk deserialize; kdo serialize namespace { struct ODK_Common_Args { uint16_t api_minor_version; uint16_t api_major_version; uint32_t nonce; uint32_t session_id; }; struct ODK_ParseLicense_Args { ODK_Common_Args common; 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_Common_Args common; uint64_t system_time; ODK_TimerLimits timer_limits; ODK_ClockValues clock_values; }; struct ODK_ParseProvisioning_Args { ODK_Common_Args common; size_t device_id_length; uint8_t device_id[64]; }; } // namespace uint8_t convert_byte_to_valid_boolean(const bool* in) { uint8_t boolean_value; memcpy(&boolean_value, in, 1); return boolean_value % 2; } 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& common = args->common; ODK_LicenseRequest core_request{common.api_minor_version, common.api_major_version, common.nonce, common.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& common = args->common; ODK_RenewalRequest core_request{common.api_minor_version, common.api_major_version, common.nonce, common.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& common = args->common; assert(args->device_id_length <= sizeof(args->device_id)); ODK_ProvisioningRequest core_request{ common.api_minor_version, common.api_major_version, common.nonce, common.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) -> size_t { // Input byte array format: [function arguments][data to parse] if (args_size > size) { return 0; } T t = {}; const uint8_t* buf = in + args_size; size_t len = size - args_size; std::shared_ptr _args(new A()); A* args = _args.get(); memcpy(args, in, args_size); const auto& common = args->common; ODK_NonceValues nonce_values = {common.api_minor_version, common.api_major_version, common.nonce, common.session_id}; OEMCryptoResult err = odk_fun(buf, len, args, &nonce_values, &t); if (err != OEMCrypto_SUCCESS) { return 0; } std::string oemcrypto_core_message; if (!kdo_fun(args, t, &oemcrypto_core_message)) { return 0; } assert(oemcrypto_core_message.size() <= size); memcpy(out, oemcrypto_core_message.data(), oemcrypto_core_message.size()); return oemcrypto_core_message.size(); }; 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(); size_t n = roundtrip(in, out, size, args_size); assert(n <= size && 0 == memcmp(in + args_size, out, n)); } // 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; }