From 439244f40d3db90171e580d6320526db21163170 Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Mon, 29 Jan 2024 16:52:43 +0100 Subject: [PATCH] src: Use ux sync API --- src/apdu/dispatcher.c | 22 +-- src/apdu/dispatcher.h | 19 --- src/app_main.c | 6 - src/constants.h | 14 ++ src/globals.h | 6 - src/handler/get_app_name.c | 6 +- src/handler/get_public_key.c | 61 ++++++-- src/handler/get_public_key.h | 2 - src/handler/get_version.c | 6 +- src/handler/sign_tx.c | 247 ++++++++++++++++++++++++------ src/handler/sign_tx.h | 2 +- src/helper/send_reponse.c | 53 ------- src/helper/send_response.h | 39 ----- src/types.h | 70 --------- src/ui/action/validate.c | 76 --------- src/ui/action/validate.h | 21 --- src/ui/display.h | 19 ++- src/ui/nbgl_display_address.c | 66 ++++---- src/ui/nbgl_display_transaction.c | 110 ++++++------- 19 files changed, 362 insertions(+), 483 deletions(-) delete mode 100644 src/helper/send_reponse.c delete mode 100644 src/helper/send_response.h delete mode 100644 src/types.h delete mode 100644 src/ui/action/validate.c delete mode 100644 src/ui/action/validate.h diff --git a/src/apdu/dispatcher.c b/src/apdu/dispatcher.c index b33b19f8c..f28287b25 100644 --- a/src/apdu/dispatcher.c +++ b/src/apdu/dispatcher.c @@ -23,14 +23,12 @@ #include "ledger_assert.h" #include "dispatcher.h" -#include "../constants.h" -#include "../globals.h" -#include "../types.h" -#include "../sw.h" -#include "../handler/get_version.h" -#include "../handler/get_app_name.h" -#include "../handler/get_public_key.h" -#include "../handler/sign_tx.h" +#include "constants.h" +#include "sw.h" +#include "handler/get_version.h" +#include "handler/get_app_name.h" +#include "handler/get_public_key.h" +#include "handler/sign_tx.h" int apdu_dispatcher(const command_t *cmd) { LEDGER_ASSERT(cmd != NULL, "NULL cmd"); @@ -69,12 +67,6 @@ int apdu_dispatcher(const command_t *cmd) { return handler_get_public_key(&buf, (bool) cmd->p1); case SIGN_TX: - if ((cmd->p1 == P1_START && cmd->p2 != P2_MORE) || // - cmd->p1 > P1_MAX || // - (cmd->p2 != P2_LAST && cmd->p2 != P2_MORE)) { - return io_send_sw(SW_WRONG_P1P2); - } - if (!cmd->data) { return io_send_sw(SW_WRONG_DATA_LENGTH); } @@ -83,7 +75,7 @@ int apdu_dispatcher(const command_t *cmd) { buf.size = cmd->lc; buf.offset = 0; - return handler_sign_tx(&buf, cmd->p1, (bool) (cmd->p2 & P2_MORE)); + return handler_sign_tx(&buf, cmd->p1, cmd->p2); default: return io_send_sw(SW_INS_NOT_SUPPORTED); } diff --git a/src/apdu/dispatcher.h b/src/apdu/dispatcher.h index 191ce3f8c..36e1f60c7 100644 --- a/src/apdu/dispatcher.h +++ b/src/apdu/dispatcher.h @@ -2,25 +2,6 @@ #include "parser.h" -#include "../types.h" - -/** - * Parameter 2 for last APDU to receive. - */ -#define P2_LAST 0x00 -/** - * Parameter 2 for more APDU to receive. - */ -#define P2_MORE 0x80 -/** - * Parameter 1 for first APDU number. - */ -#define P1_START 0x00 -/** - * Parameter 1 for maximum APDU number. - */ -#define P1_MAX 0x03 - /** * Dispatch APDU command received to the right handler. * diff --git a/src/app_main.c b/src/app_main.c index 30e877c7b..c48bd60ca 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -21,15 +21,12 @@ #include "os.h" #include "ux.h" -#include "types.h" #include "globals.h" #include "io.h" #include "sw.h" #include "ui/menu.h" #include "apdu/dispatcher.h" -global_ctx_t G_context; - const internal_storage_t N_storage_real; /** @@ -45,9 +42,6 @@ void app_main() { ui_menu_main(); - // Reset context - explicit_bzero(&G_context, sizeof(G_context)); - // Initialize the NVM data if required if (N_storage.initialized != 0x01) { internal_storage_t storage; diff --git a/src/constants.h b/src/constants.h index 487da0754..4ed83ede1 100644 --- a/src/constants.h +++ b/src/constants.h @@ -5,6 +5,16 @@ */ #define CLA 0xE0 +/** + * Enumeration with expected INS of APDU commands. + */ +typedef enum { + GET_VERSION = 0x03, /// version of the application + GET_APP_NAME = 0x04, /// name of the application + GET_PUBLIC_KEY = 0x05, /// public key of corresponding BIP32 path + SIGN_TX = 0x06 /// sign transaction with BIP32 path +} command_e; + /** * Length of APPNAME variable in the Makefile. */ @@ -34,3 +44,7 @@ * Exponent used to convert mBOL to BOL unit (N BOL = N * 10^3 mBOL). */ #define EXPONENT_SMALLEST_UNIT 3 + +#define PUBKEY_LEN 65 +#define CHAINCODE_LEN 32 +#define ADDRESS_LEN 20 diff --git a/src/globals.h b/src/globals.h index c2fd830bb..cd3fd69a6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -5,7 +5,6 @@ #include "ux.h" #include "io.h" -#include "types.h" #include "constants.h" /** @@ -23,11 +22,6 @@ extern ux_state_t G_ux; */ extern bolos_ux_params_t G_ux_params; -/** - * Global context for user requests. - */ -extern global_ctx_t G_context; - /** * Global structure for NVM data storage. */ diff --git a/src/handler/get_app_name.c b/src/handler/get_app_name.c index 1eabb8b14..2c5441e02 100644 --- a/src/handler/get_app_name.c +++ b/src/handler/get_app_name.c @@ -21,10 +21,8 @@ #include "buffer.h" #include "get_app_name.h" -#include "../constants.h" -#include "../globals.h" -#include "../sw.h" -#include "../types.h" +#include "constants.h" +#include "sw.h" int handler_get_app_name() { _Static_assert(APPNAME_LEN < MAX_APPNAME_LEN, "APPNAME must be at most 64 characters!"); diff --git a/src/handler/get_public_key.c b/src/handler/get_public_key.c index 9e1bc8c5a..0239f3e5b 100644 --- a/src/handler/get_public_key.c +++ b/src/handler/get_public_key.c @@ -25,38 +25,65 @@ #include "io.h" #include "buffer.h" #include "crypto_helpers.h" +#include "bip32.h" #include "get_public_key.h" -#include "../globals.h" -#include "../types.h" -#include "../sw.h" -#include "../ui/display.h" -#include "../helper/send_response.h" +#include "sw.h" +#include "ui/display.h" -int handler_get_public_key(buffer_t *cdata, bool display) { - explicit_bzero(&G_context, sizeof(G_context)); - G_context.req_type = CONFIRM_ADDRESS; - G_context.state = STATE_NONE; +static void send_response_pubkey(const uint8_t raw_public_key[static PUBKEY_LEN], const uint8_t chain_code[static CHAINCODE_LEN]) { + uint8_t pubkey_len[1] = {PUBKEY_LEN}; + uint8_t chain_code_len[1] = {CHAINCODE_LEN}; + + buffer_t buffers[4] = { + {.ptr = pubkey_len, .size = sizeof(pubkey_len), .offset = 0}, + {.ptr = raw_public_key, .size = PUBKEY_LEN, .offset = 0}, + {.ptr = chain_code_len, .size = sizeof(chain_code_len), .offset = 0}, + {.ptr = chain_code, .size = CHAINCODE_LEN, .offset = 0}, + }; + + io_send_response_buffers(buffers, 4, SW_OK); +} - if (!buffer_read_u8(cdata, &G_context.bip32_path_len) || - !buffer_read_bip32_path(cdata, G_context.bip32_path, (size_t) G_context.bip32_path_len)) { +int handler_get_public_key(buffer_t *cdata, bool display) { + uint8_t bip32_path_len; + uint32_t bip32_path[MAX_BIP32_PATH] = {0}; + if (!buffer_read_u8(cdata, &bip32_path_len) || + !buffer_read_bip32_path(cdata, bip32_path, (size_t) bip32_path_len)) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + + uint8_t raw_public_key[PUBKEY_LEN] = {0}; + uint8_t chain_code[CHAINCODE_LEN] = {0}; cx_err_t error = bip32_derive_get_pubkey_256(CX_CURVE_256K1, - G_context.bip32_path, - G_context.bip32_path_len, - G_context.pk_info.raw_public_key, - G_context.pk_info.chain_code, + bip32_path, + bip32_path_len, + raw_public_key, + chain_code, CX_SHA512); if (error != CX_OK) { return io_send_sw(error); } + ui_ret_e ret = UI_RET_APPROVED; + if (display) { + ret = ui_display_address(raw_public_key); + } + + if (ret == UI_RET_APPROVED) { + send_response_pubkey(raw_public_key, chain_code); + } else if (ret == UI_RET_REJECTED) { + io_send_sw(SW_DENY); + } else { + io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + + if (display) { - return ui_display_address(); + ui_display_address_status(ret); } - return helper_send_response_pubkey(); + return 0; } diff --git a/src/handler/get_public_key.h b/src/handler/get_public_key.h index 3cb538a6d..119811bdb 100644 --- a/src/handler/get_public_key.h +++ b/src/handler/get_public_key.h @@ -6,8 +6,6 @@ #include "buffer.h" -#include "../types.h" - /** * Handler for GET_PUBLIC_KEY command. If successfully parse BIP32 path, * derive public key/chain code and send APDU response. diff --git a/src/handler/get_version.c b/src/handler/get_version.c index bf37ec1f9..a8ea911d7 100644 --- a/src/handler/get_version.c +++ b/src/handler/get_version.c @@ -23,10 +23,8 @@ #include "buffer.h" #include "get_version.h" -#include "../globals.h" -#include "../constants.h" -#include "../sw.h" -#include "../types.h" +#include "constants.h" +#include "sw.h" int handler_get_version() { _Static_assert(APPVERSION_LEN == 3, "Length of (MAJOR || MINOR || PATCH) must be 3!"); diff --git a/src/handler/sign_tx.c b/src/handler/sign_tx.c index 7f0c581b5..a797b816f 100644 --- a/src/handler/sign_tx.c +++ b/src/handler/sign_tx.c @@ -23,75 +23,220 @@ #include "os.h" #include "cx.h" #include "buffer.h" +#include "parser.h" +#include "bip32.h" +#include "io.h" +#include "crypto_helpers.h" #include "sign_tx.h" -#include "../sw.h" -#include "../globals.h" -#include "../ui/display.h" -#include "../transaction/types.h" -#include "../transaction/deserialize.h" - -int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more) { - if (chunk == 0) { // first APDU, parse BIP32 path - explicit_bzero(&G_context, sizeof(G_context)); - G_context.req_type = CONFIRM_TRANSACTION; - G_context.state = STATE_NONE; - - if (!buffer_read_u8(cdata, &G_context.bip32_path_len) || - !buffer_read_bip32_path(cdata, - G_context.bip32_path, - (size_t) G_context.bip32_path_len)) { - return io_send_sw(SW_WRONG_DATA_LENGTH); - } +#include "sw.h" +#include "ui/display.h" +#include "transaction/types.h" +#include "transaction/deserialize.h" + +/** + * Parameter 2 for last APDU to receive. + */ +#define P2_LAST 0x00 +/** + * Parameter 2 for more APDU to receive. + */ +#define P2_MORE 0x80 +/** + * Parameter 1 for first APDU number. + */ +#define P1_START 0x00 +/** + * Parameter 1 for maximum APDU number. + */ +#define P1_MAX 0x03 + + +static uint16_t get_next_chunk(buffer_t *cdata, bool *more) { + int input_len = 0; + command_t cmd; + + if ((input_len = io_recv_command()) < 0) { + LEDGER_ASSERT(false, "=> io_recv_command failure\n"); + } + + // Parse APDU command from G_io_apdu_buffer + if (!apdu_parser(&cmd, G_io_apdu_buffer, input_len)) { + PRINTF("=> /!\\ BAD LENGTH: %.*H\n", input_len, G_io_apdu_buffer); + return SW_WRONG_DATA_LENGTH; + } + + PRINTF("=> CLA=%02X | INS=%02X | P1=%02X | P2=%02X | Lc=%02X | CData=%.*H\n", + cmd.cla, + cmd.ins, + cmd.p1, + cmd.p2, + cmd.lc, + cmd.lc, + cmd.data); + + if (cmd.cla != CLA) { + return SW_BAD_STATE; + } + + if (cmd.ins != SIGN_TX) { + return SW_BAD_STATE; + } + + if (cmd.p1 == P1_START) { + return SW_BAD_STATE; + } + + if ((cmd.p2 != P2_MORE) && (cmd.p2 != P2_LAST)) { + return SW_WRONG_P1P2; + } - return io_send_sw(SW_OK); + if (!cmd.data) { + return SW_WRONG_DATA_LENGTH; + } + + cdata->ptr = cmd.data; + cdata->size = cmd.lc; + cdata->offset = 0; + + if (cmd.p2 == P2_MORE) { + *more = true; + } else { + *more = false; + } + + return SW_OK; +} + +static cx_err_t crypto_sign_message(const uint32_t *bip32_path, + uint8_t bip32_path_len, + const uint8_t m_hash[static 32], + uint8_t signature[static MAX_DER_SIG_LEN], + uint8_t *signature_len, + uint8_t *v) { + uint32_t info = 0; + size_t sig_len = MAX_DER_SIG_LEN; + + cx_err_t error = bip32_derive_ecdsa_sign_hash_256(CX_CURVE_256K1, + bip32_path, + bip32_path_len, + CX_RND_RFC6979 | CX_LAST, + CX_SHA256, + m_hash, + 32, + signature, + &sig_len, + &info); + if (error != CX_OK) { + return error; + } + + PRINTF("Signature: %.*H\n", sig_len, signature); + + *signature_len = sig_len; + *v = (uint8_t)(info & CX_ECCINFO_PARITY_ODD); + + return CX_OK; +} - } else { // parse transaction - if (G_context.req_type != CONFIRM_TRANSACTION) { - return io_send_sw(SW_BAD_STATE); +static void helper_send_response_sig(uint8_t signature_len, const uint8_t *signature, uint8_t v) { + uint8_t _signature_len[1] = {signature_len}; + uint8_t _v[1] = {v}; + + buffer_t buffers[3] = { + {.ptr = _signature_len, .size = sizeof(_signature_len), .offset = 0}, + {.ptr = signature, .size = signature_len, .offset = 0}, + {.ptr = _v, .size = sizeof(_v), .offset = 0}, + }; + + io_send_response_buffers(buffers, 3, SW_OK); +} + + + +int handler_sign_tx(buffer_t *cdata, uint8_t p1, uint8_t p2) { + if (p1 != P1_START) { + return io_send_sw(SW_BAD_STATE); + } + + if (p2 != P2_MORE) { + return io_send_sw(SW_BAD_STATE); + } + + uint8_t bip32_path_len; + uint32_t bip32_path[MAX_BIP32_PATH] = {0}; + if (!buffer_read_u8(cdata, &bip32_path_len) || + !buffer_read_bip32_path(cdata, bip32_path, (size_t) bip32_path_len)) { + return io_send_sw(SW_WRONG_DATA_LENGTH); + } + + uint8_t raw_tx[MAX_TRANSACTION_LEN]; /// raw transaction serialized + size_t raw_tx_len = 0; /// length of raw transaction + bool more = true; + while (more) { + io_send_sw(SW_OK); + + uint16_t sw = get_next_chunk(cdata, &more); + + if (sw != SW_OK) { + return io_send_sw(sw); } - if (G_context.tx_info.raw_tx_len + cdata->size > sizeof(G_context.tx_info.raw_tx)) { + + if (raw_tx_len + cdata->size > sizeof(raw_tx)) { return io_send_sw(SW_WRONG_TX_LENGTH); } + if (!buffer_move(cdata, - G_context.tx_info.raw_tx + G_context.tx_info.raw_tx_len, + raw_tx + raw_tx_len, cdata->size)) { return io_send_sw(SW_TX_PARSING_FAIL); } - G_context.tx_info.raw_tx_len += cdata->size; - - if (more) { - // more APDUs with transaction part are expected. - // Send a SW_OK to signal that we have received the chunk - return io_send_sw(SW_OK); - - } else { - // last APDU for this transaction, let's parse, display and request a sign confirmation - - buffer_t buf = {.ptr = G_context.tx_info.raw_tx, - .size = G_context.tx_info.raw_tx_len, - .offset = 0}; - - parser_status_e status = transaction_deserialize(&buf, &G_context.tx_info.transaction); - PRINTF("Parsing status: %d.\n", status); - if (status != PARSING_OK) { - return io_send_sw(SW_TX_PARSING_FAIL); - } + raw_tx_len += cdata->size; + } - G_context.state = STATE_PARSED; + // last APDU for this transaction, let's parse, display and request a sign confirmation - if (cx_keccak_256_hash(G_context.tx_info.raw_tx, - G_context.tx_info.raw_tx_len, - G_context.tx_info.m_hash) != CX_OK) { - return io_send_sw(SW_TX_HASH_FAIL); - } + buffer_t buf = {.ptr = raw_tx, + .size = raw_tx_len, + .offset = 0}; + transaction_t transaction = {0}; - PRINTF("Hash: %.*H\n", sizeof(G_context.tx_info.m_hash), G_context.tx_info.m_hash); + parser_status_e status = transaction_deserialize(&buf, &transaction); + PRINTF("Parsing status: %d.\n", status); + if (status != PARSING_OK) { + return io_send_sw(SW_TX_PARSING_FAIL); + } - return ui_display_transaction(); + uint8_t m_hash[32]; + cx_keccak_256_hash(raw_tx, raw_tx_len, m_hash); + PRINTF("Hash: %.*H\n", 32, m_hash); + + ui_ret_e ret = ui_display_transaction(&transaction); + + if (ret == UI_RET_APPROVED) { + uint8_t signature[MAX_DER_SIG_LEN]; + uint8_t signature_len; + uint8_t v; + cx_err_t err; + + err = crypto_sign_message(bip32_path, + bip32_path_len, + m_hash, + signature, + &signature_len, + &v); + if (err != CX_OK) { + io_send_sw(SW_SIGNATURE_FAIL); } + helper_send_response_sig(signature_len, signature, v); + } else if (ret == UI_RET_REJECTED) { + io_send_sw(SW_DENY); + } else { + io_send_sw(SW_BAD_STATE); } + ui_display_transaction_status(ret); + return 0; } diff --git a/src/handler/sign_tx.h b/src/handler/sign_tx.h index 633ddf6d4..a1bab52b2 100644 --- a/src/handler/sign_tx.h +++ b/src/handler/sign_tx.h @@ -22,4 +22,4 @@ * @return zero or positive integer if success, negative integer otherwise. * */ -int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more); +int handler_sign_tx(buffer_t *cdata, uint8_t p1, uint8_t p2); diff --git a/src/helper/send_reponse.c b/src/helper/send_reponse.c deleted file mode 100644 index 19f9c395a..000000000 --- a/src/helper/send_reponse.c +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** - * Ledger App Boilerplate. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // size_t -#include // uint*_t -#include // memmove - -#include "buffer.h" - -#include "send_response.h" -#include "../constants.h" -#include "../globals.h" -#include "../sw.h" - -int helper_send_response_pubkey() { - uint8_t resp[1 + PUBKEY_LEN + 1 + CHAINCODE_LEN] = {0}; - size_t offset = 0; - - resp[offset++] = PUBKEY_LEN; - memmove(resp + offset, G_context.pk_info.raw_public_key, PUBKEY_LEN); - offset += PUBKEY_LEN; - resp[offset++] = CHAINCODE_LEN; - memmove(resp + offset, G_context.pk_info.chain_code, CHAINCODE_LEN); - offset += CHAINCODE_LEN; - - return io_send_response_pointer(resp, offset, SW_OK); -} - -int helper_send_response_sig() { - uint8_t resp[1 + MAX_DER_SIG_LEN + 1] = {0}; - size_t offset = 0; - - resp[offset++] = G_context.tx_info.signature_len; - memmove(resp + offset, G_context.tx_info.signature, G_context.tx_info.signature_len); - offset += G_context.tx_info.signature_len; - resp[offset++] = (uint8_t) G_context.tx_info.v; - - return io_send_response_pointer(resp, offset, SW_OK); -} diff --git a/src/helper/send_response.h b/src/helper/send_response.h deleted file mode 100644 index 52fb40db0..000000000 --- a/src/helper/send_response.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "os.h" -#include "macros.h" - -/** - * Length of public key. - */ -#define PUBKEY_LEN (MEMBER_SIZE(pubkey_ctx_t, raw_public_key)) -/** - * Length of chain code. - */ -#define CHAINCODE_LEN (MEMBER_SIZE(pubkey_ctx_t, chain_code)) - -/** - * Helper to send APDU response with public key and chain code. - * - * response = PUBKEY_LEN (1) || - * G_context.pk_info.public_key (PUBKEY_LEN) || - * CHAINCODE_LEN (1) || - * G_context.pk_info.chain_code (CHAINCODE_LEN) - * - * @return zero or positive integer if success, -1 otherwise. - * - */ -int helper_send_response_pubkey(void); - -/** - * Helper to send APDU response with signature and v (parity of - * y-coordinate of R). - * - * response = G_context.tx_info.signature_len (1) || - * G_context.tx_info.signature (G_context.tx_info.signature_len) || - * G_context.tx_info.v (1) - * - * @return zero or positive integer if success, -1 otherwise. - * - */ -int helper_send_response_sig(void); diff --git a/src/types.h b/src/types.h deleted file mode 100644 index 39bb1dadc..000000000 --- a/src/types.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include // size_t -#include // uint*_t - -#include "bip32.h" - -#include "constants.h" -#include "transaction/types.h" - -/** - * Enumeration with expected INS of APDU commands. - */ -typedef enum { - GET_VERSION = 0x03, /// version of the application - GET_APP_NAME = 0x04, /// name of the application - GET_PUBLIC_KEY = 0x05, /// public key of corresponding BIP32 path - SIGN_TX = 0x06 /// sign transaction with BIP32 path -} command_e; -/** - * Enumeration with parsing state. - */ -typedef enum { - STATE_NONE, /// No state - STATE_PARSED, /// Transaction data parsed - STATE_APPROVED /// Transaction data approved -} state_e; - -/** - * Enumeration with user request type. - */ -typedef enum { - CONFIRM_ADDRESS, /// confirm address derived from public key - CONFIRM_TRANSACTION /// confirm transaction information -} request_type_e; - -/** - * Structure for public key context information. - */ -typedef struct { - uint8_t raw_public_key[65]; /// format (1), x-coordinate (32), y-coodinate (32) - uint8_t chain_code[32]; /// for public key derivation -} pubkey_ctx_t; - -/** - * Structure for transaction information context. - */ -typedef struct { - uint8_t raw_tx[MAX_TRANSACTION_LEN]; /// raw transaction serialized - size_t raw_tx_len; /// length of raw transaction - transaction_t transaction; /// structured transaction - uint8_t m_hash[32]; /// message hash digest - uint8_t signature[MAX_DER_SIG_LEN]; /// transaction signature encoded in DER - uint8_t signature_len; /// length of transaction signature - uint8_t v; /// parity of y-coordinate of R in ECDSA signature -} transaction_ctx_t; - -/** - * Structure for global context. - */ -typedef struct { - state_e state; /// state of the context - union { - pubkey_ctx_t pk_info; /// public key context - transaction_ctx_t tx_info; /// transaction context - }; - request_type_e req_type; /// user request - uint32_t bip32_path[MAX_BIP32_PATH]; /// BIP32 path - uint8_t bip32_path_len; /// length of BIP32 path -} global_ctx_t; diff --git a/src/ui/action/validate.c b/src/ui/action/validate.c deleted file mode 100644 index a8a790182..000000000 --- a/src/ui/action/validate.c +++ /dev/null @@ -1,76 +0,0 @@ -/***************************************************************************** - * Ledger App Boilerplate. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // bool - -#include "crypto_helpers.h" - -#include "validate.h" -#include "../menu.h" -#include "../../sw.h" -#include "../../globals.h" -#include "../../helper/send_response.h" - -void validate_pubkey(bool choice) { - if (choice) { - helper_send_response_pubkey(); - } else { - io_send_sw(SW_DENY); - } -} - -static int crypto_sign_message(void) { - uint32_t info = 0; - size_t sig_len = sizeof(G_context.tx_info.signature); - - cx_err_t error = bip32_derive_ecdsa_sign_hash_256(CX_CURVE_256K1, - G_context.bip32_path, - G_context.bip32_path_len, - CX_RND_RFC6979 | CX_LAST, - CX_SHA256, - G_context.tx_info.m_hash, - sizeof(G_context.tx_info.m_hash), - G_context.tx_info.signature, - &sig_len, - &info); - if (error != CX_OK) { - return -1; - } - - PRINTF("Signature: %.*H\n", sig_len, G_context.tx_info.signature); - - G_context.tx_info.signature_len = sig_len; - G_context.tx_info.v = (uint8_t)(info & CX_ECCINFO_PARITY_ODD); - - return 0; -} - -void validate_transaction(bool choice) { - if (choice) { - G_context.state = STATE_APPROVED; - - if (crypto_sign_message() != 0) { - G_context.state = STATE_NONE; - io_send_sw(SW_SIGNATURE_FAIL); - } else { - helper_send_response_sig(); - } - } else { - G_context.state = STATE_NONE; - io_send_sw(SW_DENY); - } -} diff --git a/src/ui/action/validate.h b/src/ui/action/validate.h deleted file mode 100644 index ca1ba632b..000000000 --- a/src/ui/action/validate.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include // bool - -/** - * Action for public key validation and export. - * - * @param[in] choice - * User choice (either approved or rejected). - * - */ -void validate_pubkey(bool choice); - -/** - * Action for transaction information validation. - * - * @param[in] choice - * User choice (either approved or rejectd). - * - */ -void validate_transaction(bool choice); diff --git a/src/ui/display.h b/src/ui/display.h index 8a8ed9bc4..5163e3384 100644 --- a/src/ui/display.h +++ b/src/ui/display.h @@ -2,6 +2,9 @@ #include // bool +#include "constants.h" +#include "transaction/types.h" + #if defined(TARGET_NANOS) || defined(TARGET_NANOX) || defined(TARGET_NANOS2) #define ICON_APP_BOILERPLATE C_app_boilerplate_16px #define ICON_APP_WARNING C_icon_warning @@ -11,9 +14,13 @@ #endif /** - * Callback to reuse action with approve/reject in step FLOW. + * Enumeration with parsing state. */ -typedef void (*action_validate_cb)(bool); +typedef enum { + UI_RET_APPROVED, + UI_RET_REJECTED, + UI_RET_FAILURE +} ui_ret_e; /** * Display address on the device and ask confirmation to export. @@ -21,7 +28,9 @@ typedef void (*action_validate_cb)(bool); * @return 0 if success, negative integer otherwise. * */ -int ui_display_address(void); +ui_ret_e ui_display_address(const uint8_t raw_public_key[PUBKEY_LEN]); + +void ui_display_address_status(ui_ret_e ret); /** * Display transaction information on the device and ask confirmation to sign. @@ -29,4 +38,6 @@ int ui_display_address(void); * @return 0 if success, negative integer otherwise. * */ -int ui_display_transaction(void); +ui_ret_e ui_display_transaction(const transaction_t *transaction); + +void ui_display_transaction_status(ui_ret_e ret); diff --git a/src/ui/nbgl_display_address.c b/src/ui/nbgl_display_address.c index 4b0824cdf..ede924181 100644 --- a/src/ui/nbgl_display_address.c +++ b/src/ui/nbgl_display_address.c @@ -22,55 +22,49 @@ #include "os.h" #include "glyphs.h" -#include "nbgl_use_case.h" -#include "io.h" -#include "bip32.h" +#include "ux_sync.h" #include "format.h" -#include "display.h" #include "constants.h" -#include "../globals.h" -#include "../sw.h" -#include "../address.h" -#include "action/validate.h" -#include "../transaction/types.h" -#include "../menu.h" +#include "display.h" +#include "address.h" +#include "menu.h" -static char g_address[43]; +ui_ret_e ui_display_address(const uint8_t raw_public_key[PUBKEY_LEN]) { + char address_str[43] = {0}; + uint8_t address_bin[ADDRESS_LEN] = {0}; -static void review_choice(bool confirm) { - // Answer, display a status page and go back to main - validate_pubkey(confirm); - if (confirm) { - nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_menu_main); - } else { - nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, ui_menu_main); + if (!address_from_pubkey(raw_public_key, address_bin, sizeof(address_bin))) { + return UI_RET_FAILURE; } -} -int ui_display_address() { - if (G_context.req_type != CONFIRM_ADDRESS || G_context.state != STATE_NONE) { - G_context.state = STATE_NONE; - return io_send_sw(SW_BAD_STATE); + if (format_hex(address_bin, sizeof(address_bin), address_str, sizeof(address_str)) == -1) { + return UI_RET_FAILURE; } - memset(g_address, 0, sizeof(g_address)); - uint8_t address[ADDRESS_LEN] = {0}; - if (!address_from_pubkey(G_context.pk_info.raw_public_key, address, sizeof(address))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + ux_sync_ret_t ret = ux_sync_addressReview(address_str, + NULL, + &ICON_APP_BOILERPLATE, + "Verify BOL address", + NULL); + + if (ret == UX_SYNC_RET_APPROVED) { + return UI_RET_APPROVED; + } else if (ret == UX_SYNC_RET_REJECTED) { + return UI_RET_REJECTED; + } else { + return UI_RET_FAILURE; } +} - if (format_hex(address, sizeof(address), g_address, sizeof(g_address)) == -1) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); +void ui_display_address_status(ui_ret_e ret) { + if (ret == UI_RET_APPROVED) { + ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_VERIFIED); + } else { + ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_REJECTED); } - nbgl_useCaseAddressReview(g_address, - NULL, - &ICON_APP_BOILERPLATE, - "Verify BOL address", - NULL, - review_choice); - return 0; + ui_menu_main(); } #endif diff --git a/src/ui/nbgl_display_transaction.c b/src/ui/nbgl_display_transaction.c index 4f68f8346..8800c9095 100755 --- a/src/ui/nbgl_display_transaction.c +++ b/src/ui/nbgl_display_transaction.c @@ -22,72 +22,47 @@ #include "os.h" #include "glyphs.h" -#include "os_io_seproxyhal.h" -#include "nbgl_use_case.h" -#include "io.h" -#include "bip32.h" +#include "ux_sync.h" #include "format.h" -#include "display.h" #include "constants.h" -#include "../globals.h" -#include "../sw.h" -#include "../address.h" -#include "action/validate.h" -#include "../transaction/types.h" -#include "../menu.h" - -// Buffer where the transaction amount string is written -static char g_amount[30]; -// Buffer where the transaction address string is written -static char g_address[43]; - -static nbgl_contentTagValue_t pairs[2]; -static nbgl_contentTagValueList_t pairList; - -// called when long press button on 3rd page is long-touched or when reject footer is touched -static void review_choice(bool confirm) { - // Answer, display a status page and go back to main - validate_transaction(confirm); - if (confirm) { - nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_menu_main); - } else { - nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_menu_main); - } -} +#include "display.h" +#include "address.h" +#include "menu.h" // Public function to start the transaction review // - Check if the app is in the right state for transaction review -// - Format the amount and address strings in g_amount and g_address buffers +// - Format the amount and address strings in amount_str and address_str buffers // - Display the first screen of the transaction review -int ui_display_transaction() { - if (G_context.req_type != CONFIRM_TRANSACTION || G_context.state != STATE_PARSED) { - G_context.state = STATE_NONE; - return io_send_sw(SW_BAD_STATE); - } +ui_ret_e ui_display_transaction(const transaction_t *transaction) { + // Buffer where the transaction amount string is written + char amount_str[30] = {0}; + // Buffer where the transaction address string is written + char address_str[43] = {0}; - // Format amount and address to g_amount and g_address buffers - memset(g_amount, 0, sizeof(g_amount)); - char amount[30] = {0}; - if (!format_fpu64(amount, - sizeof(amount), - G_context.tx_info.transaction.value, + // Format amount and address to amount_str and address_str buffers + char amount_bin[30] = {0}; + if (!format_fpu64(amount_bin, + sizeof(amount_bin), + transaction->value, EXPONENT_SMALLEST_UNIT)) { - return io_send_sw(SW_DISPLAY_AMOUNT_FAIL); + return UI_RET_FAILURE; } - snprintf(g_amount, sizeof(g_amount), "BOL %.*s", sizeof(amount), amount); - memset(g_address, 0, sizeof(g_address)); + snprintf(amount_str, sizeof(amount_str), "BOL %.*s", sizeof(amount_bin), amount_bin); - if (format_hex(G_context.tx_info.transaction.to, ADDRESS_LEN, g_address, sizeof(g_address)) == + if (format_hex(transaction->to, ADDRESS_LEN, address_str, sizeof(address_str)) == -1) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + return UI_RET_FAILURE; } + nbgl_layoutTagValue_t pairs[2] = {0}; + nbgl_layoutTagValueList_t pairList = {0}; + // Setup data to display pairs[0].item = "Amount"; - pairs[0].value = g_amount; + pairs[0].value = amount_str; pairs[1].item = "Address"; - pairs[1].value = g_address; + pairs[1].value = address_str; // Setup list pairList.nbMaxLinesForValue = 0; @@ -95,18 +70,35 @@ int ui_display_transaction() { pairList.pairs = pairs; // Start review - nbgl_useCaseReview(TYPE_TRANSACTION, - &pairList, - &ICON_APP_BOILERPLATE, - "Review transaction" + ux_sync_ret_t ret = ux_sync_review( + TYPE_TRANSACTION, + &pairList, + &ICON_APP_BOILERPLATE, + "Review transaction" #ifndef TARGET_NANOS - "\nto send BOL" + "\nto send BOL" #endif - , - NULL, - "Sign transaction\nto send BOL", - review_choice); - return 0; + , + NULL, + "Sign transaction\nto send BOL"); + + if (ret == UX_SYNC_RET_APPROVED) { + return UI_RET_APPROVED; + } else if (ret == UX_SYNC_RET_REJECTED) { + return UI_RET_REJECTED; + } else { + return UI_RET_FAILURE; + } +} + +void ui_display_transaction_status(ui_ret_e ret) { + if (ret == UI_RET_APPROVED) { + ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_SIGNED); + } else { + ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_REJECTED); + } + + ui_menu_main(); } #endif