diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index b76a3eb..56e5d36 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -20,9 +20,11 @@ bool init_eip712_context(void) mem_init(); if (init_sol_typenames() == false) + { return false; + } - if (init_path() == false) + if (path_init() == false) { return false; } diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 26ed8d1..c77b182 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -108,7 +108,6 @@ const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length); const char *get_struct_field_keyname(const uint8_t *ptr, uint8_t *const length); -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); const uint8_t *get_next_struct_field(const void *ptr); const uint8_t *get_structn(const uint8_t *const ptr, const char *const name_ptr, diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 08751cc..40d42c9 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -10,6 +10,7 @@ #include "context.h" #include "sol_typenames.h" #include "field_hash.h" +#include "path.h" // lib functions @@ -380,12 +381,15 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: + // set root type + path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: field_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_ARRAY: + path_new_array_depth(data[OFFSET_DATA]); break; default: printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index f4aee73..5cbf791 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -1,16 +1,36 @@ +#include #include #include "field_hash.h" #include "encode_field.h" +#include "path.h" +#include "eip712.h" const uint8_t *field_hash(const void *const structs_array, const uint8_t *const data, const uint8_t data_length) { + const char *type; + uint8_t typelen; + const char *key; + uint8_t keylen; + const void *field; + (void)structs_array; (void)data; (void)data_length; // get field by path - encode_integer(data, data_length); - // path += 1 + //encode_integer(data, data_length); + field = path_get_field(); + if (field != NULL) + { + printf("==> "); + type = get_struct_field_typename(field, &typelen); + fwrite(type, sizeof(char), typelen, stdout); + printf(" "); + key = get_struct_field_keyname(field, &keylen); + fwrite(key, sizeof(char), keylen, stdout); + printf("\n"); + path_advance(); + } return NULL; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index dc7368d..7427896 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -1,22 +1,406 @@ #include #include +#include #include "path.h" #include "mem.h" #include "context.h" +#include "eip712.h" -uint8_t *path_indexes; +static s_path *path_struct = NULL; /** - * Allocates the the path indexes in memory and sets them all to 0 with a count of 1. + * Get the field pointer to by the first N depths of the path. + * + * @param[out] fields_count_ptr the number of fields in the last evaluated depth + * @param[in] depth_count the number of depths to evaluate (N) + * @return the feld which the first Nth depths points to */ -bool init_path(void) +static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, + uint8_t depth_count) { - // + 1 for the used index count - if ((path_indexes = mem_alloc(sizeof(uint8_t) * (MAX_PATH_DEPTH + 1))) != NULL) + const void *struct_ptr = path_struct->root_struct; + const void *field_ptr = NULL; + const char *typename; + uint8_t length; + uint8_t fields_count; + + if (depth_count > path_struct->depth_count) // sanity check { - // set all to 0 - explicit_bzero(path_indexes + 1, sizeof(uint8_t) * MAX_PATH_DEPTH); - *path_indexes = 1; // init count at 1, so the default path will be 0 + return NULL; } - return path_indexes != NULL; + for (uint8_t depth = 0; depth < depth_count; ++depth) + { + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + + if (fields_count_ptr != NULL) + { + *fields_count_ptr = fields_count; + } + // check if the index at this depth makes sense + if (path_struct->depths[depth] > fields_count) + { + return NULL; + } + + for (uint8_t index = 0; index < path_struct->depths[depth]; ++index) + { + field_ptr = get_next_struct_field(field_ptr); + } + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + typename = get_struct_field_typename(field_ptr, &length); + if ((struct_ptr = get_structn(structs_array, typename, length)) == NULL) + { + return NULL; + } + } + } + return field_ptr; +} + +/** + * Get the element the path is pointing to. (internal) + * + * @param[out] the number of fields in the depth of the returned field + * @return the field which the path points to + */ +static inline const void *get_field_from_path(uint8_t *const fields_count) +{ + return get_nth_field_from_path(fields_count, path_struct->depth_count); +} + +/** + * Get the element the path is pointing to. (public facing) + * + * @return the field which the path points to + */ +const void *path_get_field(void) +{ + return get_field_from_path(NULL); +} + +/** + * Go down (add) a depth level. + * + * @return whether the push was succesful + */ +static bool path_depth_list_push(void) +{ + if (path_struct->depth_count == MAX_PATH_DEPTH) + { + return false; + } + path_struct->depths[path_struct->depth_count] = 0; + path_struct->depth_count += 1; + return true; +} + +/** + * Go up (remove) a depth level. + * + * @return whether the pop was successful + */ +static bool path_depth_list_pop(void) +{ + if (path_struct->depth_count == 0) + { + return false; + } + path_struct->depth_count -= 1; + return true; +} + +/** + * Go down (add) an array depth level. + * + * @param[in] path_idx the index in the path list + * @param[in] the number of elements contained in that depth + * @return whether the push was successful + */ +static bool array_depth_list_push(uint8_t path_idx, uint8_t size) +{ + s_array_depth *arr = &path_struct->array_depths[path_struct->array_depth_count]; + + if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) + { + return false; + } + arr->path_index = path_idx; + arr->size = size; + path_struct->array_depth_count += 1; + return true; +} + +/** + * Go up (remove) an array depth level. + * + * @return whether the pop was successful + */ +static bool array_depth_list_pop(void) +{ + if (path_struct->array_depth_count == 0) + { + return false; + } + path_struct->array_depth_count -= 1; + return true; +} + +/** + * Updates the path so that it doesn't point to a struct-type field, but rather + * only to actual fields. + * + * @return whether the path update worked or not + */ +static bool path_update(void) +{ + uint8_t fields_count; + const void *struct_ptr = path_struct->root_struct; + const void *field_ptr; + const char *typename; + uint8_t typename_len; + + if ((field_ptr = get_field_from_path(NULL)) == NULL) + { + return false; + } + while (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + // TODO: calculate the type hash here + typename = get_struct_field_typename(field_ptr, &typename_len); + if ((struct_ptr = get_structn(structs_array, typename, typename_len)) == NULL) + { + return false; + } + if ((field_ptr = get_struct_fields_array(struct_ptr, &fields_count)) == NULL) + { + return false; + } + path_depth_list_push(); + } + return true; +} + +/** + * Set a new struct as the path root type + * + * @param[in] struct_name the root struct name + * @param[in] name_length the root struct name length + * @return boolean indicating if it was successful or not + */ +bool path_set_root(const char *const struct_name, uint8_t name_length) +{ + if (path_struct == NULL) + { + return false; + } + + path_struct->root_struct = get_structn(structs_array, struct_name, name_length); + + if (path_struct->root_struct == NULL) + { + return false; + } + + // init depth, at 0 : empty path + path_struct->depth_count = 0; + path_depth_list_push(); + + // init array levels at 0 + path_struct->array_depth_count = 0; + + // because the first field could be a struct type + path_update(); + return true; +} + +/** + * Checks the new array depth and adds it to the list + * + * @param[in] depth pointer to the array depth definition + * @param[in] total_count number of array depth contained down to this array depth + * @param[in] pidx path index + * @param[in] size requested array depth size + * @return whether the checks and add were successful or not + */ +static bool check_and_add_array_depth(const void *depth, + uint8_t total_count, + uint8_t pidx, + uint8_t size) +{ + uint8_t expected_size; + uint8_t arr_idx = (total_count - path_struct->array_depth_count) - 1; + e_array_type expected_type; + + // we skip index 0, since we already have it + for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) + { + if ((depth = get_next_struct_field_array_lvl(depth)) == NULL) + { + return false; + } + } + expected_type = struct_field_array_depth(depth, &expected_size); + if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) + { + printf("Unexpected array depth size. (expected %d, got %d)\n", + expected_size, size); + return false; + } + // add it + if (!array_depth_list_push(pidx, size)) + { + return false; + } + return true; +} + +/** + * Add a new array depth with a given size (number of elements). + * + * @return whether the add was successful or not + */ +bool path_new_array_depth(uint8_t size) +{ + const void *field_ptr, *depth; + uint8_t depth_count; + uint8_t total_count = 0; + uint8_t pidx; + + if (path_struct == NULL) // sanity check + { + printf("NULL struct check failed\n"); + return false; + } + + for (pidx = 0; pidx < path_struct->depth_count; ++pidx) + { + if ((field_ptr = get_nth_field_from_path(NULL, pidx + 1)) == NULL) + { + return false; + } + if (struct_field_is_array(field_ptr)) + { + if ((depth = get_struct_field_array_lvls_array(field_ptr, &depth_count)) == NULL) + { + return false; + } + total_count += depth_count; + if (total_count > path_struct->array_depth_count) + { + if (!check_and_add_array_depth(depth, total_count, pidx, size)) + { + return false; + } + break; + } + } + } + + if (pidx == path_struct->depth_count) + { + printf("Did not find a matching array type.\n"); + return false; + } + return true; +} + +/** + * Advance within the struct that contains the field the path points to. + * + * @return whether the end of the struct has been reached. + */ +static bool path_advance_in_struct(void) +{ + bool end_reached = true; + uint8_t *depth = &path_struct->depths[path_struct->depth_count - 1]; + uint8_t fields_count; + + if ((get_field_from_path(&fields_count)) == NULL) + { + return false; + } + if (path_struct->depth_count > 0) + { + *depth += 1; + end_reached = (*depth == fields_count); + } + if (end_reached) + { + path_depth_list_pop(); + } + return end_reached; +} + +/** + * Advance within the array levels of the current field the path points to. + * + * @return whether the end of the array levels has been reached. + */ +static bool path_advance_in_array(void) +{ + bool end_reached; + s_array_depth *arr_depth; + + do + { + end_reached = false; + arr_depth = &path_struct->array_depths[path_struct->array_depth_count - 1]; + + if ((path_struct->array_depth_count > 0) && + (arr_depth->path_index == (path_struct->depth_count - 1))) + { + arr_depth->size -= 1; + if (arr_depth->size == 0) + { + array_depth_list_pop(); + end_reached = true; + } + else + { + return false; + } + } + } + while (end_reached); + return true; +} + +/** + * Updates the path to point to the next field in order (DFS). + * + * @return whether the advancement was successful or not + */ +bool path_advance(void) +{ + bool end_reached; + + do + { + if (path_advance_in_array()) + { + end_reached = path_advance_in_struct(); + } + else + { + end_reached = false; + } + } + while (end_reached); + path_update(); + return true; +} + +/** + * Allocates the the path indexes in memory and sets it with a depth of 0. + * + * @return whether the memory allocation were successful. + */ +bool path_init(void) +{ + if (path_struct == NULL) + { + path_struct = mem_alloc(sizeof(*path_struct)); + } + return path_struct != NULL; } diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 8eda5ba..3be4c5f 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -1,12 +1,31 @@ #ifndef PATH_H_ #define PATH_H_ +#include #include #define MAX_PATH_DEPTH 16 +#define MAX_ARRAY_DEPTH 4 -extern uint8_t *path_indexes; +typedef struct __attribute__((packed)) +{ + uint8_t path_index; + uint8_t size; +} s_array_depth; -bool init_path(void); +typedef struct __attribute__((packed)) +{ + const void *root_struct; + uint8_t depth_count; + uint8_t depths[MAX_PATH_DEPTH]; + uint8_t array_depth_count; + s_array_depth array_depths[MAX_ARRAY_DEPTH]; +} s_path; + +bool path_set_root(const char *const struct_name, uint8_t length); +const void *path_get_field(void); +bool path_advance(void); +bool path_init(void); +bool path_new_array_depth(uint8_t size); #endif // PATH_H_