Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sdk timeout #54

Merged
merged 2 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ For the Ledger Nano S:

- Two bars along the top (just below the buttons) indicates that there is a double press function available (generally confirm/toggle or back). We will be working to ensure this function is always intuitive.

For the Ledger Nano X:
- Behavior is the same as the Nano S. Graphically there are no confirmation bars along the top (but double pressing the buttons still confirms/toggles).

For the Ledger Blue:
- The Ledger Blue uses a touchscreen, thus all you need to do is tap the buttons on the screen.

Expand All @@ -138,7 +141,7 @@ All warnings on the Ledger are there for a reason, **MAKE SURE TO READ THEM** an

When generating a transaction for the Ledger to sign, you will scroll through each transaction before signing off on the bundle. The transaction information will come up in order (while scrolling from left to right).

On the Ledger Nano S, the first screen will display the tx type (output, input, or change), as well as the amount. The next screen will display the corresponding address for said transaction. This will repeat until all transactions have been displayed, then you can select "Approve" or "Deny".
On the Ledger Nano S/X, the first screen will display the tx type (output, input, or change), as well as the amount. The next screen will display the corresponding address for said transaction. This will repeat until all transactions have been displayed, then you can select "Approve" or "Deny".

On the Ledger Blue each transaction entry in a bundle will fit on the screen, use the next button until you've confirmed all transactions and then select approve if everything is correct.

Expand All @@ -164,9 +167,9 @@ All warnings on the Ledger are there for a reason, **MAKE SURE TO READ THEM** an

### Limitations of Ledger Hardware Wallets

Due to the memory limitations of both the Ledger Nano S and the Ledger Blue, the transaction bundles have certain restrictions. The Ledger Nano S can only accept a transaction with a maximum bundle size of 8 and the Ledger Blue is limited to a maximum bundle size of 20.
Due to the memory limitations of the Ledger Nano S/X and the Ledger Blue, the transaction bundles have certain restrictions. The Ledger Nano S/X can only accept a transaction with a maximum bundle size of 10 and the Ledger Blue is limited to a maximum bundle size of 20.

An output and a change transaction each only require 1 bundle entry, however every input transaction requires the same number of bundle entries as the security level being used on the seed. Thus if using a Ledger Nano S you could have 1 output + 3 inputs (security level 2) + 1 change transaction and this would take up all 8 bundle entries. For security level 3 you could only have 1 output + 2 inputs + 1 change transaction.
An output and a change transaction each only require 1 bundle entry, however every input transaction requires the same number of bundle entries as the security level being used on the seed. Thus if using a Ledger Nano S or X you could have 1 output + 4 inputs (security level 2) + 1 change transaction and this would take up all 10 bundle entries. For security level 3 you could only have 1 output + 2 inputs + 1 change transaction, or 1 output + 3 inputs without a change transaction.

*Security level 2 is the default security level.*

Expand Down Expand Up @@ -231,13 +234,16 @@ See: [APDU API Specification](/docs/specification.md)
## Contributing

### Donations
Would you like to donate to help the development team? Send some IOTA to the following address:
This is a community project, done in our spare time for the betterment of the IOTA ecosystem and community.
Donating is not required, but is greatly appreciated. If you would like to donate please send some IOTA to the following address:
```
ADLJXS9SKYQKMVQFXR9JDUUJHJWGDNWHQZMDGJFGZOX9BZEKDSXBSPZTTWEYPTNM9OZMYDQWZXFHRTXRCOITXAGCJZ
TLCZOGKIARUQRBSJYSUTXVQYKPTOYOMQAUWUGBOCJJFQSXELFPEDF9LKDCUVKYDVGCJTCRANLOZJJKKNBEKVDHCJ9B
```
![IOTA Ledger Donation](resources/ledger_donation.png)

Please know that the donations made to this address will be shared with everyone who contributes (the contributions has to be worth something, of course)
Please know that the donations made to this address will be shared with everyone who meaningfully contributes to the project.

Thanks!

### As a developer
Would you like to contribute as a dev? Please check out our [Discord channel](https://discord.gg/U3qRjZj) to contact us!
Binary file modified resources/ledger_donation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 17 additions & 8 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#include "iota/seed.h"
#include "iota/signing.h"

// ms until timeout; must not be > 1min to avoid locking before timeout
#define TIMEOUT_MS 3000 // 3 sec
#define TIMEOUT_USER_BUNDLE_MS 100000 // 100 sec

#define CHECK_STATE(state, INS) \
((((state)&INS##_REQUIRED_STATE) != INS##_REQUIRED_STATE) || \
((state)&INS##_FORBIDDEN_STATE))
Expand All @@ -27,7 +31,7 @@
/// global variable storing all data needed across multiple api calls
API_CTX api;

void api_initialize(void)
void api_initialize()
{
MEMCLEAR(api);
}
Expand All @@ -38,7 +42,6 @@ static void reset_bundle(void)
MEMCLEAR(api.ctx.bundle);
MEMCLEAR(api.ctx.signing);
api.state_flags = 0;
ui_timeout_stop();
}

/** @brief Checks whether the given path differes from the stored path. */
Expand Down Expand Up @@ -283,8 +286,6 @@ unsigned int api_tx(uint8_t p1, const unsigned char *input_data,
}

ui_display_recv();
// reset next transaction timer
ui_timeout_start(false);

if (first) {
if (!IN_RANGE(input->last_index, 1, MAX_BUNDLE_SIZE - 1)) {
Expand Down Expand Up @@ -328,13 +329,16 @@ unsigned int api_tx(uint8_t p1, const unsigned char *input_data,
// perfectly valid bundle
if (!bundle_has_open_txs(&api.ctx.bundle)) {
// start interactive timeout
ui_timeout_start(true);
io_timeout_set(TIMEOUT_USER_BUNDLE_MS);
ui_sign_tx();
return IO_ASYNCH_REPLY;
}

// set timeout for the next tx to receive
io_timeout_set(TIMEOUT_MS);
// as the bundle is not yet complete, we cannot compute the hash yet
io_send_unfinished_bundle();

return 0;
}

Expand Down Expand Up @@ -395,18 +399,21 @@ unsigned int api_sign(uint8_t p1, const unsigned char *input_data,

// temporary screen during signing process
ui_display_signing();
ui_timeout_start(false);

SIGN_OUTPUT output;
output.fragments_remaining =
next_signature_fragment(&api.ctx.signing, output.signature_fragment);

// set timeout for the next fragment to be querried
if (output.fragments_remaining) {
io_timeout_set(TIMEOUT_MS);
}
io_send(&output, sizeof(output), SW_OK);

// signing is finished
if (!output.fragments_remaining) {

// signing is finished
api.state_flags &= ~SIGNING_STARTED;
io_timeout_reset();
ui_display_main_menu();
}

Expand All @@ -425,6 +432,7 @@ static void io_send_bundle_hash(const BUNDLE_CTX *ctx)

void user_sign_tx()
{
io_timeout_reset();
ui_display_validating();

const int retcode = bundle_validating_finalize(
Expand Down Expand Up @@ -475,6 +483,7 @@ unsigned int api_reset(uint8_t p1, const unsigned char *input_data,

reset_bundle();
ui_reset();
io_timeout_reset();

io_send(NULL, 0, SW_OK);
return 0;
Expand Down
27 changes: 27 additions & 0 deletions src/iota_io.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "iota_io.h"
#include "common.h"
#include "os_io_seproxyhal.h"
#include "api.h"

extern unsigned char G_io_apdu_buffer[IO_APDU_BUFFER_SIZE];
Expand All @@ -8,6 +9,7 @@ void io_initialize()
{
os_memset(G_io_apdu_buffer, 0, IO_APDU_BUFFER_SIZE);
api_initialize();
io_timeout_reset();
}

void io_send(const void *ptr, unsigned int length, unsigned short sw)
Expand Down Expand Up @@ -51,3 +53,28 @@ unsigned int iota_dispatch(const uint8_t ins, const uint8_t p1,
return 0;
}
}

void io_timeout_reset()
{
UX_CALLBACK_SET_INTERVAL(0);
}

void io_timeout_set(const unsigned int ms)
{
if (ms == 0) {
THROW(INVALID_PARAMETER);
}
UX_CALLBACK_SET_INTERVAL(ms);
}

void io_timeout_callback(const bool ux_allowed)
{
#ifdef TARGET_NANOS
UNUSED(ux_allowed);
#else
if (!ux_allowed) {
THROW(EXCEPTION_IO_RESET);
}
#endif
THROW(SW_COMMAND_TIMEOUT);
}
7 changes: 7 additions & 0 deletions src/iota_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ void io_send(const void *ptr, unsigned int length, unsigned short sw);
unsigned int iota_dispatch(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t len,
const unsigned char *input_data);

/// Sets the IO timeout to the given ms.
void io_timeout_set(unsigned int ms);
/// Resets and stops the IO timeout.
void io_timeout_reset(void);
// Callback to be called on timeout.
void io_timeout_callback(bool ux_allowed);

/* --- CLA --- */

#define CLA 0x7A
Expand Down
15 changes: 8 additions & 7 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ APDU_HEADER;
/// Returns true, if the device is not locked
static bool device_is_unlocked(void)
{
// this is the precise usage suggested by the SDK
#if CX_APILEVEL >= 9
return os_global_pin_is_validated() == BOLOS_UX_OK;
#else
return os_global_pin_is_validated();
#endif
}

static void IOTA_main()
Expand Down Expand Up @@ -99,6 +102,7 @@ static void IOTA_main()
// reset states and UI
api_initialize();
ui_reset();
io_timeout_reset();
}

// send the error code
Expand Down Expand Up @@ -168,12 +172,9 @@ unsigned char io_event(unsigned char channel)
break;

case SEPROXYHAL_TAG_TICKER_EVENT:
ui_timeout_tick();
// do not forward the ticker_event when the transaction is shown
if (ui_lock_forbidden()) {
break;
}
// fallthrough
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer,
{ io_timeout_callback(UX_ALLOWED); });
break;

default:
UX_DEFAULT_EVENT();
Expand Down
9 changes: 0 additions & 9 deletions src/ui/blue/blue_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ static void blue_ctx_initialize()
void ui_init()
{
blue_ctx_initialize();
ui_timeout_stop();

UX_SET_STATUS_BAR_COLOR(COLOUR_WHITE, COLOUR_GREEN);
ui_display_main_menu();
Expand Down Expand Up @@ -104,12 +103,4 @@ void ui_restore()
ui_force_draw();
}

bool ui_lock_forbidden()
{
if (blue_ui_state.state == STATE_TX)
return true;
else
return false;
}

#endif // TARGET_BLUE
18 changes: 0 additions & 18 deletions src/ui/nano/nano_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ void ui_init()
{
MEMCLEAR(ui_text);
MEMCLEAR(ui_state);
ui_timeout_stop();

ui_display_main_menu();
}
Expand Down Expand Up @@ -197,23 +196,6 @@ void ui_restore()
ui_force_draw();
}

bool ui_lock_forbidden(void)
{
// forbid app from locking during transaction (rely on tx timeout)
switch (ui_state.state) {
// BIP Path could be in tx or disp_addr
// (backup state will tell us which)
case STATE_BIP_PATH:
if (ui_state.nano_state_backup != STATE_BUNDLE)
return false;
case STATE_BUNDLE:
case STATE_BUNDLE_ADDR:
return true;
default:
return false;
}
}

static UI_BUTTON_PRESS translate_button_mask(const unsigned int button_mask)
{
switch (button_mask) {
Expand Down
37 changes: 0 additions & 37 deletions src/ui/ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@
#include "api.h"
#include "iota/addresses.h"

#define TICKS_PER_SECOND 10

// Seconds until UI timeout if expected inputs are not received
#define UI_TIMEOUT_SECONDS 3
#define UI_TIMEOUT_INTERACTIVE_SECONDS 100

#define WAIT_EVENT() \
io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer, \
sizeof(G_io_seproxyhal_spi_buffer), 0)

static uint16_t timer;

void ui_force_draw()
{
bool display_event_occurred = false;
Expand All @@ -33,32 +25,3 @@ void ui_force_draw()
WAIT_EVENT();
}
}

void ui_timeout_tick()
{
// timer not started
if (timer <= 0) {
return;
}

timer--;
if (timer == 0) {
// throw an exception so that a result is always returned
THROW(SW_COMMAND_TIMEOUT);
}
}

void ui_timeout_start(bool interactive)
{
if (interactive) {
timer = UI_TIMEOUT_INTERACTIVE_SECONDS * TICKS_PER_SECOND;
}
else {
timer = UI_TIMEOUT_SECONDS * TICKS_PER_SECOND;
}
}

void ui_timeout_stop()
{
timer = 0;
}
9 changes: 1 addition & 8 deletions src/ui/ui.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
#ifndef UI_H
#define UI_H

#include <stdbool.h>

/// Displays the current screen without sending an APDU message.
void ui_force_draw(void);

void ui_timeout_tick(void);
void ui_timeout_start(bool interactive);
void ui_timeout_stop(void);

// the following implementation are different for Blue and Nano
void ui_init(void);
void ui_display_main_menu(void);
Expand All @@ -21,6 +16,4 @@ void ui_sign_tx(void);
void ui_reset(void);
void ui_restore(void);

bool ui_lock_forbidden(void);

#endif // UI_H
18 changes: 9 additions & 9 deletions tests/test_mocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ void ui_display_signing()
{
}

void ui_timeout_start(bool interactive)
{
UNUSED(interactive);
}

void ui_timeout_stop()
{
}

void ui_display_address(const unsigned char *addr_bytes)
{
UNUSED(addr_bytes);
Expand Down Expand Up @@ -88,3 +79,12 @@ __attribute__((weak)) void io_send(const void *ptr, unsigned int length,
snprintf(msg, 100, "%s should not be called", __FUNCTION__);
mock_assert(false, msg, __FILE__, __LINE__);
}

void io_timeout_set(const unsigned int ms)
{
mock_assert(ms > 0, "invalid ms", __FILE__, __LINE__);
}

void io_timeout_reset()
{
}