From 8fea9ad90d02207da18635ff92103177b6f6211d Mon Sep 17 00:00:00 2001 From: abenso Date: Mon, 2 Sep 2024 17:03:04 -0300 Subject: [PATCH] add transfer handler --- app/src/apdu_handler_legacy.c | 142 ++++++++++++++++++-- app/src/apdu_handler_legacy.h | 5 +- tests_zemu/package.json | 2 +- tests_zemu/tests/legacy.test.ts | 10 +- tests_zemu/tests/testscases/transactions.ts | 1 + tests_zemu/tests/transactions.test.ts | 2 +- 6 files changed, 146 insertions(+), 16 deletions(-) diff --git a/app/src/apdu_handler_legacy.c b/app/src/apdu_handler_legacy.c index b4f2604..efaee41 100644 --- a/app/src/apdu_handler_legacy.c +++ b/app/src/apdu_handler_legacy.c @@ -25,6 +25,11 @@ static bool tx_initialized = false; static uint32_t payload_length = 0; static uint32_t hdpath_length = 0; static tx_type_t tx_type = tx_type_json; +static uint8_t local_data_len = 0; +static uint8_t local_data[LEGACY_LOCAL_BUFFER_SIZE]; +static uint8_t items = 0; +static uint8_t item_len = 0; +static bool check_item_len = false; __Z_INLINE void legacy_app_sign() { const uint8_t *message = tx_get_buffer(); @@ -55,7 +60,7 @@ void legacy_app_reply_address() { io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, action_addrResponseLen + 2); } -void legacy_extractHDPath(uint8_t *buffer, uint32_t rx, uint32_t offset) { +void legacy_extractHDPath(uint8_t *buffer, uint32_t rx, uint32_t offset, bool check_len) { if (rx < offset) { THROW(APDU_CODE_WRONG_LENGTH); } @@ -68,8 +73,10 @@ void legacy_extractHDPath(uint8_t *buffer, uint32_t rx, uint32_t offset) { ZEMU_LOGF(50, "hdPathLen: %d\n", hdPathLen); ZEMU_LOGF(50, "offset_hdpath_data: %d\n", offset_hdpath_data); - if (rx - offset_hdpath_data != hdPathLen) { - THROW(APDU_CODE_WRONG_LENGTH); + if (check_len) { + if (rx - offset_hdpath_data != hdPathLen) { + THROW(APDU_CODE_WRONG_LENGTH); + } } MEMCPY(hdPath, buffer + offset_hdpath_data, hdPathLen); @@ -114,7 +121,7 @@ uint32_t legacy_check_request(volatile uint32_t *tx) { } // get hdpath - legacy_extractHDPath(tx_buffer, tx_buffer_length, payload_length); + legacy_extractHDPath(tx_buffer, tx_buffer_length, payload_length, true); // verify sizes if (tx_buffer_length != hdpath_length + payload_length) { @@ -127,8 +134,22 @@ uint32_t legacy_check_request(volatile uint32_t *tx) { return tx_buffer_length - hdpath_length; } +void legacy_append_data(uint8_t *buffer, uint32_t len) { + uint32_t added = tx_append(buffer, len); + + char print[200] = {0}; + array_to_hexstr(print, sizeof(print), buffer, len); + ZEMU_LOGF(200, "legacy_append_data: %s\n", print); + + if (added != len) { + tx_reset(); + tx_initialized = false; + THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); + } +} + void legacy_handleGetAddr(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx, const uint8_t requireConfirmation) { - legacy_extractHDPath(G_io_apdu_buffer, rx, LEGACY_OFFSET_HDPATH_LEN); + legacy_extractHDPath(G_io_apdu_buffer, rx, LEGACY_OFFSET_HDPATH_SIZE, true); zxerr_t zxerr = app_fill_address(); if (zxerr != zxerr_ok) { @@ -178,7 +199,8 @@ bool legacy_process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx, bool ha tx_reset(); tx_initialized = true; } - + + // TODO: use legacy_append_data uint32_t added = tx_append(&(G_io_apdu_buffer[offset]), payload_size); if (added != payload_size) { tx_reset(); @@ -195,6 +217,105 @@ bool legacy_process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx, bool ha return false; } +static uint32_t legacy_initialize_transfer() { + legacy_extractHDPath(G_io_apdu_buffer, LEGACY_OFFSET_HDPATH_SIZE + 1, LEGACY_OFFSET_HDPATH_SIZE, false); + + tx_initialize(); + tx_reset(); + tx_initialized = true; + + uint32_t offset = hdpath_length + LEGACY_OFFSET_HDPATH_SIZE; + + // save tx_type + legacy_append_data(&G_io_apdu_buffer[offset], 1); + items = 0; + + return offset; +} + +static uint32_t legacy_process_existing_transfer(uint32_t *payload_size) { + legacy_append_data(local_data, local_data_len); + + uint32_t offset = LEGACY_HEADER_LENGTH; + + if (check_item_len) { + *payload_size = G_io_apdu_buffer[offset]; + legacy_append_data(&G_io_apdu_buffer[offset], *payload_size + 1); + } else { + if (item_len <= local_data_len) { + THROW(APDU_CODE_DATA_INVALID); + } + *payload_size = item_len - local_data_len; + legacy_append_data(&G_io_apdu_buffer[offset], *payload_size); + offset--; + } + + items++; + local_data_len = 0; + + return offset; +} + +static void legacy_handle_overflow(uint32_t offset, uint32_t payload_size) { + check_item_len = false; + item_len = payload_size + 1; + + if (offset > LEGACY_FULL_CHUNK_SIZE) { + THROW(APDU_CODE_DATA_INVALID); + } + local_data_len = LEGACY_FULL_CHUNK_SIZE - offset; + if (local_data_len >= LEGACY_LOCAL_BUFFER_SIZE) { + THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); + } + MEMCPY(local_data, &G_io_apdu_buffer[offset], local_data_len); +} + +bool legacy_process_transfer_chunk(uint32_t rx) { + if (rx < LEGACY_HEADER_LENGTH) { + tx_initialized = false; + THROW(APDU_CODE_WRONG_LENGTH); + } + + uint32_t payload_size = 0; + uint32_t offset = tx_initialized ? legacy_process_existing_transfer(&payload_size) : legacy_initialize_transfer(); + + while (offset < rx) { + offset += payload_size + 1; + + if (offset > rx) { + THROW(APDU_CODE_DATA_INVALID); + } + + if (offset == LEGACY_FULL_CHUNK_SIZE) { + check_item_len = true; + return false; + } + + payload_size = G_io_apdu_buffer[offset]; + uint32_t next_offset = offset + payload_size + 1; + + if (next_offset > LEGACY_FULL_CHUNK_SIZE) { + legacy_handle_overflow(offset, payload_size); + return false; + } + + legacy_append_data(&G_io_apdu_buffer[offset], payload_size + 1); + + if (++items > LEGACY_TRANSFER_NUM_ITEMS) { + THROW(APDU_CODE_DATA_INVALID); + } + + if (next_offset >= rx && next_offset != LEGACY_FULL_CHUNK_SIZE) { + if (items != LEGACY_TRANSFER_NUM_ITEMS) { + THROW(APDU_CODE_DATA_INVALID); + } + return true; + } + } + + THROW(APDU_CODE_DATA_INVALID); +} + // bytes: | 1 | 1 | 1 | 1 | 1 | 4 | payload_len | 1 | 4*hdpath_qty | // data: | CLA | INS | P1 | P2 | chunk_len | payload_len | payload | hdpath_qty | hdpath_data | void legacy_handleSignTransaction(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { @@ -244,13 +365,16 @@ void legacy_handleSignHash(volatile uint32_t *flags, volatile uint32_t *tx, uint *flags |= IO_ASYNCH_REPLY; } +// bytes: | 1 | 1 | 1 | 1 | 1 | 4*hdpath_qty | n | +// data: | CLA | INS | P1 | P2 | hdpath_qty | hdpath_data | payload | void legacy_handleSignTransferTx(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { zemu_log("handleSignLegacyTransferTx\n"); - if (!legacy_process_chunk(tx, rx, false, 32)) { + if (!legacy_process_transfer_chunk(rx)) { THROW(APDU_CODE_OK); } - uint32_t buffer_length = legacy_check_request(tx); + uint32_t buffer_length = tx_get_buffer_length(); + ZEMU_LOGF(50, "buffer_length: %d\n", buffer_length); const char *error_msg = tx_parse(buffer_length, tx_type_transaction); tx_type = tx_type_transaction; @@ -262,6 +386,8 @@ void legacy_handleSignTransferTx(volatile uint32_t *flags, volatile uint32_t *tx THROW(APDU_CODE_DATA_INVALID); } + THROW(APDU_CODE_OK); + view_review_init(tx_getItem, tx_getNumItems, legacy_app_sign); view_review_show(REVIEW_TXN); *flags |= IO_ASYNCH_REPLY; diff --git a/app/src/apdu_handler_legacy.h b/app/src/apdu_handler_legacy.h index 533c415..5dde436 100644 --- a/app/src/apdu_handler_legacy.h +++ b/app/src/apdu_handler_legacy.h @@ -26,9 +26,12 @@ extern "C" { #define LEGACY_CHUNK_SIZE 230 #define LEGACY_HEADER_LENGTH 5 +#define LEGACY_FULL_CHUNK_SIZE (LEGACY_CHUNK_SIZE + LEGACY_HEADER_LENGTH) #define LEGACY_PAYLOAD_LEN_BYTES 4 -#define LEGACY_OFFSET_HDPATH_LEN 5 +#define LEGACY_OFFSET_HDPATH_SIZE 5 #define LEGACY_HDPATH_LEN_BYTES 1 +#define LEGACY_TRANSFER_NUM_ITEMS 12 +#define LEGACY_LOCAL_BUFFER_SIZE 33 void legacy_handleGetAddr(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx, const uint8_t requireConfirmation); void legacy_handleSignTransaction(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx); diff --git a/tests_zemu/package.json b/tests_zemu/package.json index 75972e0..6bccba7 100644 --- a/tests_zemu/package.json +++ b/tests_zemu/package.json @@ -53,6 +53,6 @@ "ts-jest": "^29.2.3", "ts-node": "^10.9.2", "typescript": "^5.5.4", - "hw-app-kda": "link:hw-app-kda" + "hw-app-kda": "git+https://github.com/obsidiansystems/hw-app-kda" } } diff --git a/tests_zemu/tests/legacy.test.ts b/tests_zemu/tests/legacy.test.ts index 0b2e99a..efd5362 100644 --- a/tests_zemu/tests/legacy.test.ts +++ b/tests_zemu/tests/legacy.test.ts @@ -23,7 +23,6 @@ import { JSON_TEST_CASES } from './testscases/json' import { HASH_TEST_CASES } from './testscases/hash' import { TRANSACTIONS_TEST_CASES } from './testscases/transactions' import { APDU_TEST_CASES } from './testscases/legacy_apdu' - // @ts-expect-error import ed25519 from 'ed25519-supercop' @@ -106,7 +105,7 @@ test.concurrent.each(models)('legacy show address - reject', async function (m) }) describe.each(JSON_TEST_CASES)('Tx transactions', function (data) { - test.only.each(models)('sign json', async function (m) { + test.concurrent.each(models)('sign json', async function (m) { const sim = new Zemu(m.path) try { await sim.start({ ...defaultOptions, model: m.name }) @@ -183,18 +182,19 @@ describe.each(HASH_TEST_CASES)('Hash transactions', function (data) { }) describe.each(TRANSACTIONS_TEST_CASES)('Tx transactions', function (data) { - test.concurrent.each(models)('sign', async function (m) { + test.only.each(models)('sign', async function (m) { const sim = new Zemu(m.path) try { await sim.start({ ...defaultOptions, model: m.name }) const app = new Kda(sim.getTransport()); - const responseAddr = await app.getPublicKey(data.path) + const responseAddr = await app.getPublicKey(data.txParams.path) const pubKey = responseAddr.publicKey console.log(pubKey) + // do not wait here... we need to navigate - const signatureRequest = app.signTransferTx(data.path, data) + let signatureRequest = await app["signTransferTx"](data.txParams); // // Wait until we are not in the main menu // await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) diff --git a/tests_zemu/tests/testscases/transactions.ts b/tests_zemu/tests/testscases/transactions.ts index 0d88a75..9e1ae7a 100644 --- a/tests_zemu/tests/testscases/transactions.ts +++ b/tests_zemu/tests/testscases/transactions.ts @@ -1,5 +1,6 @@ import { PATH } from '../common' import { TransferTxType } from '@zondax/ledger-kadena' +import Kda from "hw-app-kda"; export const TRANSACTIONS_TEST_CASES = [ { diff --git a/tests_zemu/tests/transactions.test.ts b/tests_zemu/tests/transactions.test.ts index 533d447..eba517f 100644 --- a/tests_zemu/tests/transactions.test.ts +++ b/tests_zemu/tests/transactions.test.ts @@ -78,7 +78,7 @@ describe.each(TRANSACTIONS_TEST_CASES)('Tx transactions', function (data) { await sim.start({ ...defaultOptions, model: m.name }) const app = new KadenaApp(sim.getTransport()) - const responseAddr = await app.getAddressAndPubKey(data.path) + const responseAddr = await app.getAddressAndPubKey(data.txParams.path) const pubKey = responseAddr.pubkey var signatureRequest = null;