Skip to content

Commit

Permalink
Merge pull request #4 from SiaFoundation/make-back-button-work
Browse files Browse the repository at this point in the history
Make Back Button Work
  • Loading branch information
lukechampine authored Nov 28, 2023
2 parents 9d76068 + 7764d44 commit 791bbad
Show file tree
Hide file tree
Showing 43 changed files with 475 additions and 439 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64
DEFINES += HAVE_LEGACY_PID
DEFINES += APPNAME=\"$(APPNAME)\"
DEFINES += APPVERSION=\"$(APPVERSION)\"
DEFINES += UNUSED\(x\)=\(void\)x

ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_NANOS TARGET_NANOS2))
DEFINES += HAVE_BAGL
Expand Down
14 changes: 11 additions & 3 deletions src/blake2b.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
#include <stdint.h>
#include <string.h>

#include "sia.h"

void blake2b_init(cx_blake2b_t *S) {
cx_blake2b_init(S, 256);
if (cx_blake2b_init_no_throw(S, 256) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
}

void blake2b_update(cx_blake2b_t *S, const uint8_t *in, uint64_t inlen) {
cx_hash((cx_hash_t *)S, 0, in, inlen, NULL, 0);
if (cx_hash_no_throw((cx_hash_t *)S, 0, in, inlen, NULL, 0) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
}

void blake2b_final(cx_blake2b_t *S, uint8_t *out, uint64_t outlen) {
uint8_t buf[32];
cx_hash((cx_hash_t *)S, CX_LAST, NULL, 0, buf, sizeof(buf));
if (cx_hash_no_throw((cx_hash_t *)S, CX_LAST, NULL, 0, buf, sizeof(buf)) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
memmove(out, buf, outlen);
}

Expand Down
328 changes: 160 additions & 168 deletions src/calcTxnHash.c

Large diffs are not rendered by default.

182 changes: 103 additions & 79 deletions src/calcTxnHash_nbgl.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,62 @@
static calcTxnHashContext_t *ctx = &global.calcTxnHashContext;

static void fmtTxnElem(void);
static uint16_t display_index(void);
static bool nav_callback(uint8_t page, nbgl_pageContent_t *content);
static void confirm_callback(bool confirm);

// Gets the current index number to be displayed in the UI
static uint16_t display_index(void) {
txn_state_t *txn = &ctx->txn;
uint16_t first_index_of_type = 0;
const txnElemType_e current_type = txn->elements[ctx->elementIndex].elemType;
for (uint16_t i = 0; i < txn->elementIndex; i++) {
if (current_type == txn->elements[i].elemType) {
first_index_of_type = i;
break;
}
}
return ctx->elementIndex - first_index_of_type + 1;
}

// This is a helper function that prepares an element of the transaction for
// display. It stores the type of the element in labelStr, and a human-
// readable representation of the element in fullStr. As in previous screens,
// partialStr holds the visible portion of fullStr.
static void fmtTxnElem(void) {
txn_state_t *txn = &ctx->txn;

switch (txn->elemType) {
case TXN_ELEM_SC_OUTPUT:
switch (txn->elements[ctx->elementIndex].elemType) {
case TXN_ELEM_SC_OUTPUT: {
memmove(ctx->labelStr, "SC Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
bin2dec(ctx->labelStr + 11, display_index());
// An element can have multiple screens. For each siacoin output, the
// user needs to see both the destination address and the amount.
// These are rendered in separate screens, and elemPart is used to
// identify which screen is being viewed.
if (ctx->elemPart == 0) {
memmove(ctx->fullStr, txn->outAddr, sizeof(txn->outAddr));
ctx->elemPart++;
} else {
memmove(ctx->fullStr, txn->outVal, sizeof(txn->outVal));
formatSC(ctx->fullStr, txn->valLen);
ctx->elemPart = 0;
}
format_address(ctx->fullStr[0], txn->elements[ctx->elementIndex].outAddr);
const uint8_t valLen = cur2dec(ctx->fullStr[1], txn->elements[ctx->elementIndex].outVal);
formatSC(ctx->fullStr[1], valLen);
break;
}

case TXN_ELEM_SF_OUTPUT:
case TXN_ELEM_SF_OUTPUT: {
memmove(ctx->labelStr, "SF Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
if (ctx->elemPart == 0) {
memmove(ctx->fullStr, txn->outAddr, sizeof(txn->outAddr));
ctx->elemPart++;
} else {
memmove(ctx->fullStr, txn->outVal, sizeof(txn->outVal));
memmove(ctx->fullStr + txn->valLen, " SF", 4);
ctx->elemPart = 0;
}
bin2dec(ctx->labelStr + 11, display_index());
format_address(ctx->fullStr[0], txn->elements[ctx->elementIndex].outAddr);
cur2dec(ctx->fullStr[1], txn->elements[ctx->elementIndex].outVal);
break;
}

case TXN_ELEM_MINER_FEE:
case TXN_ELEM_MINER_FEE: {
// Miner fees only have one part.
memmove(ctx->labelStr, "Miner Fee #", 11);
bin2dec(ctx->labelStr + 11, txn->sliceIndex);
memmove(ctx->fullStr, txn->outVal, sizeof(txn->outVal));
formatSC(ctx->fullStr, txn->valLen);
ctx->elemPart = 0;
bin2dec(ctx->labelStr + 11, display_index());

const uint8_t valLen = cur2dec(ctx->fullStr[0], txn->elements[ctx->elementIndex].outVal);
formatSC(ctx->fullStr[0], valLen);
break;
}

default:
// This should never happen.
Expand Down Expand Up @@ -96,61 +103,61 @@ static void confirm_callback(bool confirm) {
}
}

static nbgl_layoutTagValue_t pair;
static nbgl_layoutTagValue_t pairs[3];

static bool nav_callback(uint8_t page, nbgl_pageContent_t *content) {
UNUSED(page);
if (ctx->elemPart > 0) {
fmtTxnElem();
} else {
// Attempt to decode the next element of the transaction. Note that this
// code is essentially identical to ui_calcTxnHash_elem_button. Sadly,
// there doesn't seem to be a clean way to avoid this duplication.
switch (txn_next_elem(&ctx->txn)) {
case TXN_STATE_ERR:
io_exchange_with_code(SW_INVALID_PARAM, 0);
return false;
break;
case TXN_STATE_PARTIAL:
io_exchange_with_code(SW_OK, 0);
return false;
break;
case TXN_STATE_READY:
ctx->elemPart = 0;
fmtTxnElem();
break;
case TXN_STATE_FINISHED:
ctx->finished = true;

content->type = INFO_LONG_PRESS;
content->infoLongPress.icon = &C_stax_app_sia;
if (ctx->sign) {
memmove(ctx->fullStr, "with key #", 10);
bin2dec(ctx->fullStr + 10, ctx->keyIndex);
memmove(ctx->fullStr + 10 + (bin2dec(ctx->fullStr + 10, ctx->keyIndex)), "?", 2);

content->infoLongPress.text = "Sign Transaction";
content->infoLongPress.longPressText = ctx->fullStr;
} else {
memmove(G_io_apdu_buffer, ctx->txn.sigHash, 32);
io_exchange_with_code(SW_OK, 32);
bin2hex(ctx->fullStr, ctx->txn.sigHash, sizeof(ctx->txn.sigHash));

content->infoLongPress.text = ctx->fullStr;
content->infoLongPress.longPressText = "Confirm Hash";
}
return true;
break;
ctx->elementIndex = page;
if (ctx->elementIndex >= ctx->txn.elementIndex) {
const bool wasFinished = ctx->finished;
ctx->finished = true;

content->type = INFO_LONG_PRESS;
content->infoLongPress.icon = &C_stax_app_sia;
if (ctx->sign) {
memmove(ctx->fullStr[0], "with key #", 10);
bin2dec(ctx->fullStr[0] + 10, ctx->keyIndex);
memmove(ctx->fullStr[0] + 10 + (bin2dec(ctx->fullStr[0] + 10, ctx->keyIndex)), "?", 2);

content->infoLongPress.text = "Sign Transaction";
content->infoLongPress.longPressText = ctx->fullStr[0];
} else {
memmove(G_io_apdu_buffer, ctx->txn.sigHash, 32);
// prevent this from being sent twice and causing device to hang
if (!wasFinished) {
io_exchange_with_code(SW_OK, 32);
}
bin2hex(ctx->fullStr[0], ctx->txn.sigHash, sizeof(ctx->txn.sigHash));

content->infoLongPress.text = ctx->fullStr[0];
content->infoLongPress.longPressText = "Confirm Hash";
}
return true;
}

pair.item = ctx->labelStr;
pair.value = ctx->fullStr;
fmtTxnElem();

pairs[0].item = "Label";
pairs[0].value = ctx->labelStr;

if (ctx->txn.elements[ctx->elementIndex].elemType == TXN_ELEM_MINER_FEE) {
pairs[1].item = "Value";
pairs[1].value = ctx->fullStr[0];

content->tagValueList.nbPairs = 2;
content->tagValueList.pairs = &pairs[0];
} else {
pairs[1].item = "Address";
pairs[1].value = ctx->fullStr[0];
pairs[2].item = "Value";
pairs[2].value = ctx->fullStr[1];

content->tagValueList.nbPairs = 3;
content->tagValueList.pairs = &pairs[0];
}

content->type = TAG_VALUE_LIST;
content->title = NULL;
content->tagValueList.nbPairs = 1;
content->tagValueList.pairs = &pair;
content->type = TAG_VALUE_LIST;
content->tagValueList.callback = NULL;

content->tagValueList.startIndex = 0;
Expand All @@ -161,6 +168,10 @@ static bool nav_callback(uint8_t page, nbgl_pageContent_t *content) {
return true;
}

static void zero_ctx(void) {
explicit_bzero(ctx, sizeof(calcTxnHashContext_t));
}

// handleCalcTxnHash reads a signature index and a transaction, calculates the
// SigHash of the transaction, and optionally signs the hash using a specified
// key. The transaction is processed in a streaming fashion and displayed
Expand All @@ -170,17 +181,17 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
THROW(SW_INVALID_PARAM);
}

const bool prev_initialized = ctx->initialized;
if (p1 == P1_FIRST) {
// If this is the first packet of a transaction, the transaction
// context must not already be initialized. (Otherwise, an attacker
// could fool the user by concatenating two transactions.)
//
// NOTE: ctx->initialized is set to false when the Sia app loads.
if (prev_initialized) {
if (ctx->initialized) {
zero_ctx();
THROW(SW_IMPROPER_INIT);
}
ctx->finished = false;
explicit_bzero(ctx, sizeof(calcTxnHashContext_t));
ctx->initialized = true;

// If this is the first packet, it will include the key index, sig
Expand All @@ -204,16 +215,29 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
} else {
// If this is not P1_FIRST, the transaction must have been
// initialized previously.
if (!prev_initialized) {
if (!ctx->initialized) {
zero_ctx();
THROW(SW_IMPROPER_INIT);
}
}

// Add the new data to transaction decoder.
txn_update(&ctx->txn, dataBuffer, dataLength);

*flags |= IO_ASYNCH_REPLY;
nbgl_useCaseRegularReview(0, 0, "Cancel", NULL, nav_callback, confirm_callback);
switch (txn_parse(&ctx->txn)) {
case TXN_STATE_ERR:
// don't leave state lingering
zero_ctx();
THROW(SW_INVALID_PARAM);
break;
case TXN_STATE_PARTIAL:
THROW(SW_OK);
break;
case TXN_STATE_FINISHED:
*flags |= IO_ASYNCH_REPLY;
nbgl_useCaseRegularReview(0, 0, "Cancel", NULL, nav_callback, confirm_callback);
break;
}
}

#endif /* HAVE_BAGL */
33 changes: 16 additions & 17 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/*******************************************************************************
*
* (c) 2016 Ledger
* (c) 2018 Nebulous
*
* 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.
********************************************************************************/
*
* (c) 2016 Ledger
* (c) 2018 Nebulous
*
* 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.
********************************************************************************/

// This code also serves as a walkthrough for writing your own Ledger Nano S
// app. Begin by reading this file top-to-bottom, and proceed to the next file
Expand Down Expand Up @@ -304,7 +304,6 @@ static handler_fn_t *lookupHandler(uint8_t ins) {
static void sia_main(void) {
// Mark the transaction context as uninitialized.
explicit_bzero(&global, sizeof(global));
global.calcTxnHashContext.initialized = false;

volatile unsigned int rx = 0;
volatile unsigned int tx = 0;
Expand Down
23 changes: 17 additions & 6 deletions src/sia.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,33 @@

#include <cx.h>
#include <os.h>
#include <os_seed.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "blake2b.h"

void deriveSiaKeypair(uint32_t index, cx_ecfp_private_key_t *privateKey, cx_ecfp_public_key_t *publicKey) {
uint8_t keySeed[32];
uint8_t keySeed[64];
cx_ecfp_private_key_t pk;

// bip32 path for 44'/93'/n'/0'/0'
uint32_t bip32Path[] = {44 | 0x80000000, 93 | 0x80000000, index | 0x80000000, 0x80000000, 0x80000000};
os_perso_derive_node_bip32_seed_key(HDW_ED25519_SLIP10, CX_CURVE_Ed25519, bip32Path, 5, keySeed, NULL, NULL, 0);
if (os_derive_bip32_with_seed_no_throw(HDW_ED25519_SLIP10, CX_CURVE_Ed25519, bip32Path, 5, keySeed, NULL, NULL, 0)) {
THROW(SW_DEVELOPER_ERR);
}

cx_ecfp_init_private_key(CX_CURVE_Ed25519, keySeed, sizeof(keySeed), &pk);
if (cx_ecfp_init_private_key_no_throw(CX_CURVE_Ed25519, keySeed, 32, &pk) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
if (publicKey) {
cx_ecfp_init_public_key(CX_CURVE_Ed25519, NULL, 0, publicKey);
cx_ecfp_generate_pair(CX_CURVE_Ed25519, publicKey, &pk, 1);
if (cx_ecfp_init_public_key_no_throw(CX_CURVE_Ed25519, NULL, 0, publicKey) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
if (cx_ecfp_generate_pair_no_throw(CX_CURVE_Ed25519, publicKey, &pk, 1) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
}
if (privateKey) {
*privateKey = pk;
Expand All @@ -40,7 +49,9 @@ void extractPubkeyBytes(unsigned char *dst, const cx_ecfp_public_key_t *publicKe
void deriveAndSign(uint8_t *dst, uint32_t index, const uint8_t *hash) {
cx_ecfp_private_key_t privateKey;
deriveSiaKeypair(index, &privateKey, NULL);
cx_eddsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA512, hash, 32, NULL, 0, dst, 64, NULL);
if (cx_eddsa_sign_no_throw(&privateKey, CX_SHA512, hash, 32, dst, 64) != CX_OK) {
THROW(SW_DEVELOPER_ERR);
}
explicit_bzero(&privateKey, sizeof(privateKey));
}

Expand Down
Loading

0 comments on commit 791bbad

Please sign in to comment.