// 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 #include #include #include #include #include #include #include #include #include #include "OEMCryptoCENCCommon.h" #include "gtest/gtest.h" #include "odk_endian.h" #include "odk_structs.h" #include "odk_structs_priv.h" #include "third_party/absl/types/span.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_BOOL: // Booleans are stored in the message as 32 bit ints. return sizeof(uint32_t); case ODK_SUBSTRING: return sizeof(uint32_t) + sizeof(uint32_t); case ODK_DEVICEID: return ODK_DEVICE_ID_LEN_MAX; case ODK_MESSAGECOUNTER: return ODK_MESSAGECOUNTERINFO_SIZE; case ODK_DEVICEINFO: return ODK_DEVICE_INFO_LEN_MAX; case ODK_RENEWALDATA: return ODK_KEYBOX_RENEWAL_DATA_SIZE; case ODK_HASH: return ODK_SHA256_HASH_SIZE; default: return SIZE_MAX; } } 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(field->value)); memcpy(buf, &u16, sizeof(u16)); break; } case ODK_UINT32: { const uint32_t u32 = oemcrypto_htobe32(*static_cast(field->value)); memcpy(buf, &u32, sizeof(u32)); break; } case ODK_UINT64: { const uint64_t u64 = oemcrypto_htobe64(*static_cast(field->value)); memcpy(buf, &u64, sizeof(u64)); break; } case ODK_INT64: { const int64_t i64 = oemcrypto_htobe64(*static_cast(field->value)); memcpy(buf, &i64, sizeof(i64)); break; } case ODK_BOOL: { const bool value = *static_cast(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(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(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(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(field->value); *u16p = oemcrypto_be16toh(*u16p); break; } case ODK_UINT32: { memcpy(field->value, buf, sizeof(uint32_t)); uint32_t* u32p = static_cast(field->value); *u32p = oemcrypto_be32toh(*u32p); break; } case ODK_UINT64: { memcpy(field->value, buf, sizeof(uint64_t)); uint64_t* u64p = static_cast(field->value); *u64p = oemcrypto_be64toh(*u64p); break; } case ODK_INT64: { memcpy(field->value, buf, sizeof(int64_t)); int64_t* i64p = static_cast(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(field->value) = (value != 0); break; } case ODK_SUBSTRING: { OEMCrypto_Substring* s = static_cast(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(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(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(&temp_buf[index]); info.master_generation_number = oemcrypto_be64toh(*u64); index += sizeof(uint64_t); uint32_t* u32 = reinterpret_cast(&temp_buf[index]); info.provisioning_count = oemcrypto_be32toh(*u32); index += sizeof(uint32_t); u32 = reinterpret_cast(&temp_buf[index]); info.license_count = oemcrypto_be32toh(*u32); index += sizeof(uint32_t); u32 = reinterpret_cast(&temp_buf[index]); info.decrypt_count = oemcrypto_be32toh(*u32); index += sizeof(uint32_t); uint16_t* u16 = reinterpret_cast(&temp_buf[index]); info.major_version = oemcrypto_be16toh(*u16); index += sizeof(uint16_t); u16 = reinterpret_cast(&temp_buf[index]); info.minor_version = oemcrypto_be16toh(*u16); index += sizeof(uint16_t); u16 = reinterpret_cast(&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(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, absl::Span 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(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_MakeTotalFields( absl::Span extra_fields, ODK_CoreMessage* core_message) { std::vector 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& fields) { if (memcmp(s1, s2, n) != 0) { ODK_CoreMessage core_message; std::vector 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(buffers[i]), n); out.close(); std::cerr << '\n' << "Message buffer " << i << " dumped to " << tmp << '\n'; size_t bytes_written; uint8_t* buf = const_cast(reinterpret_cast(buffers[i])); ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, total_fields); } FAIL(); } } void ODK_ResetOdkFields(std::vector* 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& extra_fields, uint8_t** buf, uint32_t* buf_size) { ASSERT_TRUE(core_message != nullptr); ASSERT_TRUE(buf_size != nullptr); std::vector total_fields = ODK_MakeTotalFields(extra_fields, core_message); for (auto& field : total_fields) { *buf_size += ODK_FieldLength(field.type); } // update message_size *(reinterpret_cast(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