odk: core serialization structs & functions
odk directory copied from wvgerrit. branch oemcrypto-v16 commit 0c9a7dc Bug: 140758896 Test: odk_test Change-Id: I0c631f771b794468a63e4395f6b9c3b60a1dfd4f
This commit is contained in:
316
libwvdrmengine/oemcrypto/odk/src/odk.c
Normal file
316
libwvdrmengine/oemcrypto/odk/src/odk.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk.h"
|
||||
#include "odk_overflow.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
#define ODK_LICENSE_REQUEST_SIZE 20
|
||||
#define ODK_RENEWAL_REQUEST_SIZE 28
|
||||
#define ODK_PROVISIONING_REQUEST_SIZE 88
|
||||
|
||||
/* @ private odk functions */
|
||||
|
||||
static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
|
||||
size_t* core_message_length,
|
||||
uint32_t message_type,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
ODK_CoreMessage* core_message) {
|
||||
if (!nonce_values || !core_message_length || !core_message ||
|
||||
*core_message_length > buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
Message* msg = NULL;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, buffer, *core_message_length);
|
||||
*core_message = (ODK_CoreMessage){
|
||||
message_type, 0, *nonce_values,
|
||||
};
|
||||
|
||||
switch (message_type) {
|
||||
case ODK_License_Request_Type: {
|
||||
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
||||
Pack_ODK_PreparedLicense(msg, (ODK_PreparedLicense*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Request_Type: {
|
||||
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
||||
Pack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Request_Type: {
|
||||
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||
Pack_ODK_ProvisioningMessage(msg, (ODK_ProvisioningMessage*)core_message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
*core_message_length = core_message->message_length;
|
||||
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
||||
GetSize(msg) != *core_message_length) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
||||
size_t message_length,
|
||||
uint32_t message_type,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
ODK_CoreMessage* const core_message) {
|
||||
Message* msg = NULL;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, (uint8_t*)buf, message_length);
|
||||
SetSize(msg, message_length);
|
||||
|
||||
switch (message_type) {
|
||||
case ODK_License_Response_Type: {
|
||||
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Response_Type: {
|
||||
Unpack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Response_Type: {
|
||||
Unpack_ODK_ProvisioningResponse(msg,
|
||||
(ODK_ProvisioningResponse*)core_message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
||||
message_type != core_message->message_type ||
|
||||
GetOffset(msg) != core_message->message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
if (nonce_values) {
|
||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||
if (nonce_values->api_version != core_message->nonce_values.api_version ||
|
||||
nonce_values->nonce != core_message->nonce_values.nonce ||
|
||||
nonce_values->session_id != core_message->nonce_values.session_id) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* @ public odk functions */
|
||||
|
||||
/* @@ prepare request functions */
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
ODK_PreparedLicense license_request = {0};
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_License_Request_Type, nonce_values,
|
||||
&license_request.core_message);
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
const ODK_ClockValues* clock_values, uint64_t system_time_seconds) {
|
||||
ODK_RenewalMessage renewal_request = {
|
||||
{0},
|
||||
};
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_first_decrypt,
|
||||
&renewal_request.playback_time)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_Renewal_Request_Type, nonce_values,
|
||||
&renewal_request.core_message);
|
||||
}
|
||||
|
||||
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) {
|
||||
ODK_ProvisioningMessage provisioning_request = {
|
||||
{0},
|
||||
};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.device_id_length = device_id_length;
|
||||
if (device_id) {
|
||||
memcpy(provisioning_request.device_id, device_id, device_id_length);
|
||||
}
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_Provisioning_Request_Type, nonce_values,
|
||||
&provisioning_request.core_message);
|
||||
}
|
||||
|
||||
/* @@ parse request functions */
|
||||
|
||||
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) {
|
||||
|
||||
if (!nonce_values || !parsed_license) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_LicenseResponse license_response = {{0}, parsed_license};
|
||||
OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, ODK_License_Response_Type, NULL,
|
||||
&license_response.core_message);
|
||||
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (license_response.core_message.nonce_values.api_version != 16) {
|
||||
return ODK_UNSUPPORTED_API;
|
||||
}
|
||||
|
||||
if (parsed_license->nonce_required) {
|
||||
if (initial_license_load) {
|
||||
if (nonce_values->nonce != license_response.core_message.nonce_values.nonce ||
|
||||
nonce_values->session_id != license_response.core_message.nonce_values.session_id) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
} else { /* !initial_license_load */
|
||||
nonce_values->nonce = license_response.core_message.nonce_values.nonce;
|
||||
nonce_values->session_id = license_response.core_message.nonce_values.session_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (initial_license_load &&
|
||||
memcmp(request_hash, parsed_license->request_hash, ODK_SHA256_HASH_SIZE)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
if (usage_entry_present && parsed_license->pst.length == 0) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!nonce_values || !timer_limits || !clock_values || !timer_value) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_RenewalMessage renewal_response = {
|
||||
{0},
|
||||
};
|
||||
OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, ODK_Renewal_Response_Type, nonce_values,
|
||||
&renewal_response.core_message);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Reference:
|
||||
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
|
||||
* Section: Renewal Message
|
||||
*/
|
||||
|
||||
uint64_t playback_timer = 0;
|
||||
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires, system_time,
|
||||
&playback_timer)) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
|
||||
uint64_t time_since_playback_began = 0;
|
||||
uint64_t time_since_reset = 0;
|
||||
uint64_t time_since_message_signed = 0;
|
||||
/* ... or use clock_values->time_of_license_signed ? */
|
||||
if (odk_sub_overflow_u64(system_time, clock_values->time_of_first_decrypt,
|
||||
&time_since_playback_began) ||
|
||||
odk_sub_overflow_u64(timer_limits->renewal_playback_duration_seconds,
|
||||
playback_timer, &time_since_reset) ||
|
||||
odk_sub_overflow_u64(time_since_playback_began,
|
||||
renewal_response.playback_time,
|
||||
&time_since_message_signed) ||
|
||||
time_since_message_signed >= time_since_reset ||
|
||||
odk_add_overflow_u64(system_time,
|
||||
timer_limits->renewal_playback_duration_seconds,
|
||||
&clock_values->time_when_timer_expires)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
/* todo: when to return ODK_DISABLE_TIMER */
|
||||
*timer_value = timer_limits->renewal_playback_duration_seconds;
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!nonce_values || !device_id || !parsed_response) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_ProvisioningResponse provisioning_response = {{
|
||||
{0},
|
||||
},
|
||||
parsed_response};
|
||||
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, ODK_Provisioning_Response_Type,
|
||||
nonce_values, &provisioning_response.core_provisioning.core_message);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (memcmp(device_id, provisioning_response.core_provisioning.device_id,
|
||||
device_id_length)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
/* check bytes beyond device_id_length are 0 */
|
||||
if (memcmp(
|
||||
zero,
|
||||
provisioning_response.core_provisioning.device_id + device_id_length,
|
||||
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
38
libwvdrmengine/oemcrypto/odk/src/odk_overflow.c
Normal file
38
libwvdrmengine/oemcrypto/odk/src/odk_overflow.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||
if (UINT64_MAX - a >= b) {
|
||||
if (c) {
|
||||
*c = a + b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int odk_add_overflow_ux(size_t a, size_t b, size_t* c) {
|
||||
if (SIZE_MAX - a >= b) {
|
||||
if (c) {
|
||||
*c = a + b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
209
libwvdrmengine/oemcrypto/odk/src/odk_serialize.c
Normal file
209
libwvdrmengine/oemcrypto/odk/src/odk_serialize.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is auto-generated, do not edit
|
||||
*/
|
||||
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
/* @ serialize */
|
||||
|
||||
/* @@ private serialize */
|
||||
|
||||
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
|
||||
Pack_uint32_t(msg, &obj->api_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) {
|
||||
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,
|
||||
OEMCrypto_KeyObject const* obj) {
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id);
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data_iv);
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data);
|
||||
Pack_OEMCrypto_Substring(msg,
|
||||
(const OEMCrypto_Substring*)&obj->key_control_iv);
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_control);
|
||||
}
|
||||
|
||||
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->soft_expiry);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->earliest_playback_start_seconds);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->latest_playback_start_seconds);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->initial_playback_duration_seconds);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->renewal_playback_duration_seconds);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->license_duration_seconds);
|
||||
}
|
||||
|
||||
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
|
||||
/* hand-coded */
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
Pack_OEMCrypto_Substring(msg,
|
||||
(const OEMCrypto_Substring*)&obj->enc_mac_keys_iv);
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->enc_mac_keys);
|
||||
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->pst);
|
||||
Pack_OEMCrypto_Substring(
|
||||
msg, (const OEMCrypto_Substring*)&obj->srm_restriction_data);
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->license_type);
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->nonce_required);
|
||||
Pack_ODK_TimerLimits(msg, (const ODK_TimerLimits*)&obj->timer_limits);
|
||||
PackArray(msg, (const uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash));
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->key_array_length);
|
||||
for (size_t i = 0; i < (size_t)obj->key_array_length; i++) {
|
||||
Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Pack_ODK_ParsedProvisioning(Message* msg,
|
||||
ODK_ParsedProvisioning const* obj) {
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->key_type);
|
||||
Pack_OEMCrypto_Substring(msg,
|
||||
(const OEMCrypto_Substring*)&obj->enc_private_key);
|
||||
Pack_OEMCrypto_Substring(
|
||||
msg, (const OEMCrypto_Substring*)&obj->enc_private_key_iv);
|
||||
Pack_OEMCrypto_Substring(
|
||||
msg, (const OEMCrypto_Substring*)&obj->encrypted_message_key);
|
||||
}
|
||||
|
||||
/* @@ odk serialize */
|
||||
|
||||
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
|
||||
}
|
||||
|
||||
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
|
||||
Pack_uint64_t(msg, (const uint64_t*)&obj->playback_time);
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningMessage(Message* msg,
|
||||
ODK_ProvisioningMessage const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
|
||||
Pack_uint32_t(msg, (const uint32_t*)&obj->device_id_length);
|
||||
PackArray(msg, (const uint8_t*)&obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
/* @@ kdo serialize */
|
||||
|
||||
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
|
||||
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse const* obj) {
|
||||
Pack_ODK_ProvisioningMessage(
|
||||
msg, (const ODK_ProvisioningMessage*)&obj->core_provisioning);
|
||||
Pack_ODK_ParsedProvisioning(
|
||||
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||
}
|
||||
|
||||
/* @ deserialize */
|
||||
|
||||
/* @@ private deserialize */
|
||||
|
||||
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
|
||||
Unpack_uint32_t(msg, &obj->api_version);
|
||||
Unpack_uint32_t(msg, &obj->nonce);
|
||||
Unpack_uint32_t(msg, &obj->session_id);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_CoreMessage(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) {
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_id);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->soft_expiry);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->earliest_playback_start_seconds);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->latest_playback_start_seconds);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->initial_playback_duration_seconds);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->renewal_playback_duration_seconds);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->license_duration_seconds);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->pst);
|
||||
Unpack_OEMCrypto_Substring(msg,
|
||||
(OEMCrypto_Substring*)&obj->srm_restriction_data);
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->license_type);
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->nonce_required);
|
||||
Unpack_ODK_TimerLimits(msg, (ODK_TimerLimits*)&obj->timer_limits);
|
||||
UnpackArray(msg, (uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash));
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->key_array_length);
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < obj->key_array_length; i++) {
|
||||
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Unpack_ODK_ParsedProvisioning(Message* msg,
|
||||
ODK_ParsedProvisioning* obj) {
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->key_type);
|
||||
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_private_key);
|
||||
Unpack_OEMCrypto_Substring(msg,
|
||||
(OEMCrypto_Substring*)&obj->enc_private_key_iv);
|
||||
Unpack_OEMCrypto_Substring(msg,
|
||||
(OEMCrypto_Substring*)&obj->encrypted_message_key);
|
||||
}
|
||||
|
||||
/* @ kdo deserialize */
|
||||
|
||||
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
|
||||
}
|
||||
|
||||
void Unpack_ODK_ProvisioningMessage(Message* msg,
|
||||
ODK_ProvisioningMessage* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
|
||||
Unpack_uint32_t(msg, (uint32_t*)&obj->device_id_length);
|
||||
UnpackArray(msg, (uint8_t*)&obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
/* @@ odk deserialize */
|
||||
|
||||
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
|
||||
Unpack_ODK_ParsedLicense(msg, (ODK_ParsedLicense*)obj->parsed_license);
|
||||
}
|
||||
|
||||
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
|
||||
Unpack_uint64_t(msg, (uint64_t*)&obj->playback_time);
|
||||
}
|
||||
|
||||
void Unpack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse* obj) {
|
||||
Unpack_ODK_ProvisioningMessage(
|
||||
msg, (ODK_ProvisioningMessage*)&obj->core_provisioning);
|
||||
Unpack_ODK_ParsedProvisioning(
|
||||
msg, (ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||
}
|
||||
229
libwvdrmengine/oemcrypto/odk/src/odk_timer.c
Normal file
229
libwvdrmengine/oemcrypto/odk/src/odk_timer.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
ODK_NonceValues* nonce_values,
|
||||
uint32_t api_version,
|
||||
uint32_t session_id) {
|
||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
timer_limits->soft_expiry = false;
|
||||
timer_limits->earliest_playback_start_seconds = 0;
|
||||
timer_limits->latest_playback_start_seconds = 0;
|
||||
timer_limits->initial_playback_duration_seconds = 0;
|
||||
timer_limits->renewal_playback_duration_seconds = 0;
|
||||
timer_limits->license_duration_seconds = 0;
|
||||
|
||||
clock_values->time_of_license_signed = 0;
|
||||
clock_values->time_of_first_decrypt = 0;
|
||||
clock_values->time_of_last_decrypt = 0;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = 0;
|
||||
clock_values->status = kUnused;
|
||||
|
||||
nonce_values->api_version = api_version;
|
||||
nonce_values->nonce = 0;
|
||||
nonce_values->session_id = session_id;
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
||||
uint32_t nonce) {
|
||||
nonce_values->nonce = nonce;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds) {
|
||||
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
clock_values->time_of_license_signed = system_time_seconds;
|
||||
clock_values->time_of_first_decrypt = 0;
|
||||
clock_values->time_of_last_decrypt = 0;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
/* TODO(b/142415188): document this. */
|
||||
clock_values->timer_status = 0;
|
||||
clock_values->status = kUnused;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||
uint64_t time_of_license_signed,
|
||||
uint64_t time_of_first_decrypt,
|
||||
uint64_t time_of_last_decrypt,
|
||||
enum OEMCrypto_Usage_Entry_Status status,
|
||||
uint64_t system_time_seconds) {
|
||||
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
clock_values->time_of_license_signed = time_of_license_signed;
|
||||
clock_values->time_of_first_decrypt = time_of_first_decrypt;
|
||||
clock_values->time_of_last_decrypt = time_of_last_decrypt;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = 0;
|
||||
clock_values->status = status;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called on the first playback for a session. */
|
||||
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
if (clock_values == NULL || timer_limits == NULL)
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
/* All times are relative to when the license was signed. */
|
||||
const uint64_t rental_time =
|
||||
system_time_seconds - clock_values->time_of_license_signed;
|
||||
if (rental_time < timer_limits->earliest_playback_start_seconds) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If the clock status is already marked as inactive, then playback is
|
||||
* not allowed. */
|
||||
/* TODO(b/142415188): add helper function. */
|
||||
if (clock_values->status > kActive) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If this license is still inactive (never used) then we just look at the
|
||||
* rental window. This is the first playback for the license, not just this
|
||||
* session. */
|
||||
if (clock_values->status == kUnused) {
|
||||
/* If the rental clock has expired, the license has expired. */
|
||||
if (rental_time > timer_limits->latest_playback_start_seconds) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* The timer should be limited by the playback duration. */
|
||||
uint64_t time_left = timer_limits->initial_playback_duration_seconds;
|
||||
/* If there is a license duration, it also limits the timer. Remeber, a
|
||||
* limit of 0 means no limit, or infinite. */
|
||||
if (timer_limits->license_duration_seconds > 0) {
|
||||
if (timer_limits->license_duration_seconds < rental_time) {
|
||||
/* If the license duration has expired. This is unusual, because this
|
||||
* can only happen if the license duration is less than the rental
|
||||
* window. */
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (timer_limits->license_duration_seconds - rental_time < time_left ||
|
||||
time_left == 0) {
|
||||
time_left = timer_limits->license_duration_seconds - rental_time;
|
||||
}
|
||||
}
|
||||
/* This is a new license, and we can start playback. */
|
||||
clock_values->status = kActive;
|
||||
clock_values->time_of_first_decrypt = system_time_seconds;
|
||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_DISABLE_TIMER;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* Set timer to limit playback. */
|
||||
if (timer_value) *timer_value = time_left;
|
||||
clock_values->time_when_timer_expires = system_time_seconds + time_left;
|
||||
clock_values->timer_status = ODK_SET_TIMER;
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
/* Otherwise, this is the second loading of a persistent license. In this
|
||||
* case, we ignore the rental window. */
|
||||
const uint64_t time_since_first_decrypt =
|
||||
system_time_seconds - clock_values->time_of_first_decrypt;
|
||||
uint64_t time_left = 0;
|
||||
/* If there is an initial playback duration, the we use that as a limit.
|
||||
* This ignores any license renewals. If renewals are allowed, then the last
|
||||
* one can be reloaded to reset the timer. */
|
||||
if (timer_limits->initial_playback_duration_seconds > 0) {
|
||||
if (timer_limits->initial_playback_duration_seconds <=
|
||||
time_since_first_decrypt) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
time_left = timer_limits->initial_playback_duration_seconds -
|
||||
time_since_first_decrypt;
|
||||
}
|
||||
/* If there is a license duration, it also limits the timer. */
|
||||
if (timer_limits->license_duration_seconds > 0) {
|
||||
if (timer_limits->license_duration_seconds < rental_time) {
|
||||
/* The license duration has expired. */
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (timer_limits->license_duration_seconds - rental_time < time_left ||
|
||||
time_left == 0) {
|
||||
time_left = timer_limits->license_duration_seconds - rental_time;
|
||||
}
|
||||
}
|
||||
/* We can restart playback for this license. Update last playback time. */
|
||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_DISABLE_TIMER;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* Set timer. */
|
||||
if (timer_value) *timer_value = time_left;
|
||||
clock_values->time_when_timer_expires = system_time_seconds + time_left;
|
||||
clock_values->timer_status = ODK_SET_TIMER;
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
/* This is called regularly during playback if OEMCrypto does not implement its
|
||||
* own timer. */
|
||||
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values) {
|
||||
if (clock_values == NULL || timer_limits == NULL)
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (clock_values->timer_status == ODK_TIMER_EXPIRED) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (clock_values->time_when_timer_expires > 0 &&
|
||||
system_time_seconds > clock_values->time_when_timer_expires) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
||||
if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (clock_values->status == kUnused) {
|
||||
clock_values->status = kInactiveUnused;
|
||||
} else {
|
||||
clock_values->status = kInactiveUsed;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
ODK_NonceValues* nonce_values,
|
||||
uint32_t key_duration,
|
||||
uint64_t system_time_seconds) {
|
||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
timer_limits->soft_expiry = false;
|
||||
timer_limits->earliest_playback_start_seconds = 0;
|
||||
timer_limits->latest_playback_start_seconds = 0;
|
||||
timer_limits->initial_playback_duration_seconds = key_duration;
|
||||
timer_limits->renewal_playback_duration_seconds = key_duration;
|
||||
timer_limits->license_duration_seconds = 0;
|
||||
nonce_values->api_version = 15;
|
||||
if (key_duration > 0) {
|
||||
clock_values->time_when_timer_expires = system_time_seconds + key_duration;
|
||||
} else {
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
205
libwvdrmengine/oemcrypto/odk/src/serialization_base.c
Normal file
205
libwvdrmengine/oemcrypto/odk/src/serialization_base.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 "serialization_base.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_assert.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;
|
||||
};
|
||||
|
||||
odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message),
|
||||
"SIZE_OF_MESSAGE_STRUCT too small");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
void Pack_uint32_t(Message* message, const uint32_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
data[0] = *value >> 24;
|
||||
data[1] = *value >> 16;
|
||||
data[2] = *value >> 8;
|
||||
data[3] = *value >> 0;
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint64_t(Message* message, const uint64_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
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;
|
||||
PackBytes(message, base, size);
|
||||
}
|
||||
|
||||
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) {
|
||||
uint32_t offset = obj->offset;
|
||||
uint32_t length = obj->length;
|
||||
Pack_uint32_t(msg, &offset);
|
||||
Pack_uint32_t(msg, &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;
|
||||
} else {
|
||||
message->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
void Unpack_uint32_t(Message* message, uint32_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
*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;
|
||||
uint32_t hi = 0;
|
||||
uint32_t lo = 0;
|
||||
Unpack_uint32_t(message, &hi);
|
||||
Unpack_uint32_t(message, &lo);
|
||||
*value = hi;
|
||||
*value = *value << 32 | lo;
|
||||
}
|
||||
|
||||
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) {
|
||||
uint32_t offset = 0, length = 0;
|
||||
Unpack_uint32_t(msg, &offset);
|
||||
Unpack_uint32_t(msg, &length);
|
||||
if (!ValidMessage(msg)) return;
|
||||
size_t end = 0;
|
||||
if (offset > msg->capacity || odk_add_overflow_ux(offset, length, &end) ||
|
||||
end > msg->capacity) {
|
||||
msg->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
return;
|
||||
}
|
||||
obj->offset = offset;
|
||||
obj->length = length;
|
||||
}
|
||||
|
||||
/* copy out */
|
||||
void UnpackArray(Message* message, uint8_t* address, size_t size) {
|
||||
if (!ValidMessage(message)) return;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* The message structure is in the first sizeof(Memory) bytes
|
||||
* of the buffer
|
||||
*/
|
||||
Message* CreateMessage(uint8_t* buffer, size_t buffer_size) {
|
||||
if (buffer == NULL || buffer_size < sizeof(Message)) return NULL;
|
||||
Message* message = (Message*)buffer;
|
||||
message->base = buffer + sizeof(Message);
|
||||
message->capacity = buffer_size - sizeof(Message);
|
||||
message->size = 0;
|
||||
message->read_offset = 0;
|
||||
message->status = MESSAGE_STATUS_OK;
|
||||
return message;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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); }
|
||||
Reference in New Issue
Block a user