From a310c3a474f56e345ebc8332ef7cc54d16868d0f Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Thu, 26 Sep 2024 19:49:49 +0530 Subject: [PATCH 01/19] feat: Added boilerplate code for xrp app --- apps/xrp_app/xrp_api.c | 196 ++++++++++++++++++++++ apps/xrp_app/xrp_api.h | 113 +++++++++++++ apps/xrp_app/xrp_context.h | 38 +++++ apps/xrp_app/xrp_main.c | 151 +++++++++++++++++ apps/xrp_app/xrp_main.h | 43 +++++ apps/xrp_app/xrp_priv.h | 25 +++ common/core/app_registry.h | 2 +- common/core/core_flow_init.c | 2 + common/cypherock-common | 2 +- utilities/cmake/firmware/firmware.cmake | 2 + utilities/cmake/simulator/simulator.cmake | 2 + 11 files changed, 574 insertions(+), 2 deletions(-) create mode 100644 apps/xrp_app/xrp_api.c create mode 100644 apps/xrp_app/xrp_api.h create mode 100644 apps/xrp_app/xrp_context.h create mode 100644 apps/xrp_app/xrp_main.c create mode 100644 apps/xrp_app/xrp_main.h create mode 100644 apps/xrp_app/xrp_priv.h diff --git a/apps/xrp_app/xrp_api.c b/apps/xrp_app/xrp_api.c new file mode 100644 index 000000000..c33e421fa --- /dev/null +++ b/apps/xrp_app/xrp_api.c @@ -0,0 +1,196 @@ +/** + * @file xrp_api.c + * @author Cypherock X1 Team + * @brief Defines helpers apis for XRP 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 "xrp_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_xrp_query(const uint8_t *data, + uint16_t data_size, + xrp_query_t *query_out) { + if (NULL == data || NULL == query_out || 0 == data_size) { + xrp_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(xrp_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, XRP_QUERY_FIELDS, query_out); + + /* Send error to host if status is false*/ + if (false == status) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_DECODING_FAILED); + } + + return status; +} + +bool encode_xrp_result(const xrp_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, XRP_RESULT_FIELDS, result); + + if (true == status) { + *bytes_written_out = stream.bytes_written; + } + + return status; +} + +bool check_xrp_query(const xrp_query_t *query, pb_size_t exp_query_tag) { + if ((NULL == query) || (exp_query_tag != query->which_request)) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + return false; + } + return true; +} + +xrp_result_t init_xrp_result(pb_size_t result_tag) { + xrp_result_t result = XRP_RESULT_INIT_ZERO; + result.which_response = result_tag; + return result; +} + +void xrp_send_error(pb_size_t which_error, uint32_t error_code) { + xrp_result_t result = init_xrp_result(XRP_RESULT_COMMON_ERROR_TAG); + result.common_error = init_common_error(which_error, error_code); + xrp_send_result(&result); +} + +void xrp_send_result(const xrp_result_t *result) { + // TODO: Set all option files + uint8_t buffer[1700] = {0}; + size_t bytes_encoded = 0; + ASSERT(encode_xrp_result(result, buffer, sizeof(buffer), &bytes_encoded)); + send_response_to_host(&buffer[0], bytes_encoded); +} + +bool xrp_get_query(xrp_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_xrp_query( + event.usb_event.p_msg, event.usb_event.msg_size, query)) { + return false; + } + + if (!check_xrp_query(query, exp_query_tag)) { + return false; + } + + return true; +} diff --git a/apps/xrp_app/xrp_api.h b/apps/xrp_app/xrp_api.h new file mode 100644 index 000000000..90eae5ea7 --- /dev/null +++ b/apps/xrp_app/xrp_api.h @@ -0,0 +1,113 @@ +/** + * @file xrp_api.h + * @author Cypherock X1 Team + * @brief Header file to export some helper functions for the XRP app + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef XRP_API_H +#define XRP_API_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief API to decode query from host with `XRP_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 xrp_query_t obj to copy the decoded result to + * @return bool True if decoding was successful, else false + */ +bool decode_xrp_query(const uint8_t *data, + uint16_t data_size, + xrp_query_t *query_out); + +/** + * @brief Encodes the XRP result with `XRP_RESULT_FIELDS` to byte-stream + * + * @param[in] result: object of populated @ref xrp_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_xrp_result(const xrp_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 + * `xrp_query_t` matches against the expected tag. + * + * @param query The query of type `xrp_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_xrp_query(const xrp_query_t *query, pb_size_t exp_query_tag); + +/** + * @brief Returns zero initialized object of type + * xrp_result_t result_tag set in result.which_response field + * + * @param result_tag Result tag to be set in the xrp_result_t result + * @return xrp_result_t Result object of type xrp_result_t + */ +xrp_result_t init_xrp_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 xrp_send_error(pb_size_t which_error, uint32_t error_code); + +/** + * @brief This API encodes xrp_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_xrp_result internally. + * + * @param result The result which needs to be sent to the host. + */ +void xrp_send_result(const xrp_result_t *result); + +/** + * @brief This API receives request of type xrp_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 xrp_get_query(xrp_query_t *query, pb_size_t exp_query_tag); + +#endif diff --git a/apps/xrp_app/xrp_context.h b/apps/xrp_app/xrp_context.h new file mode 100644 index 000000000..8a4e87143 --- /dev/null +++ b/apps/xrp_app/xrp_context.h @@ -0,0 +1,38 @@ +/** + * @file xrp_context.h + * @author Cypherock X1 Team + * @brief Header file defining typedefs and MACROS for the XRP app + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef XRP_CONTEXT_H +#define XRP_CONTEXT_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +// TODO: Populate structure for XRP +typedef struct { +} xrp_config_t; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +#endif /* XRP_CONTEXT_H */ \ No newline at end of file diff --git a/apps/xrp_app/xrp_main.c b/apps/xrp_app/xrp_main.c new file mode 100644 index 000000000..38264bde9 --- /dev/null +++ b/apps/xrp_app/xrp_main.c @@ -0,0 +1,151 @@ +/** + * @file xrp_main.c + * @author Cypherock X1 Team + * @brief A common entry point to various XRP 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 "xrp_main.h" + +#include "xrp_api.h" +#include "xrp_priv.h" +#include "status_api.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Entry point for the XRP application of the X1 vault. It is invoked + * by the X1 vault firmware, as soon as there is a USB request raised for the + * Xrp app. + * + * @param usb_evt The USB event which triggered invocation of the xrp app + */ +void xrp_main(usb_event_t usb_evt, const void *xrp_app_config); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +static const cy_app_desc_t xrp_app_desc = {.id = 20, + .version = + { + .major = 1, + .minor = 0, + .patch = 0, + }, + .app = xrp_main, + .app_config = NULL}; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +void xrp_main(usb_event_t usb_evt, const void *xrp_app_config) { + xrp_query_t query = XRP_QUERY_INIT_DEFAULT; + + if (false == decode_xrp_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 XRP_QUERY_GET_PUBLIC_KEYS_TAG: + case XRP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG: { + // TODO: Add get public key functionality + break; + } + case XRP_QUERY_SIGN_TXN_TAG: { + // TODO: Add sign txn functionality + break; + } + default: { + /* In case we ever encounter invalid query, convey to the host app */ + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + break; + } + } + + return; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +const cy_app_desc_t *get_xrp_app_desc() { + return &xrp_app_desc; +} \ No newline at end of file diff --git a/apps/xrp_app/xrp_main.h b/apps/xrp_app/xrp_main.h new file mode 100644 index 000000000..7ee7712b6 --- /dev/null +++ b/apps/xrp_app/xrp_main.h @@ -0,0 +1,43 @@ +/** + * @file xrp_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 XRP_MAIN_H +#define XRP_MAIN_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "app_registry.h" +#include "events.h" +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Returns the config for XRP chain app descriptor + * + * @return A const reference to cy_app_desc_t + */ +const cy_app_desc_t *get_xrp_app_desc(); +#endif /* XRP_MAIN_H */ diff --git a/apps/xrp_app/xrp_priv.h b/apps/xrp_app/xrp_priv.h new file mode 100644 index 000000000..58ab929cd --- /dev/null +++ b/apps/xrp_app/xrp_priv.h @@ -0,0 +1,25 @@ +/** + * @file xrp_priv.h + * @author Cypherock X1 Team + * @brief Support for xrp app internal operations + * This file is defined to separate XRP'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 XRP_PRIV_H +#define XRP_PRIV_H +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +#include "xrp_context.h" + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +#endif /* XRP_PRIV_H */ \ No newline at end of file diff --git a/common/core/app_registry.h b/common/core/app_registry.h index cc8b80d7e..8f3b3ed65 100644 --- a/common/core/app_registry.h +++ b/common/core/app_registry.h @@ -23,7 +23,7 @@ * MACROS AND DEFINES *****************************************************************************/ -#define REGISTRY_MAX_APPS 19 +#define REGISTRY_MAX_APPS 21 /***************************************************************************** * TYPEDEFS diff --git a/common/core/core_flow_init.c b/common/core/core_flow_init.c index 2bc719aa5..0097a8295 100644 --- a/common/core/core_flow_init.c +++ b/common/core/core_flow_init.c @@ -83,6 +83,7 @@ #include "restricted_app.h" #include "solana_main.h" #include "tron_main.h" +#include "xrp_main.h" /***************************************************************************** * EXTERN VARIABLES @@ -179,4 +180,5 @@ void core_init_app_registry() { registry_add_app(get_optimism_app_desc()); registry_add_app(get_arbitrum_app_desc()); registry_add_app(get_tron_app_desc()); + registry_add_app(get_xrp_app_desc()); } \ No newline at end of file diff --git a/common/cypherock-common b/common/cypherock-common index 79e2f03a5..a9b3f503d 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 79e2f03a5d16c4ce8cbb50b472620034f984d735 +Subproject commit a9b3f503df0c937f3542cfb8573dc8ac21a4a033 diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index f35609e2b..48cf03fc6 100644 --- a/utilities/cmake/firmware/firmware.cmake +++ b/utilities/cmake/firmware/firmware.cmake @@ -58,6 +58,7 @@ target_include_directories(${EXECUTABLE} PRIVATE apps/near_app apps/solana_app apps/tron_app + apps/xrp_app src/ src/menu @@ -181,6 +182,7 @@ target_include_directories(${EXECUTABLE} PRIVATE $<$:${PROJECT_SOURCE_DIR}/tests/apps/evm_app> $<$:${PROJECT_SOURCE_DIR}/tests/apps/near_app> $<$:${PROJECT_SOURCE_DIR}/tests/apps/solana_app> + $<$:${PROJECT_SOURCE_DIR}/tests/apps/xrp_app> ) target_compile_options(${EXECUTABLE} PRIVATE diff --git a/utilities/cmake/simulator/simulator.cmake b/utilities/cmake/simulator/simulator.cmake index 9a6aea286..17d577f99 100644 --- a/utilities/cmake/simulator/simulator.cmake +++ b/utilities/cmake/simulator/simulator.cmake @@ -55,6 +55,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE apps/solana_app apps/tron_app + apps/xrp_app src/ src/menu @@ -157,6 +158,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE $<$:${PROJECT_SOURCE_DIR}/tests/apps/evm_app> $<$:${PROJECT_SOURCE_DIR}/tests/apps/near_app> $<$:${PROJECT_SOURCE_DIR}/tests/apps/solana_app> + $<$:${PROJECT_SOURCE_DIR}/tests/apps/xrp_app> ) IF(UNIT_TESTS_SWITCH) From b066c3cfd340ff4a2d02ebda0be6ad795cd95e89 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Sat, 28 Sep 2024 16:23:35 +0530 Subject: [PATCH 02/19] feat: Added get-public-key functionality to xrp app --- apps/xrp_app/xrp_context.h | 5 + apps/xrp_app/xrp_helpers.c | 171 +++++++ apps/xrp_app/xrp_helpers.h | 82 +++ apps/xrp_app/xrp_main.c | 2 +- apps/xrp_app/xrp_priv.h | 17 + apps/xrp_app/xrp_pub_key.c | 467 ++++++++++++++++++ common/cypherock-common | 2 +- common/proto-options/xrp/core.options | 1 + common/proto-options/xrp/error.options | 1 + .../proto-options/xrp/get_public_key.options | 5 + 10 files changed, 751 insertions(+), 2 deletions(-) create mode 100644 apps/xrp_app/xrp_helpers.c create mode 100644 apps/xrp_app/xrp_helpers.h create mode 100644 apps/xrp_app/xrp_pub_key.c create mode 100644 common/proto-options/xrp/core.options create mode 100644 common/proto-options/xrp/error.options create mode 100644 common/proto-options/xrp/get_public_key.options diff --git a/apps/xrp_app/xrp_context.h b/apps/xrp_app/xrp_context.h index 8a4e87143..a03ff579c 100644 --- a/apps/xrp_app/xrp_context.h +++ b/apps/xrp_app/xrp_context.h @@ -18,6 +18,11 @@ /***************************************************************************** * MACROS AND DEFINES *****************************************************************************/ +#define XRP_NAME "XRP" +#define XRP_LUNIT "XRP" +#define XRP_PUB_KEY_SIZE 33 +#define XRP_PREFIXED_ACCOUNT_ID_LENGTH 21 +#define XRP_ACCOUNT_ADDRESS_LENGTH 34 /***************************************************************************** * TYPEDEFS diff --git a/apps/xrp_app/xrp_helpers.c b/apps/xrp_app/xrp_helpers.c new file mode 100644 index 000000000..680aa5e8b --- /dev/null +++ b/apps/xrp_app/xrp_helpers.c @@ -0,0 +1,171 @@ +/** + * @file xrp_helpers.c + * @author Cypherock X1 Team + * @brief Utilities specific to Xrp 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 "xrp_helpers.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +const char xrp_b58digits_ordered[] = + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels) { + bool status = false; + if (levels != XRP_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 = (XRP_PURPOSE_INDEX == purpose && XRP_COIN_INDEX == coin && + XRP_ACCOUNT_INDEX == account && XRP_CHANGE_INDEX == change && + is_non_hardened(address)); + + return status; +} + +bool xrp_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { + const uint8_t *bin = data; + int carry = 0; + size_t i = 0, j = 0, high = 0, zcount = 0; + size_t size = 0; + + while (zcount < binsz && !bin[zcount]) ++zcount; + + size = (binsz - zcount) * 138 / 100 + 1; + uint8_t buf[size]; + memzero(buf, size); + + for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { + for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { + carry += 256 * buf[j]; + buf[j] = carry % 58; + carry /= 58; + if (!j) { + // Otherwise j wraps to maxint which is > high + break; + } + } + } + + for (j = 0; j < size && !buf[j]; ++j) + ; + + if (*b58sz <= zcount + size - j) { + *b58sz = zcount + size - j + 1; + return false; + } + + if (zcount) memset(b58, 'r', zcount); + for (i = zcount; j < size; ++i, ++j) b58[i] = xrp_b58digits_ordered[buf[j]]; + b58[i] = '\0'; + *b58sz = i + 1; + + return true; +} + +int xrp_base58_encode_check(const uint8_t *data, int datalen, + HasherType hasher_type, char *str, int strsize) { + if (datalen > 128) { + return 0; + } + uint8_t buf[datalen + 32]; + memset(buf, 0, sizeof(buf)); + uint8_t *hash = buf + datalen; + memcpy(buf, data, datalen); + hasher_Raw(hasher_type, data, datalen, hash); + size_t res = strsize; + bool success = xrp_b58enc(str, &res, buf, datalen + 4); + memzero(buf, sizeof(buf)); + return success ? res : 0; +} diff --git a/apps/xrp_app/xrp_helpers.h b/apps/xrp_app/xrp_helpers.h new file mode 100644 index 000000000..ec4aff5d3 --- /dev/null +++ b/apps/xrp_app/xrp_helpers.h @@ -0,0 +1,82 @@ +/** + * @file xrp_helpers.h + * @author Cypherock X1 Team + * @brief Utilities api definitions for XRP chains + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef XRP_HELPERS_H +#define XRP_HELPERS_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include +#include + +#include "coin_utils.h" +#include "hasher.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +#define XRP_IMPLICIT_ACCOUNT_DEPTH 5 + +#define XRP_PURPOSE_INDEX 0x8000002C // 44' +#define XRP_COIN_INDEX 0x80000090 // 144' +#define XRP_ACCOUNT_INDEX 0x80000000 // 0' +#define XRP_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 xrp_derivation_path_guard(const uint32_t *path, uint8_t levels); + +/** + * @brief Generates the base58 encoded string from the data. + * @details The given data is hashed as per the given hasher_type. + * Then the checksum (first 4 bytes of hash digest) is taken and concatenated + * with the data. The concatenated data is then encoded using base58 + * with the dictionary rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz + * + * @param[in] data The data to encode + * @param[in] data_len The length of the given data + * @param[in] hasher_type The type of hash to generate checksum + * @param[out] str The string to store the encoded data in + * @param[in] strsize The size of str to generate + * @return int The size of str generated + * @retval Same as provided if successful + * @retval 0 otherwise + */ +int xrp_base58_encode_check(const uint8_t *data, int datalen, + HasherType hasher_type, char *str, int strsize); + +#endif // XRP_HELPERS_H \ No newline at end of file diff --git a/apps/xrp_app/xrp_main.c b/apps/xrp_app/xrp_main.c index 38264bde9..f854a03b8 100644 --- a/apps/xrp_app/xrp_main.c +++ b/apps/xrp_app/xrp_main.c @@ -125,7 +125,7 @@ void xrp_main(usb_event_t usb_evt, const void *xrp_app_config) { switch ((uint8_t)query.which_request) { case XRP_QUERY_GET_PUBLIC_KEYS_TAG: case XRP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG: { - // TODO: Add get public key functionality + xrp_get_pub_keys(&query); break; } case XRP_QUERY_SIGN_TXN_TAG: { diff --git a/apps/xrp_app/xrp_priv.h b/apps/xrp_app/xrp_priv.h index 58ab929cd..6a4c4b43a 100644 --- a/apps/xrp_app/xrp_priv.h +++ b/apps/xrp_app/xrp_priv.h @@ -22,4 +22,21 @@ * TYPEDEFS *****************************************************************************/ +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Handler for XRP public key derivation. + * @details This flow expects XRP_GET_PUBLIC_KEY_REQUEST_INITIATE_TAG as initial + * query, otherwise the flow is aborted + * + * @param query object for address public key query + */ +void xrp_get_pub_keys(xrp_query_t *query); + #endif /* XRP_PRIV_H */ \ No newline at end of file diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c new file mode 100644 index 000000000..47513dd77 --- /dev/null +++ b/apps/xrp_app/xrp_pub_key.c @@ -0,0 +1,467 @@ +/** + * @file xrp_pub_key.c + * @author Cypherock X1 Team + * @brief Generates public key for XRP 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 "xrp_api.h" +#include "xrp_context.h" +#include "xrp_helpers.h" +#include "xrp_priv.h" +#include "reconstruct_wallet_flow.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" +#include "bip32.h" +#include "curves.h" +#include "hasher.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 xrp_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 xrp_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 xrp_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 xrp_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 xrp_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 xrp_get_public_keys_derivation_path_t *path, + const uint8_t *seed, + uint8_t public_key_list[][XRP_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 xrp_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(xrp_query_t *query, + const uint8_t pubkey_list[][XRP_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 XRP. 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 xrp_query_t *query, + pb_size_t which_request) { + if (which_request != query->get_public_keys.which_request) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + return false; + } + + return true; +} + +static bool validate_request(const xrp_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 + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + if (XRP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request && + 1 < count) { + // `XRP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG` request contains more than + // one derivation path which is not expected + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + const xrp_get_public_keys_derivation_path_t *path = NULL; + + for (pb_size_t index = 0; index < count; index++) { + path = &req->derivation_paths[index]; + if (!xrp_derivation_path_guard(path->path, path->path_count)) { + xrp_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 + xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + memzero(&node, sizeof(HDNode)); + return false; + } + + if (NULL != public_key) { + // ecdsa_uncompress_pubkey( + // get_curve_by_name(SECP256K1_NAME)->params, node.public_key, public_key); + memcpy(public_key, node.public_key, XRP_PUB_KEY_SIZE); + } + + memzero(&node, sizeof(HDNode)); + return true; +} + +static bool fill_public_keys(const xrp_get_public_keys_derivation_path_t *path, + const uint8_t *seed, + uint8_t public_key_list[][XRP_PUB_KEY_SIZE], + pb_size_t count) { + for (pb_size_t index = 0; index < count; index++) { + const xrp_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(xrp_query_t *query, + const uint8_t pubkey_list[][XRP_PUB_KEY_SIZE], + const pb_size_t count, + const pb_size_t which_request, + const pb_size_t which_response) { + xrp_result_t response = init_xrp_result(which_response); + xrp_get_public_keys_result_response_t *result = + &response.get_public_keys.result; + size_t batch_limit = + sizeof(response.get_public_keys.result.public_keys) / XRP_PUB_KEY_SIZE; + size_t remaining = count; + + response.get_public_keys.which_response = + XRP_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 * XRP_PUB_KEY_SIZE); + + xrp_send_result(&response); + remaining -= batch_size; + if (0 == remaining) { + break; + } + + if (!xrp_get_query(query, which_request) || + !check_which_request(query, + XRP_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 (XRP_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + snprintf(msg, + sizeof(msg), + UI_TEXT_ADD_ACCOUNT_PROMPT, + XRP_NAME, + wallet_name); + } else { + snprintf(msg, + sizeof(msg), + UI_TEXT_RECEIVE_TOKEN_PROMPT, + XRP_LUNIT, + XRP_NAME, + wallet_name); + } + + return core_scroll_page(NULL, msg, xrp_send_error); +} + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +void xrp_get_pub_keys(xrp_query_t *query) { + char wallet_name[NAME_SIZE] = ""; + uint8_t seed[64] = {0}; + + const pb_size_t which_request = query->which_request; + const xrp_get_public_keys_intiate_request_t *init_req = NULL; + pb_size_t which_response; + + if (XRP_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + which_response = XRP_RESULT_GET_PUBLIC_KEYS_TAG; + init_req = &query->get_public_keys.initiate; + } else { + which_response = XRP_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(xrp_get_public_keys_derivation_path_t)] + [XRP_PUB_KEY_SIZE] = {0}; + + if (!check_which_request(query, XRP_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, + xrp_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(XRP_GET_PUBLIC_KEYS_STATUS_CONFIRM); + + if (!reconstruct_seed(init_req->wallet_id, &seed[0], xrp_send_error)) { + memzero(seed, sizeof(seed)); + return; + } + + set_app_flow_status(XRP_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 + xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + return; + } + + // In case the request is to `XRP_QUERY_GET_PUBLIC_KEY_TAG` type, then wait + // for user verification of the address + if (XRP_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://xrpl.org/docs/concepts/accounts/addresses#address-encoding + + char address[XRP_ACCOUNT_ADDRESS_LENGTH] = ""; + + uint8_t public_key_digest[32]; + hasher_Raw(HASHER_SHA2_RIPEMD, pubkey_list[0], XRP_PUB_KEY_SIZE, public_key_digest); + + uint8_t prefixed_account_id[XRP_PREFIXED_ACCOUNT_ID_LENGTH]; + prefixed_account_id[0] = 0x00; + memcpy(prefixed_account_id + 1, public_key_digest, 20); + + // xrp uses different base58 dictionary, that's why a custom function + if (!xrp_base58_encode_check(prefixed_account_id, + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { + xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); + return; + } + + if (!core_scroll_page(ui_text_receive_on, address, xrp_send_error)) { + return; + } + set_app_flow_status(XRP_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; +} diff --git a/common/cypherock-common b/common/cypherock-common index a9b3f503d..d60bfa9ab 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit a9b3f503df0c937f3542cfb8573dc8ac21a4a033 +Subproject commit d60bfa9ab8fc4f85b84ab41362e53622e7d2a2e1 diff --git a/common/proto-options/xrp/core.options b/common/proto-options/xrp/core.options new file mode 100644 index 000000000..7c4863400 --- /dev/null +++ b/common/proto-options/xrp/core.options @@ -0,0 +1 @@ +# Options for file common/cypherock-common/proto/xrp/core.proto diff --git a/common/proto-options/xrp/error.options b/common/proto-options/xrp/error.options new file mode 100644 index 000000000..5847b57ec --- /dev/null +++ b/common/proto-options/xrp/error.options @@ -0,0 +1 @@ +# Options for file common/cypherock-common/proto/xrp/error.proto diff --git a/common/proto-options/xrp/get_public_key.options b/common/proto-options/xrp/get_public_key.options new file mode 100644 index 000000000..56bb3fdf2 --- /dev/null +++ b/common/proto-options/xrp/get_public_key.options @@ -0,0 +1,5 @@ +# Options for file common/cypherock-common/proto/xrp/get_public_key.proto +xrp.GetPublicKeysDerivationPath.path type:FT_STATIC max_count:5 fixed_length:true +xrp.GetPublicKeysIntiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +xrp.GetPublicKeysIntiateRequest.derivation_paths type:FT_STATIC max_count:100 fixed_length:true +xrp.GetPublicKeysResultResponse.public_keys type:FT_STATIC max_size:33 max_count:10 fixed_length:true From 4f969d4837e34918d256c99dc394c448872274d3 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Sat, 28 Sep 2024 17:08:20 +0530 Subject: [PATCH 03/19] fix: Fixed code formatting --- apps/xrp_app/xrp_api.c | 16 ++++++------ apps/xrp_app/xrp_api.h | 12 ++++----- apps/xrp_app/xrp_helpers.c | 18 ++++++++----- apps/xrp_app/xrp_helpers.h | 19 ++++++++------ apps/xrp_app/xrp_main.c | 20 +++++++------- apps/xrp_app/xrp_priv.h | 2 +- apps/xrp_app/xrp_pub_key.c | 53 +++++++++++++++++++------------------- 7 files changed, 75 insertions(+), 65 deletions(-) diff --git a/apps/xrp_app/xrp_api.c b/apps/xrp_app/xrp_api.c index c33e421fa..88006127b 100644 --- a/apps/xrp_app/xrp_api.c +++ b/apps/xrp_app/xrp_api.c @@ -101,11 +101,11 @@ * GLOBAL FUNCTIONS *****************************************************************************/ bool decode_xrp_query(const uint8_t *data, - uint16_t data_size, - xrp_query_t *query_out) { + uint16_t data_size, + xrp_query_t *query_out) { if (NULL == data || NULL == query_out || 0 == data_size) { xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_DECODING_FAILED); + ERROR_DATA_FLOW_DECODING_FAILED); return false; } @@ -121,16 +121,16 @@ bool decode_xrp_query(const uint8_t *data, /* Send error to host if status is false*/ if (false == status) { xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_DECODING_FAILED); + ERROR_DATA_FLOW_DECODING_FAILED); } return status; } bool encode_xrp_result(const xrp_result_t *result, - uint8_t *buffer, - uint16_t max_buffer_len, - size_t *bytes_written_out) { + uint8_t *buffer, + uint16_t max_buffer_len, + size_t *bytes_written_out) { if (NULL == result || NULL == buffer || NULL == bytes_written_out) return false; @@ -150,7 +150,7 @@ bool encode_xrp_result(const xrp_result_t *result, bool check_xrp_query(const xrp_query_t *query, pb_size_t exp_query_tag) { if ((NULL == query) || (exp_query_tag != query->which_request)) { xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_QUERY); + ERROR_DATA_FLOW_INVALID_QUERY); return false; } return true; diff --git a/apps/xrp_app/xrp_api.h b/apps/xrp_app/xrp_api.h index 90eae5ea7..90d36b57e 100644 --- a/apps/xrp_app/xrp_api.h +++ b/apps/xrp_app/xrp_api.h @@ -13,8 +13,8 @@ * INCLUDES *****************************************************************************/ -#include #include +#include /***************************************************************************** * MACROS AND DEFINES @@ -41,8 +41,8 @@ * @return bool True if decoding was successful, else false */ bool decode_xrp_query(const uint8_t *data, - uint16_t data_size, - xrp_query_t *query_out); + uint16_t data_size, + xrp_query_t *query_out); /** * @brief Encodes the XRP result with `XRP_RESULT_FIELDS` to byte-stream @@ -55,9 +55,9 @@ bool decode_xrp_query(const uint8_t *data, * @return bool True if decoding was successful, else false */ bool encode_xrp_result(const xrp_result_t *result, - uint8_t *buffer, - uint16_t max_buffer_len, - size_t *bytes_written_out); + 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 diff --git a/apps/xrp_app/xrp_helpers.c b/apps/xrp_app/xrp_helpers.c index 680aa5e8b..3f3ae2c98 100644 --- a/apps/xrp_app/xrp_helpers.c +++ b/apps/xrp_app/xrp_helpers.c @@ -104,7 +104,7 @@ bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels) { } uint32_t purpose = path[0], coin = path[1], account = path[2], - change = path[3], address = path[4]; + change = path[3], address = path[4]; // m/44'/144'/0'/0/i status = (XRP_PURPOSE_INDEX == purpose && XRP_COIN_INDEX == coin && @@ -120,7 +120,8 @@ bool xrp_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { size_t i = 0, j = 0, high = 0, zcount = 0; size_t size = 0; - while (zcount < binsz && !bin[zcount]) ++zcount; + while (zcount < binsz && !bin[zcount]) + ++zcount; size = (binsz - zcount) * 138 / 100 + 1; uint8_t buf[size]; @@ -146,16 +147,21 @@ bool xrp_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { return false; } - if (zcount) memset(b58, 'r', zcount); - for (i = zcount; j < size; ++i, ++j) b58[i] = xrp_b58digits_ordered[buf[j]]; + if (zcount) + memset(b58, 'r', zcount); + for (i = zcount; j < size; ++i, ++j) + b58[i] = xrp_b58digits_ordered[buf[j]]; b58[i] = '\0'; *b58sz = i + 1; return true; } -int xrp_base58_encode_check(const uint8_t *data, int datalen, - HasherType hasher_type, char *str, int strsize) { +int xrp_base58_encode_check(const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize) { if (datalen > 128) { return 0; } diff --git a/apps/xrp_app/xrp_helpers.h b/apps/xrp_app/xrp_helpers.h index ec4aff5d3..637f589b9 100644 --- a/apps/xrp_app/xrp_helpers.h +++ b/apps/xrp_app/xrp_helpers.h @@ -45,11 +45,10 @@ /** * @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 + * @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 @@ -65,7 +64,8 @@ bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels); * @details The given data is hashed as per the given hasher_type. * Then the checksum (first 4 bytes of hash digest) is taken and concatenated * with the data. The concatenated data is then encoded using base58 - * with the dictionary rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz + * with the dictionary + * rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz * * @param[in] data The data to encode * @param[in] data_len The length of the given data @@ -76,7 +76,10 @@ bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels); * @retval Same as provided if successful * @retval 0 otherwise */ -int xrp_base58_encode_check(const uint8_t *data, int datalen, - HasherType hasher_type, char *str, int strsize); +int xrp_base58_encode_check(const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize); #endif // XRP_HELPERS_H \ No newline at end of file diff --git a/apps/xrp_app/xrp_main.c b/apps/xrp_app/xrp_main.c index f854a03b8..e67d77254 100644 --- a/apps/xrp_app/xrp_main.c +++ b/apps/xrp_app/xrp_main.c @@ -62,9 +62,9 @@ #include "xrp_main.h" +#include "status_api.h" #include "xrp_api.h" #include "xrp_priv.h" -#include "status_api.h" /***************************************************************************** * EXTERN VARIABLES @@ -99,14 +99,14 @@ void xrp_main(usb_event_t usb_evt, const void *xrp_app_config); *****************************************************************************/ static const cy_app_desc_t xrp_app_desc = {.id = 20, - .version = - { - .major = 1, - .minor = 0, - .patch = 0, - }, - .app = xrp_main, - .app_config = NULL}; + .version = + { + .major = 1, + .minor = 0, + .patch = 0, + }, + .app = xrp_main, + .app_config = NULL}; /***************************************************************************** * STATIC FUNCTIONS @@ -135,7 +135,7 @@ void xrp_main(usb_event_t usb_evt, const void *xrp_app_config) { default: { /* In case we ever encounter invalid query, convey to the host app */ xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_QUERY); + ERROR_DATA_FLOW_INVALID_QUERY); break; } } diff --git a/apps/xrp_app/xrp_priv.h b/apps/xrp_app/xrp_priv.h index 6a4c4b43a..b541abbb6 100644 --- a/apps/xrp_app/xrp_priv.h +++ b/apps/xrp_app/xrp_priv.h @@ -13,8 +13,8 @@ /***************************************************************************** * INCLUDES *****************************************************************************/ -#include #include +#include #include "xrp_context.h" diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c index 47513dd77..8f2a2444d 100644 --- a/apps/xrp_app/xrp_pub_key.c +++ b/apps/xrp_app/xrp_pub_key.c @@ -60,18 +60,18 @@ * INCLUDES *****************************************************************************/ -#include "xrp_api.h" -#include "xrp_context.h" -#include "xrp_helpers.h" -#include "xrp_priv.h" +#include "bip32.h" +#include "curves.h" +#include "hasher.h" #include "reconstruct_wallet_flow.h" #include "status_api.h" #include "ui_core_confirm.h" #include "ui_screens.h" #include "wallet_list.h" -#include "bip32.h" -#include "curves.h" -#include "hasher.h" +#include "xrp_api.h" +#include "xrp_context.h" +#include "xrp_helpers.h" +#include "xrp_priv.h" /***************************************************************************** * EXTERN VARIABLES @@ -214,7 +214,7 @@ static bool check_which_request(const xrp_query_t *query, pb_size_t which_request) { if (which_request != query->get_public_keys.which_request) { xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_REQUEST); + ERROR_DATA_FLOW_INVALID_REQUEST); return false; } @@ -229,7 +229,7 @@ static bool validate_request(const xrp_get_public_keys_intiate_request_t *req, if (0 == count) { // request does not have any derivation paths, invalid request xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_DATA); + ERROR_DATA_FLOW_INVALID_DATA); status = false; } @@ -238,7 +238,7 @@ static bool validate_request(const xrp_get_public_keys_intiate_request_t *req, // `XRP_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG` request contains more than // one derivation path which is not expected xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_DATA); + ERROR_DATA_FLOW_INVALID_DATA); status = false; } @@ -248,7 +248,7 @@ static bool validate_request(const xrp_get_public_keys_intiate_request_t *req, path = &req->derivation_paths[index]; if (!xrp_derivation_path_guard(path->path, path->path_count)) { xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_DATA); + ERROR_DATA_FLOW_INVALID_DATA); status = false; break; } @@ -263,7 +263,8 @@ static bool get_public_key(const uint8_t *seed, uint8_t *public_key) { HDNode node = {0}; - if (!derive_hdnode_from_path(path, path_length, SECP256K1_NAME, seed, &node)) { + if (!derive_hdnode_from_path( + path, path_length, SECP256K1_NAME, seed, &node)) { // send unknown error; unknown failure reason xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); memzero(&node, sizeof(HDNode)); @@ -272,7 +273,8 @@ static bool get_public_key(const uint8_t *seed, if (NULL != public_key) { // ecdsa_uncompress_pubkey( - // get_curve_by_name(SECP256K1_NAME)->params, node.public_key, public_key); + // get_curve_by_name(SECP256K1_NAME)->params, node.public_key, + // public_key); memcpy(public_key, node.public_key, XRP_PUB_KEY_SIZE); } @@ -338,11 +340,8 @@ static bool get_user_consent(const pb_size_t which_request, char msg[100] = ""; if (XRP_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { - snprintf(msg, - sizeof(msg), - UI_TEXT_ADD_ACCOUNT_PROMPT, - XRP_NAME, - wallet_name); + snprintf( + msg, sizeof(msg), UI_TEXT_ADD_ACCOUNT_PROMPT, XRP_NAME, wallet_name); } else { snprintf(msg, sizeof(msg), @@ -390,9 +389,8 @@ void xrp_get_pub_keys(xrp_query_t *query) { if (!check_which_request(query, XRP_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, - xrp_send_error)) { + !get_wallet_name_by_id( + init_req->wallet_id, (uint8_t *)wallet_name, xrp_send_error)) { return; } @@ -435,7 +433,10 @@ void xrp_get_pub_keys(xrp_query_t *query) { char address[XRP_ACCOUNT_ADDRESS_LENGTH] = ""; uint8_t public_key_digest[32]; - hasher_Raw(HASHER_SHA2_RIPEMD, pubkey_list[0], XRP_PUB_KEY_SIZE, public_key_digest); + hasher_Raw(HASHER_SHA2_RIPEMD, + pubkey_list[0], + XRP_PUB_KEY_SIZE, + public_key_digest); uint8_t prefixed_account_id[XRP_PREFIXED_ACCOUNT_ID_LENGTH]; prefixed_account_id[0] = 0x00; @@ -443,10 +444,10 @@ void xrp_get_pub_keys(xrp_query_t *query) { // xrp uses different base58 dictionary, that's why a custom function if (!xrp_base58_encode_check(prefixed_account_id, - XRP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - address, - XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return; } From 71296ef04a92dc59f1965b3f033cd8dd1a932569 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Sat, 28 Sep 2024 17:58:05 +0530 Subject: [PATCH 04/19] chore: Updated submodules-cypherock-common --- common/cypherock-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/cypherock-common b/common/cypherock-common index d60bfa9ab..a096bd3c0 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit d60bfa9ab8fc4f85b84ab41362e53622e7d2a2e1 +Subproject commit a096bd3c05ada2479ae30b7a3c2da94a83ef8dd6 From 09321bdd6a9f83585823b7596185acc61de950dc Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Tue, 1 Oct 2024 12:52:38 +0530 Subject: [PATCH 05/19] fix: Added custom base58 encdoing function --- apps/xrp_app/xrp_helpers.c | 65 -------------------------- apps/xrp_app/xrp_helpers.h | 24 ---------- apps/xrp_app/xrp_pub_key.c | 17 +++++-- common/libraries/crypto/base58.c | 80 +++++++++++++++++++++++--------- common/libraries/crypto/base58.h | 34 +++++++++++--- 5 files changed, 98 insertions(+), 122 deletions(-) diff --git a/apps/xrp_app/xrp_helpers.c b/apps/xrp_app/xrp_helpers.c index 3f3ae2c98..7cc3c3a08 100644 --- a/apps/xrp_app/xrp_helpers.c +++ b/apps/xrp_app/xrp_helpers.c @@ -86,9 +86,6 @@ * GLOBAL VARIABLES *****************************************************************************/ -const char xrp_b58digits_ordered[] = - "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; - /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ @@ -113,65 +110,3 @@ bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels) { return status; } - -bool xrp_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { - const uint8_t *bin = data; - int carry = 0; - size_t i = 0, j = 0, high = 0, zcount = 0; - size_t size = 0; - - while (zcount < binsz && !bin[zcount]) - ++zcount; - - size = (binsz - zcount) * 138 / 100 + 1; - uint8_t buf[size]; - memzero(buf, size); - - for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { - for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { - carry += 256 * buf[j]; - buf[j] = carry % 58; - carry /= 58; - if (!j) { - // Otherwise j wraps to maxint which is > high - break; - } - } - } - - for (j = 0; j < size && !buf[j]; ++j) - ; - - if (*b58sz <= zcount + size - j) { - *b58sz = zcount + size - j + 1; - return false; - } - - if (zcount) - memset(b58, 'r', zcount); - for (i = zcount; j < size; ++i, ++j) - b58[i] = xrp_b58digits_ordered[buf[j]]; - b58[i] = '\0'; - *b58sz = i + 1; - - return true; -} - -int xrp_base58_encode_check(const uint8_t *data, - int datalen, - HasherType hasher_type, - char *str, - int strsize) { - if (datalen > 128) { - return 0; - } - uint8_t buf[datalen + 32]; - memset(buf, 0, sizeof(buf)); - uint8_t *hash = buf + datalen; - memcpy(buf, data, datalen); - hasher_Raw(hasher_type, data, datalen, hash); - size_t res = strsize; - bool success = xrp_b58enc(str, &res, buf, datalen + 4); - memzero(buf, sizeof(buf)); - return success ? res : 0; -} diff --git a/apps/xrp_app/xrp_helpers.h b/apps/xrp_app/xrp_helpers.h index 637f589b9..8aed9f3dc 100644 --- a/apps/xrp_app/xrp_helpers.h +++ b/apps/xrp_app/xrp_helpers.h @@ -18,7 +18,6 @@ #include #include "coin_utils.h" -#include "hasher.h" /***************************************************************************** * MACROS AND DEFINES @@ -59,27 +58,4 @@ */ bool xrp_derivation_path_guard(const uint32_t *path, uint8_t levels); -/** - * @brief Generates the base58 encoded string from the data. - * @details The given data is hashed as per the given hasher_type. - * Then the checksum (first 4 bytes of hash digest) is taken and concatenated - * with the data. The concatenated data is then encoded using base58 - * with the dictionary - * rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz - * - * @param[in] data The data to encode - * @param[in] data_len The length of the given data - * @param[in] hasher_type The type of hash to generate checksum - * @param[out] str The string to store the encoded data in - * @param[in] strsize The size of str to generate - * @return int The size of str generated - * @retval Same as provided if successful - * @retval 0 otherwise - */ -int xrp_base58_encode_check(const uint8_t *data, - int datalen, - HasherType hasher_type, - char *str, - int strsize); - #endif // XRP_HELPERS_H \ No newline at end of file diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c index 8f2a2444d..bddfe2578 100644 --- a/apps/xrp_app/xrp_pub_key.c +++ b/apps/xrp_app/xrp_pub_key.c @@ -89,6 +89,12 @@ * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ +const char xrp_b58digits_ordered[B58DIGITS_ORDERED_SIZE] = + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; + /** * @brief Checks if the provided query contains expected request. * @details The function performs the check on the request type and if the check @@ -443,11 +449,12 @@ void xrp_get_pub_keys(xrp_query_t *query) { memcpy(prefixed_account_id + 1, public_key_digest, 20); // xrp uses different base58 dictionary, that's why a custom function - if (!xrp_base58_encode_check(prefixed_account_id, - XRP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - address, - XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { + if (!custom_base58_encode_check(prefixed_account_id, + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1, + xrp_b58digits_ordered)) { xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return; } diff --git a/common/libraries/crypto/base58.c b/common/libraries/crypto/base58.c index d95ac9a9d..21d3e1491 100644 --- a/common/libraries/crypto/base58.c +++ b/common/libraries/crypto/base58.c @@ -22,15 +22,17 @@ */ #include "base58.h" + #include #include + #include "memzero.h" #include "ripemd160.h" #include "sha2.h" -const char b58digits_ordered[] = +char b58digits_ordered[B58DIGITS_ORDERED_SIZE] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; -const int8_t b58digits_map[] = { +int8_t b58digits_map[B58DIGITS_MAP_SIZE] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, @@ -71,7 +73,8 @@ bool b58tobin(void *bin, size_t *binszp, const char *b58) { memzero(outi, sizeof(outi)); // Leading zeros, just count - for (i = 0; i < b58sz && b58u[i] == '1'; ++i) ++zerocount; + for (i = 0; i < b58sz && b58u[i] == '1'; ++i) + ++zerocount; for (; i < b58sz; ++i) { if (b58u[i] & 0x80) @@ -126,20 +129,25 @@ bool b58tobin(void *bin, size_t *binszp, const char *b58) { return true; } -int b58check(const void *bin, size_t binsz, HasherType hasher_type, +int b58check(const void *bin, + size_t binsz, + HasherType hasher_type, const char *base58str) { unsigned char buf[32] = {0}; const uint8_t *binc = bin; unsigned i = 0; - if (binsz < 4) return -4; + if (binsz < 4) + return -4; hasher_Raw(hasher_type, bin, binsz - 4, buf); - if (memcmp(&binc[binsz - 4], buf, 4)) return -1; + if (memcmp(&binc[binsz - 4], buf, 4)) + return -1; // Check number of zeros is correct AFTER verifying checksum (to avoid // possibility of accessing base58str beyond the end) for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { - } // Just finding the end of zeros, nothing to do in loop - if (binc[i] == '\0' || base58str[i] == '1') return -3; + } // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') + return -3; return binc[0]; } @@ -150,7 +158,8 @@ bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { size_t i = 0, j = 0, high = 0, zcount = 0; size_t size = 0; - while (zcount < binsz && !bin[zcount]) ++zcount; + while (zcount < binsz && !bin[zcount]) + ++zcount; size = (binsz - zcount) * 138 / 100 + 1; uint8_t buf[size]; @@ -176,16 +185,21 @@ bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { return false; } - if (zcount) memset(b58, '1', zcount); - for (i = zcount; j < size; ++i, ++j) b58[i] = b58digits_ordered[buf[j]]; + if (zcount) + memset(b58, b58digits_ordered[0], zcount); + for (i = zcount; j < size; ++i, ++j) + b58[i] = b58digits_ordered[buf[j]]; b58[i] = '\0'; *b58sz = i + 1; return true; } -int base58_encode_check(const uint8_t *data, int datalen, - HasherType hasher_type, char *str, int strsize) { +int base58_encode_check(const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize) { if (datalen > 128) { return 0; } @@ -200,7 +214,26 @@ int base58_encode_check(const uint8_t *data, int datalen, return success ? res : 0; } -int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, +int custom_base58_encode_check( + const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize, + const char input_b58digits_ordered[B58DIGITS_ORDERED_SIZE]) { + char temp[B58DIGITS_ORDERED_SIZE]; + memcpy(temp, b58digits_ordered, B58DIGITS_ORDERED_SIZE); + memcpy(b58digits_ordered, input_b58digits_ordered, B58DIGITS_ORDERED_SIZE); + + int res = base58_encode_check(data, datalen, hasher_type, str, strsize); + + memcpy(b58digits_ordered, temp, B58DIGITS_ORDERED_SIZE); + return res; +} + +int base58_decode_check(const char *str, + HasherType hasher_type, + uint8_t *data, int datalen) { if (datalen > 128) { return 0; @@ -224,20 +257,25 @@ int b58gphcheck(const void *bin, size_t binsz, const char *base58str) { unsigned char buf[32] = {0}; const uint8_t *binc = bin; unsigned i = 0; - if (binsz < 4) return -4; - ripemd160(bin, binsz - 4, buf); // No double SHA256, but a single RIPEMD160 - if (memcmp(&binc[binsz - 4], buf, 4)) return -1; + if (binsz < 4) + return -4; + ripemd160(bin, binsz - 4, buf); // No double SHA256, but a single RIPEMD160 + if (memcmp(&binc[binsz - 4], buf, 4)) + return -1; // Check number of zeros is correct AFTER verifying checksum (to avoid // possibility of accessing base58str beyond the end) for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { - } // Just finding the end of zeros, nothing to do in loop - if (binc[i] == '\0' || base58str[i] == '1') return -3; + } // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') + return -3; return binc[0]; } -int base58gph_encode_check(const uint8_t *data, int datalen, char *str, +int base58gph_encode_check(const uint8_t *data, + int datalen, + char *str, int strsize) { if (datalen > 128) { return 0; @@ -246,7 +284,7 @@ int base58gph_encode_check(const uint8_t *data, int datalen, char *str, memset(buf, 0, sizeof(buf)); uint8_t *hash = buf + datalen; memcpy(buf, data, datalen); - ripemd160(data, datalen, hash); // No double SHA256, but a single RIPEMD160 + ripemd160(data, datalen, hash); // No double SHA256, but a single RIPEMD160 size_t res = strsize; bool success = b58enc(str, &res, buf, datalen + 4); memzero(buf, sizeof(buf)); diff --git a/common/libraries/crypto/base58.h b/common/libraries/crypto/base58.h index 97cb54e60..dc9f4c786 100644 --- a/common/libraries/crypto/base58.h +++ b/common/libraries/crypto/base58.h @@ -26,25 +26,45 @@ #include #include + #include "hasher.h" #include "options.h" -extern const char b58digits_ordered[]; -extern const int8_t b58digits_map[]; +#define B58DIGITS_ORDERED_SIZE 61 +#define B58DIGITS_MAP_SIZE 128 + +extern char b58digits_ordered[B58DIGITS_ORDERED_SIZE]; +extern int8_t b58digits_map[B58DIGITS_MAP_SIZE]; -int base58_encode_check(const uint8_t *data, int len, HasherType hasher_type, - char *str, int strsize); -int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, +int custom_base58_encode_check( + const uint8_t *data, + int len, + HasherType hasher_type, + char *str, + int strsize, + const char input_b58digits_ordered[B58DIGITS_ORDERED_SIZE]); +int base58_encode_check(const uint8_t *data, + int len, + HasherType hasher_type, + char *str, + int strsize); +int base58_decode_check(const char *str, + HasherType hasher_type, + uint8_t *data, int datalen); // Private bool b58tobin(void *bin, size_t *binszp, const char *b58); -int b58check(const void *bin, size_t binsz, HasherType hasher_type, +int b58check(const void *bin, + size_t binsz, + HasherType hasher_type, const char *base58str); bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz); #if USE_GRAPHENE -int base58gph_encode_check(const uint8_t *data, int datalen, char *str, +int base58gph_encode_check(const uint8_t *data, + int datalen, + char *str, int strsize); int base58gph_decode_check(const char *str, uint8_t *data, int datalen); int b58gphcheck(const void *bin, size_t binsz, const char *base58str); From 17ae9e473771e168bd6d4fb761cef9dc46270185 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Fri, 4 Oct 2024 19:24:04 +0530 Subject: [PATCH 06/19] fix: Updated custom base58 encoding function --- apps/xrp_app/xrp_context.h | 2 ++ apps/xrp_app/xrp_pub_key.c | 22 ++++--------- common/cypherock-common | 2 +- common/libraries/crypto/base58.c | 55 +++++++++++++++++--------------- common/libraries/crypto/base58.h | 6 ++-- 5 files changed, 43 insertions(+), 44 deletions(-) diff --git a/apps/xrp_app/xrp_context.h b/apps/xrp_app/xrp_context.h index a03ff579c..445d2abb1 100644 --- a/apps/xrp_app/xrp_context.h +++ b/apps/xrp_app/xrp_context.h @@ -23,6 +23,8 @@ #define XRP_PUB_KEY_SIZE 33 #define XRP_PREFIXED_ACCOUNT_ID_LENGTH 21 #define XRP_ACCOUNT_ADDRESS_LENGTH 34 +#define XRP_BASE58_DIGITS_ORDERED \ + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz" /***************************************************************************** * TYPEDEFS diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c index bddfe2578..4ed7c24e3 100644 --- a/apps/xrp_app/xrp_pub_key.c +++ b/apps/xrp_app/xrp_pub_key.c @@ -89,12 +89,6 @@ * STATIC FUNCTION PROTOTYPES *****************************************************************************/ -/***************************************************************************** - * GLOBAL VARIABLES - *****************************************************************************/ -const char xrp_b58digits_ordered[B58DIGITS_ORDERED_SIZE] = - "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; - /** * @brief Checks if the provided query contains expected request. * @details The function performs the check on the request type and if the check @@ -278,9 +272,6 @@ static bool get_public_key(const uint8_t *seed, } if (NULL != public_key) { - // ecdsa_uncompress_pubkey( - // get_curve_by_name(SECP256K1_NAME)->params, node.public_key, - // public_key); memcpy(public_key, node.public_key, XRP_PUB_KEY_SIZE); } @@ -449,12 +440,13 @@ void xrp_get_pub_keys(xrp_query_t *query) { memcpy(prefixed_account_id + 1, public_key_digest, 20); // xrp uses different base58 dictionary, that's why a custom function - if (!custom_base58_encode_check(prefixed_account_id, - XRP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - address, - XRP_ACCOUNT_ADDRESS_LENGTH + 1, - xrp_b58digits_ordered)) { + if (!base58_encode_check_with_custom_digits_order( + prefixed_account_id, + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1, + XRP_BASE58_DIGITS_ORDERED)) { xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return; } diff --git a/common/cypherock-common b/common/cypherock-common index a096bd3c0..f39c88bf3 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit a096bd3c05ada2479ae30b7a3c2da94a83ef8dd6 +Subproject commit f39c88bf3b6262c53c35c3257c46cba731263827 diff --git a/common/libraries/crypto/base58.c b/common/libraries/crypto/base58.c index 21d3e1491..e2c837ed2 100644 --- a/common/libraries/crypto/base58.c +++ b/common/libraries/crypto/base58.c @@ -30,9 +30,9 @@ #include "ripemd160.h" #include "sha2.h" -char b58digits_ordered[B58DIGITS_ORDERED_SIZE] = +const char b58digits_ordered[B58DIGITS_ORDERED_SIZE] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; -int8_t b58digits_map[B58DIGITS_MAP_SIZE] = { +const int8_t b58digits_map[B58DIGITS_MAP_SIZE] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, @@ -152,7 +152,12 @@ int b58check(const void *bin, return binc[0]; } -bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { +bool b58enc_with_custom_digits_order( + char *b58, + size_t *b58sz, + const void *data, + size_t binsz, + const char input_b58digits_ordered[B58DIGITS_ORDERED_SIZE]) { const uint8_t *bin = data; int carry = 0; size_t i = 0, j = 0, high = 0, zcount = 0; @@ -186,20 +191,22 @@ bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { } if (zcount) - memset(b58, b58digits_ordered[0], zcount); + memset(b58, input_b58digits_ordered[0], zcount); for (i = zcount; j < size; ++i, ++j) - b58[i] = b58digits_ordered[buf[j]]; + b58[i] = input_b58digits_ordered[buf[j]]; b58[i] = '\0'; *b58sz = i + 1; return true; } -int base58_encode_check(const uint8_t *data, - int datalen, - HasherType hasher_type, - char *str, - int strsize) { +int base58_encode_check_with_custom_digits_order( + const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize, + const char input_b58digits_ordered[B58DIGITS_ORDERED_SIZE]) { if (datalen > 128) { return 0; } @@ -209,26 +216,24 @@ int base58_encode_check(const uint8_t *data, memcpy(buf, data, datalen); hasher_Raw(hasher_type, data, datalen, hash); size_t res = strsize; - bool success = b58enc(str, &res, buf, datalen + 4); + bool success = b58enc_with_custom_digits_order( + str, &res, buf, datalen + 4, input_b58digits_ordered); memzero(buf, sizeof(buf)); return success ? res : 0; } -int custom_base58_encode_check( - const uint8_t *data, - int datalen, - HasherType hasher_type, - char *str, - int strsize, - const char input_b58digits_ordered[B58DIGITS_ORDERED_SIZE]) { - char temp[B58DIGITS_ORDERED_SIZE]; - memcpy(temp, b58digits_ordered, B58DIGITS_ORDERED_SIZE); - memcpy(b58digits_ordered, input_b58digits_ordered, B58DIGITS_ORDERED_SIZE); - - int res = base58_encode_check(data, datalen, hasher_type, str, strsize); +bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { + return b58enc_with_custom_digits_order( + b58, b58sz, data, binsz, b58digits_ordered); +} - memcpy(b58digits_ordered, temp, B58DIGITS_ORDERED_SIZE); - return res; +int base58_encode_check(const uint8_t *data, + int datalen, + HasherType hasher_type, + char *str, + int strsize) { + return base58_encode_check_with_custom_digits_order( + data, datalen, hasher_type, str, strsize, b58digits_ordered); } int base58_decode_check(const char *str, diff --git a/common/libraries/crypto/base58.h b/common/libraries/crypto/base58.h index dc9f4c786..3e1086fff 100644 --- a/common/libraries/crypto/base58.h +++ b/common/libraries/crypto/base58.h @@ -33,10 +33,10 @@ #define B58DIGITS_ORDERED_SIZE 61 #define B58DIGITS_MAP_SIZE 128 -extern char b58digits_ordered[B58DIGITS_ORDERED_SIZE]; -extern int8_t b58digits_map[B58DIGITS_MAP_SIZE]; +extern const char b58digits_ordered[B58DIGITS_ORDERED_SIZE]; +extern const int8_t b58digits_map[B58DIGITS_MAP_SIZE]; -int custom_base58_encode_check( +int base58_encode_check_with_custom_digits_order( const uint8_t *data, int len, HasherType hasher_type, From 2b52ca94ebcd8cca22280f6d6ff4dc0419e97bc0 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Fri, 4 Oct 2024 12:36:43 +0530 Subject: [PATCH 07/19] feat: Added sign-txn functionality to xrp app --- apps/xrp_app/xrp_context.h | 72 ++++ apps/xrp_app/xrp_main.c | 2 +- apps/xrp_app/xrp_priv.h | 24 ++ apps/xrp_app/xrp_txn.c | 457 ++++++++++++++++++++++ apps/xrp_app/xrp_txn_helpers.c | 376 ++++++++++++++++++ apps/xrp_app/xrp_txn_helpers.h | 54 +++ common/proto-options/xrp/sign_txn.options | 4 + 7 files changed, 988 insertions(+), 1 deletion(-) create mode 100644 apps/xrp_app/xrp_txn.c create mode 100644 apps/xrp_app/xrp_txn_helpers.c create mode 100644 apps/xrp_app/xrp_txn_helpers.h create mode 100644 common/proto-options/xrp/sign_txn.options diff --git a/apps/xrp_app/xrp_context.h b/apps/xrp_app/xrp_context.h index 445d2abb1..02f692fd2 100644 --- a/apps/xrp_app/xrp_context.h +++ b/apps/xrp_app/xrp_context.h @@ -34,6 +34,78 @@ typedef struct { } xrp_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/XRPLF/xrpl.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]; +} xrp_unsigned_txn; + /***************************************************************************** * EXPORTED VARIABLES *****************************************************************************/ diff --git a/apps/xrp_app/xrp_main.c b/apps/xrp_app/xrp_main.c index e67d77254..50651dbd7 100644 --- a/apps/xrp_app/xrp_main.c +++ b/apps/xrp_app/xrp_main.c @@ -129,7 +129,7 @@ void xrp_main(usb_event_t usb_evt, const void *xrp_app_config) { break; } case XRP_QUERY_SIGN_TXN_TAG: { - // TODO: Add sign txn functionality + xrp_sign_transaction(&query); break; } default: { diff --git a/apps/xrp_app/xrp_priv.h b/apps/xrp_app/xrp_priv.h index b541abbb6..41cd123b2 100644 --- a/apps/xrp_app/xrp_priv.h +++ b/apps/xrp_app/xrp_priv.h @@ -21,6 +21,20 @@ /***************************************************************************** * TYPEDEFS *****************************************************************************/ +typedef struct { + /** + * The structure holds the wallet information of the transaction. + * @note Populated by xrp_handle_initiate_query() + */ + xrp_sign_txn_initiate_request_t init_info; + + // remembers the allocated buffer for holding complete unsigned transaction + uint8_t *transaction; + + // decoded raw txn + xrp_unsigned_txn *raw_txn; + +} xrp_txn_context_t; /***************************************************************************** * EXPORTED VARIABLES @@ -39,4 +53,14 @@ */ void xrp_get_pub_keys(xrp_query_t *query); +/** + * @brief Handler for signing a transaction on xrp. + * @details The expected request type is XRP_SIGN_TXN_REQUEST_INITIATE_TAG. The + * function controls the complete data exchange with host, user prompts and + * confirmations for signing an XRP based transaction. + * + * @param query Reference to the decoded query struct from the host app + */ +void xrp_sign_transaction(xrp_query_t *query); + #endif /* XRP_PRIV_H */ \ No newline at end of file diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c new file mode 100644 index 000000000..3d8411ab0 --- /dev/null +++ b/apps/xrp_app/xrp_txn.c @@ -0,0 +1,457 @@ +/** + * @file xrp_txn.c + * @author Cypherock X1 Team + * @brief Source file to handle transaction signing logic for XRP 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 "xrp_api.h" +#include "xrp_context.h" +#include "xrp_helpers.h" +#include "xrp_priv.h" +#include "xrp_txn_helpers.h" +// #include "xrp_txn_user_verification.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 xrp_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 xrp_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 xrp_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 xrp_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 xrp_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 + * XRP_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 xrp_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 xrp_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 xrp_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(xrp_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 XRP unsigned txn to the host + * @details The function waits for the host to send a request of type + * XRP_SIGN_TXN_REQUEST_SIGNATURE_TAG and sends the response + * + * @param query Reference to buffer of type xrp_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(xrp_query_t *query, const der_sig_t *der_signature); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ +static xrp_txn_context_t *xrp_txn_context = NULL; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static bool check_which_request(const xrp_query_t *query, + pb_size_t which_request) { + if (which_request != query->sign_txn.which_request) { + xrp_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) { + xrp_result_t result = init_xrp_result(XRP_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = which_response; + xrp_send_result(&result); +} + +static bool validate_request_data(const xrp_sign_txn_request_t *request) { + bool status = true; + + if (!xrp_derivation_path_guard(request->initiate.derivation_path, + request->initiate.derivation_path_count)) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + return status; +} + +static bool handle_initiate_query(const xrp_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, XRP_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, + xrp_send_error)) { + return false; + } + + snprintf(msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, XRP_NAME, wallet_name); + // Take user consent to sign transaction for the wallet + if (!core_confirmation(msg, xrp_send_error)) { + return false; + } + + set_app_flow_status(XRP_SIGN_TXN_STATUS_CONFIRM); + memcpy(&xrp_txn_context->init_info, + &query->sign_txn.initiate, + sizeof(xrp_sign_txn_initiate_request_t)); + + send_response(XRP_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(xrp_query_t *query) { + uint32_t size = 0; + xrp_result_t response = init_xrp_result(XRP_RESULT_SIGN_TXN_TAG); + uint32_t total_size = xrp_txn_context->init_info.transaction_size; + const xrp_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 + xrp_txn_context->transaction = (uint8_t *)malloc(total_size); + while (1) { + if (!xrp_get_query(query, XRP_QUERY_SIGN_TXN_TAG) || + !check_which_request(query, XRP_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) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + + memcpy(&xrp_txn_context->transaction[size], chunk->bytes, chunk->size); + size += chunk->size; + // Send chunk ack to host + response.sign_txn.which_response = XRP_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; + xrp_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) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + xrp_txn_context->raw_txn = + (xrp_unsigned_txn *)malloc(sizeof(xrp_unsigned_txn)); + + if (!xrp_parse_transaction( + xrp_txn_context->transaction, total_size, xrp_txn_context->raw_txn)) { + xrp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + + return true; +} + +static bool get_user_verification(void) { + const xrp_unsigned_txn *decoded_utxn = xrp_txn_context->raw_txn; + bool user_verified = false; + + char to_address[XRP_ACCOUNT_ADDRESS_LENGTH] = ""; + + uint8_t prefixed_account_id[XRP_PREFIXED_ACCOUNT_ID_LENGTH]; + prefixed_account_id[0] = 0x00; + memcpy(prefixed_account_id + 1, decoded_utxn->Destination, 20); + + // xrp uses different base58 dictionary, that's why a custom function + if (!xrp_base58_encode_check(prefixed_account_id, + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + to_address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { + xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); + return false; + } + + if (!core_scroll_page(ui_text_verify_address, to_address, xrp_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, + XRP_LUNIT); + + if (!core_confirmation(display, xrp_send_error)) { + return false; + } + + user_verified = true; + + if (user_verified) { + set_app_flow_status(XRP_SIGN_TXN_STATUS_VERIFY); + } + + return user_verified; +} + +static bool sign_txn(der_sig_t *der_signature) { + uint8_t seed[64] = {0}; + if (!reconstruct_seed( + xrp_txn_context->init_info.wallet_id, seed, xrp_send_error)) { + memzero(seed, sizeof(seed)); + // TODO: handle errors of reconstruction flow + return false; + } + + set_app_flow_status(XRP_SIGN_TXN_STATUS_SEED_GENERATED); + + uint8_t digest[SHA512_DIGEST_LENGTH] = {0}; + sha512_Raw(xrp_txn_context->transaction, + xrp_txn_context->init_info.transaction_size, + digest); + + HDNode hdnode = {0}; + derive_hdnode_from_path(xrp_txn_context->init_info.derivation_path, + xrp_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(xrp_query_t *query, const der_sig_t *der_signature) { + xrp_result_t result = init_xrp_result(XRP_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = XRP_SIGN_TXN_RESPONSE_SIGNATURE_TAG; + + if (!xrp_get_query(query, XRP_QUERY_SIGN_TXN_TAG) || + !check_which_request(query, XRP_SIGN_TXN_REQUEST_SIGNATURE_TAG)) { + return false; + } + + memcpy( + &result.sign_txn.signature.signature, der_signature, sizeof(der_sig_t)); + + xrp_send_result(&result); + return true; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void xrp_sign_transaction(xrp_query_t *query) { + xrp_txn_context = (xrp_txn_context_t *)malloc(sizeof(xrp_txn_context_t)); + memzero(xrp_txn_context, sizeof(xrp_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 (xrp_txn_context) { + free(xrp_txn_context); + xrp_txn_context = NULL; + } + + return; +} \ No newline at end of file diff --git a/apps/xrp_app/xrp_txn_helpers.c b/apps/xrp_app/xrp_txn_helpers.c new file mode 100644 index 000000000..5e987f95f --- /dev/null +++ b/apps/xrp_app/xrp_txn_helpers.c @@ -0,0 +1,376 @@ +/** + * @file xrp_txn_helpers.c + * @author Cypherock X1 Team + * @brief Helper functions for the XRP app for txn signing flow + * @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 "xrp_txn_helpers.h" + +#include +#include +#include +#include + +#include "utils.h" +#include "xrp_context.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +uint16_t decode_length(const uint8_t *byte_data, uint32_t *length) { + // See + // https://xrpl.org/docs/references/protocol/binary-format#length-prefixing + + uint32_t len = 0; + uint16_t bytes_read = 0; + + uint8_t byte1 = byte_data[0]; + if (byte1 <= 192) { + len = byte1; + bytes_read = 1; + } else if (byte1 <= 240) { + uint8_t byte2 = byte_data[1]; + len = 193 + ((byte1 - 193) * 256) + byte2; + bytes_read = 2; + } else if (byte1 <= 254) { + uint8_t byte2 = byte_data[1]; + uint8_t byte3 = byte_data[2]; + len = 12481 + ((byte1 - 241) * 65536) + (byte2 * 256) + byte3; + bytes_read = 3; + } + *length = len; + + return bytes_read; +} + +uint16_t decode_field_type(const uint8_t *byte_data, + uint8_t *type_code, + uint8_t *field_code) { + // See https://xrpl.org/docs/references/protocol/binary-format#field-ids + + uint8_t high_4_bits = byte_data[0] >> 4; + uint8_t low_4_bits = byte_data[0] & 0x0f; + + uint8_t type = 0; + uint8_t field = 0; + uint16_t bytes_read = 0; + + if (high_4_bits != 0 && low_4_bits != 0) { + type = high_4_bits; + field = low_4_bits; + bytes_read = 1; + } else if (high_4_bits == 0 && low_4_bits != 0) { + field = low_4_bits; + type = byte_data[1]; + bytes_read = 2; + } else if (high_4_bits != 0 && low_4_bits == 0) { + type = high_4_bits; + field = byte_data[1]; + bytes_read = 2; + } else if (high_4_bits == 0 && low_4_bits == 0) { + type = byte_data[1]; + field = byte_data[2]; + bytes_read = 3; + } + + *type_code = type; + *field_code = field; + + return bytes_read; +} + +uint16_t fill_INT16_type(const uint8_t *byte_data, + uint8_t field_code, + xrp_unsigned_txn *txn) { + uint16_t decoded = U16_READ_BE_ARRAY(byte_data); + switch (field_code) { + case TransactionType: { + txn->TransactionType = decoded; + break; + } + default: { + return 0; + } + } + return 2; // 16 bits = 2 bytes +} + +uint16_t fill_INT32_type(const uint8_t *byte_data, + uint8_t field_code, + xrp_unsigned_txn *txn) { + uint32_t decoded = U32_READ_BE_ARRAY(byte_data); + switch (field_code) { + case Flags: { + txn->Flags = decoded; + break; + } + case Sequence: { + txn->Sequence = decoded; + break; + } + case DestinationTag: { + txn->hasDestinationTag = true; + txn->DestinationTag = decoded; + break; + } + case LastLedgerSequence: { + txn->LastLedgerSequence = decoded; + break; + } + default: { + return 0; + } + } + + return 4; // 32 bits = 4 bytes +} + +uint16_t fill_AMOUNT_type(const uint8_t *byte_data, + uint8_t field_code, + xrp_unsigned_txn *txn) { + // See https://xrpl.org/docs/references/protocol/binary-format#amount-fields + + uint8_t not_xrp = byte_data[0] >> 7; // get the first bit + if (not_xrp) { + // means amount is not plain xrp. It is token currency amount which is not + // supported yet. + return 0; + } + + uint64_t decoded = U64_READ_BE_ARRAY(byte_data); + decoded &= 0xafffffffffffffff; // for xrp + + switch (field_code) { + case Amount: { + txn->Amount = decoded; + break; + } + case Fee: { + txn->Fee = decoded; + break; + } + default: { + return 0; + } + } + + return 8; // xrp amount is of fixed 64 bits = 8 bytes +} + +uint16_t fill_BLOB_type(const uint8_t *byte_data, + uint8_t field_code, + xrp_unsigned_txn *txn) { + // See https://xrpl.org/docs/references/protocol/binary-format#blob-fields + + uint32_t length = 0; + uint16_t bytes_read = decode_length(byte_data, &length); + if (!bytes_read || !length) { + return 0; + } + + switch (field_code) { + case SigningPubKey: { + memcpy(txn->SigningPubKey, byte_data, length); + break; + } + default: { + return 0; + } + } + bytes_read += length; + + return bytes_read; +} + +uint16_t fill_ACCOUNT_type(const uint8_t *byte_data, + uint8_t field_code, + xrp_unsigned_txn *txn) { + // See + // https://xrpl.org/docs/references/protocol/binary-format#accountid-fields + + uint32_t length = 0; + uint16_t bytes_read = decode_length(byte_data, &length); + if (!bytes_read || !length) { + return 0; + } + + switch (field_code) { + case Account: { + memcpy(txn->Account, byte_data, length); + break; + } + case Destination: { + memcpy(txn->Destination, byte_data, length); + break; + } + default: { + return 0; + } + } + bytes_read += length; + + return bytes_read; +} + +bool xrp_parse_transaction(const uint8_t *byte_array, + uint16_t byte_array_size, + xrp_unsigned_txn *txn) { + if (byte_array == NULL || txn == NULL) { + return false; + } + + // See https://xrpl.org/docs/references/protocol/binary-format + + txn->hasDestinationTag = false; + + uint16_t offset = 0; + offset += 4; // network prefix + + while (offset < byte_array_size) { + uint8_t type_code; + uint8_t field_code; + uint16_t bytes_read = + decode_field_type(byte_array + offset, &type_code, &field_code); + if (!bytes_read) { + return false; + } + + offset += bytes_read; + + switch (type_code) { + case INT16: { + bytes_read = fill_INT16_type(byte_array + offset, field_code, txn); + if (!bytes_read) { + return false; + } + offset += bytes_read; + break; + } + case INT32: { + bytes_read = fill_INT32_type(byte_array + offset, field_code, txn); + if (!bytes_read) { + return false; + } + offset += bytes_read; + break; + } + case AMOUNT: { + bytes_read = fill_AMOUNT_type(byte_array + offset, field_code, txn); + if (!bytes_read) { + return false; + } + offset += bytes_read; + break; + } + case BLOB: { + bytes_read = fill_BLOB_type(byte_array + offset, field_code, txn); + if (!bytes_read) { + return false; + } + offset += bytes_read; + break; + } + case ACCOUNT: { + bytes_read = fill_ACCOUNT_type(byte_array + offset, field_code, txn); + if (!bytes_read) { + return false; + } + offset += bytes_read; + break; + } + default: + return false; + } + } + + if (offset > byte_array_size) { + return false; + } + + return true; +} diff --git a/apps/xrp_app/xrp_txn_helpers.h b/apps/xrp_app/xrp_txn_helpers.h new file mode 100644 index 000000000..14f96508e --- /dev/null +++ b/apps/xrp_app/xrp_txn_helpers.h @@ -0,0 +1,54 @@ +/** + * @file xrp_txn_helpers.h + * @author Cypherock X1 Team + * @brief Helper functions for the XRP app for txn signing flow + * @copyright Copyright (c) 2024 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef XRP_TXN_HELPERS_H +#define XRP_TXN_HELPERS_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +#include "xrp_context.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Parse byte array of unsigned txn and store decoded information to be + * used for user confirmation. + * @details Only xrp payment transaction is supported as of now. + * + * @param byte_array Constant reference to buffer containing the raw unsigned + * txn + * @param byte_array_size Size in bytes of the txn + * @param utxn Reference to buffer where decoded information will be populated. + * It can be used at a later stage for user verification. + * @return true If the parsing was successful + * @return false If the parsing failed - it could be due to an unsupported + * transaction or data type or missing information + */ +bool xrp_parse_transaction(const uint8_t *byte_array, + uint16_t byte_array_size, + xrp_unsigned_txn *utxn); + +#endif /* XRP_TXN_HELPERS_H */ diff --git a/common/proto-options/xrp/sign_txn.options b/common/proto-options/xrp/sign_txn.options new file mode 100644 index 000000000..e83a81089 --- /dev/null +++ b/common/proto-options/xrp/sign_txn.options @@ -0,0 +1,4 @@ +# Options for file common/cypherock-common/proto/xrp/sign_txn.proto +xrp.SignTxnInitiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +xrp.SignTxnInitiateRequest.derivation_path type:FT_STATIC max_count:5 fixed_length:true +xrp.SignTxnSignatureResponse.signature type:FT_STATIC max_size:72 fixed_length:false From 1d847ce65995ae17978680e778f355c7f995df65 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Fri, 4 Oct 2024 18:52:20 +0530 Subject: [PATCH 08/19] fix: Fixed wrong address display issue --- apps/xrp_app/xrp_txn_helpers.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/xrp_app/xrp_txn_helpers.c b/apps/xrp_app/xrp_txn_helpers.c index 5e987f95f..5c1bc3388 100644 --- a/apps/xrp_app/xrp_txn_helpers.c +++ b/apps/xrp_app/xrp_txn_helpers.c @@ -114,13 +114,13 @@ uint16_t decode_length(const uint8_t *byte_data, uint32_t *length) { len = byte1; bytes_read = 1; } else if (byte1 <= 240) { - uint8_t byte2 = byte_data[1]; - len = 193 + ((byte1 - 193) * 256) + byte2; + uint16_t byte2 = byte_data[1]; + len = 193 + (((uint16_t)byte1 - 193) * 256) + byte2; bytes_read = 2; } else if (byte1 <= 254) { - uint8_t byte2 = byte_data[1]; - uint8_t byte3 = byte_data[2]; - len = 12481 + ((byte1 - 241) * 65536) + (byte2 * 256) + byte3; + uint32_t byte2 = byte_data[1]; + uint32_t byte3 = byte_data[2]; + len = 12481 + (((uint32_t)byte1 - 241) * 65536) + (byte2 * 256) + byte3; bytes_read = 3; } *length = len; @@ -255,7 +255,7 @@ uint16_t fill_BLOB_type(const uint8_t *byte_data, switch (field_code) { case SigningPubKey: { - memcpy(txn->SigningPubKey, byte_data, length); + memcpy(txn->SigningPubKey, byte_data + bytes_read, length); break; } default: { @@ -281,11 +281,11 @@ uint16_t fill_ACCOUNT_type(const uint8_t *byte_data, switch (field_code) { case Account: { - memcpy(txn->Account, byte_data, length); + memcpy(txn->Account, byte_data + bytes_read, length); break; } case Destination: { - memcpy(txn->Destination, byte_data, length); + memcpy(txn->Destination, byte_data + bytes_read, length); break; } default: { From e75f4bf2c6d3a92b1b5dc9298f0acdabc04e2706 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Sat, 5 Oct 2024 12:43:12 +0530 Subject: [PATCH 09/19] fix: Fixed custom base58 function call --- apps/xrp_app/xrp_txn.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c index 3d8411ab0..b19c07cd9 100644 --- a/apps/xrp_app/xrp_txn.c +++ b/apps/xrp_app/xrp_txn.c @@ -338,11 +338,13 @@ static bool get_user_verification(void) { memcpy(prefixed_account_id + 1, decoded_utxn->Destination, 20); // xrp uses different base58 dictionary, that's why a custom function - if (!xrp_base58_encode_check(prefixed_account_id, - XRP_PREFIXED_ACCOUNT_ID_LENGTH, - HASHER_SHA2D, - to_address, - XRP_ACCOUNT_ADDRESS_LENGTH + 1)) { + if (!base58_encode_check_with_custom_digits_order( + prefixed_account_id, + XRP_PREFIXED_ACCOUNT_ID_LENGTH, + HASHER_SHA2D, + to_address, + XRP_ACCOUNT_ADDRESS_LENGTH + 1, + XRP_BASE58_DIGITS_ORDERED)) { xrp_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); return false; } From 10d293e19909b3436a3a758b1b25d971d3a5b983 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Wed, 16 Oct 2024 12:07:37 +0530 Subject: [PATCH 10/19] feat: Added verify destination tag field to xrp --- apps/xrp_app/xrp_txn.c | 21 ++++++++++++++++----- src/constant_texts.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c index b19c07cd9..fb7fc6cc9 100644 --- a/apps/xrp_app/xrp_txn.c +++ b/apps/xrp_app/xrp_txn.c @@ -329,7 +329,6 @@ static bool fetch_valid_input(xrp_query_t *query) { static bool get_user_verification(void) { const xrp_unsigned_txn *decoded_utxn = xrp_txn_context->raw_txn; - bool user_verified = false; char to_address[XRP_ACCOUNT_ADDRESS_LENGTH] = ""; @@ -372,13 +371,25 @@ static bool get_user_verification(void) { return false; } - user_verified = true; + if (decoded_utxn->hasDestinationTag) { + // verify destination tag + uint32_t tag = 0; + memcpy(&tag, &decoded_utxn->DestinationTag, sizeof(uint32_t)); - if (user_verified) { - set_app_flow_status(XRP_SIGN_TXN_STATUS_VERIFY); + char display_tag[50] = {'\0'}; + snprintf(display_tag, + sizeof(display_tag), + UI_TEXT_VERIFY_DESTINATION_TAG, + tag); + + if (!core_confirmation(display_tag, xrp_send_error)) { + return false; + } } - return user_verified; + set_app_flow_status(XRP_SIGN_TXN_STATUS_VERIFY); + + return true; } static bool sign_txn(der_sig_t *der_signature) { diff --git a/src/constant_texts.h b/src/constant_texts.h index af5be9c8d..4d173d633 100644 --- a/src/constant_texts.h +++ b/src/constant_texts.h @@ -53,6 +53,7 @@ #define UI_TEXT_BLIND_SIGNING_WARNING \ LV_SYMBOL_WARNING " Blind Signing\nProceed at your own risk!" #define UI_TEXT_VERIFY_HD_PATH "Verify Derivation Path" +#define UI_TEXT_VERIFY_DESTINATION_TAG "Verify Destination Tag\n%lu" // product hash extern const char *product_hash; From 4cc933ee831075a674339af4add662f3af5ae71c Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Wed, 16 Oct 2024 12:16:39 +0530 Subject: [PATCH 11/19] fix: Corrected code formatting --- apps/xrp_app/xrp_txn.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c index fb7fc6cc9..2d8717c86 100644 --- a/apps/xrp_app/xrp_txn.c +++ b/apps/xrp_app/xrp_txn.c @@ -377,10 +377,8 @@ static bool get_user_verification(void) { 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); + snprintf( + display_tag, sizeof(display_tag), UI_TEXT_VERIFY_DESTINATION_TAG, tag); if (!core_confirmation(display_tag, xrp_send_error)) { return false; From b49a36cb6412c31b48dc3d02585f6e86e808b4b7 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Mon, 21 Oct 2024 14:01:45 +0530 Subject: [PATCH 12/19] fix: Resolved review comments --- apps/xrp_app/xrp_txn.c | 1 - apps/xrp_app/xrp_txn_helpers.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c index 2d8717c86..ac6eb441c 100644 --- a/apps/xrp_app/xrp_txn.c +++ b/apps/xrp_app/xrp_txn.c @@ -68,7 +68,6 @@ #include "xrp_helpers.h" #include "xrp_priv.h" #include "xrp_txn_helpers.h" -// #include "xrp_txn_user_verification.h" #include "reconstruct_wallet_flow.h" #include "status_api.h" #include "ui_core_confirm.h" diff --git a/apps/xrp_app/xrp_txn_helpers.c b/apps/xrp_app/xrp_txn_helpers.c index 5c1bc3388..1eb6e169c 100644 --- a/apps/xrp_app/xrp_txn_helpers.c +++ b/apps/xrp_app/xrp_txn_helpers.c @@ -62,7 +62,6 @@ #include "xrp_txn_helpers.h" -#include #include #include #include @@ -297,6 +296,7 @@ uint16_t fill_ACCOUNT_type(const uint8_t *byte_data, return bytes_read; } +// @TODO: add unit tests for parser bool xrp_parse_transaction(const uint8_t *byte_array, uint16_t byte_array_size, xrp_unsigned_txn *txn) { From ebe9d11c75aa78a218c6d9ab071bdc9a79a64d70 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Mon, 21 Oct 2024 14:11:04 +0530 Subject: [PATCH 13/19] fix: Fixed code formatting --- apps/xrp_app/xrp_txn.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/xrp_app/xrp_txn.c b/apps/xrp_app/xrp_txn.c index ac6eb441c..7c09c845b 100644 --- a/apps/xrp_app/xrp_txn.c +++ b/apps/xrp_app/xrp_txn.c @@ -63,16 +63,16 @@ #include -#include "xrp_api.h" -#include "xrp_context.h" -#include "xrp_helpers.h" -#include "xrp_priv.h" -#include "xrp_txn_helpers.h" #include "reconstruct_wallet_flow.h" #include "status_api.h" #include "ui_core_confirm.h" #include "ui_screens.h" #include "wallet_list.h" +#include "xrp_api.h" +#include "xrp_context.h" +#include "xrp_helpers.h" +#include "xrp_priv.h" +#include "xrp_txn_helpers.h" /***************************************************************************** * EXTERN VARIABLES From 955d9a2a89313c3aac81c4211b399afa4702cb0b Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Tue, 22 Oct 2024 23:10:35 +0530 Subject: [PATCH 14/19] chore: Updated common submodule --- common/cypherock-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/cypherock-common b/common/cypherock-common index f39c88bf3..38a0096f2 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit f39c88bf3b6262c53c35c3257c46cba731263827 +Subproject commit 38a0096f2d5d8ee50ec3205c1327a2f73395c3b1 From 6cefab50a601935bd045e75a256c64fedbe7c00d Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Fri, 25 Oct 2024 00:10:40 -0700 Subject: [PATCH 15/19] chore: Version bump (0.6.10247) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 2aba5c151..47f774a3a 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:003:000 +firmware version=000:006:004:003 hardware version=000:001:000:000 magic number=45227A01 From 3bc870d563c044959e3e1cd1aad031223f27c722 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Wed, 30 Oct 2024 21:25:30 +0530 Subject: [PATCH 16/19] fix: Corrected the receive text in xrp --- apps/xrp_app/xrp_pub_key.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c index 4ed7c24e3..426f3ee73 100644 --- a/apps/xrp_app/xrp_pub_key.c +++ b/apps/xrp_app/xrp_pub_key.c @@ -342,8 +342,7 @@ static bool get_user_consent(const pb_size_t which_request, } else { snprintf(msg, sizeof(msg), - UI_TEXT_RECEIVE_TOKEN_PROMPT, - XRP_LUNIT, + UI_TEXT_RECEIVE_PROMPT, XRP_NAME, wallet_name); } From bc5c3ada3f4c96f3cce592aa5af6ff78d0cab781 Mon Sep 17 00:00:00 2001 From: Muzaffar Ahmad Bhat Date: Wed, 30 Oct 2024 21:34:50 +0530 Subject: [PATCH 17/19] fix: Corrected code formatting --- apps/xrp_app/xrp_pub_key.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/xrp_app/xrp_pub_key.c b/apps/xrp_app/xrp_pub_key.c index 426f3ee73..4b0c04b6c 100644 --- a/apps/xrp_app/xrp_pub_key.c +++ b/apps/xrp_app/xrp_pub_key.c @@ -340,11 +340,7 @@ static bool get_user_consent(const pb_size_t which_request, snprintf( msg, sizeof(msg), UI_TEXT_ADD_ACCOUNT_PROMPT, XRP_NAME, wallet_name); } else { - snprintf(msg, - sizeof(msg), - UI_TEXT_RECEIVE_PROMPT, - XRP_NAME, - wallet_name); + snprintf(msg, sizeof(msg), UI_TEXT_RECEIVE_PROMPT, XRP_NAME, wallet_name); } return core_scroll_page(NULL, msg, xrp_send_error); From 8f0c54df4b533e210fe51ff527d601afda701a30 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 6 Nov 2024 16:18:47 +0530 Subject: [PATCH 18/19] chore: Version bump (0.6.10248) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 47f774a3a..213d74271 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:004:003 +firmware version=000:006:004:004 hardware version=000:001:000:000 magic number=45227A01 From f5edf073eed544177c58e40862ae398607725121 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 6 Nov 2024 21:40:05 +0530 Subject: [PATCH 19/19] chore: Reset version bump --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 213d74271..9f000c66a 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:004:004 +firmware version=000:006:003:001 hardware version=000:001:000:000 magic number=45227A01