Convert ODK_LAST_STRESSABLE_TYPE to an aliased enum value so that ODK_FieldLength always returns a valid value instead of SIZE_MAX. PiperOrigin-RevId: 602823670 Change-Id: I7a843cacca8201677c0f31249112c04f6c3e04cb
792 lines
29 KiB
C++
792 lines
29 KiB
C++
// Copyright 2019 Google LLC. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine
|
|
// License Agreement.
|
|
|
|
#include "odk_test_helper.h"
|
|
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <ios>
|
|
#include <iostream>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "OEMCryptoCENCCommon.h"
|
|
#include "gtest/gtest.h"
|
|
#include "odk_endian.h"
|
|
#include "odk_structs.h"
|
|
#include "odk_structs_priv.h"
|
|
|
|
namespace wvodk_test {
|
|
|
|
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
|
|
ODK_MessageType message_type) {
|
|
ASSERT_TRUE(core_message != nullptr);
|
|
core_message->message_type = message_type;
|
|
core_message->message_length = 0;
|
|
core_message->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
|
core_message->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
|
core_message->nonce_values.nonce = 0xdeadbeef;
|
|
core_message->nonce_values.session_id = 0xcafebabe;
|
|
}
|
|
|
|
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params,
|
|
uint32_t odk_major_version) {
|
|
ODK_SetDefaultCoreFields(&(params->core_message), ODK_License_Response_Type);
|
|
params->initial_license_load = true;
|
|
params->usage_entry_present = true;
|
|
params->parsed_license = {
|
|
.enc_mac_keys_iv = {.offset = 0, .length = 1},
|
|
.enc_mac_keys = {.offset = 2, .length = 3},
|
|
.pst = {.offset = 4, .length = 5},
|
|
.srm_restriction_data = {.offset = 6, .length = 7},
|
|
.license_type = OEMCrypto_EntitlementLicense,
|
|
.nonce_required = true,
|
|
.timer_limits =
|
|
{
|
|
.soft_enforce_rental_duration = true,
|
|
.soft_enforce_playback_duration = false,
|
|
.earliest_playback_start_seconds = 10,
|
|
.rental_duration_seconds = 11,
|
|
.total_playback_duration_seconds = 12,
|
|
.initial_renewal_duration_seconds = 13,
|
|
},
|
|
.watermarking = 0,
|
|
.dtcp2_required = {.dtcp2_required = 0,
|
|
.cmi_descriptor_0 =
|
|
{
|
|
.id = 0,
|
|
.extension = 0,
|
|
.length = 1,
|
|
.data = 0,
|
|
},
|
|
.cmi_descriptor_1 =
|
|
{
|
|
.id = 1,
|
|
.extension = 0,
|
|
.length = 3,
|
|
.data = {0, 0, 0},
|
|
},
|
|
.cmi_descriptor_2 =
|
|
{
|
|
.id = 2,
|
|
.extension = 0,
|
|
.length = 3,
|
|
.data = {0, 0, 0},
|
|
}},
|
|
.renewal_delay_base = OEMCrypto_License_Start,
|
|
.key_array_length = 3,
|
|
.key_array =
|
|
{
|
|
{
|
|
.key_id = {.offset = 15, .length = 16},
|
|
.key_data_iv = {.offset = 17, .length = 18},
|
|
.key_data = {.offset = 19, .length = 20},
|
|
.key_control_iv = {.offset = 21, .length = 22},
|
|
.key_control = {.offset = 23, .length = 24},
|
|
},
|
|
{
|
|
.key_id = {.offset = 25, .length = 26},
|
|
.key_data_iv = {.offset = 27, .length = 28},
|
|
.key_data = {.offset = 29, .length = 30},
|
|
.key_control_iv = {.offset = 31, .length = 32},
|
|
.key_control = {.offset = 33, .length = 34},
|
|
},
|
|
{
|
|
.key_id = {.offset = 35, .length = 36},
|
|
.key_data_iv = {.offset = 37, .length = 38},
|
|
.key_data = {.offset = 39, .length = 40},
|
|
.key_control_iv = {.offset = 41, .length = 42},
|
|
.key_control = {.offset = 43, .length = 44},
|
|
},
|
|
},
|
|
};
|
|
memset(params->request_hash, 0xaa, sizeof(params->request_hash));
|
|
params->extra_fields = {
|
|
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys_iv),
|
|
".enc_mac_keys_iv"},
|
|
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys), ".enc_mac_keys"},
|
|
{ODK_SUBSTRING, &(params->parsed_license.pst), ".pst"},
|
|
{ODK_SUBSTRING, &(params->parsed_license.srm_restriction_data),
|
|
".srm_restriction_data"},
|
|
{ODK_UINT32, &(params->parsed_license.license_type), ".license_type"},
|
|
{ODK_UINT32, &(params->parsed_license.nonce_required), ".nonce_required"},
|
|
{ODK_BOOL,
|
|
&(params->parsed_license.timer_limits.soft_enforce_rental_duration),
|
|
".soft_enforce_rental_duration"},
|
|
{ODK_BOOL,
|
|
&(params->parsed_license.timer_limits.soft_enforce_playback_duration),
|
|
".soft_enforce_playback_duration"},
|
|
{ODK_UINT64,
|
|
&(params->parsed_license.timer_limits.earliest_playback_start_seconds),
|
|
".earliest_playback_start_seconds"},
|
|
{ODK_UINT64,
|
|
&(params->parsed_license.timer_limits.rental_duration_seconds),
|
|
".rental_duration_seconds"},
|
|
{ODK_UINT64,
|
|
&(params->parsed_license.timer_limits.total_playback_duration_seconds),
|
|
".total_playback_duration_seconds"},
|
|
{ODK_UINT64,
|
|
&(params->parsed_license.timer_limits.initial_renewal_duration_seconds),
|
|
".initial_renewal_duration_seconds"},
|
|
};
|
|
if (odk_major_version >= 17) {
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT32, &(params->parsed_license.watermarking), ".watermarking"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8, &(params->parsed_license.dtcp2_required.dtcp2_required),
|
|
".dtcp2_required"});
|
|
if (params->parsed_license.dtcp2_required.dtcp2_required) {
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_0.id),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_0.extension),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT16,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_0.length),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_0.data),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.id),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.extension),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT16,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.length),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.data[0]),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.data[1]),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_1.data[2]),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.id),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.extension),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT16,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.length),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.data[0]),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.data[1]),
|
|
".cmi_descriptor_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT8,
|
|
&(params->parsed_license.dtcp2_required.cmi_descriptor_2.data[2]),
|
|
".cmi_descriptor_data"});
|
|
}
|
|
}
|
|
if (odk_major_version >= 18) {
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT32, &(params->parsed_license.renewal_delay_base),
|
|
".renewal_delay_base"});
|
|
}
|
|
params->extra_fields.push_back({ODK_UINT32,
|
|
&(params->parsed_license.key_array_length),
|
|
".key_array_length"});
|
|
params->extra_fields.push_back({ODK_SUBSTRING,
|
|
&(params->parsed_license.key_array[0].key_id),
|
|
".key_id"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data_iv),
|
|
".key_data_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data),
|
|
".key_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control_iv),
|
|
".key_control_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control),
|
|
".key_control"});
|
|
params->extra_fields.push_back({ODK_SUBSTRING,
|
|
&(params->parsed_license.key_array[1].key_id),
|
|
".key_id"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data_iv),
|
|
".key_data_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data),
|
|
".key_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control_iv),
|
|
".key_control_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control),
|
|
".key_control"});
|
|
params->extra_fields.push_back({ODK_SUBSTRING,
|
|
&(params->parsed_license.key_array[2].key_id),
|
|
".key_id"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data_iv),
|
|
".key_data_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data),
|
|
".key_data"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control_iv),
|
|
".key_control_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control),
|
|
".key_control"});
|
|
if (odk_major_version == 16) {
|
|
params->extra_fields.push_back(
|
|
{ODK_HASH, params->request_hash, ".request_hash"});
|
|
}
|
|
}
|
|
|
|
void ODK_SetDefaultReleaseResponseParams(ODK_ReleaseResponseParams* params) {
|
|
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Release_Response_Type);
|
|
params->status = kActive;
|
|
params->clock_security_level = 0;
|
|
params->seconds_since_license_requested = 0;
|
|
params->seconds_since_first_decrypt = 0;
|
|
}
|
|
|
|
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params) {
|
|
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Renewal_Response_Type);
|
|
params->system_time = 0xfaceb00c;
|
|
params->playback_clock = 10;
|
|
params->playback_timer = 20;
|
|
params->renewal_duration = 300;
|
|
params->extra_fields = {
|
|
{ODK_UINT64, &(params->playback_clock), "playback_clock"},
|
|
{ODK_UINT64, &(params->renewal_duration), "renewal_duration"},
|
|
};
|
|
params->timer_limits = {
|
|
.soft_enforce_rental_duration = false,
|
|
.soft_enforce_playback_duration = false,
|
|
.earliest_playback_start_seconds = 0,
|
|
.rental_duration_seconds = 1000,
|
|
.total_playback_duration_seconds = 2000,
|
|
.initial_renewal_duration_seconds = 300,
|
|
};
|
|
params->clock_values = {
|
|
.time_of_license_request_signed =
|
|
params->system_time - params->playback_clock - 42,
|
|
.time_of_first_decrypt = params->system_time - params->playback_clock,
|
|
.time_of_last_decrypt = params->system_time - params->playback_clock,
|
|
.time_of_renewal_request = params->playback_clock,
|
|
.time_when_timer_expires = params->system_time + params->playback_timer,
|
|
.timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE,
|
|
.status = kActive,
|
|
};
|
|
}
|
|
|
|
void ODK_SetDefaultProvisioningResponseParams(
|
|
ODK_ProvisioningResponseParams* params, uint32_t odk_major_version) {
|
|
ODK_SetDefaultCoreFields(&(params->core_message),
|
|
ODK_Provisioning_Response_Type);
|
|
params->device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
|
memset(params->device_id, 0xff, params->device_id_length);
|
|
memset(params->device_id + params->device_id_length, 0,
|
|
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
|
|
params->parsed_provisioning = {
|
|
.key_type = OEMCrypto_RSA_Private_Key,
|
|
.enc_private_key = {.offset = 0, .length = 1},
|
|
.enc_private_key_iv = {.offset = 2, .length = 3},
|
|
.encrypted_message_key = {.offset = 4, .length = 5},
|
|
};
|
|
|
|
params->extra_fields = {};
|
|
// V17 uses device_id
|
|
if (odk_major_version <= 17) {
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT32, &(params->device_id_length), "device_id_length"});
|
|
params->extra_fields.push_back(
|
|
{ODK_DEVICEID, params->device_id, "device_id"});
|
|
}
|
|
|
|
params->extra_fields.push_back(
|
|
{ODK_UINT32, &(params->parsed_provisioning).key_type, "key_type"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key,
|
|
"enc_private_key"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key_iv,
|
|
"enc_private_key_iv"});
|
|
params->extra_fields.push_back(
|
|
{ODK_SUBSTRING, &(params->parsed_provisioning).encrypted_message_key,
|
|
"encrypted_message_key"});
|
|
}
|
|
|
|
void ODK_SetDefaultProvisioning40ResponseParams(
|
|
ODK_Provisioning40ResponseParams* params) {
|
|
ODK_SetDefaultCoreFields(&(params->core_message),
|
|
ODK_Provisioning_Response_Type);
|
|
params->extra_fields = {};
|
|
}
|
|
|
|
size_t ODK_FieldLength(ODK_FieldType type) {
|
|
switch (type) {
|
|
case ODK_UINT8:
|
|
return sizeof(uint8_t);
|
|
case ODK_UINT16:
|
|
return sizeof(uint16_t);
|
|
case ODK_UINT32:
|
|
return sizeof(uint32_t);
|
|
case ODK_UINT64:
|
|
return sizeof(uint64_t);
|
|
case ODK_INT64:
|
|
return sizeof(uint64_t);
|
|
case ODK_SUBSTRING:
|
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
|
case ODK_DEVICEID:
|
|
return ODK_DEVICE_ID_LEN_MAX;
|
|
case ODK_DEVICEINFO:
|
|
return ODK_DEVICE_INFO_LEN_MAX;
|
|
case ODK_MESSAGECOUNTER:
|
|
return ODK_MESSAGECOUNTERINFO_SIZE;
|
|
case ODK_RENEWALDATA:
|
|
return ODK_KEYBOX_RENEWAL_DATA_SIZE;
|
|
case ODK_HASH:
|
|
return ODK_SHA256_HASH_SIZE;
|
|
case ODK_BOOL: // Booleans are stored in the message as 32 bit ints.
|
|
return sizeof(uint32_t);
|
|
}
|
|
}
|
|
|
|
size_t ODK_AllocSize(ODK_FieldType type) {
|
|
if (type == ODK_SUBSTRING) {
|
|
return sizeof(OEMCrypto_Substring);
|
|
}
|
|
if (type == ODK_MESSAGECOUNTER) {
|
|
return sizeof(ODK_MessageCounterInfo);
|
|
}
|
|
return ODK_FieldLength(type);
|
|
}
|
|
|
|
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
|
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
switch (field->type) {
|
|
case ODK_UINT8: {
|
|
memcpy(buf, field->value, sizeof(uint8_t));
|
|
break;
|
|
}
|
|
case ODK_UINT16: {
|
|
const uint16_t u16 =
|
|
oemcrypto_htobe16(*static_cast<uint16_t*>(field->value));
|
|
memcpy(buf, &u16, sizeof(u16));
|
|
break;
|
|
}
|
|
case ODK_UINT32: {
|
|
const uint32_t u32 =
|
|
oemcrypto_htobe32(*static_cast<uint32_t*>(field->value));
|
|
memcpy(buf, &u32, sizeof(u32));
|
|
break;
|
|
}
|
|
case ODK_UINT64: {
|
|
const uint64_t u64 =
|
|
oemcrypto_htobe64(*static_cast<uint64_t*>(field->value));
|
|
memcpy(buf, &u64, sizeof(u64));
|
|
break;
|
|
}
|
|
case ODK_INT64: {
|
|
const int64_t i64 =
|
|
oemcrypto_htobe64(*static_cast<int64_t*>(field->value));
|
|
memcpy(buf, &i64, sizeof(i64));
|
|
break;
|
|
}
|
|
case ODK_BOOL: {
|
|
const bool value = *static_cast<bool*>(field->value);
|
|
const uint32_t u32 = oemcrypto_htobe32(value ? 1 : 0);
|
|
memcpy(buf, &u32, sizeof(u32));
|
|
break;
|
|
}
|
|
case ODK_SUBSTRING: {
|
|
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
|
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;
|
|
}
|
|
case ODK_DEVICEID:
|
|
case ODK_DEVICEINFO:
|
|
case ODK_RENEWALDATA:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
const uint8_t* const id = static_cast<uint8_t*>(field->value);
|
|
memcpy(buf, id, field_len);
|
|
|
|
break;
|
|
}
|
|
case ODK_MESSAGECOUNTER: {
|
|
// Size required in field->value, which may get padding from the compiler.
|
|
const size_t src_len = ODK_AllocSize(field->type);
|
|
// Size taken up in serialized message buffer, which is tightly packed.
|
|
const size_t dest_len = ODK_FieldLength(field->type);
|
|
const uint8_t* const write_src = static_cast<uint8_t*>(field->value);
|
|
|
|
// Copy data from field to buf, fixing endian-ness
|
|
ODK_MessageCounterInfo info;
|
|
memcpy(&info, write_src, src_len);
|
|
info.master_generation_number =
|
|
oemcrypto_htobe64(info.master_generation_number);
|
|
info.provisioning_count = oemcrypto_htobe32(info.provisioning_count);
|
|
info.license_count = oemcrypto_htobe32(info.license_count);
|
|
info.decrypt_count = oemcrypto_htobe32(info.decrypt_count);
|
|
info.major_version = oemcrypto_htobe16(info.major_version);
|
|
info.minor_version = oemcrypto_htobe16(info.minor_version);
|
|
info.patch_version = oemcrypto_htobe16(info.patch_version);
|
|
memcpy(buf, &info, dest_len);
|
|
break;
|
|
}
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
|
|
const ODK_Field* field) {
|
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
switch (field->type) {
|
|
case ODK_UINT8: {
|
|
memcpy(field->value, buf, sizeof(uint8_t));
|
|
break;
|
|
}
|
|
case ODK_UINT16: {
|
|
memcpy(field->value, buf, sizeof(uint16_t));
|
|
uint16_t* u16p = static_cast<uint16_t*>(field->value);
|
|
*u16p = oemcrypto_be16toh(*u16p);
|
|
break;
|
|
}
|
|
case ODK_UINT32: {
|
|
memcpy(field->value, buf, sizeof(uint32_t));
|
|
uint32_t* u32p = static_cast<uint32_t*>(field->value);
|
|
*u32p = oemcrypto_be32toh(*u32p);
|
|
break;
|
|
}
|
|
case ODK_UINT64: {
|
|
memcpy(field->value, buf, sizeof(uint64_t));
|
|
uint64_t* u64p = static_cast<uint64_t*>(field->value);
|
|
*u64p = oemcrypto_be64toh(*u64p);
|
|
break;
|
|
}
|
|
case ODK_INT64: {
|
|
memcpy(field->value, buf, sizeof(int64_t));
|
|
int64_t* i64p = static_cast<int64_t*>(field->value);
|
|
*i64p = oemcrypto_be64toh(*i64p);
|
|
break;
|
|
}
|
|
case ODK_BOOL: {
|
|
uint32_t value;
|
|
memcpy(&value, buf, sizeof(uint32_t));
|
|
value = oemcrypto_be32toh(value);
|
|
*static_cast<bool*>(field->value) = (value != 0);
|
|
break;
|
|
}
|
|
case ODK_SUBSTRING: {
|
|
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
|
uint32_t off = 0;
|
|
uint32_t len = 0;
|
|
memcpy(&off, buf, sizeof(off));
|
|
memcpy(&len, buf + sizeof(off), sizeof(len));
|
|
s->offset = oemcrypto_be32toh(off);
|
|
s->length = oemcrypto_be32toh(len);
|
|
break;
|
|
}
|
|
case ODK_DEVICEID:
|
|
case ODK_DEVICEINFO:
|
|
case ODK_RENEWALDATA:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
uint8_t* const id = static_cast<uint8_t*>(field->value);
|
|
memcpy(id, buf, field_len);
|
|
break;
|
|
}
|
|
case ODK_MESSAGECOUNTER: {
|
|
// Size required in field->value, which may get padding from the compiler.
|
|
const size_t dest_len = ODK_AllocSize(field->type);
|
|
// Size taken up in serialized message buffer, which is tightly packed.
|
|
const size_t src_len = ODK_FieldLength(field->type);
|
|
uint8_t* const read_dest = static_cast<uint8_t*>(field->value);
|
|
|
|
// Copy data from buf to field, fixing endian-ness
|
|
uint8_t temp_buf[sizeof(ODK_MessageCounterInfo)] = {0};
|
|
memcpy(temp_buf, buf, src_len);
|
|
|
|
size_t index = 0;
|
|
ODK_MessageCounterInfo info;
|
|
uint64_t* u64 = reinterpret_cast<uint64_t*>(&temp_buf[index]);
|
|
info.master_generation_number = oemcrypto_be64toh(*u64);
|
|
index += sizeof(uint64_t);
|
|
|
|
uint32_t* u32 = reinterpret_cast<uint32_t*>(&temp_buf[index]);
|
|
info.provisioning_count = oemcrypto_be32toh(*u32);
|
|
index += sizeof(uint32_t);
|
|
|
|
u32 = reinterpret_cast<uint32_t*>(&temp_buf[index]);
|
|
info.license_count = oemcrypto_be32toh(*u32);
|
|
index += sizeof(uint32_t);
|
|
|
|
u32 = reinterpret_cast<uint32_t*>(&temp_buf[index]);
|
|
info.decrypt_count = oemcrypto_be32toh(*u32);
|
|
index += sizeof(uint32_t);
|
|
|
|
uint16_t* u16 = reinterpret_cast<uint16_t*>(&temp_buf[index]);
|
|
info.major_version = oemcrypto_be16toh(*u16);
|
|
index += sizeof(uint16_t);
|
|
|
|
u16 = reinterpret_cast<uint16_t*>(&temp_buf[index]);
|
|
info.minor_version = oemcrypto_be16toh(*u16);
|
|
index += sizeof(uint16_t);
|
|
|
|
u16 = reinterpret_cast<uint16_t*>(&temp_buf[index]);
|
|
info.patch_version = oemcrypto_be16toh(*u16);
|
|
index += sizeof(uint16_t);
|
|
|
|
memcpy(info.soc_vendor, &temp_buf[index], sizeof(info.soc_vendor));
|
|
index += sizeof(info.soc_vendor);
|
|
memcpy(info.chipset_model, &temp_buf[index], sizeof(info.chipset_model));
|
|
index += sizeof(info.chipset_model);
|
|
memcpy(info.extra, &temp_buf[index], sizeof(info.extra));
|
|
memcpy(read_dest, &info, dest_len);
|
|
break;
|
|
}
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
|
|
const ODK_Field* field) {
|
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
switch (field->type) {
|
|
case ODK_UINT8: {
|
|
uint8_t val;
|
|
memcpy(&val, buf, sizeof(uint8_t));
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_UINT16: {
|
|
uint16_t val;
|
|
memcpy(&val, buf, sizeof(uint16_t));
|
|
val = oemcrypto_be16toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_BOOL:
|
|
case ODK_UINT32: {
|
|
uint32_t val;
|
|
memcpy(&val, buf, sizeof(uint32_t));
|
|
val = oemcrypto_be32toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_UINT64: {
|
|
uint64_t val;
|
|
memcpy(&val, buf, sizeof(uint64_t));
|
|
val = oemcrypto_be64toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_INT64: {
|
|
int64_t val;
|
|
memcpy(&val, buf, sizeof(int64_t));
|
|
val = oemcrypto_be64toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_SUBSTRING: {
|
|
uint32_t off = 0;
|
|
uint32_t len = 0;
|
|
memcpy(&off, buf, sizeof(off));
|
|
memcpy(&len, buf + sizeof(off), sizeof(len));
|
|
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
|
|
break;
|
|
}
|
|
case ODK_DEVICEID:
|
|
case ODK_MESSAGECOUNTER:
|
|
case ODK_DEVICEINFO:
|
|
case ODK_RENEWALDATA:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
std::cerr << field->name << ": ";
|
|
for (size_t i = 0; i < field_len; i++) {
|
|
std::cerr << std::hex << std::setfill('0') << std::setw(2)
|
|
<< static_cast<unsigned int>(buf[i]);
|
|
}
|
|
std::cerr << "\n";
|
|
break;
|
|
}
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
std::cerr << std::dec; // Return to normal.
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Parameters:
|
|
* [in] size_in: buffer size
|
|
* [out] size_out: bytes processed
|
|
*/
|
|
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
|
|
const size_t size_in, size_t* size_out,
|
|
const std::vector<ODK_Field>& fields) {
|
|
if (buf == nullptr || size_out == nullptr) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
size_t off = 0, off2 = 0;
|
|
for (size_t i = 0; i < fields.size(); i++) {
|
|
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
|
|
off2 > size_in) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
|
|
if (__builtin_add_overflow(base, off, &base)) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
uint8_t* const buf_off = buf + off;
|
|
switch (mode) {
|
|
case ODK_WRITE:
|
|
ODK_WriteSingleField(buf_off, &fields[i]);
|
|
break;
|
|
case ODK_READ:
|
|
ODK_ReadSingleField(buf_off, &fields[i]);
|
|
break;
|
|
case ODK_DUMP:
|
|
ODK_DumpSingleField(buf_off, &fields[i]);
|
|
break;
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
off = off2;
|
|
}
|
|
*size_out = off;
|
|
if (*size_out > size_in) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
std::vector<ODK_Field> ODK_MakeTotalFields(
|
|
const std::vector<ODK_Field>& extra_fields, ODK_CoreMessage* core_message) {
|
|
std::vector<ODK_Field> total_fields = {
|
|
{ODK_UINT32, &(core_message->message_type), "message_type"},
|
|
{ODK_UINT32, &(core_message->message_length), "message_size"},
|
|
{ODK_UINT16, &(core_message->nonce_values.api_minor_version),
|
|
"api_minor_version"},
|
|
{ODK_UINT16, &(core_message->nonce_values.api_major_version),
|
|
"api_major_version"},
|
|
{ODK_UINT32, &(core_message->nonce_values.nonce), "nonce"},
|
|
{ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"},
|
|
};
|
|
total_fields.insert(total_fields.end(), extra_fields.begin(),
|
|
extra_fields.end());
|
|
return total_fields;
|
|
}
|
|
|
|
// Expect the two buffers of size n to be equal. If not, dump the messages.
|
|
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
|
|
const std::vector<ODK_Field>& fields) {
|
|
if (memcmp(s1, s2, n) != 0) {
|
|
ODK_CoreMessage core_message;
|
|
std::vector<ODK_Field> total_fields =
|
|
ODK_MakeTotalFields(fields, &core_message);
|
|
const void* buffers[] = {s1, s2};
|
|
for (int i = 0; i < 2; i++) {
|
|
char _tmp[] = "/tmp/fileXXXXXX";
|
|
const int temp_fd = mkstemp(_tmp);
|
|
if (temp_fd >= 0) {
|
|
close(temp_fd);
|
|
} else {
|
|
std::cerr << "Failed to open temp file." << '\n';
|
|
break;
|
|
}
|
|
std::string tmp(_tmp);
|
|
std::fstream out(tmp, std::ios::out | std::ios::binary);
|
|
out.write(static_cast<const char*>(buffers[i]), n);
|
|
out.close();
|
|
std::cerr << '\n'
|
|
<< "Message buffer " << i << " dumped to " << tmp << '\n';
|
|
size_t bytes_written;
|
|
uint8_t* buf =
|
|
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
|
|
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, total_fields);
|
|
}
|
|
FAIL();
|
|
}
|
|
}
|
|
|
|
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields) {
|
|
if (fields == nullptr) {
|
|
return;
|
|
}
|
|
for (auto& field : *fields) {
|
|
if (field.value != nullptr) {
|
|
const size_t size = ODK_AllocSize(field.type);
|
|
memset(field.value, 0, size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
|
|
const std::vector<ODK_Field>& extra_fields,
|
|
uint8_t** buf, uint32_t* buf_size) {
|
|
ASSERT_TRUE(core_message != nullptr);
|
|
ASSERT_TRUE(buf_size != nullptr);
|
|
std::vector<ODK_Field> total_fields =
|
|
ODK_MakeTotalFields(extra_fields, core_message);
|
|
|
|
for (auto& field : total_fields) {
|
|
*buf_size += ODK_FieldLength(field.type);
|
|
}
|
|
// update message_size
|
|
*(reinterpret_cast<uint32_t*>(total_fields[1].value)) = *buf_size;
|
|
|
|
*buf = new uint8_t[*buf_size]{};
|
|
size_t bytes_written = 0;
|
|
// serialize ODK fields to message buffer
|
|
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, *buf, SIZE_MAX,
|
|
&bytes_written, total_fields));
|
|
EXPECT_EQ(bytes_written, *buf_size);
|
|
}
|
|
|
|
} // namespace wvodk_test
|