diff --git a/common/protob/messages-stellar.proto b/common/protob/messages-stellar.proto index 8970a3300..fe38e7272 100644 --- a/common/protob/messages-stellar.proto +++ b/common/protob/messages-stellar.proto @@ -59,6 +59,7 @@ message StellarSignTx { optional uint64 memo_id = 12; // 8-byte uint64 optional bytes memo_hash = 13; // 32 bytes representing a hash required uint32 num_operations = 14; // number of operations in this transaction + optional uint32 soroban_data_size = 60[default=0]; // soroban transaction // https://github.com/stellar/stellar-core/blob/02d26858069de7c0eefe065056fb0a19bf72ea56/src/xdr/Stellar-transaction.x#L506-L513 enum StellarMemoType { @@ -85,6 +86,7 @@ message StellarSignTx { * @next StellarAccountMergeOp * @next StellarManageDataOp * @next StellarBumpSequenceOp + * @next StellarInvokeHostFunctionOp */ message StellarTxOpRequest { } @@ -268,6 +270,44 @@ message StellarBumpSequenceOp { required uint64 bump_to = 2; // new sequence number } +/** + * Request: ask device to confirm this operation type + * @next StellarSorobanDataRequest + * @next StellarSignedTx + */ +message StellarInvokeHostFunctionOp { + optional string source_account = 1; // (optional) source account address + required string contract_address = 2; // contract id string + required string function_name = 3; // invoked contract function name (SCSymbol, max 32 bytes) + required uint32 call_args_xdr_size = 4; // the total size of call args xdr + required bytes call_args_xdr_initial_chunk = 5; // invokecontract call args xdr bytes + required uint32 soroban_auth_xdr_size = 6; + required bytes soroban_auth_xdr_initial_chunk = 7; // soroban authorization entries xdr +} + +/** + * Response: device is ready for client to send the soroban data + * @next StellarSorobanDataAck + */ +message StellarSorobanDataRequest { + required StellarRequestType type = 1; + required uint32 data_length = 2; // Number of bytes being requested (<= 1024) + + enum StellarRequestType { + CALL = 0; + AUTH = 1; + EXT = 2; + } +} + +/** + * Request: ask device to confirm + * @next StellarSignedTx + */ +message StellarSorobanDataAck { + required bytes data_chunk_xdr = 1; // the soroban data in xdr format +} + /** * Response: signature for transaction * @end @@ -276,3 +316,26 @@ message StellarSignedTx { required bytes public_key = 1; // public key for the private key used to sign data required bytes signature = 2; // signature suitable for sending to the Stellar network } + +/** + * Request: ask device to sign Stellar message + * @start + * @next StellarSignedTx + */ +message StellarSignMessage { + repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/148'/index' + required bytes message = 2; +} + +/** + * Request: ask device to sign Sorban address authorization + * @start + * @next StellarSignedTx + */ +message StellarSignAuthorization { + repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/148'/index' + required string network_passphrase = 2; // passphrase for signing messages on the destination network + required uint64 nonce = 3; // used to protect from replay attack + required uint32 expiration = 4; // the ledger sequence number on which the signature expires + required bytes invocation = 5; // invocation tree xdr +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 9004361e4..6a4c12917 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -283,6 +283,9 @@ enum MessageType { MessageType_StellarManageBuyOfferOp = 222 [(wire_in) = true]; MessageType_StellarPathPaymentStrictSendOp = 223 [(wire_in) = true]; MessageType_StellarSignedTx = 230 [(wire_out) = true]; + MessageType_StellarInvokeHostFunctionOp = 260 [(wire_in) = true]; + MessageType_StellarSorobanDataRequest = 261 [(wire_out) = true]; + MessageType_StellarSorobanDataAck = 262 [(wire_in) = true, (wire_no_fsm) = true]; // Cardano // dropped Sign/VerifyMessage ids 300-302 diff --git a/legacy/firmware/alephium.c b/legacy/firmware/alephium.c index 7e844707a..85b5c31ee 100644 --- a/legacy/firmware/alephium.c +++ b/legacy/firmware/alephium.c @@ -17,7 +17,6 @@ bool alephium_get_address(const AlephiumGetAddress *msg, } void alephium_sign_tx(const HDNode *node, const AlephiumSignTx *msg) { - char log_buffer[1024]; memcpy(&global_node, node, sizeof(HDNode)); alephium_data_total_size = msg->data_initial_chunk.size; memcpy(alephium_data_buffer, msg->data_initial_chunk.bytes, @@ -34,9 +33,6 @@ void alephium_sign_tx(const HDNode *node, const AlephiumSignTx *msg) { alephium_data_total_size = msg->data_length; alephium_data_left = alephium_data_total_size - msg->data_initial_chunk.size; - snprintf(log_buffer, sizeof(log_buffer), - "Requesting more data chunks, total size: %zu, data left: %zu", - (size_t)alephium_data_total_size, (size_t)alephium_data_left); alephium_send_request_chunk(); } else { if (alephium_data_buffer[2] == 1) { diff --git a/legacy/firmware/config.c b/legacy/firmware/config.c index cf969c7b7..72919c5f1 100644 --- a/legacy/firmware/config.c +++ b/legacy/firmware/config.c @@ -148,6 +148,7 @@ static const uint8_t TRUE_BYTE = '\x01'; static bool derive_cardano = 0; static bool session_seed_cached_btc = false; static bool session_seed_cached_cardano = false; +bool reset_after_usb_lock = false; static secbool usb_lock = secfalse; @@ -270,8 +271,10 @@ void config_init(void) { se_set_ui_callback(&layoutProgressAdapter); // Restore safetyCheckLevel from soft reset if preserved - uint16_t preserved_level = soft_reset_get_preserved_data(); - if (preserved_level != PRESERVED_RESET_DATA_INVALID) { + uint16_t preserved_data = soft_reset_get_preserved_data(); + if (preserved_data != PRESERVED_RESET_DATA_INVALID) { + uint8_t preserved_level = (uint8_t)(preserved_data & 0x03); + reset_after_usb_lock = (bool)(preserved_data & 0x04); if (preserved_level == SafetyCheckLevel_PromptTemporarily) { safetyCheckLevel = (SafetyCheckLevel)preserved_level; } diff --git a/legacy/firmware/ethereum_onekey.c b/legacy/firmware/ethereum_onekey.c index 7997799c4..fc83a5132 100644 --- a/legacy/firmware/ethereum_onekey.c +++ b/legacy/firmware/ethereum_onekey.c @@ -2689,16 +2689,34 @@ static void ethereum_gnosis_safe_tx_sign( fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); return; } - display_info_init(&display_info, 2); - prepare_domain_items(&display_info, ack); + if (!display_info_init(&display_info, 2)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display items"); + return; + } + if (!prepare_domain_items(&display_info, ack)) { + display_info_cleanup(&display_info); + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to prepare display items"); + return; + } if (!layoutTypedData(&display_info, TYPE_NAME_DOMAIN)) { display_info_cleanup(&display_info); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); return; } display_info_cleanup(&display_info); - display_info_init(&display_info, 10); - prepare_safe_items(&display_info, ack); + if (!display_info_init(&display_info, 10)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display items"); + return; + } + if (!prepare_safe_items(&display_info, ack)) { + display_info_cleanup(&display_info); + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to prepare display items"); + return; + } if (!layoutTypedData(&display_info, "SafeTx")) { display_info_cleanup(&display_info); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); @@ -2727,7 +2745,7 @@ static void ethereum_gnosis_safe_tx_sign( void ethereum_typed_data_sign_onekey(const EthereumSignTypedDataOneKey *msg, const HDNode *node, EthereumTypedDataSignatureOneKey *resp) { - if (strncmp(msg->primary_type, "SafeTx", strlen("SafeTx")) == 0) { + if (strcmp(msg->primary_type, "SafeTx") == 0) { EthereumGnosisSafeTxRequest request = {0}; const EthereumGnosisSafeTxAck *ack = call(MessageType_MessageType_EthereumGnosisSafeTxRequest, &request, @@ -2791,8 +2809,7 @@ void ethereum_typed_data_sign_onekey(const EthereumSignTypedDataOneKey *msg, } display_info_cleanup(&display_info); bool has_message_hash = true; - if (strncmp(envelope.primary_type, TYPE_NAME_DOMAIN, - strlen(TYPE_NAME_DOMAIN)) == 0) { + if (strcmp(envelope.primary_type, TYPE_NAME_DOMAIN) == 0) { has_message_hash = false; } uint8_t message_hash[32] = {0}; diff --git a/legacy/firmware/ethereum_typed_data.h b/legacy/firmware/ethereum_typed_data.h index 8c15d004d..c918f9a6f 100644 --- a/legacy/firmware/ethereum_typed_data.h +++ b/legacy/firmware/ethereum_typed_data.h @@ -94,15 +94,15 @@ static bool display_item_set_name(DisplayItem *item, const char *name, int name_intent) { if (!item || !name) return false; + size_t len = strlen(name); + char *new_name = malloc(len + 1); + if (!new_name) return false; + + strcpy(new_name, name); if (item->name) { free(item->name); } - - size_t len = strlen(name); - item->name = malloc(len + 1); - if (!item->name) return false; - - strcpy(item->name, name); + item->name = new_name; item->name_len = len; item->name_intent = name_intent; return true; @@ -111,15 +111,15 @@ static bool display_item_set_name(DisplayItem *item, const char *name, static bool display_item_set_value(DisplayItem *item, const char *value) { if (!item || !value) return false; + size_t len = strlen(value); + char *new_value = malloc(len + 1); + if (!new_value) return false; + + strcpy(new_value, value); if (item->value) { free(item->value); } - - size_t len = strlen(value); - item->value = malloc(len + 1); - if (!item->value) return false; - - strcpy(item->value, value); + item->value = new_value; item->value_len = len; return true; } @@ -162,6 +162,7 @@ static bool display_info_add_item_name(DisplayInfo *info, const char *name, if (!info || !name) return false; if (info->items_count >= info->items_capacity) { + if (info->items_capacity > 127) return false; uint8_t new_capacity = info->items_capacity * 2; DisplayItem *new_items = realloc(info->items, sizeof(DisplayItem) * new_capacity); @@ -207,9 +208,10 @@ typedef struct { EthereumTypedDataStruct types[2]; uint8_t dependent_types_count; uint8_t dependent_types_capacity; - EthereumTypedDataStruct dependent_types[8]; - EthereumFieldTypeOneKey entry_types[8]; + EthereumTypedDataStruct dependent_types[10]; + EthereumFieldTypeOneKey entry_types[24]; uint8_t entry_types_count; + uint8_t entry_types_capacity; uint8_t current_name_intent; } TypedDataEnvelope; extern void *call(const MessageType req_type, const void *msg_ptr, @@ -220,13 +222,19 @@ static void TypedDataEnvelope_init(TypedDataEnvelope *envelope, uint8_t primary_type_len, bool metamask_v4_compat) { memset(envelope, 0, sizeof(TypedDataEnvelope)); + if (primary_type_len >= sizeof(envelope->primary_type)) { + primary_type_len = sizeof(envelope->primary_type) - 1; + } strncpy(envelope->primary_type, primary_type, primary_type_len); + envelope->primary_type[primary_type_len] = '\0'; envelope->metamask_v4_compat = metamask_v4_compat; envelope->dependent_types_capacity = sizeof(envelope->dependent_types) / sizeof(EthereumTypedDataStruct); envelope->dependent_types_count = 0; envelope->primary_type_len = primary_type_len; envelope->entry_types_count = 0; + envelope->entry_types_capacity = + sizeof(envelope->entry_types) / sizeof(EthereumFieldTypeOneKey); envelope->current_name_intent = 0; memset(envelope->entry_types, 0, sizeof(envelope->entry_types)); } @@ -234,20 +242,42 @@ static void TypedDataEnvelope_init(TypedDataEnvelope *envelope, static const EthereumTypedDataStruct *TypedDataEnvelope_find_dependent_type( const TypedDataEnvelope *envelope, const char *type_name, uint8_t type_name_len) { + (void)type_name_len; for (uint8_t i = 0; i < envelope->dependent_types_count; i++) { - if (strncmp(envelope->dependent_types[i].name, type_name, type_name_len) == - 0) { + if (strcmp(envelope->dependent_types[i].name, type_name) == 0) { return &envelope->dependent_types[i]; } } return NULL; } +static bool resolve_entry_type(TypedDataEnvelope *envelope, + EthereumTypedDataStructAckOneKey *dst_type, + uint8_t member_index) { + const EthereumFieldTypeOneKey *member_type = + &dst_type->members[member_index].type; + while (member_type->data_type == EthereumDataTypeOneKey_ARRAY && + member_type->entry_type != NULL) { + member_type = member_type->entry_type; + if (envelope->entry_types_count >= envelope->entry_types_capacity) { + return false; + } + envelope->entry_types[envelope->entry_types_count] = *member_type; + envelope->entry_types[envelope->entry_types_count].entry_type = NULL; + dst_type->members[member_index].type.entry_type = + &envelope->entry_types[envelope->entry_types_count]; + envelope->entry_types_count++; + } + return true; +} + static bool TypedDataEnvelope_add_dependent_type( TypedDataEnvelope *envelope, const char *type_name, uint8_t type_name_len, const EthereumTypedDataStructAckOneKey *type) { if (envelope->dependent_types_count >= envelope->dependent_types_capacity) { return false; + } else if (envelope->entry_types_count >= envelope->entry_types_capacity) { + return false; } const EthereumTypedDataStruct *dependent_type = TypedDataEnvelope_find_dependent_type(envelope, type_name, type_name_len); @@ -258,19 +288,11 @@ static bool TypedDataEnvelope_add_dependent_type( .name[type_name_len] = '\0'; envelope->dependent_types[envelope->dependent_types_count].type = *type; for (uint8_t i = 0; i < type->members_count; i++) { - const EthereumStructMemberOneKey *member = &type->members[i]; - const EthereumFieldTypeOneKey *member_type = &member->type; - while (member_type->data_type == EthereumDataTypeOneKey_ARRAY) { - member_type = member_type->entry_type; - if (member_type != NULL) { - envelope->entry_types[envelope->entry_types_count] = *member_type; - envelope->entry_types[envelope->entry_types_count].entry_type = NULL; - envelope->dependent_types[envelope->dependent_types_count] - .type.members[i] - .type.entry_type = - &envelope->entry_types[envelope->entry_types_count]; - envelope->entry_types_count++; - } + if (!resolve_entry_type( + envelope, + &envelope->dependent_types[envelope->dependent_types_count].type, + i)) { + return false; } } envelope->dependent_types_count++; @@ -285,17 +307,8 @@ static bool TypedDataEnvelope_add_domain_type( strncpy(envelope->types[0].name, TYPE_NAME_DOMAIN, strlen(TYPE_NAME_DOMAIN)); envelope->types[0].name[strlen(TYPE_NAME_DOMAIN)] = '\0'; for (uint8_t i = 0; i < domain_type->members_count; i++) { - const EthereumStructMemberOneKey *member = &domain_type->members[i]; - const EthereumFieldTypeOneKey *member_type = &member->type; - while (member_type->data_type == EthereumDataTypeOneKey_ARRAY) { - member_type = member_type->entry_type; - if (member_type != NULL) { - envelope->entry_types[envelope->entry_types_count] = *member_type; - envelope->entry_types[envelope->entry_types_count].entry_type = NULL; - envelope->types[0].type.members[i].type.entry_type = - &envelope->entry_types[envelope->entry_types_count]; - envelope->entry_types_count++; - } + if (!resolve_entry_type(envelope, &envelope->types[0].type, i)) { + return false; } } return true; @@ -309,17 +322,8 @@ static bool TypedDataEnvelope_add_primary_type( envelope->primary_type_len); envelope->types[1].name[envelope->primary_type_len] = '\0'; for (uint8_t i = 0; i < primary_type->members_count; i++) { - const EthereumStructMemberOneKey *member = &primary_type->members[i]; - const EthereumFieldTypeOneKey *member_type = &member->type; - while (member_type->data_type == EthereumDataTypeOneKey_ARRAY) { - member_type = member_type->entry_type; - if (member_type != NULL) { - envelope->entry_types[envelope->entry_types_count] = *member_type; - envelope->entry_types[envelope->entry_types_count].entry_type = NULL; - envelope->types[1].type.members[i].type.entry_type = - &envelope->entry_types[envelope->entry_types_count]; - envelope->entry_types_count++; - } + if (!resolve_entry_type(envelope, &envelope->types[1].type, i)) { + return false; } } return true; @@ -327,10 +331,9 @@ static bool TypedDataEnvelope_add_primary_type( static const EthereumTypedDataStruct *TypedDataEnvelope_find_type( const TypedDataEnvelope *envelope, const char *type_name, uint8_t type_name_len) { - if (strncmp(type_name, TYPE_NAME_DOMAIN, strlen(TYPE_NAME_DOMAIN)) == 0) { + if (strcmp(type_name, TYPE_NAME_DOMAIN) == 0) { return &envelope->types[0]; - } else if (strncmp(type_name, envelope->primary_type, - envelope->primary_type_len) == 0) { + } else if (strcmp(type_name, envelope->primary_type) == 0) { return &envelope->types[1]; } return TypedDataEnvelope_find_dependent_type(envelope, type_name, @@ -339,18 +342,18 @@ static const EthereumTypedDataStruct *TypedDataEnvelope_find_type( static bool TypedDataEnvelope_add_type( TypedDataEnvelope *envelope, const char *type_name, uint8_t type_name_len, const EthereumTypedDataStructAckOneKey *type) { - if (strncmp(type_name, TYPE_NAME_DOMAIN, strlen(TYPE_NAME_DOMAIN)) == 0) { + if (strcmp(type_name, TYPE_NAME_DOMAIN) == 0) { return TypedDataEnvelope_add_domain_type(envelope, type); - } else if (strncmp(type_name, envelope->primary_type, type_name_len) == 0) { + } else if (strcmp(type_name, envelope->primary_type) == 0) { return TypedDataEnvelope_add_primary_type(envelope, type); } else { return TypedDataEnvelope_add_dependent_type(envelope, type_name, type_name_len, type); } - return false; } static void write_rightpad32(BufferWriter *w, const uint8_t *value, const uint8_t value_len) { + if (value_len > 32) return; uint8_t padding[32] = {0}; memcpy(padding, value, value_len); write_bytes(padding, 32, w); @@ -358,6 +361,7 @@ static void write_rightpad32(BufferWriter *w, const uint8_t *value, static void write_leftpad32(BufferWriter *w, const uint8_t *value, const uint8_t value_len, bool is_signed) { + if (value_len > 32) return; uint8_t padding[32]; if (is_signed && value[0] & 0x80) { memset(padding, 0xFF, 32); @@ -540,7 +544,7 @@ static char *decode_typed_data(const uint8_t *data, uint16_t data_len, const char *type_name) { char *result = NULL; if (strncmp(type_name, "bytes", 5) == 0) { - result = malloc(48); + result = malloc(64); if (!result) return NULL; result[0] = '0'; result[1] = 'x'; @@ -560,7 +564,7 @@ static char *decode_typed_data(const uint8_t *data, uint16_t data_len, strncpy(result, (char *)data, data_len); result[data_len] = '\0'; } else if (strncmp(type_name, "address", 7) == 0) { - result = malloc(data_len * 2 + 1); + result = malloc(data_len * 2 + 3); if (!result) return NULL; result[0] = '0'; result[1] = 'x'; @@ -633,10 +637,10 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, fsm_sendFailure(FailureType_Failure_DataError, "Failed to find type"); return false; } - uint32_t member_value_path[16] = {0}; + uint32_t member_value_path[6] = {0}; memcpy(member_value_path, member_path, member_path_len * sizeof(uint32_t)); member_path_len++; - char current_parent_objects[16][64] = {0}; + char current_parent_objects[6][64] = {0}; for (uint8_t i = 0; i < parent_objects_len; i++) { strncpy(current_parent_objects[i], parent_objects[i], strlen(parent_objects[i])); @@ -645,18 +649,26 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, for (uint8_t i = 0; i < type->type.members_count; i++) { const EthereumStructMemberOneKey *member = &type->type.members[i]; member_value_path[member_path_len - 1] = i; + char temp_field_name[68] = {0}; char *field_name = (char *)member->name; const EthereumFieldTypeOneKey *field_type = &member->type; if (name_intent != 0) { - char temp_field_name[68] = {0}; snprintf(temp_field_name, 68, "[%s]", field_name); field_name = temp_field_name; } - display_info_add_item_name(&display_info, field_name, name_intent); + if (!display_info_add_item_name(&display_info, field_name, name_intent)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display item"); + return false; + } if (field_type->data_type == EthereumDataTypeOneKey_STRUCT) { strncpy(current_parent_objects[parent_objects_len - 1], field_name, strlen(field_name)); - display_info_set_value(&display_info, field_type->struct_name); + if (!display_info_set_value(&display_info, field_type->struct_name)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display value"); + return false; + } if (!hash_struct(envelope, field_type->struct_name, strlen(field_type->struct_name), member_value_path, member_path_len, name_intent + 4, current_parent_objects, @@ -681,13 +693,24 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, if (!get_type_name(field_type, field_type_str, &field_type_str_len)) { return false; } - display_info_set_value(&display_info, field_type_str); + if (!display_info_set_value(&display_info, field_type_str)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display value"); + return false; + } strncpy(current_parent_objects[parent_objects_len - 1], field_name, strlen(field_name)); BufferWriter arr_w = {0}; - uint8_t arr_buffer[256] = {0}; + uint8_t arr_buffer[768] = {0}; init_buffer_writer(&arr_w, arr_buffer, sizeof(arr_buffer)); - uint32_t el_member_value_path[16] = {0}; + if ((entry_type->data_type != EthereumDataTypeOneKey_STRUCT || + envelope->metamask_v4_compat) && + array_size > sizeof(arr_buffer) / 32) { + fsm_sendFailure(FailureType_Failure_DataError, + "Array too large for typed data"); + return false; + } + uint32_t el_member_value_path[6] = {0}; uint8_t el_member_value_path_len = member_path_len; memcpy(el_member_value_path, member_value_path, el_member_value_path_len * sizeof(uint32_t)); @@ -726,13 +749,26 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, } char array_item_str[80] = {0}; snprintf(array_item_str, 80, "%s[%" PRIu32 "]", field_name, j); - display_info_add_item_name(&display_info, array_item_str, - name_intent + 4); + if (!display_info_add_item_name(&display_info, array_item_str, + name_intent + 4)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display item"); + return false; + } char *array_item_value_str = decode_typed_data( value, value_len, TYPE_TRANSLATION_DICT[entry_type->data_type - 1]); - display_info_set_value(&display_info, array_item_value_str); - free(array_item_value_str); + if (array_item_value_str == NULL) { + return false; + } else { + if (!display_info_set_value(&display_info, array_item_value_str)) { + free(array_item_value_str); + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display value"); + return false; + } + free(array_item_value_str); + } } } keccak_256(arr_w.buffer, arr_w.position, w->buffer + w->position); @@ -749,8 +785,17 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, } char *field_value_str = decode_typed_data( value, value_len, TYPE_TRANSLATION_DICT[field_type->data_type - 1]); - display_info_set_value(&display_info, field_value_str); - free(field_value_str); + if (field_value_str == NULL) { + return false; + } else { + if (!display_info_set_value(&display_info, field_value_str)) { + free(field_value_str); + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to allocate display value"); + return false; + } + free(field_value_str); + } } } return true; @@ -758,40 +803,43 @@ static bool get_and_encode_data(const TypedDataEnvelope *envelope, static void find_typed_dependencies(const TypedDataEnvelope *envelope, const char *type_name, uint8_t type_name_len, char (*results)[64], - uint8_t *results_count) { + uint8_t *results_count, + uint8_t max_results) { const EthereumTypedDataStruct *type = TypedDataEnvelope_find_type(envelope, type_name, type_name_len); if (type == NULL) { return; } for (uint8_t i = 0; i < *results_count; i++) { - if (strncmp(results[i], type_name, type_name_len) == 0) { + if (strcmp(results[i], type_name) == 0) { return; } } + if (*results_count >= max_results) return; strncpy(results[*results_count], type_name, type_name_len); results[*results_count][type_name_len] = '\0'; (*results_count)++; for (uint8_t i = 0; i < type->type.members_count; i++) { const EthereumStructMemberOneKey *member = &type->type.members[i]; const EthereumFieldTypeOneKey *member_type = &member->type; - while (member_type->data_type == EthereumDataTypeOneKey_ARRAY) { + while (member_type->data_type == EthereumDataTypeOneKey_ARRAY && + member_type->entry_type != NULL) { member_type = member_type->entry_type; } if (member_type->data_type == EthereumDataTypeOneKey_STRUCT) { find_typed_dependencies(envelope, member_type->struct_name, strlen(member_type->struct_name), results, - results_count); + results_count, max_results); } } } static bool encode_type(const TypedDataEnvelope *envelope, BufferWriter *w, const char *type_name, uint8_t type_name_len) { - char deps[16][64] = {0}; + char deps[12][64] = {0}; uint8_t deps_count = 0; - find_typed_dependencies(envelope, type_name, type_name_len, deps, - &deps_count); + find_typed_dependencies(envelope, type_name, type_name_len, deps, &deps_count, + 12); if (deps_count > 1) { qsort(deps + 1, deps_count - 1, 64, compare_strings); } @@ -849,7 +897,7 @@ static bool encode_type(const TypedDataEnvelope *envelope, BufferWriter *w, static bool hash_type(const TypedDataEnvelope *envelope, BufferWriter *w, const char *type_name, uint8_t type_name_len) { BufferWriter type_w = {0}; - uint8_t buffer[1024] = {0}; + uint8_t buffer[2048] = {0}; init_buffer_writer(&type_w, buffer, sizeof(buffer)); if (!encode_type(envelope, &type_w, type_name, type_name_len)) { return false; @@ -864,7 +912,7 @@ static bool hash_struct(const TypedDataEnvelope *envelope, uint8_t name_intent, const char (*parent_objects)[64], uint8_t parent_objects_len, uint8_t *digest) { BufferWriter w = {0}; - uint8_t struct_buffer[1024] = {0}; + uint8_t struct_buffer[608] = {0}; init_buffer_writer(&w, struct_buffer, sizeof(struct_buffer)); if (!hash_type(envelope, &w, type_name, type_name_len)) { return false; @@ -966,14 +1014,17 @@ static bool _collect_types(TypedDataEnvelope *envelope, const char *type_name, if (!validate_field_type(member_type)) { return false; } - while (member_type->data_type == EthereumDataTypeOneKey_ARRAY) { + while (member_type->data_type == EthereumDataTypeOneKey_ARRAY && + member_type->entry_type != NULL) { member_type = member_type->entry_type; } if (member_type->data_type == EthereumDataTypeOneKey_STRUCT && TypedDataEnvelope_find_type(envelope, member_type->struct_name, strlen(member_type->struct_name)) == NULL) { - _collect_types(envelope, member_type->struct_name, - strlen(member_type->struct_name)); + if (!_collect_types(envelope, member_type->struct_name, + strlen(member_type->struct_name))) { + return false; + } } } return true; @@ -992,33 +1043,7 @@ static bool collect_types(TypedDataEnvelope *envelope) { extern void layout_index_count(int index, int count); extern void drawScrollbar(int pages, int index); -static const char *truncate_text_for_display(const char *text, - uint8_t max_lines) { - if (!text || max_lines == 0) return ""; - - size_t text_len = strlen(text); - size_t chars_per_line = 21; - size_t max_chars = max_lines * chars_per_line; - - static char truncated_value[64]; - memzero(truncated_value, sizeof(truncated_value)); - if (text_len > max_chars) { - size_t full_lines = max_lines - 1; - size_t full_chars = full_lines * chars_per_line; - strncpy(truncated_value, text, full_chars); - truncated_value[full_chars] = '\n'; - memcpy(truncated_value + full_chars + 1, "...", 3); - size_t remaining_chars = chars_per_line - 3; - size_t start_pos = text_len - remaining_chars; - strncpy(truncated_value + full_chars + 4, text + start_pos, - remaining_chars); - } else { - return text; - } - - return truncated_value; -} static bool layoutTypedData(DisplayInfo *display_context, const char *primary_type) { bool result = false; @@ -1165,79 +1190,172 @@ static bool layoutSafeTx(bool is_delegate_call, const uint8_t *domain_hash, HANDLE_KEY(bubble_key); return true; } -static void prepare_domain_items(DisplayInfo *info, +static bool prepare_domain_items(DisplayInfo *info, const EthereumGnosisSafeTxAck *ack) { - display_info_add_item_name(info, "chainId", 0); + if (!display_info_add_item_name(info, "chainId", 0)) { + return false; + } uint8_t chain_id_bytes[8] = {0}; for (int i = 0; i < 8; i++) { chain_id_bytes[7 - i] = (ack->chain_id >> (i * 8)) & 0xFF; } char *chain_id_str = decode_typed_data(chain_id_bytes, 8, "uint"); - display_info_set_value(info, chain_id_str); + if (chain_id_str == NULL) { + return false; + } + if (!display_info_set_value(info, chain_id_str)) { + free(chain_id_str); + return false; + } free(chain_id_str); - display_info_add_item_name(info, "verifyingContract", 0); + if (!display_info_add_item_name(info, "verifyingContract", 0)) { + return false; + } uint8_t verifying_contract_bytes[20] = {0}; ethereum_parse_onekey(ack->verifyingContract, verifying_contract_bytes); char *verifying_contract_str = decode_typed_data(verifying_contract_bytes, 20, "address"); - display_info_set_value(info, verifying_contract_str); + if (verifying_contract_str == NULL) { + return false; + } + if (!display_info_set_value(info, verifying_contract_str)) { + free(verifying_contract_str); + return false; + } free(verifying_contract_str); + return true; } -static void prepare_safe_items(DisplayInfo *info, +static bool prepare_safe_items(DisplayInfo *info, const EthereumGnosisSafeTxAck *ack) { - display_info_add_item_name(info, "to", 0); + if (!display_info_add_item_name(info, "to", 0)) { + return false; + } uint8_t to_bytes[20] = {0}; ethereum_parse_onekey(ack->to, to_bytes); char *to_str = decode_typed_data(to_bytes, 20, "address"); - display_info_set_value(info, to_str); + if (to_str == NULL) { + return false; + } + if (!display_info_set_value(info, to_str)) { + free(to_str); + return false; + } free(to_str); - display_info_add_item_name(info, "value", 0); + if (!display_info_add_item_name(info, "value", 0)) { + return false; + } char *value_str = decode_typed_data(ack->value.bytes, ack->value.size, "uint"); - display_info_set_value(info, value_str); + if (value_str == NULL) { + return false; + } + if (!display_info_set_value(info, value_str)) { + free(value_str); + return false; + } free(value_str); - display_info_add_item_name(info, "data", 0); + if (!display_info_add_item_name(info, "data", 0)) { + return false; + } char *data_str = decode_typed_data(ack->data.bytes, ack->data.size, "bytes"); - display_info_set_value(info, data_str); + if (data_str == NULL) { + return false; + } + if (!display_info_set_value(info, data_str)) { + free(data_str); + return false; + } free(data_str); - display_info_add_item_name(info, "operation", 0); - if (ack->operation == EthereumGnosisSafeTxOperation_DELEGATE_CALL) { - display_info_set_value(info, "1(DELEGATECALL)"); - } else { - display_info_set_value(info, "0(CALL)"); + if (!display_info_add_item_name(info, "operation", 0)) { + return false; + } + if (!display_info_set_value( + info, ack->operation == EthereumGnosisSafeTxOperation_DELEGATE_CALL + ? "1(DELEGATECALL)" + : "0(CALL)")) { + return false; + } + if (!display_info_add_item_name(info, "safeTxGas", 0)) { + return false; } - display_info_add_item_name(info, "safeTxGas", 0); char *safeTxGas_str = decode_typed_data(ack->safeTxGas.bytes, ack->safeTxGas.size, "uint"); - display_info_set_value(info, safeTxGas_str); + if (safeTxGas_str == NULL) { + return false; + } + if (!display_info_set_value(info, safeTxGas_str)) { + free(safeTxGas_str); + return false; + } free(safeTxGas_str); - display_info_add_item_name(info, "baseGas", 0); + if (!display_info_add_item_name(info, "baseGas", 0)) { + return false; + } char *baseGas_str = decode_typed_data(ack->baseGas.bytes, ack->baseGas.size, "uint"); - display_info_set_value(info, baseGas_str); + if (baseGas_str == NULL) { + return false; + } + if (!display_info_set_value(info, baseGas_str)) { + free(baseGas_str); + return false; + } free(baseGas_str); - display_info_add_item_name(info, "gasPrice", 0); + if (!display_info_add_item_name(info, "gasPrice", 0)) { + return false; + } char *gasPrice_str = decode_typed_data(ack->gasPrice.bytes, ack->gasPrice.size, "uint"); - display_info_set_value(info, gasPrice_str); + if (gasPrice_str == NULL) { + return false; + } + if (!display_info_set_value(info, gasPrice_str)) { + free(gasPrice_str); + return false; + } free(gasPrice_str); - display_info_add_item_name(info, "gasToken", 0); + if (!display_info_add_item_name(info, "gasToken", 0)) { + return false; + } uint8_t gas_token_bytes[20] = {0}; ethereum_parse_onekey(ack->gasToken, gas_token_bytes); char *gasToken_str = decode_typed_data(gas_token_bytes, 20, "address"); - display_info_set_value(info, gasToken_str); + if (gasToken_str == NULL) { + return false; + } + if (!display_info_set_value(info, gasToken_str)) { + free(gasToken_str); + return false; + } free(gasToken_str); - display_info_add_item_name(info, "refundReceiver", 0); + if (!display_info_add_item_name(info, "refundReceiver", 0)) { + return false; + } uint8_t refund_receiver_bytes[20] = {0}; ethereum_parse_onekey(ack->refundReceiver, refund_receiver_bytes); char *refundReceiver_str = decode_typed_data(refund_receiver_bytes, 20, "address"); - display_info_set_value(info, refundReceiver_str); + if (refundReceiver_str == NULL) { + return false; + } + if (!display_info_set_value(info, refundReceiver_str)) { + free(refundReceiver_str); + return false; + } free(refundReceiver_str); - display_info_add_item_name(info, "nonce", 0); + if (!display_info_add_item_name(info, "nonce", 0)) { + return false; + } char *nonce_str = decode_typed_data(ack->nonce.bytes, ack->nonce.size, "uint"); - display_info_set_value(info, nonce_str); + if (nonce_str == NULL) { + return false; + } + if (!display_info_set_value(info, nonce_str)) { + free(nonce_str); + return false; + } free(nonce_str); + return true; } #endif /* __ETHEREUM_TYPED_DATA_H__ */ diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index 3dba3a7b5..77b0a2796 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -201,7 +201,7 @@ void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg); void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg); void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg); void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg); - +void fsm_msgStellarInvokeHostFunctionOp(const StellarInvokeHostFunctionOp *msg); void fsm_msgRebootToBootloader(void); bool fsm_layoutSignMessage(const char *chain_name, const char *signer, diff --git a/legacy/firmware/fsm_msg_alephium.h b/legacy/firmware/fsm_msg_alephium.h index ba58510f0..71aba3a46 100644 --- a/legacy/firmware/fsm_msg_alephium.h +++ b/legacy/firmware/fsm_msg_alephium.h @@ -54,7 +54,6 @@ void fsm_msgAlephiumSignTx(const AlephiumSignTx *msg) { COIN_TYPE, SECP256K1_NAME, true), "Invalid path"); CHECK_PIN - RESP_INIT(AlephiumSignedTx); HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index a39fae319..d42477c76 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -420,16 +420,16 @@ void fsm_msgChangePin(const ChangePin *msg) { if (config_hasPin()) { layoutDialogCenterAdapterV2( NULL, &bmp_icon_warning, &bmp_bottom_left_close, - &bmp_bottom_right_confirm, NULL, NULL, __("Do you really want to"), - __("change current PIN?"), NULL, NULL, NULL); + &bmp_bottom_right_confirm, NULL, NULL, NULL, NULL, NULL, NULL, + _(C__DO_YOU_REALLY_WANT_TO_CHANGE_CURRENT_PIN_QUES)); } else { if (g_bIsBixinAPP) { button_confirm = false; } else { layoutDialogCenterAdapterV2( NULL, &bmp_icon_warning, &bmp_bottom_left_close, - &bmp_bottom_right_confirm, NULL, NULL, __("Do you really want to"), - __("set new PIN?"), NULL, NULL, NULL); + &bmp_bottom_right_confirm, NULL, NULL, NULL, NULL, NULL, NULL, + _(C__DO_YOU_REALLY_WANT_TO_SET_NEW_PIN_QUES)); } } } diff --git a/legacy/firmware/fsm_msg_stellar.h b/legacy/firmware/fsm_msg_stellar.h index 308948533..1ee30a3b9 100644 --- a/legacy/firmware/fsm_msg_stellar.h +++ b/legacy/firmware/fsm_msg_stellar.h @@ -215,3 +215,11 @@ void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) { GO_AHEAD } + +void fsm_msgStellarInvokeHostFunctionOp( + const StellarInvokeHostFunctionOp *msg) { + CHECK_UNLOCKED + if (!stellar_confirmInvokeHostFunctionOp(msg)) return; + + GO_AHEAD +} diff --git a/legacy/firmware/i18n/i18n.h b/legacy/firmware/i18n/i18n.h index d291f5d84..1642ad0ce 100644 --- a/legacy/firmware/i18n/i18n.h +++ b/legacy/firmware/i18n/i18n.h @@ -5,7 +5,7 @@ #include #include "keys.h" -#define I18N_ITEMS_COUNT 455 +#define I18N_ITEMS_COUNT 454 #define I18N_LANGUAGE_ITEMS 8 typedef enum { diff --git a/legacy/firmware/i18n/keys.h b/legacy/firmware/i18n/keys.h index d9eee985b..e2116961f 100644 --- a/legacy/firmware/i18n/keys.h +++ b/legacy/firmware/i18n/keys.h @@ -737,8 +737,9 @@ #define FIDO_2_LIST_CREDENTIALS 351 // Remove Credentials #define FIDO_2_REMOVE_CREDENTIALS 352 -// Security Keys +// Security Key #define FIDO_FIDO_KEYS_LABEL 353 +#define FIDO_DISABLE_PROMPT_TITLE 353 // Secure accounts with security keys #define FIDO_FIDO_KEYS_DESC 354 // Remove Security Key? @@ -779,176 +780,174 @@ #define GLOBAL_UNKNOWN_TOKEN 371 // Enable Security Keys #define FIDO_ENABLE_FIDO_KEYS 372 -// Security Key -#define FIDO_DISABLE_PROMPT_TITLE 373 // Credentials will be unavailable when disabled. Proceed? -#define FIDO_DISABLE_PROMPT_DESC 374 +#define FIDO_DISABLE_PROMPT_DESC 373 // Vote count -#define GLOBAL__VOTE_COUNT 375 +#define GLOBAL__VOTE_COUNT 374 // This transaction contains a non-zero OP_RETURN value. Once confirmed, your f // unds will be permanently locked. -#define TITLE__OP_RETURN_DESC 376 +#define TITLE__OP_RETURN_DESC 375 // To (Token Account): -#define LIST_KEY__TO_TOKEN_ACCOUNT__COLON 377 +#define LIST_KEY__TO_TOKEN_ACCOUNT__COLON 376 // From (Token Account): -#define LIST_KEY__FROM_TOKEN_ACCOUNT__COLON 378 +#define LIST_KEY__FROM_TOKEN_ACCOUNT__COLON 377 // Revoking EIP-7702 delegation will disable smart account features. -#define I_REVOKE_SMART_ACCOUNT_WARNING 379 +#define I_REVOKE_SMART_ACCOUNT_WARNING 378 // Revoke 7702 Delegation -#define T_REVOKE_SMART_ACCOUNT_DELEGATION 380 +#define T_REVOKE_SMART_ACCOUNT_DELEGATION 379 // Revoke on Network: -#define I_REVOKE_ON_NETWORK 381 +#define I_REVOKE_ON_NETWORK 380 // 7702 delegation blocked: smart contract not on the whitelist. -#define I_NOT_IN_SMART_ACCOUNT_WHITELIST 382 +#define I_NOT_IN_SMART_ACCOUNT_WHITELIST 381 // 7702 Upgrade -#define T_UPGRADE_SMART_ACCOUNT 383 +#define T_UPGRADE_SMART_ACCOUNT 382 // {} 7702 upgrade requested. Sign only if you understand the risks. -#define I_UPGRADING_SMART_ACCOUNT_WARNING 384 +#define I_UPGRADING_SMART_ACCOUNT_WARNING 383 // Delegate to: -#define I_DELEGATE_TO 385 +#define I_DELEGATE_TO 384 // Delegate on Network: -#define I_DELEGATE_ON_NETWORK 386 +#define I_DELEGATE_ON_NETWORK 385 // Invalid phrase! You can edit a single word or start over -#define C__INVALID_PHRASE_YOU_CAN_EDIT_A_SINGLE_WORD_OR_START_OVER 387 +#define C__INVALID_PHRASE_YOU_CAN_EDIT_A_SINGLE_WORD_OR_START_OVER 386 // Edit -#define O__EDIT 388 +#define O__EDIT 387 // Start Over -#define O__START_OVER 389 +#define O__START_OVER 388 // Edit Word #{} -#define T__EDIT_WORD_STR 390 +#define T__EDIT_WORD_STR 389 // Attach to PIN -#define M__ATTACH_TO_PIN 391 -#define T__ATTACH_TO_PIN 391 +#define M__ATTACH_TO_PIN 390 +#define T__ATTACH_TO_PIN 390 // Disable Passphrase? Hidden wallet PIN won’t unlock your device -#define C__DISABLE_PASSPHRASE_HIDDEN_WALLET_PIN_WILL_NOT_UNLOCK_YOUR_DEVICE 392 +#define C__DISABLE_PASSPHRASE_HIDDEN_WALLET_PIN_WILL_NOT_UNLOCK_YOUR_DEVICE 391 // By setting up different PIN codes linked to different Passphrases, you can e // asily access different wallets. Just enter a specific PIN, and you’ll open t // he wallet that’s tied to it -#define C__ATTACH_TO_PIN_DESC 393 +#define C__ATTACH_TO_PIN_DESC 392 // Set Hidden PIN -#define T__SET_HIDDEN_PIN 394 +#define T__SET_HIDDEN_PIN 393 // Hit the limit: 3 PINs max! Do you want to remove some hidden wallet PINs? -#define C__HIT_THE_LIMIT_30_PINS_MAX 395 +#define C__HIT_THE_LIMIT_30_PINS_MAX 394 // Attach Passphrase -#define T__ATTACH_PASSPHRASE 396 +#define T__ATTACH_PASSPHRASE 395 // PIN has attached one Passphrase! You can remove this PIN or set a new Passph // rase -#define C__PIN_HAS_ATTACHED_ONE_PASSPHRASE 397 +#define C__PIN_HAS_ATTACHED_ONE_PASSPHRASE 396 // Remove this PIN -#define O__REMOVE_THIS_PIN 398 +#define O__REMOVE_THIS_PIN 397 // Set a new Passphrase -#define O__SET_A_NEW_PASSPHRASE 399 +#define O__SET_A_NEW_PASSPHRASE 398 // You can attach a Passphrase to this PIN -#define C__YOU_CAN_ATTACH_A_PASSPHRASE_TO_THIS_PIN 400 +#define C__YOU_CAN_ATTACH_A_PASSPHRASE_TO_THIS_PIN 399 // You understand that the Passphrase PIN is an alternative login for your hidd // en wallet -#define C__PASSPHRASE__ATTACH_ONE_PASSPHRASE_DESC1 401 +#define C__PASSPHRASE__ATTACH_ONE_PASSPHRASE_DESC1 400 // You know that if you forget the Passphrase, you might lose your assets -#define C__PASSPHRASE__ATTACH_ONE_PASSPHRASE_DESC2 402 +#define C__PASSPHRASE__ATTACH_ONE_PASSPHRASE_DESC2 401 // Forget your passphrase word will lose access to funds linked to your hidden // wallet -#define C__PASSPHRASE_SAVE_DESC 403 +#define C__PASSPHRASE_SAVE_DESC 402 // Passphrase Set and Attached to PIN! -#define C__PASSPHRASE_SET_AND_ATTACHED_TO_PIN 404 +#define C__PASSPHRASE_SET_AND_ATTACHED_TO_PIN 403 // Next, please enter the standard wallet PIN -#define C__NEXT_PLEASE_ENTER_THE_STANDARD_WALLET_PIN 405 +#define C__NEXT_PLEASE_ENTER_THE_STANDARD_WALLET_PIN 404 // Standard PIN -#define T__STANDARD_PIN 406 +#define T__STANDARD_PIN 405 // Next, please enter the hidden wallet PIN you want to remove -#define C__NEXT_PLEASE_ENTER_THE_HIDDEN_WALLET_PIN_YOU_WANT_TO_REMOVE 407 +#define C__NEXT_PLEASE_ENTER_THE_HIDDEN_WALLET_PIN_YOU_WANT_TO_REMOVE 406 // Enter Hidden PIN -#define T__ENTER_HIDDEN_PIN 408 +#define T__ENTER_HIDDEN_PIN 407 // Remove this PIN? You won’t be able to use it to unlock the device -#define C__REMOVE_PIN_YOU_WILL_NOT_BE_ABLE_TO_USE_IT_TO_UNLOCK_THE_DEVICE 409 +#define C__REMOVE_PIN_YOU_WILL_NOT_BE_ABLE_TO_USE_IT_TO_UNLOCK_THE_DEVICE 408 // PIN removed! -#define C__PIN_REMOVED 410 +#define C__PIN_REMOVED 409 // You do not have Passphrase turned on -#define C__YOU_DO_NOT_HAVE_PASSPHRASE_TURNED_ON 411 +#define C__YOU_DO_NOT_HAVE_PASSPHRASE_TURNED_ON 410 // PIN Already Used! Do you want to overwrite it? -#define C__PIN_ALREADY_USED_DO_YOU_WANT_TO_OVERWRITE_IT 412 +#define C__PIN_ALREADY_USED_DO_YOU_WANT_TO_OVERWRITE_IT 411 // PIN already used! Please try a different one -#define C__PIN_ALREADY_USED_PLEASE_TRY_A_DIFFERENT_ONE 413 +#define C__PIN_ALREADY_USED_PLEASE_TRY_A_DIFFERENT_ONE 412 // TRON Energy Rental -#define T_ENERGY_RENTAL 414 +#define T_ENERGY_RENTAL 413 // Recipient is a known energy rental service provider address. -#define I_ENERGY_RENTAL 415 +#define I_ENERGY_RENTAL 414 // Confirm Phrase -#define T__CONFIRM_PHRASE 416 +#define T__CONFIRM_PHRASE 415 // Save Passphrase -#define T__SAVE_PASSPHRASE 417 +#define T__SAVE_PASSPHRASE 416 // Remove PIN -#define T__REMOVE_PIN 418 -// Management Security Key -#define M__MANAGEMENT_SECURITY_KEY 419 +#define T__REMOVE_PIN 417 +// Manage Security Key +#define M__MANAGEMENT_SECURITY_KEY 418 // Incorrect PIN! The PIN you entered is incorrect -#define C__INCORRECT_PIN_THE_PIN_YOU_ENTERED_IS_INCORRECT 420 +#define C__INCORRECT_PIN_THE_PIN_YOU_ENTERED_IS_INCORRECT 419 // Lock the screen? -#define C__LOCK_THE_SCREEN 421 +#define C__LOCK_THE_SCREEN 420 // Confirm Typed Data -#define T_CONFIRM_TYPED_DATA 422 +#define T_CONFIRM_TYPED_DATA 421 // {} authorization. Check dApp trust first. -#define I_TYPED_DATA_AUTHORIZATION_WARNING 423 +#define I_TYPED_DATA_AUTHORIZATION_WARNING 422 // Review Struct: -#define I_REVIEW_STRUCT 424 +#define I_REVIEW_STRUCT 423 // Tips: Press “ ” both at once to switch case, numbers, symbols. -#define C__TIPS_PRESS_BOTH_AT_ONCE_TO_SWITCH_CASE_NUMBERS_SYMBOLS 425 +#define C__TIPS_PRESS_BOTH_AT_ONCE_TO_SWITCH_CASE_NUMBERS_SYMBOLS 424 // Safe Transaction -#define T_CONFIRM_SAFE_TX 426 +#define T_CONFIRM_SAFE_TX 425 // Delegatecall may let external code affect your wallet. -#define I_SAFE_DELEGATE_WARNING 427 +#define I_SAFE_DELEGATE_WARNING 426 // View execTransaction -#define I_VIEW_EXEC_TRANSACTION 428 +#define I_VIEW_EXEC_TRANSACTION 427 // Next, enter the PIN you want to change -#define C__NEXT_ENTER_THE_PIN_YOU_WANT_TO_CHANGE 429 +#define C__NEXT_ENTER_THE_PIN_YOU_WANT_TO_CHANGE 428 // Request Confirmation -#define T_CONFIRM_REQUEST 430 +#define T_CONFIRM_REQUEST 429 // Approve {} -#define I_APPROVE_TOKEN 431 +#define I_APPROVE_TOKEN 430 // Revoke {} -#define I_REVOKE_TOKEN 432 +#define I_REVOKE_TOKEN 431 // for {} -#define I_AUTHORIZATION_PROVIDER 433 +#define I_AUTHORIZATION_PROVIDER 432 // Full access to this asset. Trust the dApp before continuing. -#define I_UNLIMITED_AUTHORIZATION_BANNER 434 +#define I_UNLIMITED_AUTHORIZATION_BANNER 433 // Approve to: -#define I_APPROVE_TO 435 +#define I_APPROVE_TO 434 // Revoke from: -#define I_REVOKE_FROM 436 +#define I_REVOKE_FROM 435 // Token Address: -#define I_TOKEN_ADDRESS 437 +#define I_TOKEN_ADDRESS 436 // Approve unlimited {} -#define I_APPROVE_UNLIMITED_TOKEN 438 +#define I_APPROVE_UNLIMITED_TOKEN 437 // Revoke unlimited {} -#define I_REVOKE_UNLIMITED_TOKEN 439 +#define I_REVOKE_UNLIMITED_TOKEN 438 // Do you want to change language? -#define C__CHANGE_LANGUAGE 440 +#define C__CHANGE_LANGUAGE 439 // Do you want to change auto lock time? -#define C__CHANGE_AUTO_LOCK 441 +#define C__CHANGE_AUTO_LOCK 440 // Do you want to change auto shutdown time? -#define C__CHANGE_AUTO_SHUTDOWN 442 +#define C__CHANGE_AUTO_SHUTDOWN 441 // Confirm Pairing -#define T__CONFIRM_PAIRING 443 +#define T__CONFIRM_PAIRING 442 // Pair failed -#define C__PAIR_FAILED 444 +#define C__PAIR_FAILED 443 // Device paired -#define C__DEVICE_PAIRED 445 +#define C__DEVICE_PAIRED 444 // Memo {}: -#define I_MEMO_TEXT 446 +#define I_MEMO_TEXT 445 // Tx Source: -#define I_TX_SOURCE 447 +#define I_TX_SOURCE 446 // Asset Issuer: -#define I_ASSET_ISSUER 448 +#define I_ASSET_ISSUER 447 // Sequence Number: -#define I_SEQUENCE_NUMBER 449 +#define I_SEQUENCE_NUMBER 448 // Missing Memo/Tag during exchange deposits may result in fund loss. -#define I_MISSING_MEMO_WARNING 450 +#define I_MISSING_MEMO_WARNING 449 // Add Trust: -#define I_ADD_TRUST 451 +#define I_ADD_TRUST 450 // Source Account: -#define I_SOURCE_ACCOUNT 452 +#define I_SOURCE_ACCOUNT 451 // Remove Trust: -#define I_REMOVE_TRUST 453 +#define I_REMOVE_TRUST 452 // Trust Account: -#define I_TRUST_ACCOUNT 454 +#define I_TRUST_ACCOUNT 453 #endif // clang-format on diff --git a/legacy/firmware/i18n/locales/de.inc b/legacy/firmware/i18n/locales/de.inc index 0857f9bf5..9d691384d 100644 --- a/legacy/firmware/i18n/locales/de.inc +++ b/legacy/firmware/i18n/locales/de.inc @@ -86,7 +86,7 @@ const char *const languages_de[] = { "Standard-Eingaberichtung", "Eingaberichtung auf Standard zurücksetzen?", "PIN ändern", - "Wiederherstellungsphrase prüfen", + "Wdh.-Phrase prüfen", "Passphrase", "Gerät zurücksetzen", " um zurückzugehen", @@ -352,7 +352,7 @@ const char *const languages_de[] = { "Nachweis importieren", "Anmeldedaten anzeigen", "Anmeldedaten entfernen", - "Sicherheitsschlüssel", + "Sich.-Schlüssel", "Sichere Konten mit Sicherheitsschlüsseln", "Schlüssel entfernen?", "Dieser Sicherheitsschlüssel wird dauerhaft entfernt.", @@ -372,7 +372,6 @@ const char *const languages_de[] = { "Stimme entfernen", "Unbekanntes Token", "Security Key aktivieren", - "Sicherheitsschlüssel", "Anmeldedaten sind nicht verfügbar, wenn sie deaktiviert sind. Fortfahren?", "Stimmenanzahl", "Diese Transaktion enthält einen OP_RETURN-Wert. Nach Bestätigung sind Ihre Gelder dauerhaft gesperrt.", @@ -418,7 +417,7 @@ const char *const languages_de[] = { "Phrase bestätigen", "Passphrase speichern", "PIN entfernen", - "Verwaltungsschlüssel", + "Sich.-Schl. verwalten", "Falsche PIN! Die von Ihnen eingegebene PIN ist falsch.", "Den Bildschirm sperren?", "Eingegebene Daten bestätigen", diff --git a/legacy/firmware/i18n/locales/en.inc b/legacy/firmware/i18n/locales/en.inc index a0ef1614e..1bab00be7 100644 --- a/legacy/firmware/i18n/locales/en.inc +++ b/legacy/firmware/i18n/locales/en.inc @@ -352,7 +352,7 @@ const char *const languages_en[] = { "Import Credential", "List Credentials", "Remove Credentials", - "Security Keys", + "Security Key", "Secure accounts with security keys", "Remove Security Key?", "This security key will be removed permanently.", @@ -372,7 +372,6 @@ const char *const languages_en[] = { "Remove vote", "Unknown Token", "Enable Security Keys", - "Security Key", "Credentials will be unavailable when disabled. Proceed?", "Vote count", "This transaction contains a non-zero OP_RETURN value. Once confirmed, your funds will be permanently locked.", @@ -418,7 +417,7 @@ const char *const languages_en[] = { "Confirm Phrase", "Save Passphrase", "Remove PIN", - "Management Security Key", + "Manage Security Key", "Incorrect PIN! The PIN you entered is incorrect", "Lock the screen?", "Confirm Typed Data", diff --git a/legacy/firmware/i18n/locales/es.inc b/legacy/firmware/i18n/locales/es.inc index ef1c2c460..1c3d5cb64 100644 --- a/legacy/firmware/i18n/locales/es.inc +++ b/legacy/firmware/i18n/locales/es.inc @@ -86,7 +86,7 @@ const char *const languages_es[] = { "Dirección de entrada predeterminada", "¿Deseas restaurar la dirección de entrada a la predeterminada?", "Cambiar PIN", - "Veríf. frase de recuperación", + "Verif. frase recup.", "Passphrase", "Restablecer Dispositivo", " para volver", @@ -352,7 +352,7 @@ const char *const languages_es[] = { "Importar credencial", "Listar credenciales", "Eliminar credenciales", - "Claves", + "Clave seg.", "Proteja cuentas con claves de seguridad", "¿Eliminar clave?", "Esta clave de seguridad se eliminará de forma permanente.", @@ -372,7 +372,6 @@ const char *const languages_es[] = { "Eliminar voto", "Token desconocido", "Habilitar claves de seguridad", - "Claves", "Las credenciales no estarán disponibles cuando estén desactivadas. ¿Desea continuar?", "Recuento de votos", "Esta transacción tiene un valor OP_RETURN distinto de cero. Tus fondos quedarán bloqueados permanentemente.", @@ -418,7 +417,7 @@ const char *const languages_es[] = { "Confirmar frase", "Guardar Passphrase", "Eliminar PIN", - "Clave de Seguridad", + "Gest. clave seg.", "¡PIN incorrecto! El PIN que ingresaste es incorrecto", "¿Bloquear la pantalla?", "Confirmar datos tipados", diff --git a/legacy/firmware/i18n/locales/ja.inc b/legacy/firmware/i18n/locales/ja.inc index 20db001d3..d1beaa285 100644 --- a/legacy/firmware/i18n/locales/ja.inc +++ b/legacy/firmware/i18n/locales/ja.inc @@ -352,7 +352,7 @@ const char *const languages_ja[] = { "クレデンシャル取込", "クレデンシャルを一覧表示", "認証情報を削除", - "セキュリティ・キー", + "セキュリティキー", "セキュリティ・キーでアカウントを保護", "セキュリティキーを削除?", "このセキュリティキーは完全に削除されます。", @@ -372,7 +372,6 @@ const char *const languages_ja[] = { "投票を削除", "不明なトークン", "セキュリティキーを有効化", - "セキュリティキー", "無効にすると認証情報が利用できなくなります。続行しますか?", "票数", "この取引にはゼロ以外のOP_RETURN値が含まれています。一度承認されると、あなたの資金は永久にロックされます。", @@ -418,7 +417,7 @@ const char *const languages_ja[] = { "フレーズの確認", "パスフレーズを保存", "PINを削除", - "管理セキュリティキー", + "セキュリティキー管理", "PINが正しくありません!入力したPINが間違っています。", "画面をロックしますか?", "型付きデータ確認", @@ -453,5 +452,5 @@ const char *const languages_ja[] = { "信頼を追加:", "ソースアカウント:", "信頼を削除:", - "信託口座:", + "発行元アドレス:", }; diff --git a/legacy/firmware/i18n/locales/ko_kr.inc b/legacy/firmware/i18n/locales/ko_kr.inc index 1b4560cba..d83ccde1d 100644 --- a/legacy/firmware/i18n/locales/ko_kr.inc +++ b/legacy/firmware/i18n/locales/ko_kr.inc @@ -372,7 +372,6 @@ const char *const languages_ko_kr[] = { "투표 제거", "알 수 없는 토큰", "보안 키 활성화", - "보안 키", "비활성화하면 자격 증명을 사용할 수 없어요. 계속할까요?", "투표 수", "이 거래에는 0이 아닌 OP_RETURN 값이 포함되어 있어요. 승인되면 자금이 영구적으로 잠겨요.", @@ -418,7 +417,7 @@ const char *const languages_ko_kr[] = { "문구 확인", "Passphrase 저장", "PIN 제거", - "관리 보안 키", + "보안 키 관리", "잘못된 PIN입니다! 입력하신 PIN이 올바르지 않습니다.", "화면을 잠그시겠습니까?", "입력된 데이터 확인", diff --git a/legacy/firmware/i18n/locales/pt_br.inc b/legacy/firmware/i18n/locales/pt_br.inc index c9e73687a..ee2e8fdf4 100644 --- a/legacy/firmware/i18n/locales/pt_br.inc +++ b/legacy/firmware/i18n/locales/pt_br.inc @@ -352,7 +352,7 @@ const char *const languages_pt_br[] = { "Importar Credencial", "Listar Credenciais", "Remover Credenciais", - "Chave", + "Chave seg.", "Proteja contas com chaves de segurança", "Remover chave?", "Esta chave de segurança será removida permanentemente.", @@ -372,7 +372,6 @@ const char *const languages_pt_br[] = { "Remover voto", "Token Desconhecido", "Habilitar chaves de segurança", - "Chave de segurança", "As credenciais ficarão indisponíveis quando desabilitadas. Prosseguir?", "Contagem de votos", "Esta transação tem um valor OP_RETURN diferente de zero. Após a confirmação, seus fundos ficarão permanentemente bloqueados.", @@ -418,7 +417,7 @@ const char *const languages_pt_br[] = { "Confirmar Passphrase", "Salvar Passphrase", "Remover PIN", - "Chave de Segurança", + "Ger. chave seg.", "PIN incorreto! O PIN que você inseriu está incorreto", "Bloquear a tela?", "Confirmar dados", diff --git a/legacy/firmware/i18n/locales/zh_cn.inc b/legacy/firmware/i18n/locales/zh_cn.inc index 4c86241a5..19d687ada 100644 --- a/legacy/firmware/i18n/locales/zh_cn.inc +++ b/legacy/firmware/i18n/locales/zh_cn.inc @@ -372,7 +372,6 @@ const char *const languages_zh_cn[] = { "移除投票", "未知代币", "启用安全密钥", - "安全密钥", "禁用后凭据将不可用。是否继续?", "票数", "此交易包含非零 OP_RETURN 值。一旦确认,您的资金将被永久锁定。", @@ -453,5 +452,5 @@ const char *const languages_zh_cn[] = { "添加信任:", "源账户:", "移除信任:", - "信托账户:", + "发行人账户:", }; diff --git a/legacy/firmware/i18n/locales/zh_tw.inc b/legacy/firmware/i18n/locales/zh_tw.inc index 17b576e6e..cf5f4d474 100644 --- a/legacy/firmware/i18n/locales/zh_tw.inc +++ b/legacy/firmware/i18n/locales/zh_tw.inc @@ -372,7 +372,6 @@ const char *const languages_zh_tw[] = { "移除投票", "未知代幣", "啟用安全金鑰", - "安全金鑰", "停用時將無法使用憑證。是否繼續?", "票數", "此交易包含非零的 OP_RETURN 值。一旦確認,您的資金將被永久鎖定。", @@ -453,5 +452,5 @@ const char *const languages_zh_tw[] = { "新增信任:", "來源帳戶:", "移除信任:", - "信託帳戶:", + "發行人帳戶:", }; diff --git a/legacy/firmware/layout2.c b/legacy/firmware/layout2.c index b058a44f8..4feba75c1 100644 --- a/legacy/firmware/layout2.c +++ b/legacy/firmware/layout2.c @@ -2265,7 +2265,7 @@ void layoutCosiSign(const uint32_t *address_n, size_t address_n_count, layoutDialogSwipe(&bmp_icon_question, __("Cancel"), __("Confirm"), desc, str[0], str[1], str[2], str[3], NULL, NULL); } - +extern bool reset_after_usb_lock; void layoutHomeInfo(void) { uint8_t key = KEY_NULL; key = keyScan(); @@ -2296,6 +2296,9 @@ void layoutHomeInfo(void) { if (layoutLast == onboarding) { onboarding(key); } else { + if (reset_after_usb_lock && key != KEY_NULL) { + reset_after_usb_lock = false; + } layoutEnterSleep(0); if (layoutNeedRefresh()) { layoutHome(); @@ -3971,8 +3974,12 @@ void layoutAboutCertifications(int num) { bool layoutEnterSleep(int mode) { #if !EMULATOR static uint32_t system_millis_logo_refresh = 0; - - if (config_getSleepDelayMs() > 0) { + if (reset_after_usb_lock) { + if (timer_get_sleep_count() >= 30000) { + reset_after_usb_lock = false; + enter_sleep(); + } + } else if (config_getSleepDelayMs() > 0) { if (timer_get_sleep_count() >= config_getSleepDelayMs()) { if (mode) { return true; diff --git a/legacy/firmware/menu_list.c b/legacy/firmware/menu_list.c index de1ff3829..82eee48d1 100644 --- a/legacy/firmware/menu_list.c +++ b/legacy/firmware/menu_list.c @@ -685,7 +685,7 @@ static struct menu fido_switch_set_menu = { .start = 0, .current = 0, .counts = COUNT_OF(fido_switch_set_menu_items), - .title = "Enable Security Keys", + .title = "Security Key", .items = (struct menu_item *)fido_switch_set_menu_items, .previous = &main_menu, }; @@ -905,8 +905,8 @@ static const struct menu_item main_menu_items[] = { #if !BITCOIN_ONLY {"Security Key", NULL, false, .sub_menu = &fido_switch_set_menu, menu_para_fido_switch, false, menu_para_fido_switch_index}, - {"Management Security Key", NULL, true, menu_fido2_resident_credential, - NULL, false, NULL}, + {"Manage Security Key", NULL, true, menu_fido2_resident_credential, NULL, + false, NULL}, #endif }; diff --git a/legacy/firmware/messages.c b/legacy/firmware/messages.c index ee921b1b8..0849e9b54 100644 --- a/legacy/firmware/messages.c +++ b/legacy/firmware/messages.c @@ -329,6 +329,9 @@ void msg_read_common(char type, const uint8_t *buf, uint32_t len) { case MessageType_MessageType_EthereumGnosisSafeTxAck: fields = EthereumGnosisSafeTxAck_fields; break; + case MessageType_MessageType_StellarSorobanDataAck: + fields = StellarSorobanDataAck_fields; + break; #endif default: msg_pos = 0; diff --git a/legacy/firmware/protob/messages-ethereum-eip712-onekey.options b/legacy/firmware/protob/messages-ethereum-eip712-onekey.options index 74b8f3f2b..f5d8c5868 100644 --- a/legacy/firmware/protob/messages-ethereum-eip712-onekey.options +++ b/legacy/firmware/protob/messages-ethereum-eip712-onekey.options @@ -8,7 +8,7 @@ EthereumStructMemberOneKey.name max_size:64 EthereumFieldTypeOneKey.struct_name max_size:64 EthereumFieldTypeOneKey.entry_type type: FT_POINTER -EthereumTypedDataValueRequestOneKey.member_path max_count:5 +EthereumTypedDataValueRequestOneKey.member_path max_count:6 EthereumTypedDataValueAckOneKey.value max_size:1536 diff --git a/legacy/firmware/protob/messages-stellar.options b/legacy/firmware/protob/messages-stellar.options index 1e26c4827..a3a0bee74 100755 --- a/legacy/firmware/protob/messages-stellar.options +++ b/legacy/firmware/protob/messages-stellar.options @@ -51,5 +51,12 @@ StellarManageDataOp.value max_size:65 StellarBumpSequenceOp.source_account max_size:57 +StellarInvokeHostFunctionOp.source_account max_size:57 +StellarInvokeHostFunctionOp.contract_address max_size:57 +StellarInvokeHostFunctionOp.function_name max_size:33 +StellarInvokeHostFunctionOp.call_args_xdr_initial_chunk max_size:1024 +StellarInvokeHostFunctionOp.soroban_auth_xdr_initial_chunk max_size:1024 +StellarSorobanDataAck.data_chunk_xdr max_size:1024 + StellarSignedTx.public_key max_size:32 StellarSignedTx.signature max_size:64 # ed25519 signatures are 64 bytes, this does not include the hint diff --git a/legacy/firmware/stellar.c b/legacy/firmware/stellar.c index 65b962250..a7cf71975 100644 --- a/legacy/firmware/stellar.c +++ b/legacy/firmware/stellar.c @@ -55,7 +55,34 @@ static bool stellar_signing = false; static StellarTransaction stellar_activeTx; static bool memo_type_none = false; static CONFIDENTIAL HDNode *stellar_node = NULL; -#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0]) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +#define STELLAR_OP_TYPE_INVOKE_HOST_FUNCTION 24 +#define STELLAR_HOST_FUNCTION_TYPE_INVOKE_CONTRACT 0 +#define STELLAR_TX_EXT_SOROBAN 1 +extern void *call(const MessageType req_type, const void *msg_ptr, + const MessageType expected_response_type); +static void stellar_signingFail(const char *reason, bool user_cancel); + +static void stellar_hashupdate_transaction_ext(void) { + if (stellar_activeTx.soroban_data_size == 0) { + stellar_hashupdate_uint32(0); + return; + } +} + +static bool stellar_confirmSorobanWarning(void) { + layoutDialogCenterAdapterV2( + NULL, &bmp_icon_warning, &bmp_bottom_left_close, &bmp_bottom_right_arrow, + NULL, NULL, NULL, NULL, NULL, NULL, + _(C__UNBALE_TO_DECODE_TX_DATA_SIGN_AT_YOUR_OWN_RISK_EXCLAM)); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingFail("User canceled", true); + return false; + } + return true; +} + /* * Starts the signing process and parses the transaction header */ @@ -76,6 +103,21 @@ bool stellar_signingInit(const StellarSignTx *msg) { // Copy some data into the active tx stellar_activeTx.num_operations = msg->num_operations; + bool is_soroban_tx = msg->soroban_data_size > 0; + if (is_soroban_tx) { + if (msg->num_operations != 1) { + fsm_sendFailure(FailureType_Failure_DataError, + "Soroban requires single operation"); + return false; + } + if (msg->memo_type != StellarMemoType_NONE) { + fsm_sendFailure(FailureType_Failure_DataError, + "Soroban requires MEMO_NONE"); + return false; + } + + stellar_activeTx.soroban_data_size = msg->soroban_data_size; + } // Start building what will be signed: // sha256 of: @@ -1187,6 +1229,12 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) { // } // memzero(rows, sizeof(rows)); // row_idx = 0; + // Hash: signer type + stellar_hashupdate_uint32(msg->signer_type); + // key + stellar_hashupdate_bytes(msg->signer_key.bytes, 32); + // weight + stellar_hashupdate_uint32(msg->signer_weight); } const char *const expected_keys[] = { msg->has_inflation_destination_account ? "Inflation Account" : NULL, @@ -1219,12 +1267,6 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) { stellar_signingFail("User canceled", true); return false; } - // Hash: signer type - stellar_hashupdate_uint32(msg->signer_type); - // key - stellar_hashupdate_bytes(msg->signer_key.bytes, 32); - // weight - stellar_hashupdate_uint32(msg->signer_weight); return true; } @@ -1264,8 +1306,7 @@ bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) { // Validate destination account and convert to bytes uint8_t asset_issuer_bytes[STELLAR_KEY_SIZE] = {0}; if (!stellar_getAddressBytes(msg->asset.issuer, asset_issuer_bytes)) { - stellar_signingFail("User canceled", true); - fsm_sendFailure(FailureType_Failure_ProcessError, "Invalid asset issuer"); + stellar_signingFail("Invalid asset issuer", false); return false; } @@ -1547,13 +1588,163 @@ bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) { return true; } +static bool request_chunk(StellarRequestType type, uint32_t req_len, + StellarSorobanDataAck **ack) { + const StellarSorobanDataRequest soroban_data_req = { + .type = type, + .data_length = req_len, + }; + *ack = call(MessageType_MessageType_StellarSorobanDataRequest, + &soroban_data_req, MessageType_MessageType_StellarSorobanDataAck); + if (*ack == NULL) { + stellar_signingFail("Invalid soroban data", false); + return false; + } + return true; +} + +static bool hash_requested_chunks(StellarRequestType type, uint32_t data_left, + SHA256_CTX *hex_str_ctx, + const char *error_message) { + StellarSorobanDataAck *ack = NULL; + + while (data_left > 0) { + if (!request_chunk(type, data_left >= 1024 ? 1024 : data_left, &ack)) { + return false; + } + if (ack->data_chunk_xdr.size == 0 || ack->data_chunk_xdr.size > data_left) { + stellar_signingFail(error_message, false); + return false; + } + stellar_hashupdate_bytes(ack->data_chunk_xdr.bytes, + ack->data_chunk_xdr.size); + sha256_Update(hex_str_ctx, ack->data_chunk_xdr.bytes, + ack->data_chunk_xdr.size); + data_left -= ack->data_chunk_xdr.size; + } + + return true; +} + +bool stellar_confirmInvokeHostFunctionOp( + const StellarInvokeHostFunctionOp *msg) { + if (!stellar_signing) return false; + if (stellar_activeTx.soroban_data_size <= 0) { + stellar_signingFail("Missing Soroban tx data", false); + return false; + } + + if (msg->function_name[0] == '\0' || msg->call_args_xdr_size <= 0 || + msg->soroban_auth_xdr_size <= 0 || + msg->call_args_xdr_initial_chunk.size > msg->call_args_xdr_size || + msg->soroban_auth_xdr_initial_chunk.size > msg->soroban_auth_xdr_size) { + stellar_signingFail("Invalid Soroban invoke payload", false); + return false; + } + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingFail("Source account error", false); + return false; + } + + stellar_hashupdate_uint32(STELLAR_OP_TYPE_INVOKE_HOST_FUNCTION); + stellar_hashupdate_uint32(STELLAR_HOST_FUNCTION_TYPE_INVOKE_CONTRACT); + uint8_t bytes[32] = {0}; + if (!stellar_getContractAddressBytes(msg->contract_address, bytes)) { + stellar_signingFail("Invalid contract address", false); + return false; + } + stellar_hashupdate_contract_address(bytes); + + stellar_hashupdate_string((const uint8_t *)msg->function_name, + strnlen(msg->function_name, 32)); + + char source_account[57] = {0}; + char contract_address[57] = {0}; + bool has_source_account = msg->has_source_account; + char function_name[33] = {0}; + if (has_source_account) { + memcpy(source_account, msg->source_account, sizeof(source_account)); + } + memcpy(function_name, msg->function_name, sizeof(function_name)); + memcpy(contract_address, msg->contract_address, sizeof(contract_address)); + + uint32_t soroban_auth_xdr_size = msg->soroban_auth_xdr_size; + uint32_t soroban_auth_xdr_initial_size = + msg->soroban_auth_xdr_initial_chunk.size; + uint8_t soroban_auth_xdr_initial_chunk[1024] = {0}; + memcpy(soroban_auth_xdr_initial_chunk, + msg->soroban_auth_xdr_initial_chunk.bytes, + soroban_auth_xdr_initial_size); + + stellar_hashupdate_bytes(msg->call_args_xdr_initial_chunk.bytes, + msg->call_args_xdr_initial_chunk.size); + uint32_t call_args_xdr_left = + msg->call_args_xdr_size - msg->call_args_xdr_initial_chunk.size; + uint8_t data_hash[32] = {0}; + char args_hash[65] = {0}; + SHA256_CTX hex_str_ctx = {0}; + sha256_Init(&hex_str_ctx); + sha256_Update(&hex_str_ctx, msg->call_args_xdr_initial_chunk.bytes, + msg->call_args_xdr_initial_chunk.size); + if (!hash_requested_chunks(StellarRequestType_CALL, call_args_xdr_left, + &hex_str_ctx, "Invalid soroban call args")) { + return false; + } + sha256_Final(&hex_str_ctx, data_hash); + data2hex(data_hash, sizeof(data_hash), args_hash); + + char auth_hash[65] = {0}; + sha256_Init(&hex_str_ctx); + stellar_hashupdate_bytes(soroban_auth_xdr_initial_chunk, + soroban_auth_xdr_initial_size); + sha256_Update(&hex_str_ctx, soroban_auth_xdr_initial_chunk, + soroban_auth_xdr_initial_size); + uint32_t auth_data_xdr_left = + soroban_auth_xdr_size - soroban_auth_xdr_initial_size; + + if (!hash_requested_chunks(StellarRequestType_AUTH, auth_data_xdr_left, + &hex_str_ctx, "Invalid soroban auth data")) { + return false; + } + sha256_Final(&hex_str_ctx, data_hash); + data2hex(data_hash, sizeof(data_hash), auth_hash); + + char soroban_data_hash[65] = {0}; + // update soroban transaction ext + stellar_hashupdate_uint32(STELLAR_TX_EXT_SOROBAN); + uint32_t soroban_data_left = stellar_activeTx.soroban_data_size; + sha256_Init(&hex_str_ctx); + if (!hash_requested_chunks(StellarRequestType_EXT, soroban_data_left, + &hex_str_ctx, "Invalid soroban data")) { + return false; + } + sha256_Final(&hex_str_ctx, data_hash); + data2hex(data_hash, sizeof(data_hash), soroban_data_hash); + + { + const char *const expected_keys[] = { + "Contract", "Function", "Args Hash", + "Auths Hash", "Ext Hash", has_source_account ? "Source Account" : NULL}; + const char *const values[] = {contract_address, function_name, + args_hash, auth_hash, + soroban_data_hash, source_account}; + + stellar_activeTx.confirmed_operations++; + if (!stellar_layoutTransactionDialog(ARRAY_SIZE(expected_keys), + expected_keys, values)) { + stellar_signingFail("User canceled", true); + return false; + } + } + return true; +} + /** * Populates the fields of resp with the signature of the active transaction */ void stellar_fillSignedTx(StellarSignedTx *resp) { - // Finalize the transaction by hashing 4 null bytes representing a (currently - // unused) empty union - stellar_hashupdate_uint32(0); + stellar_hashupdate_transaction_ext(); // Add the public key for verification that the right account was used for // signing @@ -1562,14 +1753,12 @@ void stellar_fillSignedTx(StellarSignedTx *resp) { // Add the signature (note that this does not include the 4-byte hint since it // can be calculated from the public key) - uint8_t signature[64] = {0}; // Note: this calls sha256_Final on the hash context - stellar_getSignatureForActiveTx(signature); - memcpy(resp->signature.bytes, signature, sizeof(signature)); - resp->signature.size = sizeof(signature); + stellar_getSignatureForActiveTx(resp->signature.bytes); + resp->signature.size = 64; } -bool stellar_allOperationsConfirmed() { +bool stellar_allOperationsConfirmed(void) { return stellar_activeTx.confirmed_operations == stellar_activeTx.num_operations; } @@ -1591,7 +1780,6 @@ void stellar_getSignatureForActiveTx(uint8_t *out_signature) { // that have been read so far uint8_t to_sign[32] = {0}; sha256_Final(&(stellar_activeTx.sha256_ctx), to_sign); - uint8_t signature[64] = {0}; #if EMULATOR ed25519_sign(to_sign, sizeof(to_sign), stellar_node->private_key, signature); @@ -1712,7 +1900,7 @@ void stellar_format_asset(const StellarAsset *asset, char *str_formatted, // Validate issuer account for non-native assets if (asset->type != StellarAssetType_NATIVE && - !stellar_validateAddress(asset->issuer)) { + !stellar_validateAddress(asset->issuer, 0x30)) { stellar_signingFail("Invalid asset issuer", false); return; } @@ -1779,7 +1967,7 @@ size_t stellar_publicAddressAsStr(const uint8_t *bytes, char *out, * Note that the stellar "seed" (private key) also uses this format except the * version byte is 0xC0 which encodes to "S" in base32 */ -bool stellar_validateAddress(const char *str_address) { +bool stellar_validateAddress(const char *str_address, uint8_t version) { bool valid = false; uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW] = {0}; memzero(decoded, sizeof(decoded)); @@ -1794,7 +1982,7 @@ bool stellar_validateAddress(const char *str_address) { valid = (ret != NULL); // ... and that version byte is 0x30 - if (valid && decoded[0] != 0x30) { + if (valid && decoded[0] != version) { valid = false; } @@ -1818,7 +2006,7 @@ bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes) { memzero(decoded, sizeof(decoded)); // Ensure address is valid - if (!stellar_validateAddress(str_address)) return false; + if (!stellar_validateAddress(str_address, 0x30)) return false; base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, sizeof(decoded), BASE32_ALPHABET_RFC4648); @@ -1830,6 +2018,26 @@ bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes) { return true; } +/** + * Converts a string address (C...) to the 32-byte raw address + */ +bool stellar_getContractAddressBytes(const char *str_address, + uint8_t *out_bytes) { + uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW] = {0}; + memzero(decoded, sizeof(decoded)); + + // Ensure address is valid + if (!stellar_validateAddress(str_address, 0x10)) return false; + + base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, sizeof(decoded), + BASE32_ALPHABET_RFC4648); + + // The 32 bytes with offset 1-33 represent the public key + memcpy(out_bytes, &decoded[1], 32); + + memzero(decoded, sizeof(decoded)); + return true; +} /* * CRC16 implementation compatible with the Stellar version * Ported from this implementation: @@ -1938,13 +2146,20 @@ void stellar_hashupdate_string(const uint8_t *data, size_t len) { } void stellar_hashupdate_address(const uint8_t *address_bytes) { - // First 4 bytes of an address are the type. There's only one type (0) + // First 4 bytes of an address are the type. 0 for KEY_TYPE_ED25519 stellar_hashupdate_uint32(0); // Remaining part of the address is 32 bytes stellar_hashupdate_bytes(address_bytes, 32); } +void stellar_hashupdate_contract_address(const uint8_t *address_bytes) { + // First 4 bytes of an address are the type. 1 for SC_ADDRESS_TYPE_CONTRACT + stellar_hashupdate_uint32(1); + + // Remaining part of the address is 32 bytes + stellar_hashupdate_bytes(address_bytes, 32); +} /* * Note about string handling below: this field is an XDR "opaque" field and not * a typical string, so if "TEST" is the asset code then the hashed value needs @@ -2096,15 +2311,22 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) { // data2hex(msg->memo_hash.bytes + 16, 8, str_lines[3]); // data2hex(msg->memo_hash.bytes + 24, 8, str_lines[4]); } + const char *const expected_keys[] = {memo_type_none ? NULL : memo_key, - _(I_TX_SOURCE), _(I_SEQUENCE_NUMBER), - _(I__FEE_COLON)}; + _(I_TX_SOURCE), _(I_SEQUENCE_NUMBER), + _(I__FEE_COLON)}; const char *const values[] = {memo, signer_addr, str_seq_num, str_fee}; + if (!stellar_layoutTransactionDialog(ARRAY_SIZE(expected_keys), expected_keys, values)) { stellar_signingFail("User canceled", true); return; } + if (stellar_activeTx.soroban_data_size > 0) { + if (!stellar_confirmSorobanWarning()) { + return; + } + } // if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { // stellar_signingFail("User canceled"); // return; @@ -2187,7 +2409,8 @@ bool stellar_layoutSigningDialog(const char *title, size_t n, if (index < max_index) { layoutHeader(tx_msg[0]); oledDrawStringAdapter(0, y, keys[index], FONT_STANDARD); - oledDrawStringAdapter(0, y + 10, values[index], FONT_STANDARD); + const char *display_value = truncate_text_for_display(values[index], 3); + oledDrawStringAdapter(0, y + 10, display_value, FONT_STANDARD); layoutButtonNoAdapter(NULL, (index == first_nonull_index) ? &bmp_bottom_left_close : &bmp_bottom_left_arrow); diff --git a/legacy/firmware/stellar.h b/legacy/firmware/stellar.h index 92f579f15..5d87ada82 100644 --- a/legacy/firmware/stellar.h +++ b/legacy/firmware/stellar.h @@ -48,6 +48,7 @@ typedef struct { // Number that have been confirmed by the user uint32_t confirmed_operations; + uint32_t soroban_data_size; // sha256 context that will eventually be signed SHA256_CTX sha256_ctx; } StellarTransaction; @@ -73,6 +74,8 @@ bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg); bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg); bool stellar_confirmManageDataOp(const StellarManageDataOp *msg); bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg); +bool stellar_confirmInvokeHostFunctionOp( + const StellarInvokeHostFunctionOp *msg); // Layout bool stellar_layoutTransactionDialog(size_t n, const char *const keys[n], @@ -96,6 +99,7 @@ void stellar_hashupdate_uint64(uint64_t value); void stellar_hashupdate_bool(bool value); void stellar_hashupdate_string(const uint8_t *data, size_t len); void stellar_hashupdate_address(const uint8_t *address_bytes); +void stellar_hashupdate_contract_address(const uint8_t *address_bytes); void stellar_hashupdate_asset(const StellarAsset *asset); void stellar_hashupdate_bytes(const uint8_t *data, size_t len); @@ -111,8 +115,10 @@ void stellar_format_asset(const StellarAsset *asset, char *str_formatted, void stellar_format_price(uint32_t numerator, uint32_t denominator, char *out, size_t outlen); -bool stellar_validateAddress(const char *str_address); +bool stellar_validateAddress(const char *str_address, uint8_t version); bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes); +bool stellar_getContractAddressBytes(const char *str_address, + uint8_t *out_bytes); uint16_t stellar_crc16(uint8_t *bytes, uint32_t length); bool stellar_path_check(uint32_t address_n_count, const uint32_t *address_n); diff --git a/legacy/firmware/usb.c b/legacy/firmware/usb.c index c627504ee..28c82e63b 100644 --- a/legacy/firmware/usb.c +++ b/legacy/firmware/usb.c @@ -505,56 +505,53 @@ void usbPoll(void) { static const uint8_t *data; volatile bool reset = false; - bool lock = true; + bool usb_lock_enabled = true; - static bool usb_status_bak = false; + static uint8_t usb_status_bak = 0; if (sys_usbState() == false) { usb_connect_status = 0; } ble_update_poll(); - if (usb_connect_status && !usb_status_bak) { - usb_status_bak = true; - if (config_hasPin() && session_isUnlocked()) { - reset = true; - } - } else if (!usb_connect_status && usb_status_bak) { - usb_status_bak = false; - if (config_hasPin() && session_isUnlocked()) { - reset = true; - } else { - // FTFixed: 设备重启后,需要usb重新初始化 + bool usb_status_changed = usb_connect_status != usb_status_bak; + if (usb_status_changed) { + usb_status_bak = usb_connect_status; + reset = config_hasPin() && + session_isUnlocked(); // usb status changed and unlocked + if (usb_connect_status == 0 && !reset) { + // usb disconnected and locked, only need to re-init usb stack usbInit(); } } - if (reset) config_getUsblock(&lock, false); - - if (reset && lock) { - if (host_channel == CHANNEL_SLAVE) { - Failure resp = { - .has_code = true, - .code = FailureType_Failure_ActionCancelled, - .has_message = true, - .message = "Disconnected by device", - }; - msg_write(MessageType_MessageType_Failure, &resp); - } - session_clear(true); - soft_reset_set_preserved_data((uint16_t)config_getSafetyCheckLevel()); - svc_system_privileged(); - vector_table_t *ivt = (vector_table_t *)FLASH_PTR(FLASH_APP_START); - __asm__ volatile("msr msp, %0" ::"r"(ivt->initial_sp_value)); - if (cpu_mode == UNPRIVILEGED) { - mpu_config_firmware(); + if (reset) { // usb status changed and unlocked + config_getUsblock(&usb_lock_enabled, false); + if (usb_lock_enabled) { + if (host_channel == CHANNEL_SLAVE) { + Failure resp = { + .has_code = true, + .code = FailureType_Failure_ActionCancelled, + .has_message = true, + .message = "Disconnected by device", + }; + msg_write(MessageType_MessageType_Failure, &resp); + } + session_clear(true); + uint16_t data2preserved = (config_getSafetyCheckLevel() & 0x03) | 0x04; + soft_reset_set_preserved_data(data2preserved); + svc_system_privileged(); + vector_table_t *ivt = (vector_table_t *)FLASH_PTR(FLASH_APP_START); + __asm__ volatile("msr msp, %0" ::"r"(ivt->initial_sp_value)); + if (cpu_mode == UNPRIVILEGED) { + mpu_config_firmware(); + } + ble_ctl_disconnect(); + __asm__ volatile("b reset_handler"); + } else { + clear_msg_out(); + RCC_AHB2RSTR |= RCC_AHB2RSTR_OTGFSRST; + RCC_AHB2RSTR &= ~RCC_AHB2RSTR_OTGFSRST; + usbInit(); } - ble_ctl_disconnect(); - __asm__ volatile("b reset_handler"); - } else if (reset) { - clear_msg_out(); - - RCC_AHB2RSTR |= RCC_AHB2RSTR_OTGFSRST; - RCC_AHB2RSTR &= ~RCC_AHB2RSTR_OTGFSRST; - usbInit(); } i2c_slave_poll(); diff --git a/legacy/firmware/version.h b/legacy/firmware/version.h index 36b7397c7..cc1f2697f 100755 --- a/legacy/firmware/version.h +++ b/legacy/firmware/version.h @@ -7,10 +7,10 @@ #define FIX_VERSION_PATCH 99 #define ONEKEY_VER_MAJOR 3 -#define ONEKEY_VER_MINOR 18 +#define ONEKEY_VER_MINOR 19 #define ONEKEY_VER_PATCH 0 -#define ONEKEY_VERSION "3.18.0" +#define ONEKEY_VERSION "3.19.0" // Deprecated #define ONEKEY_VERSION_HEX 0x3F00 diff --git a/legacy/util.c b/legacy/util.c index 4143b4485..1392bf665 100644 --- a/legacy/util.c +++ b/legacy/util.c @@ -320,3 +320,30 @@ uint64_t deser_compact_size(BufferReader *s) { return value; } + +const char *truncate_text_for_display(const char *text, uint8_t max_lines) { + if (!text || max_lines == 0 || max_lines > 4) return ""; + + size_t text_len = strlen(text); + size_t chars_per_line = 21; + size_t max_chars = max_lines * chars_per_line; + + static char truncated_value[64]; + memset(truncated_value, 0, sizeof(truncated_value)); + + if (text_len > max_chars) { + size_t full_lines = max_lines - 1; + size_t full_chars = full_lines * chars_per_line; + strncpy(truncated_value, text, full_chars); + truncated_value[full_chars] = '\n'; + memcpy(truncated_value + full_chars + 1, "...", 3); + size_t remaining_chars = chars_per_line - 3; + size_t start_pos = text_len - remaining_chars; + strncpy(truncated_value + full_chars + 4, text + start_pos, + remaining_chars); + } else { + return text; + } + + return truncated_value; +} diff --git a/legacy/util.h b/legacy/util.h index 4461c9180..0671819b1 100644 --- a/legacy/util.h +++ b/legacy/util.h @@ -89,7 +89,7 @@ void init_buffer_writer(BufferWriter *writer, uint8_t *buffer, size_t length); int read_bytes(BufferReader *reader, uint8_t *dest, size_t count); int write_bytes(const uint8_t *src, size_t count, BufferWriter *writer); uint64_t deser_compact_size(BufferReader *s); - +const char *truncate_text_for_display(const char *text, uint8_t max_lines); // defined in startup.s (or setup.c for emulator) extern void __attribute__((noreturn)) shutdown(void); @@ -189,4 +189,4 @@ static inline void reverse_bytes(uint8_t *data, size_t length) { } } -#endif +#endif // __UTIL_H_