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;
|
||||
}
|
||||
Reference in New Issue
Block a user