From 6a59fee30abc86dc5642f808202a51e81336b0e8 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Fri, 9 Apr 2021 14:43:02 -0700 Subject: [PATCH] Update ODK library for OPK compatibility This is a merge from http://go/wvgerrit of several changes to the ODK library that allow it to be used in the same compilation unit as the OPK serialization/deserialization code. Merge of: http://go/wvgerrit/104403 http://go/wvgerrit/105663 http://go/wvgerrit/106004 http://go/wvgerrit/107903 http://go/wvgerrit/107985 http://go/wvgerrit/110167 http://go/wvgerrit/110403 http://go/wvgerrit/110423 http://go/wvgerrit/110663 http://go/wvgerrit/110703 http://go/wvgerrit/110985 http://go/wvgerrit/111703 http://go/wvgerrit/112563 http://go/wvgerrit/113243 http://go/wvgerrit/115204 http://go/wvgerrit/117803 http://go/wvgerrit/121949 bug: 174518179 bug: 175920940 bug: 175126254 Change-Id: I433459182043ca43a040cdbc16d04f2b8215067a --- libwvdrmengine/oemcrypto/odk/Android.bp | 1 + libwvdrmengine/oemcrypto/odk/README | 6 +- .../odk/include/OEMCryptoCENCCommon.h | 1 + .../oemcrypto/odk/include/odk_message.h | 143 +++++++++++++ .../oemcrypto/odk/include/odk_structs.h | 8 +- .../odk/src/core_message_deserialize.cpp | 12 +- .../odk/src/core_message_serialize.cpp | 16 +- libwvdrmengine/oemcrypto/odk/src/odk.c | 74 +++---- libwvdrmengine/oemcrypto/odk/src/odk_endian.h | 14 +- .../oemcrypto/odk/src/odk_message.c | 171 +++++++++++++++ .../oemcrypto/odk/src/odk_message_priv.h | 41 ++++ .../oemcrypto/odk/src/odk_overflow.c | 10 + .../oemcrypto/odk/src/odk_overflow.h | 1 + .../oemcrypto/odk/src/odk_serialize.c | 58 ++--- .../oemcrypto/odk/src/odk_serialize.h | 26 +-- .../oemcrypto/odk/src/serialization_base.c | 200 +++++++----------- .../oemcrypto/odk/src/serialization_base.h | 80 ++----- .../odk/test/fuzzing/odk_fuzz_helper.cpp | 8 +- ...rovisioning_response_fuzz_with_mutator.cpp | 4 +- ...odk_renewal_response_fuzz_with_mutator.cpp | 4 +- .../oemcrypto/odk/test/odk_test.cpp | 2 - .../oemcrypto/odk/test/odk_test_helper.cpp | 32 +-- 22 files changed, 588 insertions(+), 324 deletions(-) create mode 100644 libwvdrmengine/oemcrypto/odk/include/odk_message.h create mode 100644 libwvdrmengine/oemcrypto/odk/src/odk_message.c create mode 100644 libwvdrmengine/oemcrypto/odk/src/odk_message_priv.h diff --git a/libwvdrmengine/oemcrypto/odk/Android.bp b/libwvdrmengine/oemcrypto/odk/Android.bp index 01af3332..ef284bdf 100644 --- a/libwvdrmengine/oemcrypto/odk/Android.bp +++ b/libwvdrmengine/oemcrypto/odk/Android.bp @@ -27,6 +27,7 @@ cc_library_static { srcs: [ "src/odk.c", + "src/odk_message.c", "src/odk_overflow.c", "src/odk_serialize.c", "src/odk_timer.c", diff --git a/libwvdrmengine/oemcrypto/odk/README b/libwvdrmengine/oemcrypto/odk/README index ba8c8c73..408fc448 100644 --- a/libwvdrmengine/oemcrypto/odk/README +++ b/libwvdrmengine/oemcrypto/odk/README @@ -1,8 +1,6 @@ This ODK Library is used to generate and parse core OEMCrypto messages for -OEMCrypto v16 and above. - -This library is used by both OEMCrypto on a device, and by Widevine license and -provisioning servers. +OEMCrypto v16 and above. This library is used by both OEMCrypto on a device +and by Widevine license and provisioning servers. The source of truth for these files is in the server code base on piper. Do not edit these files in the Android directory tree or in the Widevine Git diff --git a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h index 5e54cc6b..69ba2ad3 100644 --- a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -87,6 +87,7 @@ typedef enum OEMCryptoResult { OEMCrypto_ERROR_LICENSE_RELOAD = 57, OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59, + OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION = 60, /* ODK return values */ ODK_ERROR_BASE = 1000, ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE, diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_message.h b/libwvdrmengine/oemcrypto/odk/include/odk_message.h new file mode 100644 index 00000000..4e97804c --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/include/odk_message.h @@ -0,0 +1,143 @@ +/* + * 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_INCLUDE_ODK_MESSAGE_H_ +#define WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* + * ODK_Message is the structure that defines the serialized messages passed + * between the REE and TEE. ODK_Message is an abstract data type that represents + * the concept of a message without disclosing the implementation details. By + * hiding the internal structure, modification of the message fields by code + * that is not privy to the message definition can be prevented. If the message + * definition was exposed, there could be serious yet subtle errors in message + * manipulation anywhere in the code base. By restricting message modification + * it is possible to enforce validity and integrity with a small set of + * primitives that can be carefully reviewed. Checks can be added to verify that + * a message's fields are internally consistent before every operation. As an + * example, it can be guaranteed that the message status will be checked prior + * to accessing any field so parsing will be stopped when the message status is + * set after any parse error is detected. This also makes development easier + * since any access to the message structure can be tracked through a single + * point so, for example, it becomes possible to add trace statements globally + * to all message operations by only changing the field accessors. Finally it + * simplifies maintenance by localizing changes to the message structure to a + * few files. + */ + +#if defined(__GNUC__) || defined(__clang__) +# define ALIGNED __attribute__((aligned)) +#else +# define ALIGNED +# error ODK_Message must be aligned to the maximum useful alignment of the \ + machine you are compiling for. Define the ALIGNED macro accordingly. +#endif + +typedef struct { +#define SIZE_OF_ODK_MESSAGE_IMPL 64 + uint8_t opaque_data[SIZE_OF_ODK_MESSAGE_IMPL]; +} ALIGNED ODK_Message; + +typedef enum { + MESSAGE_STATUS_OK = 0xe937fcf7, + MESSAGE_STATUS_UNKNOWN_ERROR = 0xe06c1190, + MESSAGE_STATUS_OVERFLOW_ERROR = 0xc43ae4bc, + MESSAGE_STATUS_UNDERFLOW_ERROR = 0x7123cd0b, + MESSAGE_STATUS_PARSE_ERROR = 0x0b9f6189, + MESSAGE_STATUS_NULL_POINTER_ERROR = 0x2d66837a, + MESSAGE_STATUS_API_VALUE_ERROR = 0x6ba34f47, + MESSAGE_STATUS_END_OF_MESSAGE_ERROR = 0x998db72a, + MESSAGE_STATUS_INVALID_ENUM_VALUE = 0xedb88197, + MESSAGE_STATUS_INVALID_TAG_ERROR = 0x14dce06a, + MESSAGE_STATUS_NOT_INITIALIZED = 0x2990b6c6, + MESSAGE_STATUS_OUT_OF_MEMORY = 0xfc5c64cc, + MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED = 0xfafecacf, + MESSAGE_STATUS_SECURE_BUFFER_ERROR = 0x78f0e873 +} ODK_MessageStatus; + +/* + * Create a message structure that references a separate data buffer. An + * initialized message is returned. The caller is responsible for ensuring that + * the buffer remains allocated for the lifetime of the message. If |buffer| + * is NULL or |capacity| is zero, the message is invalid and the status + * will be set to MESSAGE_STATUS_NOT_INITIALIZED. + */ +ODK_Message ODK_Message_Create(uint8_t* buffer, size_t capacity); + +/* + * Erase the contents of the message, set it to an empty state by setting the + * message size and read offset to 0, effectively erasing the contents of the + * message. The message data buffer pointer remains unchanged, i.e. the message + * retains ownership of the buffer. The message status is reset to + * MESSAGE_STATUS_OK. + */ +void ODK_Message_Clear(ODK_Message* message); + +/* + * Reset read pointer to the beginning of the message and clear status + * so that parsing of the message will restart at the beginning of the + * message. The message status is reset to MESSAGE_STATUS_OK. + */ +void ODK_Message_Reset(ODK_Message* message); + +/* + * Return a pointer to the message data buffer, i.e. the message payload. + * This is the buffer address that was passed into ODK_Message_Create. + */ +uint8_t* ODK_Message_GetBase(ODK_Message* message); + +/* + * Get the maximum number of bytes the message can hold. + */ +size_t ODK_Message_GetCapacity(ODK_Message* message); + +/* + * Get the number of bytes currently in the message + */ +size_t ODK_Message_GetSize(ODK_Message* message); + +/* + * Get the offset of where the next bytes will be read from the message data + * buffer. + */ +size_t ODK_Message_GetOffset(ODK_Message* message); + +/* + * Return the status of the message + */ +ODK_MessageStatus ODK_Message_GetStatus(ODK_Message* message); + +/* + * Set the message status to a specific value + */ +void ODK_Message_SetStatus(ODK_Message* message, ODK_MessageStatus status); + +/* + * Set the size of the message to a value. This may be needed after writing data + * into the message data buffer. + */ +void ODK_Message_SetSize(ODK_Message* message, size_t size); + +/* + * Test if the integrity of a message. This means that the status must be + * MESSAGE_STATUS_OK and that the internal fields of the message are + * within the range of valid values. + */ +bool ODK_Message_IsValid(ODK_Message* message); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index bd500d87..5ed6f1ab 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -12,10 +12,14 @@ /* The version of this library. */ #define ODK_MAJOR_VERSION 16 -#define ODK_MINOR_VERSION 3 +// TODO(b/163416999): Do not change minor version to 16.5 on master branch. The +// version 16.5 is reserved for Alcatraz, iOS, and other L3 platforms using +// third-party obfuscation tools. The version should not be used for CE CDM or +// Android CDM. We should jump straight to 17.0. +#define ODK_MINOR_VERSION 4 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v16.3 2020-08-18" +#define ODK_RELEASE_DATE "ODK v16.4 2020-10-23" /* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp index 47d4478c..f659a6db 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp @@ -39,13 +39,11 @@ bool ParseRequest(uint32_t message_type, reinterpret_cast(oemcrypto_core_message.c_str()); const size_t buf_length = oemcrypto_core_message.size(); - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; - Message* msg = reinterpret_cast(blk); - InitMessage(msg, const_cast(buf), buf_length); - SetSize(msg, buf_length); + ODK_Message msg = ODK_Message_Create(const_cast(buf), buf_length); + ODK_Message_SetSize(&msg, buf_length); - unpacker(msg, prepared); - if (!ValidMessage(msg)) { + unpacker(&msg, prepared); + if (!ODK_Message_IsValid(&msg)) { return false; } @@ -80,7 +78,7 @@ bool ParseRequest(uint32_t message_type, // than the total message size. We allow the total message size to be larger // for forward compatibility because future messages might have extra fields // that we can ignore. - if (core_message.message_length < GetOffset(msg)) return false; + if (core_message.message_length < ODK_Message_GetOffset(&msg)) return false; return true; } diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp index 0e05330a..e4c6fe07 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp @@ -50,18 +50,16 @@ bool CreateResponse(uint32_t message_type, const S& core_request, static constexpr size_t BUF_CAPACITY = 2048; std::vector buf(BUF_CAPACITY, 0); - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; - Message* msg = reinterpret_cast(blk); - InitMessage(msg, buf.data(), buf.capacity()); - packer(msg, &response); - if (!ValidMessage(msg)) { + ODK_Message msg = ODK_Message_Create(buf.data(), buf.capacity()); + packer(&msg, &response); + if (!ODK_Message_IsValid(&msg)) { return false; } - uint32_t message_length = GetSize(msg); - InitMessage(msg, buf.data() + sizeof(header->message_type), - sizeof(header->message_length)); - Pack_uint32_t(msg, &message_length); + uint32_t message_length = ODK_Message_GetSize(&msg); + msg = ODK_Message_Create(buf.data() + sizeof(header->message_type), + sizeof(header->message_length)); + Pack_uint32_t(&msg, &message_length); oemcrypto_core_message->assign(reinterpret_cast(buf.data()), message_length); return true; diff --git a/libwvdrmengine/oemcrypto/odk/src/odk.c b/libwvdrmengine/oemcrypto/odk/src/odk.c index 0bb6ab18..f0ec34ec 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk.c @@ -27,9 +27,7 @@ static OEMCryptoResult ODK_PrepareRequest( return ODK_ERROR_CORE_MESSAGE; } - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; - Message* msg = (Message*)blk; - InitMessage(msg, message, *core_message_length); + ODK_Message msg = ODK_Message_Create(message, *core_message_length); /* The core message should be at the beginning of the buffer, and with a * shorter length. */ @@ -52,7 +50,7 @@ static OEMCryptoResult ODK_PrepareRequest( return ODK_ERROR_CORE_MESSAGE; } Pack_ODK_PreparedLicenseRequest( - msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer); + &msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer); break; } case ODK_Renewal_Request_Type: { @@ -61,7 +59,7 @@ static OEMCryptoResult ODK_PrepareRequest( return ODK_ERROR_CORE_MESSAGE; } Pack_ODK_PreparedRenewalRequest( - msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer); + &msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer); break; } case ODK_Provisioning_Request_Type: { @@ -71,7 +69,7 @@ static OEMCryptoResult ODK_PrepareRequest( return ODK_ERROR_CORE_MESSAGE; } Pack_ODK_PreparedProvisioningRequest( - msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer); + &msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer); break; } default: { @@ -80,13 +78,13 @@ static OEMCryptoResult ODK_PrepareRequest( } *core_message_length = core_message->message_length; - if (GetStatus(msg) != MESSAGE_STATUS_OK) { + if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK) { /* This is to indicate the caller that the core_message_length has been * appropriately set, but the message buffer is either empty or too small, * which needs to be initialized and filled in the subsequent call. */ return OEMCrypto_ERROR_SHORT_BUFFER; } - if (GetSize(msg) != *core_message_length) { + if (ODK_Message_GetSize(&msg) != *core_message_length) { /* This should not happen. Something is wrong. */ return ODK_ERROR_CORE_MESSAGE; } @@ -102,20 +100,11 @@ static OEMCryptoResult ODK_ParseResponse( return ODK_ERROR_CORE_MESSAGE; } - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; - Message* msg = (Message*)blk; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" - /* We initialize the message buffer with a size of the entire message - * length. */ - /* TODO(b/164486737): Fix the cast-qual warning */ - InitMessage(msg, (uint8_t*)message, message_length); -#pragma GCC diagnostic pop + ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length); /* The core message should be at the beginning of the buffer, and with a * shorter length. The core message is the part we are parsing. */ - SetSize(msg, core_message_length); + ODK_Message_SetSize(&msg, core_message_length); /* Parse message and unpack it into response buffer. */ switch (message_type) { @@ -123,14 +112,14 @@ static OEMCryptoResult ODK_ParseResponse( if (sizeof(ODK_LicenseResponse) > response_buffer_length) { return ODK_ERROR_CORE_MESSAGE; } - Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer); + Unpack_ODK_LicenseResponse(&msg, (ODK_LicenseResponse*)response_buffer); break; } case ODK_Renewal_Response_Type: { if (sizeof(ODK_RenewalResponse) > response_buffer_length) { return ODK_ERROR_CORE_MESSAGE; } - Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer); + Unpack_ODK_RenewalResponse(&msg, (ODK_RenewalResponse*)response_buffer); break; } case ODK_Provisioning_Response_Type: { @@ -138,7 +127,7 @@ static OEMCryptoResult ODK_ParseResponse( return ODK_ERROR_CORE_MESSAGE; } Unpack_ODK_ProvisioningResponse( - msg, (ODK_ProvisioningResponse*)response_buffer); + &msg, (ODK_ProvisioningResponse*)response_buffer); break; } default: { @@ -147,9 +136,9 @@ static OEMCryptoResult ODK_ParseResponse( } ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer; - if (GetStatus(msg) != MESSAGE_STATUS_OK || + if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK || message_type != core_message->message_type || - GetOffset(msg) != core_message->message_length) { + ODK_Message_GetOffset(&msg) != core_message->message_length) { return ODK_ERROR_CORE_MESSAGE; } @@ -307,20 +296,24 @@ OEMCryptoResult ODK_ParseLicense( return ODK_ERROR_CORE_MESSAGE; } - if (parsed_license->nonce_required) { - if (initial_license_load) { - if (nonce_values->nonce != - license_response.request.core_message.nonce_values.nonce || - nonce_values->session_id != - license_response.request.core_message.nonce_values.session_id) { - return OEMCrypto_ERROR_INVALID_NONCE; - } - } else { /* !initial_license_load */ - nonce_values->nonce = - license_response.request.core_message.nonce_values.nonce; - nonce_values->session_id = - license_response.request.core_message.nonce_values.session_id; + /* If this is the first time we load this license, then we verify that the + * nonce values are the correct, otherwise we copy the nonce values. If the + * nonce values are not required to be correct, then we don't know if this is + * an initial load or not. In that case, we also copy the values so that we + * can use the nonce values later for a renewal. + */ + if (parsed_license->nonce_required && initial_license_load) { + if (nonce_values->nonce != + license_response.request.core_message.nonce_values.nonce || + nonce_values->session_id != + license_response.request.core_message.nonce_values.session_id) { + return OEMCrypto_ERROR_INVALID_NONCE; } + } else { /* !initial_license_load, or can't tell if initial. */ + nonce_values->nonce = + license_response.request.core_message.nonce_values.nonce; + nonce_values->session_id = + license_response.request.core_message.nonce_values.session_id; } /* For v16, in order to be backwards compatible with a v15 license server, * OEMCrypto stores a hash of the core license request and only signs the @@ -367,9 +360,12 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, */ /* If a renewal request is lost in transit, we should throw it out and create * a new one. We use the timestamp to make sure we have the latest request. + * We only do this if playback has already started. This allows us to reload + * an offline license and also reload a renewal before starting playback. */ - if (clock_values->time_of_renewal_request < - renewal_response.request.playback_time) { + if (clock_values->timer_status != ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED && + clock_values->time_of_renewal_request < + renewal_response.request.playback_time) { return ODK_STALE_RENEWAL; } return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time, diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h index 00dc01ec..f65234ec 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h @@ -11,11 +11,23 @@ extern "C" { #if defined(__linux__) || defined(__ANDROID__) #include +#define oemcrypto_htobe16 htobe16 +#define oemcrypto_be16toh be16toh #define oemcrypto_htobe32 htobe32 #define oemcrypto_be32toh be32toh #define oemcrypto_htobe64 htobe64 #define oemcrypto_be64toh be64toh -#else /* defined(__linux__) || defined(__ANDROID__) */ +#elif defined(__APPLE__) +#include +#define oemcrypto_htobe16 OSSwapHostToBigInt16 +#define oemcrypto_be16toh OSSwapBigToHostInt16 +#define oemcrypto_htobe32 OSSwapHostToBigInt32 +#define oemcrypto_be32toh OSSwapBigToHostInt32 +#define oemcrypto_htobe64 OSSwapHostToBigInt64 +#define oemcrypto_be64toh OSSwapBigToHostInt64 +#else /* defined(__linux__) || defined(__ANDROID__) */ +uint32_t oemcrypto_htobe16(uint16_t u16); +uint32_t oemcrypto_be16toh(uint16_t u16); uint32_t oemcrypto_htobe32(uint32_t u32); uint32_t oemcrypto_be32toh(uint32_t u32); uint64_t oemcrypto_htobe64(uint64_t u64); diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_message.c b/libwvdrmengine/oemcrypto/odk/src/odk_message.c new file mode 100644 index 00000000..890c8d17 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/src/odk_message.c @@ -0,0 +1,171 @@ +/* + * 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_message.h" +#include "odk_message_priv.h" + +#include +#include +#include + +/* + * C11 defines static_assert in assert.h. If it is available, force a compile + * time error if the abstract ODK_Message struct size does not match its + * implementation. If static_assert is not available, the runtime assert in + * InitMessage will catch the mismatch at the time a message is initialized. + */ +#ifdef static_assert +static_assert( + sizeof(ODK_Message) >= sizeof(ODK_Message_Impl), + "sizeof(ODK_Message) is too small. You can increase " + "SIZE_OF_ODK_MESSAGE_IMPL in odk_message.h to make it large enough."); +#endif + +/* + * Create a message structure that references a separate data buffer. An + * initialized message is returned. The caller is responsible for ensuring that + * the buffer remains allocated for the lifetime of the message. |buffer| may be + * NULL. Serialization into a message with a NULL buffer will cause the message + * size to be incremented, but no data will be written into the message + * buffer. This is useful for calculating the amount of space a message will + * need, prior to doing the actual serialization. The buffer contents are + * unchanged by this function. + */ +ODK_Message ODK_Message_Create(uint8_t* buffer, size_t capacity) { + assert(sizeof(ODK_Message) >= sizeof(ODK_Message_Impl)); + ODK_Message message; + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)&message; + message_impl->base = buffer; + message_impl->capacity = capacity; + message_impl->size = 0; + message_impl->read_offset = 0; + message_impl->status = MESSAGE_STATUS_OK; + return message; +} + +/* + * Erase the contents of the message, set it to an empty state by setting the + * message size and read offset to 0, effectively erasing the contents of the + * message. The message data buffer pointer remains unchanged, i.e. the message + * retains ownership of the buffer. The message buffer is zero-filled. The + * message status is reset to MESSAGE_STATUS_OK. + */ +void ODK_Message_Clear(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + message_impl->read_offset = 0; + message_impl->size = 0; + message_impl->status = MESSAGE_STATUS_OK; + if (message_impl->base) { + memset(message_impl->base, 0, message_impl->capacity); + } +} + +/* + * Reset read pointer to the beginning of the message and clear status + * so that parsing of the message will restart at the beginning of the + * message. The message status is reset to MESSAGE_STATUS_OK. + */ +void ODK_Message_Reset(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + message_impl->read_offset = 0; + message_impl->status = MESSAGE_STATUS_OK; +} + +/* + * Return a pointer to the message data buffer, i.e. the message payload. + * This is the buffer address that was passed into ODK_Message_Create. + */ +uint8_t* ODK_Message_GetBase(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + return message_impl->base; +} + +/* + * Get the maximum number of bytes the message can hold. + */ +size_t ODK_Message_GetCapacity(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + return message_impl->capacity; +} + +/* + * Get the number of bytes currently in the message + */ +size_t ODK_Message_GetSize(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + return message_impl->size; +} + +/* + * Get the offset of where the next bytes will be read from the message data + * buffer. + */ +size_t ODK_Message_GetOffset(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + return message_impl->read_offset; +} + +/* + * Return the status of the message + */ +ODK_MessageStatus ODK_Message_GetStatus(ODK_Message* message) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + return message_impl->status; +} + +/* + * Set the message status to a specific value + */ +void ODK_Message_SetStatus(ODK_Message* message, ODK_MessageStatus status) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + /* preserve the first error */ + if (message_impl->status == MESSAGE_STATUS_OK) { + message_impl->status = status; + } +} + +/* + * Set the size of the message to a value. This may be needed after writing data + * into the message data buffer. + */ +void ODK_Message_SetSize(ODK_Message* message, size_t size) { + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + assert(message_impl != NULL); + assert(size <= message_impl->capacity); + message_impl->size = size; +} + +/* + * Test if the integrity of a message. This means that the status must be + * MESSAGE_STATUS_OK and that the base, read_offset, size and capacity of the + * message are within the range of valid values. The message's base pointer + * may be NULL if the buffer has not been assigned yet, that is not invalid. + */ +bool ODK_Message_IsValid(ODK_Message* message) { + assert(message); + ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message; + if (message_impl == NULL) { + return false; + } + if (message_impl->status != MESSAGE_STATUS_OK) { + return false; + } + if (message_impl->read_offset > message_impl->capacity || + message_impl->size > message_impl->capacity || + message_impl->read_offset > message_impl->size) { + message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR; + return false; + } + return true; +} diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_message_priv.h b/libwvdrmengine/oemcrypto/odk/src/odk_message_priv.h new file mode 100644 index 00000000..c13cca47 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/src/odk_message_priv.h @@ -0,0 +1,41 @@ +/* + * 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_INCLUDE_ODK_MESSAGE_PRIV_H_ +#define WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_PRIV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This file must only be included by odk_message.c and serialization_base.c. + */ + +#include +#include + +/* + * This is the implementation of a message. This structure is private, i.e. it + * should only be included by files that are allowed to modify the internals of + * a message, that being odk_message.c and serialization_base.c. To ensure + * proper alignment and message size, an ODK_Message_Impl should never be + * allocated directly, instead allocate ODK_Message and cast to ODK_Message_Impl + * because ODK_Message_Impl may be smaller than ODK_Message. + */ +typedef struct { + uint8_t* base; + size_t capacity; + size_t size; + size_t read_offset; + ODK_MessageStatus status; +} ODK_Message_Impl; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_PRIV_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c index a03f0f61..ce62a4f7 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c @@ -34,3 +34,13 @@ int odk_add_overflow_ux(size_t a, size_t b, size_t* c) { } return 1; } + +int odk_mul_overflow_ux(size_t a, size_t b, size_t* c) { + if (b > 0 && a > SIZE_MAX / b) { + return 1; + } + if (c) { + *c = a * b; + } + return 0; +} diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h index 6f3f994e..a7a670bc 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h @@ -15,6 +15,7 @@ extern "C" { int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c); int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c); int odk_add_overflow_ux(size_t a, size_t b, size_t* c); +int odk_mul_overflow_ux(size_t a, size_t b, size_t* c); #ifdef __cplusplus } diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c index ae53e735..d881e3df 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c @@ -13,20 +13,20 @@ /* @@ private serialize */ -static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) { +static void Pack_ODK_NonceValues(ODK_Message* msg, ODK_NonceValues const* obj) { Pack_uint16_t(msg, &obj->api_minor_version); Pack_uint16_t(msg, &obj->api_major_version); Pack_uint32_t(msg, &obj->nonce); Pack_uint32_t(msg, &obj->session_id); } -static void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) { +static void Pack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage const* obj) { Pack_uint32_t(msg, &obj->message_type); Pack_uint32_t(msg, &obj->message_length); Pack_ODK_NonceValues(msg, &obj->nonce_values); } -static void Pack_OEMCrypto_KeyObject(Message* msg, +static void Pack_OEMCrypto_KeyObject(ODK_Message* msg, OEMCrypto_KeyObject const* obj) { Pack_OEMCrypto_Substring(msg, &obj->key_id); Pack_OEMCrypto_Substring(msg, &obj->key_data_iv); @@ -35,7 +35,7 @@ static void Pack_OEMCrypto_KeyObject(Message* msg, Pack_OEMCrypto_Substring(msg, &obj->key_control); } -static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) { +static void Pack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits const* obj) { Pack_bool(msg, &obj->soft_enforce_rental_duration); Pack_bool(msg, &obj->soft_enforce_playback_duration); Pack_uint64_t(msg, &obj->earliest_playback_start_seconds); @@ -44,10 +44,11 @@ static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) { Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds); } -static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) { +static void Pack_ODK_ParsedLicense(ODK_Message* msg, + ODK_ParsedLicense const* obj) { /* hand-coded */ if (obj->key_array_length > ODK_MAX_NUM_KEYS) { - SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); + ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); return; } Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv); @@ -64,7 +65,7 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) { } } -static void Pack_ODK_ParsedProvisioning(Message* msg, +static void Pack_ODK_ParsedProvisioning(ODK_Message* msg, ODK_ParsedProvisioning const* obj) { Pack_enum(msg, obj->key_type); Pack_OEMCrypto_Substring(msg, &obj->enc_private_key); @@ -74,19 +75,19 @@ static void Pack_ODK_ParsedProvisioning(Message* msg, /* @@ odk serialize */ -void Pack_ODK_PreparedLicenseRequest(Message* msg, +void Pack_ODK_PreparedLicenseRequest(ODK_Message* msg, ODK_PreparedLicenseRequest const* obj) { Pack_ODK_CoreMessage(msg, &obj->core_message); } -void Pack_ODK_PreparedRenewalRequest(Message* msg, +void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg, ODK_PreparedRenewalRequest const* obj) { Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_uint64_t(msg, &obj->playback_time); } void Pack_ODK_PreparedProvisioningRequest( - Message* msg, ODK_PreparedProvisioningRequest const* obj) { + ODK_Message* msg, ODK_PreparedProvisioningRequest const* obj) { Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_uint32_t(msg, &obj->device_id_length); PackArray(msg, &obj->device_id[0], sizeof(obj->device_id)); @@ -94,18 +95,20 @@ void Pack_ODK_PreparedProvisioningRequest( /* @@ kdo serialize */ -void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) { +void Pack_ODK_LicenseResponse(ODK_Message* msg, + ODK_LicenseResponse const* obj) { Pack_ODK_PreparedLicenseRequest(msg, &obj->request); Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license); PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash)); } -void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) { +void Pack_ODK_RenewalResponse(ODK_Message* msg, + ODK_RenewalResponse const* obj) { Pack_ODK_PreparedRenewalRequest(msg, &obj->request); Pack_uint64_t(msg, &obj->renewal_duration_seconds); } -void Pack_ODK_ProvisioningResponse(Message* msg, +void Pack_ODK_ProvisioningResponse(ODK_Message* msg, ODK_ProvisioningResponse const* obj) { Pack_ODK_PreparedProvisioningRequest(msg, &obj->request); Pack_ODK_ParsedProvisioning( @@ -116,20 +119,21 @@ void Pack_ODK_ProvisioningResponse(Message* msg, /* @@ private deserialize */ -static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) { +static void Unpack_ODK_NonceValues(ODK_Message* msg, ODK_NonceValues* obj) { Unpack_uint16_t(msg, &obj->api_minor_version); Unpack_uint16_t(msg, &obj->api_major_version); Unpack_uint32_t(msg, &obj->nonce); Unpack_uint32_t(msg, &obj->session_id); } -static void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) { +static void Unpack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage* obj) { Unpack_uint32_t(msg, &obj->message_type); Unpack_uint32_t(msg, &obj->message_length); Unpack_ODK_NonceValues(msg, &obj->nonce_values); } -static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) { +static void Unpack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject* obj) { Unpack_OEMCrypto_Substring(msg, &obj->key_id); Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv); Unpack_OEMCrypto_Substring(msg, &obj->key_data); @@ -137,7 +141,7 @@ static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) { Unpack_OEMCrypto_Substring(msg, &obj->key_control); } -static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { +static void Unpack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits* obj) { Unpack_bool(msg, &obj->soft_enforce_rental_duration); Unpack_bool(msg, &obj->soft_enforce_playback_duration); Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds); @@ -146,7 +150,7 @@ static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds); } -static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { +static void Unpack_ODK_ParsedLicense(ODK_Message* msg, ODK_ParsedLicense* obj) { Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv); Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys); Unpack_OEMCrypto_Substring(msg, &obj->pst); @@ -156,7 +160,7 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { Unpack_ODK_TimerLimits(msg, &obj->timer_limits); Unpack_uint32_t(msg, &obj->key_array_length); if (obj->key_array_length > ODK_MAX_NUM_KEYS) { - SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); + ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); return; } uint32_t i; @@ -165,7 +169,7 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { } } -static void Unpack_ODK_ParsedProvisioning(Message* msg, +static void Unpack_ODK_ParsedProvisioning(ODK_Message* msg, ODK_ParsedProvisioning* obj) { obj->key_type = (OEMCrypto_PrivateKeyType)Unpack_enum(msg); Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key); @@ -175,42 +179,42 @@ static void Unpack_ODK_ParsedProvisioning(Message* msg, /* @ kdo deserialize */ -void Unpack_ODK_PreparedLicenseRequest(Message* msg, +void Unpack_ODK_PreparedLicenseRequest(ODK_Message* msg, ODK_PreparedLicenseRequest* obj) { Unpack_ODK_CoreMessage(msg, &obj->core_message); } -void Unpack_ODK_PreparedRenewalRequest(Message* msg, +void Unpack_ODK_PreparedRenewalRequest(ODK_Message* msg, ODK_PreparedRenewalRequest* obj) { Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_uint64_t(msg, &obj->playback_time); } void Unpack_ODK_PreparedProvisioningRequest( - Message* msg, ODK_PreparedProvisioningRequest* obj) { + ODK_Message* msg, ODK_PreparedProvisioningRequest* obj) { Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_uint32_t(msg, &obj->device_id_length); UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id)); } -void Unpack_ODK_PreparedCommonRequest(Message* msg, +void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg, ODK_PreparedCommonRequest* obj) { Unpack_ODK_CoreMessage(msg, &obj->core_message); } /* @@ odk deserialize */ -void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) { +void Unpack_ODK_LicenseResponse(ODK_Message* msg, ODK_LicenseResponse* obj) { Unpack_ODK_PreparedLicenseRequest(msg, &obj->request); Unpack_ODK_ParsedLicense(msg, obj->parsed_license); UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash)); } -void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) { +void Unpack_ODK_RenewalResponse(ODK_Message* msg, ODK_RenewalResponse* obj) { Unpack_ODK_PreparedRenewalRequest(msg, &obj->request); Unpack_uint64_t(msg, &obj->renewal_duration_seconds); } -void Unpack_ODK_ProvisioningResponse(Message* msg, +void Unpack_ODK_ProvisioningResponse(ODK_Message* msg, ODK_ProvisioningResponse* obj) { Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request); Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning); diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h index 73f4abd1..33944b5d 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h @@ -16,34 +16,34 @@ extern "C" { #endif /* odk pack */ -void Pack_ODK_PreparedLicenseRequest(Message* msg, +void Pack_ODK_PreparedLicenseRequest(ODK_Message* msg, const ODK_PreparedLicenseRequest* obj); -void Pack_ODK_PreparedRenewalRequest(Message* msg, +void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg, const ODK_PreparedRenewalRequest* obj); void Pack_ODK_PreparedProvisioningRequest( - Message* msg, const ODK_PreparedProvisioningRequest* obj); + ODK_Message* msg, const ODK_PreparedProvisioningRequest* obj); /* odk unpack */ -void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj); -void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj); -void Unpack_ODK_ProvisioningResponse(Message* msg, +void Unpack_ODK_LicenseResponse(ODK_Message* msg, ODK_LicenseResponse* obj); +void Unpack_ODK_RenewalResponse(ODK_Message* msg, ODK_RenewalResponse* obj); +void Unpack_ODK_ProvisioningResponse(ODK_Message* msg, ODK_ProvisioningResponse* obj); /* kdo pack */ -void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj); -void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj); -void Pack_ODK_ProvisioningResponse(Message* msg, +void Pack_ODK_LicenseResponse(ODK_Message* msg, const ODK_LicenseResponse* obj); +void Pack_ODK_RenewalResponse(ODK_Message* msg, const ODK_RenewalResponse* obj); +void Pack_ODK_ProvisioningResponse(ODK_Message* msg, const ODK_ProvisioningResponse* obj); /* kdo unpack */ -void Unpack_ODK_PreparedLicenseRequest(Message* msg, +void Unpack_ODK_PreparedLicenseRequest(ODK_Message* msg, ODK_PreparedLicenseRequest* obj); -void Unpack_ODK_PreparedRenewalRequest(Message* msg, +void Unpack_ODK_PreparedRenewalRequest(ODK_Message* msg, ODK_PreparedRenewalRequest* obj); void Unpack_ODK_PreparedProvisioningRequest( - Message* msg, ODK_PreparedProvisioningRequest* obj); + ODK_Message* msg, ODK_PreparedProvisioningRequest* obj); -void Unpack_ODK_PreparedCommonRequest(Message* msg, +void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg, ODK_PreparedCommonRequest* obj); #ifdef __cplusplus diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c index 1b32d055..b9d55452 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c @@ -4,71 +4,61 @@ #include "serialization_base.h" +#include #include #include #include #include "OEMCryptoCENCCommon.h" +#include "odk_message.h" +#include "odk_message_priv.h" #include "odk_overflow.h" -struct _Message { - uint8_t* base; - size_t capacity; - size_t size; /* bytes written */ - size_t read_offset; /* bytes read */ - MessageStatus status; -}; - -bool ValidMessage(Message* message) { - if (message == NULL) { - return false; - } - if (message->status != MESSAGE_STATUS_OK) { - return false; - } - if (message->base == NULL) { - message->status = MESSAGE_STATUS_NULL_POINTER_ERROR; - return false; - } - if (message->size > message->capacity || - message->read_offset > message->size) { - message->status = MESSAGE_STATUS_OVERFLOW_ERROR; - return false; - } - return true; +/* + * An ODK_Message_Impl pointer must only be obtained by calling GetMessageImpl. + * This forces any message to pass the validity check before being operated on, + * which means that no function can modify or access the internals of a message + * without having it be validated first. + */ +static ODK_Message_Impl* GetMessageImpl(ODK_Message* message) { + if (!ODK_Message_IsValid(message)) return NULL; + return (ODK_Message_Impl*)message; } -static void PackBytes(Message* message, const uint8_t* ptr, size_t count) { - if (count <= message->capacity - message->size) { - memcpy((void*)(message->base + message->size), (void*)ptr, count); - message->size += count; +static void PackBytes(ODK_Message* message, const uint8_t* ptr, size_t count) { + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + if (count <= message_impl->capacity - message_impl->size) { + memcpy((void*)(message_impl->base + message_impl->size), (const void*)ptr, + count); + message_impl->size += count; } else { - message->status = MESSAGE_STATUS_OVERFLOW_ERROR; + message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR; } } -void Pack_enum(Message* message, int value) { +void Pack_enum(ODK_Message* message, int value) { uint32_t v32 = value; Pack_uint32_t(message, &v32); } -void Pack_bool(Message* message, const bool* value) { - if (!ValidMessage(message)) return; +void Pack_bool(ODK_Message* message, const bool* value) { + assert(value); uint8_t data[4] = {0}; data[3] = *value ? 1 : 0; PackBytes(message, data, sizeof(data)); } -void Pack_uint16_t(Message* message, const uint16_t* value) { - if (!ValidMessage(message)) return; +void Pack_uint16_t(ODK_Message* message, const uint16_t* value) { + assert(value); uint8_t data[2] = {0}; data[0] = *value >> 8; data[1] = *value >> 0; PackBytes(message, data, sizeof(data)); } -void Pack_uint32_t(Message* message, const uint32_t* value) { - if (!ValidMessage(message)) return; +void Pack_uint32_t(ODK_Message* message, const uint32_t* value) { + assert(value); uint8_t data[4] = {0}; data[0] = *value >> 24; data[1] = *value >> 16; @@ -77,160 +67,112 @@ void Pack_uint32_t(Message* message, const uint32_t* value) { PackBytes(message, data, sizeof(data)); } -void Pack_uint64_t(Message* message, const uint64_t* value) { - if (!ValidMessage(message)) return; +void Pack_uint64_t(ODK_Message* message, const uint64_t* value) { + assert(value); uint32_t hi = *value >> 32; uint32_t lo = *value; Pack_uint32_t(message, &hi); Pack_uint32_t(message, &lo); } -void PackArray(Message* message, const uint8_t* base, size_t size) { - if (!ValidMessage(message)) return; +void PackArray(ODK_Message* message, const uint8_t* base, size_t size) { PackBytes(message, base, size); } -void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) { +void Pack_OEMCrypto_Substring(ODK_Message* message, + const OEMCrypto_Substring* obj) { + assert(obj); uint32_t offset = (uint32_t)obj->offset; uint32_t length = (uint32_t)obj->length; - Pack_uint32_t(msg, &offset); - Pack_uint32_t(msg, &length); + Pack_uint32_t(message, &offset); + Pack_uint32_t(message, &length); } -static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) { - if (count <= message->size - message->read_offset) { - memcpy((void*)ptr, (void*)(message->base + message->read_offset), count); - message->read_offset += count; +static void UnpackBytes(ODK_Message* message, uint8_t* ptr, size_t count) { + assert(ptr); + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + if (count <= message_impl->size - message_impl->read_offset) { + memcpy((void*)ptr, (void*)(message_impl->base + message_impl->read_offset), + count); + message_impl->read_offset += count; } else { - message->status = MESSAGE_STATUS_UNDERFLOW_ERROR; + message_impl->status = MESSAGE_STATUS_UNDERFLOW_ERROR; } } -int Unpack_enum(Message* message) { +int Unpack_enum(ODK_Message* message) { uint32_t v32; Unpack_uint32_t(message, &v32); return v32; } -void Unpack_bool(Message* message, bool* value) { - if (!ValidMessage(message)) return; +void Unpack_bool(ODK_Message* message, bool* value) { uint8_t data[4] = {0}; UnpackBytes(message, data, sizeof(data)); + assert(value); *value = (0 != data[3]); } -void Unpack_uint16_t(Message* message, uint16_t* value) { - if (!ValidMessage(message)) return; +void Unpack_uint16_t(ODK_Message* message, uint16_t* value) { + assert(value); uint8_t data[2] = {0}; UnpackBytes(message, data, sizeof(data)); *value = data[0]; *value = *value << 8 | data[1]; } -void Unpack_uint32_t(Message* message, uint32_t* value) { - if (!ValidMessage(message)) return; +void Unpack_uint32_t(ODK_Message* message, uint32_t* value) { + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; uint8_t data[4] = {0}; UnpackBytes(message, data, sizeof(data)); + assert(value); *value = data[0]; *value = *value << 8 | data[1]; *value = *value << 8 | data[2]; *value = *value << 8 | data[3]; } -void Unpack_uint64_t(Message* message, uint64_t* value) { - if (!ValidMessage(message)) return; +void Unpack_uint64_t(ODK_Message* message, uint64_t* value) { uint32_t hi = 0; uint32_t lo = 0; Unpack_uint32_t(message, &hi); Unpack_uint32_t(message, &lo); + assert(value); *value = hi; *value = *value << 32 | lo; } -void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) { +void Unpack_OEMCrypto_Substring(ODK_Message* message, + OEMCrypto_Substring* obj) { uint32_t offset = 0, length = 0; - Unpack_uint32_t(msg, &offset); - Unpack_uint32_t(msg, &length); - if (!ValidMessage(msg)) return; + Unpack_uint32_t(message, &offset); + Unpack_uint32_t(message, &length); + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + /* Each substring should be contained within the message body, which is in the * total message, just after the core message. The offset of a substring is * relative to the message body. So we need to verify: - * 0 < offset and offset + length < message->capacity - message->size - * or offset + length + message->size < message->capacity + * 0 < offset and offset + length < message_impl->capacity - + * message_impl->size or offset + length + message_impl->size < + * message_impl->capacity */ size_t substring_end = 0; /* = offset + length; */ - size_t end = 0; /* = substring_end + message->size; */ + size_t end = 0; /* = substring_end + message_impl->size; */ if (odk_add_overflow_ux(offset, length, &substring_end) || - odk_add_overflow_ux(substring_end, msg->size, &end) || - end > msg->capacity) { - msg->status = MESSAGE_STATUS_OVERFLOW_ERROR; + odk_add_overflow_ux(substring_end, message_impl->size, &end) || + end > message_impl->capacity) { + message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR; return; } + assert(obj); obj->offset = offset; obj->length = length; } /* copy out */ -void UnpackArray(Message* message, uint8_t* address, size_t size) { - if (!ValidMessage(message)) return; +void UnpackArray(ODK_Message* message, uint8_t* address, size_t size) { UnpackBytes(message, address, size); } - -/* - * The message structure, which is separate from the buffer, - * is initialized to reference the buffer - */ -void InitMessage(Message* message, uint8_t* buffer, size_t capacity) { - if (message == NULL) return; - memset(message, 0, sizeof(Message)); - message->base = buffer; - message->capacity = capacity; - message->size = 0; - message->read_offset = 0; - message->status = MESSAGE_STATUS_OK; -} - -/* - * Set the message to an empty state - */ -void ResetMessage(Message* message) { - message->size = 0; - message->read_offset = 0; - message->status = MESSAGE_STATUS_OK; -} - -uint8_t* GetBase(Message* message) { - if (message == NULL) return NULL; - return message->base; -} - -size_t GetCapacity(Message* message) { - if (message == NULL) return 0; - return message->capacity; -} - -size_t GetSize(Message* message) { - if (message == NULL) return 0; - return message->size; -} - -void SetSize(Message* message, size_t size) { - if (message == NULL) return; - if (size > message->capacity) - message->status = MESSAGE_STATUS_OVERFLOW_ERROR; - else - message->size = size; -} - -MessageStatus GetStatus(Message* message) { return message->status; } - -void SetStatus(Message* message, MessageStatus status) { - message->status = status; -} - -size_t GetOffset(Message* message) { - if (message == NULL) return 0; - return message->read_offset; -} - -size_t SizeOfMessageStruct() { return sizeof(Message); } diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h index 9e7776bf..36276449 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h @@ -13,74 +13,24 @@ extern "C" { #include #include "OEMCryptoCENCCommon.h" +#include "odk_message.h" -#define SIZE_OF_MESSAGE_STRUCT 64 +void Pack_enum(ODK_Message* message, int value); +void Pack_bool(ODK_Message* message, const bool* value); +void Pack_uint16_t(ODK_Message* message, const uint16_t* value); +void Pack_uint32_t(ODK_Message* message, const uint32_t* value); +void Pack_uint64_t(ODK_Message* message, const uint64_t* value); +void PackArray(ODK_Message* message, const uint8_t* base, size_t size); +void Pack_OEMCrypto_Substring(ODK_Message* msg, const OEMCrypto_Substring* obj); -/* - * Description: - * Point |msg| to stack-array |blk|. - * |blk| is guaranteed large enough to hold a |Message| struct. - * |blk| cannot be used in the same scope as a variable name. - * |msg| points to valid memory in the same scope |AllocateMessage| is used. - * Parameters: - * msg: pointer to pointer to |Message| struct - * blk: variable name for stack-array - */ -#define AllocateMessage(msg, blk) \ - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \ - *(msg) = (Message*)(blk) - -typedef struct _Message Message; - -typedef enum { - MESSAGE_STATUS_OK, - MESSAGE_STATUS_UNKNOWN_ERROR, - MESSAGE_STATUS_OVERFLOW_ERROR, - MESSAGE_STATUS_UNDERFLOW_ERROR, - MESSAGE_STATUS_PARSE_ERROR, - MESSAGE_STATUS_NULL_POINTER_ERROR, - MESSAGE_STATUS_API_VALUE_ERROR -} MessageStatus; - -bool ValidMessage(Message* message); - -void Pack_enum(Message* message, int value); -void Pack_bool(Message* message, const bool* value); -void Pack_uint16_t(Message* message, const uint16_t* value); -void Pack_uint32_t(Message* message, const uint32_t* value); -void Pack_uint64_t(Message* message, const uint64_t* value); -void PackArray(Message* message, const uint8_t* base, size_t size); -void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj); - -int Unpack_enum(Message* message); -void Unpack_bool(Message* message, bool* value); -void Unpack_uint16_t(Message* message, uint16_t* value); -void Unpack_uint32_t(Message* message, uint32_t* value); -void Unpack_uint64_t(Message* message, uint64_t* value); -void UnpackArray(Message* message, uint8_t* address, +int Unpack_enum(ODK_Message* message); +void Unpack_bool(ODK_Message* message, bool* value); +void Unpack_uint16_t(ODK_Message* message, uint16_t* value); +void Unpack_uint32_t(ODK_Message* message, uint32_t* value); +void Unpack_uint64_t(ODK_Message* message, uint64_t* value); +void UnpackArray(ODK_Message* message, uint8_t* address, size_t size); /* copy out */ -void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj); - -/* - * Initialize a message structure to reference a separate buffer. The caller - * is responsible for ensuring that the buffer remains allocated for the - * lifetime of the message. - */ -void InitMessage(Message* message, uint8_t* buffer, size_t capacity); - -/* - * Reset an existing the message to an empty state - */ -void ResetMessage(Message* message); -uint8_t* GetBase(Message* message); -size_t GetCapacity(Message* message); -size_t GetSize(Message* message); -void SetSize(Message* message, size_t size); -MessageStatus GetStatus(Message* message); -void SetStatus(Message* message, MessageStatus status); -size_t GetOffset(Message* message); - -size_t SizeOfMessageStruct(); +void Unpack_OEMCrypto_Substring(ODK_Message* msg, OEMCrypto_Substring* obj); #ifdef __cplusplus } // extern "C" diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp index d87e4647..5ea04c15 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp @@ -99,11 +99,9 @@ OEMCryptoResult odk_deserialize_RenewalResponse( // odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method. // playback_time cannot be captured from publicly exposed API // ODK_ParseRenewal. - uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; - Message* msg = reinterpret_cast(blk); - InitMessage(msg, const_cast(buf), len); - SetSize(msg, len); - Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); + ODK_Message msg = ODK_Message_Create(const_cast(buf), len); + ODK_Message_SetSize(&msg, len); + Unpack_ODK_PreparedRenewalRequest(&msg, renewal_msg); return OEMCrypto_SUCCESS; } diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp index 2be4a91b..17787a41 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp @@ -6,15 +6,13 @@ #include #include "fuzzing/odk_fuzz_helper.h" -#include "odk_attributes.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 UNUSED) { + size_t max_size, unsigned int seed) { const size_t kProvisioningResponseArgsSize = sizeof(ODK_ParseProvisioning_Args); if (size < kProvisioningResponseArgsSize) { diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp index 25fa7b00..0073c4ee 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp @@ -6,15 +6,13 @@ #include #include "fuzzing/odk_fuzz_helper.h" -#include "odk_attributes.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 UNUSED) { + size_t max_size, unsigned int seed) { const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args); if (size < kRenewalResponseArgsSize) { return 0; diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index b6526df8..6428fd1e 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -4,8 +4,6 @@ #include "odk.h" -#include // TODO(b/147944591): use this one? Or odk_endian.h? - #include #include diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp index 167d9e59..9650c337 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp @@ -4,8 +4,6 @@ #include "odk_test_helper.h" -#include - #include #include #include @@ -15,6 +13,7 @@ #include "OEMCryptoCENCCommon.h" #include "gtest/gtest.h" +#include "odk_endian.h" #include "odk_structs.h" #include "odk_structs_priv.h" @@ -226,24 +225,27 @@ OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) { } switch (field->type) { case ODK_UINT16: { - const uint16_t u16 = htobe16(*static_cast(field->value)); + const uint16_t u16 = + oemcrypto_htobe16(*static_cast(field->value)); memcpy(buf, &u16, sizeof(u16)); break; } case ODK_UINT32: { - const uint32_t u32 = htobe32(*static_cast(field->value)); + const uint32_t u32 = + oemcrypto_htobe32(*static_cast(field->value)); memcpy(buf, &u32, sizeof(u32)); break; } case ODK_UINT64: { - const uint64_t u64 = htobe64(*static_cast(field->value)); + const uint64_t u64 = + oemcrypto_htobe64(*static_cast(field->value)); memcpy(buf, &u64, sizeof(u64)); break; } case ODK_SUBSTRING: { OEMCrypto_Substring* s = static_cast(field->value); - const uint32_t off = htobe32(s->offset); - const uint32_t len = htobe32(s->length); + const uint32_t off = oemcrypto_htobe32(s->offset); + const uint32_t len = oemcrypto_htobe32(s->length); memcpy(buf, &off, sizeof(off)); memcpy(buf + sizeof(off), &len, sizeof(len)); break; @@ -271,19 +273,19 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, case ODK_UINT16: { memcpy(field->value, buf, sizeof(uint16_t)); uint16_t* u16p = static_cast(field->value); - *u16p = be16toh(*u16p); + *u16p = oemcrypto_be16toh(*u16p); break; } case ODK_UINT32: { memcpy(field->value, buf, sizeof(uint32_t)); uint32_t* u32p = static_cast(field->value); - *u32p = be32toh(*u32p); + *u32p = oemcrypto_be32toh(*u32p); break; } case ODK_UINT64: { memcpy(field->value, buf, sizeof(uint64_t)); uint64_t* u64p = static_cast(field->value); - *u64p = be64toh(*u64p); + *u64p = oemcrypto_be64toh(*u64p); break; } case ODK_SUBSTRING: { @@ -292,8 +294,8 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, uint32_t len = 0; memcpy(&off, buf, sizeof(off)); memcpy(&len, buf + sizeof(off), sizeof(len)); - s->offset = be32toh(off); - s->length = be32toh(len); + s->offset = oemcrypto_be32toh(off); + s->length = oemcrypto_be32toh(len); break; } case ODK_DEVICEID: @@ -318,7 +320,7 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, case ODK_UINT16: { uint16_t val; memcpy(&val, buf, sizeof(uint16_t)); - val = be16toh(val); + val = oemcrypto_be16toh(val); std::cerr << field->name << ": " << val << " = 0x" << std::hex << val << "\n"; break; @@ -326,7 +328,7 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, case ODK_UINT32: { uint32_t val; memcpy(&val, buf, sizeof(uint32_t)); - val = be32toh(val); + val = oemcrypto_be32toh(val); std::cerr << field->name << ": " << val << " = 0x" << std::hex << val << "\n"; break; @@ -334,7 +336,7 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, case ODK_UINT64: { uint64_t val; memcpy(&val, buf, sizeof(uint64_t)); - val = be64toh(val); + val = oemcrypto_be64toh(val); std::cerr << field->name << ": " << val << " = 0x" << std::hex << val << "\n"; break;