Merge Starkware branch

This commit is contained in:
BTChip github
2020-06-27 13:24:04 +02:00
parent 20e9f46c3c
commit 8d0544bf68
57 changed files with 4439 additions and 2243 deletions

View File

@@ -0,0 +1,73 @@
#include "shared_context.h"
#include "ui_callbacks.h"
UX_FLOW_DEF_NOCB(ux_approval_allowance_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_allowance_2_step,
bnnn_paging,
{
.title = "Allowance",
.text = " "
});
UX_FLOW_DEF_NOCB(
ux_approval_allowance_3_step,
bnnn_paging,
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_FLOW_DEF_NOCB(
ux_approval_allowance_4_step,
bnnn_paging,
{
.title = "Amount",
.text = strings.common.fullAmount
});
UX_FLOW_DEF_NOCB(
ux_approval_allowance_5_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_allowance_6_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_allowance_7_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_allowance_flow [] = {
&ux_approval_allowance_1_step,
&ux_approval_allowance_2_step,
&ux_approval_allowance_3_step,
&ux_approval_allowance_4_step,
&ux_approval_allowance_5_step,
&ux_approval_allowance_6_step,
&ux_approval_allowance_7_step,
FLOW_END_STEP,
};

View File

@@ -0,0 +1,29 @@
#include "shared_context.h"
#include "apdu_constants.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(workBuffer);
UNUSED(dataLength);
UNUSED(flags);
G_io_apdu_buffer[0] = (N_storage.dataAllowed ? APP_FLAG_DATA_ALLOWED : 0x00);
#ifndef HAVE_TOKENS_LIST
G_io_apdu_buffer[0] |= APP_FLAG_EXTERNAL_TOKEN_NEEDED;
#endif
#ifdef HAVE_STARKWARE
G_io_apdu_buffer[0] |= APP_FLAG_STARKWARE;
#endif
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);
}

View File

@@ -0,0 +1,76 @@
#include "shared_context.h"
#include "apdu_constants.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
#include "feature_getPublicKey.h"
void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, 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;
reset_app_context();
if ((bip32PathLength < 0x01) ||
(bip32PathLength > MAX_BIP32_PATH)) {
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] = U4BE(dataBuffer, 0);
dataBuffer += 4;
}
tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
io_seproxyhal_io_heartbeat();
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);
io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat();
getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey, tmpCtx.publicKeyContext.address, &sha3);
#ifndef NO_CONSENT
if (p1 == P1_NON_CONFIRM)
#endif // NO_CONSENT
{
*tx = set_result_get_publicKey();
THROW(0x9000);
}
#ifndef NO_CONSENT
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';
*/
// prepare for a UI based reply
#if defined(TARGET_BLUE)
snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address);
UX_DISPLAY(ui_address_blue, ui_address_blue_prepro);
#else
snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address);
ux_flow_init(0, ux_display_public_flow, NULL);
#endif // #if TARGET_ID
*flags |= IO_ASYNCH_REPLY;
}
#endif // NO_CONSENT
}

View File

@@ -0,0 +1,4 @@
#include "shared_context.h"
uint32_t set_result_get_publicKey(void);

View File

@@ -0,0 +1,17 @@
#include "shared_context.h"
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;
}

View File

@@ -0,0 +1,27 @@
#include "shared_context.h"
#include "feature_getPublicKey.h"
#include "ui_callbacks.h"
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;
reset_app_context();
// 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;
reset_app_context();
// 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
}

View File

@@ -0,0 +1,47 @@
#include "shared_context.h"
#include "ui_callbacks.h"
#ifdef HAVE_UX_FLOW
UX_FLOW_DEF_NOCB(
ux_display_public_flow_1_step,
pnn,
{
&C_icon_eye,
"Verify",
"address",
});
UX_FLOW_DEF_NOCB(
ux_display_public_flow_2_step,
bnnn_paging,
{
.title = "Address",
.text = strings.common.fullAddress,
});
UX_FLOW_DEF_VALID(
ux_display_public_flow_3_step,
pb,
io_seproxyhal_touch_address_ok(NULL),
{
&C_icon_validate_14,
"Approve",
});
UX_FLOW_DEF_VALID(
ux_display_public_flow_4_step,
pb,
io_seproxyhal_touch_address_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_display_public_flow [] = {
&ux_display_public_flow_1_step,
&ux_display_public_flow_2_step,
&ux_display_public_flow_3_step,
&ux_display_public_flow_4_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,179 @@
#include "shared_context.h"
#include "apdu_constants.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
static const uint8_t const TOKEN_SIGNATURE_PUBLIC_KEY[] = {
// production key 2019-01-11 03:07PM (erc20signer)
0x04,
0x5e,0x6c,0x10,0x20,0xc1,0x4d,0xc4,0x64,
0x42,0xfe,0x89,0xf9,0x7c,0x0b,0x68,0xcd,
0xb1,0x59,0x76,0xdc,0x24,0xf2,0x4c,0x31,
0x6e,0x7b,0x30,0xfe,0x4e,0x8c,0xc7,0x6b,
0x14,0x89,0x15,0x0c,0x21,0x51,0x4e,0xbf,
0x44,0x0f,0xf5,0xde,0xa5,0x39,0x3d,0x83,
0xde,0x53,0x58,0xcd,0x09,0x8f,0xce,0x8f,
0xd0,0xf8,0x1d,0xaa,0x94,0x97,0x91,0x83
};
#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR
void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(flags);
uint32_t offset = 0;
uint8_t tickerLength, contractNameLength;
uint32_t chainId;
uint8_t hash[32];
cx_sha256_t sha256;
cx_ecfp_public_key_t tokenKey;
cx_sha256_init(&sha256);
tmpCtx.transactionContext.currentTokenIndex = (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN;
tokenDefinition_t* token = &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex];
if (dataLength < 1) {
THROW(0x6A80);
}
tickerLength = workBuffer[offset++];
dataLength--;
if ((tickerLength + 2) >= sizeof(token->ticker)) { // +2 because ' \0' is appended to ticker
THROW(0x6A80);
}
if (dataLength < tickerLength + 1) {
THROW(0x6A80);
}
cx_hash((cx_hash_t*)&sha256, 0, workBuffer + offset, tickerLength, NULL, 0);
os_memmove(token->ticker, workBuffer + offset, tickerLength);
token->ticker[tickerLength] = ' ';
token->ticker[tickerLength + 1] = '\0';
offset += tickerLength;
dataLength -= tickerLength;
contractNameLength = workBuffer[offset++];
dataLength--;
if (dataLength < contractNameLength + 20 + 4 + 4) {
THROW(0x6A80);
}
cx_hash((cx_hash_t*)&sha256, CX_LAST, workBuffer + offset, contractNameLength + 20 + 4 + 4, hash, 32);
os_memmove(token->contractName, workBuffer + offset, MIN(contractNameLength, sizeof(token->contractName)-1));
token->contractName[MIN(contractNameLength, sizeof(token->contractName)-1)] = '\0';
offset += contractNameLength;
dataLength -= contractNameLength;
os_memmove(token->address, workBuffer + offset, 20);
offset += 20;
dataLength -= 20;
token->decimals = U4BE(workBuffer, offset);
offset += 4;
dataLength -= 4;
chainId = U4BE(workBuffer, offset);
if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) {
PRINTF("ChainId token mismatch\n");
THROW(0x6A80);
}
offset += 4;
dataLength -= 4;
cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey);
if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) {
PRINTF("Invalid token signature\n");
THROW(0x6A80);
}
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1;
THROW(0x9000);
}
#else
void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(flags);
uint32_t offset = 0;
uint8_t tickerLength;
uint32_t chainId;
uint8_t hash[32];
cx_ecfp_public_key_t tokenKey;
tmpCtx.transactionContext.currentTokenIndex = (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN;
tokenDefinition_t* token = &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex];
PRINTF("Provisioning currentTokenIndex %d\n", tmpCtx.transactionContext.currentTokenIndex);
if (dataLength < 1) {
THROW(0x6A80);
}
tickerLength = workBuffer[offset++];
dataLength--;
if ((tickerLength + 1) >= sizeof(token->ticker)) {
THROW(0x6A80);
}
if (dataLength < tickerLength + 20 + 4 + 4) {
THROW(0x6A80);
}
cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash, 32);
os_memmove(token->ticker, workBuffer + offset, tickerLength);
token->ticker[tickerLength] = ' ';
token->ticker[tickerLength + 1] = '\0';
offset += tickerLength;
dataLength -= tickerLength;
os_memmove(token->address, workBuffer + offset, 20);
offset += 20;
dataLength -= 20;
token->decimals = U4BE(workBuffer, offset);
offset += 4;
dataLength -= 4;
chainId = U4BE(workBuffer, offset);
if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) {
PRINTF("ChainId token mismatch\n");
THROW(0x6A80);
}
offset += 4;
dataLength -= 4;
#ifdef HAVE_TOKENS_EXTRA_LIST
tokenDefinition_t *currentToken = NULL;
uint32_t index;
for (index=0; index < NUM_TOKENS_EXTRA; index++) {
currentToken = (tokenDefinition_t *)PIC(&TOKENS_EXTRA[index]);
if (os_memcmp(currentToken->address, token->address, 20) == 0) {
strcpy((char*)token->ticker, (char*)currentToken->ticker);
token->decimals = currentToken->decimals;
break;
}
}
if (index < NUM_TOKENS_EXTRA) {
PRINTF("Descriptor whitelisted\n");
}
else {
cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey);
if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) {
PRINTF("Invalid token signature\n");
THROW(0x6A80);
}
}
#else
cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey);
if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) {
PRINTF("Invalid token signature\n");
THROW(0x6A80);
}
#endif
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1;
THROW(0x9000);
}
#endif

View File

@@ -0,0 +1,105 @@
#include "shared_context.h"
#include "apdu_constants.h"
#include "utils.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
static const char const SIGN_MAGIC[] = "\x19"
"Ethereum Signed Message:\n";
void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
UNUSED(tx);
uint8_t hashMessage[32];
if (p1 == P1_FIRST) {
char tmp[11];
uint32_t index;
uint32_t base = 10;
uint8_t pos = 0;
uint32_t i;
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
appState = APP_STATE_SIGNING_MESSAGE;
tmpCtx.messageSigningContext.pathLength = workBuffer[0];
if ((tmpCtx.messageSigningContext.pathLength < 0x01) ||
(tmpCtx.messageSigningContext.pathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) {
if (dataLength < 4) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += 4;
dataLength -= 4;
}
if (dataLength < 4) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0);
workBuffer += 4;
dataLength -= 4;
// Initialize message header + length
cx_keccak_init(&sha3, 256);
cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0);
for (index = 1; (((index * base) <= tmpCtx.messageSigningContext.remainingLength) &&
(((index * base) / base) == index));
index *= base);
for (; index; index /= base) {
tmp[pos++] = '0' + ((tmpCtx.messageSigningContext.remainingLength / index) % base);
}
tmp[pos] = '\0';
cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)tmp, pos, NULL, 0);
cx_sha256_init(&tmpContent.sha2);
}
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 *)&sha3, 0, workBuffer, dataLength, NULL, 0);
cx_hash((cx_hash_t *)&tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0);
tmpCtx.messageSigningContext.remainingLength -= dataLength;
if (tmpCtx.messageSigningContext.remainingLength == 0) {
cx_hash((cx_hash_t *)&sha3, CX_LAST, workBuffer, 0, tmpCtx.messageSigningContext.hash, 32);
cx_hash((cx_hash_t *)&tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32);
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%.*H", sizeof(hashMessage), hashMessage);
#ifdef NO_CONSENT
io_seproxyhal_touch_signMessage_ok(NULL);
#else //NO_CONSENT
#if defined(TARGET_BLUE)
ui_approval_message_sign_blue_init();
#else
ux_flow_init(0, ux_sign_flow, NULL);
#endif // #if TARGET_ID
#endif // NO_CONSENT
*flags |= IO_ASYNCH_REPLY;
} else {
THROW(0x9000);
}
}

View File

@@ -0,0 +1,53 @@
#include "shared_context.h"
#include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_signMessage_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;
io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(
CX_CURVE_256K1, tmpCtx.messageSigningContext.bip32Path,
tmpCtx.messageSigningContext.pathLength, privateKeyData, NULL);
io_seproxyhal_io_heartbeat();
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
os_memset(privateKeyData, 0, sizeof(privateKeyData));
unsigned int info = 0;
io_seproxyhal_io_heartbeat();
signatureLength =
cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
tmpCtx.messageSigningContext.hash,
sizeof(tmpCtx.messageSigningContext.hash), signature, sizeof(signature), &info);
os_memset(&privateKey, 0, sizeof(privateKey));
G_io_apdu_buffer[0] = 27;
if (info & CX_ECCINFO_PARITY_ODD) {
G_io_apdu_buffer[0]++;
}
if (info & CX_ECCINFO_xGTn) {
G_io_apdu_buffer[0] += 2;
}
format_signature_out(signature);
tx = 65;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
reset_app_context();
// 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_signMessage_cancel(const bagl_element_t *e) {
reset_app_context();
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
}

View File

@@ -0,0 +1,45 @@
#include "shared_context.h"
#include "ui_callbacks.h"
UX_FLOW_DEF_NOCB(
ux_sign_flow_1_step,
pnn,
{
&C_icon_certificate,
"Sign",
"message",
});
UX_FLOW_DEF_NOCB(
ux_sign_flow_2_step,
bnnn_paging,
{
.title = "Message hash",
.text = strings.tmp.tmp,
});
UX_FLOW_DEF_VALID(
ux_sign_flow_3_step,
pbb,
io_seproxyhal_touch_signMessage_ok(NULL),
{
&C_icon_validate_14,
"Sign",
"message",
});
UX_FLOW_DEF_VALID(
ux_sign_flow_4_step,
pbb,
io_seproxyhal_touch_signMessage_cancel(NULL),
{
&C_icon_crossmark,
"Cancel",
"signature",
});
const ux_flow_step_t * const ux_sign_flow [] = {
&ux_sign_flow_1_step,
&ux_sign_flow_2_step,
&ux_sign_flow_3_step,
&ux_sign_flow_4_step,
FLOW_END_STEP,
};

View File

@@ -0,0 +1,81 @@
#include "shared_context.h"
#include "apdu_constants.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
#include "feature_signTx.h"
void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
UNUSED(tx);
parserStatus_e txResult;
uint32_t i;
if (p1 == P1_FIRST) {
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
appState = APP_STATE_SIGNING_TX;
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++) {
if (dataLength < 4) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.transactionContext.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += 4;
dataLength -= 4;
}
dataPresent = false;
contractProvisioned = CONTRACT_NONE;
initTx(&txContext, &sha3, &tmpContent.txContent, customProcessor, NULL);
}
else
if (p1 != P1_MORE) {
THROW(0x6B00);
}
if (p2 != 0) {
THROW(0x6B00);
}
if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_TX)) {
PRINTF("Signature not initialized\n");
THROW(0x6985);
}
if (txContext.currentField == TX_RLP_NONE) {
PRINTF("Parser not initialized\n");
THROW(0x6985);
}
txResult = processTx(&txContext, workBuffer, dataLength, (chainConfig->kind == CHAIN_KIND_WANCHAIN ? TX_FLAG_TYPE : 0));
switch (txResult) {
case USTREAM_SUSPENDED:
break;
case USTREAM_FINISHED:
break;
case USTREAM_PROCESSING:
THROW(0x9000);
case USTREAM_FAULT:
THROW(0x6A80);
default:
PRINTF("Unexpected parser status\n");
THROW(0x6A80);
}
*flags |= IO_ASYNCH_REPLY;
if (txResult == USTREAM_FINISHED) {
finalizeParsing(false);
}
}

View File

@@ -0,0 +1,5 @@
#include "shared_context.h"
customStatus_e customProcessor(txContext_t *context);
void finalizeParsing(bool direct);

View File

@@ -0,0 +1,484 @@
#include "shared_context.h"
#include "utils.h"
#include "ui_callbacks.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
#ifdef HAVE_STARKWARE
#include "stark_utils.h"
#endif
#define TOKEN_TRANSFER_DATA_SIZE 4 + 32 + 32
static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb };
#define ALLOWANCE_DATA_SIZE 4 + 32 + 32
static const uint8_t const ALLOWANCE_ID[] = { 0x09, 0x5e, 0xa7, 0xb3 };
#ifdef HAVE_STARKWARE
#define STARKWARE_REGISTER_DATA_SIZE 4 + 32
static const uint8_t const STARKWARE_REGISTER_ID[] = { 0x76, 0x57, 0x18, 0xd7 };
#define STARKWARE_DEPOSIT_TOKEN_DATA_SIZE 4 + 32 + 32 + 32
static const uint8_t const STARKWARE_DEPOSIT_TOKEN_ID[] = { 0x00, 0xae, 0xef, 0x8a };
#define STARKWARE_DEPOSIT_ETH_DATA_SIZE 4 + 32 + 32
static const uint8_t const STARKWARE_DEPOSIT_ETH_ID[] = { 0xe2, 0xbb, 0xb1, 0x58 };
#define STARKWARE_DEPOSIT_CANCEL_DATA_SIZE 4 + 32 + 32
static const uint8_t const STARKWARE_DEPOSIT_CANCEL_ID[] = { 0xc7, 0xfb, 0x11, 0x7c };
#define STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE 4 + 32 + 32
static const uint8_t const STARKWARE_DEPOSIT_RECLAIM_ID[] = { 0x4e, 0xab, 0x38, 0xf4 };
#define STARKWARE_WITHDRAW_DATA_SIZE 4 + 32
static const uint8_t const STARKWARE_WITHDRAW_ID[] = { 0x2e, 0x1a, 0x7d, 0x4d };
#define STARKWARE_FULL_WITHDRAWAL_DATA_SIZE 4 + 32
static const uint8_t const STARKWARE_FULL_WITHDRAWAL_ID[] = { 0x27, 0x6d, 0xd1, 0xde };
#define STARKWARE_FREEZE_DATA_SIZE 4 + 32
static const uint8_t const STARKWARE_FREEZE_ID[] = { 0xb9, 0x10, 0x72, 0x09 };
#define STARKWARE_ESCAPE_DATA_SIZE 4 + 32 + 32 + 32 + 32
static const uint8_t const STARKWARE_ESCAPE_ID[] = { 0x9e, 0x3a, 0xda, 0xc4 };
static const uint8_t const STARKWARE_VERIFY_ESCAPE_ID[] = { 0x2d, 0xd5, 0x30, 0x06 };
#endif
uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) {
uint32_t i;
for (i=0; i<8; i++) {
if (parameter[i] != 0x00) {
break;
}
}
if (i == 8) {
result[0] = '0';
result[1] = '0';
result[2] = '\0';
return 2;
}
else {
array_hexstr(result, parameter + i, 8 - i);
return ((8 - i) * 2);
}
}
customStatus_e customProcessor(txContext_t *context) {
if ((context->currentField == TX_RLP_DATA) &&
(context->currentFieldLength != 0)) {
dataPresent = true;
// If handling a new contract rather than a function call, abort immediately
if (tmpContent.txContent.destinationLength == 0) {
return CUSTOM_NOT_HANDLED;
}
if (context->currentFieldPos == 0) {
// If handling the beginning of the data field, assume that the function selector is present
if (context->commandLength < 4) {
PRINTF("Missing function selector\n");
return CUSTOM_FAULT;
}
// Initial check to see if the call can be processed
if ((context->currentFieldLength == TOKEN_TRANSFER_DATA_SIZE) &&
(os_memcmp(context->workBuffer, TOKEN_TRANSFER_ID, 4) == 0) &&
(getKnownToken(tmpContent.txContent.destination) != NULL)) {
contractProvisioned = CONTRACT_ERC20;
}
else
if ((context->currentFieldLength == ALLOWANCE_DATA_SIZE) &&
(os_memcmp(context->workBuffer, ALLOWANCE_ID, 4) == 0)) {
contractProvisioned = CONTRACT_ALLOWANCE;
}
#ifdef HAVE_STARKWARE
else
if ((context->currentFieldLength >= STARKWARE_REGISTER_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_REGISTER_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_REGISTER;
}
else
if ((context->currentFieldLength == STARKWARE_DEPOSIT_ETH_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_ETH_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_ETH;
}
else
if ((context->currentFieldLength == STARKWARE_DEPOSIT_TOKEN_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_TOKEN_ID, 4) == 0) &&
quantumSet) {
contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_TOKEN;
}
else
if ((context->currentFieldLength == STARKWARE_WITHDRAW_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_WITHDRAW_ID, 4) == 0) &&
quantumSet) {
contractProvisioned = CONTRACT_STARKWARE_WITHDRAW;
}
else
if ((context->currentFieldLength == STARKWARE_DEPOSIT_CANCEL_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_CANCEL_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_CANCEL;
}
else
if ((context->currentFieldLength == STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_RECLAIM_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_RECLAIM;
}
else
if ((context->currentFieldLength == STARKWARE_FULL_WITHDRAWAL_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_FULL_WITHDRAWAL_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_FULL_WITHDRAWAL;
}
else
if ((context->currentFieldLength == STARKWARE_FREEZE_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_FREEZE_ID, 4) == 0)) {
contractProvisioned = CONTRACT_STARKWARE_FREEZE;
}
else
if ((context->currentFieldLength == STARKWARE_ESCAPE_DATA_SIZE) &&
(os_memcmp(context->workBuffer, STARKWARE_ESCAPE_ID, 4) == 0) &&
quantumSet) {
contractProvisioned = CONTRACT_STARKWARE_ESCAPE;
}
else
if (os_memcmp(context->workBuffer, STARKWARE_VERIFY_ESCAPE_ID, 4) == 0) {
contractProvisioned = CONTRACT_STARKWARE_VERIFY_ESCAPE;
}
#endif
}
// Sanity check
// Also handle exception that only need to process the beginning of the data
if ((contractProvisioned != CONTRACT_NONE) &&
#ifdef HAVE_STARKWARE
(contractProvisioned != CONTRACT_STARKWARE_VERIFY_ESCAPE) &&
(contractProvisioned != CONTRACT_STARKWARE_REGISTER) &&
#endif
(context->currentFieldLength > sizeof(dataContext.tokenContext.data))) {
PRINTF("Data field overflow - dropping customization\n");
contractProvisioned = CONTRACT_NONE;
}
PRINTF("contractProvisioned %d\n", contractProvisioned);
if (contractProvisioned != CONTRACT_NONE) {
if (context->currentFieldPos < context->currentFieldLength) {
uint32_t copySize = MIN(context->commandLength,
context->currentFieldLength - context->currentFieldPos);
// Handle the case where we only need to handle the beginning of the data parameter
if ((context->currentFieldPos + copySize) < sizeof(dataContext.tokenContext.data)) {
copyTxData(context,
dataContext.tokenContext.data + context->currentFieldPos,
copySize);
}
else {
if (context->currentFieldPos < sizeof(dataContext.tokenContext.data)) {
uint32_t copySize2 = sizeof(dataContext.tokenContext.data) - context->currentFieldPos;
copyTxData(context,
dataContext.tokenContext.data + context->currentFieldPos,
copySize2);
copySize -= copySize2;
}
copyTxData(context, NULL, copySize);
}
}
if (context->currentFieldPos == context->currentFieldLength) {
context->currentField++;
context->processingField = false;
}
return CUSTOM_HANDLED;
}
else {
uint32_t blockSize;
uint32_t copySize;
uint32_t fieldPos = context->currentFieldPos;
if (fieldPos == 0) {
if (!N_storage.dataAllowed) {
PRINTF("Data field forbidden\n");
return CUSTOM_FAULT;
}
if (!N_storage.contractDetails) {
return CUSTOM_NOT_HANDLED;
}
dataContext.rawDataContext.fieldIndex = 0;
dataContext.rawDataContext.fieldOffset = 0;
blockSize = 4;
}
else {
if (!N_storage.contractDetails) {
return CUSTOM_NOT_HANDLED;
}
blockSize = 32 - (dataContext.rawDataContext.fieldOffset % 32);
}
// Sanity check
if ((context->currentFieldLength - fieldPos) < blockSize) {
PRINTF("Unconsistent data\n");
return CUSTOM_FAULT;
}
copySize = (context->commandLength < blockSize ? context->commandLength : blockSize);
copyTxData(context,
dataContext.rawDataContext.data + dataContext.rawDataContext.fieldOffset,
copySize);
if (context->currentFieldPos == context->currentFieldLength) {
context->currentField++;
context->processingField = false;
}
dataContext.rawDataContext.fieldOffset += copySize;
if (copySize == blockSize) {
// Can display
if (fieldPos != 0) {
dataContext.rawDataContext.fieldIndex++;
}
dataContext.rawDataContext.fieldOffset = 0;
if (fieldPos == 0) {
array_hexstr(strings.tmp.tmp, dataContext.rawDataContext.data, 4);
#if defined(TARGET_BLUE)
UX_DISPLAY(ui_data_selector_blue, ui_data_selector_blue_prepro);
#else
ux_flow_init(0, ux_confirm_selector_flow, NULL);
#endif // #if TARGET_ID
}
else {
uint32_t offset = 0;
uint32_t i;
snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Field %d", dataContext.rawDataContext.fieldIndex);
for (i=0; i<4; i++) {
offset += splitBinaryParameterPart(strings.tmp.tmp + offset, dataContext.rawDataContext.data + 8 * i);
if (i != 3) {
strings.tmp.tmp[offset++] = ':';
}
}
#if defined(TARGET_BLUE)
UX_DISPLAY(ui_data_parameter_blue, ui_data_parameter_blue_prepro);
#else
ux_flow_init(0, ux_confirm_parameter_flow, NULL);
#endif // #if TARGET_ID
}
}
else {
return CUSTOM_HANDLED;
}
return CUSTOM_SUSPENDED;
}
}
return CUSTOM_NOT_HANDLED;
}
void finalizeParsing(bool direct) {
uint256_t gasPrice, startGas, uint256;
uint32_t i;
uint8_t address[41];
uint8_t decimals = WEI_TO_ETHER;
uint8_t *ticker = (uint8_t *)PIC(chainConfig->coinName);
uint8_t *feeTicker = (uint8_t *)PIC(chainConfig->coinName);
uint8_t tickerOffset = 0;
// Verify the chain
if (chainConfig->chainId != 0) {
uint32_t v = getV(&tmpContent.txContent);
if (chainConfig->chainId != v) {
reset_app_context();
PRINTF("Invalid chainId %d expected %d\n", v, chainConfig->chainId);
if (direct) {
THROW(0x6A80);
}
else {
io_seproxyhal_send_status(0x6A80);
ui_idle();
return;
}
}
}
// Store the hash
cx_hash((cx_hash_t *)&sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32);
#ifdef HAVE_STARKWARE
if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) ||
(contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) ||
(contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) ||
(contractProvisioned == CONTRACT_STARKWARE_ESCAPE)) {
// For a deposit / withdrawal / escape, check if the token ID is known or can't parse
uint8_t tokenIdOffset = (4 + ((contractProvisioned == CONTRACT_STARKWARE_ESCAPE) ? 32 + 32 : 0));
if (quantumSet) {
tokenDefinition_t *currentToken = NULL;
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
}
compute_token_id(&sha3,
(currentToken != NULL ? currentToken->address : NULL),
dataContext.tokenContext.quantum, G_io_apdu_buffer + 100);
if (os_memcmp(dataContext.tokenContext.data + tokenIdOffset, G_io_apdu_buffer + 100, 32) != 0) {
PRINTF("Token ID not matching - computed %.*H\n", 32, G_io_apdu_buffer + 100);
PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum);
PRINTF("Requested %.*H\n", 32, dataContext.tokenContext.data + tokenIdOffset);
contractProvisioned = CONTRACT_NONE;
}
}
else {
PRINTF("Quantum not set\n");
contractProvisioned = CONTRACT_NONE;
}
}
#endif
// If there is a token to process, check if it is well known
if ((contractProvisioned == CONTRACT_ERC20) || (contractProvisioned == CONTRACT_ALLOWANCE)) {
tokenDefinition_t *currentToken = getKnownToken(tmpContent.txContent.destination);
if (currentToken != NULL) {
dataPresent = false;
decimals = currentToken->decimals;
ticker = currentToken->ticker;
tmpContent.txContent.destinationLength = 20;
os_memmove(tmpContent.txContent.destination, dataContext.tokenContext.data + 4 + 12, 20);
os_memmove(tmpContent.txContent.value.value, dataContext.tokenContext.data + 4 + 32, 32);
tmpContent.txContent.value.length = 32;
}
}
else {
if (dataPresent && contractProvisioned == CONTRACT_NONE && !N_storage.dataAllowed) {
reset_app_context();
PRINTF("Data field forbidden\n");
if (direct) {
THROW(0x6A80);
}
else {
io_seproxyhal_send_status(0x6A80);
ui_idle();
return;
}
}
}
// Add address
if (tmpContent.txContent.destinationLength != 0) {
getEthAddressStringFromBinary(tmpContent.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';
*/
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
else
{
#ifdef TARGET_BLUE
os_memmove((void*)addressSummary, CONTRACT_ADDRESS, sizeof(CONTRACT_ADDRESS));
#endif
strcpy(strings.common.fullAddress, "Contract");
}
if ((contractProvisioned == CONTRACT_NONE) || (contractProvisioned == CONTRACT_ERC20) ||
(contractProvisioned == CONTRACT_ALLOWANCE)) {
// Add amount in ethers or tokens
if ((contractProvisioned == CONTRACT_ALLOWANCE) && ismaxint(tmpContent.txContent.value.value, 32)) {
strcpy((char*)G_io_apdu_buffer, "Unlimited");
}
else {
convertUint256BE(tmpContent.txContent.value.value, tmpContent.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, decimals);
}
i = 0;
tickerOffset = 0;
while (ticker[tickerOffset]) {
strings.common.fullAmount[tickerOffset] = ticker[tickerOffset];
tickerOffset++;
}
while (G_io_apdu_buffer[i]) {
strings.common.fullAmount[tickerOffset + i] = G_io_apdu_buffer[i];
i++;
}
strings.common.fullAmount[tickerOffset + i] = '\0';
}
// Compute maximum fee
PRINTF("Max fee\n");
PRINTF("Gasprice %.*H\n", tmpContent.txContent.gasprice.length, tmpContent.txContent.gasprice.value);
PRINTF("Startgas %.*H\n", tmpContent.txContent.startgas.length, tmpContent.txContent.startgas.value);
convertUint256BE(tmpContent.txContent.gasprice.value, tmpContent.txContent.gasprice.length, &gasPrice);
convertUint256BE(tmpContent.txContent.startgas.value, tmpContent.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;
tickerOffset=0;
while (feeTicker[tickerOffset]) {
strings.common.maxFee[tickerOffset] = feeTicker[tickerOffset];
tickerOffset++;
}
tickerOffset++;
while (G_io_apdu_buffer[i]) {
strings.common.maxFee[tickerOffset + i] = G_io_apdu_buffer[i];
i++;
}
strings.common.maxFee[tickerOffset + i] = '\0';
#ifdef NO_CONSENT
io_seproxyhal_touch_tx_ok(NULL);
#else // NO_CONSENT
#if defined(TARGET_BLUE)
ui_approval_transaction_blue_init();
#else
#ifdef HAVE_STARKWARE
if (contractProvisioned == CONTRACT_STARKWARE_REGISTER) {
ux_flow_init(0, ux_approval_starkware_register_flow, NULL);
return;
}
else
if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) {
ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL);
return;
}
else
if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) {
ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL);
return;
}
else
if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) ||
(contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) ||
(contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) ||
(contractProvisioned == CONTRACT_STARKWARE_FREEZE)) {
ux_flow_init(0, ux_approval_starkware_verify_vault_id_flow, NULL);
return;
}
else
if (contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) {
ux_flow_init(0, ux_approval_starkware_withdraw_flow, NULL);
return;
}
else
if (contractProvisioned == CONTRACT_STARKWARE_ESCAPE) {
ux_flow_init(0, ux_approval_starkware_escape_flow, NULL);
return;
}
else
if (contractProvisioned == CONTRACT_STARKWARE_VERIFY_ESCAPE) {
ux_flow_init(0, ux_approval_starkware_verify_escape_flow, NULL);
return;
}
#endif
if (contractProvisioned == CONTRACT_ALLOWANCE) {
ux_flow_init(0, ux_approval_allowance_flow, NULL);
return;
}
ux_flow_init(0,
((dataPresent && !N_storage.contractDetails) ? ux_approval_tx_data_warning_flow : ux_approval_tx_flow),
NULL);
#endif // #if TARGET_ID
#endif // NO_CONSENT
}

View File

@@ -0,0 +1,103 @@
#include "shared_context.h"
#include "utils.h"
#include "ui_callbacks.h"
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;
uint32_t v = getV(&tmpContent.txContent);
io_seproxyhal_io_heartbeat();
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));
unsigned int info = 0;
io_seproxyhal_io_heartbeat();
signatureLength =
cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
tmpCtx.transactionContext.hash,
sizeof(tmpCtx.transactionContext.hash), signature, sizeof(signature), &info);
os_memset(&privateKey, 0, sizeof(privateKey));
// Parity is present in the sequence tag in the legacy API
if (tmpContent.txContent.vLength == 0) {
// Legacy API
G_io_apdu_buffer[0] = 27;
}
else {
// New API
// Note that this is wrong for a large v, but the client can always recover
G_io_apdu_buffer[0] = (v * 2) + 35;
}
if (info & CX_ECCINFO_PARITY_ODD) {
G_io_apdu_buffer[0]++;
}
if (info & CX_ECCINFO_xGTn) {
G_io_apdu_buffer[0] += 2;
}
format_signature_out(signature);
tx = 65;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
reset_app_context();
// 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) {
reset_app_context();
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_data_ok(const bagl_element_t *e) {
parserStatus_e txResult = USTREAM_FINISHED;
txResult = continueTx(&txContext);
switch (txResult) {
case USTREAM_SUSPENDED:
break;
case USTREAM_FINISHED:
break;
case USTREAM_PROCESSING:
io_seproxyhal_send_status(0x9000);
ui_idle();
break;
case USTREAM_FAULT:
reset_app_context();
io_seproxyhal_send_status(0x6A80);
ui_idle();
break;
default:
PRINTF("Unexpected parser status\n");
reset_app_context();
io_seproxyhal_send_status(0x6A80);
ui_idle();
}
if (txResult == USTREAM_FINISHED) {
finalizeParsing(false);
}
return 0;
}
unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e) {
reset_app_context();
io_seproxyhal_send_status(0x6985);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}

View File

@@ -0,0 +1,165 @@
#include "shared_context.h"
#include "ui_callbacks.h"
#ifdef HAVE_UX_FLOW
UX_FLOW_DEF_NOCB(
ux_confirm_selector_flow_1_step,
pnn,
{
&C_icon_eye,
"Verify",
"selector",
});
UX_FLOW_DEF_NOCB(
ux_confirm_selector_flow_2_step,
bn,
{
"Selector",
strings.tmp.tmp
});
UX_FLOW_DEF_VALID(
ux_confirm_selector_flow_3_step,
pb,
io_seproxyhal_touch_data_ok(NULL),
{
&C_icon_validate_14,
"Approve",
});
UX_FLOW_DEF_VALID(
ux_confirm_selector_flow_4_step,
pb,
io_seproxyhal_touch_data_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_confirm_selector_flow [] = {
&ux_confirm_selector_flow_1_step,
&ux_confirm_selector_flow_2_step,
&ux_confirm_selector_flow_3_step,
&ux_confirm_selector_flow_4_step,
FLOW_END_STEP,
};
//////////////////////////////////////////////////////////////////////
UX_FLOW_DEF_NOCB(
ux_confirm_parameter_flow_1_step,
pnn,
{
&C_icon_eye,
"Verify",
strings.tmp.tmp2
});
UX_FLOW_DEF_NOCB(
ux_confirm_parameter_flow_2_step,
bnnn_paging,
{
.title = "Parameter",
.text = strings.tmp.tmp,
});
UX_FLOW_DEF_VALID(
ux_confirm_parameter_flow_3_step,
pb,
io_seproxyhal_touch_data_ok(NULL),
{
&C_icon_validate_14,
"Approve",
});
UX_FLOW_DEF_VALID(
ux_confirm_parameter_flow_4_step,
pb,
io_seproxyhal_touch_data_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_confirm_parameter_flow [] = {
&ux_confirm_parameter_flow_1_step,
&ux_confirm_parameter_flow_2_step,
&ux_confirm_parameter_flow_3_step,
&ux_confirm_parameter_flow_4_step,
FLOW_END_STEP,
};
//////////////////////////////////////////////////////////////////////
UX_FLOW_DEF_NOCB(ux_approval_tx_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_tx_2_step,
bnnn_paging,
{
.title = "Amount",
.text = strings.common.fullAmount
});
UX_FLOW_DEF_NOCB(
ux_approval_tx_3_step,
bnnn_paging,
{
.title = "Address",
.text = strings.common.fullAddress,
});
UX_FLOW_DEF_NOCB(
ux_approval_tx_4_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_tx_5_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_tx_6_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
UX_FLOW_DEF_NOCB(ux_approval_tx_data_warning_step,
pbb,
{
&C_icon_warning,
"Data",
"Present",
});
const ux_flow_step_t * const ux_approval_tx_flow [] = {
&ux_approval_tx_1_step,
&ux_approval_tx_2_step,
&ux_approval_tx_3_step,
&ux_approval_tx_4_step,
&ux_approval_tx_5_step,
&ux_approval_tx_6_step,
FLOW_END_STEP,
};
const ux_flow_step_t * const ux_approval_tx_data_warning_flow [] = {
&ux_approval_tx_1_step,
&ux_approval_tx_data_warning_step,
&ux_approval_tx_2_step,
&ux_approval_tx_3_step,
&ux_approval_tx_4_step,
&ux_approval_tx_5_step,
&ux_approval_tx_6_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,126 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
#include "utils.h"
void prepare_deposit_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_deposit_4() {
snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 + 32 - 4));
}
void prepare_deposit_5() {
uint256_t amount, amountPre, quantum;
uint8_t decimals;
char *ticker = (char*)PIC(chainConfig->coinName);
if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) {
decimals = WEI_TO_ETHER;
convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &amountPre);
}
else {
tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
decimals = token->decimals;
ticker = (char*)token->ticker;
readu256BE(dataContext.tokenContext.data + 4 + 32 + 32, &amountPre);
}
readu256BE(dataContext.tokenContext.quantum, &quantum);
mul256(&amountPre, &quantum, &amount);
tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100);
strcpy(strings.common.fullAmount, ticker);
adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals);
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_deposit_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_deposit_2_step,
bnnn_paging,
{
.title = "Deposit",
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_deposit_3_step,
bnnn_paging,
prepare_deposit_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_deposit_4_step,
bnnn_paging,
prepare_deposit_4(),
{
.title = "Token Account",
.text = strings.common.fullAddress
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_deposit_5_step,
bnnn_paging,
prepare_deposit_5(),
{
.title = "Amount",
.text = strings.common.fullAmount
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_deposit_6_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_deposit_7_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_deposit_8_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_deposit_flow [] = {
&ux_approval_starkware_deposit_1_step,
&ux_approval_starkware_deposit_2_step,
&ux_approval_starkware_deposit_3_step,
&ux_approval_starkware_deposit_4_step,
&ux_approval_starkware_deposit_5_step,
&ux_approval_starkware_deposit_6_step,
&ux_approval_starkware_deposit_7_step,
&ux_approval_starkware_deposit_8_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,136 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
void prepare_escape_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_escape_4() {
uint256_t amount, amountPre, quantum;
uint8_t decimals;
char *ticker = (char*)PIC(chainConfig->coinName);
if (dataContext.tokenContext.quantumIndex == MAX_TOKEN) {
decimals = WEI_TO_ETHER;
}
else {
tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
decimals = token->decimals;
ticker = (char*)token->ticker;
}
readu256BE(dataContext.tokenContext.data + 4 + 32 + 32 + 32, &amountPre);
readu256BE(dataContext.tokenContext.quantum, &quantum);
mul256(&amountPre, &quantum, &amount);
tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100);
strcpy(strings.common.fullAmount, ticker);
adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals);
}
void prepare_escape_5() {
snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4 + 32);
}
void prepare_escape_6() {
snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 - 4));
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_escape_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_escape_2_step,
bnnn_paging,
{
.title = "Escape",
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_escape_3_step,
bnnn_paging,
prepare_escape_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_escape_4_step,
bnnn_paging,
prepare_escape_4(),
{
.title = "Amount",
.text = strings.common.fullAmount
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_escape_5_step,
bnnn_paging,
prepare_escape_5(),
{
.title = "Master Account",
.text = strings.tmp.tmp
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_escape_6_step,
bnnn_paging,
prepare_escape_6(),
{
.title = "Token Account",
.text = strings.common.fullAddress
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_escape_7_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_escape_8_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_escape_9_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_escape_flow [] = {
&ux_approval_starkware_escape_1_step,
&ux_approval_starkware_escape_2_step,
&ux_approval_starkware_escape_3_step,
&ux_approval_starkware_escape_4_step,
&ux_approval_starkware_escape_5_step,
&ux_approval_starkware_escape_6_step,
&ux_approval_starkware_escape_7_step,
&ux_approval_starkware_escape_8_step,
&ux_approval_starkware_escape_9_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,124 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
void prepare_register_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_register_4() {
uint8_t privateKeyData[32];
uint8_t address[41];
cx_ecfp_private_key_t privateKey;
cx_ecfp_public_key_t publicKey;
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);
io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1);
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat();
getEthAddressStringFromKey(&publicKey, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_register_5() {
snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4);
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_register_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_register_2_step,
bnnn_paging,
{
.title = "Registration",
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_register_3_step,
bnnn_paging,
prepare_register_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_register_4_step,
bnnn_paging,
prepare_register_4(),
{
.title = "From ETH address",
.text = strings.common.fullAddress
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_register_5_step,
bnnn_paging,
prepare_register_5(),
{
.title = "Master account",
.text = strings.tmp.tmp
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_register_6_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_register_7_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_register_8_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_register_flow [] = {
&ux_approval_starkware_register_1_step,
&ux_approval_starkware_register_2_step,
&ux_approval_starkware_register_3_step,
&ux_approval_starkware_register_4_step,
&ux_approval_starkware_register_5_step,
&ux_approval_starkware_register_6_step,
&ux_approval_starkware_register_7_step,
&ux_approval_starkware_register_8_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,77 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
void prepare_verify_escape_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_verify_escape_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_verify_escape_2_step,
bnnn_paging,
{
.title = "Verify Escape",
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_verify_escape_3_step,
bnnn_paging,
prepare_verify_escape_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_verify_escape_4_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_verify_escape_5_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_verify_escape_6_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_verify_escape_flow [] = {
&ux_approval_starkware_verify_escape_1_step,
&ux_approval_starkware_verify_escape_2_step,
&ux_approval_starkware_verify_escape_3_step,
&ux_approval_starkware_verify_escape_4_step,
&ux_approval_starkware_verify_escape_5_step,
&ux_approval_starkware_verify_escape_6_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,116 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
void prepare_verify_vault_id_2() {
if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) {
strcpy(strings.common.fullAddress, "Cancel Deposit");
}
else
if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) {
strcpy(strings.common.fullAddress, "Reclaim Deposit");
}
else
if (contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) {
strcpy(strings.common.fullAddress, "Full Withdrawal");
}
else
if (contractProvisioned == CONTRACT_STARKWARE_FREEZE) {
strcpy(strings.common.fullAddress, "Freeze");
}
}
void prepare_verify_vault_id_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_verify_vault_id_4() {
uint8_t offset = 0;
if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) || (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM)) {
offset = 32;
}
snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + offset + 32 - 4));
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_verify_vault_id_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_verify_vault_id_2_step,
bnnn_paging,
prepare_verify_vault_id_2(),
{
.title = strings.common.fullAddress,
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_verify_vault_id_3_step,
bnnn_paging,
prepare_verify_vault_id_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_verify_vault_id_4_step,
bnnn_paging,
prepare_verify_vault_id_4(),
{
.title = "Token Account",
.text = strings.common.fullAddress
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_verify_vault_id_5_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_verify_vault_id_6_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_verify_vault_id_7_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_verify_vault_id_flow [] = {
&ux_approval_starkware_verify_vault_id_1_step,
&ux_approval_starkware_verify_vault_id_2_step,
&ux_approval_starkware_verify_vault_id_3_step,
&ux_approval_starkware_verify_vault_id_4_step,
&ux_approval_starkware_verify_vault_id_5_step,
&ux_approval_starkware_verify_vault_id_6_step,
&ux_approval_starkware_verify_vault_id_7_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,110 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
void prepare_register_4();
void prepare_withdraw_3() {
uint8_t address[41];
getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3);
strings.common.fullAddress[0] = '0';
strings.common.fullAddress[1] = 'x';
os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40);
strings.common.fullAddress[42] = '\0';
}
void prepare_withdraw_5() {
char *ticker = (char*)PIC(chainConfig->coinName);
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
ticker = (char*)token->ticker;
}
strcpy(strings.common.fullAmount, ticker);
}
UX_FLOW_DEF_NOCB(ux_approval_starkware_withdraw_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_withdraw_2_step,
bnnn_paging,
{
.title = "Withdrawal",
.text = " "
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_withdraw_3_step,
bnnn_paging,
prepare_withdraw_3(),
{
.title = "Contract Name",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_withdraw_4_step,
bnnn_paging,
prepare_register_4(),
{
.title = "To Eth Address",
.text = strings.common.fullAddress
});
UX_STEP_NOCB_INIT(
ux_approval_starkware_withdraw_5_step,
bnnn_paging,
prepare_withdraw_5(),
{
.title = "Token Symbol",
.text = strings.common.fullAmount
});
UX_FLOW_DEF_NOCB(
ux_approval_starkware_withdraw_6_step,
bnnn_paging,
{
.title = "Max Fees",
.text = strings.common.maxFee,
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_withdraw_7_step,
pbb,
io_seproxyhal_touch_tx_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_approval_starkware_withdraw_8_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_approval_starkware_withdraw_flow [] = {
&ux_approval_starkware_withdraw_1_step,
&ux_approval_starkware_withdraw_2_step,
&ux_approval_starkware_withdraw_3_step,
&ux_approval_starkware_withdraw_4_step,
&ux_approval_starkware_withdraw_5_step,
&ux_approval_starkware_withdraw_6_step,
&ux_approval_starkware_withdraw_7_step,
&ux_approval_starkware_withdraw_8_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,65 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "apdu_constants.h"
#include "stark_utils.h"
#include "feature_stark_getPublicKey.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
void handleStarkwareGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, 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;
reset_app_context();
if ((bip32PathLength < 0x01) ||
(bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00);
}
if (p2 != 0) {
THROW(0x6B00);
}
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0);
dataBuffer += 4;
}
io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(bip32Path, bip32PathLength, privateKeyData);
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);
io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_Stark256, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat();
#ifndef NO_CONSENT
if (p1 == P1_NON_CONFIRM)
#endif // NO_CONSENT
{
*tx = set_result_get_stark_publicKey();
THROW(0x9000);
}
#ifndef NO_CONSENT
else
{
// prepare for a UI based reply
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, tmpCtx.publicKeyContext.publicKey.W + 1);
ux_flow_init(0, ux_display_stark_public_flow, NULL);
*flags |= IO_ASYNCH_REPLY;
}
#endif // NO_CONSENT
}
#endif

View File

@@ -0,0 +1,4 @@
#include "shared_context.h"
uint32_t set_result_get_stark_publicKey(void);

View File

@@ -0,0 +1,15 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "feature_stark_getPublicKey.h"
uint32_t set_result_get_stark_publicKey() {
uint32_t tx = 0;
os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.publicKey.W, 65);
tx += 65;
return tx;
}
#endif

View File

@@ -0,0 +1,20 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
#include "feature_stark_getPublicKey.h"
unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e) {
uint32_t tx = set_result_get_stark_publicKey();
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
reset_app_context();
// 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
}
#endif

View File

@@ -0,0 +1,49 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e);
UX_FLOW_DEF_NOCB(
ux_display_stark_public_flow_1_step,
pnn,
{
&C_icon_eye,
"Verify",
"Stark key",
});
UX_FLOW_DEF_NOCB(
ux_display_stark_public_flow_2_step,
bnnn_paging,
{
.title = "Stark Key",
.text = strings.tmp.tmp,
});
UX_FLOW_DEF_VALID(
ux_display_stark_public_flow_3_step,
pb,
io_seproxyhal_touch_stark_pubkey_ok(NULL),
{
&C_icon_validate_14,
"Approve",
});
UX_FLOW_DEF_VALID(
ux_display_stark_public_flow_4_step,
pb,
io_seproxyhal_touch_address_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_display_stark_public_flow [] = {
&ux_display_stark_public_flow_1_step,
&ux_display_stark_public_flow_2_step,
&ux_display_stark_public_flow_3_step,
&ux_display_stark_public_flow_4_step,
FLOW_END_STEP,
};
#endif

View File

@@ -0,0 +1,42 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "apdu_constants.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
void handleStarkwareProvideQuantum(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
size_t i = 0;
tokenDefinition_t *currentToken = NULL;
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
if (dataLength != 20 + 32) {
THROW(0x6700);
}
if (!allzeroes(dataBuffer, 20)) {
for(i=0; i<MAX_TOKEN; i++){
currentToken = &tmpCtx.transactionContext.tokens[i];
if (tmpCtx.transactionContext.tokenSet[i] && (os_memcmp(currentToken->address, dataBuffer, 20) == 0)) {
break;
}
}
if (i == MAX_TOKEN) {
PRINTF("Associated token not found\n");
THROW(0x6A80);
}
}
else {
i = MAX_TOKEN;
}
os_memmove(dataContext.tokenContext.quantum, dataBuffer + 20, 32);
dataContext.tokenContext.quantumIndex = i;
quantumSet = true;
THROW(0x9000);
}
#endif

View File

@@ -0,0 +1,140 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "apdu_constants.h"
#include "stark_utils.h"
#ifdef TARGET_BLUE
#include "ui_blue.h"
#endif
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
#include "poorstream.h"
#include "ui_callbacks.h"
#define U8BE(buf, off) (uint64_t)((((uint64_t)U4BE(buf, off)) << 32) | (((uint64_t)U4BE(buf, off + 4)) & 0xFFFFFFFF))
#define TMP_OFFSET 140
void handleStarkwareSignMessage(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) {
uint8_t privateKeyData[32];
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer);
uint8_t offset = 1;
cx_ecfp_private_key_t privateKey;
poorstream_t bitstream;
bool selfTransfer = false;
// Initial checks
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
if ((bip32PathLength < 0x01) ||
(bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
switch(p1) {
case P1_STARK_ORDER:
if (dataLength != (20 + 32 + 20 + 32 + 4 + 4 + 8 + 8 + 4 + 4 + 1 + 4 * bip32PathLength)) {
THROW(0x6700);
}
break;
case P1_STARK_TRANSFER:
if (dataLength != (20 + 32 + 32 + 4 + 4 + 8 + 4 + 4 + 1 + 4 * bip32PathLength)) {
THROW(0x6700);
}
break;
default:
THROW(0x6B00);
}
if (p2 != 0) {
THROW(0x6B00);
}
tmpCtx.transactionContext.pathLength = bip32PathLength;
for (i = 0; i < bip32PathLength; i++) {
tmpCtx.transactionContext.bip32Path[i] = U4BE(dataBuffer, offset);
PRINTF("Storing path %d %d\n", i, tmpCtx.transactionContext.bip32Path[i]);
offset += 4;
}
// Discard the path to use part of dataBuffer as a temporary buffer
os_memmove(dataBuffer, dataBuffer + offset, dataLength - offset);
// Fail immediately if the contract is unknown
if (!allzeroes(dataBuffer, 20) && getKnownToken(dataBuffer) == NULL) {
PRINTF("stark - cannot process unknown token %.*H", 20, dataBuffer);
THROW(0x6A80);
}
if ((p1 == P1_STARK_ORDER) && (!allzeroes(dataBuffer + 20 + 32, 20) && getKnownToken(dataBuffer + 20 + 32) == NULL)) {
PRINTF("stark - cannot process unknown token %.*H", 20, dataBuffer + 20 + 32);
THROW(0x6A80);
}
// Prepare the Stark parameters
io_seproxyhal_io_heartbeat();
compute_token_id(&sha3, dataBuffer, dataBuffer + 20, dataContext.starkContext.w1);
if (p1 == P1_STARK_ORDER) {
io_seproxyhal_io_heartbeat();
compute_token_id(&sha3, dataBuffer + 20 + 32, dataBuffer + 20 + 32 + 20, dataContext.starkContext.w2);
offset = 20 + 32 + 20 + 32;
}
else {
os_memmove(dataContext.starkContext.w2, dataBuffer + 20 + 32, 32);
offset = 20 + 32 + 32;
}
poorstream_init(&bitstream, dataContext.starkContext.w3);
poorstream_write_bits(&bitstream, 0, 11); // padding
poorstream_write_bits(&bitstream, (p1 == P1_STARK_ORDER ? STARK_ORDER_TYPE : STARK_TRANSFER_TYPE), 4);
poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset), 31);
poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset + 4), 31);
poorstream_write_bits(&bitstream, U8BE(dataBuffer, offset + 4 + 4), 63);
if (p1 == P1_STARK_ORDER) {
poorstream_write_bits(&bitstream, U8BE(dataBuffer, offset + 4 + 4 + 8), 63);
offset += 4 + 4 + 8 + 8;
}
else {
poorstream_write_bits(&bitstream, 0, 63);
offset += 4 + 4 + 8;
}
poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset), 31);
poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset + 4), 22);
PRINTF("stark w1 %.*H\n", 32, dataContext.starkContext.w1);
PRINTF("stark w2 %.*H\n", 32, dataContext.starkContext.w2);
PRINTF("stark w3 %.*H\n", 32, dataContext.starkContext.w3);
// Prepare the UI
if (p1 == P1_STARK_ORDER) {
io_seproxyhal_io_heartbeat();
// amount to sell
stark_get_amount_string(dataBuffer, dataBuffer + 20, dataBuffer + 20 + 32 + 20 + 32 + 4 + 4, (char*)(dataBuffer + TMP_OFFSET), strings.common.fullAmount);
io_seproxyhal_io_heartbeat();
// amount to buy
stark_get_amount_string(dataBuffer + 20 + 32, dataBuffer + 20 + 32 + 20, dataBuffer + 20 + 32 + 20 + 32 + 4 + 4 + 8, (char*)(dataBuffer + TMP_OFFSET), strings.common.maxFee);
// src vault ID
snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "%d", U4BE(dataBuffer, 20 + 32 + 20 + 32));
}
else {
cx_ecfp_public_key_t publicKey;
// Check if the transfer is a self transfer
io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, bip32PathLength, privateKeyData);
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);
io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1);
os_memset(&privateKey, 0, sizeof(privateKey));
os_memset(privateKeyData, 0, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat();
selfTransfer = (os_memcmp(publicKey.W + 1, dataBuffer + 20 + 32, 32) == 0);
PRINTF("self transfer %d\n", selfTransfer);
io_seproxyhal_io_heartbeat();
// amount to transfer
stark_get_amount_string(dataBuffer, dataBuffer + 20, dataBuffer + 20 + 32 + 32 + 4 + 4, (char*)(dataBuffer + TMP_OFFSET), tmpContent.tmp);
// dest vault ID
snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "%d", U4BE(dataBuffer, 20 + 32 + 32 + 4));
if (!selfTransfer) {
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataBuffer + 20 + 32);
}
}
ux_flow_init(0, p1 == P1_STARK_ORDER ? ux_stark_limit_order_flow : selfTransfer ?
ux_stark_self_transfer_flow : ux_stark_transfer_flow, NULL);
*flags |= IO_ASYNCH_REPLY;
}
#endif

View File

@@ -0,0 +1,28 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "stark_utils.h"
#include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e) {
uint8_t privateKeyData[32];
uint8_t signature[72];
uint32_t tx = 0;
io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, tmpCtx.transactionContext.pathLength, privateKeyData);
io_seproxyhal_io_heartbeat();
stark_sign(signature, privateKeyData, dataContext.starkContext.w1, dataContext.starkContext.w2, dataContext.starkContext.w3);
G_io_apdu_buffer[0] = 0;
format_signature_out(signature);
tx = 65;
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
reset_app_context();
// 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
}
#endif

View File

@@ -0,0 +1,165 @@
#ifdef HAVE_STARKWARE
#include "shared_context.h"
#include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e);
UX_FLOW_DEF_NOCB(ux_stark_limit_order_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(ux_stark_limit_order_2_step,
bnnn_paging,
{
.title = "Limit",
.text = "Order"
});
UX_FLOW_DEF_NOCB(ux_stark_limit_order_3_step,
bnnn_paging,
{
.title = "Trading",
.text = "Pair"
});
UX_FLOW_DEF_NOCB(ux_stark_limit_order_4_step,
bnnn_paging,
{
.title = "Sell",
.text = strings.common.fullAmount
});
UX_FLOW_DEF_NOCB(ux_stark_limit_order_5_step,
bnnn_paging,
{
.title = "Buy",
.text = strings.common.maxFee
});
UX_FLOW_DEF_NOCB(ux_stark_limit_order_6_step,
bnnn_paging,
{
.title = "Token Accont",
.text = strings.common.fullAddress
});
UX_FLOW_DEF_VALID(
ux_stark_limit_order_7_step,
pbb,
io_seproxyhal_touch_stark_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_stark_limit_order_8_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_stark_limit_order_flow [] = {
&ux_stark_limit_order_1_step,
&ux_stark_limit_order_2_step,
&ux_stark_limit_order_3_step,
&ux_stark_limit_order_4_step,
&ux_stark_limit_order_5_step,
&ux_stark_limit_order_6_step,
&ux_stark_limit_order_7_step,
&ux_stark_limit_order_8_step,
FLOW_END_STEP,
};
//////////////////////////////////////////////////////////////////////
UX_FLOW_DEF_NOCB(ux_stark_transfer_1_step,
pnn,
{
&C_icon_eye,
"Review",
"transaction",
});
UX_FLOW_DEF_NOCB(ux_stark_transfer_2_step,
bnnn_paging,
{
.title = "Transfer",
.text = " "
});
UX_FLOW_DEF_NOCB(ux_stark_self_transfer_2_step,
bnnn_paging,
{
.title = "Self",
.text = "Transfer"
});
UX_FLOW_DEF_NOCB(ux_stark_transfer_3_step,
bnnn_paging,
{
.title = "Amount",
.text = tmpContent.tmp
});
UX_FLOW_DEF_NOCB(ux_stark_transfer_4_step,
bnnn_paging,
{
.title = "Master Account",
.text = strings.tmp.tmp
});
UX_FLOW_DEF_NOCB(ux_stark_transfer_5_step,
bnnn_paging,
{
.title = "Token Accont",
.text = strings.tmp.tmp2
});
UX_FLOW_DEF_VALID(
ux_stark_transfer_6_step,
pbb,
io_seproxyhal_touch_stark_ok(NULL),
{
&C_icon_validate_14,
"Accept",
"and send",
});
UX_FLOW_DEF_VALID(
ux_stark_transfer_7_step,
pb,
io_seproxyhal_touch_tx_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});
const ux_flow_step_t * const ux_stark_transfer_flow [] = {
&ux_stark_transfer_1_step,
&ux_stark_transfer_2_step,
&ux_stark_transfer_3_step,
&ux_stark_transfer_4_step,
&ux_stark_transfer_5_step,
&ux_stark_transfer_6_step,
&ux_stark_transfer_7_step,
FLOW_END_STEP,
};
const ux_flow_step_t * const ux_stark_self_transfer_flow [] = {
&ux_stark_transfer_1_step,
&ux_stark_self_transfer_2_step,
&ux_stark_transfer_3_step,
&ux_stark_transfer_5_step,
&ux_stark_transfer_6_step,
&ux_stark_transfer_7_step,
FLOW_END_STEP,
};
#endif