diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 0f670d2..7eeb4b8 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -7,56 +7,13 @@ /** - * Hash field value + * Encode a field value to 32 bytes * - * @param[in] value pointer to value - * @param[in] length its bytelength - * @param[in] dealloc if the value length should be deallocated from the memory + * @param[in] value field value to encode + * @param[in] length field length before encoding + * @return encoded field value */ -static void *hash_field_value(const void *const value, uint16_t length, bool dealloc) -{ - uint8_t *hash_ptr = NULL; - - if (value != NULL) - { - cx_keccak_init((cx_hash_t*)&global_sha3, 256); - cx_hash((cx_hash_t*)&global_sha3, - 0, - (uint8_t*)value, - length, - NULL, - 0); - - if (dealloc) - { - // restore the memory location - mem_dealloc(length); - } - - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) - { - return NULL; - } - - // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, - 0, - hash_ptr, - KECCAK256_HASH_BYTESIZE); - } - return hash_ptr; -} - -/** - * Encode an integer and hash it - * - * @param[in] value pointer to the "packed" integer received - * @param[in] length its byte-length - * @return the encoded (hashed) value - */ -void *encode_integer(const uint8_t *const value, uint16_t length) +static void *field_encode(const uint8_t *const value, uint8_t length) { uint8_t *padded_value; @@ -73,29 +30,30 @@ void *encode_integer(const uint8_t *const value, uint16_t length) padded_value[EIP_712_ENCODED_FIELD_LENGTH - (length - idx)] = value[idx]; } } - return hash_field_value(padded_value, EIP_712_ENCODED_FIELD_LENGTH, true); + return padded_value; } /** - * Encode a string and hash it + * Encode an integer * - * @param[in] value pointer to the string received + * @param[in] value pointer to the "packed" integer received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_string(const char *const value, uint16_t length) +void *encode_integer(const uint8_t *const value, uint8_t length) { - return hash_field_value(value, length, false); + // no length check here since it will be checked by field_encode + return field_encode(value, length); } /** - * Encode a boolean and hash it + * Encode a boolean * * @param[in] value pointer to the boolean received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_bool(const bool *const value, uint16_t length) +void *encode_boolean(const bool *const value, uint8_t length) { if (length != 1) // sanity check { @@ -105,13 +63,13 @@ void *encode_bool(const bool *const value, uint16_t length) } /** - * Encode an address and hash it + * Encode an address * * @param[in] value pointer to the address received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_address(const uint8_t *const value, uint16_t length) +void *encode_address(const uint8_t *const value, uint8_t length) { if (length != ADDRESS_LENGTH) // sanity check { @@ -119,15 +77,3 @@ void *encode_address(const uint8_t *const value, uint16_t length) } return encode_integer(value, length); } - -/** - * Encode bytes and hash it - * - * @param[in] value pointer to the bytes received - * @param[in] length its byte-length - * @return the encoded (hashed) value - */ -void *encode_bytes(const uint8_t *const value, uint16_t length) -{ - return hash_field_value(value, length, false); -} diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index eea2429..bcdaf2a 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -6,10 +6,8 @@ #define EIP_712_ENCODED_FIELD_LENGTH 32 -void *encode_integer(const uint8_t *const value, uint16_t length); -void *encode_string(const char *const value, uint16_t length); -void *encode_bool(const bool *const value, uint16_t length); -void *encode_address(const uint8_t *const value, uint16_t length); -void *encode_bytes(const uint8_t *const value, uint16_t length); +void *encode_integer(const uint8_t *const value, uint8_t length); +void *encode_boolean(const bool *const value, uint8_t length); +void *encode_address(const uint8_t *const value, uint8_t length); #endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 40d42c9..7444d7e 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -386,7 +386,11 @@ bool handle_apdu(const uint8_t *const data) 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]); + if ((data[OFFSET_P1] != P1_COMPLETE) && (data[OFFSET_P1] != P1_PARTIAL)) + { + return false; + } + field_hash(&data[OFFSET_DATA], data[OFFSET_LC], data[OFFSET_P1] == P1_PARTIAL); break; case P2_ARRAY: path_new_array_depth(data[OFFSET_DATA]); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 5cbf791..24361b7 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -3,34 +3,140 @@ #include "field_hash.h" #include "encode_field.h" #include "path.h" +#include "mem.h" #include "eip712.h" +#include "shared_context.h" -const uint8_t *field_hash(const void *const structs_array, - const uint8_t *const data, - const uint8_t data_length) +static s_field_hashing *fh = NULL; + +static bool field_hash_init(void) { + if (fh == NULL) + { + if ((fh = mem_alloc(sizeof(*fh))) == NULL) + { + return false; + } + fh->state = FHS_IDLE; + } + return true; +} + +const uint8_t *field_hash(const uint8_t *data, + uint8_t data_length, + bool partial) +{ + const void *field_ptr; const char *type; uint8_t typelen; const char *key; uint8_t keylen; - const void *field; + e_type field_type; + uint8_t *hash_ptr = NULL; - (void)structs_array; (void)data; - (void)data_length; - // get field by path - //encode_integer(data, data_length); - field = path_get_field(); - if (field != NULL) + if ((fh == NULL) && (field_hash_init() == false)) { + return NULL; + } + // get field by path + if ((field_ptr = path_get_field()) == NULL) + { + return NULL; + } + field_type = struct_field_type(field_ptr); + if (fh->state == FHS_IDLE) // first packet for this frame + { + fh->remaining_size = (data[0] << 8) | data[1]; // network byte order + data += sizeof(uint16_t); + data_length -= sizeof(uint16_t); + fh->state = FHS_WAITING_FOR_MORE; + cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + } + fh->remaining_size -= data_length; + // if a dynamic type -> continue progressive hash + if (IS_DYN(field_type)) + { + cx_hash((cx_hash_t*)&global_sha3, + 0, + data, + data_length, + NULL, + 0); + } + if (fh->remaining_size == 0) + { + if (partial) // only makes sense if marked as complete + { + return NULL; + } printf("==> "); - type = get_struct_field_typename(field, &typelen); + type = get_struct_field_typename(field_ptr, &typelen); fwrite(type, sizeof(char), typelen, stdout); printf(" "); - key = get_struct_field_keyname(field, &keylen); + key = get_struct_field_keyname(field_ptr, &keylen); fwrite(key, sizeof(char), keylen, stdout); printf("\n"); + + uint8_t *value = NULL; + + if (!IS_DYN(field_type)) + { + switch (field_type) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + case TYPE_SOL_BYTES_FIX: + value = encode_integer(data, data_length); + break; + case TYPE_SOL_ADDRESS: + value = encode_address(data, data_length); + break; + case TYPE_SOL_BOOL: + value = encode_boolean((bool*)data, data_length); + break; + case TYPE_CUSTOM: + default: + printf("Unknown solidity type!\n"); + return NULL; + } + + if (value == NULL) + { + return NULL; + } + cx_hash((cx_hash_t*)&global_sha3, + 0, + (uint8_t*)value, + EIP_712_ENCODED_FIELD_LENGTH, + NULL, + 0); + + // restore the memory location + mem_dealloc(EIP_712_ENCODED_FIELD_LENGTH); + } + + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + { + return NULL; + } + // copy hash into memory + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + hash_ptr, + KECCAK256_HASH_BYTESIZE); + path_advance(); + fh->state = FHS_IDLE; } - return NULL; + else + { + if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial + { + return NULL; + } + } + return hash_ptr; } diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index 9b2a2ac..251f5a7 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -2,8 +2,23 @@ #define FIELD_HASH_H_ #include +#include -const uint8_t *field_hash(const void *const structs_array, - const uint8_t *const data, - const uint8_t data_length); +#define IS_DYN(type) (((type) == TYPE_SOL_STRING) || ((type) == TYPE_SOL_BYTES_DYN)) + +typedef enum +{ + FHS_IDLE, + FHS_WAITING_FOR_MORE +} e_field_hashing_state; + +typedef struct +{ + uint16_t remaining_size; + uint8_t state; // e_field_hashing_state +} s_field_hashing; + +const uint8_t *field_hash(const uint8_t *data, + uint8_t data_length, + bool partial); #endif // FIELD_HASH_H_