Files
app-ethereum/src_genericwallet/main.c

1971 lines
53 KiB
C

/*******************************************************************************
* Ledger Blue
* (c) 2016 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/
#include "os.h"
#include "cx.h"
#include <stdbool.h>
#include "ethUstream.h"
#include "ethUtils.h"
#include "uint256.h"
#include "os_io_seproxyhal.h"
#include "string.h"
#ifdef HAVE_U2F
#include "u2f_service.h"
#include "u2f_transport.h"
volatile unsigned char u2fInputBuffer[64];
volatile unsigned char u2fOutputBuffer[64];
volatile unsigned char u2fMessageBuffer[U2F_MAX_MESSAGE_SIZE];
extern void USB_power_U2F(unsigned char enabled, unsigned char fido);
extern bool fidoActivated;
#endif
unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];
unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e);
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);
uint32_t set_result_get_publicKey(void);
#define MAX_BIP32_PATH 10
#define CLA 0xE0
#define INS_GET_PUBLIC_KEY 0x02
#define INS_SIGN 0x04
#define INS_GET_APP_CONFIGURATION 0x06
#define P1_CONFIRM 0x01
#define P1_NON_CONFIRM 0x00
#define P2_NO_CHAINCODE 0x00
#define P2_CHAINCODE 0x01
#define P1_FIRST 0x00
#define P1_MORE 0x80
#define OFFSET_CLA 0
#define OFFSET_INS 1
#define OFFSET_P1 2
#define OFFSET_P2 3
#define OFFSET_LC 4
#define OFFSET_CDATA 5
#define WEI_TO_ETHER 18
typedef struct publicKeyContext_t {
cx_ecfp_public_key_t publicKey;
uint8_t address[41];
uint8_t chainCode[32];
bool getChaincode;
} publicKeyContext_t;
typedef struct transactionContext_t {
uint8_t pathLength;
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t hash[32];
} transactionContext_t;
union {
publicKeyContext_t publicKeyContext;
transactionContext_t transactionContext;
} tmpCtx;
txContext_t txContext;
txContent_t txContent;
cx_sha3_t sha3;
volatile uint8_t dataAllowed;
volatile uint8_t fidoTransport;
volatile char addressSummary[21];
volatile char address1[21];
volatile char address2[21];
volatile char fullAmount[50];
volatile char maxFee[50];
volatile bool dataPresent;
volatile bool skipWarning;
#ifdef HAVE_U2F
volatile u2f_service_t u2fService;
#endif
ux_state_t ux;
// display stepped screens
unsigned int ux_step;
unsigned int ux_step_count;
typedef struct internalStorage_t {
uint8_t dataAllowed;
uint8_t fidoTransport;
uint8_t initialized;
} internalStorage_t;
WIDE internalStorage_t N_storage_real;
#define N_storage (*(WIDE internalStorage_t *)PIC(&N_storage_real))
static const char const CONTRACT_ADDRESS[] = "New contract";
#ifdef HAVE_U2F
void u2f_proxy_response(u2f_service_t *service, unsigned int tx) {
os_memset(service->messageBuffer, 0, 5);
os_memmove(service->messageBuffer + 5, G_io_apdu_buffer, tx);
service->messageBuffer[tx + 5] = 0x90;
service->messageBuffer[tx + 6] = 0x00;
u2f_send_fragmented_response(service, U2F_CMD_MSG, service->messageBuffer,
tx + 7, true);
}
#endif
// UI displayed when no signature proposal has been received
static const bagl_element_t const ui_idle_blue[] = {
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 480, 0, 0, BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0, BAGL_FILL, 0x1d2028, 0x1d2028,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0, BAGL_FILL, 0xFFFFFF, 0x1d2028,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
"Ethereum Wallet",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 190, 215, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER |
BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"Exit",
0,
0x37ae99,
0xF9F9F9,
io_seproxyhal_touch_exit,
NULL,
NULL}};
unsigned int ui_idle_blue_button(unsigned int button_mask,
unsigned int button_mask_counter) {
return 0;
}
const bagl_element_t ui_idle_nanos[] = {
// type userid x y w h str rad
// fill fg bg fid iid txt touchparams... ]
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x01, 12, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_ETHEREUM_BADGE},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 33, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Use wallet to",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 34, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"view accounts",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x01, 118, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DOWN},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
// NO! //{{BAGL_LABELINE , 0x02, 34, 3, 128, 32,
// 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px, 0
// }, "view accounts", 0, 0, 0, NULL, NULL, NULL },
{{BAGL_LABELINE, 0x02, 0, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Settings",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 3, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_UP},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 118, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DOWN},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x03, 29, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DASHBOARD_BADGE},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x03, 50, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Quit app",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x03, 3, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_UP},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_idle_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
unsigned int ui_idle_nanos_state;
unsigned int ui_idle_nanos_prepro(const bagl_element_t *element) {
if (element->component.userid > 0) {
return (ui_idle_nanos_state == element->component.userid - 1);
}
return 1;
}
#ifdef HAVE_U2F
const bagl_element_t ui_settings_nanos[] = {
// type userid x y w h str rad
// fill fg bg fid iid txt touchparams... ]
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Contract data",
0,
0,
0,
NULL,
NULL,
NULL},
//{{BAGL_ICON , 0x01, 3, 14, 7, 4, 0, 0, 0
//, 0xFFFFFF, 0x000000, 0,
//BAGL_GLYPH_ICON_UP }, NULL, 0, 0, 0, NULL, NULL, NULL },
{{BAGL_ICON, 0x01, 118, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DOWN},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 35, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Browser support",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 3, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_UP},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 0, 3, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Contract data",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 0, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Browser support",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 118, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DOWN},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x13, 29, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x03, 61, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Back",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x03, 3, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_UP},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
#else
const bagl_element_t ui_settings_nanos[] = {
// type userid x y w h str rad
// fill fg bg fid iid txt touchparams... ]
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Contract data",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x01, 118, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_DOWN},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x12, 29, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 61, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Back",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 3, 14, 7, 4, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_UP},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
#endif
unsigned int ui_settings_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
unsigned int ui_settings_nanos_state;
unsigned int ui_settings_nanos_prepro(const bagl_element_t *element) {
if (element->component.userid > 0) {
unsigned char displayed = (ui_settings_nanos_state ==
((element->component.userid - 1) & 0x0F));
// display custom icon for
if (displayed && (element->component.userid == 0x13 ||
element->component.userid == 0x12)) {
extern unsigned int const C_icon_back_colors[];
extern unsigned char const C_icon_back_bitmap[];
io_seproxyhal_display_bitmap(40, 9, 14, 14, C_icon_back_colors, 1,
C_icon_back_bitmap);
// superseded
return 0;
}
return displayed;
}
return 1;
}
const bagl_element_t ui_settings_data_nanos[] = {
// erase
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 37, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px, 0},
"Allow",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 74, 11, 16, 10, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_TOGGLE_ON},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x03, 74, 11, 16, 10, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_TOGGLE_OFF},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
// icons
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CROSS},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CHECK},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_settings_data_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
unsigned int ui_settings_data_nanos_prepro(const bagl_element_t *element) {
unsigned int display = 1;
switch (element->component.userid) {
case 0x02:
display = dataAllowed;
break;
case 0x03:
display = !dataAllowed;
break;
}
return display;
}
#ifdef HAVE_U2F
const bagl_element_t ui_settings_fido_nanos[] = {
// erase
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 35, 19, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px, 0},
"Enable",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x02, 76, 11, 16, 10, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_TOGGLE_ON},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x03, 76, 11, 16, 10, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_TOGGLE_OFF},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
// icons
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CROSS},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CHECK},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_settings_fido_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
unsigned int ui_settings_fido_nanos_prepro(const bagl_element_t *element) {
unsigned int display = 1;
switch (element->component.userid) {
case 0x02:
display = fidoTransport;
break;
case 0x03:
display = !fidoTransport;
break;
}
return display;
}
#endif
bagl_element_t const ui_address_blue[] = {
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 480, 0, 0, BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
// type id x y w h s r fill
// fg bg font icon text, out, over, touch
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0, BAGL_FILL, 0x1d2028, 0x1d2028,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0, BAGL_FILL, 0xFFFFFF, 0x1d2028,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
"Ethereum Wallet",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 35, 385, 120, 40, 0, 6,
BAGL_FILL, 0xcccccc, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER |
BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"CANCEL",
0,
0x37ae99,
0xF9F9F9,
io_seproxyhal_touch_address_cancel,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 165, 385, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER |
BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"CONFIRM",
0,
0x37ae99,
0xF9F9F9,
io_seproxyhal_touch_address_ok,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 147, 320, 32, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Confirm address",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 280, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_16px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)addressSummary,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 310, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)address1,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 330, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)address2,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_address_blue_button(unsigned int button_mask,
unsigned int button_mask_counter) {
return 0;
}
const bagl_element_t ui_address_nanos[] = {
// type userid x y w h str rad
// fill fg bg fid iid txt touchparams... ]
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x01, 31, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_EYE_BADGE},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 52, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Confirm",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 53, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"account",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Account",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(char *)addressSummary,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CROSS},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CHECK},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_address_prepro(const bagl_element_t *element) {
if (element->component.userid > 0) {
unsigned int display = (ux_step == element->component.userid - 1);
if (display) {
switch (element->component.userid) {
case 1:
io_seproxyhal_setup_ticker(2000);
break;
case 2:
io_seproxyhal_setup_ticker(3000);
break;
}
}
return display;
}
return 1;
}
unsigned int ui_address_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
// UI to approve or deny the signature proposal
static const bagl_element_t const ui_approval_blue[] = {
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 480, 0, 0, BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
// type id x y w h s r fill
// fg bg font icon text, out, over, touch
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0, BAGL_FILL, 0x1d2028, 0x1d2028,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0, BAGL_FILL, 0xFFFFFF, 0x1d2028,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
"Ethereum Wallet",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 35, 385, 120, 40, 0, 6,
BAGL_FILL, 0xcccccc, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER |
BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"CANCEL",
0,
0x37ae99,
0xF9F9F9,
io_seproxyhal_touch_tx_cancel,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 165, 385, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER |
BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"CONFIRM",
0,
0x37ae99,
0xF9F9F9,
io_seproxyhal_touch_tx_ok,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 87, 320, 32, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"CONFIRM TRANSACTION",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 125, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_16px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)fullAmount,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 175, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_16px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)addressSummary,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 205, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)address1,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 225, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)address2,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 265, 320, 32, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Maximum fee",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 0, 305, 320, 33, 0, 0, 0, 0x000000, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_16px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(const char *)maxFee,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_approval_blue_button(unsigned int button_mask,
unsigned int button_mask_counter) {
return 0;
}
const bagl_element_t ui_approval_nanos[] = {
// type userid x y w h str rad
// fill fg bg fid iid txt touchparams... ]
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CROSS},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_CHECK},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_ICON, 0x01, 21, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_TRANSACTION_BADGE},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 42, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Confirm",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 43, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"transaction",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"WARNING",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 23, 26, 82, 11, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Data present",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x03, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Amount",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x03, 23, 26, 82, 11, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullAmount,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x04, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Recipient account",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x04, 16, 26, 96, 11, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
(char *)addressSummary,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x05, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Maximum fees",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x05, 23, 26, 82, 11, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)maxFee,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_approval_prepro(const bagl_element_t *element) {
unsigned int display = 1;
if (element->component.userid > 0) {
display = (ux_step == element->component.userid - 1);
if (display) {
switch (element->component.userid) {
case 1:
io_seproxyhal_setup_ticker(2000);
break;
case 2:
if (dataPresent) {
io_seproxyhal_setup_ticker(3000);
} else {
display = 0;
ux_step++; // display the next step
}
break;
case 3:
io_seproxyhal_setup_ticker(MAX(
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
case 4:
io_seproxyhal_setup_ticker(3000);
break;
case 5:
io_seproxyhal_setup_ticker(MAX(
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
}
}
}
return display;
}
unsigned int ui_approval_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
void ui_idle(void) {
skipWarning = false;
if (os_seph_features() &
SEPROXYHAL_TAG_SESSION_START_EVENT_FEATURE_SCREEN_BIG) {
UX_DISPLAY(ui_idle_blue, NULL);
} else {
ui_idle_nanos_state = 0; // start by displaying the idle first screen
UX_DISPLAY(ui_idle_nanos, ui_idle_nanos_prepro);
}
}
unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e) {
// Go back to the dashboard
os_sched_exit(0);
return 0; // do not redraw the widget
}
unsigned int ui_idle_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT: // UP
if (ui_idle_nanos_state != 0) {
ui_idle_nanos_state--;
UX_DISPLAY(ui_idle_nanos, ui_idle_nanos_prepro);
}
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT: // DOWN
if (ui_idle_nanos_state != 2) {
ui_idle_nanos_state++;
UX_DISPLAY(ui_idle_nanos, ui_idle_nanos_prepro);
}
break;
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: // Settings, EXIT
if (ui_idle_nanos_state == 1) {
ui_settings_nanos_state = 0;
UX_DISPLAY(ui_settings_nanos, ui_settings_nanos_prepro);
} else if (ui_idle_nanos_state == 2) {
io_seproxyhal_touch_exit(NULL);
}
break;
}
return 0;
}
unsigned int ui_settings_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT: // UP
if (ui_settings_nanos_state != 0) {
ui_settings_nanos_state--;
UX_DISPLAY(ui_settings_nanos, ui_settings_nanos_prepro);
}
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT: // DOWN
#ifdef HAVE_U2F
if (ui_settings_nanos_state != 2) {
#else
if (ui_settings_nanos_state != 1) {
#endif
ui_settings_nanos_state++;
UX_DISPLAY(ui_settings_nanos, ui_settings_nanos_prepro);
}
break;
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: // Settings, EXIT
if (ui_settings_nanos_state == 0) {
dataAllowed = N_storage.dataAllowed;
UX_DISPLAY(ui_settings_data_nanos, ui_settings_data_nanos_prepro);
}
#ifdef HAVE_U2F
else if (ui_settings_nanos_state == 1) {
fidoTransport = N_storage.fidoTransport;
UX_DISPLAY(ui_settings_fido_nanos, ui_settings_fido_nanos_prepro);
} else if (ui_settings_nanos_state == 2) {
UX_DISPLAY(ui_idle_nanos, ui_idle_nanos_prepro);
}
#else
else if (ui_settings_nanos_state == 1) {
UX_DISPLAY(ui_idle_nanos, ui_idle_nanos_prepro);
}
#endif
break;
}
return 0;
}
unsigned int ui_settings_data_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
dataAllowed = 0x00;
goto set;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
dataAllowed = 0x01;
set:
if (N_storage.dataAllowed != dataAllowed) {
nvm_write(&N_storage.dataAllowed, (void *)&dataAllowed,
sizeof(uint8_t));
}
// no break is intentional
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT:
UX_DISPLAY(ui_settings_nanos, ui_settings_nanos_prepro);
break;
}
return 0;
}
#ifdef HAVE_U2F
unsigned int ui_settings_fido_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
fidoTransport = 0x00;
goto set;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
fidoTransport = 0x01;
set:
if (N_storage.fidoTransport != fidoTransport) {
nvm_write(&N_storage.fidoTransport, (void *)&fidoTransport,
sizeof(uint8_t));
USB_power_U2F(0, 0);
USB_power_U2F(1, N_storage.fidoTransport);
}
// no break is intentional
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT:
UX_DISPLAY(ui_settings_nanos, ui_settings_nanos_prepro);
break;
}
return 0;
}
#endif
unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e) {
uint32_t tx = set_result_get_publicKey();
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
#ifdef HAVE_U2F
if (fidoActivated) {
u2f_proxy_response((u2f_service_t *)&u2fService, tx);
} else {
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
}
#else
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
#endif
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e) {
G_io_apdu_buffer[0] = 0x69;
G_io_apdu_buffer[1] = 0x85;
#ifdef HAVE_U2F
if (fidoActivated) {
u2f_proxy_response((u2f_service_t *)&u2fService, 2);
} else {
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
}
#else
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
#endif
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int ui_address_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT: // CANCEL
io_seproxyhal_touch_address_cancel(NULL);
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT: { // OK
io_seproxyhal_touch_address_ok(NULL);
break;
}
}
return 0;
}
unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e) {
uint8_t privateKeyData[32];
uint8_t signature[100];
uint8_t signatureLength;
cx_ecfp_private_key_t privateKey;
uint32_t tx = 0;
uint8_t rLength, sLength, rOffset, sOffset;
os_perso_derive_node_bip32(
CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path,
tmpCtx.transactionContext.pathLength, privateKeyData, NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
os_memset(privateKeyData, 0, sizeof(privateKeyData));
signatureLength =
cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
tmpCtx.transactionContext.hash,
sizeof(tmpCtx.transactionContext.hash), signature);
os_memset(&privateKey, 0, sizeof(privateKey));
// Parity is present in the sequence tag in the legacy API
G_io_apdu_buffer[0] = 27 + (signature[0] & 0x01);
rLength = signature[3];
sLength = signature[4 + rLength + 1];
rOffset = (rLength == 33 ? 1 : 0);
sOffset = (sLength == 33 ? 1 : 0);
os_memmove(G_io_apdu_buffer + 1, signature + 4 + rOffset, 32);
os_memmove(G_io_apdu_buffer + 1 + 32, signature + 4 + rLength + 2 + sOffset,
32);
tx = 65;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
#ifdef HAVE_U2F
if (fidoActivated) {
u2f_proxy_response((u2f_service_t *)&u2fService, tx);
} else {
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
}
#else
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
#endif
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e) {
G_io_apdu_buffer[0] = 0x69;
G_io_apdu_buffer[1] = 0x85;
#ifdef HAVE_U2F
if (fidoActivated) {
u2f_proxy_response((u2f_service_t *)&u2fService, 2);
} else {
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
}
#else
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
#endif
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int ui_approval_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
io_seproxyhal_touch_tx_cancel(NULL);
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT: {
io_seproxyhal_touch_tx_ok(NULL);
break;
}
}
return 0;
}
unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
switch (channel & ~(IO_FLAGS)) {
case CHANNEL_KEYBOARD:
break;
// multiplexed io exchange over a SPI channel and TLV encapsulated protocol
case CHANNEL_SPI:
if (tx_len) {
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);
if (channel & IO_RESET_AFTER_REPLIED) {
reset();
}
return 0; // nothing received from the master so far (it's a tx
// transaction)
} else {
return io_seproxyhal_spi_recv(G_io_apdu_buffer,
sizeof(G_io_apdu_buffer), 0);
}
default:
THROW(INVALID_PARAMETER);
}
return 0;
}
uint32_t set_result_get_publicKey() {
uint32_t tx = 0;
G_io_apdu_buffer[tx++] = 65;
os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.publicKey.W, 65);
tx += 65;
G_io_apdu_buffer[tx++] = 40;
os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.address, 40);
tx += 40;
if (tmpCtx.publicKeyContext.getChaincode) {
os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.chainCode,
32);
tx += 32;
}
return tx;
}
void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target) {
uint8_t tmp[32];
os_memset(tmp, 0, 32);
os_memmove(tmp + 32 - length, data, length);
readu256BE(tmp, target);
}
bool customProcessor(txContext_t *context) {
if ((context->currentField == TX_RLP_DATA) &&
(context->currentFieldLength != 0)) {
if (!N_storage.dataAllowed) {
screen_printf("Data field forbidden\n");
THROW(EXCEPTION);
} else {
dataPresent = true;
}
}
return false;
}
void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer,
uint16_t dataLength, volatile unsigned int *flags,
volatile unsigned int *tx) {
UNUSED(dataLength);
uint8_t privateKeyData[32];
uint32_t bip32Path[MAX_BIP32_PATH];
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer++);
cx_ecfp_private_key_t privateKey;
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
screen_printf("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00);
}
if ((p2 != P2_CHAINCODE) && (p2 != P2_NO_CHAINCODE)) {
THROW(0x6B00);
}
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = (dataBuffer[0] << 24) | (dataBuffer[1] << 16) |
(dataBuffer[2] << 8) | (dataBuffer[3]);
dataBuffer += 4;
}
tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength,
privateKeyData,
(tmpCtx.publicKeyContext.getChaincode
? tmpCtx.publicKeyContext.chainCode
: NULL));
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey,
&privateKey, 1);
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey,
tmpCtx.publicKeyContext.address, &sha3);
if (p1 == P1_NON_CONFIRM) {
*tx = set_result_get_publicKey();
THROW(0x9000);
} else {
addressSummary[0] = '0';
addressSummary[1] = 'x';
os_memmove((unsigned char *)(addressSummary + 2),
tmpCtx.publicKeyContext.address, 4);
os_memmove((unsigned char *)(addressSummary + 6), "...", 3);
os_memmove((unsigned char *)(addressSummary + 9),
tmpCtx.publicKeyContext.address + 40 - 4, 4);
addressSummary[13] = '\0';
os_memmove((unsigned char *)address1, tmpCtx.publicKeyContext.address,
20);
address1[20] = '\0';
os_memmove((unsigned char *)address2,
tmpCtx.publicKeyContext.address + 20, 20);
address2[20] = '\0';
// prepare for a UI based reply
skipWarning = false;
if (os_seph_features() &
SEPROXYHAL_TAG_SESSION_START_EVENT_FEATURE_SCREEN_BIG) {
UX_DISPLAY(ui_address_blue, NULL);
} else {
ux_step = 0;
ux_step_count = 2;
UX_DISPLAY(ui_address_nanos, ui_address_prepro);
}
*flags |= IO_ASYNCH_REPLY;
}
}
void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer,
uint16_t dataLength, volatile unsigned int *flags,
volatile unsigned int *tx) {
UNUSED(tx);
parserStatus_e txResult;
uint256_t gasPrice, startGas, uint256;
uint32_t i;
uint8_t address[41];
if (p1 == P1_FIRST) {
tmpCtx.transactionContext.pathLength = workBuffer[0];
if ((tmpCtx.transactionContext.pathLength < 0x01) ||
(tmpCtx.transactionContext.pathLength > MAX_BIP32_PATH)) {
screen_printf("Invalid path\n");
THROW(0x6a80);
}
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) {
tmpCtx.transactionContext.bip32Path[i] =
(workBuffer[0] << 24) | (workBuffer[1] << 16) |
(workBuffer[2] << 8) | (workBuffer[3]);
workBuffer += 4;
dataLength -= 4;
}
dataPresent = false;
initTx(&txContext, &sha3, &txContent, customProcessor, NULL);
} else if (p1 != P1_MORE) {
THROW(0x6B00);
}
if (p2 != 0) {
THROW(0x6B00);
}
if (txContext.currentField == TX_RLP_NONE) {
screen_printf("Parser not initialized\n");
THROW(0x6985);
}
txResult = processTx(&txContext, workBuffer, dataLength);
switch (txResult) {
case USTREAM_FINISHED:
break;
case USTREAM_PROCESSING:
THROW(0x9000);
case USTREAM_FAULT:
THROW(0x6A80);
default:
screen_printf("Unexpected parser status\n");
THROW(0x6A80);
}
// Store the hash
cx_hash((cx_hash_t *)&sha3, CX_LAST, tmpCtx.transactionContext.hash, 0,
tmpCtx.transactionContext.hash);
// Add address
if (txContent.destinationLength != 0) {
getEthAddressStringFromBinary(txContent.destination, address, &sha3);
addressSummary[0] = '0';
addressSummary[1] = 'x';
os_memmove((unsigned char *)(addressSummary + 2), address, 4);
os_memmove((unsigned char *)(addressSummary + 6), "...", 3);
os_memmove((unsigned char *)(addressSummary + 9), address + 40 - 4, 4);
addressSummary[13] = '\0';
os_memmove((unsigned char *)address1, address, 20);
address1[20] = '\0';
os_memmove((unsigned char *)address2, address + 20, 20);
address2[20] = '\0';
} else {
os_memmove((void *)addressSummary, CONTRACT_ADDRESS,
sizeof(CONTRACT_ADDRESS));
address1[0] = '\0';
address2[0] = '\0';
}
// Add amount in ethers
convertUint256BE(txContent.value.value, txContent.value.length, &uint256);
tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100);
i = 0;
while (G_io_apdu_buffer[100 + i]) {
i++;
}
adjustDecimals((char *)(G_io_apdu_buffer + 100), i,
(char *)G_io_apdu_buffer, 100, WEI_TO_ETHER);
i = 0;
fullAmount[0] = 'E';
fullAmount[1] = 'T';
fullAmount[2] = 'H';
fullAmount[3] = ' ';
while (G_io_apdu_buffer[i]) {
fullAmount[4 + i] = G_io_apdu_buffer[i];
i++;
}
fullAmount[4 + i] = '\0';
// Compute maximum fee
convertUint256BE(txContent.gasprice.value, txContent.gasprice.length,
&gasPrice);
convertUint256BE(txContent.startgas.value, txContent.startgas.length,
&startGas);
mul256(&gasPrice, &startGas, &uint256);
tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100);
i = 0;
while (G_io_apdu_buffer[100 + i]) {
i++;
}
adjustDecimals((char *)(G_io_apdu_buffer + 100), i,
(char *)G_io_apdu_buffer, 100, WEI_TO_ETHER);
i = 0;
maxFee[0] = 'E';
maxFee[1] = 'T';
maxFee[2] = 'H';
maxFee[3] = ' ';
while (G_io_apdu_buffer[i]) {
maxFee[4 + i] = G_io_apdu_buffer[i];
i++;
}
maxFee[4 + i] = '\0';
if (os_seph_features() &
SEPROXYHAL_TAG_SESSION_START_EVENT_FEATURE_SCREEN_BIG) {
skipWarning = false;
UX_DISPLAY(ui_approval_blue, NULL);
} else {
skipWarning = !dataPresent;
ux_step = 0;
ux_step_count = 5;
UX_DISPLAY(ui_approval_nanos, ui_approval_prepro);
}
*flags |= IO_ASYNCH_REPLY;
}
void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer,
uint16_t dataLength,
volatile unsigned int *flags,
volatile unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(workBuffer);
UNUSED(dataLength);
UNUSED(flags);
G_io_apdu_buffer[0] = (N_storage.dataAllowed ? 0x01 : 0x00);
G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION;
G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION;
G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION;
*tx = 4;
THROW(0x9000);
}
void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) {
unsigned short sw = 0;
BEGIN_TRY {
TRY {
if (G_io_apdu_buffer[OFFSET_CLA] != CLA) {
THROW(0x6E00);
}
switch (G_io_apdu_buffer[OFFSET_INS]) {
case INS_GET_PUBLIC_KEY:
handleGetPublicKey(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);
break;
case INS_SIGN:
handleSign(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);
break;
case INS_GET_APP_CONFIGURATION:
handleGetAppConfiguration(
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);
break;
default:
THROW(0x6D00);
break;
}
}
CATCH_OTHER(e) {
switch (e & 0xF000) {
case 0x6000:
// Wipe the transaction context and report the exception
sw = e;
os_memset(&txContext, 0, sizeof(txContext));
break;
case 0x9000:
// All is well
sw = e;
break;
default:
// Internal error
sw = 0x6800 | (e & 0x7FF);
break;
}
// Unexpected exception => report
G_io_apdu_buffer[*tx] = sw >> 8;
G_io_apdu_buffer[*tx + 1] = sw;
*tx += 2;
}
FINALLY {
}
}
END_TRY;
}
void sample_main(void) {
volatile unsigned int rx = 0;
volatile unsigned int tx = 0;
volatile unsigned int flags = 0;
// DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only
// goal is to retrieve APDU.
// When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make
// sure the io_event is called with a
// switch event, before the apdu is replied to the bootloader. This avoid
// APDU injection faults.
for (;;) {
volatile unsigned short sw = 0;
BEGIN_TRY {
TRY {
rx = tx;
tx = 0; // ensure no race in catch_other if io_exchange throws
// an error
rx = io_exchange(CHANNEL_APDU | flags, rx);
flags = 0;
// no apdu received, well, reset the session, and reset the
// bootloader configuration
if (rx == 0) {
THROW(0x6982);
}
handleApdu(&flags, &tx);
}
CATCH_OTHER(e) {
switch (e & 0xF000) {
case 0x6000:
// Wipe the transaction context and report the exception
sw = e;
os_memset(&txContext, 0, sizeof(txContext));
break;
case 0x9000:
// All is well
sw = e;
break;
default:
// Internal error
sw = 0x6800 | (e & 0x7FF);
break;
}
// Unexpected exception => report
G_io_apdu_buffer[tx] = sw >> 8;
G_io_apdu_buffer[tx + 1] = sw;
tx += 2;
}
FINALLY {
}
}
END_TRY;
}
// return_to_dashboard:
return;
}
void io_seproxyhal_display(const bagl_element_t *element) {
return io_seproxyhal_display_default((bagl_element_t *)element);
}
unsigned char io_event(unsigned char channel) {
// nothing done with the event, throw an error on the transport layer if
// needed
// can't have more than one tag in the reply, not supported yet.
switch (G_io_seproxyhal_spi_buffer[0]) {
case SEPROXYHAL_TAG_FINGER_EVENT:
UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer);
break;
case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT:
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
break;
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
if (UX_DISPLAYED()) {
// TODO perform actions after all screen elements have been
// displayed
} else {
// UX_DISPLAY_PROCESSED_EVENT();
// process elements until the prepro accepts or send a new one in
// the background
while (ux.elements && ux.elements_current < ux.elements_count &&
!io_seproxyhal_spi_is_status_sent()) {
if (!ux.elements_preprocessor ||
ux.elements_preprocessor(
&ux.elements[ux.elements_current])) {
io_seproxyhal_display(&ux.elements[ux.elements_current++]);
break;
}
ux.elements_current++;
}
}
break;
case SEPROXYHAL_TAG_TICKER_EVENT:
// prepare next screen
if (skipWarning && (ux_step == 0)) {
ux_step++;
}
ux_step = (ux_step + 1) % ux_step_count;
// redisplay screen
UX_REDISPLAY();
break;
// unknown events are acknowledged
default:
break;
}
// close the event if not done previously (by a display or whatever)
if (!io_seproxyhal_spi_is_status_sent()) {
io_seproxyhal_general_status();
}
// command has been processed, DO NOT reset the current APDU transport
return 1;
}
void app_exit(void) {
BEGIN_TRY_L(exit) {
TRY_L(exit) {
os_sched_exit(-1);
}
FINALLY_L(exit) {
}
}
END_TRY_L(exit);
}
__attribute__((section(".boot"))) int main(void) {
// exit critical section
__asm volatile("cpsie i");
os_memset(&txContext, 0, sizeof(txContext));
UX_INIT();
// ensure exception will work as planned
os_boot();
BEGIN_TRY {
TRY {
io_seproxyhal_init();
if (N_storage.initialized != 0x01) {
internalStorage_t storage;
storage.dataAllowed = 0x00;
storage.fidoTransport = 0x00;
storage.initialized = 0x01;
nvm_write(&N_storage, (void *)&storage,
sizeof(internalStorage_t));
}
#ifdef HAVE_U2F
os_memset((unsigned char *)&u2fService, 0, sizeof(u2fService));
u2fService.inputBuffer = (uint8_t *)u2fInputBuffer;
u2fService.outputBuffer = (uint8_t *)u2fOutputBuffer;
u2fService.messageBuffer = (uint8_t *)u2fMessageBuffer;
u2fService.messageBufferSize = U2F_MAX_MESSAGE_SIZE;
u2f_initialize_service((u2f_service_t *)&u2fService);
USB_power_U2F(1, N_storage.fidoTransport);
#else
USB_power_U2F(1, 0);
#endif
ui_idle();
sample_main();
}
CATCH_OTHER(e) {
}
FINALLY {
}
}
END_TRY;
app_exit();
return 0;
}