Files

295 lines
8.7 KiB
C
Raw Permalink Normal View History

#ifdef HAVE_EIP712_FULL_SUPPORT
2022-05-02 15:30:14 +02:00
#include <stdlib.h>
#include "field_hash.h"
#include "encode_field.h"
#include "path.h"
2022-04-14 18:22:35 +02:00
#include "mem.h"
#include "mem_utils.h"
2022-04-14 18:22:35 +02:00
#include "shared_context.h"
2022-05-12 17:29:35 +02:00
#include "ui_logic.h"
#include "context_712.h" // contract_addr
2024-01-16 13:51:36 +01:00
#include "common_utils.h" // u64_from_BE
2022-07-19 11:49:18 +02:00
#include "apdu_constants.h" // APDU response codes
#include "typed_data.h"
#include "commands_712.h"
2022-07-07 17:52:01 +02:00
#include "hash_bytes.h"
2022-05-02 15:30:14 +02:00
2022-04-14 18:22:35 +02:00
static s_field_hashing *fh = NULL;
2022-07-11 17:12:58 +02:00
/**
* Initialize the field hash context
*
* @return whether the initialization was successful or not
*/
2022-07-19 11:49:18 +02:00
bool field_hash_init(void) {
if (fh == NULL) {
if ((fh = MEM_ALLOC_AND_ALIGN_TYPE(*fh)) == NULL) {
apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY;
2022-04-14 18:22:35 +02:00
return false;
}
fh->state = FHS_IDLE;
}
return true;
}
2022-07-11 17:12:58 +02:00
/**
* Deinitialize the field hash context
*/
2022-07-19 11:49:18 +02:00
void field_hash_deinit(void) {
2022-05-12 17:30:26 +02:00
fh = NULL;
}
2022-07-11 17:12:58 +02:00
/**
* Special handling of the first chunk received from a field value
*
* @param[in] field_ptr pointer to the struct field definition
* @param[in] data the field value
* @param[in,out] data_length the value length
* @return the data pointer
*/
2022-07-07 17:52:01 +02:00
static const uint8_t *field_hash_prepare(const void *const field_ptr,
2022-07-11 17:12:58 +02:00
const uint8_t *data,
2022-07-19 11:49:18 +02:00
uint8_t *data_length) {
2022-05-02 17:32:00 +02:00
e_type field_type;
2024-03-18 08:58:05 +01:00
cx_err_t error = CX_INTERNAL_ERROR;
2022-07-07 17:52:01 +02:00
field_type = struct_field_type(field_ptr);
2022-07-19 11:49:18 +02:00
fh->remaining_size = __builtin_bswap16(*(uint16_t *) &data[0]); // network byte order
2022-07-07 17:52:01 +02:00
data += sizeof(uint16_t);
*data_length -= sizeof(uint16_t);
fh->state = FHS_WAITING_FOR_MORE;
2022-07-19 11:49:18 +02:00
if (IS_DYN(field_type)) {
2024-03-18 08:58:05 +01:00
CX_CHECK(cx_keccak_init_no_throw(&global_sha3, 256));
2022-07-07 17:52:01 +02:00
}
return data;
2024-03-18 08:58:05 +01:00
end:
return NULL;
2022-07-07 17:52:01 +02:00
}
2022-07-11 17:12:58 +02:00
/**
* Finalize static field hash
*
* Encode the field data depending on its type
*
* @param[in] field_ptr pointer to the struct field definition
* @param[in] data the field value
* @param[in] data_length the value length
* @return pointer to the encoded value
*/
2022-07-07 17:52:01 +02:00
static const uint8_t *field_hash_finalize_static(const void *const field_ptr,
const uint8_t *const data,
2022-07-19 11:49:18 +02:00
uint8_t data_length) {
2022-05-02 17:32:00 +02:00
uint8_t *value = NULL;
2022-07-07 17:52:01 +02:00
e_type field_type;
field_type = struct_field_type(field_ptr);
2022-07-19 11:49:18 +02:00
switch (field_type) {
2022-07-07 17:52:01 +02:00
case TYPE_SOL_INT:
value = encode_int(data, data_length, get_struct_field_typesize(field_ptr));
break;
case TYPE_SOL_UINT:
value = encode_uint(data, data_length);
break;
case TYPE_SOL_BYTES_FIX:
value = encode_bytes(data, data_length);
break;
case TYPE_SOL_ADDRESS:
value = encode_address(data, data_length);
break;
case TYPE_SOL_BOOL:
2022-07-19 11:49:18 +02:00
value = encode_boolean((bool *) data, data_length);
2022-07-07 17:52:01 +02:00
break;
case TYPE_CUSTOM:
default:
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
PRINTF("Unknown solidity type!\n");
}
return value;
}
2022-07-11 17:12:58 +02:00
/**
* Finalize dynamic field hash
*
* Allocate and hash the data
*
* @return pointer to the hash, \ref NULL if it failed
*/
2022-07-19 11:49:18 +02:00
static uint8_t *field_hash_finalize_dynamic(void) {
2022-07-07 17:52:01 +02:00
uint8_t *value;
2024-03-18 08:58:05 +01:00
cx_err_t error = CX_INTERNAL_ERROR;
2022-07-07 17:52:01 +02:00
2022-07-19 11:49:18 +02:00
if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) {
2022-07-07 17:52:01 +02:00
apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY;
return NULL;
}
// copy hash into memory
2024-03-18 08:58:05 +01:00
CX_CHECK(cx_hash_no_throw((cx_hash_t *) &global_sha3,
CX_LAST,
NULL,
0,
value,
KECCAK256_HASH_BYTESIZE));
2022-07-07 17:52:01 +02:00
return value;
2024-03-18 08:58:05 +01:00
end:
return NULL;
2022-07-07 17:52:01 +02:00
}
2022-07-11 17:12:58 +02:00
/**
* Feed the newly created field hash into the parent struct's progressive hash
*
* @param[in] field_type the struct field's type
* @param[in] hash the field hash
*/
2022-07-19 11:49:18 +02:00
static void field_hash_feed_parent(e_type field_type, const uint8_t *const hash) {
2022-07-07 17:52:01 +02:00
uint8_t len;
2022-07-19 11:49:18 +02:00
if (IS_DYN(field_type)) {
2022-07-07 17:52:01 +02:00
len = KECCAK256_HASH_BYTESIZE;
2022-07-19 11:49:18 +02:00
} else {
2022-07-07 17:52:01 +02:00
len = EIP_712_ENCODED_FIELD_LENGTH;
}
// last thing in mem is the hash of the previous field
// and just before it is the current hash context
2022-07-19 11:49:18 +02:00
cx_sha3_t *hash_ctx = (cx_sha3_t *) (hash - sizeof(cx_sha3_t));
2022-07-11 17:12:58 +02:00
// continue the progressive hash on it
2022-07-19 11:49:18 +02:00
hash_nbytes(hash, len, (cx_hash_t *) hash_ctx);
2022-07-07 17:52:01 +02:00
// deallocate it
mem_dealloc(len);
}
2022-07-11 17:12:58 +02:00
/**
* Special domain fields handling
*
* Do something special for certain EIP712Domain fields
*
* @param[in] field_ptr pointer to the struct field definition
* @param[in] data the field value
* @param[in] data_length the value length
* @return whether an error occurred or not
2022-07-11 17:12:58 +02:00
*/
2022-07-07 17:52:01 +02:00
static bool field_hash_domain_special_fields(const void *const field_ptr,
const uint8_t *const data,
2022-07-19 11:49:18 +02:00
uint8_t data_length) {
const char *key;
uint8_t keylen;
2022-05-02 11:04:59 +02:00
2022-07-07 17:52:01 +02:00
key = get_struct_field_keyname(field_ptr, &keylen);
// copy contract address into context
2022-07-19 11:49:18 +02:00
if (strncmp(key, "verifyingContract", keylen) == 0) {
if (data_length != sizeof(eip712_context->contract_addr)) {
2022-07-07 17:52:01 +02:00
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
PRINTF("Unexpected verifyingContract length!\n");
return false;
}
memcpy(eip712_context->contract_addr, data, data_length);
2022-07-19 11:49:18 +02:00
} else if (strncmp(key, "chainId", keylen) == 0) {
eip712_context->chain_id = u64_from_BE(data, data_length);
2022-07-07 17:52:01 +02:00
}
return true;
}
2022-07-11 17:12:58 +02:00
/**
* Finalize the data hashing
*
* @param[in] field_ptr pointer to the struct field definition
* @param[in] data the field value
* @param[in] data_length the value length
* @return whether an error occurred or not
2022-07-11 17:12:58 +02:00
*/
2022-07-07 17:52:01 +02:00
static bool field_hash_finalize(const void *const field_ptr,
const uint8_t *const data,
2022-07-19 11:49:18 +02:00
uint8_t data_length) {
2022-07-07 17:52:01 +02:00
const uint8_t *value = NULL;
e_type field_type;
field_type = struct_field_type(field_ptr);
2022-07-19 11:49:18 +02:00
if (!IS_DYN(field_type)) {
if ((value = field_hash_finalize_static(field_ptr, data, data_length)) == NULL) {
2022-07-07 17:52:01 +02:00
return false;
}
2022-07-19 11:49:18 +02:00
} else {
if ((value = field_hash_finalize_dynamic()) == NULL) {
2022-07-07 17:52:01 +02:00
return false;
}
}
field_hash_feed_parent(field_type, value);
2022-07-19 11:49:18 +02:00
if (path_get_root_type() == ROOT_DOMAIN) {
if (field_hash_domain_special_fields(field_ptr, data, data_length) == false) {
2022-07-07 17:52:01 +02:00
return false;
}
2022-04-14 18:22:35 +02:00
}
path_advance(true);
2022-07-07 17:52:01 +02:00
fh->state = FHS_IDLE;
ui_712_finalize_field();
return true;
}
2022-07-11 17:12:58 +02:00
/**
* Hash a field value
*
* @param[in] data the field value
* @param[in] data_length the value length
* @param[in] partial whether there is more of that data coming later or not
* @return whether the data hashing was successful or not
*/
2022-07-19 11:49:18 +02:00
bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) {
2022-07-07 17:52:01 +02:00
const void *field_ptr;
e_type field_type;
bool first = fh->state == FHS_IDLE;
2022-07-07 17:52:01 +02:00
2022-07-19 11:49:18 +02:00
if ((fh == NULL) || ((field_ptr = path_get_field()) == NULL)) {
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
2022-05-04 11:54:22 +02:00
return false;
2022-04-14 18:22:35 +02:00
}
2022-07-07 17:52:01 +02:00
2022-04-14 18:22:35 +02:00
field_type = struct_field_type(field_ptr);
// first packet for this frame
if (first) {
if (!ui_712_show_raw_key(field_ptr)) {
return false;
}
if (data_length < 2) {
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
return false;
}
2022-07-07 17:52:01 +02:00
data = field_hash_prepare(field_ptr, data, &data_length);
2022-04-14 18:22:35 +02:00
}
2022-07-19 11:49:18 +02:00
if (data_length > fh->remaining_size) {
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
return false;
}
2022-04-14 18:22:35 +02:00
fh->remaining_size -= data_length;
// if a dynamic type -> continue progressive hash
2022-07-19 11:49:18 +02:00
if (IS_DYN(field_type)) {
hash_nbytes(data, data_length, (cx_hash_t *) &global_sha3);
2022-04-14 18:22:35 +02:00
}
if (!ui_712_feed_to_display(field_ptr, data, data_length, first, fh->remaining_size == 0)) {
return false;
}
2022-07-19 11:49:18 +02:00
if (fh->remaining_size == 0) {
if (partial) // only makes sense if marked as complete
2022-04-14 18:22:35 +02:00
{
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
2022-05-04 11:54:22 +02:00
return false;
2022-04-14 18:22:35 +02:00
}
2022-07-19 11:49:18 +02:00
if (field_hash_finalize(field_ptr, data, data_length) == false) {
2022-07-07 17:52:01 +02:00
return false;
}
2022-07-19 11:49:18 +02:00
} else {
if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial
2022-04-14 18:22:35 +02:00
{
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
2022-05-04 11:54:22 +02:00
return false;
2022-04-14 18:22:35 +02:00
}
handle_eip712_return_code(true);
}
2022-05-04 11:54:22 +02:00
return true;
2022-05-02 15:30:14 +02:00
}
2022-07-19 11:49:18 +02:00
#endif // HAVE_EIP712_FULL_SUPPORT