From 1b5231db80f7683531d9b64e3d4f203523f875b7 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Fri, 12 Jun 2020 16:43:01 +0200 Subject: [PATCH 01/37] multi-account: added `accountId` argument to `algorand_key_derive()` (WIP). --- src/algo_keys.c | 4 ++-- src/algo_keys.h | 2 +- src/main.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index ff50cb01..0b741603 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -11,13 +11,13 @@ static uint8_t privateKeyData[64]; uint8_t publicKey[32]; void -algorand_key_derive(void) +algorand_key_derive(uint32_t accountId) { uint32_t bip32Path[5]; bip32Path[0] = 44 | 0x80000000; bip32Path[1] = 283 | 0x80000000; - bip32Path[2] = 0 | 0x80000000; + bip32Path[2] = accountId | 0x80000000; bip32Path[3] = 0; bip32Path[4] = 0; os_perso_derive_node_bip32(CX_CURVE_Ed25519, bip32Path, sizeof(bip32Path) / sizeof(bip32Path[0]), privateKeyData, NULL); diff --git a/src/algo_keys.h b/src/algo_keys.h index ae3c6c35..5e4f4583 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -2,6 +2,6 @@ extern uint8_t publicKey[32]; -void algorand_key_derive(void); +void algorand_key_derive(uint32_t accountId); void algorand_private_key(cx_ecfp_private_key_t *privateKey); void algorand_public_key(uint8_t *buf); diff --git a/src/main.c b/src/main.c index e0939dbe..1fab34af 100644 --- a/src/main.c +++ b/src/main.c @@ -416,7 +416,7 @@ main(void) // Key derivation is quite slow, and must come *after* the calls to // BLE_power, otherwise the device will freeze on BLE disconnect on // the lock screen. - algorand_key_derive(); + algorand_key_derive(0); algorand_public_key(publicKey); ui_idle(); From 4316d5e503e437a2810ddd99f6b2e649d5a18a4c Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Fri, 12 Jun 2020 18:15:59 +0200 Subject: [PATCH 02/37] multi-account: pass `privateKeyData` as argument and removed `static` variable from `algo_keys.c` (WIP). WIP: not optimal yet as `privateDataKey` becomes a 64-bytes array allocated on the stack. --- src/algo_keys.c | 14 +++++--------- src/algo_keys.h | 7 ++++--- src/main.c | 9 ++++++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index 0b741603..b96892ed 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -3,15 +3,11 @@ #include "algo_keys.h" -// Allocate 64 bytes for privateKeyData because os_perso_derive_node_bip32 -// appears to write more than 32 bytes to this buffer. However, we only -// need 32 bytes for cx_ecfp_init_private_key.. -static uint8_t privateKeyData[64]; uint8_t publicKey[32]; void -algorand_key_derive(uint32_t accountId) +algorand_key_derive(uint32_t accountId, uint8_t *privateKeyData) { uint32_t bip32Path[5]; @@ -24,18 +20,18 @@ algorand_key_derive(uint32_t accountId) } void -algorand_private_key(cx_ecfp_private_key_t *privateKey) +algorand_private_key(uint8_t *privateKeyData, cx_ecfp_private_key_t *privateKey) { cx_ecfp_init_private_key(CX_CURVE_Ed25519, privateKeyData, 32, privateKey); } void -algorand_public_key(uint8_t *buf) +algorand_public_key(uint8_t *privateKeyData, uint8_t *buf) { cx_ecfp_private_key_t privateKey; - cx_ecfp_public_key_t publicKey; + cx_ecfp_public_key_t publicKey; - algorand_private_key(&privateKey); + algorand_private_key(privateKeyData, &privateKey); cx_ecfp_generate_pair(CX_CURVE_Ed25519, &publicKey, &privateKey, 1); // publicKey.W is 65 bytes: a header byte, followed by a 32-byte diff --git a/src/algo_keys.h b/src/algo_keys.h index 5e4f4583..82b18800 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -2,6 +2,7 @@ extern uint8_t publicKey[32]; -void algorand_key_derive(uint32_t accountId); -void algorand_private_key(cx_ecfp_private_key_t *privateKey); -void algorand_public_key(uint8_t *buf); +void algorand_key_derive(uint32_t accountId, uint8_t *privateKeyData); +void algorand_private_key(uint8_t *privateKeyData, + cx_ecfp_private_key_t *privateKey); +void algorand_public_key(uint8_t *privateKeyData, uint8_t *buf); diff --git a/src/main.c b/src/main.c index 1fab34af..60ca993a 100644 --- a/src/main.c +++ b/src/main.c @@ -57,8 +57,10 @@ txn_approve() PRINTF("Signing message: %.*h\n", msg_len, msgpack_buf); + uint8_t privateKeyData[64]; cx_ecfp_private_key_t privateKey; - algorand_private_key(&privateKey); + algorand_key_derive(0, privateKeyData); + algorand_private_key(privateKeyData, &privateKey); int sig_len = cx_eddsa_sign(&privateKey, 0, CX_SHA512, @@ -416,8 +418,9 @@ main(void) // Key derivation is quite slow, and must come *after* the calls to // BLE_power, otherwise the device will freeze on BLE disconnect on // the lock screen. - algorand_key_derive(0); - algorand_public_key(publicKey); + uint8_t privateKeyData[64]; + algorand_key_derive(0, privateKeyData); + algorand_public_key(privateKeyData, publicKey); ui_idle(); From 18c253684993eb3b66c50205f69b47336f3b6658 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Sat, 13 Jun 2020 06:51:05 +0200 Subject: [PATCH 03/37] multi-account: merged `algorand_private_key()` into `algorand_key_derive()`. --- src/algo_keys.c | 21 +++++++++------------ src/algo_keys.h | 6 ++---- src/main.c | 29 +++++++++++++---------------- 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index b96892ed..0ae953a1 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -7,9 +7,10 @@ uint8_t publicKey[32]; void -algorand_key_derive(uint32_t accountId, uint8_t *privateKeyData) +algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) { - uint32_t bip32Path[5]; + static uint8_t privateKeyData[64]; + static uint32_t bip32Path[5]; bip32Path[0] = 44 | 0x80000000; bip32Path[1] = 283 | 0x80000000; @@ -17,22 +18,18 @@ algorand_key_derive(uint32_t accountId, uint8_t *privateKeyData) bip32Path[3] = 0; bip32Path[4] = 0; os_perso_derive_node_bip32(CX_CURVE_Ed25519, bip32Path, sizeof(bip32Path) / sizeof(bip32Path[0]), privateKeyData, NULL); -} - -void -algorand_private_key(uint8_t *privateKeyData, cx_ecfp_private_key_t *privateKey) -{ cx_ecfp_init_private_key(CX_CURVE_Ed25519, privateKeyData, 32, privateKey); + + os_memset(bip32Path, 0, sizeof(bip32Path)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); } void -algorand_public_key(uint8_t *privateKeyData, uint8_t *buf) +algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf) { - cx_ecfp_private_key_t privateKey; - cx_ecfp_public_key_t publicKey; + cx_ecfp_public_key_t publicKey; - algorand_private_key(privateKeyData, &privateKey); - cx_ecfp_generate_pair(CX_CURVE_Ed25519, &publicKey, &privateKey, 1); + cx_ecfp_generate_pair(CX_CURVE_Ed25519, &publicKey, privateKey, 1); // publicKey.W is 65 bytes: a header byte, followed by a 32-byte // x coordinate, followed by a 32-byte y coordinate. The bytes diff --git a/src/algo_keys.h b/src/algo_keys.h index 82b18800..82615fba 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -2,7 +2,5 @@ extern uint8_t publicKey[32]; -void algorand_key_derive(uint32_t accountId, uint8_t *privateKeyData); -void algorand_private_key(uint8_t *privateKeyData, - cx_ecfp_private_key_t *privateKey); -void algorand_public_key(uint8_t *privateKeyData, uint8_t *buf); +void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey); +void algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf); diff --git a/src/main.c b/src/main.c index 60ca993a..1194342f 100644 --- a/src/main.c +++ b/src/main.c @@ -57,20 +57,17 @@ txn_approve() PRINTF("Signing message: %.*h\n", msg_len, msgpack_buf); - uint8_t privateKeyData[64]; cx_ecfp_private_key_t privateKey; - algorand_key_derive(0, privateKeyData); - algorand_private_key(privateKeyData, &privateKey); - - int sig_len = cx_eddsa_sign(&privateKey, - 0, CX_SHA512, - &msgpack_buf[0], msg_len, - NULL, 0, - G_io_apdu_buffer, - 6+2*(32+1), // Formerly from cx_compliance_141.c - NULL); - - tx = sig_len; + algorand_key_derive(0, &privateKey); + + tx = cx_eddsa_sign(&privateKey, + 0, CX_SHA512, + &msgpack_buf[0], msg_len, + NULL, 0, + G_io_apdu_buffer, + 6+2*(32+1), // Formerly from cx_compliance_141.c + NULL); + G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; @@ -418,9 +415,9 @@ main(void) // Key derivation is quite slow, and must come *after* the calls to // BLE_power, otherwise the device will freeze on BLE disconnect on // the lock screen. - uint8_t privateKeyData[64]; - algorand_key_derive(0, privateKeyData); - algorand_public_key(privateKeyData, publicKey); + cx_ecfp_private_key_t privateKey; + algorand_key_derive(0, &privateKey); + algorand_public_key(&privateKey, publicKey); ui_idle(); From e65edd984fc7dcec9a9d56610c6df5beb80b269c Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Sat, 13 Jun 2020 06:52:42 +0200 Subject: [PATCH 04/37] multi-account: removed `publicKey` (WIP). WIP: temporarily removed not displaying sender with our public address. --- src/algo_keys.c | 2 -- src/algo_keys.h | 1 - src/main.c | 14 +++++--------- src/ui_address.c | 5 +++++ src/ui_txn.c | 2 ++ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index 0ae953a1..b892f336 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -4,8 +4,6 @@ #include "algo_keys.h" -uint8_t publicKey[32]; - void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) { diff --git a/src/algo_keys.h b/src/algo_keys.h index 82615fba..6a1b94ae 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -1,6 +1,5 @@ #include "cx.h" -extern uint8_t publicKey[32]; void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey); void algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf); diff --git a/src/main.c b/src/main.c index 1194342f..50181a22 100644 --- a/src/main.c +++ b/src/main.c @@ -238,8 +238,11 @@ algorand_main(void) } break; case INS_GET_PUBLIC_KEY: { - os_memmove(G_io_apdu_buffer, publicKey, sizeof(publicKey)); - tx = sizeof(publicKey); + cx_ecfp_private_key_t privateKey; + + algorand_key_derive(0, &privateKey); + algorand_public_key(&privateKey, G_io_apdu_buffer); + tx = 32; THROW(0x9000); } break; @@ -412,13 +415,6 @@ main(void) BLE_power(1, "Nano X"); #endif - // Key derivation is quite slow, and must come *after* the calls to - // BLE_power, otherwise the device will freeze on BLE disconnect on - // the lock screen. - cx_ecfp_private_key_t privateKey; - algorand_key_derive(0, &privateKey); - algorand_public_key(&privateKey, publicKey); - ui_idle(); algorand_main(); diff --git a/src/ui_address.c b/src/ui_address.c index 31f7d741..4c1166cb 100644 --- a/src/ui_address.c +++ b/src/ui_address.c @@ -65,6 +65,11 @@ void step_address() { char checksummed[65]; + uint8_t publicKey[32]; + cx_ecfp_private_key_t privateKey; + + algorand_key_derive(0, &privateKey); + algorand_public_key(&privateKey, publicKey); checksummed_addr(publicKey, checksummed); ui_text_put(checksummed); } diff --git a/src/ui_txn.c b/src/ui_txn.c index 34ec43ec..5bcdb711 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -71,9 +71,11 @@ static int step_txn_type() { } static int step_sender() { +#if 0 // TEMP if (os_memcmp(publicKey, current_txn.sender, sizeof(current_txn.sender)) == 0) { return 0; } +#endif char checksummed[65]; checksummed_addr(current_txn.sender, checksummed); From 21949372ae27716679b980aa4e414b627e9358f0 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Sat, 13 Jun 2020 07:12:34 +0200 Subject: [PATCH 05/37] multi-account: made `algorand_public_key()` return public key size. --- src/algo_keys.c | 3 ++- src/algo_keys.h | 2 +- src/main.c | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index b892f336..7a2c14c1 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -22,7 +22,7 @@ algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) os_memset(privateKeyData, 0, sizeof(privateKeyData)); } -void +int algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf) { cx_ecfp_public_key_t publicKey; @@ -40,4 +40,5 @@ algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf) if (publicKey.W[32] & 1) { buf[31] |= 0x80; } + return 32; } diff --git a/src/algo_keys.h b/src/algo_keys.h index 6a1b94ae..93e1fcfe 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -2,4 +2,4 @@ void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey); -void algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf); +int algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf); diff --git a/src/main.c b/src/main.c index 50181a22..95422679 100644 --- a/src/main.c +++ b/src/main.c @@ -241,8 +241,7 @@ algorand_main(void) cx_ecfp_private_key_t privateKey; algorand_key_derive(0, &privateKey); - algorand_public_key(&privateKey, G_io_apdu_buffer); - tx = 32; + tx = algorand_public_key(&privateKey, G_io_apdu_buffer); THROW(0x9000); } break; From c3fd60da8029675164f5305e1449380b22dbbcaf Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 18 Jun 2020 18:12:06 +0200 Subject: [PATCH 06/37] multi-account: handle account id in `INS_GET_PUBLIC_KEY` and `INS_SIGN_MSGPACK` apdus. --- src/algo_tx.h | 2 ++ src/main.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/algo_tx.h b/src/algo_tx.h index a7980e4b..5c0b8c60 100644 --- a/src/algo_tx.h +++ b/src/algo_tx.h @@ -60,6 +60,8 @@ struct txn_asset_config { struct txn { enum TXTYPE type; + // Account Id asscociated with this transaction. + uint32_t accountId; // Common header fields uint8_t sender[32]; diff --git a/src/main.c b/src/main.c index 95422679..1f6d2593 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; #define P1_FIRST 0x00 #define P1_MORE 0x80 +#define P1_WITH_ACCOUNT_ID 0x01 #define P2_LAST 0x00 #define P2_MORE 0x80 @@ -59,7 +60,6 @@ txn_approve() cx_ecfp_private_key_t privateKey; algorand_key_derive(0, &privateKey); - tx = cx_eddsa_sign(&privateKey, 0, CX_SHA512, &msgpack_buf[0], msg_len, @@ -193,8 +193,16 @@ algorand_main(void) } break; case INS_SIGN_MSGPACK: { - switch (G_io_apdu_buffer[OFFSET_P1]) { + uint8_t *cdata = &G_io_apdu_buffer[OFFSET_CDATA]; + + switch (G_io_apdu_buffer[OFFSET_P1] & 0x80) { case P1_FIRST: + os_memset(¤t_txn, 0, sizeof(current_txn)); + current_txn.accountId = 0; + if (G_io_apdu_buffer[OFFSET_P1] & P1_WITH_ACCOUNT_ID) { + current_txn.accountId = U4BE(cdata, 0); + cdata += sizeof(uint32_t); + } msgpack_next_off = 0; break; case P1_MORE: @@ -208,7 +216,7 @@ algorand_main(void) THROW(0x6700); } - os_memmove(&msgpack_buf[msgpack_next_off], &G_io_apdu_buffer[OFFSET_CDATA], lc); + os_memmove(&msgpack_buf[msgpack_next_off], cdata, lc); msgpack_next_off += lc; switch (G_io_apdu_buffer[OFFSET_P2]) { @@ -239,8 +247,22 @@ algorand_main(void) case INS_GET_PUBLIC_KEY: { cx_ecfp_private_key_t privateKey; + uint32_t accountId = 0; + + if (rx > OFFSET_LC) { + uint8_t lc = G_io_apdu_buffer[OFFSET_LC]; + if (lc == sizeof(uint32_t)) { + accountId = U4BE(G_io_apdu_buffer, OFFSET_CDATA); + } else if (lc != 0) { + return THROW(0x6a85); + } + } - algorand_key_derive(0, &privateKey); + /* + * Push derived key to `G_io_apdu_buffer` + * and return pushed buffer length. + */ + algorand_key_derive(accountId, &privateKey); tx = algorand_public_key(&privateKey, G_io_apdu_buffer); THROW(0x9000); } break; From e43a53855acd50221f08b29ffc108d01d99d2eb3 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 18 Jun 2020 18:19:46 +0200 Subject: [PATCH 07/37] multi-account: restore 'do-not-display-sender-if-me' feature. => not optimizing stack... => feature requirement to be checked. --- src/ui_txn.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index 5bcdb711..056943bb 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -71,11 +71,14 @@ static int step_txn_type() { } static int step_sender() { -#if 0 // TEMP + uint8_t publicKey[32]; + cx_ecfp_private_key_t privateKey; + algorand_key_derive(0, &privateKey); + algorand_public_key(&privateKey, publicKey); if (os_memcmp(publicKey, current_txn.sender, sizeof(current_txn.sender)) == 0) { return 0; } -#endif + char checksummed[65]; checksummed_addr(current_txn.sender, checksummed); From 82e55080d79b47c257d7c7f674d11e86f4f828d3 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 18 Jun 2020 23:58:24 +0200 Subject: [PATCH 08/37] muti-account: fixed `lc` length when `uint32_t` account id is included in apdu. --- src/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 1f6d2593..f833d056 100644 --- a/src/main.c +++ b/src/main.c @@ -194,14 +194,19 @@ algorand_main(void) case INS_SIGN_MSGPACK: { uint8_t *cdata = &G_io_apdu_buffer[OFFSET_CDATA]; + uint8_t lc = G_io_apdu_buffer[OFFSET_LC]; switch (G_io_apdu_buffer[OFFSET_P1] & 0x80) { case P1_FIRST: os_memset(¤t_txn, 0, sizeof(current_txn)); current_txn.accountId = 0; if (G_io_apdu_buffer[OFFSET_P1] & P1_WITH_ACCOUNT_ID) { + if (lc < sizeof(uint32_t)) { + THROW(0x6700); + } current_txn.accountId = U4BE(cdata, 0); cdata += sizeof(uint32_t); + lc -= sizeof(uint32_t); } msgpack_next_off = 0; break; @@ -211,7 +216,6 @@ algorand_main(void) THROW(0x6B00); } - uint8_t lc = G_io_apdu_buffer[OFFSET_LC]; if (msgpack_next_off + lc > sizeof(msgpack_buf)) { THROW(0x6700); } From 8c37b8505ac708f3da54f6fe54ef9f2ffb3306ff Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 09:42:30 +0200 Subject: [PATCH 09/37] multi-account: pass `privateKey` input argument as `const` pointer to `algorand_public_key()`. --- src/algo_keys.c | 7 +++++-- src/algo_keys.h | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index 7a2c14c1..6485aa97 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -23,11 +23,14 @@ algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) } int -algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf) +algorand_public_key(const cx_ecfp_private_key_t *privateKey, uint8_t *buf) { cx_ecfp_public_key_t publicKey; - cx_ecfp_generate_pair(CX_CURVE_Ed25519, &publicKey, privateKey, 1); + cx_ecfp_generate_pair(CX_CURVE_Ed25519, + &publicKey, + (cx_ecfp_private_key_t *)privateKey, + 1); // publicKey.W is 65 bytes: a header byte, followed by a 32-byte // x coordinate, followed by a 32-byte y coordinate. The bytes diff --git a/src/algo_keys.h b/src/algo_keys.h index 93e1fcfe..37008190 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -1,5 +1,4 @@ #include "cx.h" - void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey); -int algorand_public_key(cx_ecfp_private_key_t *privateKey, uint8_t *buf); +int algorand_public_key(const cx_ecfp_private_key_t *privateKey, uint8_t *buf); From e4e5acc1884d72681aa5ca7121c690963d5482b7 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 09:49:24 +0200 Subject: [PATCH 10/37] multi-account: fixed signing transaction with account number from `current_txn` state struct. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index f833d056..771f84a2 100644 --- a/src/main.c +++ b/src/main.c @@ -59,7 +59,7 @@ txn_approve() PRINTF("Signing message: %.*h\n", msg_len, msgpack_buf); cx_ecfp_private_key_t privateKey; - algorand_key_derive(0, &privateKey); + algorand_key_derive(current_txn.accountId, &privateKey); tx = cx_eddsa_sign(&privateKey, 0, CX_SHA512, &msgpack_buf[0], msg_len, From af7f1ad85df3fee69d3e05cc8345edad1c254769 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 10:21:02 +0200 Subject: [PATCH 11/37] mutli-ccount: fixed `step_sender()` taking `current_txn.accountId` to derive key. --- src/ui_txn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index 056943bb..4b5271a2 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -73,13 +73,12 @@ static int step_txn_type() { static int step_sender() { uint8_t publicKey[32]; cx_ecfp_private_key_t privateKey; - algorand_key_derive(0, &privateKey); + algorand_key_derive(current_txn.accountId, &privateKey); algorand_public_key(&privateKey, publicKey); if (os_memcmp(publicKey, current_txn.sender, sizeof(current_txn.sender)) == 0) { return 0; } - char checksummed[65]; checksummed_addr(current_txn.sender, checksummed); ui_text_put(checksummed); From 7ecc2d870e0d41816587353aa6c2f89cfaf28272 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 13:07:28 +0200 Subject: [PATCH 12/37] multi-account: wrapped `os_perso_derive_node_bip32()` and `cx_eddsa_sign()` with `io_seproxyhal_io_heartbeat()`. calls --- src/algo_keys.c | 6 ++++++ src/main.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/algo_keys.c b/src/algo_keys.c index 6485aa97..bc63f6ae 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -1,5 +1,6 @@ #include "os.h" #include "cx.h" +#include "os_io_seproxyhal.h" #include "algo_keys.h" @@ -15,9 +16,14 @@ algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) bip32Path[2] = accountId | 0x80000000; bip32Path[3] = 0; bip32Path[4] = 0; + + io_seproxyhal_io_heartbeat(); + os_perso_derive_node_bip32(CX_CURVE_Ed25519, bip32Path, sizeof(bip32Path) / sizeof(bip32Path[0]), privateKeyData, NULL); cx_ecfp_init_private_key(CX_CURVE_Ed25519, privateKeyData, 32, privateKey); + io_seproxyhal_io_heartbeat(); + os_memset(bip32Path, 0, sizeof(bip32Path)); os_memset(privateKeyData, 0, sizeof(privateKeyData)); } diff --git a/src/main.c b/src/main.c index 771f84a2..b63f6ea5 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,9 @@ txn_approve() cx_ecfp_private_key_t privateKey; algorand_key_derive(current_txn.accountId, &privateKey); + + io_seproxyhal_io_heartbeat(); + tx = cx_eddsa_sign(&privateKey, 0, CX_SHA512, &msgpack_buf[0], msg_len, @@ -68,6 +71,8 @@ txn_approve() 6+2*(32+1), // Formerly from cx_compliance_141.c NULL); + io_seproxyhal_io_heartbeat(); + G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; From 78e6c8d750878caf613e5105ab39e3c55b4311c3 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 14:35:32 +0200 Subject: [PATCH 13/37] multi-account: fixed bug where `current_txn.accountId` was erased by `tx_decode()` leading to wrong signature with non zero BIP44 accounts. --- src/algo_tx_dec.c | 2 ++ src/main.c | 1 + 2 files changed, 3 insertions(+) diff --git a/src/algo_tx_dec.c b/src/algo_tx_dec.c index 862a3385..56c6ae0e 100644 --- a/src/algo_tx_dec.c +++ b/src/algo_tx_dec.c @@ -216,8 +216,10 @@ tx_decode(uint8_t *buf, int buflen, struct txn *t) { char* ret = NULL; uint8_t* buf_end = buf + buflen; + uint32_t accountId = t->accountId; // Save `accountId` os_memset(t, 0, sizeof(*t)); + t->accountId = accountId; BEGIN_TRY { TRY { diff --git a/src/main.c b/src/main.c index b63f6ea5..baeca83a 100644 --- a/src/main.c +++ b/src/main.c @@ -57,6 +57,7 @@ txn_approve() msg_len = 2 + tx_encode(¤t_txn, msgpack_buf+2, sizeof(msgpack_buf)-2); PRINTF("Signing message: %.*h\n", msg_len, msgpack_buf); + PRINTF("Signing message: accountId:%d\n", current_txn.accountId); cx_ecfp_private_key_t privateKey; algorand_key_derive(current_txn.accountId, &privateKey); From e6b784863769e18938524ee5b9467d5a1ef2ae55 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Thu, 25 Jun 2020 14:43:09 +0200 Subject: [PATCH 14/37] multi-account: made `algorand_public_key()` return `size_t` type instead of `int`. --- src/algo_keys.c | 2 +- src/algo_keys.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algo_keys.c b/src/algo_keys.c index bc63f6ae..1aa13527 100644 --- a/src/algo_keys.c +++ b/src/algo_keys.c @@ -28,7 +28,7 @@ algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey) os_memset(privateKeyData, 0, sizeof(privateKeyData)); } -int +size_t algorand_public_key(const cx_ecfp_private_key_t *privateKey, uint8_t *buf) { cx_ecfp_public_key_t publicKey; diff --git a/src/algo_keys.h b/src/algo_keys.h index 37008190..289c82f5 100644 --- a/src/algo_keys.h +++ b/src/algo_keys.h @@ -1,4 +1,4 @@ #include "cx.h" void algorand_key_derive(uint32_t accountId, cx_ecfp_private_key_t *privateKey); -int algorand_public_key(const cx_ecfp_private_key_t *privateKey, uint8_t *buf); +size_t algorand_public_key(const cx_ecfp_private_key_t *privateKey, uint8_t *buf); From 49be7ee9fe82b88d298c1333fa82f7ff0a8ac7ec Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Fri, 10 Jul 2020 12:50:00 +0200 Subject: [PATCH 15/37] Switch nano s UI to flows --- .gitignore | 1 + Makefile | 3 +- cli/.gitignore | 2 +- glyphs/icon_back.gif | Bin 0 -> 74 bytes glyphs/icon_back_x.gif | Bin 0 -> 1123 bytes glyphs/icon_certificate.gif | Bin 0 -> 1133 bytes glyphs/icon_eye.gif | Bin 0 -> 1130 bytes glyphs/icon_toggle_reset.gif | Bin 0 -> 225 bytes glyphs/icon_toggle_set.gif | Bin 0 -> 227 bytes glyphs/icon_warning.gif | Bin 0 -> 85 bytes src/algo_keys.c | 18 +- src/algo_keys.h | 11 +- src/algo_tx.c | 2 +- src/algo_tx.h | 15 +- src/algo_tx_dec.c | 2 +- src/algo_ui.h | 55 +--- src/glyphs.c | 108 -------- src/glyphs.h | 88 ------ src/main.c | 70 +++-- src/ui_address.c | 105 +++----- src/ui_idle.c | 90 ++----- src/ui_text.c | 35 --- src/ui_txn.c | 429 +++++++++++++----------------- src/ux.c | 9 +- tests/.gitignore | 5 + tests/Makefile | 17 ++ tests/README.md | 130 +++++++++ tests/pytest.ini | 5 + tests/requirements.txt | 59 ++++ tests/test/__init__.py | 0 tests/test/conftest.py | 66 +++++ tests/test/dongle.py | 96 +++++++ tests/test/speculos.py | 111 ++++++++ tests/test/test_get_public_key.py | 123 +++++++++ tests/test/test_sign_msgpack.py | 120 +++++++++ 35 files changed, 1072 insertions(+), 703 deletions(-) create mode 100644 glyphs/icon_back.gif create mode 100644 glyphs/icon_back_x.gif create mode 100644 glyphs/icon_certificate.gif create mode 100644 glyphs/icon_eye.gif create mode 100644 glyphs/icon_toggle_reset.gif create mode 100644 glyphs/icon_toggle_set.gif create mode 100644 glyphs/icon_warning.gif delete mode 100644 src/glyphs.c delete mode 100644 src/glyphs.h create mode 100644 tests/.gitignore create mode 100644 tests/Makefile create mode 100644 tests/README.md create mode 100644 tests/pytest.ini create mode 100644 tests/requirements.txt create mode 100644 tests/test/__init__.py create mode 100644 tests/test/conftest.py create mode 100644 tests/test/dongle.py create mode 100644 tests/test/speculos.py create mode 100644 tests/test/test_get_public_key.py create mode 100644 tests/test/test_sign_msgpack.py diff --git a/.gitignore b/.gitignore index a5dda430..3a42a935 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /dep /obj *.swp +/src/glyphs.* \ No newline at end of file diff --git a/Makefile b/Makefile index 875e298e..7201e572 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.0.8 +APPVERSION = 1.2.0 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" @@ -31,7 +31,6 @@ DEFINES += HAVE_BAGL HAVE_SPRINTF DEFINES += HAVE_BOLOS_APP_STACK_CANARY ifeq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += HAVE_UX_LEGACY DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 else ifeq ($(TARGET_NAME),TARGET_NANOX) DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 diff --git a/cli/.gitignore b/cli/.gitignore index a74b07ae..ed948393 100644 --- a/cli/.gitignore +++ b/cli/.gitignore @@ -1 +1 @@ -/*.pyc +/*.pyc \ No newline at end of file diff --git a/glyphs/icon_back.gif b/glyphs/icon_back.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2a7e6d4fa290e4875992d4024e988d14b91df26 GIT binary patch literal 74 zcmZ?wbhEHbh+i z#(Mch>H3D2mX`VkM*2oZxP`3=Lf^oD9vKom`v^ z4IGV}EX^$3V0vBhlS^|`^GaZPQxJOHaOwpmh};68%`T}$nPsUdZbkXI3Sf_0W#V>= zGfwlMdQ)(_#RaEceV}9XLD7p8-7q0w8Uiuli5JL$C;!wuV45!iCT_<6|Nj2@{p;tC z@87({PcxqRv3h4bgmo;iK$ z#u35cm<%;FYmM&SmXyJnS^XAT( zJ!|HS>C>i8nLKIYg#NzXp6;&Bj`p_Jmgc6$hWfhNn(C^`it@73lH#Jmg8aPPob0U3 zjP$hBl;otug!s7FnCPg;i14t`kl>)e0DnJUA8#*D4|g|LV6Zqk*xT9KSX)_In46iJ z7#kTH=@}C@U!{$jiyfNJ~jdh>MAe2nz`c@bmHVaC32Tu(PqUFf%a% wONgH=z;c3tK?g*D$_@r5iJtzIs~KwUGz+zPEiKD<-x8T<*15Dbij~0{0O2}{jsO4v literal 0 HcmV?d00001 diff --git a/glyphs/icon_certificate.gif b/glyphs/icon_certificate.gif new file mode 100644 index 0000000000000000000000000000000000000000..89b529f792aeb77c8cc3b4d4782d8280b3c6c204 GIT binary patch literal 1133 zcmZ?wbhEHbh+i z#(Mch>H3D2mX`VkM*2oZxP`3=Lf^oD9vKom`v^ z4IGV}EX^$3V0vBhlS^|`^GaZPQxJOHaOwpmh};68%`T}$nPsUdZbkXI3Sf_0W#V>= zGfwlMdQ)(_#RaEceV}9XLD7p8-7q0w8Uiuli5JL$C;!wuV45!iCT_<6|Nj2@{p;tC z@87({PcxqRv3h4bgmo;iK$ z#u35cm<%;FYmM&SmXyJnS^XAT( zJ!|HS>C>i8nLKIYg#NzXp6;&Bj`p_Jmgc6$hWfhNn(C^`it@73lH#Jmg8aPPob0U3 zjP$hBl;otug!s7FnCPg;i14t`kl>)e0DnJUA8#*D4|g|LV6Zqk*xT9KSX)_In46iJ z7#kTH=@}C@U!{$jiyfNJ~jdh>MAe2nz`c@bmHVaC32Tu(PqUFf%a% zONgH=z;c3tK?g*D$_@r5rJnxG17}|h+i z#(Mch>H3D2mX`VkM*2oZxP`3=Lf^oD9vKom`v^ z4IGV}EX^$3V0vBhlS^|`^GaZPQxJOHaOwpmh};68%`T}$nPsUdZbkXI3Sf_0W#V>= zGfwlMdQ)(_#RaEceV}9XLD7p8-7q0w8Uiuli5JL$C;!wuV45!iCT_<6|Nj2@{p;tC z@87({PcxqRv3h4bgmo;iK$ z#u35cm<%;FYmM&SmXyJnS^XAT( zJ!|HS>C>i8nLKIYg#NzXp6;&Bj`p_Jmgc6$hWfhNn(C^`it@73lH#Jmg8aPPob0U3 zjP$hBl;otug!s7FnCPg;i14t`kl>)e0DnJUA8#*D4|g|LV6Zqk*xT9KSX)_In46iJ z7#kTH=@}C@U!{$jiyfNJ~jdh>MAe2nz`c@bmHVaC32Tu(PqUFf%a% zONgH=z;c3tK?g*D$_@r5`JVojvmYFo*YYB?T-40^%iq9xH)NNlPJ66kronvNgOR}+ E02G{#(EtDd literal 0 HcmV?d00001 diff --git a/glyphs/icon_toggle_reset.gif b/glyphs/icon_toggle_reset.gif new file mode 100644 index 0000000000000000000000000000000000000000..450bc869d02ecc35afc46e622e175c5e4afca20d GIT binary patch literal 225 zcmV<703QEGNk%w1VITk!0J8u9-QC^R*4EO}()RZD?d|RI^78!r{Ls+Q{{H^W&CTB4 z-p=jZ47`T76<|IEzHA^8LV00000EC2ui03ZMo000Dzut`ajNH6QXlTgGYHbual z=oS2p@Hr>=Y?x*0!CneV{8Cb2UlrYbRkT6QDgxWI!9gq*06JE~1l$0M&>}L>STr|?VH8@(Es)oYV7nDO zEa41+93?z1>BoT$Jvtype) { diff --git a/src/algo_tx.h b/src/algo_tx.h index 5c0b8c60..2d4d6114 100644 --- a/src/algo_tx.h +++ b/src/algo_tx.h @@ -58,7 +58,7 @@ struct txn_asset_config { struct asset_params params; }; -struct txn { +typedef struct{ enum TXTYPE type; // Account Id asscociated with this transaction. uint32_t accountId; @@ -88,23 +88,24 @@ struct txn { struct txn_asset_freeze asset_freeze; struct txn_asset_config asset_config; }; -}; +} txn_t; // tx_encode produces a canonical msgpack encoding of a transaction. // buflen is the size of the buffer. The return value is the length // of the resulting encoding. -unsigned int tx_encode(struct txn *t, uint8_t *buf, int buflen); +unsigned int tx_encode(txn_t *t, uint8_t *buf, int buflen); // tx_decode takes a canonical msgpack encoding of a transaction, and -// unpacks it into a struct txn. The return value is NULL for success, +// unpacks it into a txn_t. The return value is NULL for success, // or a string describing the error on failure. Decoding may or may // not succeed for a non-canonical encoding. -char* tx_decode(uint8_t *buf, int buflen, struct txn *t); +char* tx_decode(uint8_t *buf, int buflen, txn_t *t); // We have a global transaction that is the subject of the current // operation, if any. -extern struct txn current_txn; +extern txn_t current_txn; // Two callbacks into the main code: approve and deny signing. void txn_approve(); -void txn_deny(); +void address_approve(); +void user_approval_denied(); diff --git a/src/algo_tx_dec.c b/src/algo_tx_dec.c index 56c6ae0e..15cdef14 100644 --- a/src/algo_tx_dec.c +++ b/src/algo_tx_dec.c @@ -212,7 +212,7 @@ decode_asset_params(uint8_t **bufp, uint8_t *buf_end, struct asset_params *res) } char* -tx_decode(uint8_t *buf, int buflen, struct txn *t) +tx_decode(uint8_t *buf, int buflen, txn_t *t) { char* ret = NULL; uint8_t* buf_end = buf + buflen; diff --git a/src/algo_ui.h b/src/algo_ui.h index 83898431..2499a10f 100644 --- a/src/algo_ui.h +++ b/src/algo_ui.h @@ -1,62 +1,15 @@ -#if defined(TARGET_NANOX) #include "ux.h" -#endif // TARGET_NANOX extern char lineBuffer[]; +extern char caption[20]; extern char text[128]; -void ui_loading(); void ui_idle(); -void ui_address(); +void ui_address_approval(); void ui_txn(); +void ux_approve_txn(); void ui_text_put(const char *msg); void ui_text_putn(const char *msg, size_t maxlen); -int ui_text_more(); -void step_address(); - -// Override some of the Ledger X UI macros to enable step skipping -#if defined(TARGET_NANOX) - -// If going backwards, skip backwards. Otherwise, skip forwards. -#define SKIPEMPTY(stepnum) \ - if (stepnum < ux_last_step) { \ - ux_flow_prev(); \ - } else { \ - ux_flow_next(); \ - } \ - return - -#define ALGO_UX_STEP_NOCB_INIT(txtype, stepnum, layoutkind, preinit, ...) \ - void txn_flow_ ## stepnum ##_init (unsigned int stack_slot) { \ - if (txtype != ALL_TYPES && txtype != current_txn.type) { SKIPEMPTY(stepnum); }; \ - if (preinit == 0) { SKIPEMPTY(stepnum); }; \ - ux_last_step = stepnum; \ - ux_layout_ ## layoutkind ## _init(stack_slot); \ - } \ - const ux_layout_ ## layoutkind ## _params_t txn_flow_ ## stepnum ##_val = __VA_ARGS__; \ - const ux_flow_step_t txn_flow_ ## stepnum = { \ - txn_flow_ ## stepnum ## _init, \ - & txn_flow_ ## stepnum ## _val, \ - NULL, \ - NULL, \ - } - -#define ALGO_UX_STEP(stepnum, layoutkind, preinit, timeout_ms, validate_cb, error_flow, ...) \ - UX_FLOW_CALL(txn_flow_ ## stepnum ## _validate, { validate_cb; }) \ - void txn_flow_ ## stepnum ##_init (unsigned int stack_slot) { \ - preinit; \ - ux_last_step = stepnum; \ - ux_layout_ ## layoutkind ## _init(stack_slot); \ - ux_layout_set_timeout(stack_slot, timeout_ms);\ - } \ - const ux_layout_ ## layoutkind ## _params_t txn_flow_ ## stepnum ##_val = __VA_ARGS__; \ - const ux_flow_step_t txn_flow_ ## stepnum = { \ - txn_flow_ ## stepnum ## _init, \ - & txn_flow_ ## stepnum ## _val, \ - txn_flow_ ## stepnum ## _validate, \ - error_flow, \ - } - -#endif // TARGET_NANOX +#define ALGORAND_PUBLIC_KEY_SIZE 32 diff --git a/src/glyphs.c b/src/glyphs.c deleted file mode 100644 index 554a56c8..00000000 --- a/src/glyphs.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "glyphs.h" -unsigned int const C_icon_crossmark_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_crossmark_bitmap[] = { - 0x00, 0x80, 0x01, 0xe6, 0xc0, 0x71, 0x38, 0x38, 0x07, 0xfc, 0x00, 0x1e, 0x80, 0x07, 0xf0, 0x03, - 0xce, 0xc1, 0xe1, 0x38, 0x70, 0x06, 0x18, 0x00, 0x00, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_crossmark = { GLYPH_icon_crossmark_WIDTH, GLYPH_icon_crossmark_HEIGHT, 1, C_icon_crossmark_colors, C_icon_crossmark_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_dashboard_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_dashboard_bitmap[] = { - 0xe0, 0x01, 0xfe, 0xc1, 0xff, 0x38, 0x70, 0x06, 0xd8, 0x79, 0x7e, 0x9e, 0x9f, 0xe7, 0xe7, 0xb9, - 0x01, 0xe6, 0xc0, 0xf1, 0x3f, 0xf8, 0x07, 0x78, 0x00, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_dashboard = { GLYPH_icon_dashboard_WIDTH, GLYPH_icon_dashboard_HEIGHT, 1, C_icon_dashboard_colors, C_icon_dashboard_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_dashboard_x_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_dashboard_x_bitmap[] = { - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, 0xf0, 0x03, 0xfe, 0xc1, 0xff, 0xf0, 0x3f, 0xf0, 0x03, - 0xcc, 0x00, 0x33, 0xc0, 0x0c, 0x00, 0x00, 0x00, 0x00, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_dashboard_x = { GLYPH_icon_dashboard_x_WIDTH, GLYPH_icon_dashboard_x_HEIGHT, 1, C_icon_dashboard_x_colors, C_icon_dashboard_x_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_down_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_down_bitmap[] = { - 0x41, 0x11, 0x05, 0x01, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_down = { GLYPH_icon_down_WIDTH, GLYPH_icon_down_HEIGHT, 1, C_icon_down_colors, C_icon_down_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_left_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_left_bitmap[] = { - 0x48, 0x12, 0x42, 0x08, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_left = { GLYPH_icon_left_WIDTH, GLYPH_icon_left_HEIGHT, 1, C_icon_left_colors, C_icon_left_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_right_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_right_bitmap[] = { - 0x21, 0x84, 0x24, 0x01, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_right = { GLYPH_icon_right_WIDTH, GLYPH_icon_right_HEIGHT, 1, C_icon_right_colors, C_icon_right_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_up_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_up_bitmap[] = { - 0x08, 0x8a, 0x28, 0x08, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_up = { GLYPH_icon_up_WIDTH, GLYPH_icon_up_HEIGHT, 1, C_icon_up_colors, C_icon_up_bitmap }; - #endif // OS_IO_SEPROXYHAL -#include "glyphs.h" -unsigned int const C_icon_validate_14_colors[] = { - 0x00000000, - 0x00ffffff, -}; - -unsigned char const C_icon_validate_14_bitmap[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x67, 0xe0, 0x38, 0x1c, 0x9c, 0x03, - 0x7e, 0x00, 0x0f, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, -}; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - const bagl_icon_details_t C_icon_validate_14 = { GLYPH_icon_validate_14_WIDTH, GLYPH_icon_validate_14_HEIGHT, 1, C_icon_validate_14_colors, C_icon_validate_14_bitmap }; - #endif // OS_IO_SEPROXYHAL diff --git a/src/glyphs.h b/src/glyphs.h deleted file mode 100644 index 8d57a5cc..00000000 --- a/src/glyphs.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef GLYPH_icon_crossmark_BPP - #define GLYPH_icon_crossmark_WIDTH 14 - #define GLYPH_icon_crossmark_HEIGHT 14 - #define GLYPH_icon_crossmark_BPP 1 -extern unsigned int const C_icon_crossmark_colors[]; -extern unsigned char const C_icon_crossmark_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_crossmark; - #endif // GLYPH_icon_crossmark_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_dashboard_BPP - #define GLYPH_icon_dashboard_WIDTH 14 - #define GLYPH_icon_dashboard_HEIGHT 14 - #define GLYPH_icon_dashboard_BPP 1 -extern unsigned int const C_icon_dashboard_colors[]; -extern unsigned char const C_icon_dashboard_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_dashboard; - #endif // GLYPH_icon_dashboard_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_dashboard_x_BPP - #define GLYPH_icon_dashboard_x_WIDTH 14 - #define GLYPH_icon_dashboard_x_HEIGHT 14 - #define GLYPH_icon_dashboard_x_BPP 1 -extern unsigned int const C_icon_dashboard_x_colors[]; -extern unsigned char const C_icon_dashboard_x_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_dashboard_x; - #endif // GLYPH_icon_dashboard_x_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_down_BPP - #define GLYPH_icon_down_WIDTH 7 - #define GLYPH_icon_down_HEIGHT 4 - #define GLYPH_icon_down_BPP 1 -extern unsigned int const C_icon_down_colors[]; -extern unsigned char const C_icon_down_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_down; - #endif // GLYPH_icon_down_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_left_BPP - #define GLYPH_icon_left_WIDTH 4 - #define GLYPH_icon_left_HEIGHT 7 - #define GLYPH_icon_left_BPP 1 -extern unsigned int const C_icon_left_colors[]; -extern unsigned char const C_icon_left_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_left; - #endif // GLYPH_icon_left_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_right_BPP - #define GLYPH_icon_right_WIDTH 4 - #define GLYPH_icon_right_HEIGHT 7 - #define GLYPH_icon_right_BPP 1 -extern unsigned int const C_icon_right_colors[]; -extern unsigned char const C_icon_right_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_right; - #endif // GLYPH_icon_right_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_up_BPP - #define GLYPH_icon_up_WIDTH 7 - #define GLYPH_icon_up_HEIGHT 4 - #define GLYPH_icon_up_BPP 1 -extern unsigned int const C_icon_up_colors[]; -extern unsigned char const C_icon_up_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_up; - #endif // GLYPH_icon_up_BPP - #endif // OS_IO_SEPROXYHAL -#ifndef GLYPH_icon_validate_14_BPP - #define GLYPH_icon_validate_14_WIDTH 14 - #define GLYPH_icon_validate_14_HEIGHT 14 - #define GLYPH_icon_validate_14_BPP 1 -extern unsigned int const C_icon_validate_14_colors[]; -extern unsigned char const C_icon_validate_14_bitmap[]; -#ifdef OS_IO_SEPROXYHAL - #include "os_io_seproxyhal.h" - extern const bagl_icon_details_t C_icon_validate_14; - #endif // GLYPH_icon_validate_14_BPP - #endif // OS_IO_SEPROXYHAL diff --git a/src/main.c b/src/main.c index baeca83a..8729ef4a 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "algo_keys.h" #include "algo_ui.h" +#include "algo_addr.h" #include "algo_tx.h" unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; @@ -20,6 +21,7 @@ unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; #define P1_FIRST 0x00 #define P1_MORE 0x80 #define P1_WITH_ACCOUNT_ID 0x01 +#define P1_WITH_REQUEST_USER_APPROVAL 0x80 #define P2_LAST 0x00 #define P2_MORE 0x80 @@ -33,7 +35,8 @@ unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; #define INS_SIGN_MSGPACK 0x08 /* The transaction that we might ask the user to approve. */ -struct txn current_txn; +txn_t current_txn; +already_computed_key_t current_pubkey; /* A buffer for collecting msgpack-encoded transaction via APDUs, * as well as for msgpack-encoding transaction prior to signing. @@ -84,8 +87,22 @@ txn_approve() ui_idle(); } +void address_approve() +{ + unsigned int tx = ALGORAND_PUBLIC_KEY_SIZE; + + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + + // Display back the original UX + ui_idle(); +} + void -txn_deny() +user_approval_denied() { G_io_apdu_buffer[0] = 0x69; G_io_apdu_buffer[1] = 0x85; @@ -104,6 +121,12 @@ copy_and_advance(void *dst, uint8_t **p, size_t len) *p += len; } +void init_globals(){ + memset(¤t_txn, 0, sizeof(current_txn)); + memset(¤t_pubkey, 0, sizeof(current_pubkey)); + fetch_public_key(0, text); +} + static void algorand_main(void) { @@ -113,11 +136,6 @@ algorand_main(void) msgpack_next_off = 0; -#if defined(TARGET_NANOS) - // next timer callback in 500 ms - UX_CALLBACK_SET_INTERVAL(500); -#endif - // DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only // goal is to retrieve APDU. // When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make @@ -135,6 +153,8 @@ algorand_main(void) rx = io_exchange(CHANNEL_APDU | flags, rx); flags = 0; + PRINTF("New APDU received:\n%.*H\n", rx, G_io_apdu_buffer); + // no apdu received, well, reset the session, and reset the // bootloader configuration if (rx == 0) { @@ -256,8 +276,9 @@ algorand_main(void) } break; case INS_GET_PUBLIC_KEY: { - cx_ecfp_private_key_t privateKey; - uint32_t accountId = 0; + uint32_t accountId = 0; + char checksummed[65]; + uint8_t user_approval_required = G_io_apdu_buffer[OFFSET_P1] == P1_WITH_REQUEST_USER_APPROVAL; if (rx > OFFSET_LC) { uint8_t lc = G_io_apdu_buffer[OFFSET_LC]; @@ -272,9 +293,18 @@ algorand_main(void) * Push derived key to `G_io_apdu_buffer` * and return pushed buffer length. */ - algorand_key_derive(accountId, &privateKey); - tx = algorand_public_key(&privateKey, G_io_apdu_buffer); - THROW(0x9000); + fetch_public_key(accountId, G_io_apdu_buffer); + + if(user_approval_required){ + checksummed_addr(G_io_apdu_buffer, checksummed); + ui_text_put(checksummed); + ui_address_approval(); + flags |= IO_ASYNCH_REPLY; + } + else{ + tx = ALGORAND_PUBLIC_KEY_SIZE; + THROW(0x9000); + } } break; case 0xFF: // return to dashboard @@ -350,12 +380,6 @@ unsigned char io_event(unsigned char channel) { case SEPROXYHAL_TAG_TICKER_EVENT: UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { -#if defined(TARGET_NANOS) - // defaulty retrig very soon (will be overriden during - // stepper_prepro) - UX_CALLBACK_SET_INTERVAL(500); - UX_REDISPLAY(); -#endif }); break; } @@ -422,10 +446,6 @@ main(void) for (;;) { UX_INIT(); -#if defined(TARGET_NANOS) - UX_MENU_INIT(); -#endif - BEGIN_TRY { TRY { io_seproxyhal_init(); @@ -437,10 +457,6 @@ main(void) USB_power(0); USB_power(1); - // Display a loading screen before (slowly) deriving keys. BLE_power - // also seems to be a bit slow, so show the loading screen here. - ui_loading(); - #if defined(TARGET_NANOX) BLE_power(0, NULL); BLE_power(1, "Nano X"); @@ -448,6 +464,8 @@ main(void) ui_idle(); + init_globals(); + algorand_main(); } CATCH(EXCEPTION_IO_RESET) { diff --git a/src/ui_address.c b/src/ui_address.c index 4c1166cb..cef9291a 100644 --- a/src/ui_address.c +++ b/src/ui_address.c @@ -2,74 +2,55 @@ #include "os_io_seproxyhal.h" #include "algo_ui.h" +#include "algo_tx.h" #include "algo_keys.h" #include "algo_addr.h" -#if defined(TARGET_NANOS) -static const bagl_element_t -bagl_ui_address_nanos[] = { - { {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, - 0, 0}, - NULL, - 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Public address", - 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_LABELINE, 0x02, 23, 26, 82, 11, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - lineBuffer, - 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_CROSS}, - NULL, - 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_RIGHT}, - NULL, - 0, 0, 0, NULL, NULL, NULL, }, -}; -static unsigned int -bagl_ui_address_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) -{ - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - if (ui_text_more()) { - UX_REDISPLAY(); - } else { - ui_idle(); - } - break; +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 = text, + }); +UX_FLOW_DEF_VALID( + ux_display_public_flow_3_step, + pbb, + address_approve(), + { + &C_icon_validate_14, + "Approve", + "address", + }); +UX_FLOW_DEF_VALID( + ux_display_public_flow_4_step, + pb, + user_approval_denied(), + { + &C_icon_crossmark, + "Reject", + }); - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - ui_idle(); - break; - } - return 0; -} +UX_FLOW(ux_address_approval_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 +); -void -ui_address() +void ui_address_approval() { - step_address(); - if (ui_text_more()) { - UX_DISPLAY(bagl_ui_address_nanos, NULL); - } else { - ui_idle(); + if (G_ux.stack_count == 0) { + ux_stack_push(); } -} -#endif - -void -step_address() -{ - char checksummed[65]; - uint8_t publicKey[32]; - cx_ecfp_private_key_t privateKey; - - algorand_key_derive(0, &privateKey); - algorand_public_key(&privateKey, publicKey); - checksummed_addr(publicKey, checksummed); - ui_text_put(checksummed); + ux_flow_init(0, ux_address_approval_flow, NULL); } diff --git a/src/ui_idle.c b/src/ui_idle.c index dd56f522..d56eb2eb 100644 --- a/src/ui_idle.c +++ b/src/ui_idle.c @@ -2,75 +2,43 @@ #include "os_io_seproxyhal.h" #include "algo_ui.h" -#if defined(TARGET_NANOS) -static const ux_menu_entry_t menu_top[]; -static const ux_menu_entry_t menu_about[]; +UX_FLOW_DEF_NOCB( + ux_idle_flow_welcome_step, + nn, //pnn, + { + //"", //&C_icon_dashboard, + "Application", + "is ready", + }); +UX_FLOW_DEF_NOCB( + ux_idle_flow_version_step, + bn, + { + "Version", + APPVERSION, + }); +UX_FLOW_DEF_VALID( + ux_idle_flow_exit_step, + pb, + os_sched_exit(-1), + { + &C_icon_dashboard_x, + "Quit", + }); +UX_FLOW(ux_idle_flow, + &ux_idle_flow_welcome_step, + &ux_idle_flow_version_step, + &ux_idle_flow_exit_step, + FLOW_LOOP +); -static const ux_menu_entry_t menu_about[] = { - {NULL, NULL, 0, NULL, "Version", APPVERSION, 0, 0}, - {menu_top, NULL, 1, NULL, "Back", NULL, 0, 0}, - UX_MENU_END -}; - -static const ux_menu_entry_t menu_top[] = { - {NULL, ui_address, 0, NULL, "Address", NULL, 0, 0}, - {menu_about, NULL, 0, NULL, "About", NULL, 0, 0}, - {NULL, os_sched_exit, 0, NULL, "Quit app", NULL, 0, 0}, - UX_MENU_END -}; - -static const ux_menu_entry_t menu_loading[] = { - {NULL, NULL, 0, NULL, "Loading...", "Please wait", 0, 0}, - UX_MENU_END -}; -#endif - -#if defined(TARGET_NANOX) -UX_STEP_NOCB(ux_idle_flow_1_step, bn, {"Version", APPVERSION}); -UX_STEP_NOCB_INIT(ux_idle_flow_2_step, bnnn_paging, step_address(), {"Address", text}); -UX_STEP_VALID(ux_idle_flow_3_step, pb, os_sched_exit(-1), {&C_icon_dashboard, "Quit"}); - -const ux_flow_step_t * const ux_idle_flow [] = { - &ux_idle_flow_1_step, - &ux_idle_flow_2_step, - &ux_idle_flow_3_step, - FLOW_END_STEP, -}; - -UX_STEP_NOCB(ux_loading_1_step, bn, {"Loading...", "Please wait"}); - -const ux_flow_step_t * const ux_loading_flow [] = { - &ux_loading_1_step, - FLOW_END_STEP, -}; -#endif - -void -ui_loading() -{ -#if defined(TARGET_NANOX) - // reserve a display stack slot if none yet - if(G_ux.stack_count == 0) { - ux_stack_push(); - } - ux_flow_init(0, ux_loading_flow, NULL); -#endif -#if defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_loading, NULL); -#endif -} void ui_idle() { -#if defined(TARGET_NANOX) // reserve a display stack slot if none yet if(G_ux.stack_count == 0) { ux_stack_push(); } ux_flow_init(0, ux_idle_flow, NULL); -#endif -#if defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_top, NULL); -#endif } diff --git a/src/ui_text.c b/src/ui_text.c index 9d3391d5..7508cf1c 100644 --- a/src/ui_text.c +++ b/src/ui_text.c @@ -12,39 +12,6 @@ static int lineBufferPos; // 1 extra byte for the null termination char lineBuffer[MAX_CHARS_PER_LINE+2+1]; -int -ui_text_more() -{ - int linePos; - - if (text[lineBufferPos] == '\0') { - lineBuffer[0] = '\0'; - return 0; - } - - for (linePos = 0; linePos < MAX_CHARS_PER_LINE; linePos++) { - if (text[lineBufferPos] == '\0') { - break; - } - - if (text[lineBufferPos] == '\n') { - lineBufferPos++; - break; - } - - lineBuffer[linePos] = text[lineBufferPos]; - lineBufferPos++; - } - - if (text[lineBufferPos] != '\0') { - lineBuffer[linePos++] = '.'; - lineBuffer[linePos++] = '.'; - } - - lineBuffer[linePos++] = '\0'; - return 1; -} - void ui_text_putn(const char *msg, size_t maxlen) { @@ -60,8 +27,6 @@ ui_text_putn(const char *msg, size_t maxlen) lineBufferPos = 0; PRINTF("ui_text_putn: text %s\n", &text[0]); - - /* Caller should invoke ui_text_more() after ui_text_putn(). */ } void diff --git a/src/ui_txn.c b/src/ui_txn.c index 4b5271a2..326561c7 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -9,6 +9,8 @@ #include "base64.h" #include "glyphs.h" +char caption[20]; + static char * u64str(uint64_t v) { @@ -72,9 +74,7 @@ static int step_txn_type() { static int step_sender() { uint8_t publicKey[32]; - cx_ecfp_private_key_t privateKey; - algorand_key_derive(current_txn.accountId, &privateKey); - algorand_public_key(&privateKey, publicKey); + fetch_public_key(current_txn.accountId, publicKey); if (os_memcmp(publicKey, current_txn.sender, sizeof(current_txn.sender)) == 0) { return 0; } @@ -101,15 +101,15 @@ static int step_fee() { return 1; } -static int step_firstvalid() { - ui_text_put(u64str(current_txn.firstValid)); - return 1; -} +// static int step_firstvalid() { +// ui_text_put(u64str(current_txn.firstValid)); +// return 1; +// } -static int step_lastvalid() { - ui_text_put(u64str(current_txn.lastValid)); - return 1; -} +// static int step_lastvalid() { +// ui_text_put(u64str(current_txn.lastValid)); +// return 1; +// } static const char* default_genesisID = "mainnet-v1.0"; static const uint8_t default_genesisHash[] = { @@ -392,258 +392,195 @@ static int step_asset_config_clawback() { return step_asset_config_addr_helper(current_txn.asset_config.params.clawback); } -#if defined(TARGET_NANOX) -static unsigned int ux_last_step; - -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 0, bn, step_txn_type(), {"Txn type", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 1, bnnn_paging, step_sender(), {"Sender", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 2, bnnn_paging, step_rekey(), {"RekeyTo", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 3, bn, step_fee(), {"Fee (uAlg)", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 4, bn, step_firstvalid(), {"First valid", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 5, bn, step_lastvalid(), {"Last valid", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 6, bn, step_genesisID(), {"Genesis ID", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 7, bnnn_paging, step_genesisHash(), {"Genesis hash", text}); -ALGO_UX_STEP_NOCB_INIT(ALL_TYPES, 8, bn, step_note(), {"Note", text}); - -ALGO_UX_STEP_NOCB_INIT(PAYMENT, 9, bnnn_paging, step_receiver(), {"Receiver", text}); -ALGO_UX_STEP_NOCB_INIT(PAYMENT, 10, bn, step_amount(), {"Amount (uAlg)", text}); -ALGO_UX_STEP_NOCB_INIT(PAYMENT, 11, bnnn_paging, step_close(), {"Close to", text}); - -ALGO_UX_STEP_NOCB_INIT(KEYREG, 12, bnnn_paging, step_votepk(), {"Vote PK", text}); -ALGO_UX_STEP_NOCB_INIT(KEYREG, 13, bnnn_paging, step_vrfpk(), {"VRF PK", text}); -ALGO_UX_STEP_NOCB_INIT(KEYREG, 14, bn, step_votefirst(), {"Vote first", text}); -ALGO_UX_STEP_NOCB_INIT(KEYREG, 15, bn, step_votelast(), {"Vote last", text}); -ALGO_UX_STEP_NOCB_INIT(KEYREG, 16, bn, step_keydilution(), {"Key dilution", text}); -ALGO_UX_STEP_NOCB_INIT(KEYREG, 17, bn, step_participating(), {"Participating", text}); - -ALGO_UX_STEP_NOCB_INIT(ASSET_XFER, 18, bn, step_asset_xfer_id(), {"Asset ID", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_XFER, 19, bn, step_asset_xfer_amount(), {"Asset amt", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_XFER, 20, bnnn_paging, step_asset_xfer_sender(), {"Asset src", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_XFER, 21, bnnn_paging, step_asset_xfer_receiver(), {"Asset dst", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_XFER, 22, bnnn_paging, step_asset_xfer_close(), {"Asset close", text}); - -ALGO_UX_STEP_NOCB_INIT(ASSET_FREEZE, 23, bn, step_asset_freeze_id(), {"Asset ID", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_FREEZE, 24, bnnn_paging, step_asset_freeze_account(), {"Asset account", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_FREEZE, 25, bn, step_asset_freeze_flag(), {"Freeze flag", text}); - -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 26, bn, step_asset_config_id(), {"Asset ID", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 27, bn, step_asset_config_total(), {"Total units", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 28, bn, step_asset_config_default_frozen(), {"Default frozen", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 29, bnnn_paging, step_asset_config_unitname(), {"Unit name", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 30, bn, step_asset_config_decimals(), {"Decimals", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 31, bnnn_paging, step_asset_config_assetname(), {"Asset name", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 32, bnnn_paging, step_asset_config_url(), {"URL", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 33, bnnn_paging, step_asset_config_metadata_hash(), {"Metadata hash", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 34, bnnn_paging, step_asset_config_manager(), {"Manager", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 35, bnnn_paging, step_asset_config_reserve(), {"Reserve", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 36, bnnn_paging, step_asset_config_freeze(), {"Freezer", text}); -ALGO_UX_STEP_NOCB_INIT(ASSET_CONFIG, 37, bnnn_paging, step_asset_config_clawback(), {"Clawback", text}); - -ALGO_UX_STEP(38, pbb, NULL, 0, txn_approve(), NULL, {&C_icon_validate_14, "Sign", "transaction"}); -ALGO_UX_STEP(39, pbb, NULL, 0, txn_deny(), NULL, {&C_icon_crossmark, "Cancel", "signature"}); - -const ux_flow_step_t * const ux_txn_flow [] = { - &txn_flow_0, - &txn_flow_1, - &txn_flow_2, - &txn_flow_3, - &txn_flow_4, - &txn_flow_5, - &txn_flow_6, - &txn_flow_7, - &txn_flow_8, - &txn_flow_9, - &txn_flow_10, - &txn_flow_11, - &txn_flow_12, - &txn_flow_13, - &txn_flow_14, - &txn_flow_15, - &txn_flow_16, - &txn_flow_17, - &txn_flow_18, - &txn_flow_19, - &txn_flow_20, - &txn_flow_21, - &txn_flow_22, - &txn_flow_23, - &txn_flow_24, - &txn_flow_25, - &txn_flow_26, - &txn_flow_27, - &txn_flow_28, - &txn_flow_29, - &txn_flow_30, - &txn_flow_31, - &txn_flow_32, - &txn_flow_33, - &txn_flow_34, - &txn_flow_35, - &txn_flow_36, - &txn_flow_37, - &txn_flow_38, - &txn_flow_39, - FLOW_END_STEP, -}; -#endif // TARGET_NANOX - -#if defined(TARGET_NANOS) -struct ux_step { - // The display callback returns a non-zero value if it placed information - // about the associated caption into lineBuffer, which should be displayed. - // If it returns 0, the approval flow moves on to the next step. The - // callback is invoked only if the transaction type matches txtype. - int txtype; - const char *caption; - int (*display)(void); -}; - -static unsigned int ux_current_step; -static const struct ux_step ux_steps[] = { - { ALL_TYPES, "Txn type", &step_txn_type }, - { ALL_TYPES, "Sender", &step_sender }, - { ALL_TYPES, "RekeyTo", &step_rekey }, - { ALL_TYPES, "Fee (uAlg)", &step_fee }, - { ALL_TYPES, "First valid", &step_firstvalid }, - { ALL_TYPES, "Last valid", &step_lastvalid }, - { ALL_TYPES, "Genesis ID", &step_genesisID }, - { ALL_TYPES, "Genesis hash", &step_genesisHash }, - { ALL_TYPES, "Note", &step_note }, - { PAYMENT, "Receiver", &step_receiver }, - { PAYMENT, "Amount (uAlg)", &step_amount }, - { PAYMENT, "Close to", &step_close }, - { KEYREG, "Vote PK", &step_votepk }, - { KEYREG, "VRF PK", &step_vrfpk }, - { KEYREG, "Vote first", &step_votefirst }, - { KEYREG, "Vote last", &step_votelast }, - { KEYREG, "Key dilution", &step_keydilution }, - { KEYREG, "Participating", &step_participating }, - { ASSET_XFER, "Asset ID", &step_asset_xfer_id }, - { ASSET_XFER, "Asset amt", &step_asset_xfer_amount }, - { ASSET_XFER, "Asset src", &step_asset_xfer_sender }, - { ASSET_XFER, "Asset dst", &step_asset_xfer_receiver }, - { ASSET_XFER, "Asset close", &step_asset_xfer_close }, - { ASSET_FREEZE, "Asset ID", &step_asset_freeze_id }, - { ASSET_FREEZE, "Asset account", &step_asset_freeze_account }, - { ASSET_FREEZE, "Freeze flag", &step_asset_freeze_flag }, - { ASSET_CONFIG, "Asset ID", &step_asset_config_id }, - { ASSET_CONFIG, "Total units", &step_asset_config_total }, - { ASSET_CONFIG, "Default frozen", &step_asset_config_default_frozen }, - { ASSET_CONFIG, "Unit name", &step_asset_config_unitname }, - { ASSET_CONFIG, "Decimals", &step_asset_config_decimals }, - { ASSET_CONFIG, "Asset name", &step_asset_config_assetname }, - { ASSET_CONFIG, "URL", &step_asset_config_url }, - { ASSET_CONFIG, "Metadata hash", &step_asset_config_metadata_hash }, - { ASSET_CONFIG, "Manager", &step_asset_config_manager }, - { ASSET_CONFIG, "Reserve", &step_asset_config_reserve }, - { ASSET_CONFIG, "Freezer", &step_asset_config_freeze }, - { ASSET_CONFIG, "Clawback", &step_asset_config_clawback }, +typedef int (*format_function_t)(); +typedef struct{ + char* caption; + format_function_t value_setter; + uint8_t type; +} screen_t; + +screen_t const screen_table[] = { + {"Txn type", &step_txn_type, ALL_TYPES}, + {"Sender", &step_sender, ALL_TYPES}, + {"RekeyTo", &step_rekey, ALL_TYPES}, + {"Fee (uAlg)", &step_fee, ALL_TYPES}, + // {"First valid", step_firstvalid, ALL_TYPES}, + // {"Last valid", step_lastvalid, ALL_TYPES}, + {"Genesis ID", &step_genesisID, ALL_TYPES}, + {"Genesis hash", &step_genesisHash, ALL_TYPES}, + {"Note", &step_note, ALL_TYPES}, + {"Receiver", &step_receiver, PAYMENT}, + {"Amount (uAlg)", step_amount, PAYMENT}, + {"Close to", &step_close, PAYMENT}, + {"Vote PK", &step_votepk, KEYREG}, + {"VRF PK", &step_vrfpk, KEYREG}, + {"Vote first", &step_votefirst, KEYREG}, + {"Vote last", &step_votelast, KEYREG}, + {"Key dilution", &step_keydilution, KEYREG}, + {"Participating", &step_participating, KEYREG}, + {"Asset ID", &step_asset_xfer_id, ASSET_XFER}, + {"Asset amt", &step_asset_xfer_amount, ASSET_XFER}, + {"Asset src", &step_asset_xfer_sender, ASSET_XFER}, + {"Asset dst", &step_asset_xfer_receiver, ASSET_XFER}, + {"Asset close", &step_asset_xfer_close, ASSET_XFER}, + {"Asset ID", &step_asset_freeze_id, ASSET_FREEZE}, + {"Asset account", &step_asset_freeze_account, ASSET_FREEZE}, + {"Freeze flag", &step_asset_freeze_flag, ASSET_FREEZE}, + {"Asset ID", &step_asset_config_id, ASSET_CONFIG}, + {"Total units", &step_asset_config_total, ASSET_CONFIG}, + {"Default frozen", &step_asset_config_default_frozen, ASSET_CONFIG}, + {"Unit name", &step_asset_config_unitname, ASSET_CONFIG}, + {"Decimals", &step_asset_config_decimals, ASSET_CONFIG}, + {"Asset name", &step_asset_config_assetname, ASSET_CONFIG}, + {"URL", &step_asset_config_url, ASSET_CONFIG}, + {"Metadata hash", &step_asset_config_metadata_hash, ASSET_CONFIG}, + {"Manager", &step_asset_config_manager, ASSET_CONFIG}, + {"Reserve", &step_asset_config_reserve, ASSET_CONFIG}, + {"Freezer", &step_asset_config_freeze, ASSET_CONFIG}, + {"Clawback", &step_asset_config_clawback, ASSET_CONFIG} }; -static const bagl_element_t bagl_ui_approval_nanos[] = { - { {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Sign transaction", 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, -}; - -static unsigned int -bagl_ui_approval_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) -{ - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - txn_approve(); - break; - - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - txn_deny(); +#define SCREEN_NUM (int8_t)(sizeof(screen_table)/sizeof(screen_t)) + +void display_next_state(bool is_upper_border); + +UX_STEP_NOCB( + ux_confirm_tx_init_flow_step, + pnn, + { + &C_icon_eye, + "Review", + "Transaction", + }); + +UX_STEP_INIT( + ux_init_upper_border, + NULL, + NULL, + { + display_next_state(true); + }); +UX_STEP_NOCB( + ux_variable_display, + bnnn_paging, + { + .title = caption, + .text = text, + }); +UX_STEP_INIT( + ux_init_lower_border, + NULL, + NULL, + { + display_next_state(false); + }); + +UX_FLOW_DEF_VALID( + ux_confirm_tx_finalize_step, + pnn, + txn_approve(), + { + &C_icon_validate_14, + "Sign", + "Transaction", + }); + +UX_FLOW_DEF_VALID( + ux_reject_tx_flow_step, + pnn, + user_approval_denied(), + { + &C_icon_crossmark, + "Cancel", + "Transaction" + }); + +UX_FLOW(ux_txn_flow, + &ux_confirm_tx_init_flow_step, + + &ux_init_upper_border, + &ux_variable_display, + &ux_init_lower_border, + + &ux_confirm_tx_finalize_step, + &ux_reject_tx_flow_step +); + +volatile int8_t current_data_index; + +bool set_state_data(bool forward){ + // Apply last formatter to fill the screen's buffer + do{ + current_data_index = forward ? current_data_index+1 : current_data_index-1; + if(screen_table[current_data_index].type == ALL_TYPES || + screen_table[current_data_index].type == current_txn.type){ + if(((format_function_t)PIC(screen_table[current_data_index].value_setter))() != 0){ break; } - return 0; } + } while(current_data_index >= 0 && + current_data_index < SCREEN_NUM); -static char captionBuffer[32]; + if(current_data_index < 0 || current_data_index >= SCREEN_NUM){ + return false; + } -static const bagl_element_t bagl_ui_step_nanos[] = { - { {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, - 0, 0}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, + strncpy(caption, (char*)PIC(screen_table[current_data_index].caption), sizeof(caption)); - /* Caption */ - { {BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - captionBuffer, 0, 0, 0, NULL, NULL, NULL, }, + PRINTF("caption: %s\n", caption); + PRINTF("details: %s\n\n", text); + return true; +} - /* Value */ - { {BAGL_LABELINE, 0x02, 23, 26, 82, 11, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - lineBuffer, 0, 0, 0, NULL, NULL, NULL, }, +volatile uint8_t current_state; - { {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, - 0, BAGL_GLYPH_ICON_CROSS}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, - { {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, - 0, BAGL_GLYPH_ICON_RIGHT}, - NULL, 0, 0, 0, NULL, NULL, NULL, }, -}; +#define INSIDE_BORDERS 0 +#define OUT_OF_BORDERS 1 -static void bagl_ui_step_nanos_display(); +void display_next_state(bool is_upper_border){ -static unsigned int -bagl_ui_step_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) -{ - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - if (ui_text_more()) { - UX_REDISPLAY(); - return 0; + if(is_upper_border){ + if(current_state == OUT_OF_BORDERS){ // -> from first screen + current_state = INSIDE_BORDERS; + set_state_data(true); + ux_flow_next(); } - - ux_current_step++; - bagl_ui_step_nanos_display(); - return 0; - - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - txn_deny(); - return 0; + else{ + if(set_state_data(false)){ // <- from middle, more screens available + ux_flow_next(); } - - return 0; + else{ // <- from middle, no more screens available + current_state = OUT_OF_BORDERS; + ux_flow_prev(); } - -static void -bagl_ui_step_nanos_display() + } + } + else // walking over the second border { - while (1) { - if (ux_current_step >= sizeof(ux_steps) / sizeof(ux_steps[0])) { - UX_DISPLAY(bagl_ui_approval_nanos, NULL); - return; + if(current_state == OUT_OF_BORDERS){ // <- from last screen + current_state = INSIDE_BORDERS; + set_state_data(false); + ux_flow_prev(); } - - int txtype = ux_steps[ux_current_step].txtype; - if (txtype == ALL_TYPES || txtype == current_txn.type) { - const char* step_caption = (const char*) PIC(ux_steps[ux_current_step].caption); - int (*step_display)(void) = (int (*)(void)) PIC(ux_steps[ux_current_step].display); - if (step_display()) { - snprintf(captionBuffer, sizeof(captionBuffer), "%s", step_caption); - ui_text_more(); - UX_DISPLAY(bagl_ui_step_nanos, NULL); - return; + else{ + if(set_state_data(true)){ // -> from middle, more screens available + /*dirty hack to have coherent behavior on bnnn_paging when there are multiple screens*/ + G_ux.flow_stack[G_ux.stack_count-1].prev_index = G_ux.flow_stack[G_ux.stack_count-1].index-2; + G_ux.flow_stack[G_ux.stack_count-1].index--; + ux_flow_relayout(); + /*end of dirty hack*/ } + else{ // -> from middle, no more screens available + current_state = OUT_OF_BORDERS; + ux_flow_next(); } - - ux_current_step++; } } -#endif // TARGET_NANOS -void -ui_txn() -{ +} + + +void ui_txn(void) { PRINTF("Transaction:\n"); PRINTF(" Type: %d\n", current_txn.type); PRINTF(" Sender: %.*h\n", 32, current_txn.sender); @@ -662,16 +599,10 @@ ui_txn() PRINTF(" VRF PK: %.*h\n", 32, current_txn.keyreg.vrfpk); } -#if defined(TARGET_NANOS) - ux_current_step = 0; - bagl_ui_step_nanos_display(); -#endif - -#if defined(TARGET_NANOX) - ux_last_step = 0; + current_data_index = -1; + current_state = OUT_OF_BORDERS; if (G_ux.stack_count == 0) { ux_stack_push(); } ux_flow_init(0, ux_txn_flow, NULL); -#endif } diff --git a/src/ux.c b/src/ux.c index 30cfbfe9..bd2aa107 100644 --- a/src/ux.c +++ b/src/ux.c @@ -1,12 +1,5 @@ #include "os.h" #include "os_io_seproxyhal.h" -#ifdef TARGET_NANOS -// This seems to be implicitly required by the SDK at link-time. -ux_state_t ux; -#endif - -#ifdef TARGET_NANOX ux_state_t G_ux; -bolos_ux_params_t G_ux_params; -#endif +bolos_ux_params_t G_ux_params; \ No newline at end of file diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..e6286eb0 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +*.pyc +.*.swp +.*.swo + diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..f66b386a --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,17 @@ +APP_ALGORAND_SRC=../ +APP_ALGORAND_CLI=$(APP_ALGORAND_SRC)/cli/ +APP_ALGORAND_BIN=$(APP_ALGORAND_SRC)/bin/ + +DEBUG=1 + + +.PHONY: test +test: $(APP_ALGORAND_BIN)/app.elf + PYTHONPATH=$(APP_ALGORAND_CLI) pytest --verbose --app $< test/ + +$(APP_ALGORAND_BIN)/app.elf: FORCE + $(MAKE) -C $(APP_ALGORAND_SRC) DEBUG=$(DEBUG) + + +.PHONY: FORCE +FORCE: diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..2e827ef0 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,130 @@ +# app-algorand-test + +## Overview + +Pytest-base test suite for the [app-algorand](https://github.com/LedgerHQ/app-algorand) Nano App. + +This test suite is mainly based on: + - `pytest` framework. + - Ě€speculos` Docker container. + - `ledgerblue` tool. + - Docker Python SDK. + - Algorand Python SDK. + + +## Install + +This test suite requires [https://docs.docker.com/engine/install/ubuntu/](Docker) engine +and assumes the [https://hub.docker.com/r/ledgerhq/speculos](`speculos`) image being pulled. +Python environment may be installed within a virtualenv: + + ``` + docker pull speculos + virtualenv env + env/bin/activate + pip install -r requirements.txt + ``` + +## Run tests + + ``` + make test + ``` + +## APDU Format for Multi-Account Support + +The format of the APDUs in app release that implements multi-account support has been kept backward compatible with +previous message format (1.0.7). Two messages have been modified to implement multi-account support. + +### `INS_GET_PUBLIC_KEY`: + +Original format of this instruction is fixed with no payload. +
+    ------------------------------------
+    | CLA  | INS  |  P1  |  P2  |  LC  |
+    ------------------------------------
+    | 0x80 | 0x03 | 0x00 | 0x00 | 0x00 |
+    ------------------------------------
+
+ +New format enhances this APDU with a 4-byte payload that encodes an account number (big endian 32-bit unsigned integer). +This payload is optional so that former format may still be used. So user may send: +
+    ------------------------------------
+    | CLA  | INS  |  P1  |  P2  |  LC  |
+    ------------------------------------
+    | 0x80 | 0x03 | 0x00 | 0x00 | 0x00 |
+    ------------------------------------
+
+or +
+    ----------------------------------------------------------------
+    | CLA  | INS  |  P1  |  P2  |  LC  |    PAYLOAD (4 bytes)      |
+    ----------------------------------------------------------------
+    | 0x80 | 0x03 | 0x00 | 0x00 | 0x04 |        {account}          |
+    ----------------------------------------------------------------
+
+ +The account number is used to derive keys from BIP32 path `44'/283'/'/0/0` +(note that the account number is hardened as shown by the `'` sign). Account number defaults +to `0x0` in the case of APDU with empty payload. + + +### `INS_SIGN_MSGPACK` + +Original format is as shown below where transaction contents may be split in multiple APDUs: +
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (N1 bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x00 | 0x80 |  N1  | {MessagePack Chunk#1}
+    ------------------------------------------------------------------------ - - -
+    ...
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (Ni bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x80 | 0x80 |  Ni   | {MessagePack Chunk#i}
+    ------------------------------------------------------------------------ - - -
+    ...
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (NI bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x80 | 0x00 |  NI  | {MessagePack Chunk#I}
+    ------------------------------------------------------------------------ - - -
+
+If one single APDU may contain a whole transaction, `P1` and `P2` are both `0x00`. + +New format enhances messaging with an optional account number that must be inserted +in the first chunk of the sequence. As an optional payload, bit `0` of field `P1` in +the first chunk must be set if present in the message. + +And as for `INS_GET_PUBLIC_KEY` instruction, it is a big-endian encoded 32-bit +unsigned integer word. + +The resulting sequence of chunks is as follows: +
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (N1 bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x01 | 0x80 |  N1  | {account (4 bytes)} + {MessagePack Chunk#1 (N1 - 4 bytes)}
+    ------------------------------------------------------------------------ - - -
+    ...
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (Ni bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x80 | 0x80 |  Ni   | {MessagePack Chunk#i}
+    ------------------------------------------------------------------------ - - -
+    ...
+    ------------------------------------------------------------------------ - - -
+    | CLA  | INS  |  P1  |  P2  |  LC  |  PAYLOAD (NI bytes)
+    ------------------------------------------------------------------------ - - -
+    | 0x80 | 0x03 | 0x80 | 0x00 |  NI  | {MessagePack Chunk#I}
+    ------------------------------------------------------------------------ - - -
+
+If one signle APDU is needed for the whole transaction along with the account number, +`P1` and `P2` are `0x01` and `0x00` respectively. + +If the account number is not inserted within the message, the former message format is used +(`P1` in the first chunk is `0x00`) and the account number defaults to `0x00` for the transaction +signature. + diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 00000000..513fa78f --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +log_cli = True +log_cli_format = %(asctime)s %(levelname)s %(message)s +log_cli_date_format = %Y-%m-%d %H:%M:%S +log_cli_level = DEBUG diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..b8552c46 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,59 @@ +attrs==19.3.0 +backports.shutil-which==3.5.2 +certifi==2020.4.5.1 +cffi==1.14.0 +chardet==3.0.4 +click==7.1.2 +ConfigArgParse==1.2.3 +construct==2.10.56 +cryptography==2.9.2 +docker==4.2.1 +docutils==0.16 +ecdsa==0.15 +ECPy==0.10.0 +ed25519==1.5 +future==0.18.2 +hidapi==0.9.0.post2 +idna==2.9 +importlib-metadata==1.6.0 +intelhex==2.2.1 +jsonschema==3.2.0 +ledger-agent==0.9.0 +ledgerblue==0.1.31 +ledgerwallet==0.1.2 +libagent==0.14.1 +lockfile==0.12.2 +mnemonic==0.19 +more-itertools==8.4.0 +msgpack==1.0.0 +packaging==20.4 +pbkdf2==1.3 +Pillow==7.1.2 +pluggy==0.13.1 +protobuf==3.12.2 +py==1.8.2 +py-algorand-sdk==1.3.0 +pycparser==2.20 +pycrypto==2.6.1 +pycryptodomex==3.9.7 +pyelftools==0.26 +PyMsgBox==1.0.8 +PyNaCl==1.4.0 +pyparsing==2.4.7 +PyQt5==5.14.2 +PyQt5-sip==12.7.2 +pyrsistent==0.16.0 +pytest==5.4.3 +python-daemon==2.2.4 +python-u2flib-host==3.0.3 +requests==2.23.0 +secp256k1==0.13.2 +semantic-version==2.8.5 +semver==2.10.2 +six==1.15.0 +tabulate==0.8.7 +Unidecode==1.1.1 +urllib3==1.25.9 +wcwidth==0.2.4 +websocket-client==0.57.0 +zipp==3.1.0 diff --git a/tests/test/__init__.py b/tests/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test/conftest.py b/tests/test/conftest.py new file mode 100644 index 00000000..fdcbcb2c --- /dev/null +++ b/tests/test/conftest.py @@ -0,0 +1,66 @@ +import pytest + +from .speculos import SpeculosContainer + +import base64 +import msgpack +from algosdk import transaction + +import algomsgpack + + +@pytest.fixture(scope='session') +def app(pytestconfig): + return pytestconfig.option.app + + +@pytest.fixture(scope='session') +def apdu_port(pytestconfig): + return pytestconfig.option.apdu_port + + +@pytest.fixture(scope='session') +def speculos(app, apdu_port): + speculos = SpeculosContainer(app=app, apdu_port=apdu_port) + speculos.start() + print("Started container") + yield speculos + print("Stopping container") + speculos.stop() + + +@pytest.fixture(scope='session') +def dongle(speculos, pytestconfig): + dongle = speculos.connect(debug=pytestconfig.option.verbose > 0) + print("Connected dongle") + yield dongle + print("Disconnecting dongle") + dongle.close() + + +def pytest_addoption(parser, pluginmanager): + parser.addoption("--app", dest="app") + parser.addoption("--apdu_port", dest="apdu_port", type=int, default=9999) + + + + +def genTxns(): + yield transaction.PaymentTxn( + sender="YK54TGVZ37C7P76GKLXTY2LAH2522VD3U2434HRKE7NMXA65VHJVLFVOE4", + receiver="RNZZNMS5L35EF6IQHH24ISSYQIKTUTWKGCB4Q5PBYYSTVB5EYDQRVYWMLE", + fee=0.001, + flat_fee=True, + amt=1000000, + first=5667360, + last=5668360, + note="Hello World".encode(), + gen="testnet-v1.0", + gh="SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" + ) + +def genTxnPayload(txns): + for txn in txns: + if isinstance(txn, Transaction): + txn = {"txn": txn.dictify()} + yield base64.b64decode(encoding.msgpack_encode(txn)) diff --git a/tests/test/dongle.py b/tests/test/dongle.py new file mode 100644 index 00000000..ba14036f --- /dev/null +++ b/tests/test/dongle.py @@ -0,0 +1,96 @@ +import time +import threading +import socket +import json +import logging +from contextlib import contextmanager + +import traceback + +import ledgerblue.commTCP +from ledgerblue.commException import CommException + + +logger = logging.getLogger('speculos') + + +class Dongle: + def __init__(self, apdu_port=9999, automation_port=None, button_port=None, + debug=False): + self.apdu_port = apdu_port + self.automation_port = automation_port + self.button_port = button_port + self.dongle = ledgerblue.commTCP.getDongle(server='127.0.0.1', + port=self.apdu_port, + debug=debug) + + def exchange(self, apdu, timeout=20000): + return bytes(self.dongle.exchange(apdu, timeout)) + + def close(self): + self.dongle.close() + + @contextmanager + def screen_event_handler(self, handler): + def do_handle_events(_handler, _fd): + buttons = Buttons(self.button_port) + try: + for line in _fd: + if callable(handler): + handler(json.loads(line.strip('\n')), buttons) + except ValueError: + pass + except Exception as e: + logger.error(e) + for l in traceback.extract_stack(): + logger.error(l) + finally: + buttons.close() + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('127.0.0.1', self.automation_port)) + fd = s.makefile() + logger.info('Connected to 127.0.0.1:%d' % self.automation_port) + + t = threading.Thread(target=do_handle_events, + args=(handler, fd), + daemon=True) + t.start() + yield self + fd.close() + t.join() + + logger.info('Closing connection to 127.0.0.1:%d' % self.automation_port) + s.close() + + +class Buttons: + LEFT = b'L' + RIGHT = b'R' + LEFT_RELEASE = b'l' + RIGHT_RELEASE = b'r' + + def __init__(self, button_port): + self.button_port = button_port + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect(('127.0.0.1', button_port)) + logger.info('Buttons: connected to port: %d' % self.button_port) + + def close(self): + logger.info('Buttons: closing connection to port: %d' % self.button_port) + self.s.close() + + def press(self, *args): + for action in args: + logger.info('Buttons: actions:%s' % action) + if type(action) == bytes: + self.s.send(action) + elif type(action) == str: + self.s.send(action.encode()) + elif type(action) == int or type(action) == float: + self.delay(seconds=action) + return self + + def delay(self, seconds=0.1): + time.sleep(seconds) + return self diff --git a/tests/test/speculos.py b/tests/test/speculos.py new file mode 100644 index 00000000..0688e6b5 --- /dev/null +++ b/tests/test/speculos.py @@ -0,0 +1,111 @@ +import os.path +import threading +import socket +import atexit +import logging + +import docker + +from . import dongle + + +CommException = dongle.CommException +logger = logging.getLogger('speculos') + + +class SpeculosContainer: + """ + `SpeculosContainer` handles running the Bolos App under test within + the `speculos` Docker` image. + + A `SpeculosContainer` instance is constructed with the Bolos App ELF + filename passed as `app` argument and with an optional tcp port passed + as `apdu_port` argument. + + The Docker container mounts the directory of the `app` within the + container on the `/app` mountpoint and exposes the `apdu_port` as tcp + port linked to the default Speculos APDU port (9999). + + The `start()` method starts running the container and starts a background + thread that reads and logs `stdout` and `stderr` output logs from the + container. Note that speculos is run in `headless` display mode. + + Besides the `connect()` method creates a `ledgerblue` tcp connection to + the `speculos` process through the `apdu_port` tcp port. + """ + + def __init__(self, app, apdu_port=9999, + automation_port=None, button_port=None): + self.app = app + self.apdu_port = apdu_port + self.automation_port = automation_port or (apdu_port + 1) + self.button_port = button_port or (apdu_port + 2) + self.docker = docker.from_env().containers + self.container = None + + def start(self): + self.container = self._run_speculos_container() + self.log_handler = self._log_speculos_output(self.container) + atexit.register(self.stop) + logger.info("Started docker container: %s (%s)" + % (self.container.image, self.container.name)) + + def stop(self): + logger.info("Stopping docker container: %s (%s)..." + % (self.container.image, self.container.name)) + self.container.stop() + self.log_handler.join() + + def connect(self, debug=False): + if self.container is None: + raise dongle.CommException("speculos not started yet") + return dongle.Dongle(self.apdu_port, + self.automation_port, + self.button_port, + debug=debug) + + def _run_speculos_container(self): + appdir = os.path.abspath(os.path.dirname(self.app)) + args = [ + '--display headless', + '--apdu-port 9999', + '--automation-port 10000', + '--button-port 10001', + '--log-level button:DEBUG', + '/app/%s' % os.path.basename(self.app) + ] + c = self.docker.create(image='ledgerhq/speculos', + command=' '.join(args), + volumes={appdir: {'bind': '/app', 'mode': 'ro'}}, + ports={ + '9999/tcp': self.apdu_port, + '10000/tcp': self.automation_port, + '10001/tcp': self.button_port, + }) + c.start() + return c + + + def _log_speculos_output(self, container): + # Synchronize on first log output from container + cv = threading.Condition() + started = False + + def do_log(): + for log in container.logs(stream=True, follow=True): + nonlocal started + if not started: + with cv: + started = True + cv.notify() + logger.info(log.decode('utf-8').strip('\n')) + + t = threading.Thread(target=do_log, daemon=True) + t.start() + with cv: + while not started: + cv.wait() + return t + + + diff --git a/tests/test/test_get_public_key.py b/tests/test/test_get_public_key.py new file mode 100644 index 00000000..99439003 --- /dev/null +++ b/tests/test/test_get_public_key.py @@ -0,0 +1,123 @@ +import pytest +import logging +import struct + +from . import speculos + + +def test_ins_with_no_payload(dongle): + """ + Test that sending `INS_GET_PUBLIC_KEY` (0x03) APDU without payload + returns a public key as a 32-byte long `bytes`. + """ + try: + apdu = struct.pack('>BBBBB', 0x80, 0x3, 0x0, 0x0, 0x0) + key = dongle.exchange(apdu) + assert type(key) == bytes + assert len(key) == 32 + except speculos.CommException as e: + logging.error(e) + assert False + + +def test_ins_with_4_bytes_payload(dongle): + """ + Test that sending `INS_GET_PUBLIC_KEY` (0x03) APDU with a + 4-byte (`uint32_t`) payload returns a public key as a + 32-byte long `bytes`. + """ + try: + apdu = struct.pack('>BBBBBI', 0x80, 0x3, 0x0, 0x0, 0x0, 0x0) + key = dongle.exchange(apdu) + assert len(key) == 32 + except speculos.CommException as e: + logging.error(e) + assert False + +labels = { + 'verify', 'address', 'approve' +} + +def getPubKey_ui_handler(event, buttons): + logging.warning(event) + label = sorted(event, key=lambda e: e['y'])[0]['text'].lower() + logging.warning('label => %s' % label) + if len(list(filter(lambda l: l in label, labels))) > 0: + if label == "approve": + buttons.press(buttons.RIGHT, buttons.LEFT, buttons.RIGHT_RELEASE, buttons.LEFT_RELEASE) + else: + buttons.press(buttons.RIGHT, buttons.RIGHT_RELEASE) + +def test_ins_with_4_bytes_payload_and_user_approval(dongle): + """ + Test that sending `INS_GET_PUBLIC_KEY` (0x03) APDU with a + 4-byte (`uint32_t`) payload returns a public key as a + 32-byte long `bytes`, after asking the user to approve the corresponding address + """ + try: + apdu = struct.pack('>BBBBBI', 0x80, 0x3, 0x80, 0x0, 0x0, 0x0) + + with dongle.screen_event_handler(getPubKey_ui_handler): + key = dongle.exchange(apdu) + + assert len(key) == 32 + except speculos.CommException as e: + logging.error(e) + assert False + + +@pytest.fixture(params=[1, 2, 3, 5, 8, 14]) +def invalid_size_apdu(request): + l = request.param + return struct.pack('>BBBBB%ds' % l, 0x80, 0x3, 0x0, 0x0, l, bytes(l)) + + +def test_ins_with_invalid_paylod_sizes(dongle, invalid_size_apdu): + """ + """ + with pytest.raises(speculos.CommException) as excinfo: + dongle.exchange(invalid_size_apdu) + assert excinfo.value.sw == 0x6a85 + + +def test_ins_without_payload_returns_account_0_key(dongle): + """ + """ + try: + apdu = struct.pack('>BBBBB', 0x80, 0x3, 0x0, 0x0, 0x0) + key1 = dongle.exchange(apdu) + assert type(key1) == bytes + assert len(key1) == 32 + apdu = struct.pack('>BBBBBI', 0x80, 0x3, 0x0, 0x0, 0x0, 0x0) + key2 = dongle.exchange(apdu) + assert type(key2) == bytes + assert len(key2) == 32 + + assert key1 == key2 + except speculos.CommException as e: + logging.error(e) + assert False + + +@pytest.fixture(params=[1, 2, 3, 5, 8, 14]) +def account_apdu(request): + account = request.param + return struct.pack('>BBBBBI', 0x80, 0x3, 0x0, 0x0, 0x4, account) + + +def test_ins_with_non_0_account_does_not_return_account_0_key(dongle, account_apdu): + """ + """ + try: + key1 = dongle.exchange(struct.pack('>BBBBB', 0x80, 0x3, 0x0, 0x0, 0x0)) + assert type(key1) == bytes + assert len(key1) == 32 + key2 = dongle.exchange(account_apdu) + assert type(key2) == bytes + assert len(key2) == 32 + + assert key1 != key2 + except speculos.CommException as e: + logging.error(e) + assert False + diff --git a/tests/test/test_sign_msgpack.py b/tests/test/test_sign_msgpack.py new file mode 100644 index 00000000..3b6c0c0f --- /dev/null +++ b/tests/test/test_sign_msgpack.py @@ -0,0 +1,120 @@ +import pytest +import logging +import struct +import base64 + +import msgpack +import nacl.signing + +import algosdk + +from . import speculos + + +labels = { + 'review', 'txn type', 'sender', 'fee', 'first valid', 'last valid', + 'genesis', 'note', 'receiver', 'amount', 'sign' +} + + +@pytest.fixture +def txn(): + txn = algosdk.transaction.PaymentTxn( + sender="YK54TGVZ37C7P76GKLXTY2LAH2522VD3U2434HRKE7NMXA65VHJVLFVOE4", + receiver="RNZZNMS5L35EF6IQHH24ISSYQIKTUTWKGCB4Q5PBYYSTVB5EYDQRVYWMLE", + fee=0.001, + flat_fee=True, + amt=1000000, + first=5667360, + last=5668360, + note="Hello World".encode(), + gen="testnet-v1.0", + gh="SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" + ) + # txn = {"txn": txn.dictify()} + return base64.b64decode(algosdk.encoding.msgpack_encode(txn)) + + +def test_sign_msgpack_with_default_account(dongle, txn): + """ + """ + apdu = struct.pack('>BBBBB', 0x80, 0x3, 0x0, 0x0, 0x0) + pubKey = dongle.exchange(apdu) + + with dongle.screen_event_handler(txn_ui_handler): + logging.info(txn) + txnSig = sign_algo_txn(dongle, txn) + + assert len(txnSig) == 64 + verify_key = nacl.signing.VerifyKey(pubKey) + verify_key.verify(smessage=b'TX' + txn, signature=txnSig) + + +@pytest.mark.parametrize('account_id', [0, 1, 3, 7, 10, 42, 12345]) +def test_sign_msgpack_with_valid_account_id(dongle, txn, account_id): + """ + """ + apdu = struct.pack('>BBBBBI', 0x80, 0x3, 0x0, 0x0, 0x4, account_id) + pubKey = dongle.exchange(apdu) + + with dongle.screen_event_handler(txn_ui_handler): + logging.info(txn) + txnSig = sign_algo_txn(dongle=dongle, + txn=struct.pack('>I', account_id) + txn, + p1=0x1) + + assert len(txnSig) == 64 + verify_key = nacl.signing.VerifyKey(pubKey) + verify_key.verify(smessage=b'TX' + txn, signature=txnSig) + + +def test_sign_msgpack_returns_same_signature(dongle, txn): + """ + """ + with dongle.screen_event_handler(txn_ui_handler): + defaultTxnSig = sign_algo_txn(dongle, txn) + + with dongle.screen_event_handler(txn_ui_handler): + txnSig = sign_algo_txn(dongle=dongle, + txn=struct.pack('>I', 0x0) + txn, + p1=0x1) + + assert txnSig == defaultTxnSig + + +def txn_ui_handler(event, buttons): + logging.warning(event) + label = sorted(event, key=lambda e: e['y'])[0]['text'].lower() + logging.warning('label => %s' % label) + if len(list(filter(lambda l: l in label, labels))) > 0: + if label == "sign": + buttons.press(buttons.RIGHT, buttons.LEFT, buttons.RIGHT_RELEASE, buttons.LEFT_RELEASE) + else: + buttons.press(buttons.RIGHT, buttons.RIGHT_RELEASE) + + +def chunks(txn, chunk_size=250, first_chunk_size=250): + size = first_chunk_size + last = False + while not last: + chunk = txn[:size] + txn = txn[len(chunk):] + last = not txn + size = chunk_size + yield chunk, last + + +def apdus(chunks, p1=0x00, p2=0x80): + for chunk, last in chunks: + if last: + p2 &= ~0x80 + size = len(chunk) + yield struct.pack('>BBBBB%ds' % size, 0x80, 0x08, p1, p2, size, chunk) + p1 |= 0x80 + + +def sign_algo_txn(dongle, txn, p1=0x00): + for apdu in apdus(chunks(txn), p1=p1 & 0x7f): + sig = dongle.exchange(apdu) + return sig + From 820f9366069b999b9276c585d4fa3a7cb310799b Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Thu, 16 Jul 2020 12:15:26 +0200 Subject: [PATCH 16/37] faster compile time for tests --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index f66b386a..815a1c67 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,7 +10,7 @@ test: $(APP_ALGORAND_BIN)/app.elf PYTHONPATH=$(APP_ALGORAND_CLI) pytest --verbose --app $< test/ $(APP_ALGORAND_BIN)/app.elf: FORCE - $(MAKE) -C $(APP_ALGORAND_SRC) DEBUG=$(DEBUG) + $(MAKE) -j -C $(APP_ALGORAND_SRC) DEBUG=$(DEBUG) .PHONY: FORCE From 7e453ae73049d99b0a9ccf6efc89c48db9c2bd71 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Tue, 28 Jul 2020 13:09:53 +0200 Subject: [PATCH 17/37] Amount are displayed with Alg unit instead of uAlg + bump version 1.2.1 --- Makefile | 2 +- src/algo_ui.h | 1 + src/ui_txn.c | 113 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 95 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 7201e572..19a4d52e 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.0 +APPVERSION = 1.2.1 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" diff --git a/src/algo_ui.h b/src/algo_ui.h index 2499a10f..f055818b 100644 --- a/src/algo_ui.h +++ b/src/algo_ui.h @@ -13,3 +13,4 @@ void ui_text_put(const char *msg); void ui_text_putn(const char *msg, size_t maxlen); #define ALGORAND_PUBLIC_KEY_SIZE 32 +#define ALGORAND_DECIMALS 6 \ No newline at end of file diff --git a/src/ui_txn.c b/src/ui_txn.c index 326561c7..1161e190 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -14,7 +14,7 @@ char caption[20]; static char * u64str(uint64_t v) { - static char buf[24]; + static char buf[27]; char *p = &buf[sizeof(buf)]; *(--p) = '\0'; @@ -32,6 +32,80 @@ u64str(uint64_t v) return p; } +bool adjustDecimals(char *src, uint32_t srcLength, char *target, + uint32_t targetLength, uint8_t decimals) { + uint32_t startOffset; + uint32_t lastZeroOffset = 0; + uint32_t offset = 0; + if ((srcLength == 1) && (*src == '0')) { + if (targetLength < 2) { + return false; + } + target[0] = '0'; + target[1] = '\0'; + return true; + } + if (srcLength <= decimals) { + uint32_t delta = decimals - srcLength; + if (targetLength < srcLength + 1 + 2 + delta) { + return false; + } + target[offset++] = '0'; + target[offset++] = '.'; + for (uint32_t i = 0; i < delta; i++) { + target[offset++] = '0'; + } + startOffset = offset; + for (uint32_t i = 0; i < srcLength; i++) { + target[offset++] = src[i]; + } + target[offset] = '\0'; + } else { + uint32_t sourceOffset = 0; + uint32_t delta = srcLength - decimals; + if (targetLength < srcLength + 1 + 1) { + return false; + } + while (offset < delta) { + target[offset++] = src[sourceOffset++]; + } + if (decimals != 0) { + target[offset++] = '.'; + } + startOffset = offset; + while (sourceOffset < srcLength) { + target[offset++] = src[sourceOffset++]; + } + target[offset] = '\0'; + } + for (uint32_t i = startOffset; i < offset; i++) { + if (target[i] == '0') { + if (lastZeroOffset == 0) { + lastZeroOffset = i; + } + } else { + lastZeroOffset = 0; + } + } + if (lastZeroOffset != 0) { + target[lastZeroOffset] = '\0'; + if (target[lastZeroOffset - 1] == '.') { + target[lastZeroOffset - 1] = '\0'; + } + } + return true; +} + +char* amount_to_str(uint64_t amount){ + char* result = u64str(amount); + char tmp[24]; + memcpy(tmp, result, sizeof(tmp)); + memset(result, 0, sizeof(tmp)); + adjustDecimals(tmp, strlen(tmp), result, 27, ALGORAND_DECIMALS); + result[26] = '\0'; + return result; +} + static int all_zero_key(uint8_t *buf) { @@ -97,7 +171,7 @@ static int step_rekey() { } static int step_fee() { - ui_text_put(u64str(current_txn.fee)); + ui_text_put(amount_to_str(current_txn.fee)); return 1; } @@ -166,7 +240,7 @@ static int step_receiver() { } static int step_amount() { - ui_text_put(u64str(current_txn.payment.amount)); + ui_text_put(amount_to_str(current_txn.payment.amount)); return 1; } @@ -225,7 +299,7 @@ static int step_asset_xfer_id() { } static int step_asset_xfer_amount() { - ui_text_put(u64str(current_txn.asset_xfer.amount)); + ui_text_put(amount_to_str(current_txn.asset_xfer.amount)); return 1; } @@ -402,15 +476,14 @@ typedef struct{ screen_t const screen_table[] = { {"Txn type", &step_txn_type, ALL_TYPES}, {"Sender", &step_sender, ALL_TYPES}, - {"RekeyTo", &step_rekey, ALL_TYPES}, - {"Fee (uAlg)", &step_fee, ALL_TYPES}, + {"Fee (Alg)", &step_fee, ALL_TYPES}, // {"First valid", step_firstvalid, ALL_TYPES}, // {"Last valid", step_lastvalid, ALL_TYPES}, {"Genesis ID", &step_genesisID, ALL_TYPES}, {"Genesis hash", &step_genesisHash, ALL_TYPES}, {"Note", &step_note, ALL_TYPES}, {"Receiver", &step_receiver, PAYMENT}, - {"Amount (uAlg)", step_amount, PAYMENT}, + {"Amount (Alg)", step_amount, PAYMENT}, {"Close to", &step_close, PAYMENT}, {"Vote PK", &step_votepk, KEYREG}, {"VRF PK", &step_vrfpk, KEYREG}, @@ -515,9 +588,9 @@ bool set_state_data(bool forward){ if(screen_table[current_data_index].type == ALL_TYPES || screen_table[current_data_index].type == current_txn.type){ if(((format_function_t)PIC(screen_table[current_data_index].value_setter))() != 0){ - break; - } -} + break; + } + } } while(current_data_index >= 0 && current_data_index < SCREEN_NUM); @@ -544,24 +617,24 @@ void display_next_state(bool is_upper_border){ current_state = INSIDE_BORDERS; set_state_data(true); ux_flow_next(); - } + } else{ if(set_state_data(false)){ // <- from middle, more screens available ux_flow_next(); - } + } else{ // <- from middle, no more screens available current_state = OUT_OF_BORDERS; ux_flow_prev(); -} + } } } else // walking over the second border -{ + { if(current_state == OUT_OF_BORDERS){ // <- from last screen current_state = INSIDE_BORDERS; set_state_data(false); ux_flow_prev(); - } + } else{ if(set_state_data(true)){ // -> from middle, more screens available /*dirty hack to have coherent behavior on bnnn_paging when there are multiple screens*/ @@ -569,13 +642,13 @@ void display_next_state(bool is_upper_border){ G_ux.flow_stack[G_ux.stack_count-1].index--; ux_flow_relayout(); /*end of dirty hack*/ - } + } else{ // -> from middle, no more screens available current_state = OUT_OF_BORDERS; ux_flow_next(); + } + } } - } -} } @@ -584,14 +657,14 @@ void ui_txn(void) { PRINTF("Transaction:\n"); PRINTF(" Type: %d\n", current_txn.type); PRINTF(" Sender: %.*h\n", 32, current_txn.sender); - PRINTF(" Fee: %s\n", u64str(current_txn.fee)); + PRINTF(" Fee: %s\n", amount_to_str(current_txn.fee)); PRINTF(" First valid: %s\n", u64str(current_txn.firstValid)); PRINTF(" Last valid: %s\n", u64str(current_txn.lastValid)); PRINTF(" Genesis ID: %.*s\n", 32, current_txn.genesisID); PRINTF(" Genesis hash: %.*h\n", 32, current_txn.genesisHash); if (current_txn.type == PAYMENT) { PRINTF(" Receiver: %.*h\n", 32, current_txn.payment.receiver); - PRINTF(" Amount: %s\n", u64str(current_txn.payment.amount)); + PRINTF(" Amount: %s\n", amount_to_str(current_txn.payment.amount)); PRINTF(" Close to: %.*h\n", 32, current_txn.payment.close); } if (current_txn.type == KEYREG) { From 9c81013b429a3a017234b38b326cef67bbc40685 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:36:57 +0200 Subject: [PATCH 18/37] Fix compilation warnings on Nano S --- Makefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 19a4d52e..046f0485 100755 --- a/Makefile +++ b/Makefile @@ -22,13 +22,14 @@ endif # Build configuration APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f +SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f lib_ux DEFINES += APPVERSION=\"$(APPVERSION)\" DEFINES += OS_IO_SEPROXYHAL DEFINES += HAVE_BAGL HAVE_SPRINTF DEFINES += HAVE_BOLOS_APP_STACK_CANARY +DEFINES += HAVE_UX_FLOW ifeq ($(TARGET_NAME),TARGET_NANOS) DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 @@ -44,10 +45,7 @@ DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -DEFINES += HAVE_UX_FLOW - SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -SDK_SOURCE_PATH += lib_ux else $(error unknown device TARGET_NAME) endif @@ -93,7 +91,7 @@ $(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) endif CC := $(CLANGPATH)clang -CFLAGS += -O3 -Oz +CFLAGS += -O3 -Oz -I/usr/include AS := $(GCCPATH)arm-none-eabi-gcc AFLAGS += From a401ad934162026ced6367c481a79d51b35baa8f Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Wed, 29 Jul 2020 19:02:29 +0200 Subject: [PATCH 19/37] 0z -> 0s + Bump version 1.2.2 --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 046f0485..4b4ea28e 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.1 +APPVERSION = 1.2.2 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" @@ -91,13 +91,13 @@ $(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) endif CC := $(CLANGPATH)clang -CFLAGS += -O3 -Oz -I/usr/include +CFLAGS += -O3 -Os AS := $(GCCPATH)arm-none-eabi-gcc AFLAGS += LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -O3 -Oz +LDFLAGS += -O3 -Os LDLIBS += -lm -lgcc -lc # Main rules From 9d23373a25da935642790101f9b893a222ab847e Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Thu, 30 Jul 2020 18:04:51 +0200 Subject: [PATCH 20/37] Add rekey step --- src/ui_txn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui_txn.c b/src/ui_txn.c index da1e9fa4..cee5693f 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -477,6 +477,7 @@ typedef struct{ screen_t const screen_table[] = { {"Txn type", &step_txn_type, ALL_TYPES}, {"Sender", &step_sender, ALL_TYPES}, + {"Rekey to", &step_rekey, ALL_TYPES}, {"Fee (Alg)", &step_fee, ALL_TYPES}, // {"First valid", step_firstvalid, ALL_TYPES}, // {"Last valid", step_lastvalid, ALL_TYPES}, From 0bb8c91b5870bb19b9f4041001bf441a889295cf Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Thu, 30 Jul 2020 18:05:26 +0200 Subject: [PATCH 21/37] Bump version 1.2.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4b4ea28e..b05a886b 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.2 +APPVERSION = 1.2.3 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" From 431d004488a64b2d46a8f96ea037435fd23246d0 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Fri, 31 Jul 2020 16:04:01 +0200 Subject: [PATCH 22/37] Reduce stack usage on nano S --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index b627cac9..34eeb295 100644 --- a/src/main.c +++ b/src/main.c @@ -45,7 +45,7 @@ already_computed_key_t current_pubkey; #if defined(TARGET_NANOX) static uint8_t msgpack_buf[2048]; #else -static uint8_t msgpack_buf[1024]; +static uint8_t msgpack_buf[900]; #endif static unsigned int msgpack_next_off; From 7ce6f0b76516a81abf56cb688ba0006cdaef1612 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Fri, 31 Jul 2020 16:26:43 +0200 Subject: [PATCH 23/37] Add opt-in scenario --- src/ui_txn.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index cee5693f..664aa7ef 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -9,6 +9,18 @@ #include "base64.h" #include "glyphs.h" +bool is_opt_in_tx(){ + if(current_txn.type == ASSET_XFER && + current_txn.payment.amount == 0 && + current_txn.asset_xfer.id != 0 && + memcmp(current_txn.asset_xfer.receiver, + current_txn.asset_xfer.sender, + sizeof(current_txn.asset_xfer.receiver) == 0)){ + return true; + } + return false; +} + char caption[20]; static char * @@ -129,7 +141,11 @@ static int step_txn_type() { break; case ASSET_XFER: - ui_text_put("Asset xfer"); + if(is_opt_in_tx()){ + ui_text_put("Opt-in"); + }else{ + ui_text_put("Asset xfer"); + } break; case ASSET_FREEZE: @@ -149,7 +165,6 @@ static int step_txn_type() { static int step_sender() { uint8_t publicKey[32]; fetch_public_key(current_txn.accountId, publicKey); - if (os_memcmp(publicKey, current_txn.sender, sizeof(current_txn.sender)) == 0) { return 0; } @@ -300,6 +315,9 @@ static int step_asset_xfer_id() { } static int step_asset_xfer_amount() { + if(is_opt_in_tx()){ + return 0; + } ui_text_put(amount_to_str(current_txn.asset_xfer.amount)); return 1; } @@ -316,7 +334,8 @@ static int step_asset_xfer_sender() { } static int step_asset_xfer_receiver() { - if (all_zero_key(current_txn.asset_xfer.receiver)) { + if (all_zero_key(current_txn.asset_xfer.receiver) || + is_opt_in_tx()) { return 0; } From f98fdb707d70edc8476ae423f0fbe5e3ade6afbe Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Fri, 31 Jul 2020 16:27:14 +0200 Subject: [PATCH 24/37] Bump version 1.2.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b05a886b..49f3e304 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.3 +APPVERSION = 1.2.4 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" From 7ce6ed15140971b20ee2c4ae387e7c05b72e999b Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Tue, 4 Aug 2020 16:27:05 +0200 Subject: [PATCH 25/37] ui-improvements: disabled amount unit conversion in `ASSET_XFER` transactions. Unit of `aamt` in Asset Transfer transactions is asset specific and more investigation are needed to define whether it is possible to handle this and how. --- src/ui_txn.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index 664aa7ef..ce437205 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -318,7 +318,7 @@ static int step_asset_xfer_amount() { if(is_opt_in_tx()){ return 0; } - ui_text_put(amount_to_str(current_txn.asset_xfer.amount)); + ui_text_put(u64str(current_txn.asset_xfer.amount)); return 1; } @@ -688,6 +688,12 @@ void ui_txn(void) { PRINTF(" Amount: %s\n", amount_to_str(current_txn.payment.amount)); PRINTF(" Close to: %.*h\n", 32, current_txn.payment.close); } + if (current_txn.type == ASSET_XFER) { + PRINTF(" Sender: %.*h\n", 32, current_txn.asset_xfer.sender); + PRINTF(" Receiver: %.*h\n", 32, current_txn.asset_xfer.receiver); + PRINTF(" Amount: %s\n", u64str(current_txn.asset_xfer.amount)); + PRINTF(" Close to: %.*h\n", 32, current_txn.asset_xfer.close); + } if (current_txn.type == KEYREG) { PRINTF(" Vote PK: %.*h\n", 32, current_txn.keyreg.votepk); PRINTF(" VRF PK: %.*h\n", 32, current_txn.keyreg.vrfpk); From f232e728aa25e4d36182016b4e803ba2028f1d38 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Tue, 4 Aug 2020 16:30:11 +0200 Subject: [PATCH 26/37] ui-improvements: fixed misplaced parenthesis fourbe bug in `is_opt_in_tx()`. --- src/ui_txn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index ce437205..7d117a70 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -15,7 +15,7 @@ bool is_opt_in_tx(){ current_txn.asset_xfer.id != 0 && memcmp(current_txn.asset_xfer.receiver, current_txn.asset_xfer.sender, - sizeof(current_txn.asset_xfer.receiver) == 0)){ + sizeof(current_txn.asset_xfer.receiver)) == 0){ return true; } return false; From 2061cd259c926a647a123ecabce0e7dcede7acca Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Tue, 4 Aug 2020 16:35:17 +0200 Subject: [PATCH 27/37] Bump version 1.2.5 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 49f3e304..683d047b 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.4 +APPVERSION = 1.2.5 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" From bcf1157a0c2cf00c6f0bb972f5690d4c7af36c46 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:38:58 +0200 Subject: [PATCH 28/37] ui-improvements: pass `decimals` as argument to `amount_to_str()`. --- src/ui_txn.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index 7d117a70..c9b80bc4 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -108,12 +108,13 @@ bool adjustDecimals(char *src, uint32_t srcLength, char *target, return true; } -char* amount_to_str(uint64_t amount){ +static char* +amount_to_str(uint64_t amount, uint8_t decimals){ char* result = u64str(amount); char tmp[24]; memcpy(tmp, result, sizeof(tmp)); memset(result, 0, sizeof(tmp)); - adjustDecimals(tmp, strlen(tmp), result, 27, ALGORAND_DECIMALS); + adjustDecimals(tmp, strlen(tmp), result, 27, decimals); result[26] = '\0'; return result; } @@ -187,7 +188,7 @@ static int step_rekey() { } static int step_fee() { - ui_text_put(amount_to_str(current_txn.fee)); + ui_text_put(amount_to_str(current_txn.fee, ALGORAND_DECIMALS)); return 1; } @@ -256,7 +257,7 @@ static int step_receiver() { } static int step_amount() { - ui_text_put(amount_to_str(current_txn.payment.amount)); + ui_text_put(amount_to_str(current_txn.payment.amount, ALGORAND_DECIMALS)); return 1; } @@ -678,14 +679,14 @@ void ui_txn(void) { PRINTF("Transaction:\n"); PRINTF(" Type: %d\n", current_txn.type); PRINTF(" Sender: %.*h\n", 32, current_txn.sender); - PRINTF(" Fee: %s\n", amount_to_str(current_txn.fee)); + PRINTF(" Fee: %s\n", amount_to_str(current_txn.fee, ALGORAND_DECIMALS)); PRINTF(" First valid: %s\n", u64str(current_txn.firstValid)); PRINTF(" Last valid: %s\n", u64str(current_txn.lastValid)); PRINTF(" Genesis ID: %.*s\n", 32, current_txn.genesisID); PRINTF(" Genesis hash: %.*h\n", 32, current_txn.genesisHash); if (current_txn.type == PAYMENT) { PRINTF(" Receiver: %.*h\n", 32, current_txn.payment.receiver); - PRINTF(" Amount: %s\n", amount_to_str(current_txn.payment.amount)); + PRINTF(" Amount: %s\n", amount_to_str(current_txn.payment.amount, ALGORAND_DECIMALS)); PRINTF(" Close to: %.*h\n", 32, current_txn.payment.close); } if (current_txn.type == ASSET_XFER) { From 849bf837af7aea3ff312a20ae6b9d8a6e517b07d Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:45:59 +0200 Subject: [PATCH 29/37] ui-improvments: mark `Amount` screen with dynamic caption flag (WIP) - set `caption` field to `NULL` in `screen_t` definition. - do not override caption if `caption` is `NULL` when jumping to next screen. - WIP: screen captions are not correctly updated for now. --- src/ui_txn.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index c9b80bc4..b936afcd 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -494,6 +494,8 @@ typedef struct{ uint8_t type; } screen_t; +#define SCREEN_DYN_CAPTION NULL + screen_t const screen_table[] = { {"Txn type", &step_txn_type, ALL_TYPES}, {"Sender", &step_sender, ALL_TYPES}, @@ -514,7 +516,7 @@ screen_t const screen_table[] = { {"Key dilution", &step_keydilution, KEYREG}, {"Participating", &step_participating, KEYREG}, {"Asset ID", &step_asset_xfer_id, ASSET_XFER}, - {"Asset amt", &step_asset_xfer_amount, ASSET_XFER}, + {SCREEN_DYN_CAPTION, &step_asset_xfer_amount, ASSET_XFER}, {"Asset src", &step_asset_xfer_sender, ASSET_XFER}, {"Asset dst", &step_asset_xfer_receiver, ASSET_XFER}, {"Asset close", &step_asset_xfer_close, ASSET_XFER}, @@ -620,7 +622,11 @@ bool set_state_data(bool forward){ return false; } - strncpy(caption, (char*)PIC(screen_table[current_data_index].caption), sizeof(caption)); + if (screen_table[current_data_index].caption != SCREEN_DYN_CAPTION) { + strncpy(caption, + (char*)PIC(screen_table[current_data_index].caption), + sizeof(caption)); + } PRINTF("caption: %s\n", caption); PRINTF("details: %s\n\n", text); From b143975d2e7e280ba8022072a75bf561f97fc325 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:48:58 +0200 Subject: [PATCH 30/37] ui-improvements: added hardcoded ASA definitions for user-friendly display. --- src/algo_asa.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/algo_asa.h | 17 +++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/algo_asa.c create mode 100644 src/algo_asa.h diff --git a/src/algo_asa.c b/src/algo_asa.c new file mode 100644 index 00000000..46268652 --- /dev/null +++ b/src/algo_asa.c @@ -0,0 +1,44 @@ +#include "os.h" +#include "algo_asa.h" + + +#define ARRAY_SIZE(__arr) (sizeof(__arr) / sizeof(__arr[0])) + +#define ALGO_ASA(__id, __name, __unit, __decimals) { \ + .assetId = __id, \ + .decimals = __decimals, \ + .unit = __unit, \ + .name = __name, \ + } + + +static const algo_asset_info_t algo_assets[] = { + ALGO_ASA(438840, "Micro-Tesla", "M-TSLA", 0), + ALGO_ASA(438839, "Micro-Apple", "M-AAPL", 0), + ALGO_ASA(438838, "Micro-Google", "M-GOOGL", 0), + ALGO_ASA(438837, "Micro-Netflix", "M-NFLX", 0), + ALGO_ASA(438836, "Micro-Twitter", "M-TWTR", 0), + ALGO_ASA(438833, "Micro-Amazon", "M-AMZN", 0), + ALGO_ASA(438832, "Micro-Microsoft", "M-MSFT", 0), + ALGO_ASA(438831, "MESE Index Fund", "MESX", 6), + ALGO_ASA(438828, "MESE USD Exchange Token", "USD-MESE", 6), + ALGO_ASA(312769, "Thether USDt", "USDt", 6), + ALGO_ASA(163650, "Asia Reserve Currency Coin", "ARCC", 6), + ALGO_ASA(342836, "Asia Reserve Currency Coin", "ARCC", 3), +}; + + +const algo_asset_info_t * +algo_asa_get(uint64_t id) +{ + const algo_asset_info_t *p; + const algo_asset_info_t *endp = algo_assets + ARRAY_SIZE(algo_assets); + + for (p = algo_assets; p && p < endp; p++) { + if (p->assetId == id) { + return p; + } + } + return NULL; +} + diff --git a/src/algo_asa.h b/src/algo_asa.h new file mode 100644 index 00000000..7dd8ada1 --- /dev/null +++ b/src/algo_asa.h @@ -0,0 +1,17 @@ +#ifndef __ALGO_ASA_H__ +#define __ALGO_ASA_H__ + +#define __packed __attribute__((packed)) + + +typedef struct { + uint64_t assetId; + uint8_t decimals; + const char unit[15]; + const char name[32]; +} __packed algo_asset_info_t; + + +const algo_asset_info_t *algo_asa_get(uint64_t id); + +#endif From 80938decd3a390072ad4652894cf78b5af2dbdfa Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:50:10 +0200 Subject: [PATCH 31/37] ui-improvements: display asset ID and Amount screens using `algo_asa_get()` meta-data during `ASSET_XFER` approval. --- src/ui_txn.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ui_txn.c b/src/ui_txn.c index b936afcd..6d1112ba 100644 --- a/src/ui_txn.c +++ b/src/ui_txn.c @@ -6,6 +6,7 @@ #include "algo_tx.h" #include "algo_addr.h" #include "algo_keys.h" +#include "algo_asa.h" #include "base64.h" #include "glyphs.h" @@ -311,7 +312,14 @@ static int step_participating() { } static int step_asset_xfer_id() { - ui_text_put(u64str(current_txn.asset_xfer.id)); + const algo_asset_info_t *asa = algo_asa_get(current_txn.asset_xfer.id); + const char *id = u64str(current_txn.asset_xfer.id); + + if (asa == NULL) { + snprintf(text, sizeof(text), "#%s", id); + } else { + snprintf(text, sizeof(text), "%s (#%s)", asa->name, id); + } return 1; } @@ -319,7 +327,15 @@ static int step_asset_xfer_amount() { if(is_opt_in_tx()){ return 0; } - ui_text_put(u64str(current_txn.asset_xfer.amount)); + + const algo_asset_info_t *asa = algo_asa_get(current_txn.asset_xfer.id); + if (asa != NULL) { + snprintf(caption, sizeof(caption), "Amount (%s)", asa->unit); + ui_text_put(amount_to_str(current_txn.asset_xfer.amount, asa->decimals)); + } else { + snprintf(caption, sizeof(caption), "Amount (base unit)"); + ui_text_put(u64str(current_txn.asset_xfer.amount)); + } return 1; } From c447a5d0fd691297bdf028b0178826b836e2e8e2 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:53:09 +0200 Subject: [PATCH 32/37] Bump version 1.2.6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 683d047b..7afe108c 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.5 +APPVERSION = 1.2.6 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" From d5fc5dc7732301d15a62eaa88dbf2a2817b61778 Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 17:01:41 +0200 Subject: [PATCH 33/37] ui-improvements: removed debug code. --- src/algo_asa.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algo_asa.c b/src/algo_asa.c index 46268652..a5d883eb 100644 --- a/src/algo_asa.c +++ b/src/algo_asa.c @@ -24,7 +24,6 @@ static const algo_asset_info_t algo_assets[] = { ALGO_ASA(438828, "MESE USD Exchange Token", "USD-MESE", 6), ALGO_ASA(312769, "Thether USDt", "USDt", 6), ALGO_ASA(163650, "Asia Reserve Currency Coin", "ARCC", 6), - ALGO_ASA(342836, "Asia Reserve Currency Coin", "ARCC", 3), }; From acfda1b0f17fb91e6c7bb3f8f438ac36e630396a Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Tue, 11 Aug 2020 10:43:31 +0200 Subject: [PATCH 34/37] ui-improvements: fixed typo. --- src/algo_asa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algo_asa.c b/src/algo_asa.c index a5d883eb..b50c4426 100644 --- a/src/algo_asa.c +++ b/src/algo_asa.c @@ -22,7 +22,7 @@ static const algo_asset_info_t algo_assets[] = { ALGO_ASA(438832, "Micro-Microsoft", "M-MSFT", 0), ALGO_ASA(438831, "MESE Index Fund", "MESX", 6), ALGO_ASA(438828, "MESE USD Exchange Token", "USD-MESE", 6), - ALGO_ASA(312769, "Thether USDt", "USDt", 6), + ALGO_ASA(312769, "Tether USDt", "USDt", 6), ALGO_ASA(163650, "Asia Reserve Currency Coin", "ARCC", 6), }; From d61fde73ca5d4509d6003217f30373ead8e91cda Mon Sep 17 00:00:00 2001 From: Olivier Goulpeau Date: Mon, 10 Aug 2020 16:53:09 +0200 Subject: [PATCH 35/37] Bump version 1.2.7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7afe108c..3e709f1f 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.6 +APPVERSION = 1.2.7 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'" From 5f1efe29d40dce74b76f8423f9fe9bab79acb62d Mon Sep 17 00:00:00 2001 From: Raf Date: Tue, 25 Aug 2020 11:03:37 +0200 Subject: [PATCH 36/37] Fix a USB race by initializing the globals before managing the USB connexion. This race crashed the nano S device when attempting at adding an Algorand account within the Ledger Live when using (at least) a specific Windows version along with a specific USB hub (Windos 10.0.18362 + an asus USB hub). --- src/main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 34eeb295..b6f0be49 100644 --- a/src/main.c +++ b/src/main.c @@ -456,6 +456,8 @@ main(void) G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); #endif + init_globals(); + USB_power(0); USB_power(1); @@ -465,9 +467,6 @@ main(void) #endif ui_idle(); - - init_globals(); - algorand_main(); } CATCH(EXCEPTION_IO_RESET) { From d1d1557a554dae01bb7d95fb255bd92ce5275584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Colas?= Date: Tue, 25 Aug 2020 15:28:41 +0200 Subject: [PATCH 37/37] Bump version to 1.2.8 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3e709f1f..84262b02 100755 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include $(BOLOS_SDK)/Makefile.glyphs # Main app configuration APPNAME = "Algorand" -APPVERSION = 1.2.7 +APPVERSION = 1.2.8 APP_LOAD_PARAMS = --appFlags 0x250 $(COMMON_LOAD_PARAMS) APP_LOAD_PARAMS += --path "44'/283'"