From 2465d7a08f78015d9c4b4a033d5ddba7f4ef7bbd Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Tue, 26 Nov 2024 16:11:01 +0530 Subject: [PATCH 01/16] fix(app): Btc parser out of bounds check --- apps/btc_family/btc_txn.c | 2 +- apps/btc_family/btc_txn_helpers.c | 40 +++++++++++++++++-------------- apps/btc_family/btc_txn_helpers.h | 8 +++---- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/apps/btc_family/btc_txn.c b/apps/btc_family/btc_txn.c index 38033939f..d066b19c2 100644 --- a/apps/btc_family/btc_txn.c +++ b/apps/btc_family/btc_txn.c @@ -435,7 +435,7 @@ static bool fetch_valid_input(btc_query_t *query) { verify_input_data.size_last_chunk = total_size % CHUNK_SIZE; if (verify_input_data.size_last_chunk < 4) { - verify_input_data.isLocktimeSplit = true; + verify_input_data.is_locktime_split = true; } } diff --git a/apps/btc_family/btc_txn_helpers.c b/apps/btc_family/btc_txn_helpers.c index 7e40adef3..772cf2acb 100644 --- a/apps/btc_family/btc_txn_helpers.c +++ b/apps/btc_family/btc_txn_helpers.c @@ -339,7 +339,7 @@ static void update_hash(btc_verify_input_t *verify_input_data, } switch (update) { case FIRST_CHUNK_HASH: { - if (verify_input_data->isSegwit) { + if (verify_input_data->is_segwit) { sha256_Update(&(verify_input_data->sha_256_ctx), raw_txn_chunk, 4); // skip marker and flag sha256_Update( @@ -361,7 +361,7 @@ static void update_hash(btc_verify_input_t *verify_input_data, static void update_locktime(btc_verify_input_t *verify_input_data, const uint8_t *raw_txn_chunk, int chunk_index) { - if (verify_input_data->isLocktimeSplit) { + if (verify_input_data->is_locktime_split) { // last second chunk if (chunk_index + 2 == verify_input_data->chunk_total) { memcpy( @@ -374,7 +374,7 @@ static void update_locktime(btc_verify_input_t *verify_input_data, verify_input_data->locktime + 4 - verify_input_data->size_last_chunk, raw_txn_chunk, verify_input_data->size_last_chunk); - verify_input_data->hasLocktime = true; + verify_input_data->has_locktime = true; return; } else { // wait for subsequent chunks @@ -384,7 +384,7 @@ static void update_locktime(btc_verify_input_t *verify_input_data, memcpy(verify_input_data->locktime, raw_txn_chunk + verify_input_data->size_last_chunk - 4, 4); - verify_input_data->hasLocktime = true; + verify_input_data->has_locktime = true; } else { // wait for subsequent chunks return; @@ -425,16 +425,18 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, // ignore network version (4-bytes), skip marker & flag (in segwit) offset += (raw_txn_chunk[4] == 0 ? 6 : 4); if (6 == offset) { - verify_input_data->isSegwit = true; + verify_input_data->is_segwit = true; } - // store the number of inputs in the raw_txn - verify_input_data->count = raw_txn_chunk[offset++]; // TODO: Improve varint decode. // size of variable containing script size and ip-count/op-count // varies (1-9 Bytes) depending on its value. // refer: // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer + verify_input_data->count = + raw_txn_chunk[offset++]; ///< store the number of inputs in the + ///< raw_txn + verify_input_data->parsetype = INPUT; sha256_Init(&(verify_input_data->sha_256_ctx)); } else { @@ -447,7 +449,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, input_case ip_case = verify_input_data->input_parse; switch (ip_case) { case PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE: { - if (offset + 36 > CHUNK_SIZE) { + if (offset + 36 >= CHUNK_SIZE) { verify_input_data->prev_offset = (offset + 36) - CHUNK_SIZE; update_hash( verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); @@ -460,7 +462,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, case SCRIPT_LENGTH_CASE: { int64_t script_length = varint_decode(raw_txn_chunk, &offset); - if (offset + script_length + 1 + 4 > CHUNK_SIZE) { + if (offset + script_length + 1 + 4 >= CHUNK_SIZE) { verify_input_data->prev_offset = (offset + script_length + 1 + 4) - CHUNK_SIZE; update_hash( @@ -485,9 +487,11 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, } case OP_COUNT: { - if (offset + 1 > CHUNK_SIZE) { + if (offset + 1 >= CHUNK_SIZE) { // reset prev offset - verify_input_data->prev_offset = -1; + verify_input_data->prev_offset = + offset - CHUNK_SIZE; ///< Did not add +1 as returning back to + ///< this stage to read op count update_hash(verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); return 4; } else { @@ -503,7 +507,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, switch (op_case) { case VALUE_CASE: { if (verify_input_data->output_index == input->prev_output_index) { - if (offset + 8 > CHUNK_SIZE) { + if (offset + 8 >= CHUNK_SIZE) { verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE; memcpy(verify_input_data->value, raw_txn_chunk + offset, @@ -511,13 +515,13 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, update_hash( verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); verify_input_data->output_parse = VALUE_SPLIT_CASE; - verify_input_data->isSplit = 1; + verify_input_data->is_split = 1; return 4; } else { memcpy(verify_input_data->value, raw_txn_chunk + offset, 8); } } - if (offset + 8 > CHUNK_SIZE) { + if (offset + 8 >= CHUNK_SIZE) { verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE; update_hash( verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); @@ -529,17 +533,17 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, } case VALUE_SPLIT_CASE: { - if (verify_input_data->isSplit) { + if (verify_input_data->is_split) { memcpy(verify_input_data->value + (8 - offset), raw_txn_chunk, offset); verify_input_data->output_parse = SCRIPT_PUBKEY_CASE; - verify_input_data->isSplit = 0; + verify_input_data->is_split = 0; } } case SCRIPT_PUBKEY_CASE: { - if (offset + raw_txn_chunk[offset] + 1 > CHUNK_SIZE) { + if (offset + raw_txn_chunk[offset] + 1 >= CHUNK_SIZE) { verify_input_data->prev_offset = (offset + raw_txn_chunk[offset] + 1) - CHUNK_SIZE; update_hash( @@ -565,7 +569,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk, case LOCK_TIME: { update_locktime(verify_input_data, raw_txn_chunk, chunk_index); - if (false == verify_input_data->hasLocktime) { + if (false == verify_input_data->has_locktime) { return 4; } sha256_Update( diff --git a/apps/btc_family/btc_txn_helpers.h b/apps/btc_family/btc_txn_helpers.h index dd5149eca..3555b0139 100644 --- a/apps/btc_family/btc_txn_helpers.h +++ b/apps/btc_family/btc_txn_helpers.h @@ -51,10 +51,10 @@ typedef struct btc_verify_input { parse_type parsetype; input_case input_parse; output_case output_parse; - bool isSegwit; - bool isSplit; - bool hasLocktime; - bool isLocktimeSplit; + bool is_segwit; + bool is_split; + bool has_locktime; + bool is_locktime_split; int32_t size_last_chunk; uint8_t value[8]; uint8_t locktime[4]; From 09a50101aaf6139625d8fa343fd6685131e89b04 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Tue, 26 Nov 2024 16:13:27 +0530 Subject: [PATCH 02/16] chore: Version bump v0.6.1281(patch++) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index db2b19812..caeb09334 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:005:000 +firmware version=000:006:005:001 hardware version=000:001:000:000 magic number=45227A01 From 84a3d678b7ca5c1dda877d9c5a7847f55c10eb6d Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Mon, 21 Oct 2024 18:45:01 -0800 Subject: [PATCH 03/16] feat: Core proto options --- common/proto-options/icp/core.options | 1 + 1 file changed, 1 insertion(+) create mode 100644 common/proto-options/icp/core.options diff --git a/common/proto-options/icp/core.options b/common/proto-options/icp/core.options new file mode 100644 index 000000000..22f6f23ff --- /dev/null +++ b/common/proto-options/icp/core.options @@ -0,0 +1 @@ +# Options for file common/cypherock-common/proto/icp/core.proto From e540d12a1998b25f0adf1a5dfe2d0610cb5c904e Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Tue, 22 Oct 2024 18:45:01 -0800 Subject: [PATCH 04/16] feat: Include common submodule --- common/cypherock-common | 2 +- common/proto-options/icp/error.options | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 common/proto-options/icp/error.options diff --git a/common/cypherock-common b/common/cypherock-common index 19912e8d0..fdb99fdb3 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 19912e8d052064c477d6b11ca2e26efa4c483bfe +Subproject commit fdb99fdb39813727561dad33b6f0aa6493818766 diff --git a/common/proto-options/icp/error.options b/common/proto-options/icp/error.options new file mode 100644 index 000000000..77f8f4ec7 --- /dev/null +++ b/common/proto-options/icp/error.options @@ -0,0 +1 @@ +# Options for file common/cypherock-common/proto/icp/error.proto From f8fb00b279a5af500f41183a245bb62b531a2112 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 23 Oct 2024 18:45:01 -0800 Subject: [PATCH 05/16] feat: Options file for get public key proto --- common/proto-options/icp/get_public_key.options | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 common/proto-options/icp/get_public_key.options diff --git a/common/proto-options/icp/get_public_key.options b/common/proto-options/icp/get_public_key.options new file mode 100644 index 000000000..b306aae72 --- /dev/null +++ b/common/proto-options/icp/get_public_key.options @@ -0,0 +1,5 @@ +# Options for file common/cypherock-common/proto/icp/get_public_key.proto +icp.GetPublicKeysDerivationPath.path type:FT_STATIC max_count:5 fixed_length:true +icp.GetPublicKeysIntiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +icp.GetPublicKeysIntiateRequest.derivation_paths type:FT_STATIC max_count:100 fixed_length:true +icp.GetPublicKeysResultResponse.public_keys type:FT_STATIC max_size:33 max_count:10 fixed_length:true From f30b15077b48d2655648520dac80295d36bc5405 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Thu, 24 Oct 2024 18:45:01 -0800 Subject: [PATCH 06/16] feat: Options file for generic txn signing --- common/proto-options/icp/sign_txn.options | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 common/proto-options/icp/sign_txn.options diff --git a/common/proto-options/icp/sign_txn.options b/common/proto-options/icp/sign_txn.options new file mode 100644 index 000000000..cce98e4c4 --- /dev/null +++ b/common/proto-options/icp/sign_txn.options @@ -0,0 +1,4 @@ +# Options for file common/cypherock-common/proto/icp/sign_txn.proto +icp.SignTxnInitiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +icp.SignTxnInitiateRequest.derivation_path type:FT_STATIC max_count:5 fixed_length:true +icp.SignTxnSignatureResponse.signature type:FT_STATIC max_size:72 fixed_length:false From 2f388ed15479a97913cb38a792f7a8def6ff5e72 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Mon, 28 Oct 2024 18:45:01 -0800 Subject: [PATCH 07/16] feat: Define icp apis for device --- apps/icp_app/icp_api.c | 196 +++++++++++++++++++++++++++++++++++++++++ apps/icp_app/icp_api.h | 113 ++++++++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 apps/icp_app/icp_api.c create mode 100644 apps/icp_app/icp_api.h diff --git a/apps/icp_app/icp_api.c b/apps/icp_app/icp_api.c new file mode 100644 index 000000000..33f50768c --- /dev/null +++ b/apps/icp_app/icp_api.c @@ -0,0 +1,196 @@ +/** + * @file icp_api.c + * @author Cypherock X1 Team + * @brief Defines helpers apis for ICP app. + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2024 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "icp_api.h" + +#include +#include + +#include "common_error.h" +#include "core_api.h" +#include "events.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +bool decode_icp_query(const uint8_t *data, + uint16_t data_size, + icp_query_t *query_out) { + if (NULL == data || NULL == query_out || 0 == data_size) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_DECODING_FAILED); + return false; + } + + // zeroise for safety from garbage in the query reference + memzero(query_out, sizeof(icp_query_t)); + + /* Create a stream that reads from the buffer. */ + pb_istream_t stream = pb_istream_from_buffer(data, data_size); + + /* Now we are ready to decode the message. */ + bool status = pb_decode(&stream, ICP_QUERY_FIELDS, query_out); + + /* Send error to host if status is false*/ + if (false == status) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_DECODING_FAILED); + } + + return status; +} + +bool encode_icp_result(const icp_result_t *result, + uint8_t *buffer, + uint16_t max_buffer_len, + size_t *bytes_written_out) { + if (NULL == result || NULL == buffer || NULL == bytes_written_out) + return false; + + /* Create a stream that will write to our buffer. */ + pb_ostream_t stream = pb_ostream_from_buffer(buffer, max_buffer_len); + + /* Now we are ready to encode the message! */ + bool status = pb_encode(&stream, ICP_RESULT_FIELDS, result); + + if (true == status) { + *bytes_written_out = stream.bytes_written; + } + + return status; +} + +bool check_icp_query(const icp_query_t *query, pb_size_t exp_query_tag) { + if ((NULL == query) || (exp_query_tag != query->which_request)) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + return false; + } + return true; +} + +icp_result_t init_icp_result(pb_size_t result_tag) { + icp_result_t result = ICP_RESULT_INIT_ZERO; + result.which_response = result_tag; + return result; +} + +void icp_send_error(pb_size_t which_error, uint32_t error_code) { + icp_result_t result = init_icp_result(ICP_RESULT_COMMON_ERROR_TAG); + result.common_error = init_common_error(which_error, error_code); + icp_send_result(&result); +} + +void icp_send_result(const icp_result_t *result) { + // TODO: Set all option files + uint8_t buffer[1700] = {0}; + size_t bytes_encoded = 0; + ASSERT(encode_icp_result(result, buffer, sizeof(buffer), &bytes_encoded)); + send_response_to_host(&buffer[0], bytes_encoded); +} + +bool icp_get_query(icp_query_t *query, pb_size_t exp_query_tag) { + evt_status_t event = get_events(EVENT_CONFIG_USB, MAX_INACTIVITY_TIMEOUT); + + if (true == event.p0_event.flag) { + return false; + } + + if (!decode_icp_query( + event.usb_event.p_msg, event.usb_event.msg_size, query)) { + return false; + } + + if (!check_icp_query(query, exp_query_tag)) { + return false; + } + + return true; +} diff --git a/apps/icp_app/icp_api.h b/apps/icp_app/icp_api.h new file mode 100644 index 000000000..7079aa347 --- /dev/null +++ b/apps/icp_app/icp_api.h @@ -0,0 +1,113 @@ +/** + * @file icp_api.h + * @author Cypherock X1 Team + * @brief Header file to export some helper functions for the ICP app + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef ICP_API_H +#define ICP_API_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief API to decode query from host with `ICP_QUERY_FIELDS` + * + * @param[in] data: PB encoded bytestream received from host + * @param[in] data_size: size of pb encoded bytestream + * @param[out] query_out: @ref icp_query_t obj to copy the decoded result to + * @return bool True if decoding was successful, else false + */ +bool decode_icp_query(const uint8_t *data, + uint16_t data_size, + icp_query_t *query_out); + +/** + * @brief Encodes the ICP result with `ICP_RESULT_FIELDS` to byte-stream + * + * @param[in] result: object of populated @ref icp_result_t to be encoded + * @param[out] buffer: buffer to fill byte-stream into + * @param[in] max_buffer_len: Max length allowed for writing bytestream to + * buffer + * @param[out] bytes_written_out: bytes written to bytestream + * @return bool True if decoding was successful, else false + */ +bool encode_icp_result(const icp_result_t *result, + uint8_t *buffer, + uint16_t max_buffer_len, + size_t *bytes_written_out); + +/** + * @brief This API checks if the `which_request` field of the query of type + * `icp_query_t` matches against the expected tag. + * + * @param query The query of type `icp_query_t` to be checked + * @param exp_query_tag The expected tag of the query + * @return true If the query tag matches the expected tag + * @return false If the query tag does not match the expected tag + */ +bool check_icp_query(const icp_query_t *query, pb_size_t exp_query_tag); + +/** + * @brief Returns zero initialized object of type + * icp_result_t result_tag set in result.which_response field + * + * @param result_tag Result tag to be set in the icp_result_t result + * @return icp_result_t Result object of type icp_result_t + */ +icp_result_t init_icp_result(pb_size_t result_tag); + +/** + * @brief Send the error to the host. + * + * @param which_error The error type to be sent + * @param error_code The error code to sent to the host + */ +void icp_send_error(pb_size_t which_error, uint32_t error_code); + +/** + * @brief This API encodes icp_result_t in protobuf structure. + * @details If the encoding is successful, then it sends the corresponding + * result to the host. + * + * The function ASSERTs the result of encode_icp_result internally. + * + * @param result The result which needs to be sent to the host. + */ +void icp_send_result(const icp_result_t *result); + +/** + * @brief This API receives request of type icp_query_t of type + * exp_query_tag from the host. + * + * @param query The reference to which the query needs to be populated + * @param exp_query_tag The expected tag of the query + * @return true If the query was recieved from the host matching the tag + * @return false If the request timed out or the recieved request did not match + * the tag + */ +bool icp_get_query(icp_query_t *query, pb_size_t exp_query_tag); + +#endif From 6b2e279aed6b008f99dae425deaab7325c76027c Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 30 Oct 2024 18:45:01 -0800 Subject: [PATCH 08/16] feat: Define context for icp --- apps/icp_app/icp_context.h | 117 +++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 apps/icp_app/icp_context.h diff --git a/apps/icp_app/icp_context.h b/apps/icp_app/icp_context.h new file mode 100644 index 000000000..d3bb431a7 --- /dev/null +++ b/apps/icp_app/icp_context.h @@ -0,0 +1,117 @@ +/** + * @file icp_context.h + * @author Cypherock X1 Team + * @brief Header file defining typedefs and MACROS for the ICP app + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef ICP_CONTEXT_H +#define ICP_CONTEXT_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define ICP_NAME "ICP" +#define ICP_LUNIT "ICP" +#define ICP_PUB_KEY_SIZE 33 +#define ICP_PREFIXED_ACCOUNT_ID_LENGTH 21 +#define ICP_ACCOUNT_ADDRESS_LENGTH 34 +#define ICP_BASE58_DIGITS_ORDERED \ + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz" + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +// TODO: Populate structure for ICP +typedef struct { +} icp_config_t; + +typedef enum { + INT16 = 1, + INT32 = 2, + HASH256 = 5, + AMOUNT = 6, + BLOB = 7, + ACCOUNT = 8, + VECTOR256 = 19 +} TYPECODE; + +typedef enum { TransactionType = 2 } INT16FIELDCODE; + +typedef enum { + Flags = 2, + Sequence = 4, + DestinationTag = 14, + OfferSequence = 25, + LastLedgerSequence = 27, + CancelAfter = 36, + FinishAfter = 37 +} INT132FIELDCODE; + +typedef enum { + NFTokenId = 10, +} HASH256FIELDCODE; + +typedef enum { + Amount = 1, + LimitAmount = 3, + Fee = 8, +} AMOUNTFIELDCODE; + +typedef enum { + SigningPubKey = 3, + TxnSignature = 4, + Fulfillment = 16, + Condition = 17 +} BLOBFIELDCODE; + +typedef enum { Account = 1, Owner = 2, Destination = 3 } ACCOUNTFIELDCODE; + +typedef enum { NFTokenOffers = 4 } VECTOR256FILEDCODE; + +// See +// https://github.com/ICPLF/icpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json +typedef enum { + no_type = -1, + payment = 0, + EscrowCreate = 1, + EscrowFinish = 2, + EscrowCancel = 4, + TrustSet = 20, + NFTokenBurn = 26, + NFTokenCreateOffer = 27, + NFTokenCancelOffer = 28, + NFTokenAcceptOffer = 29 +} TRANSACTIONTYPE; + +typedef struct { + uint16_t TransactionType; + uint32_t Flags; + uint32_t Sequence; + bool hasDestinationTag; + uint32_t DestinationTag; // optional + uint32_t LastLedgerSequence; + uint64_t Amount; + uint64_t Fee; + uint8_t SigningPubKey[33]; + uint8_t Account[20]; + uint8_t Destination[20]; +} icp_unsigned_txn; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +#endif /* ICP_CONTEXT_H */ From 728c62493a71d5ac466c03e5ac92e3d2d45ce3c3 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Fri, 1 Nov 2024 18:45:01 -0800 Subject: [PATCH 09/16] feat: Create icp helpers --- apps/icp_app/icp_helpers.c | 112 +++++++++++++++++++++++++++++++++++++ apps/icp_app/icp_helpers.h | 61 ++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 apps/icp_app/icp_helpers.c create mode 100644 apps/icp_app/icp_helpers.h diff --git a/apps/icp_app/icp_helpers.c b/apps/icp_app/icp_helpers.c new file mode 100644 index 000000000..b3b466460 --- /dev/null +++ b/apps/icp_app/icp_helpers.c @@ -0,0 +1,112 @@ +/** + * @file icp_helpers.c + * @author Cypherock X1 Team + * @brief Utilities specific to Icp chains + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2024 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "icp_helpers.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +bool icp_derivation_path_guard(const uint32_t *path, uint8_t levels) { + bool status = false; + if (levels != ICP_IMPLICIT_ACCOUNT_DEPTH) { + return status; + } + + uint32_t purpose = path[0], coin = path[1], account = path[2], + change = path[3], address = path[4]; + + // m/44'/144'/0'/0/i + status = (ICP_PURPOSE_INDEX == purpose && ICP_COIN_INDEX == coin && + ICP_ACCOUNT_INDEX == account && ICP_CHANGE_INDEX == change && + is_non_hardened(address)); + + return status; +} diff --git a/apps/icp_app/icp_helpers.h b/apps/icp_app/icp_helpers.h new file mode 100644 index 000000000..361940b91 --- /dev/null +++ b/apps/icp_app/icp_helpers.h @@ -0,0 +1,61 @@ +/** + * @file icp_helpers.h + * @author Cypherock X1 Team + * @brief Utilities api definitions for ICP chains + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef ICP_HELPERS_H +#define ICP_HELPERS_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include +#include + +#include "coin_utils.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +#define ICP_IMPLICIT_ACCOUNT_DEPTH 5 + +#define ICP_PURPOSE_INDEX 0x8000002C // 44' +#define ICP_COIN_INDEX 0x80000090 // 144' +#define ICP_ACCOUNT_INDEX 0x80000000 // 0' +#define ICP_CHANGE_INDEX 0x00000000 // 0 + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Verifies the derivation path. + * @details The derivation depth is fixed at level 5. So if the depth level != + * 5, then this function return false indicating invalid derivation path. The + * function supports checking derivation paths for HD wallets Types of + * derivations: address: m/44'/144'/0'/0/i + * + * @param[in] path The derivation path as an uint32 array + * @param[in] levels The number of levels in the derivation path + * + * @return bool Indicates if the provided derivation path is valid + * @retval true if the derivation path is valid + * @retval false otherwise + */ +bool icp_derivation_path_guard(const uint32_t *path, uint8_t levels); + +#endif // ICP_HELPERS_H From e9cf369b5e4ab9242aca7643c84269b5015b51e0 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Tue, 5 Nov 2024 18:45:01 -0800 Subject: [PATCH 10/16] feat: Create private headers --- apps/icp_app/icp_priv.h | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 apps/icp_app/icp_priv.h diff --git a/apps/icp_app/icp_priv.h b/apps/icp_app/icp_priv.h new file mode 100644 index 000000000..e7bea696a --- /dev/null +++ b/apps/icp_app/icp_priv.h @@ -0,0 +1,66 @@ +/** + * @file icp_priv.h + * @author Cypherock X1 Team + * @brief Support for icp app internal operations + * This file is defined to separate ICP's internal use + * functions, flows, common APIs + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef ICP_PRIV_H +#define ICP_PRIV_H +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +#include "icp_context.h" + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ +typedef struct { + /** + * The structure holds the wallet information of the transaction. + * @note Populated by icp_handle_initiate_query() + */ + icp_sign_txn_initiate_request_t init_info; + + // remembers the allocated buffer for holding complete unsigned transaction + uint8_t *transaction; + + // decoded raw txn + icp_unsigned_txn *raw_txn; + +} icp_txn_context_t; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Handler for ICP public key derivation. + * @details This flow expects ICP_GET_PUBLIC_KEY_REQUEST_INITIATE_TAG as initial + * query, otherwise the flow is aborted + * + * @param query object for address public key query + */ +void icp_get_pub_keys(icp_query_t *query); + +/** + * @brief Handler for signing a transaction on icp. + * @details The expected request type is ICP_SIGN_TXN_REQUEST_INITIATE_TAG. The + * function controls the complete data exchange with host, user prompts and + * confirmations for signing an ICP based transaction. + * + * @param query Reference to the decoded query struct from the host app + */ +void icp_sign_transaction(icp_query_t *query); + +#endif /* ICP_PRIV_H */ From ff80fe47ba986f9f10e430aecd3940d54f925872 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Thu, 7 Nov 2024 18:45:01 -0800 Subject: [PATCH 11/16] feat: Icp publick key implementation --- apps/icp_app/icp_pub_key.c | 462 +++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 apps/icp_app/icp_pub_key.c diff --git a/apps/icp_app/icp_pub_key.c b/apps/icp_app/icp_pub_key.c new file mode 100644 index 000000000..8cd564e00 --- /dev/null +++ b/apps/icp_app/icp_pub_key.c @@ -0,0 +1,462 @@ +/** + * @file icp_pub_key.c + * @author Cypherock X1 Team + * @brief Generates public key for ICP derivations. + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2024 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "bip32.h" +#include "curves.h" +#include "hasher.h" +#include "icp_api.h" +#include "icp_context.h" +#include "icp_helpers.h" +#include "icp_priv.h" +#include "reconstruct_wallet_flow.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Checks if the provided query contains expected request. + * @details The function performs the check on the request type and if the check + * fails, then it will send an error to the host bitcoin app and return false. + * + * @param query Reference to an instance of icp_query_t containing query + * received from host app + * @param which_request The expected request type enum + * + * @return bool Indicating if the check succeeded or failed + * @retval true If the query contains the expected request + * @retval false If the query does not contain the expected request + */ +static bool check_which_request(const icp_query_t *query, + pb_size_t which_request); + +/** + * @brief Validates the derivation paths received in the request from host + * @details The function validates each path index in the request. If any + * invalid index is detected, the function will send an error to the host and + * return false. + * + * @param req Reference to an instance of icp_get_public_keys_intiate_request_t + * @param which_request The type of request received from the host. + * @return bool Indicating if the verification passed or failed + * @retval true If the derivation path entries are valid + * @retval false If any of the derivation path entries are invalid + */ +static bool validate_request(const icp_get_public_keys_intiate_request_t *req, + const pb_size_t which_request); + +/** + * @brief Fills the list of public keys corresponding to the provided list of + * derivation paths in the buffer + * @details The function expects the size of list for derivation paths and + * location for storing derived public keys to be a match with provided count. + * + * @param path Reference to the list of icp_get_public_keys_derivation_path_t + * @param seed Reference to a const array containing the seed + * @param public_key Reference to the location to store all the public keys to + * be derived + * @param count Number of derivation paths in the list and consequently, + * sufficient space in memory for storing derived public keys. + * + * @retval true If all the requested public keys were derived successfully + * @retval false If there is any issue occurred during the key derivation + */ +static bool fill_public_keys(const icp_get_public_keys_derivation_path_t *path, + const uint8_t *seed, + uint8_t public_key_list[][ICP_PUB_KEY_SIZE], + pb_size_t count); + +/** + * @brief The function sends public keys for the requested batch + * @details The function determines the batch size from the static struct + * member declaration of nanopb options. The function batches the result based + * on the definition and sends the result. The function expects that the entire + * list of public keys requested is already derived and provided to this + * function as pubkey_list. The function will return false if either the query + * was wrong or a P0 event is occurred. In case of wrong query, the function + * also sends an error to the host app. + * + * @param query Reference to an instance of icp_query_t + * @param pubkey_list Reference to list of derived public key to be sent to the + * host + * @param count Number of public keys entries in the list of public keys + * @param which_request The type of request to be expected from the host + * @param which_response The type of response to be sent to the host + * + * @return bool Indicating if the public keys was exported completely to the + * host + * @retval true If all the requested public keys were exported to the host app + * @retval false If the export was interrupted by a P0 event or an invalid query + * was received from the host app. + */ +static bool send_public_keys(icp_query_t *query, + const uint8_t pubkey_list[][ICP_PUB_KEY_SIZE], + const pb_size_t count, + const pb_size_t which_request, + const pb_size_t which_response); + +/** + * @details The function provides an ED25519 public key for ICP. It accepts + * NULL for output parameter and handles accordingly. The function also manages + * all the terminal errors during derivation/encoding, in which case it will + * return false and send a relevant error to the host closing the + * request-response pair All the errors/invalid cases are conveyed to the host + * as unknown_error = 1 because we expect the data validation was success. + * TODO: Make this a common utility function + * + * @param seed Reference to the wallet seed generated + * @param path Derivation path of the node to be derived + * @param path_length Expected length of the provided derivation path + * @param public_key Storage location for raw uncompressed public key + * + * @retval false If derivation failed + */ +static bool get_public_key(const uint8_t *seed, + const uint32_t *path, + uint32_t path_length, + uint8_t *public_key); + +/** + * @brief Helper function to take user consent before exporting public keys to + * the host. Uses an appropriate message template based on the query request + * received from the host. + * + * @param which_request The type of request received from host + * @param wallet_name The name of the wallet on which the request needs to be + * performed + * @return true If the user accepted the request + * @return false If the user rejected or any P0 event occurred during the + * confirmation. + */ +static bool get_user_consent(const pb_size_t which_request, + const char *wallet_name); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +static bool check_which_request(const icp_query_t *query, + pb_size_t which_request) { + if (which_request != query->get_public_keys.which_request) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + return false; + } + + return true; +} + +static bool validate_request(const icp_get_public_keys_intiate_request_t *req, + const pb_size_t which_request) { + bool status = true; + const pb_size_t count = req->derivation_paths_count; + + if (0 == count) { + // request does not have any derivation paths, invalid request + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + if (ICP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request && + 1 < count) { + // `ICP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG` request contains more than + // one derivation path which is not expected + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + const icp_get_public_keys_derivation_path_t *path = NULL; + + for (pb_size_t index = 0; index < count; index++) { + path = &req->derivation_paths[index]; + if (!icp_derivation_path_guard(path->path, path->path_count)) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + break; + } + } + + return status; +} + +static bool get_public_key(const uint8_t *seed, + const uint32_t *path, + uint32_t path_length, + uint8_t *public_key) { + HDNode node = {0}; + + if (!derive_hdnode_from_path( + path, path_length, SECP256K1_NAME, seed, &node)) { + // send unknown error; unknown failure reason + icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + memzero(&node, sizeof(HDNode)); + return false; + } + + if (NULL != public_key) { + memcpy(public_key, node.public_key, ICP_PUB_KEY_SIZE); + } + + memzero(&node, sizeof(HDNode)); + return true; +} + +static bool fill_public_keys(const icp_get_public_keys_derivation_path_t *path, + const uint8_t *seed, + uint8_t public_key_list[][ICP_PUB_KEY_SIZE], + pb_size_t count) { + for (pb_size_t index = 0; index < count; index++) { + const icp_get_public_keys_derivation_path_t *current = &path[index]; + if (!get_public_key( + seed, current->path, current->path_count, public_key_list[index])) { + return false; + } + } + + return true; +} + +static bool send_public_keys(icp_query_t *query, + const uint8_t pubkey_list[][ICP_PUB_KEY_SIZE], + const pb_size_t count, + const pb_size_t which_request, + const pb_size_t which_response) { + icp_result_t response = init_icp_result(which_response); + icp_get_public_keys_result_response_t *result = + &response.get_public_keys.result; + size_t batch_limit = + sizeof(response.get_public_keys.result.public_keys) / ICP_PUB_KEY_SIZE; + size_t remaining = count; + + response.get_public_keys.which_response = + ICP_GET_PUBLIC_KEYS_RESPONSE_RESULT_TAG; + while (true) { + // send response as batched list of public keys + size_t batch_size = CY_MIN(batch_limit, remaining); + result->public_keys_count = batch_size; + + memcpy(result->public_keys, + &pubkey_list[count - remaining], + batch_size * ICP_PUB_KEY_SIZE); + + icp_send_result(&response); + remaining -= batch_size; + if (0 == remaining) { + break; + } + + if (!icp_get_query(query, which_request) || + !check_which_request(query, + ICP_GET_PUBLIC_KEYS_REQUEST_FETCH_NEXT_TAG)) { + return false; + } + } + return true; +} + +static bool get_user_consent(const pb_size_t which_request, + const char *wallet_name) { + char msg[100] = ""; + + if (ICP_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + snprintf( + msg, sizeof(msg), UI_TEXT_ADD_ACCOUNT_PROMPT, ICP_NAME, wallet_name); + } else { + snprintf(msg, sizeof(msg), UI_TEXT_RECEIVE_PROMPT, ICP_NAME, wallet_name); + } + + return core_scroll_page(NULL, msg, icp_send_error); +} + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +void icp_get_pub_keys(icp_query_t *query) { + char wallet_name[NAME_SIZE] = ""; + uint8_t seed[64] = {0}; + + const pb_size_t which_request = query->which_request; + const icp_get_public_keys_intiate_request_t *init_req = NULL; + pb_size_t which_response; + + if (ICP_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + which_response = ICP_RESULT_GET_PUBLIC_KEYS_TAG; + init_req = &query->get_public_keys.initiate; + } else { + which_response = ICP_RESULT_GET_USER_VERIFIED_PUBLIC_KEY_TAG; + init_req = &query->get_user_verified_public_key.initiate; + } + + const pb_size_t count = init_req->derivation_paths_count; + + uint8_t pubkey_list[sizeof(init_req->derivation_paths) / + sizeof(icp_get_public_keys_derivation_path_t)] + [ICP_PUB_KEY_SIZE] = {0}; + + if (!check_which_request(query, ICP_GET_PUBLIC_KEYS_REQUEST_INITIATE_TAG) || + !validate_request(init_req, which_request) || + !get_wallet_name_by_id( + init_req->wallet_id, (uint8_t *)wallet_name, icp_send_error)) { + return; + } + + // Take user consent to export public key for the wallet + if (!get_user_consent(which_request, wallet_name)) { + return; + } + + set_app_flow_status(ICP_GET_PUBLIC_KEYS_STATUS_CONFIRM); + + if (!reconstruct_seed(init_req->wallet_id, &seed[0], icp_send_error)) { + memzero(seed, sizeof(seed)); + return; + } + + set_app_flow_status(ICP_GET_PUBLIC_KEYS_STATUS_SEED_GENERATED); + delay_scr_init(ui_text_processing, DELAY_SHORT); + + bool result = + fill_public_keys(init_req->derivation_paths, seed, pubkey_list, count); + + // Clear seed as soon as it is not needed + memzero(seed, sizeof(seed)); + + if (!result) { + // send unknown error; do not know failure reason + icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + return; + } + + // In case the request is to `ICP_QUERY_GET_PUBLIC_KEY_TAG` type, then wait + // for user verification of the address + if (ICP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request) { + // address = base58.encode(prefixed_account_id || checksum) + // prefixed_account_id = 0x00 || ripemd160(sha256(public_key)) + // checksum = first 4 bytes of sha256(sha256(prefixed_account_id)) + + // see https://icpl.org/docs/concepts/accounts/addresses#address-encoding + + char address[ICP_ACCOUNT_ADDRESS_LENGTH] = ""; + + uint8_t public_key_digest[32]; + hasher_Raw(HASHER_SHA2_RIPEMD, + pubkey_list[0], + ICP_PUB_KEY_SIZE, + public_key_digest); + + uint8_t prefixed_account_id[ICP_PREFIXED_ACCOUNT_ID_LENGTH]; + prefixed_account_id[0] = 0x00; + memcpy(prefixed_account_id + 1, public_key_digest, 20); + + // icp uses different base58 dictionary, that's why a custom function + if (!base58_encode_check_with_custom_digits_order( + prefixed_account_id, + ICP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + ICP_ACCOUNT_ADDRESS_LENGTH + 1, + ICP_BASE58_DIGITS_ORDERED)) { + icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); + return; + } + + if (!core_scroll_page(ui_text_receive_on, address, icp_send_error)) { + return; + } + set_app_flow_status(ICP_GET_PUBLIC_KEYS_STATUS_VERIFY); + } + + if (!send_public_keys( + query, pubkey_list, count, which_request, which_response)) { + return; + } + + delay_scr_init(ui_text_check_cysync_app, DELAY_TIME); + return; +} From b30e6983fd3837867f93a46cd0f7a1c717827fc6 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Mon, 11 Nov 2024 18:45:01 -0800 Subject: [PATCH 12/16] feat: Icp generic transaction implementation --- apps/icp_app/icp_txn.c | 461 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 apps/icp_app/icp_txn.c diff --git a/apps/icp_app/icp_txn.c b/apps/icp_app/icp_txn.c new file mode 100644 index 000000000..2fd3a4551 --- /dev/null +++ b/apps/icp_app/icp_txn.c @@ -0,0 +1,461 @@ +/** + * @file icp_txn.c + * @author Cypherock X1 Team + * @brief Source file to handle transaction signing logic for ICP protocol + * + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2024 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include + +#include "icp_api.h" +#include "icp_context.h" +#include "icp_helpers.h" +#include "icp_priv.h" +#include "reconstruct_wallet_flow.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ +typedef icp_sign_txn_signature_response_signature_t der_sig_t; + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Checks if the provided query contains expected request. + * @details The function performs the check on the request type and if the check + * fails, then it will send an error to the host bitcoin app and return false. + * + * @param query Reference to an instance of icp_query_t containing query + * received from host app + * @param which_request The expected request type enum + * + * @return bool Indicating if the check succeeded or failed + * @retval true If the query contains the expected request + * @retval false If the query does not contain the expected request + */ +static bool check_which_request(const icp_query_t *query, + pb_size_t which_request); + +/** + * @brief The function prepares and sends empty responses + * + * @param which_response Constant value for the response type to be sent + */ +static void send_response(const pb_size_t which_response); + +/** + * @brief Validates the derivation path received in the request from host + * @details The function validates the provided account derivation path in the + * request. If invalid path is detected, the function will send an error to the + * host and return false. + * + * @param request Reference to an instance of icp_sign_txn_request_t + * @return bool Indicating if the verification passed or failed + * @retval true If all the derivation path entries are valid + * @retval false If any of the derivation path entries are invalid + */ +static bool validate_request_data(const icp_sign_txn_request_t *request); + +/** + * @brief Takes already received and decoded query for the user confirmation. + * @details The function will verify if the query contains the + * ICP_SIGN_TXN_REQUEST_INITIATE_TAG type of request. Additionally, the + * wallet-id is validated for sanity and the derivation path for the account is + * also validated. After the validations, user is prompted about the action for + * confirmation. The function returns true indicating all the validation and + * user confirmation was a success. The function also duplicates the data from + * query into the icp_txn_context for further processing. + * + * @param query Constant reference to the decoded query received from the host + * + * @return bool Indicating if the function actions succeeded or failed + * @retval true If all the validation and user confirmation was positive + * @retval false If any of the validation or user confirmation was negative + */ +static bool handle_initiate_query(const icp_query_t *query); + +/** + * @brief Receives unsigned txn from the host. If reception is successful, it + * also parses the txn to ensure it's validity. + * @note In case of any failure, a corresponding message is conveyed to the host + * + * @param query Reference to buffer of type icp_query_t + * @return true If the txn is received in the internal buffers and is valid + * @return false If the txn could not be received or it's validation failed + */ +static bool fetch_valid_input(icp_query_t *query); + +/** + * @brief This function executes user verification flow of the unsigned txn + * received from the host. + * @details The user verification flow is different for different type of action + * types identified from the unsigned txn + * @note This function expected that the unsigned txn is parsed using the helper + * function as only few action types are supported currently. + * + * @return true If the user accepted the transaction display + * @return false If any user rejection occured or P0 event occured + */ +static bool get_user_verification(void); + +/** + * @brief Calculates ED25519 curve based signature over the digest of the user + * verified unsigned txn. + * @details Seed reconstruction takes place within this function + * + * @param signature_buffer Reference to buffer where the signature will be + * populated + * @return true If the signature was computed successfully + * @return false If signature could not be computed - maybe due to some error + * during seed reconstruction phase + */ +static bool sign_txn(der_sig_t *der_signature); + +/** + * @brief Sends signature of the ICP unsigned txn to the host + * @details The function waits for the host to send a request of type + * ICP_SIGN_TXN_REQUEST_SIGNATURE_TAG and sends the response + * + * @param query Reference to buffer of type icp_query_t + * @param signature Reference to signature to be sent to the host + * @return true If the signature was sent successfully + * @return false If the signature could not be sent - maybe due to and P0 event + * or invalid request received from the host + */ +static bool send_signature(icp_query_t *query, const der_sig_t *der_signature); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ +static icp_txn_context_t *icp_txn_context = NULL; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static bool check_which_request(const icp_query_t *query, + pb_size_t which_request) { + if (which_request != query->sign_txn.which_request) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + return false; + } + + return true; +} + +static void send_response(const pb_size_t which_response) { + icp_result_t result = init_icp_result(ICP_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = which_response; + icp_send_result(&result); +} + +static bool validate_request_data(const icp_sign_txn_request_t *request) { + bool status = true; + + if (!icp_derivation_path_guard(request->initiate.derivation_path, + request->initiate.derivation_path_count)) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + return status; +} + +static bool handle_initiate_query(const icp_query_t *query) { + char wallet_name[NAME_SIZE] = ""; + char msg[100] = ""; + + // TODO: Handle wallet search failures - eg: Wallet ID not found, Wallet + // ID found but is invalid/locked wallet + if (!check_which_request(query, ICP_SIGN_TXN_REQUEST_INITIATE_TAG) || + !validate_request_data(&query->sign_txn) || + !get_wallet_name_by_id(query->sign_txn.initiate.wallet_id, + (uint8_t *)wallet_name, + icp_send_error)) { + return false; + } + + snprintf(msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, ICP_NAME, wallet_name); + // Take user consent to sign transaction for the wallet + if (!core_confirmation(msg, icp_send_error)) { + return false; + } + + set_app_flow_status(ICP_SIGN_TXN_STATUS_CONFIRM); + memcpy(&icp_txn_context->init_info, + &query->sign_txn.initiate, + sizeof(icp_sign_txn_initiate_request_t)); + + send_response(ICP_SIGN_TXN_RESPONSE_CONFIRMATION_TAG); + // show processing screen for a minimum duration (additional time will add due + // to actual processing) + delay_scr_init(ui_text_processing, DELAY_SHORT); + return true; +} + +static bool fetch_valid_input(icp_query_t *query) { + uint32_t size = 0; + icp_result_t response = init_icp_result(ICP_RESULT_SIGN_TXN_TAG); + uint32_t total_size = icp_txn_context->init_info.transaction_size; + const icp_sign_txn_data_t *txn_data = &query->sign_txn.txn_data; + const common_chunk_payload_t *payload = &txn_data->chunk_payload; + const common_chunk_payload_chunk_t *chunk = &txn_data->chunk_payload.chunk; + + // allocate memory for storing transaction + icp_txn_context->transaction = (uint8_t *)malloc(total_size); + while (1) { + if (!icp_get_query(query, ICP_QUERY_SIGN_TXN_TAG) || + !check_which_request(query, ICP_SIGN_TXN_REQUEST_TXN_DATA_TAG)) { + return false; + } + + if (!txn_data->has_chunk_payload || + payload->chunk_index >= payload->total_chunks || + size + payload->chunk.size > total_size) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + + memcpy(&icp_txn_context->transaction[size], chunk->bytes, chunk->size); + size += chunk->size; + // Send chunk ack to host + response.sign_txn.which_response = ICP_SIGN_TXN_RESPONSE_DATA_ACCEPTED_TAG; + response.sign_txn.data_accepted.has_chunk_ack = true; + response.sign_txn.data_accepted.chunk_ack.chunk_index = + payload->chunk_index; + icp_send_result(&response); + + if (0 == payload->remaining_size || + payload->chunk_index + 1 == payload->total_chunks) { + break; + } + } + + // make sure all chunks were received + if (size != total_size) { + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + icp_txn_context->raw_txn = + (icp_unsigned_txn *)malloc(sizeof(icp_unsigned_txn)); + + // TODO: parse transaction + + return true; +} + +static bool get_user_verification(void) { + const icp_unsigned_txn *decoded_utxn = icp_txn_context->raw_txn; + + char to_address[ICP_ACCOUNT_ADDRESS_LENGTH] = ""; + + uint8_t prefixed_account_id[ICP_PREFIXED_ACCOUNT_ID_LENGTH]; + prefixed_account_id[0] = 0x00; + memcpy(prefixed_account_id + 1, decoded_utxn->Destination, 20); + + // icp uses different base58 dictionary, that's why a custom function + if (!base58_encode_check_with_custom_digits_order( + prefixed_account_id, + ICP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + to_address, + ICP_ACCOUNT_ADDRESS_LENGTH + 1, + ICP_BASE58_DIGITS_ORDERED)) { + icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); + return false; + } + + if (!core_scroll_page(ui_text_verify_address, to_address, icp_send_error)) { + return false; + } + + // verify recipient amount + uint64_t amount = 0; + memcpy(&amount, &decoded_utxn->Amount, sizeof(uint64_t)); + char amount_string[30] = {'\0'}; + double decimal_amount = (double)amount; + decimal_amount *= 1e-6; + snprintf(amount_string, sizeof(amount_string), "%.6f", decimal_amount); + + char display[100] = {'\0'}; + snprintf(display, + sizeof(display), + UI_TEXT_VERIFY_AMOUNT, + amount_string, + ICP_LUNIT); + + if (!core_confirmation(display, icp_send_error)) { + return false; + } + + if (decoded_utxn->hasDestinationTag) { + // verify destination tag + uint32_t tag = 0; + memcpy(&tag, &decoded_utxn->DestinationTag, sizeof(uint32_t)); + + char display_tag[50] = {'\0'}; + snprintf( + display_tag, sizeof(display_tag), UI_TEXT_VERIFY_DESTINATION_TAG, tag); + + if (!core_confirmation(display_tag, icp_send_error)) { + return false; + } + } + + set_app_flow_status(ICP_SIGN_TXN_STATUS_VERIFY); + + return true; +} + +static bool sign_txn(der_sig_t *der_signature) { + uint8_t seed[64] = {0}; + if (!reconstruct_seed( + icp_txn_context->init_info.wallet_id, seed, icp_send_error)) { + memzero(seed, sizeof(seed)); + // TODO: handle errors of reconstruction flow + return false; + } + + set_app_flow_status(ICP_SIGN_TXN_STATUS_SEED_GENERATED); + + uint8_t digest[SHA512_DIGEST_LENGTH] = {0}; + sha512_Raw(icp_txn_context->transaction, + icp_txn_context->init_info.transaction_size, + digest); + + HDNode hdnode = {0}; + derive_hdnode_from_path(icp_txn_context->init_info.derivation_path, + icp_txn_context->init_info.derivation_path_count, + SECP256K1_NAME, + seed, + &hdnode); + + uint8_t signature[64]; + ecdsa_sign_digest( + &secp256k1, hdnode.private_key, digest, signature, NULL, NULL); + + der_signature->size = ecdsa_sig_to_der(signature, der_signature->bytes); + + memzero(digest, sizeof(digest)); + memzero(seed, sizeof(seed)); + memzero(&hdnode, sizeof(hdnode)); + memzero(signature, sizeof(signature)); + + return true; +} + +static bool send_signature(icp_query_t *query, const der_sig_t *der_signature) { + icp_result_t result = init_icp_result(ICP_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = ICP_SIGN_TXN_RESPONSE_SIGNATURE_TAG; + + if (!icp_get_query(query, ICP_QUERY_SIGN_TXN_TAG) || + !check_which_request(query, ICP_SIGN_TXN_REQUEST_SIGNATURE_TAG)) { + return false; + } + + memcpy( + &result.sign_txn.signature.signature, der_signature, sizeof(der_sig_t)); + + icp_send_result(&result); + return true; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void icp_sign_transaction(icp_query_t *query) { + icp_txn_context = (icp_txn_context_t *)malloc(sizeof(icp_txn_context_t)); + memzero(icp_txn_context, sizeof(icp_txn_context_t)); + + der_sig_t der_signature = {0}; + + if (handle_initiate_query(query) && fetch_valid_input(query) && + get_user_verification() && sign_txn(&der_signature) && + send_signature(query, &der_signature)) { + delay_scr_init(ui_text_check_cysync, DELAY_TIME); + } + + if (icp_txn_context) { + free(icp_txn_context); + icp_txn_context = NULL; + } + + return; +} From 8acbd5c0463c1b072ebcfdb034ce5628904b02fa Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 13 Nov 2024 18:45:01 -0800 Subject: [PATCH 13/16] feat: Create main entry point for icp app --- apps/icp_app/icp_main.c | 151 ++++++++++++++++++++++++++++++++++++++++ apps/icp_app/icp_main.h | 43 ++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 apps/icp_app/icp_main.c create mode 100644 apps/icp_app/icp_main.h diff --git a/apps/icp_app/icp_main.c b/apps/icp_app/icp_main.c new file mode 100644 index 000000000..f4819171d --- /dev/null +++ b/apps/icp_app/icp_main.c @@ -0,0 +1,151 @@ +/** + * @file icp_main.c + * @author Cypherock X1 Team + * @brief A common entry point to various ICP coin actions supported. + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2024 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "icp_main.h" + +#include "icp_api.h" +#include "icp_priv.h" +#include "status_api.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Entry point for the ICP application of the X1 vault. It is invoked + * by the X1 vault firmware, as soon as there is a USB request raised for the + * Icp app. + * + * @param usb_evt The USB event which triggered invocation of the icp app + */ +void icp_main(usb_event_t usb_evt, const void *icp_app_config); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +static const cy_app_desc_t icp_app_desc = {.id = 20, + .version = + { + .major = 1, + .minor = 0, + .patch = 0, + }, + .app = icp_main, + .app_config = NULL}; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +void icp_main(usb_event_t usb_evt, const void *icp_app_config) { + icp_query_t query = ICP_QUERY_INIT_DEFAULT; + + if (false == decode_icp_query(usb_evt.p_msg, usb_evt.msg_size, &query)) { + return; + } + + /* Set status to CORE_DEVICE_IDLE_STATE_USB to indicate host that we are now + * servicing a USB initiated command */ + core_status_set_idle_state(CORE_DEVICE_IDLE_STATE_USB); + + switch ((uint8_t)query.which_request) { + case ICP_QUERY_GET_PUBLIC_KEYS_TAG: + case ICP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG: { + icp_get_pub_keys(&query); + break; + } + case ICP_QUERY_SIGN_TXN_TAG: { + icp_sign_transaction(&query); + break; + } + default: { + /* In case we ever encounter invalid query, convey to the host app */ + icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + break; + } + } + + return; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +const cy_app_desc_t *get_icp_app_desc() { + return &icp_app_desc; +} diff --git a/apps/icp_app/icp_main.h b/apps/icp_app/icp_main.h new file mode 100644 index 000000000..4ebaf934a --- /dev/null +++ b/apps/icp_app/icp_main.h @@ -0,0 +1,43 @@ +/** + * @file icp_main.h + * @author Cypherock X1 Team + * @brief + * @details + + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + */ + +#ifndef ICP_MAIN_H +#define ICP_MAIN_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "app_registry.h" +#include "events.h" +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Returns the config for ICP chain app descriptor + * + * @return A const reference to cy_app_desc_t + */ +const cy_app_desc_t *get_icp_app_desc(); +#endif /* ICP_MAIN_H */ From 7446db6c6478dc6d652c22374ad29ab7a19d32ac Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Fri, 15 Nov 2024 18:45:01 -0800 Subject: [PATCH 14/16] chore: Remove aboslete code and use correct hasher --- apps/icp_app/icp_context.h | 2 -- apps/icp_app/icp_pub_key.c | 18 +++++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/icp_app/icp_context.h b/apps/icp_app/icp_context.h index d3bb431a7..5bd416dd0 100644 --- a/apps/icp_app/icp_context.h +++ b/apps/icp_app/icp_context.h @@ -23,8 +23,6 @@ #define ICP_PUB_KEY_SIZE 33 #define ICP_PREFIXED_ACCOUNT_ID_LENGTH 21 #define ICP_ACCOUNT_ADDRESS_LENGTH 34 -#define ICP_BASE58_DIGITS_ORDERED \ - "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz" /***************************************************************************** * TYPEDEFS diff --git a/apps/icp_app/icp_pub_key.c b/apps/icp_app/icp_pub_key.c index 8cd564e00..a7b1f8d6e 100644 --- a/apps/icp_app/icp_pub_key.c +++ b/apps/icp_app/icp_pub_key.c @@ -425,23 +425,19 @@ void icp_get_pub_keys(icp_query_t *query) { char address[ICP_ACCOUNT_ADDRESS_LENGTH] = ""; uint8_t public_key_digest[32]; - hasher_Raw(HASHER_SHA2_RIPEMD, - pubkey_list[0], - ICP_PUB_KEY_SIZE, - public_key_digest); + hasher_Raw( + HASHER_SHA2, pubkey_list[0], ICP_PUB_KEY_SIZE, public_key_digest); uint8_t prefixed_account_id[ICP_PREFIXED_ACCOUNT_ID_LENGTH]; prefixed_account_id[0] = 0x00; memcpy(prefixed_account_id + 1, public_key_digest, 20); // icp uses different base58 dictionary, that's why a custom function - if (!base58_encode_check_with_custom_digits_order( - prefixed_account_id, - ICP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - address, - ICP_ACCOUNT_ADDRESS_LENGTH + 1, - ICP_BASE58_DIGITS_ORDERED)) { + if (!base58_encode_check(prefixed_account_id, + ICP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + ICP_ACCOUNT_ADDRESS_LENGTH + 1)) { icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return; } From c7f85340c0bf1bf5a5b7390ea07058848cdba2b3 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Mon, 18 Nov 2024 18:45:01 -0800 Subject: [PATCH 15/16] chore: Update submodule --- common/cypherock-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/cypherock-common b/common/cypherock-common index fdb99fdb3..0cb57fd67 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit fdb99fdb39813727561dad33b6f0aa6493818766 +Subproject commit 0cb57fd67f5f8b510d972d40a824c44ff22a4919 From 30eb4f02eb02e10ab3600e96cbc40139f20d91b5 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 20 Nov 2024 18:45:01 -0800 Subject: [PATCH 16/16] fix: Remove obsolete code --- apps/icp_app/icp_txn.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/icp_app/icp_txn.c b/apps/icp_app/icp_txn.c index 2fd3a4551..4b5532089 100644 --- a/apps/icp_app/icp_txn.c +++ b/apps/icp_app/icp_txn.c @@ -63,6 +63,7 @@ #include +#include "base58.h" #include "icp_api.h" #include "icp_context.h" #include "icp_helpers.h" @@ -329,14 +330,11 @@ static bool get_user_verification(void) { prefixed_account_id[0] = 0x00; memcpy(prefixed_account_id + 1, decoded_utxn->Destination, 20); - // icp uses different base58 dictionary, that's why a custom function - if (!base58_encode_check_with_custom_digits_order( - prefixed_account_id, - ICP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - to_address, - ICP_ACCOUNT_ADDRESS_LENGTH + 1, - ICP_BASE58_DIGITS_ORDERED)) { + if (!base58_encode_check(prefixed_account_id, + ICP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + to_address, + ICP_ACCOUNT_ADDRESS_LENGTH + 1)) { icp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return false; }