Skip to content

Instantly share code, notes, and snippets.

@alexi190
Created September 11, 2018 11:36
Show Gist options
  • Save alexi190/ee4197a513b7bfde161da8dd1291a487 to your computer and use it in GitHub Desktop.
Save alexi190/ee4197a513b7bfde161da8dd1291a487 to your computer and use it in GitHub Desktop.
/*******************************************************************************
* Tron Ledger Wallet
* (c) 2018 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 "helpers.h"
#include "os_io_seproxyhal.h"
#include "string.h"
#include "glyphs.h"
#include "parse.h"
extern bool fidoActivated;
bagl_element_t tmp_element;
unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];
uint32_t set_result_get_publicKey(void);
// Define command events
#define CLA 0xE0 // Start byte for any communications
#define INS_GET_PUBLIC_KEY 0x02
#define INS_SIGN 0x04
#define INS_GET_APP_CONFIGURATION 0x06 // Get Configuration
#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 P1_LAST 0x90
#define P1_SIGN 0x10
#define OFFSET_CLA 0
#define OFFSET_INS 1
#define OFFSET_P1 2
#define OFFSET_P2 3
#define OFFSET_LC 4
#define OFFSET_CDATA 5
union {
publicKeyContext_t publicKeyContext;
transactionContext_t transactionContext;
} tmpCtx;
txContent_t txContent;
cx_sha3_t sha3;
cx_sha256_t sha2;
volatile char fullAddress[BASE58CHECK_ADDRESS_SIZE+1];
volatile char fullAmount[50];
volatile char fullContract[50];
volatile char fullHash[HASH_SIZE*2+1];
bagl_element_t tmp_element;
unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e);
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_tx_simple_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_tx_simple_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_address_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e);
void ui_idle(void);
ux_state_t ux;
// display stepped screens
unsigned int ux_step;
unsigned int ux_step_count;
typedef struct internalStorage_t {
uint8_t fidoTransport;
uint8_t initialized;
} internalStorage_t;
WIDE internalStorage_t N_storage_real;
#define N_storage (*(WIDE internalStorage_t *)PIC(&N_storage_real))
const bagl_element_t *ui_menu_item_out_over(const bagl_element_t *e) {
// the selection rectangle is after the none|touchable
e = (const bagl_element_t *)(((unsigned int)e) + sizeof(bagl_element_t));
return e;
}
#define BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH 10
#define BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH 8
#define MAX_CHAR_PER_LINE 25
#if defined(TARGET_NANOS)
const ux_menu_entry_t menu_main[];
const ux_menu_entry_t menu_settings[];
const ux_menu_entry_t menu_settings_browser[];
// change the setting
void menu_settings_browser_change(unsigned int enabled) {
uint8_t fidoTransport = enabled;
nvm_write(&N_storage.fidoTransport, (void *)&fidoTransport,
sizeof(uint8_t));
// go back to the menu entry
UX_MENU_DISPLAY(1, menu_settings, NULL);
}
// show the currently activated entry
void menu_settings_browser_init(unsigned int ignored) {
UNUSED(ignored);
UX_MENU_DISPLAY(N_storage.fidoTransport ? 1 : 0, menu_settings_browser,
NULL);
}
// Menu Entry Yes/No
const ux_menu_entry_t menu_settings_browser[] = {
{NULL, menu_settings_browser_change, 0, NULL, "No", NULL, 0, 0},
{NULL, menu_settings_browser_change, 1, NULL, "Yes", NULL, 0, 0},
UX_MENU_END};
// Menu Settings
const ux_menu_entry_t menu_settings[] = {
{NULL, menu_settings_browser_init, 0, NULL, "Browser support", NULL, 0, 0},
{menu_main, NULL, 1, &C_icon_back, "Back", NULL, 61, 40},
UX_MENU_END};
// Menu About
const ux_menu_entry_t menu_about[] = {
{NULL, NULL, 0, NULL, "Version", APPVERSION, 0, 0},
{menu_main, NULL, 2, &C_icon_back, "Back", NULL, 61, 40},
UX_MENU_END};
// Main menu
const ux_menu_entry_t menu_main[] = {
{NULL, NULL, 0, &C_icon, "Use wallet to", "view accounts", 33, 12},
{menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0},
{menu_about, NULL, 0, NULL, "About", NULL, 0, 0},
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app", NULL, 50, 29},
UX_MENU_END};
#endif // #if TARGET_NANOS
#if defined(TARGET_NANOS)
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, 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, 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, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Confirm",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"address",
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},
"Address",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullAddress,
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:
UX_CALLBACK_SET_INTERVAL(2000);
break;
case 2:
UX_CALLBACK_SET_INTERVAL(MAX(
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
}
}
return display;
}
return 1;
}
unsigned int ui_address_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
#endif // #if defined(TARGET_NANOS)
#if defined(TARGET_NANOS)
const bagl_element_t ui_approval_simple_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, 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, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Confirm",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 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},
"Transaction Type",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullContract,
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},
"Transaction Hash",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x03, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullHash,
0,
0,
0,
NULL,
NULL,
NULL},
};
unsigned int ui_approval_simple_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:
UX_CALLBACK_SET_INTERVAL(2000);
break;
case 2:
UX_CALLBACK_SET_INTERVAL(MAX(
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
case 3:
UX_CALLBACK_SET_INTERVAL(MAX(
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
}
}
return display;
}
return 1;
}
unsigned int ui_approval_simple_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter);
#endif // #if defined(TARGET_NANOS)
#if defined(TARGET_NANOS)
// Show transactions details for approval
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, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
"Confirm",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 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},
"Token Name",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullContract,
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, 12, 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},
"Send To",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x04, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
(char *)fullAddress,
0,
0,
0,
NULL,
NULL,
NULL},
};
/*
ux_step 0: confirm
1: amount
2: address
*/
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 0x01:
UX_CALLBACK_SET_INTERVAL(2000);
break;
case 0x02:
UX_CALLBACK_SET_INTERVAL(MAX(
3000,
1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
case 0x03:
UX_CALLBACK_SET_INTERVAL(MAX(
3000,
1000 + bagl_label_roundtrip_duration_ms(element, 7)));
break;
case 0x04:
UX_CALLBACK_SET_INTERVAL(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);
#endif // #if defined(TARGET_NANOS)
void ui_idle(void) {
#if defined(TARGET_NANOS)
UX_MENU_DISPLAY(0, menu_main, NULL);
#endif // #if TARGET_ID
}
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 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;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
// 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;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
#if defined(TARGET_NANOS)
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;
}
#endif // #if defined(TARGET_NANOS)
#if defined(TARGET_NANOS)
unsigned int ui_approval_simple_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT: // CANCEL
io_seproxyhal_touch_tx_simple_cancel(NULL);
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT: { // OK
io_seproxyhal_touch_tx_simple_ok(NULL);
break;
}
}
return 0;
}
#endif // #if defined(TARGET_NANOS)
unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e) {
uint32_t tx = 0;
signTransaction(&tmpCtx.transactionContext);
// send to output buffer
os_memmove(G_io_apdu_buffer, tmpCtx.transactionContext.signature, tmpCtx.transactionContext.signatureLength);
tx=tmpCtx.transactionContext.signatureLength;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
// 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;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int io_seproxyhal_touch_tx_simple_ok(const bagl_element_t *e) {
uint32_t tx = 0;
signTransaction(&tmpCtx.transactionContext);
// send to output buffer
os_memmove(G_io_apdu_buffer, tmpCtx.transactionContext.signature, tmpCtx.transactionContext.signatureLength);
tx=tmpCtx.transactionContext.signatureLength;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
unsigned int io_seproxyhal_touch_tx_simple_cancel(const bagl_element_t *e) {
G_io_apdu_buffer[0] = 0x69;
G_io_apdu_buffer[1] = 0x85;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
#if defined(TARGET_NANOS)
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;
}
#endif // #if defined(TARGET_NANOS)
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;
uint32_t addressLength = BASE58CHECK_ADDRESS_SIZE;
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++] = addressLength;
os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.address58,
addressLength);
tx += addressLength;
return tx;
}
// APDU public key
void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer,
uint16_t dataLength, volatile unsigned int *flags,
volatile unsigned int *tx) {
//Clear Buffer
UNUSED(dataLength);
// Get private key data
uint8_t privateKeyData[33];
uint32_t bip32Path[MAX_BIP32_PATH];
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer++);
cx_ecfp_private_key_t privateKey;
uint8_t p2Chain = p2 & 0x3F;
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6A80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00);
}
if ((p2Chain != P2_CHAINCODE) && (p2Chain != P2_NO_CHAINCODE)) {
THROW(0x6B00);
}
// Add requested BIP path to tmp array
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = (dataBuffer[0] << 24) | (dataBuffer[1] << 16) |
(dataBuffer[2] << 8) | (dataBuffer[3]);
dataBuffer += 4;
}
// Get private key
os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength,
privateKeyData, NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey,
&privateKey, 1);
// Clear tmp buffer data
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
// Get address from PK
getAddressFromKey(&tmpCtx.publicKeyContext.publicKey,
tmpCtx.publicKeyContext.address,&sha3);
// Get Base58
getBase58FromAddres(tmpCtx.publicKeyContext.address,
tmpCtx.publicKeyContext.address58, &sha2);
os_memmove((void *)fullAddress,tmpCtx.publicKeyContext.address58,BASE58CHECK_ADDRESS_SIZE);
if (p1 == P1_NON_CONFIRM) {
//os_memmove(G_io_apdu_buffer, fullAddress,sizeof(fullAddress));
*tx=set_result_get_publicKey();
THROW(0x9000);
} else {
// prepare for a UI based reply
#if defined(TARGET_NANOS)
ux_step = 0;
ux_step_count = 2;
UX_DISPLAY(ui_address_nanos, ui_address_prepro);
#endif // #if TARGET
*flags |= IO_ASYNCH_REPLY;
}
}
// APDU Sign
void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer,
uint16_t dataLength, volatile unsigned int *flags,
volatile unsigned int *tx) {
UNUSED(tx);
uint32_t i;
if ((p1 == P1_FIRST) || (p1 == P1_SIGN)) {
tmpCtx.transactionContext.pathLength = workBuffer[0];
if ((tmpCtx.transactionContext.pathLength < 0x01) ||
(tmpCtx.transactionContext.pathLength > MAX_BIP32_PATH)) {
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;
}
//Parse contract type
// Load raw Data
os_memmove(tmpCtx.transactionContext.rawTx, workBuffer, dataLength);
tmpCtx.transactionContext.rawTxLength = dataLength;
//Parse Raw transaction dada
if (parseTx(workBuffer, dataLength, &txContent) != USTREAM_FINISHED) {
PRINTF("Unexpected parser status\n");
THROW(0x6A80);
}
cx_sha256_init(&sha2); //init sha
} else if ((p1 != P1_MORE) && (p1 != P1_LAST)) {
THROW(0x6B00);
}else {
}
if (p2 != 0) {
THROW(0x6B00);
}
switch (txContent.contractType){
case 1:
case 2:
// get Hash
cx_hash((cx_hash_t *)&sha2, CX_LAST, tmpCtx.transactionContext.rawTx,
tmpCtx.transactionContext.rawTxLength, tmpCtx.transactionContext.hash);
print_amount(txContent.amount,(void *)fullAmount,sizeof(fullAmount), (txContent.contractType==1)?SUN_DIG:0);
getBase58FromAddres(txContent.destination,
(void *)fullAddress, &sha2);
/*os_memmove((void *)(fullAddress + 5), "...", 3);
os_memmove((void *)(fullAddress + 8), (void *)(fullAddress + BASE58CHECK_ADDRESS_SIZE - 4), 4);*/
//fullAddress[BASE58CHECK_ADDRESS_SIZE]='\0';
// get token name
os_memmove((void *)fullContract, txContent.tokenName, txContent.tokenNameLength+1);
signTransaction(&tmpCtx.transactionContext);
// send to output buffer
os_memmove(G_io_apdu_buffer, tmpCtx.transactionContext.signature, tmpCtx.transactionContext.signatureLength);
*tx=tmpCtx.transactionContext.signatureLength+2;
THROW(0x9000);
#if defined(TARGET_BLUE)
ui_approval_transaction_blue_init();
#elif defined(TARGET_NANOS)
ux_step = 0;
ux_step_count = 4;
UX_DISPLAY(ui_approval_nanos, ui_approval_prepro);
#endif // #if TARGET_ID
break;
default:
cx_hash(&sha2, 0, workBuffer, dataLength, NULL);
if ((p1 == P1_MORE) || (p1 == P1_FIRST)) {
THROW(0x9000);
}
cx_hash(&sha2, CX_LAST, workBuffer,
0, tmpCtx.transactionContext.hash);
// Write fullHash
array_hexstr(fullHash, tmpCtx.transactionContext.hash, 32);
// write contract type
if (!setContractType(txContent.contractType, fullContract)) THROW(0x6A80);
#if defined(TARGET_NANOS)
ux_step = 0;
ux_step_count = 3;
UX_DISPLAY(ui_approval_simple_nanos, ui_approval_simple_prepro);
#endif // #if TARGET_ID
break;
}
*flags |= IO_ASYNCH_REPLY;
}
// // APDU App Config and Version
void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer,
uint16_t dataLength,
volatile unsigned int *flags,
volatile unsigned int *tx) {
//Clear buffer
UNUSED(p1);
UNUSED(p2);
UNUSED(workBuffer);
UNUSED(dataLength);
UNUSED(flags);
//Add info to buffer
G_io_apdu_buffer[0] = 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; // Set return size
THROW(0x9000); //Return OK
}
// Check ADPU and process the assigned task
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:
// Request Publick 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:
// Request Signature
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:
// Request 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(&txContent, 0, sizeof(txContent));
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;
}
// App main loop
void tron_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(&txContent, 0, sizeof(txContent));
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;
}
// override point, but nothing more to do
void io_seproxyhal_display(const bagl_element_t *element) {
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_STATUS_EVENT:
if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID &&
!(U4BE(G_io_seproxyhal_spi_buffer, 3) &
SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) {
THROW(EXCEPTION_IO_RESET);
}
// no break is intentional
default:
UX_DEFAULT_EVENT();
break;
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
UX_DISPLAYED_EVENT({});
break;
case SEPROXYHAL_TAG_TICKER_EVENT:
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {
if (UX_ALLOWED) {
#if 0
if (skipWarning && (ux_step == 0)) {
ux_step++;
}
#endif
if (ux_step_count) {
// prepare next screen
ux_step = (ux_step + 1) % ux_step_count;
// redisplay screen
UX_REDISPLAY();
}
}
});
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;
}
// Exit application
void app_exit(void) {
BEGIN_TRY_L(exit) {
TRY_L(exit) {
os_sched_exit(-1);
}
FINALLY_L(exit) {
}
}
END_TRY_L(exit);
}
// App boot loop
__attribute__((section(".boot"))) int main(void) {
// exit critical section
__asm volatile("cpsie i");
// ensure exception will work as planned
os_boot();
for (;;) {
os_memset(&txContent, 0, sizeof(txContent));
UX_INIT();
BEGIN_TRY {
TRY {
io_seproxyhal_init();
if (N_storage.initialized != 0x01) {
internalStorage_t storage;
storage.fidoTransport = 0x00;
storage.initialized = 0x01;
nvm_write(&N_storage, (void *)&storage,
sizeof(internalStorage_t));
}
USB_power(1);
ui_idle();
//Call Tron main Loop
tron_main();
}
CATCH(EXCEPTION_IO_RESET) {
// reset IO and UX
continue;
}
CATCH_ALL {
break;
}
FINALLY {
}
}
END_TRY;
}
app_exit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment