Merge remote-tracking branch 'origin/develop' into HEAD
30
CHANGELOG.md
@@ -5,7 +5,29 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-XX-XX
|
||||
## [1.10.0](https://github.com/ledgerhq/app-ethereum/compare/1.9.20...1.10.0) - 2022-XX-XX
|
||||
|
||||
## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-10-10
|
||||
|
||||
### Added
|
||||
|
||||
- (clone) XDCNetwork
|
||||
- (clone) Meter
|
||||
- (clone) Multivac
|
||||
- (clone) Tecra
|
||||
- (clone) ApothemNetwork
|
||||
|
||||
### Changed
|
||||
|
||||
- EIP-191 improvements, now lets the user see the entire message one chunk at a time (255 characters for LNX & LNS+, 99 for LNS)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Allow swap with variants
|
||||
|
||||
### Removed
|
||||
|
||||
- Compound support (will become its own plugin)
|
||||
|
||||
## [1.9.19](https://github.com/ledgerhq/app-ethereum/compare/1.9.18...1.9.19) - 2022-06-15
|
||||
|
||||
@@ -18,7 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Changed
|
||||
|
||||
- EIP-191 signatures now show (up to 99 characters on LNS and 255 on LNX & LNS) the actual data contained in the message (clear-signing)
|
||||
- EIP-191 signatures now show (up to 99 characters on LNS and 255 on LNX & LNS+) the actual data contained in the message (clear-signing)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -66,7 +88,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed stark order signature on LNS
|
||||
- Fixed stark order signature on LNS
|
||||
|
||||
## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17
|
||||
|
||||
@@ -78,7 +100,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed stark order signature on LNX
|
||||
- Fixed stark order signature on LNX
|
||||
|
||||
## [1.9.11](https://github.com/ledgerhq/app-ethereum/compare/1.9.10...1.9.11) - 2021-10-12
|
||||
|
||||
|
||||
6
Makefile
@@ -33,10 +33,10 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'"
|
||||
##################
|
||||
|
||||
APPVERSION_M=1
|
||||
APPVERSION_N=9
|
||||
APPVERSION_P=20
|
||||
APPVERSION_N=10
|
||||
APPVERSION_P=0
|
||||
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)-dev
|
||||
APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION)
|
||||
APP_LOAD_FLAGS= --appFlags 0xa40 --dep Ethereum:$(APPVERSION)
|
||||
|
||||
###########################
|
||||
# Set Chain environnement #
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
|
||||
enum { OFFSET_CLA = 0, OFFSET_INS, OFFSET_P1, OFFSET_P2, OFFSET_LC, OFFSET_CDATA };
|
||||
|
||||
#define ERR_APDU_EMPTY 0x6982
|
||||
#define ERR_APDU_SIZE_MISMATCH 0x6983
|
||||
|
||||
void handleGetPublicKey(uint8_t p1,
|
||||
uint8_t p2,
|
||||
const uint8_t *dataBuffer,
|
||||
@@ -95,12 +98,10 @@ void handleGetAppConfiguration(uint8_t p1,
|
||||
uint8_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
void handleSignPersonalMessage(uint8_t p1,
|
||||
bool handleSignPersonalMessage(uint8_t p1,
|
||||
uint8_t p2,
|
||||
const uint8_t *dataBuffer,
|
||||
uint8_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
const uint8_t *const payload,
|
||||
uint8_t length);
|
||||
void handleSignEIP712Message_v0(uint8_t p1,
|
||||
uint8_t p2,
|
||||
const uint8_t *dataBuffer,
|
||||
|
||||
@@ -18,7 +18,14 @@ void ui_stark_limit_order(void);
|
||||
void ui_stark_unsafe_sign(void);
|
||||
void ui_stark_transfer(bool selfTransfer, bool conditional);
|
||||
|
||||
// EIP-191
|
||||
void ui_191_start(void);
|
||||
void ui_191_switch_to_message(void);
|
||||
void ui_191_switch_to_message_end(void);
|
||||
void ui_191_switch_to_sign(void);
|
||||
void ui_191_switch_to_question(void);
|
||||
|
||||
#include "ui_callbacks.h"
|
||||
#include <string.h>
|
||||
|
||||
#endif // _COMMON_UI_H_
|
||||
#endif // _COMMON_UI_H_
|
||||
|
||||
@@ -348,13 +348,13 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_ID:
|
||||
if (((ethQueryContractID_t *) parameter)->result <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
if (((ethQueryContractID_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_UI:
|
||||
if (((ethQueryContractUI_t *) parameter)->result <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
if (((ethQueryContractUI_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
bool erc20_plugin_available_check(void);
|
||||
|
||||
void erc20_plugin_call(int message, void* parameters);
|
||||
void compound_plugin_call(int message, void* parameters);
|
||||
|
||||
void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size) {
|
||||
uint8_t copy_size = MIN(dst_size, ADDRESS_LENGTH);
|
||||
@@ -29,17 +28,6 @@ static const uint8_t ERC20_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7,
|
||||
const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = {ERC20_TRANSFER_SELECTOR,
|
||||
ERC20_APPROVE_SELECTOR};
|
||||
|
||||
static const uint8_t COMPOUND_REDEEM_UNDERLYING_SELECTOR[SELECTOR_SIZE] = {0x85, 0x2a, 0x12, 0xe3};
|
||||
static const uint8_t COMPOUND_REDEEM_SELECTOR[SELECTOR_SIZE] = {0xdb, 0x00, 0x6a, 0x75};
|
||||
static const uint8_t COMPOUND_MINT_SELECTOR[SELECTOR_SIZE] = {0xa0, 0x71, 0x2d, 0x68};
|
||||
static const uint8_t CETH_MINT_SELECTOR[SELECTOR_SIZE] = {0x12, 0x49, 0xc5, 0x8b};
|
||||
|
||||
const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS] = {
|
||||
COMPOUND_REDEEM_UNDERLYING_SELECTOR,
|
||||
COMPOUND_REDEEM_SELECTOR,
|
||||
COMPOUND_MINT_SELECTOR,
|
||||
CETH_MINT_SELECTOR};
|
||||
|
||||
#ifdef HAVE_ETH2
|
||||
|
||||
static const uint8_t ETH2_DEPOSIT_SELECTOR[SELECTOR_SIZE] = {0x22, 0x89, 0x51, 0x18};
|
||||
@@ -111,12 +99,6 @@ const internalEthPlugin_t INTERNAL_ETH_PLUGINS[] = {
|
||||
"-erc20",
|
||||
erc20_plugin_call},
|
||||
|
||||
{NULL,
|
||||
(const uint8_t**) COMPOUND_SELECTORS,
|
||||
NUM_COMPOUND_SELECTORS,
|
||||
"-cmpd",
|
||||
compound_plugin_call},
|
||||
|
||||
#ifdef HAVE_ETH2
|
||||
|
||||
{NULL, (const uint8_t**) ETH2_SELECTORS, NUM_ETH2_SELECTORS, "-eth2", eth2_plugin_call},
|
||||
|
||||
@@ -29,9 +29,6 @@ typedef struct internalEthPlugin_t {
|
||||
#define NUM_ERC20_SELECTORS 2
|
||||
extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS];
|
||||
|
||||
#define NUM_COMPOUND_SELECTORS 4
|
||||
extern const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS];
|
||||
|
||||
#ifdef HAVE_ETH2
|
||||
|
||||
#define NUM_ETH2_SELECTORS 1
|
||||
|
||||
11
src/main.c
@@ -666,12 +666,11 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
|
||||
case INS_SIGN_PERSONAL_MESSAGE:
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
|
||||
*flags |= IO_ASYNCH_REPLY;
|
||||
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
G_io_apdu_buffer[OFFSET_LC],
|
||||
flags,
|
||||
tx);
|
||||
G_io_apdu_buffer[OFFSET_LC]);
|
||||
break;
|
||||
|
||||
case INS_SIGN_EIP_712_MESSAGE:
|
||||
@@ -799,9 +798,11 @@ void app_main(void) {
|
||||
// no apdu received, well, reset the session, and reset the
|
||||
// bootloader configuration
|
||||
if (rx == 0) {
|
||||
THROW(0x6982);
|
||||
THROW(ERR_APDU_EMPTY);
|
||||
}
|
||||
if (rx > OFFSET_LC && rx != (G_io_apdu_buffer[OFFSET_LC] + 5)) {
|
||||
THROW(ERR_APDU_SIZE_MISMATCH);
|
||||
}
|
||||
PRINTF("New APDU received:\n%.*H\n", rx, G_io_apdu_buffer);
|
||||
|
||||
handleApdu(&flags, &tx);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_signMessage_ok(void);
|
||||
unsigned int io_seproxyhal_touch_signMessage_cancel(void);
|
||||
unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e);
|
||||
@@ -21,7 +21,6 @@ unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e);
|
||||
unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e);
|
||||
|
||||
void ui_idle(void);
|
||||
void ui_warning_contract_data(void);
|
||||
|
||||
void io_seproxyhal_send_status(uint32_t sw);
|
||||
|
||||
@@ -32,10 +32,6 @@ void ui_display_public_key(void) {
|
||||
ux_flow_init(0, ux_display_public_flow, NULL);
|
||||
}
|
||||
|
||||
void ui_display_sign(void) {
|
||||
ux_flow_init(0, ux_sign_flow, NULL);
|
||||
}
|
||||
|
||||
void ui_sign_712_v0(void) {
|
||||
ux_flow_init(0, ux_sign_712_v0_flow, NULL);
|
||||
}
|
||||
@@ -74,4 +70,4 @@ void ui_confirm_parameter(void) {
|
||||
ux_flow_init(0, ux_confirm_parameter_flow, NULL);
|
||||
}
|
||||
|
||||
#endif // HAVE_BAGL
|
||||
#endif // HAVE_BAGL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "shared_context.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
void display_settings(const ux_flow_step_t* const start_step);
|
||||
void switch_settings_blind_signing(void);
|
||||
|
||||
@@ -20,8 +20,6 @@ extern const ux_flow_step_t* const ux_confirm_parameter_flow[];
|
||||
|
||||
extern const ux_flow_step_t* const ux_approval_allowance_flow[];
|
||||
|
||||
extern const ux_flow_step_t* const ux_sign_flow[];
|
||||
|
||||
extern const ux_flow_step_t* const ux_sign_712_v0_flow[];
|
||||
|
||||
extern const ux_flow_step_t* const ux_display_public_eth2_flow[];
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
#include "shared_context.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
#include "sign_message.h"
|
||||
|
||||
typedef enum { UI_191_POS_REVIEW, UI_191_POS_QUESTION, UI_191_POS_END } e_ui_191_position;
|
||||
|
||||
static uint8_t ui_pos;
|
||||
|
||||
static void dummy_pre_cb(void) {
|
||||
if (ui_pos == UI_191_POS_REVIEW) {
|
||||
question_switcher();
|
||||
} else {
|
||||
ux_flow_prev();
|
||||
ui_pos = UI_191_POS_REVIEW;
|
||||
}
|
||||
}
|
||||
|
||||
static void dummy_post_cb(void) {
|
||||
if (ui_pos == UI_191_POS_QUESTION) {
|
||||
continue_displaying_message();
|
||||
} else // UI_191_END
|
||||
{
|
||||
ui_191_switch_to_message_end();
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
UX_STEP_NOCB(
|
||||
ux_sign_flow_1_step,
|
||||
ux_191_step_review,
|
||||
pnn,
|
||||
{
|
||||
&C_icon_certificate,
|
||||
@@ -11,25 +35,56 @@ UX_STEP_NOCB(
|
||||
"message",
|
||||
});
|
||||
UX_STEP_NOCB(
|
||||
ux_sign_flow_2_step,
|
||||
ux_191_step_message,
|
||||
bnnn_paging,
|
||||
{
|
||||
.title = "Message",
|
||||
.text = strings.tmp.tmp,
|
||||
});
|
||||
UX_STEP_INIT(
|
||||
ux_191_step_dummy_pre,
|
||||
NULL,
|
||||
NULL,
|
||||
{
|
||||
dummy_pre_cb();
|
||||
});
|
||||
UX_STEP_CB(
|
||||
ux_sign_flow_3_step,
|
||||
ux_191_step_theres_more,
|
||||
#ifdef TARGET_NANOS
|
||||
nn,
|
||||
#else
|
||||
nnn,
|
||||
#endif
|
||||
skip_rest_of_message(),
|
||||
{
|
||||
#ifndef TARGET_NANOS
|
||||
"Press right to",
|
||||
"continue message",
|
||||
#else
|
||||
"Press right to read",
|
||||
#endif
|
||||
"Double-press to skip"
|
||||
});
|
||||
UX_STEP_INIT(
|
||||
ux_191_step_dummy_post,
|
||||
NULL,
|
||||
NULL,
|
||||
{
|
||||
dummy_post_cb();
|
||||
});
|
||||
UX_STEP_CB(
|
||||
ux_191_step_sign,
|
||||
pbb,
|
||||
io_seproxyhal_touch_signMessage_ok(NULL),
|
||||
io_seproxyhal_touch_signMessage_ok(),
|
||||
{
|
||||
&C_icon_validate_14,
|
||||
"Sign",
|
||||
"message",
|
||||
});
|
||||
UX_STEP_CB(
|
||||
ux_sign_flow_4_step,
|
||||
ux_191_step_cancel,
|
||||
pbb,
|
||||
io_seproxyhal_touch_signMessage_cancel(NULL),
|
||||
io_seproxyhal_touch_signMessage_cancel(),
|
||||
{
|
||||
&C_icon_crossmark,
|
||||
"Cancel",
|
||||
@@ -37,8 +92,37 @@ UX_STEP_CB(
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
UX_FLOW(ux_sign_flow,
|
||||
&ux_sign_flow_1_step,
|
||||
&ux_sign_flow_2_step,
|
||||
&ux_sign_flow_3_step,
|
||||
&ux_sign_flow_4_step);
|
||||
UX_FLOW(ux_191_flow,
|
||||
&ux_191_step_review,
|
||||
&ux_191_step_message,
|
||||
&ux_191_step_dummy_pre,
|
||||
&ux_191_step_theres_more,
|
||||
&ux_191_step_dummy_post,
|
||||
&ux_191_step_sign,
|
||||
&ux_191_step_cancel);
|
||||
|
||||
void ui_191_start(void) {
|
||||
ux_flow_init(0, ux_191_flow, NULL);
|
||||
ui_pos = UI_191_POS_REVIEW;
|
||||
}
|
||||
|
||||
void ui_191_switch_to_message(void) {
|
||||
ux_flow_init(0, ux_191_flow, &ux_191_step_message);
|
||||
ui_pos = UI_191_POS_REVIEW;
|
||||
}
|
||||
|
||||
void ui_191_switch_to_message_end(void) {
|
||||
// Force it to a value that will make it automatically do a prev()
|
||||
ui_pos = UI_191_POS_QUESTION;
|
||||
ux_flow_init(0, ux_191_flow, &ux_191_step_dummy_pre);
|
||||
}
|
||||
|
||||
void ui_191_switch_to_sign(void) {
|
||||
ux_flow_init(0, ux_191_flow, &ux_191_step_sign);
|
||||
ui_pos = UI_191_POS_END;
|
||||
}
|
||||
|
||||
void ui_191_switch_to_question(void) {
|
||||
ux_flow_init(0, ux_191_flow, &ux_191_step_theres_more);
|
||||
ui_pos = UI_191_POS_QUESTION;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "eth_plugin_handler.h"
|
||||
#include "ui_plugin.h"
|
||||
#include "common_ui.h"
|
||||
#include "plugins.h"
|
||||
|
||||
// clang-format off
|
||||
UX_STEP_NOCB(
|
||||
|
||||
@@ -3,18 +3,7 @@
|
||||
#include "shared_context.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "ethUtils.h"
|
||||
|
||||
void stark_sign_display_master_account() {
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"0x%.*H",
|
||||
32,
|
||||
dataContext.starkContext.transferDestination);
|
||||
}
|
||||
|
||||
void stark_sign_display_condition_fact() {
|
||||
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataContext.starkContext.fact);
|
||||
}
|
||||
#include "starkDisplayUtils.h"
|
||||
|
||||
// clang-format off
|
||||
UX_STEP_NOCB(ux_stark_limit_order_1_step,
|
||||
|
||||
@@ -3,38 +3,11 @@
|
||||
#include "eth_plugin_handler.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "ui_plugin.h"
|
||||
#include "plugins.h"
|
||||
|
||||
// This function is not exported by the SDK
|
||||
void ux_layout_paging_redisplay_by_addr(unsigned int stack_slot);
|
||||
|
||||
void plugin_ui_get_id() {
|
||||
ethQueryContractID_t pluginQueryContractID;
|
||||
eth_plugin_prepare_query_contract_ID(&pluginQueryContractID,
|
||||
strings.common.fullAddress,
|
||||
sizeof(strings.common.fullAddress),
|
||||
strings.common.fullAmount,
|
||||
sizeof(strings.common.fullAmount));
|
||||
// Query the original contract for ID if it's not an internal alias
|
||||
if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, (void *) &pluginQueryContractID)) {
|
||||
PRINTF("Plugin query contract ID call failed\n");
|
||||
io_seproxyhal_touch_tx_cancel(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void plugin_ui_get_item() {
|
||||
ethQueryContractUI_t pluginQueryContractUI;
|
||||
eth_plugin_prepare_query_contract_UI(&pluginQueryContractUI,
|
||||
dataContext.tokenContext.pluginUiCurrentItem,
|
||||
strings.common.fullAddress,
|
||||
sizeof(strings.common.fullAddress),
|
||||
strings.common.fullAmount,
|
||||
sizeof(strings.common.fullAmount));
|
||||
if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_UI, (void *) &pluginQueryContractUI)) {
|
||||
PRINTF("Plugin query contract UI call failed\n");
|
||||
io_seproxyhal_touch_tx_cancel(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void display_next_plugin_item(bool entering) {
|
||||
if (entering) {
|
||||
if (dataContext.tokenContext.pluginUiState == PLUGIN_UI_OUTSIDE) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef _UI_PLUGIN_H_
|
||||
#define _UI_PLUGIN_H_
|
||||
|
||||
void plugin_ui_get_id();
|
||||
void plugin_ui_get_item();
|
||||
void display_next_plugin_item(bool entering);
|
||||
|
||||
#endif // _UI_PLUGIN_H_
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#define MAX_NETWORK_TICKER_LEN 8
|
||||
|
||||
typedef struct network_info_s {
|
||||
const char name[NETWORK_STRING_MAX_SIZE];
|
||||
const char ticker[MAX_NETWORK_TICKER_LEN];
|
||||
const char *name;
|
||||
const char *ticker;
|
||||
uint64_t chain_id;
|
||||
} network_info_t;
|
||||
|
||||
|
||||
40
src_common/plugins.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "eth_plugin_handler.h"
|
||||
#include "ui_callbacks.h"
|
||||
|
||||
void plugin_ui_get_id(void) {
|
||||
ethQueryContractID_t pluginQueryContractID;
|
||||
eth_plugin_prepare_query_contract_ID(&pluginQueryContractID,
|
||||
strings.common.fullAddress,
|
||||
sizeof(strings.common.fullAddress),
|
||||
strings.common.fullAmount,
|
||||
sizeof(strings.common.fullAmount));
|
||||
// Query the original contract for ID if it's not an internal alias
|
||||
if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, (void *) &pluginQueryContractID)) {
|
||||
PRINTF("Plugin query contract ID call failed\n");
|
||||
io_seproxyhal_touch_tx_cancel(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void plugin_ui_get_item_internal(char *title_buffer,
|
||||
size_t title_buffer_size,
|
||||
char *msg_buffer,
|
||||
size_t msg_buffer_size) {
|
||||
ethQueryContractUI_t pluginQueryContractUI;
|
||||
eth_plugin_prepare_query_contract_UI(&pluginQueryContractUI,
|
||||
dataContext.tokenContext.pluginUiCurrentItem,
|
||||
title_buffer,
|
||||
title_buffer_size,
|
||||
msg_buffer,
|
||||
msg_buffer_size);
|
||||
if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_UI, (void *) &pluginQueryContractUI)) {
|
||||
PRINTF("Plugin query contract UI call failed\n");
|
||||
io_seproxyhal_touch_tx_cancel(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void plugin_ui_get_item(void) {
|
||||
plugin_ui_get_item_internal(strings.common.fullAddress,
|
||||
sizeof(strings.common.fullAddress),
|
||||
strings.common.fullAmount,
|
||||
sizeof(strings.common.fullAmount));
|
||||
}
|
||||
11
src_common/plugins.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _PLUGIN_H_
|
||||
#define _PLUGIN_H_
|
||||
|
||||
void plugin_ui_get_id();
|
||||
void plugin_ui_get_item();
|
||||
void plugin_ui_get_item_internal(uint8_t *title_buffer,
|
||||
size_t title_buffer_size,
|
||||
uint8_t *msg_buffer,
|
||||
size_t msg_buffer_size);
|
||||
|
||||
#endif // _PLUGIN_H_
|
||||
17
src_common/starkDisplayUtils.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifdef HAVE_STARKWARE
|
||||
|
||||
#include "shared_context.h"
|
||||
|
||||
void stark_sign_display_master_account() {
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"0x%.*H",
|
||||
32,
|
||||
dataContext.starkContext.transferDestination);
|
||||
}
|
||||
|
||||
void stark_sign_display_condition_fact() {
|
||||
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataContext.starkContext.fact);
|
||||
}
|
||||
|
||||
#endif
|
||||
6
src_common/starkDisplayUtils.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifdef HAVE_STARKWARE
|
||||
|
||||
void stark_sign_display_master_account();
|
||||
void stark_sign_display_condition_fact();
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "shared_context.h"
|
||||
#include "feature_getEth2PublicKey.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_eth2_address_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
uint32_t tx = set_result_get_eth2_publicKey();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "shared_context.h"
|
||||
#include "feature_getPublicKey.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_address_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
uint32_t tx = set_result_get_publicKey();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "shared_context.h"
|
||||
#include "feature_getPublicKey.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
#include "feature_performPrivacyOperation.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_privacy_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
|
||||
@@ -1,199 +1,283 @@
|
||||
#include <stdbool.h>
|
||||
#include "shared_context.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "apdu_constants.h"
|
||||
#include "utils.h"
|
||||
#include "sign_message.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
static uint8_t processed_size;
|
||||
static struct {
|
||||
sign_message_state sign_state : 1;
|
||||
bool ui_started : 1;
|
||||
} states;
|
||||
|
||||
static const char SIGN_MAGIC[] =
|
||||
"\x19"
|
||||
"Ethereum Signed Message:\n";
|
||||
|
||||
/**
|
||||
* Check if a given character is a "special" displayable ASCII character
|
||||
* Send a response APDU with the given Status Word
|
||||
*
|
||||
* @param[in] c character we're checking
|
||||
* @return whether the character is special or not
|
||||
* @param[in] sw status word
|
||||
*/
|
||||
static inline bool is_char_special(char c) {
|
||||
return ((c >= '\b') && (c <= '\r'));
|
||||
static void apdu_reply(uint16_t sw) {
|
||||
G_io_apdu_buffer[0] = (sw >> 8) & 0xff;
|
||||
G_io_apdu_buffer[1] = sw & 0xff;
|
||||
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given data is made of ASCII characters
|
||||
* Get unprocessed data from last received APDU
|
||||
*
|
||||
* @param[in] data the input data
|
||||
* @param[in] the length of the input data
|
||||
* @return whether the data is fully ASCII or not
|
||||
* @return pointer to data in APDU buffer
|
||||
*/
|
||||
static bool is_data_ascii(const uint8_t *const data, size_t length) {
|
||||
for (uint8_t idx = 0; idx < length; ++idx) {
|
||||
if (!is_char_special(data[idx]) && ((data[idx] < 0x20) || (data[idx] > 0x7e))) {
|
||||
static const uint8_t *unprocessed_data(void) {
|
||||
return &G_io_apdu_buffer[OFFSET_CDATA] + processed_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of unprocessed data from last received APDU
|
||||
*
|
||||
* @return size of data in bytes
|
||||
*/
|
||||
static size_t unprocessed_length(void) {
|
||||
return G_io_apdu_buffer[OFFSET_LC] - processed_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get used space from UI buffer
|
||||
*
|
||||
* @return size in bytes
|
||||
*/
|
||||
static size_t ui_buffer_length(void) {
|
||||
return strlen(UI_191_BUFFER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remaining space from UI buffer
|
||||
*
|
||||
* @return size in bytes
|
||||
*/
|
||||
static size_t remaining_ui_buffer_length(void) {
|
||||
// -1 for the ending NULL byte
|
||||
return (sizeof(UI_191_BUFFER) - 1) - ui_buffer_length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get free space from UI buffer
|
||||
*
|
||||
* @return pointer to the free space
|
||||
*/
|
||||
static char *remaining_ui_buffer(void) {
|
||||
return &UI_191_BUFFER[ui_buffer_length()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the UI buffer
|
||||
*
|
||||
* Simply sets its first byte to a NULL character
|
||||
*/
|
||||
static void reset_ui_buffer(void) {
|
||||
UI_191_BUFFER[0] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the data specific to the first APDU of an EIP-191 signature
|
||||
*
|
||||
* @param[in] data the APDU payload
|
||||
* @param[in] length the payload size
|
||||
* @return pointer to the start of the start of the message; \ref NULL if it failed
|
||||
*/
|
||||
static const uint8_t *first_apdu_data(const uint8_t *data, uint8_t *length) {
|
||||
if (appState != APP_STATE_IDLE) {
|
||||
reset_app_context();
|
||||
}
|
||||
appState = APP_STATE_SIGNING_MESSAGE;
|
||||
data = parseBip32(data, length, &tmpCtx.messageSigningContext.bip32);
|
||||
if (data == NULL) {
|
||||
apdu_reply(0x6a80);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*length < sizeof(uint32_t)) {
|
||||
PRINTF("Invalid data\n");
|
||||
apdu_reply(0x6a80);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmpCtx.messageSigningContext.remainingLength = U4BE(data, 0);
|
||||
data += sizeof(uint32_t);
|
||||
*length -= sizeof(uint32_t);
|
||||
|
||||
// Initialize message header + length
|
||||
cx_keccak_init(&global_sha3, 256);
|
||||
cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0);
|
||||
snprintf(strings.tmp.tmp2,
|
||||
sizeof(strings.tmp.tmp2),
|
||||
"%u",
|
||||
tmpCtx.messageSigningContext.remainingLength);
|
||||
cx_hash((cx_hash_t *) &global_sha3,
|
||||
0,
|
||||
(uint8_t *) strings.tmp.tmp2,
|
||||
strlen(strings.tmp.tmp2),
|
||||
NULL,
|
||||
0);
|
||||
reset_ui_buffer();
|
||||
states.sign_state = STATE_191_HASH_DISPLAY;
|
||||
states.ui_started = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the progressive hash with new data
|
||||
*
|
||||
* @param[in] data the new data
|
||||
* @param[in] length the data length
|
||||
* @return whether it was successful or not
|
||||
*/
|
||||
static bool feed_hash(const uint8_t *const data, const uint8_t length) {
|
||||
if (length > tmpCtx.messageSigningContext.remainingLength) {
|
||||
PRINTF("Error: Length mismatch ! (%u > %u)!\n",
|
||||
length,
|
||||
tmpCtx.messageSigningContext.remainingLength);
|
||||
apdu_reply(0x6a80);
|
||||
return false;
|
||||
}
|
||||
cx_hash((cx_hash_t *) &global_sha3, 0, data, length, NULL, 0);
|
||||
if ((tmpCtx.messageSigningContext.remainingLength -= length) == 0) {
|
||||
// Finalize hash
|
||||
cx_hash((cx_hash_t *) &global_sha3,
|
||||
CX_LAST,
|
||||
NULL,
|
||||
0,
|
||||
tmpCtx.messageSigningContext.hash,
|
||||
32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the UI with new data
|
||||
*/
|
||||
static void feed_display(void) {
|
||||
int c;
|
||||
|
||||
while ((unprocessed_length() > 0) && (remaining_ui_buffer_length() > 0)) {
|
||||
c = *(char *) unprocessed_data();
|
||||
if (isspace(c)) // to replace all white-space characters as spaces
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
if (isprint(c)) {
|
||||
sprintf(remaining_ui_buffer(), "%c", (char) c);
|
||||
processed_size += 1;
|
||||
} else {
|
||||
if (remaining_ui_buffer_length() >= 4) // 4 being the fixed length of \x00
|
||||
{
|
||||
snprintf(remaining_ui_buffer(), remaining_ui_buffer_length(), "\\x%02x", c);
|
||||
processed_size += 1;
|
||||
} else {
|
||||
// fill the rest of the UI buffer spaces, to consider the buffer full
|
||||
memset(remaining_ui_buffer(), ' ', remaining_ui_buffer_length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((remaining_ui_buffer_length() == 0) ||
|
||||
(tmpCtx.messageSigningContext.remainingLength == 0)) {
|
||||
if (!states.ui_started) {
|
||||
ui_191_start();
|
||||
states.ui_started = true;
|
||||
} else {
|
||||
ui_191_switch_to_message();
|
||||
}
|
||||
}
|
||||
|
||||
if ((unprocessed_length() == 0) && (tmpCtx.messageSigningContext.remainingLength > 0)) {
|
||||
apdu_reply(0x9000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EIP-191 APDU handler
|
||||
*
|
||||
* @param[in] p1 instruction parameter 1
|
||||
* @param[in] p2 instruction parameter 2
|
||||
* @param[in] payload received data
|
||||
* @param[in] length data length
|
||||
* @return whether the handling of the APDU was successful or not
|
||||
*/
|
||||
bool handleSignPersonalMessage(uint8_t p1,
|
||||
uint8_t p2,
|
||||
const uint8_t *const payload,
|
||||
uint8_t length) {
|
||||
const uint8_t *data = payload;
|
||||
|
||||
(void) p2;
|
||||
processed_size = 0;
|
||||
if (p1 == P1_FIRST) {
|
||||
if ((data = first_apdu_data(data, &length)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
processed_size = data - payload;
|
||||
} else if (p1 != P1_MORE) {
|
||||
PRINTF("Error: Unexpected P1 (%u)!\n", p1);
|
||||
apdu_reply(0x6B00);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!feed_hash(data, length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (states.sign_state == STATE_191_HASH_DISPLAY) {
|
||||
feed_display();
|
||||
} else // hash only
|
||||
{
|
||||
if (tmpCtx.messageSigningContext.remainingLength == 0) {
|
||||
#ifdef NO_CONSENT
|
||||
io_seproxyhal_touch_signMessage_ok();
|
||||
#else
|
||||
ui_191_switch_to_sign();
|
||||
#endif
|
||||
} else {
|
||||
apdu_reply(0x9000);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize value string that will be displayed in the UX STEP
|
||||
*
|
||||
* @param[in] if the value is ASCII
|
||||
* Decide whether to show the question to show more of the message or not
|
||||
*/
|
||||
static void init_value_str(bool is_ascii) {
|
||||
if (is_ascii) {
|
||||
strings.tmp.tmp[0] = '\0'; // init string as empty
|
||||
void question_switcher(void) {
|
||||
if ((states.sign_state == STATE_191_HASH_DISPLAY) &&
|
||||
((tmpCtx.messageSigningContext.remainingLength > 0) || (unprocessed_length() > 0))) {
|
||||
ui_191_switch_to_question();
|
||||
} else {
|
||||
strcpy(strings.tmp.tmp, "0x"); // will display the hex bytes instead
|
||||
// Go to Sign / Cancel
|
||||
ui_191_switch_to_sign();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the currently stored data is initialized as ASCII or not
|
||||
* The user has decided to skip the rest of the message
|
||||
*/
|
||||
static bool is_value_str_ascii() {
|
||||
return (memcmp(strings.tmp.tmp, "0x", 2) != 0);
|
||||
void skip_rest_of_message(void) {
|
||||
states.sign_state = STATE_191_HASH_ONLY;
|
||||
if (tmpCtx.messageSigningContext.remainingLength > 0) {
|
||||
apdu_reply(0x9000);
|
||||
} else {
|
||||
ui_191_switch_to_sign();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the global UI string variable by formatting & appending the new data to it
|
||||
*
|
||||
* @param[in] data the input data
|
||||
* @param[in] length the data length
|
||||
* @param[in] is_ascii whether the data is ASCII or not
|
||||
* The user has decided to see the next chunk of the message
|
||||
*/
|
||||
static void feed_value_str(const uint8_t *const data, size_t length, bool is_ascii) {
|
||||
uint16_t value_strlen = strlen(strings.tmp.tmp);
|
||||
|
||||
if ((value_strlen + 1) < sizeof(strings.tmp.tmp)) {
|
||||
if (is_ascii) {
|
||||
uint8_t src_idx = 0;
|
||||
uint16_t dst_idx = value_strlen;
|
||||
bool prev_is_special = false;
|
||||
|
||||
while ((src_idx < length) && (dst_idx < sizeof(strings.tmp.tmp))) {
|
||||
if (prev_is_special) {
|
||||
if (!is_char_special(data[src_idx])) {
|
||||
prev_is_special = false;
|
||||
}
|
||||
} else {
|
||||
if (is_char_special(data[src_idx])) {
|
||||
prev_is_special = true;
|
||||
strings.tmp.tmp[dst_idx] = ' ';
|
||||
dst_idx += 1;
|
||||
}
|
||||
}
|
||||
if (!is_char_special(data[src_idx])) {
|
||||
strings.tmp.tmp[dst_idx] = data[src_idx];
|
||||
dst_idx += 1;
|
||||
}
|
||||
src_idx += 1;
|
||||
}
|
||||
|
||||
if (dst_idx < sizeof(strings.tmp.tmp)) {
|
||||
strings.tmp.tmp[dst_idx] = '\0';
|
||||
} else {
|
||||
const char marker[] = "...";
|
||||
|
||||
memcpy(strings.tmp.tmp + sizeof(strings.tmp.tmp) - sizeof(marker),
|
||||
marker,
|
||||
sizeof(marker));
|
||||
}
|
||||
} else {
|
||||
// truncate to strings.tmp.tmp 's size
|
||||
length = MIN(length, (sizeof(strings.tmp.tmp) - value_strlen) / 2);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
snprintf(strings.tmp.tmp + value_strlen + 2 * i,
|
||||
sizeof(strings.tmp.tmp) - value_strlen - 2 * i,
|
||||
"%02X",
|
||||
data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleSignPersonalMessage(uint8_t p1,
|
||||
uint8_t p2,
|
||||
const uint8_t *workBuffer,
|
||||
uint8_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx) {
|
||||
UNUSED(tx);
|
||||
uint8_t hashMessage[INT256_LENGTH];
|
||||
|
||||
if (p1 == P1_FIRST) {
|
||||
char tmp[11] = {0};
|
||||
|
||||
if (appState != APP_STATE_IDLE) {
|
||||
reset_app_context();
|
||||
}
|
||||
appState = APP_STATE_SIGNING_MESSAGE;
|
||||
|
||||
workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32);
|
||||
|
||||
if (workBuffer == NULL) {
|
||||
THROW(0x6a80);
|
||||
}
|
||||
|
||||
if (dataLength < sizeof(uint32_t)) {
|
||||
PRINTF("Invalid data\n");
|
||||
THROW(0x6a80);
|
||||
}
|
||||
|
||||
tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0);
|
||||
workBuffer += sizeof(uint32_t);
|
||||
dataLength -= sizeof(uint32_t);
|
||||
// Initialize message header + length
|
||||
cx_keccak_init(&global_sha3, 256);
|
||||
cx_hash((cx_hash_t *) &global_sha3,
|
||||
0,
|
||||
(uint8_t *) SIGN_MAGIC,
|
||||
sizeof(SIGN_MAGIC) - 1,
|
||||
NULL,
|
||||
0);
|
||||
snprintf(tmp, sizeof(tmp), "%u", tmpCtx.messageSigningContext.remainingLength);
|
||||
cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) tmp, strlen(tmp), NULL, 0);
|
||||
cx_sha256_init(&tmpContent.sha2);
|
||||
|
||||
init_value_str(is_data_ascii(workBuffer, dataLength));
|
||||
|
||||
} else if (p1 != P1_MORE) {
|
||||
THROW(0x6B00);
|
||||
}
|
||||
if (p2 != 0) {
|
||||
THROW(0x6B00);
|
||||
}
|
||||
if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_MESSAGE)) {
|
||||
PRINTF("Signature not initialized\n");
|
||||
THROW(0x6985);
|
||||
}
|
||||
if (dataLength > tmpCtx.messageSigningContext.remainingLength) {
|
||||
THROW(0x6A80);
|
||||
}
|
||||
|
||||
cx_hash((cx_hash_t *) &global_sha3, 0, workBuffer, dataLength, NULL, 0);
|
||||
cx_hash((cx_hash_t *) &tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0);
|
||||
tmpCtx.messageSigningContext.remainingLength -= dataLength;
|
||||
|
||||
feed_value_str(workBuffer, dataLength, is_value_str_ascii());
|
||||
|
||||
if (tmpCtx.messageSigningContext.remainingLength == 0) {
|
||||
cx_hash((cx_hash_t *) &global_sha3,
|
||||
CX_LAST,
|
||||
workBuffer,
|
||||
0,
|
||||
tmpCtx.messageSigningContext.hash,
|
||||
32);
|
||||
cx_hash((cx_hash_t *) &tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32);
|
||||
|
||||
#ifdef NO_CONSENT
|
||||
io_seproxyhal_touch_signMessage_ok(NULL);
|
||||
#else // NO_CONSENT
|
||||
ui_display_sign();
|
||||
#endif // NO_CONSENT
|
||||
|
||||
*flags |= IO_ASYNCH_REPLY;
|
||||
|
||||
} else {
|
||||
THROW(0x9000);
|
||||
void continue_displaying_message(void) {
|
||||
reset_ui_buffer();
|
||||
if (unprocessed_length() > 0) {
|
||||
feed_display();
|
||||
}
|
||||
}
|
||||
|
||||
12
src_features/signMessage/sign_message.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef SIGN_MESSAGE_H_
|
||||
#define SIGN_MESSAGE_H_
|
||||
|
||||
#define UI_191_BUFFER strings.tmp.tmp
|
||||
|
||||
typedef enum { STATE_191_HASH_DISPLAY = 0, STATE_191_HASH_ONLY } sign_message_state;
|
||||
|
||||
void question_switcher(void);
|
||||
void skip_rest_of_message(void);
|
||||
void continue_displaying_message(void);
|
||||
|
||||
#endif // SIGN_MESSAGE_H_
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "shared_context.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_signMessage_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
unsigned int io_seproxyhal_touch_signMessage_ok(void) {
|
||||
uint8_t privateKeyData[INT256_LENGTH];
|
||||
uint8_t signature[100];
|
||||
cx_ecfp_private_key_t privateKey;
|
||||
@@ -46,8 +45,7 @@ unsigned int io_seproxyhal_touch_signMessage_ok(__attribute__((unused)) const ba
|
||||
return 0; // do not redraw the widget
|
||||
}
|
||||
|
||||
unsigned int io_seproxyhal_touch_signMessage_cancel(__attribute__((unused))
|
||||
const bagl_element_t *e) {
|
||||
unsigned int io_seproxyhal_touch_signMessage_cancel(void) {
|
||||
reset_app_context();
|
||||
G_io_apdu_buffer[0] = 0x69;
|
||||
G_io_apdu_buffer[1] = 0x85;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "typed_data.h"
|
||||
#include "apdu_constants.h" // APDU response codes
|
||||
#include "shared_context.h" // reset_app_context
|
||||
#include "ui_callbacks.h" // ui_idle
|
||||
#include "common_ui.h" // ui_idle
|
||||
|
||||
e_struct_init struct_state = NOT_INITIALIZED;
|
||||
s_eip712_context *eip712_context = NULL;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_712.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "shared_context.h"
|
||||
#include "utils.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_tx_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
uint8_t privateKeyData[INT256_LENGTH];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifdef HAVE_STARKWARE
|
||||
|
||||
#include "shared_context.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
#include "feature_stark_getPublicKey.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_stark_pubkey_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "shared_context.h"
|
||||
#include "stark_utils.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_stark_ok(__attribute__((unused)) const bagl_element_t *e) {
|
||||
uint8_t privateKeyData[32];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "shared_context.h"
|
||||
#include "stark_utils.h"
|
||||
#include "ui_callbacks.h"
|
||||
#include "common_ui.h"
|
||||
|
||||
unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(__attribute__((unused))
|
||||
const bagl_element_t *e) {
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "eth_plugin_interface.h"
|
||||
#include "shared_context.h" // TODO : rewrite as independant code
|
||||
#include "eth_plugin_internal.h" // TODO : rewrite as independant code
|
||||
#include "utils.h"
|
||||
#include "ethUtils.h"
|
||||
|
||||
typedef enum {
|
||||
COMPOUND_REDEEM_UNDERLYING = 0,
|
||||
COMPOUND_REDEEM,
|
||||
COMPOUND_MINT,
|
||||
CETH_MINT
|
||||
} compoundSelector_t;
|
||||
|
||||
static const uint8_t COMPOUND_EXPECTED_DATA_SIZE[] = {
|
||||
4 + 32,
|
||||
4 + 32,
|
||||
4 + 32,
|
||||
4,
|
||||
};
|
||||
|
||||
// redeemUnderlying : redeemAmount (32)
|
||||
// redeem underlying token
|
||||
// redeem : redeemTokens (32)
|
||||
// redeem Ctoken
|
||||
// mint : mintAmount (32)
|
||||
// lend some token
|
||||
// mint :
|
||||
// lend some Ether
|
||||
|
||||
typedef struct compound_parameters_t {
|
||||
uint8_t selectorIndex;
|
||||
uint8_t amount[32];
|
||||
char ticker_1[MAX_TICKER_LEN];
|
||||
uint8_t decimals;
|
||||
} compound_parameters_t;
|
||||
|
||||
typedef struct underlying_asset_decimals_t {
|
||||
char c_ticker[MAX_TICKER_LEN];
|
||||
uint8_t decimals;
|
||||
} underlying_asset_decimals_t;
|
||||
|
||||
/* Sadly, we don't have the information about the underlying asset's decimals, which can differ from
|
||||
the cToken decimals. Therefore, we hardcode a binding table. If Compound adds a lot of token in the
|
||||
future, we will have to move to a CAL based architecture instead, as this one doesn't scale well.*/
|
||||
#define NUM_COMPOUND_BINDINGS 9
|
||||
const underlying_asset_decimals_t UNDERLYING_ASSET_DECIMALS[NUM_COMPOUND_BINDINGS] = {
|
||||
{"cDAI", 18},
|
||||
{"CETH", 18},
|
||||
{"CUSDC", 6},
|
||||
{"CZRX", 18},
|
||||
{"CUSDT", 6},
|
||||
{"CBTC", 8},
|
||||
{"CBAT", 18},
|
||||
{"CREP", 18},
|
||||
{"cSAI", 18},
|
||||
};
|
||||
|
||||
bool get_underlying_asset_decimals(char *compound_ticker, uint8_t *out_decimals) {
|
||||
for (size_t i = 0; i < NUM_COMPOUND_BINDINGS; i++) {
|
||||
underlying_asset_decimals_t *binding =
|
||||
(underlying_asset_decimals_t *) PIC(&UNDERLYING_ASSET_DECIMALS[i]);
|
||||
if (strncmp(binding->c_ticker,
|
||||
compound_ticker,
|
||||
strnlen(binding->c_ticker, MAX_TICKER_LEN)) == 0) {
|
||||
*out_decimals = binding->decimals;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void compound_plugin_call(int message, void *parameters) {
|
||||
switch (message) {
|
||||
case ETH_PLUGIN_INIT_CONTRACT: {
|
||||
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
|
||||
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
||||
size_t i;
|
||||
for (i = 0; i < NUM_COMPOUND_SELECTORS; i++) {
|
||||
if (memcmp((uint8_t *) PIC(COMPOUND_SELECTORS[i]), msg->selector, SELECTOR_SIZE) ==
|
||||
0) {
|
||||
context->selectorIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// enforce that ETH amount should be 0, except in ceth.mint case
|
||||
if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)) {
|
||||
if (context->selectorIndex != CETH_MINT) {
|
||||
PRINTF("Eth amount is not zero and token minted is not CETH!\n");
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == NUM_COMPOUND_SELECTORS) {
|
||||
PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector);
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
if (msg->dataSize != COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex]) {
|
||||
PRINTF("Unexpected data size for command %d expected %d got %d\n",
|
||||
context->selectorIndex,
|
||||
COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex],
|
||||
msg->dataSize);
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
if (context->selectorIndex == CETH_MINT) {
|
||||
// ETH amount 0x1234 is stored 0x12340000...000 instead of 0x00....001234, so we
|
||||
// strip the following zeroes when copying
|
||||
memset(context->amount, 0, sizeof(context->amount));
|
||||
memmove(context->amount + sizeof(context->amount) -
|
||||
msg->pluginSharedRO->txContent->value.length,
|
||||
msg->pluginSharedRO->txContent->value.value,
|
||||
msg->pluginSharedRO->txContent->value.length);
|
||||
}
|
||||
PRINTF("compound plugin inititialized\n");
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_PROVIDE_PARAMETER: {
|
||||
ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
|
||||
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
||||
PRINTF("compound plugin provide parameter %d %.*H\n",
|
||||
msg->parameterOffset,
|
||||
32,
|
||||
msg->parameter);
|
||||
if (context->selectorIndex != CETH_MINT) {
|
||||
switch (msg->parameterOffset) {
|
||||
case 4:
|
||||
memmove(context->amount, msg->parameter, 32);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unhandled parameter offset\n");
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
PRINTF("CETH contract expects no parameters\n");
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_FINALIZE: {
|
||||
ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
|
||||
PRINTF("compound plugin finalize\n");
|
||||
msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination;
|
||||
msg->numScreens = 2;
|
||||
msg->uiType = ETH_UI_TYPE_GENERIC;
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_PROVIDE_INFO: {
|
||||
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
|
||||
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
||||
PRINTF("compound plugin provide token: %d\n", (msg->item1 != NULL));
|
||||
if (msg->item1 != NULL) {
|
||||
strlcpy(context->ticker_1, msg->item1->token.ticker, MAX_TICKER_LEN);
|
||||
switch (context->selectorIndex) {
|
||||
case COMPOUND_REDEEM_UNDERLYING:
|
||||
case COMPOUND_MINT:
|
||||
case CETH_MINT:
|
||||
msg->result =
|
||||
get_underlying_asset_decimals(context->ticker_1, &context->decimals)
|
||||
? ETH_PLUGIN_RESULT_OK
|
||||
: ETH_PLUGIN_RESULT_FALLBACK;
|
||||
break;
|
||||
|
||||
// Only case where we use the compound contract decimals
|
||||
case COMPOUND_REDEEM:
|
||||
context->decimals = msg->item1->token.decimals;
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
break;
|
||||
|
||||
default:
|
||||
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_ID: {
|
||||
ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
|
||||
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
||||
strlcpy(msg->name, "Type", msg->nameLength);
|
||||
switch (context->selectorIndex) {
|
||||
case COMPOUND_REDEEM_UNDERLYING:
|
||||
case COMPOUND_REDEEM:
|
||||
strlcpy(msg->version, "Redeem", msg->versionLength);
|
||||
break;
|
||||
|
||||
case COMPOUND_MINT:
|
||||
case CETH_MINT:
|
||||
strlcpy(msg->version, "Lend", msg->versionLength);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
strlcat(msg->version, " Assets", msg->versionLength);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_UI: {
|
||||
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
|
||||
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
||||
switch (msg->screenIndex) {
|
||||
case 0: {
|
||||
strlcpy(msg->title, "Amount", msg->titleLength);
|
||||
char *ticker_ptr = context->ticker_1;
|
||||
/* skip "c" in front of cToken unless we use "redeem", as
|
||||
redeem is the only operation dealing with a cToken amount */
|
||||
if (context->selectorIndex != COMPOUND_REDEEM) {
|
||||
ticker_ptr++;
|
||||
}
|
||||
amountToString(context->amount,
|
||||
sizeof(context->amount),
|
||||
context->decimals,
|
||||
ticker_ptr,
|
||||
msg->msg,
|
||||
100);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
case 1:
|
||||
strlcpy(msg->title, "Contract", msg->titleLength);
|
||||
strlcpy(msg->msg, "Compound ", msg->msgLength);
|
||||
strlcat(msg->msg,
|
||||
context->ticker_1 + 1,
|
||||
msg->msgLength); // remove the 'c' char at beginning of compound ticker
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
PRINTF("Unhandled message %d\n", message);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
def test_configuration(cmd):
|
||||
if cmd.model == "nanos":
|
||||
assert cmd.get_configuration() == (14, 1, 9, 20)
|
||||
|
||||
assert cmd.get_configuration() == (14, 1, 10, 0)
|
||||
|
||||
if cmd.model == "nanox":
|
||||
assert cmd.get_configuration() == (14, 1, 9, 20)
|
||||
assert cmd.get_configuration() == (14, 1, 10, 0)
|
||||
|
||||
if cmd.model == "nanosp":
|
||||
assert cmd.get_configuration() == (14, 1, 9, 20)
|
||||
assert cmd.get_configuration() == (14, 1, 10, 0)
|
||||
|
||||
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 486 B |
|
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 481 B |
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 484 B |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 449 B |
BIN
tests/zemu/snapshots/nanos_eip191_nonascii/00009.png
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
tests/zemu/snapshots/nanos_eip191_nonascii/00010.png
Normal file
|
After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 478 B |
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 404 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00010.png
Normal file
|
After Width: | Height: | Size: 448 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00011.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00012.png
Normal file
|
After Width: | Height: | Size: 406 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00013.png
Normal file
|
After Width: | Height: | Size: 462 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00014.png
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00015.png
Normal file
|
After Width: | Height: | Size: 449 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00016.png
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
tests/zemu/snapshots/nanos_eip191_opensea/00017.png
Normal file
|
After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 846 B |
|
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 857 B |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 829 B |
|
Before Width: | Height: | Size: 837 B After Width: | Height: | Size: 723 B |
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 849 B |
|
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 626 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 912 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 692 B |
|
Before Width: | Height: | Size: 382 B After Width: | Height: | Size: 463 B |
BIN
tests/zemu/snapshots/nanox_eip191_opensea/00010.png
Normal file
|
After Width: | Height: | Size: 499 B |
BIN
tests/zemu/snapshots/nanox_eip191_opensea/00011.png
Normal file
|
After Width: | Height: | Size: 463 B |
BIN
tests/zemu/snapshots/nanox_eip191_opensea/00012.png
Normal file
|
After Width: | Height: | Size: 382 B |
@@ -34,7 +34,7 @@ nano_models.forEach(function(model) {
|
||||
|
||||
await waitForAppScreen(sim);
|
||||
|
||||
const rclicks = (model.letter == 'S') ? 6 : 4;
|
||||
const rclicks = (model.letter == 'S') ? 8 : 4;
|
||||
await sim.navigateAndCompareSnapshots('.', model.name + '_eip191_nonascii', [rclicks, -1, 0]);
|
||||
|
||||
await expect(tx).resolves.toEqual({
|
||||
@@ -54,7 +54,14 @@ nano_models.forEach(function(model) {
|
||||
|
||||
await waitForAppScreen(sim);
|
||||
|
||||
await sim.navigateAndCompareSnapshots('.', model.name + '_eip191_opensea', [7, -1, 0]);
|
||||
if (model.letter == 'S')
|
||||
{
|
||||
await sim.navigateAndCompareSnapshots('.', model.name + '_eip191_opensea', [1, 5, 1, 6, 0, 1, -1, 0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
await sim.navigateAndCompareSnapshots('.', model.name + '_eip191_opensea', [1, 5, 1, 2, 1, -1, 0]);
|
||||
}
|
||||
|
||||
await expect(tx).resolves.toEqual({
|
||||
"v": 28,
|
||||
|
||||