From 889b5ad2200d432341436da699de61e8f59417ae Mon Sep 17 00:00:00 2001 From: "pwnh4 (@loicttn)" Date: Thu, 12 Dec 2024 17:24:22 +0100 Subject: [PATCH] fix(ocv2): offset parsing verification --- src/kiln_plugin.h | 9 +- src/provide_parameter/ocv2.c | 183 +++++++++++++++++++++++++++++++- tests/src/multiClaim.v2.test.js | 5 +- 3 files changed, 189 insertions(+), 8 deletions(-) diff --git a/src/kiln_plugin.h b/src/kiln_plugin.h index a8ed6a4..272a956 100644 --- a/src/kiln_plugin.h +++ b/src/kiln_plugin.h @@ -236,13 +236,20 @@ typedef struct { } v2_request_exit_t; typedef struct { - uint16_t cask_ids_offset; // -- utils + uint16_t cask_ids_offset; + uint16_t current_item_count; } v2_claim_t; typedef struct { // -- utils + uint16_t ticket_ids_offset; + uint16_t cask_ids_offset; + uint8_t checksum_preview[CX_KECCAK_256_SIZE]; + uint8_t checksum_value[CX_KECCAK_256_SIZE]; + uint32_t cached_offset; + uint16_t parent_item_count; uint16_t current_item_count; } v2_multiclaim_t; diff --git a/src/provide_parameter/ocv2.c b/src/provide_parameter/ocv2.c index 5f9ec3c..f60fd8b 100644 --- a/src/provide_parameter/ocv2.c +++ b/src/provide_parameter/ocv2.c @@ -169,13 +169,27 @@ void handle_v2_multiclaim(ethPluginProvideParameter_t *msg, context_t *context) v2_multiclaim_t *params = &context->param_data.v2_multiclaim; switch (context->next_param) { - case V2_MULTICLAIM_EXIT_QUEUES_OFFSET: + case V2_MULTICLAIM_EXIT_QUEUES_OFFSET: { + uint16_t offset; + U2BE_from_parameter(msg->parameter, &offset); + if (offset != PARAMETER_LENGTH * 3) { + PRINTF("Malformed calldata, unexpected exitqueues parameter offset %d != %d\n", + offset, + PARAMETER_LENGTH); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } context->next_param = V2_MULTICLAIM_TICKET_IDS_OFFSET; break; + } case V2_MULTICLAIM_TICKET_IDS_OFFSET: + U2BE_from_parameter(msg->parameter, ¶ms->ticket_ids_offset); + params->ticket_ids_offset += SELECTOR_SIZE; context->next_param = V2_MULTICLAIM_CASK_IDS_OFFSET; break; case V2_MULTICLAIM_CASK_IDS_OFFSET: + U2BE_from_parameter(msg->parameter, ¶ms->cask_ids_offset); + params->cask_ids_offset += SELECTOR_SIZE; context->next_param = V2_MULTICLAIM_EXIT_QUEUES_LENGTH; break; case V2_MULTICLAIM_EXIT_QUEUES_LENGTH: @@ -209,50 +223,209 @@ void handle_v2_multiclaim(ethPluginProvideParameter_t *msg, context_t *context) break; } case V2_MULTICLAIM_TICKETIDS_LENGTH: + if (msg->parameterOffset != params->ticket_ids_offset) { + PRINTF("Malformed calldata, unexpected ticketids[X][] parameter offset %d != %d\n", + msg->parameterOffset, + params->ticket_ids_offset); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + + memset(params->checksum_preview, 0, sizeof(params->checksum_preview)); + memset(params->checksum_value, 0, sizeof(params->checksum_value)); + params->cached_offset = 0; + U2BE_from_parameter(msg->parameter, ¶ms->parent_item_count); params->current_item_count = params->parent_item_count; + context->next_param = V2_MULTICLAIM_TICKETIDS__OFFSET_ITEMS; break; - case V2_MULTICLAIM_TICKETIDS__OFFSET_ITEMS: + // ******************************************************************** + // TICKETIDS[][] + // ******************************************************************** + case V2_MULTICLAIM_TICKETIDS__OFFSET_ITEMS: { + uint16_t offset; + U2BE_from_parameter(msg->parameter, &offset); + // We have limited size on the context and can't store all the offset values + // of the ticketids subarrays. So we compute their checksum and expect to + // be able to recompute it using the offset of the parsed structures later. + // _preview will be equal to _value at the end of the parsing if everything is fine + checksum_offset_params_t h_params; + memset(&h_params, 0, sizeof(h_params)); + memcpy(&h_params.prev_checksum, + &(params->checksum_preview), + sizeof(h_params.prev_checksum)); + + // if we are on the first element of the array, we save the offset, which all + // received offset values will be based on for checksum computation + if (params->cached_offset == 0) { + params->cached_offset = msg->parameterOffset; + } + + // we hash the previous checksum with the offset of the beginning of the structure. + h_params.new_offset = offset + params->cached_offset; + PRINTF("V2_MULTICLAIM_TICKETIDS__OFFSET_ITEMS_PREVIEW: %d\n", h_params.new_offset); + + if (cx_keccak_256_hash((void *) &h_params, + sizeof(h_params), + params->checksum_preview) != CX_OK) { + PRINTF("unable to compute keccak hash\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + params->current_item_count -= 1; if (params->current_item_count == 0) { context->next_param = V2_MULTICLAIM_TICKETIDS__ITEM_LENGTH; } break; - case V2_MULTICLAIM_TICKETIDS__ITEM_LENGTH: + } + // ******************************************************************** + // TICKETIDS[][] + // . ^ + // ******************************************************************** + case V2_MULTICLAIM_TICKETIDS__ITEM_LENGTH: { + // here we compute the offset of the ticketIds subarray checksum + checksum_offset_params_t h_params; + memset(&h_params, 0, sizeof(h_params)); + memcpy(&h_params.prev_checksum, + &(params->checksum_value), + sizeof(h_params.prev_checksum)); + + // we hash the previous checksum with the offset of the beginning of the structure. + h_params.new_offset = msg->parameterOffset; + PRINTF("V2_MULTICLAIM_TICKETIDS__OFFSET_ITEMS_VALUE: %d\n", h_params.new_offset); + + if (cx_keccak_256_hash((void *) &h_params, sizeof(h_params), params->checksum_value) != + CX_OK) { + PRINTF("unable to compute keccak hash\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + U2BE_from_parameter(msg->parameter, ¶ms->current_item_count); context->next_param = V2_MULTICLAIM_TICKETIDS__ITEM__ITEMS; break; + } case V2_MULTICLAIM_TICKETIDS__ITEM__ITEMS: params->current_item_count -= 1; if (params->current_item_count == 0) { params->parent_item_count -= 1; if (params->parent_item_count == 0) { + // we check the checksums + if (memcmp(params->checksum_preview, + params->checksum_value, + sizeof(params->checksum_preview)) != 0) { + PRINTF("Tokenids[][] checksums do not match\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + context->next_param = V2_MULTICLAIM_CASKIDS_LENGTH; + } else { context->next_param = V2_MULTICLAIM_TICKETIDS__ITEM_LENGTH; } } break; + // ******************************************************************** + // CASKIDS[][] + // ******************************************************************** case V2_MULTICLAIM_CASKIDS_LENGTH: + if (msg->parameterOffset != params->cask_ids_offset) { + PRINTF("Malformed calldata, unexpected caskids parameter offset %d != %d\n", + msg->parameterOffset, + params->cask_ids_offset); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + + memset(params->checksum_preview, 0, sizeof(params->checksum_preview)); + memset(params->checksum_value, 0, sizeof(params->checksum_value)); + params->cached_offset = 0; + U2BE_from_parameter(msg->parameter, ¶ms->parent_item_count); params->current_item_count = params->parent_item_count; context->next_param = V2_MULTICLAIM_CASKIDS__OFFSET_ITEMS; break; - case V2_MULTICLAIM_CASKIDS__OFFSET_ITEMS: + case V2_MULTICLAIM_CASKIDS__OFFSET_ITEMS: { + uint16_t offset; + U2BE_from_parameter(msg->parameter, &offset); + // We have limited size on the context and can't store all the offset values + // of the caskids subarrays. So we compute their checksum and expect to + // be able to recompute it using the offset of the parsed structures later. + // _preview will be equal to _value at the end of the parsing if everything is fine + checksum_offset_params_t h_params; + memset(&h_params, 0, sizeof(h_params)); + memcpy(&h_params.prev_checksum, + &(params->checksum_preview), + sizeof(h_params.prev_checksum)); + + // if we are on the first element of the array, we save the offset, which all + // received offset values will be based on for checksum computation + if (params->cached_offset == 0) { + params->cached_offset = msg->parameterOffset; + } + + // we hash the previous checksum with the offset of the beginning of the structure. + h_params.new_offset = offset + params->cached_offset; + PRINTF("V2_MULTICLAIM_CASKIDS__OFFSET_ITEMS_PREVIEW: %d\n", h_params.new_offset); + + if (cx_keccak_256_hash((void *) &h_params, + sizeof(h_params), + params->checksum_preview) != CX_OK) { + PRINTF("unable to compute keccak hash\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + params->current_item_count -= 1; if (params->current_item_count == 0) { context->next_param = V2_MULTICLAIM_CASKIDS__ITEM_LENGTH; } break; - case V2_MULTICLAIM_CASKIDS__ITEM_LENGTH: + } + // ******************************************************************** + // CASKIDS[][] + // . ^ + // ******************************************************************** + case V2_MULTICLAIM_CASKIDS__ITEM_LENGTH: { + // here we compute the offset of the caskIds subarray checksum + checksum_offset_params_t h_params; + + memset(&h_params, 0, sizeof(h_params)); + memcpy(&h_params.prev_checksum, + &(params->checksum_value), + sizeof(h_params.prev_checksum)); + + // we hash the previous checksum with the offset of the beginning of the structure. + h_params.new_offset = msg->parameterOffset; + PRINTF("V2_MULTICLAIM_CASKIDS__OFFSET_ITEMS_VALUE: %d\n", h_params.new_offset); + + if (cx_keccak_256_hash((void *) &h_params, sizeof(h_params), params->checksum_value) != + CX_OK) { + PRINTF("unable to compute keccak hash\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + U2BE_from_parameter(msg->parameter, ¶ms->current_item_count); context->next_param = V2_MULTICLAIM_CASKIDS__ITEM__ITEMS; break; + } case V2_MULTICLAIM_CASKIDS__ITEM__ITEMS: params->current_item_count -= 1; if (params->current_item_count == 0) { if (params->parent_item_count == 0) { + // we check the checksums + if (memcmp(params->checksum_preview, + params->checksum_value, + sizeof(params->checksum_preview)) != 0) { + PRINTF("Caskids[][] checksums do not match\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + context->next_param = V2_MULTICLAIM_UNEXPECTED_PARAMETER; } else { context->next_param = V2_MULTICLAIM_CASKIDS__ITEM_LENGTH; diff --git a/tests/src/multiClaim.v2.test.js b/tests/src/multiClaim.v2.test.js index a210101..0faab39 100644 --- a/tests/src/multiClaim.v2.test.js +++ b/tests/src/multiClaim.v2.test.js @@ -23,8 +23,9 @@ nano_models.forEach(function (model) { '0x86358F7B33b599c484e0335B8Ee4f7f7f92d8b60', ], [ - [42, 47], - [150, 2], + [42, 47, 3], + [150, 2, 3], + [150, 2, 3], ], [ [0, 1],