From 3251bb2452b243c04e949f5038dc5acd969f42c9 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Sat, 30 Nov 2024 07:09:41 -0800 Subject: [PATCH 01/26] chore: Remove obsolete bitcoin input parser and its test --- apps/btc_family/btc_txn.c | 3 - apps/btc_family/btc_txn_helpers.c | 268 ------------------ apps/btc_family/btc_txn_helpers.h | 63 ----- tests/apps/btc_app/btc_txn_helpers_tests.c | 309 --------------------- tests/unit_test_lists.c | 7 - 5 files changed, 650 deletions(-) diff --git a/apps/btc_family/btc_txn.c b/apps/btc_family/btc_txn.c index ed3fb0f7..9622d174 100644 --- a/apps/btc_family/btc_txn.c +++ b/apps/btc_family/btc_txn.c @@ -446,9 +446,6 @@ static bool fetch_valid_input(btc_query_t *query) { // verify transaction details and discard the raw-transaction (prev_txn) const btc_prev_txn_chunk_t *prev_txn = &(query->sign_txn.prev_txn_chunk); - btc_verify_input_t verify_input_data; - memzero(&(verify_input_data), sizeof(btc_verify_input_t)); - // req prev txn chunk from host if (!btc_get_query(query, BTC_QUERY_SIGN_TXN_TAG) || !check_which_request(query, BTC_SIGN_TXN_REQUEST_PREV_TXN_CHUNK_TAG)) { diff --git a/apps/btc_family/btc_txn_helpers.c b/apps/btc_family/btc_txn_helpers.c index 772cf2ac..47f79390 100644 --- a/apps/btc_family/btc_txn_helpers.c +++ b/apps/btc_family/btc_txn_helpers.c @@ -65,7 +65,6 @@ #include #include "bignum.h" -#include "btc_helpers.h" #include "btc_script.h" #include "utils.h" @@ -328,277 +327,10 @@ STATIC bool calculate_p2wpkh_digest(const btc_txn_context_t *context, return true; } -static void update_hash(btc_verify_input_t *verify_input_data, - const uint8_t *raw_txn_chunk, - int chunk_index, - int32_t offset) { - hash_case update = DEFAULT; - - if (0 == chunk_index) { - update = FIRST_CHUNK_HASH; - } - switch (update) { - case FIRST_CHUNK_HASH: { - if (verify_input_data->is_segwit) { - sha256_Update(&(verify_input_data->sha_256_ctx), raw_txn_chunk, 4); - // skip marker and flag - sha256_Update( - &(verify_input_data->sha_256_ctx), raw_txn_chunk + 6, offset - 6); - } else { - sha256_Update(&(verify_input_data->sha_256_ctx), raw_txn_chunk, offset); - } - return; - break; - } - - default: { - sha256_Update(&(verify_input_data->sha_256_ctx), raw_txn_chunk, offset); - break; - } - } -} - -static void update_locktime(btc_verify_input_t *verify_input_data, - const uint8_t *raw_txn_chunk, - int chunk_index) { - if (verify_input_data->is_locktime_split) { - // last second chunk - if (chunk_index + 2 == verify_input_data->chunk_total) { - memcpy( - verify_input_data->locktime, - raw_txn_chunk + (CHUNK_SIZE - 4 - verify_input_data->size_last_chunk), - 4 - verify_input_data->size_last_chunk); - return; - } else if (chunk_index + 1 == verify_input_data->chunk_total) { - memcpy( - verify_input_data->locktime + 4 - verify_input_data->size_last_chunk, - raw_txn_chunk, - verify_input_data->size_last_chunk); - verify_input_data->has_locktime = true; - return; - } else { - // wait for subsequent chunks - return; - } - } else if (chunk_index + 1 == verify_input_data->chunk_total) { - memcpy(verify_input_data->locktime, - raw_txn_chunk + verify_input_data->size_last_chunk - 4, - 4); - verify_input_data->has_locktime = true; - } else { - // wait for subsequent chunks - return; - } -} - -// TODO: Add chunking condition for varint decode -// refer: https://app.clickup.com/t/9002019994/PRF-7288 -static int64_t varint_decode(const uint8_t *raw_txn_chunk, int32_t *offset) { - uint8_t first_byte = raw_txn_chunk[*offset]; - if (first_byte < 0xFD) { - return first_byte; - } else { - // TODO: var-int varies between 1-9 bytes - // current implementation supports decoding - // upto 3 bytes only - uint8_t result[2]; - memcpy(result, raw_txn_chunk + *offset + 1, 2); - *offset += 2; - return U16_READ_LE_ARRAY(result); - } -} /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ -int btc_verify_input(const uint8_t *raw_txn_chunk, - const uint32_t chunk_index, - btc_verify_input_t *verify_input_data, - const btc_sign_txn_input_t *input) { - if (NULL == input || NULL == raw_txn_chunk || - 0 == verify_input_data->chunk_total) { - return -1; - } - - int32_t offset = 0; - if (chunk_index == 0) { - // ignore network version (4-bytes), skip marker & flag (in segwit) - offset += (raw_txn_chunk[4] == 0 ? 6 : 4); - if (6 == offset) { - verify_input_data->is_segwit = true; - } - - // TODO: Improve varint decode. - // size of variable containing script size and ip-count/op-count - // varies (1-9 Bytes) depending on its value. - // refer: - // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - verify_input_data->count = - raw_txn_chunk[offset++]; ///< store the number of inputs in the - ///< raw_txn - - verify_input_data->parsetype = INPUT; - sha256_Init(&(verify_input_data->sha_256_ctx)); - } else { - offset += verify_input_data->prev_offset; - } - switch (verify_input_data->parsetype) { - case INPUT: { - while (verify_input_data->input_index < (verify_input_data->count) && - INPUT == verify_input_data->parsetype) { - input_case ip_case = verify_input_data->input_parse; - switch (ip_case) { - case PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE: { - if (offset + 36 >= CHUNK_SIZE) { - verify_input_data->prev_offset = (offset + 36) - CHUNK_SIZE; - update_hash( - verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - verify_input_data->input_parse = SCRIPT_LENGTH_CASE; - return 4; - } else { - offset += 36; - } - } - - case SCRIPT_LENGTH_CASE: { - int64_t script_length = varint_decode(raw_txn_chunk, &offset); - if (offset + script_length + 1 + 4 >= CHUNK_SIZE) { - verify_input_data->prev_offset = - (offset + script_length + 1 + 4) - CHUNK_SIZE; - update_hash( - verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - verify_input_data->input_parse = - PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE; - verify_input_data->input_index++; - return 4; - } else { - offset += (script_length + 1 + 4); - } - break; - } - - default: - break; - } - verify_input_data->input_parse = PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE; - verify_input_data->input_index++; - } - verify_input_data->parsetype = OP_COUNT; - } - - case OP_COUNT: { - if (offset + 1 >= CHUNK_SIZE) { - // reset prev offset - verify_input_data->prev_offset = - offset - CHUNK_SIZE; ///< Did not add +1 as returning back to - ///< this stage to read op count - update_hash(verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - return 4; - } else { - verify_input_data->count = raw_txn_chunk[offset++]; - } - verify_input_data->parsetype = OUTPUT; - verify_input_data->output_parse = VALUE_CASE; - } - - case OUTPUT: { - while (verify_input_data->output_index < verify_input_data->count) { - output_case op_case = verify_input_data->output_parse; - switch (op_case) { - case VALUE_CASE: { - if (verify_input_data->output_index == input->prev_output_index) { - if (offset + 8 >= CHUNK_SIZE) { - verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE; - memcpy(verify_input_data->value, - raw_txn_chunk + offset, - CHUNK_SIZE - offset); - update_hash( - verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - verify_input_data->output_parse = VALUE_SPLIT_CASE; - verify_input_data->is_split = 1; - return 4; - } else { - memcpy(verify_input_data->value, raw_txn_chunk + offset, 8); - } - } - if (offset + 8 >= CHUNK_SIZE) { - verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE; - update_hash( - verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - verify_input_data->output_parse = SCRIPT_PUBKEY_CASE; - return 4; - } else { - offset += 8; - } - } - - case VALUE_SPLIT_CASE: { - if (verify_input_data->is_split) { - memcpy(verify_input_data->value + (8 - offset), - raw_txn_chunk, - offset); - verify_input_data->output_parse = SCRIPT_PUBKEY_CASE; - verify_input_data->is_split = 0; - } - } - - case SCRIPT_PUBKEY_CASE: { - if (offset + raw_txn_chunk[offset] + 1 >= CHUNK_SIZE) { - verify_input_data->prev_offset = - (offset + raw_txn_chunk[offset] + 1) - CHUNK_SIZE; - update_hash( - verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE); - verify_input_data->output_parse = VALUE_CASE; - verify_input_data->output_index++; - return 4; - } else { - offset += (raw_txn_chunk[offset] + 1); - } - break; - } - default: - break; - } - verify_input_data->output_parse = VALUE_CASE; - verify_input_data->output_index++; - } - - verify_input_data->parsetype = LOCK_TIME; - update_hash(verify_input_data, raw_txn_chunk, chunk_index, offset); - } - - case LOCK_TIME: { - update_locktime(verify_input_data, raw_txn_chunk, chunk_index); - if (false == verify_input_data->has_locktime) { - return 4; - } - sha256_Update( - &(verify_input_data->sha_256_ctx), verify_input_data->locktime, 4); - } - default: - break; - } - - verify_input_data->parsetype = END; - - // Finalize hashing - uint8_t hash[SHA256_DIGEST_LENGTH] = {0}; - sha256_Final(&(verify_input_data->sha_256_ctx), hash); - - if (U64_READ_LE_ARRAY(verify_input_data->value) == 0) { - return 1; - } - sha256_Raw(hash, sizeof(hash), hash); - // verify input txn hash - if (memcmp(hash, input->prev_txn_hash, sizeof(input->prev_txn_hash)) != 0) { - return 2; - } - if (U64_READ_LE_ARRAY(verify_input_data->value) != input->value) { - return 3; - } - return 0; -} - uint64_t get_transaction_fee_threshold(const btc_txn_context_t *txn_ctx) { return (g_btc_app->max_fee / 1000) * (get_transaction_weight(txn_ctx) / 4); } diff --git a/apps/btc_family/btc_txn_helpers.h b/apps/btc_family/btc_txn_helpers.h index 3555b013..fb4893ff 100644 --- a/apps/btc_family/btc_txn_helpers.h +++ b/apps/btc_family/btc_txn_helpers.h @@ -13,9 +13,7 @@ * INCLUDES *****************************************************************************/ -#include "btc/sign_txn.pb.h" #include "btc_priv.h" -#include "sha2.h" /***************************************************************************** * MACROS AND DEFINES @@ -26,39 +24,6 @@ /***************************************************************************** * TYPEDEFS *****************************************************************************/ -typedef enum parse_type { INPUT, OP_COUNT, OUTPUT, LOCK_TIME, END } parse_type; - -typedef enum input_case { - PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE, - SCRIPT_LENGTH_CASE, - SEQ_CASE -} input_case; - -typedef enum output_case { - VALUE_CASE, - VALUE_SPLIT_CASE, - SCRIPT_PUBKEY_CASE -} output_case; - -typedef enum hash_case { FIRST_CHUNK_HASH, DEFAULT } hash_case; -typedef struct btc_verify_input { - int32_t chunk_total; - int32_t count; // count of ip/op - int32_t prev_offset; // offset to remember from prev chunk - int32_t input_index; - int32_t output_index; - SHA256_CTX sha_256_ctx; - parse_type parsetype; - input_case input_parse; - output_case output_parse; - bool is_segwit; - bool is_split; - bool has_locktime; - bool is_locktime_split; - int32_t size_last_chunk; - uint8_t value[8]; - uint8_t locktime[4]; -} btc_verify_input_t; /***************************************************************************** * EXPORTED VARIABLES @@ -68,34 +33,6 @@ typedef struct btc_verify_input { * GLOBAL FUNCTION PROTOTYPES *****************************************************************************/ -/** - * @brief Verifies the provided input with its related raw transaction byte - * @details The function verifies if the input details match with the details in - * the raw transaction. This is done by checking the output value against the - * specified output index in the raw transaction and then finally matching the - * specified hash with the calculated hash from the raw transactions bytes. - * To remove size limitations, the function requests the prev_txn from host - * in chunks of size CHUNK_SIZE. - * - * @param [in] raw_txn_chunk current chunk of transaction. - * @param [in] chunk_index index of current chunk. - * @param [in] verify_input_data struct to hold data and flags required by - * parser. - * @param [in] input Immutable reference to the btc_txn_input_t. - * - * @return int Result of verification, 0 if verified otherwise error status. - * @retval 0 Input verified successfully. - * @retval -1 If function parameters are invalid - * @retval 1 If specified output index (input->prev_output_index) is not present - * @retval 2 If there is a hash (input->prev_txn_hash) mismatch - * @retval 3 If there is a value (input->value) mismatch - * @retval 4 If in processing state, not all chunks parsed - */ -int btc_verify_input(const uint8_t *raw_txn_chunk, - const uint32_t chunk_index, - btc_verify_input_t *verify_input_data, - const btc_sign_txn_input_t *input); - /** * @brief Calculates an estimated upper cap on the transaction fee. * @details The function calculates the fee according to the assumed upper cap diff --git a/tests/apps/btc_app/btc_txn_helpers_tests.c b/tests/apps/btc_app/btc_txn_helpers_tests.c index 18d6a6bb..95ef429d 100644 --- a/tests/apps/btc_app/btc_txn_helpers_tests.c +++ b/tests/apps/btc_app/btc_txn_helpers_tests.c @@ -69,39 +69,6 @@ uint32_t get_transaction_weight(const btc_txn_context_t *txn_ctx); -// wrapper function to call 'btc_verify_input' -// The function takes the entire 'raw_txn' and feeds it to -// 'btc_verify_input' in chunks of size CHUNK_SIZE, simulating the sdk -int btc_verify_input_test(btc_sign_txn_input_t *input, - uint8_t *raw_txn, - int ip_txn_bytes_size) { - btc_verify_input_t verify_input_data; - int status = 4; - memzero(&(verify_input_data), sizeof(btc_verify_input_t)); - verify_input_data.chunk_total = (ip_txn_bytes_size % CHUNK_SIZE == 0) - ? (ip_txn_bytes_size / CHUNK_SIZE) - : (ip_txn_bytes_size / CHUNK_SIZE) + 1; - - uint8_t txn_chunk[CHUNK_SIZE] = {0}; - verify_input_data.size_last_chunk = ip_txn_bytes_size % CHUNK_SIZE; - int index = 0; - for (int chunk_var = ip_txn_bytes_size; chunk_var > 0; - chunk_var -= CHUNK_SIZE) { - int chvar = (chunk_var >= CHUNK_SIZE) ? CHUNK_SIZE : chunk_var % CHUNK_SIZE; - - for (int i = 0; i < (chvar); i++) { - txn_chunk[i] = raw_txn[i + (CHUNK_SIZE * index)]; - } - - status = btc_verify_input(txn_chunk, index, &verify_input_data, input); - if (status != 4) { - break; - } - index++; - } - return status; -} - TEST_GROUP(btc_txn_helper_test); /** @@ -124,282 +91,6 @@ TEST_TEAR_DOWN(btc_txn_helper_test) { g_btc_app = NULL; } -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2pk) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9?format=hex - * txnElements - - * https://blockchain.info/rawtx/f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[300] = {0}; - hex_string_to_byte_array( - "010000000100000000000000000000000000000000000000000000000000000000000000" - "00ffffffff0704ffff001d0134ffffffff0100f2052a0100000043410411db93e1dcdb8a" - "016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464" - "f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000", - 268, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = {.prev_output_index = 0, - .value = 5000000000, - .script_pub_key = { - .size = 67, - }}; - hex_string_to_byte_array( - "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9", - 64, - input.prev_txn_hash); - // revere order of txn-id: - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - - hex_string_to_byte_array( - "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0" - "eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac", - 134, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 134); - - TEST_ASSERT_EQUAL_INT(0, status); -} - -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2pk_fail) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9?format=hex - * txnElements - - * https://blockchain.info/rawtx/f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[300] = {0}; - hex_string_to_byte_array( - "010000000100000000000000000000000000000000000000000000000000000000000000" - "00ffffffff0704ffff001d0134ffffffff0100f2052a0100000043410411db93e1dcdb8a" - "016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464" - "f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000", - 268, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = { - .prev_output_index = 1, // incorrect index; correct is '0' - .value = 5000000000, - .script_pub_key = { - .size = 67, - }}; - hex_string_to_byte_array( - "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9", - 64, - input.prev_txn_hash); - // revere order of txn-id: - // 0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9 - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - hex_string_to_byte_array( - "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0" - "eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac", - 134, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 134); - - TEST_ASSERT_EQUAL_INT(1, status); -} - -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2pkh) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe?format=hex - * txnElements - - * https://blockchain.info/rawtx/16fbc39570ac5f16c103a39da1920ab3b77ad4f21f3c6d415c745bd6a37097e1?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[300] = {0}; - hex_string_to_byte_array( - "01000000014da2d059c1c6eb1c66884643f3bfa917cdb182273bf9dd2361db0c1c6bc706" - "61000000008b483045022100f2522df4a0d2193ee53ad95b698bf502e5874d340b4bb4f8" - "0720015f2ce87296022061d33d02d6f4a3b131a18328c3b1249e53fa115735c2597e29b0" - "738a1d5f3f8801410445bd85326dabc1772b4b319e0dc924ef93caf2360a033941e427f0" - "397b265f3c46e805be40904034880e781ab758a9e67518c624393e2ac14339faa45fafc5" - "deffffffff0100e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a" - "238c88ac00000000", - 448, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = {.prev_output_index = 0, - .value = 100000000, - .script_pub_key = { - .size = 25, - }}; - hex_string_to_byte_array( - "eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe", - 64, - input.prev_txn_hash); - // revere order of txn-id: - // eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - hex_string_to_byte_array("76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac", - 50, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 224); - TEST_ASSERT_EQUAL_INT(0, status); -} - -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2pkh_fail) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe?format=hex - * txnElements - - * https://blockchain.info/rawtx/16fbc39570ac5f16c103a39da1920ab3b77ad4f21f3c6d415c745bd6a37097e1?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[300] = {0}; - hex_string_to_byte_array( - "01000000014da2d059c1c6eb1c66884643f3bfa917cdb182273bf9dd2361db0c1c6bc706" - "61000000008b483045022100f2522df4a0d2193ee53ad95b698bf502e5874d340b4bb4f8" - "0720015f2ce87296022061d33d02d6f4a3b131a18328c3b1249e53fa115735c2597e29b0" - "738a1d5f3f8801410445bd85326dabc1772b4b319e0dc924ef93caf2360a033941e427f0" - "397b265f3c46e805be40904034880e781ab758a9e67518c624393e2ac14339faa45fafc5" - "deffffffff0100e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a" - "238c88ac00000000", - 448, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = { - .prev_output_index = 0, - .value = 1000000000, // invalid value; more by a factor of 10 - .script_pub_key = { - .size = 25, - }}; - hex_string_to_byte_array( - "eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe", - 64, - input.prev_txn_hash); - // revere order of txn-id: - // eb0e2029310edade8e2a034aea4f0c4a1e243fe2dce67d05f95fddb7ac11bfbe - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - hex_string_to_byte_array("76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac", - 50, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 224); - TEST_ASSERT_EQUAL_INT(3, status); -} - -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2wpkh) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b03?format=hex - * txnElements - - * https://blockchain.info/rawtx/fcb26cf6235d591b89494398b51746610917fd49c376994fa0ce24fcc383eac0?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[2000] = {0}; - hex_string_to_byte_array( - "0100000006d572e075a155d3fa334230a691cf085d463215aead3c417f08f931ccbbe6a1" - "321b0000006b483045022100cdeb3add0650fc8b8351512d6df17fbfc602ec484d8c5c6a" - "7800d3cd5e71fac60220481616b00629c8c7a81a10397db2942ce21676eb5fd4be20bc44" - "31754c657f6001210217630c3bfab894e6e7322ae6d104d9b9aff58ae8ce4c30f45e9903" - "51ba3668a4ffffffff9b4139cb7a02366cd228514a89b46cb55fc8183b03c3f9b8fe583e" - "fe93f28958420000006b483045022100a19519b0d4af6a5f50301c36d620c7c2b5fea705" - "7874a184c03db88196933fd4022070e559e34fe80e17a6d9e4b400d183c22af094684004" - "1220a0965487964b46f301210334f414ce378f5f24a128dbf34a1be8d8a1b863e665f782" - "99a52f06991be8406dffffffff4b98c9a61ad1b2070ebbcfc2d616824ff3259c5c0d09b8" - "1f38975446f12e57cb3a0000006b483045022100e9b10baf0226b7394474142b87edcd26" - "fef74f6fcb6b82833559e95481a51f5802204db8a3f7962f1e731b815f01807ef16b6d48" - "bd73a40e6534d8e4abc1d72a2554012103e7dcb93f93afdf17b60e00b21f8a283e8140f5" - "3619970daf7d3b5934230c420dfffffffff63acaa7893f29aa069dbbd6c74bdb9f597d70" - "3e0f9a99d52b5930edd568ff54000000006b483045022100a3b9578999c1fb5d07c047c9" - "6569476a73cc8684b1b6f1461cdeaa81de58e631022049cb3e73977605aad472e470a995" - "9ab913f2b101c268b8cddadd4bca16db4bf10121033011c8839eb82d1c37596bee611cf7" - "0564d21fb38ce0f4535c4d1d5e53dad958ffffffffd5c0e501e2045ba6fed26aba945e0d" - "6333d2ca555a60e4c1c920f3e27030bc17400000006b483045022100bbf7e176a402c223" - "32ab21c3aa0a5ca05bb9efe0d2a3f3797d5174873d4fc5e8022061aba78025eb56e7a8e1" - "663c68bb63ff0d0d7db0b6fc5064e021994a59cf8c0c012103ca9827e71289cfc3581340" - "ae413ae7860af88829b05e30083cee496cb9cdff39ffffffff64cca9bfba56c7f2dfb497" - "53a7f90fe8e2b2121f3d1cd9221a1032425095ed3a8f0000006b483045022100b275537b" - "a5e33b3511925e68df84d62487640dd813cf179cf65abd2920f4c29f022072523568c14f" - "50881bd527d352847a343d65f5620a0df6e6683c8a793ff77a240121030ab067dab80cd5" - "89f30e36d45902e933abc270304829b4e7323ba51695f8d331ffffffff01f0270f000000" - "0000160014854fe623a8a6a4c76779b57c3895ed2e0962647400000000", - 1858, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = {.prev_output_index = 0, - .value = 993264, - .script_pub_key = { - .size = 22, - }}; - hex_string_to_byte_array( - "21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b03", - 64, - input.prev_txn_hash); - // revere order of txn-id: - // 21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b03 - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - hex_string_to_byte_array("0014854fe623a8a6a4c76779b57c3895ed2e09626474", - 44, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 929); - TEST_ASSERT_EQUAL_INT(0, status); -} - -TEST(btc_txn_helper_test, btc_txn_helper_verify_input_p2wpkh_fail) { - /* Test data source: rawTxn - - * https://blockchain.info/rawtx/21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b03?format=hex - * txnElements - - * https://blockchain.info/rawtx/fcb26cf6235d591b89494398b51746610917fd49c376994fa0ce24fcc383eac0?format=json - * Code reference - https://www.blockchain.com/explorer/api/blockchain_api - */ - uint8_t raw_txn[2000] = {0}; - hex_string_to_byte_array( - "0100000006d572e075a155d3fa334230a691cf085d463215aead3c417f08f931ccbbe6a1" - "321b0000006b483045022100cdeb3add0650fc8b8351512d6df17fbfc602ec484d8c5c6a" - "7800d3cd5e71fac60220481616b00629c8c7a81a10397db2942ce21676eb5fd4be20bc44" - "31754c657f6001210217630c3bfab894e6e7322ae6d104d9b9aff58ae8ce4c30f45e9903" - "51ba3668a4ffffffff9b4139cb7a02366cd228514a89b46cb55fc8183b03c3f9b8fe583e" - "fe93f28958420000006b483045022100a19519b0d4af6a5f50301c36d620c7c2b5fea705" - "7874a184c03db88196933fd4022070e559e34fe80e17a6d9e4b400d183c22af094684004" - "1220a0965487964b46f301210334f414ce378f5f24a128dbf34a1be8d8a1b863e665f782" - "99a52f06991be8406dffffffff4b98c9a61ad1b2070ebbcfc2d616824ff3259c5c0d09b8" - "1f38975446f12e57cb3a0000006b483045022100e9b10baf0226b7394474142b87edcd26" - "fef74f6fcb6b82833559e95481a51f5802204db8a3f7962f1e731b815f01807ef16b6d48" - "bd73a40e6534d8e4abc1d72a2554012103e7dcb93f93afdf17b60e00b21f8a283e8140f5" - "3619970daf7d3b5934230c420dfffffffff63acaa7893f29aa069dbbd6c74bdb9f597d70" - "3e0f9a99d52b5930edd568ff54000000006b483045022100a3b9578999c1fb5d07c047c9" - "6569476a73cc8684b1b6f1461cdeaa81de58e631022049cb3e73977605aad472e470a995" - "9ab913f2b101c268b8cddadd4bca16db4bf10121033011c8839eb82d1c37596bee611cf7" - "0564d21fb38ce0f4535c4d1d5e53dad958ffffffffd5c0e501e2045ba6fed26aba945e0d" - "6333d2ca555a60e4c1c920f3e27030bc17400000006b483045022100bbf7e176a402c223" - "32ab21c3aa0a5ca05bb9efe0d2a3f3797d5174873d4fc5e8022061aba78025eb56e7a8e1" - "663c68bb63ff0d0d7db0b6fc5064e021994a59cf8c0c012103ca9827e71289cfc3581340" - "ae413ae7860af88829b05e30083cee496cb9cdff39ffffffff64cca9bfba56c7f2dfb497" - "53a7f90fe8e2b2121f3d1cd9221a1032425095ed3a8f0000006b483045022100b275537b" - "a5e33b3511925e68df84d62487640dd813cf179cf65abd2920f4c29f022072523568c14f" - "50881bd527d352847a343d65f5620a0df6e6683c8a793ff77a240121030ab067dab80cd5" - "89f30e36d45902e933abc270304829b4e7323ba51695f8d331ffffffff01f0270f000000" - "0000160014854fe623a8a6a4c76779b57c3895ed2e0962647400000000", - 1858, - raw_txn); - // only fill necessary values - btc_sign_txn_input_t input = {.prev_output_index = 0, - .value = 993264, - .script_pub_key = { - .size = 22, - }}; - hex_string_to_byte_array( - // invalid txn hash test. valid txn hash/id: - // 21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b03 - "21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b04", - 64, - input.prev_txn_hash); - // revere order of txn-id: - // 21706dfac590a74e7d083ad60e790c3a1775a4818afd7aa3ddf1a3d76dc16b04 - cy_reverse_byte_array(input.prev_txn_hash, sizeof(input.prev_txn_hash)); - hex_string_to_byte_array("0014854fe623a8a6a4c76779b57c3895ed2e09626474", - 44, - input.script_pub_key.bytes); - - int status = btc_verify_input_test(&input, raw_txn, 929); - TEST_ASSERT_EQUAL_INT(2, status); -} - /* FIX: Required to fix the hardcoded value of 106 (2 + 33 + 71) since the * signature part of the script can vary (71 bytes | 72 bytes | 73 bytes). * Check the get_transaction_weight function. */ diff --git a/tests/unit_test_lists.c b/tests/unit_test_lists.c index 57264965..4b0f93f2 100644 --- a/tests/unit_test_lists.c +++ b/tests/unit_test_lists.c @@ -145,13 +145,6 @@ TEST_GROUP_RUNNER(manager_api_test) { } TEST_GROUP_RUNNER(btc_txn_helper_test) { - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2pk); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2pk_fail); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2pkh); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2pkh_fail); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2wpkh); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_verify_input_p2wpkh_fail); - RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_transaction_weight_legacy1); RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_transaction_weight_legacy2); RUN_TEST_CASE(btc_txn_helper_test, btc_txn_helper_transaction_weight_segwit1); From 90ddce5a446279d8df77509e65c03ecb9ff840c3 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Fri, 13 Dec 2024 22:22:23 -0800 Subject: [PATCH 02/26] fix: Python setup in ci --- .github/workflows/ci-tests.yml | 294 +++++++++++++++++---------------- 1 file changed, 152 insertions(+), 142 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index c7596c29..fadbf883 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -7,80 +7,80 @@ on: - develop pull_request: branches: - - '**' + - "**" jobs: check-commit-msg: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Validate PR commits - if: github.event_name == 'pull_request' - run: bash ./utilities/ci/test-commit-msg.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} + - name: Validate PR commits + if: github.event_name == 'pull_request' + run: bash ./utilities/ci/test-commit-msg.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} check-format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Restore clang-tools - id: cache-clang-tools - uses: actions/cache@v3 - with: - path: clang-tools - key: clang-tools - - - name: Check formatting - env: - CACHED: ${{ steps.cache-clang-tools.outputs.cache-hit }} - SHA_BASE: ${{ github.event.pull_request.base.sha }} - run: | - if [ ! "${CACHED}" = "true" ]; then - bash ./utilities/ci/setup-clang-tools.sh; - fi - PATH="$PATH:$(pwd)/clang-tools" - # Check event type here so ci chaching can take effect - if [ ${{ github.event_name }} = 'pull_request' ]; then - bash ./utilities/ci/format-checker.sh ${SHA_BASE} - fi + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Restore clang-tools + id: cache-clang-tools + uses: actions/cache@v3 + with: + path: clang-tools + key: clang-tools + + - name: Check formatting + env: + CACHED: ${{ steps.cache-clang-tools.outputs.cache-hit }} + SHA_BASE: ${{ github.event.pull_request.base.sha }} + run: | + if [ ! "${CACHED}" = "true" ]; then + bash ./utilities/ci/setup-clang-tools.sh; + fi + PATH="$PATH:$(pwd)/clang-tools" + # Check event type here so ci chaching can take effect + if [ ${{ github.event_name }} = 'pull_request' ]; then + bash ./utilities/ci/format-checker.sh ${SHA_BASE} + fi static-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Restore clang-tools - id: cache-clang-tools - uses: actions/cache@v3 - with: - path: clang-tools - key: clang-tools - - - name: Check formatting - env: - CACHED: ${{ steps.cache-clang-tools.outputs.cache-hit }} - SHA_BASE: ${{ github.event.pull_request.base.sha }} - run: | - if [ ! "${CACHED}" = "true" ]; then - bash ./utilities/ci/setup-clang-tools.sh; - fi - PATH="$PATH:$(pwd)/clang-tools" - # Check event type here so ci chaching can take effect - if [ ${{ github.event_name }} = 'pull_request' ]; then - bash ./utilities/ci/static-analyzer.sh ${SHA_BASE} - fi - - name: Upload result - uses: actions/upload-artifact@v3 - with: - name: clang-tidy-result - path: anaylysis.results + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Restore clang-tools + id: cache-clang-tools + uses: actions/cache@v3 + with: + path: clang-tools + key: clang-tools + + - name: Check formatting + env: + CACHED: ${{ steps.cache-clang-tools.outputs.cache-hit }} + SHA_BASE: ${{ github.event.pull_request.base.sha }} + run: | + if [ ! "${CACHED}" = "true" ]; then + bash ./utilities/ci/setup-clang-tools.sh; + fi + PATH="$PATH:$(pwd)/clang-tools" + # Check event type here so ci chaching can take effect + if [ ${{ github.event_name }} = 'pull_request' ]; then + bash ./utilities/ci/static-analyzer.sh ${SHA_BASE} + fi + - name: Upload result + uses: actions/upload-artifact@v3 + with: + name: clang-tidy-result + path: anaylysis.results build-firmwares: needs: check-format @@ -93,60 +93,65 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Restore build-tools - id: cache-build-tools - uses: actions/cache@v3 - with: - path: build-tools - key: build-tools - - - name: Restore arm-gcc - if: matrix.platform == 'device' - id: cache-arm-gcc - uses: actions/cache@v3 - with: - path: arm-gcc - key: arm-gcc - - - name: Restore proto-tools - id: cache-proto-tools - uses: actions/cache@v3 - with: - path: proto-tools - key: proto-tools - - - name: Install target dependencies - if: matrix.platform == 'device' - env: - CACHED: ${{ steps.cache-arm-gcc.outputs.cache-hit }} - PROTO_CACHED: ${{ steps.cache-proto-tools.outputs.cache-hit }} - run: | - if [ ! "${CACHED}" = "true" ]; then - bash ./utilities/ci/setup-arm-gcc.sh; - fi - if [ ! "${PROTO_CACHED}" = "true" ]; then - bash ./utilities/ci/setup-protoc.sh; - fi - pip install -r utilities/script/requirements.txt - - - name: Install simulator dependencies - if: matrix.platform == 'simulator' - run: sudo apt update && sudo apt install libsdl2-dev --no-install-recommends -y - - - name: Build binaries - env: - CACHED: ${{ steps.cache-build-tools.outputs.cache-hit }} - run: | - if [ ! "${CACHED}" = "true" ]; then - bash ./utilities/ci/setup-build-tools.sh; - fi - pip install -r vendor/nanopb/extra/requirements.txt - PATH="$PATH:$(pwd)/build-tools:$(pwd)/arm-gcc/bin:$(pwd)/proto-tools/bin" - ./utilities/build.sh -u -f ${{ matrix.firmware }} -t ${{ matrix.target }} -p ${{ matrix.platform }} + - uses: actions/checkout@v3 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: "3.9" + cache: "pip" + + - name: Restore build-tools + id: cache-build-tools + uses: actions/cache@v3 + with: + path: build-tools + key: build-tools + + - name: Restore arm-gcc + if: matrix.platform == 'device' + id: cache-arm-gcc + uses: actions/cache@v3 + with: + path: arm-gcc + key: arm-gcc + + - name: Restore proto-tools + id: cache-proto-tools + uses: actions/cache@v3 + with: + path: proto-tools + key: proto-tools + + - name: Install target dependencies + if: matrix.platform == 'device' + env: + CACHED: ${{ steps.cache-arm-gcc.outputs.cache-hit }} + PROTO_CACHED: ${{ steps.cache-proto-tools.outputs.cache-hit }} + run: | + if [ ! "${CACHED}" = "true" ]; then + bash ./utilities/ci/setup-arm-gcc.sh; + fi + if [ ! "${PROTO_CACHED}" = "true" ]; then + bash ./utilities/ci/setup-protoc.sh; + fi + pip install -r utilities/script/requirements.txt + + - name: Install simulator dependencies + if: matrix.platform == 'simulator' + run: sudo apt update && sudo apt install libsdl2-dev --no-install-recommends -y + + - name: Build binaries + env: + CACHED: ${{ steps.cache-build-tools.outputs.cache-hit }} + run: | + if [ ! "${CACHED}" = "true" ]; then + bash ./utilities/ci/setup-build-tools.sh; + fi + pip install -r vendor/nanopb/extra/requirements.txt + PATH="$PATH:$(pwd)/build-tools:$(pwd)/arm-gcc/bin:$(pwd)/proto-tools/bin" + ./utilities/build.sh -u -f ${{ matrix.firmware }} -t ${{ matrix.target }} -p ${{ matrix.platform }} run-unit-tests: needs: check-format @@ -160,31 +165,36 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Restore build-tools - id: cache-build-tools - uses: actions/cache@v3 - with: - path: build-tools - key: build-tools - - - name: Install simulator dependencies - run: sudo apt update && sudo apt install libsdl2-dev --no-install-recommends -y - - - name: Run tests - env: - CACHED: ${{ steps.cache-build-tools.outputs.cache-hit }} - run: | - if [ ! "${CACHED}" = "true" ]; then - bash ./utilities/ci/setup-build-tools.sh; - fi - PATH="$PATH:$(pwd)/build-tools" - pip install -r vendor/nanopb/extra/requirements.txt - # Ignore any non-zero exits from simulator run using '|| true' - bash ./utilities/run_unit_tests.sh -f ${{ matrix.firmware }} -p ${{ matrix.platform }} > test_results.txt || true - cat test_results.txt - # Unity prints "OK" if all tests pass, "FAIL" if tests fail; return 1 to indicate failure - if [ ! "$(tail -n 1 test_results.txt)" = "OK" ]; then exit 1; fi + - uses: actions/checkout@v3 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: "3.9" + cache: "pip" + + - name: Restore build-tools + id: cache-build-tools + uses: actions/cache@v3 + with: + path: build-tools + key: build-tools + + - name: Install simulator dependencies + run: sudo apt update && sudo apt install libsdl2-dev --no-install-recommends -y + + - name: Run tests + env: + CACHED: ${{ steps.cache-build-tools.outputs.cache-hit }} + run: | + if [ ! "${CACHED}" = "true" ]; then + bash ./utilities/ci/setup-build-tools.sh; + fi + PATH="$PATH:$(pwd)/build-tools" + pip install -r vendor/nanopb/extra/requirements.txt + # Ignore any non-zero exits from simulator run using '|| true' + bash ./utilities/run_unit_tests.sh -f ${{ matrix.firmware }} -p ${{ matrix.platform }} > test_results.txt || true + cat test_results.txt + # Unity prints "OK" if all tests pass, "FAIL" if tests fail; return 1 to indicate failure + if [ ! "$(tail -n 1 test_results.txt)" = "OK" ]; then exit 1; fi From 4f4dbc6b55d86a01b12c3332e019bc3c373cfdd1 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 11 Dec 2024 19:46:04 +0530 Subject: [PATCH 03/26] feat(app): Add starknet gen pubkey --- .gitmodules | 3 + apps/starknet_app/starknet_api.c | 199 +++++++ apps/starknet_app/starknet_api.h | 117 ++++ apps/starknet_app/starknet_context.c | 102 ++++ apps/starknet_app/starknet_context.h | 59 ++ apps/starknet_app/starknet_crypto.c | 455 +++++++++++++++ apps/starknet_app/starknet_crypto.h | 71 +++ apps/starknet_app/starknet_helpers.c | 220 +++++++ apps/starknet_app/starknet_helpers.h | 91 +++ apps/starknet_app/starknet_main.c | 150 +++++ apps/starknet_app/starknet_main.h | 44 ++ apps/starknet_app/starknet_pedersen.c | 212 +++++++ apps/starknet_app/starknet_priv.h | 56 ++ apps/starknet_app/starknet_pub_key.c | 538 ++++++++++++++++++ common/core/app_registry.h | 2 +- common/core/core_flow_init.c | 2 + common/cypherock-common | 2 +- common/proto-options/starknet/core.options | 1 + .../starknet/get_public_keys.options | 5 + utilities/cmake/firmware/firmware.cmake | 6 +- utilities/cmake/simulator/simulator.cmake | 5 +- vendor/mini-gmp | 1 + 22 files changed, 2336 insertions(+), 5 deletions(-) create mode 100644 apps/starknet_app/starknet_api.c create mode 100644 apps/starknet_app/starknet_api.h create mode 100644 apps/starknet_app/starknet_context.c create mode 100644 apps/starknet_app/starknet_context.h create mode 100644 apps/starknet_app/starknet_crypto.c create mode 100644 apps/starknet_app/starknet_crypto.h create mode 100644 apps/starknet_app/starknet_helpers.c create mode 100644 apps/starknet_app/starknet_helpers.h create mode 100644 apps/starknet_app/starknet_main.c create mode 100644 apps/starknet_app/starknet_main.h create mode 100644 apps/starknet_app/starknet_pedersen.c create mode 100644 apps/starknet_app/starknet_priv.h create mode 100644 apps/starknet_app/starknet_pub_key.c create mode 100644 common/proto-options/starknet/core.options create mode 100644 common/proto-options/starknet/get_public_keys.options create mode 160000 vendor/mini-gmp diff --git a/.gitmodules b/.gitmodules index d1c67791..39795457 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "nanopb"] path = vendor/nanopb url = ../nanopb +[submodule "vendor/mini-gmp"] + path = vendor/mini-gmp + url = https://github.com/Cypherock/mini-gmp diff --git a/apps/starknet_app/starknet_api.c b/apps/starknet_app/starknet_api.c new file mode 100644 index 00000000..c2f4bd81 --- /dev/null +++ b/apps/starknet_app/starknet_api.c @@ -0,0 +1,199 @@ +/** + * @file starknet_api.c + * @author Cypherock X1 Team + * @brief Defines helpers apis for Starknet app. + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_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_starknet_query(const uint8_t *data, + uint16_t data_size, + starknet_query_t *query_out) { + if (NULL == data || NULL == query_out || 0 == data_size) { + starknet_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(starknet_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, STARKNET_QUERY_FIELDS, query_out); + + /* Send error to host if status is false*/ + if (false == status) { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_DECODING_FAILED); + } + + return status; +} + +bool encode_starknet_result(const starknet_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, STARKNET_RESULT_FIELDS, result); + + if (true == status) { + *bytes_written_out = stream.bytes_written; + } + + return status; +} + +bool check_starknet_query(const starknet_query_t *query, + pb_size_t exp_query_tag) { + if ((NULL == query) || (exp_query_tag != query->which_request)) { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + return false; + } + return true; +} + +starknet_result_t init_starknet_result(pb_size_t result_tag) { + starknet_result_t result = STARKNET_RESULT_INIT_ZERO; + result.which_response = result_tag; + return result; +} + +void starknet_send_error(pb_size_t which_error, uint32_t error_code) { + starknet_result_t result = + init_starknet_result(STARKNET_RESULT_COMMON_ERROR_TAG); + result.common_error = init_common_error(which_error, error_code); + starknet_send_result(&result); +} + +void starknet_send_result(const starknet_result_t *result) { + // TODO: Set the options file for all + uint8_t buffer[1700] = {0}; + size_t bytes_encoded = 0; + ASSERT( + encode_starknet_result(result, buffer, sizeof(buffer), &bytes_encoded)); + send_response_to_host(&buffer[0], bytes_encoded); +} + +bool starknet_get_query(starknet_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_starknet_query( + event.usb_event.p_msg, event.usb_event.msg_size, query)) { + return false; + } + + if (!check_starknet_query(query, exp_query_tag)) { + return false; + } + + return true; +} diff --git a/apps/starknet_app/starknet_api.h b/apps/starknet_app/starknet_api.h new file mode 100644 index 00000000..5f2bbcea --- /dev/null +++ b/apps/starknet_app/starknet_api.h @@ -0,0 +1,117 @@ +/** + * @file starknet_api.h + * @author Cypherock X1 Team + * @brief Header file to export some helper functions for the Starknet app + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef STARKNET_API_H +#define STARKNET_API_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define STARKNET_PUB_KEY_SIZE 32 + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief API to decode query from host with `STARKNET_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 starknet_query_t obj to copy the decoded result + * to + * @return bool True if decoding was successful, else false + */ +bool decode_starknet_query(const uint8_t *data, + uint16_t data_size, + starknet_query_t *query_out); + +/** + * @brief Encodes the STARKNET result with `STARKNET_RESULT_FIELDS` to + * byte-stream + * + * @param[in] result: object of populated @ref starknet_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_starknet_result(const starknet_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 + * `starknet_query_t` matches against the expected tag. + * + * @param query The query of type `starknet_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_starknet_query(const starknet_query_t *query, + pb_size_t exp_query_tag); + +/** + * @brief Returns zero initialized object of type + * starknet_result_t result_tag set in result.which_response field + * + * @param result_tag Result tag to be set in the starknet_result_t result + * @return starknet_result_t Result object of type starknet_result_t + */ +starknet_result_t init_starknet_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 starknet_send_error(pb_size_t which_error, uint32_t error_code); + +/** + * @brief This API encodes starknet_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_starknet_result internally. + * + * @param result The result which needs to be sent to the host. + */ +void starknet_send_result(const starknet_result_t *result); + +/** + * @brief This API receives request of type starknet_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 starknet_get_query(starknet_query_t *query, pb_size_t exp_query_tag); + +#endif diff --git a/apps/starknet_app/starknet_context.c b/apps/starknet_app/starknet_context.c new file mode 100644 index 00000000..dc4a77d4 --- /dev/null +++ b/apps/starknet_app/starknet_context.c @@ -0,0 +1,102 @@ +/** + * @file starknet_context.c + * @author Cypherock X1 Team + * @brief Constant variables and configurations for the Starknet app + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_context.h" + +#include "coin_utils.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ +const starknet_config_t starknet_app = { + .lunit1_name = "STRK", + .lunit2_name = "STRK", + .name = "Starknet", +}; + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ diff --git a/apps/starknet_app/starknet_context.h b/apps/starknet_app/starknet_context.h new file mode 100644 index 00000000..5bd731d2 --- /dev/null +++ b/apps/starknet_app/starknet_context.h @@ -0,0 +1,59 @@ +/** + * @file starknet_context.h + * @author Cypherock X1 Team + * @brief Header file defining typedefs and MACROS for the STARKNET app + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef STARKNET_CONTEXT_H +#define STARKNET_CONTEXT_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define STARKNET_IMPLICIT_ACCOUNT_DEPTH 6 + +// m/2645'/1195502025'/1148870696'/0'/0'/i +#define STARKNET_PURPOSE_INDEX (0x80000000 + 0xA55) // 2645' +#define STARKNET_LAYER_INDEX (0x80000000 + 0x4741E9C9) // 1195502025' +#define STARKNET_APPLICATION_INDEX (0x80000000 + 0x447A6028) // 1148870696' +#define STARKNET_ETH_1_INDEX 0x80000000 // 0' +#define STARKNET_ETH_2_INDEX 0x80000000 // 0' + +/// this makes length of 5 with a termination NULL byte +#define STARKNET_SHORT_NAME_MAX_SIZE 6 +/// this makes length of 5 with a termination NULL byte +#define STARKNET_LONG_NAME_MAX_SIZE 9 + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +typedef struct { + /** Largest unit/denomination indicator/symbol. This will be used in UX for + * displaying fees and amount. + */ + const char lunit1_name[STARKNET_SHORT_NAME_MAX_SIZE]; + const char lunit2_name[STARKNET_SHORT_NAME_MAX_SIZE]; + /** Common name of the blockchain known to the users. This will be used in UX + */ + const char name[STARKNET_LONG_NAME_MAX_SIZE]; +} starknet_config_t; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ +extern const starknet_config_t starknet_app; + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +#endif /* STARKNET_CONTEXT_H */ diff --git a/apps/starknet_app/starknet_crypto.c b/apps/starknet_app/starknet_crypto.c new file mode 100644 index 00000000..f7a1c156 --- /dev/null +++ b/apps/starknet_app/starknet_crypto.c @@ -0,0 +1,455 @@ +/** + * @file starknet_crypto.c + * @author Cypherock X1 Team + * @brief Crypto specific to Starknet chain + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_crypto.h" + +// #include "bignum_internal.h" +#include + +#include "mini-gmp.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ +stark_curve *starkCurve; +stark_pedersen *starkPts; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static void stark_curve_init(); +static void stark_pedersen_init(); + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void starknet_init() { + stark_curve_init(); + stark_pedersen_init(); +} + +void stark_point_init(stark_point *p) { + mpz_init(p->x); + mpz_init(p->y); +} + +void stark_point_clear(stark_point *p) { + mpz_clear(p->x); + mpz_clear(p->y); +} + +void stark_pedersen_clear(stark_pedersen *pedersen) { + for (int i = 0; i < 5; i++) { + stark_point_clear(&pedersen->P[i]); + } +} + +void stark_curve_init() { + static stark_curve stark256; + // char str[STARK_BN_LEN] = {0}; + + /* stark_curve_params ref: + https://github.com/xJonathanLEI/starknet-rs/blob/f31e426a65225b9830bbf3c148f7ea05bf9dc257/starknet-curve/src/curve_params.rs + + struct bn prime; // prime order of the finite field + stark_point G; // initial curve point + struct bn order; // order of G + struct bn order_half; // order of G divided by 2 + struct bn a; // coefficient 'a' of the elliptic curve OR alpha + struct bn b; // coefficient 'b' of the elliptic curve OR beta + */ + + // Initialize mpz_t variables in stark256 + mpz_init(stark256.prime); + mpz_init(stark256.G.x); + mpz_init(stark256.G.y); + mpz_init(stark256.order); + mpz_init(stark256.order_half); + mpz_init(stark256.a); + mpz_init(stark256.b); + + // Prime + mpz_set_str( + stark256.prime, + "0800000000000011000000000000000000000000000000000000000000000001", + 16); + + // Generator_point x + mpz_set_str( + stark256.G.x, + "01EF15C18599971B7BECED415A40F0C7DEACFD9B0D1819E03D723D8BC943CFCA", + 16); + + // Generator_point y + mpz_set_str( + stark256.G.y, + "005668060AA49730B7BE4801DF46EC62DE53ECD11ABE43A32873000C36E8DC1F", + 16); + + // Order + mpz_set_str( + stark256.order, + "0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f", + 16); + + // Order half + mpz_set_str( + stark256.order_half, + "04000000000000087fffffffffffffffdbc08936e573d9190f335120d6e32697", + 16); + + // Alpha + mpz_set_str( + stark256.a, + "0000000000000000000000000000000000000000000000000000000000000001", + 16); + + // Beta + mpz_set_str( + stark256.b, + "06f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89", + 16); + + starkCurve = &stark256; + // print_stark_curve(); +} + +static void stark_pedersen_init() { + // Ref: https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html + + static stark_pedersen pedersen; + // Initialize all mpz_t variables in the pedersen structure + for (int i = 0; i < 5; i++) { + mpz_init(pedersen.P[i].x); + mpz_init(pedersen.P[i].y); + } + + // Shift_point x + mpz_set_str( + pedersen.P[0].x, + "049EE3EBA8C1600700EE1B87EB599F16716B0B1022947733551FDE4050CA6804", + 16); + + // Shift_point y + mpz_set_str( + pedersen.P[0].y, + "03CA0CFE4B3BC6DDF346D49D06EA0ED34E621062C0E056C1D0405D266E10268A", + 16); + + // Pedersen_point_1 x + mpz_set_str( + pedersen.P[1].x, + "0234287DCBAFFE7F969C748655FCA9E58FA8120B6D56EB0C1080D17957EBE47B", + 16); + + // Pedersen_point_1 y + mpz_set_str( + pedersen.P[1].y, + "03B056F100F96FB21E889527D41F4E39940135DD7A6C94CC6ED0268EE89E5615", + 16); + + // Pedersen_point_2 x + mpz_set_str( + pedersen.P[2].x, + "04FA56F376C83DB33F9DAB2656558F3399099EC1DE5E3018B7A6932DBA8AA378", + 16); + + // Pedersen_point_2 y + mpz_set_str( + pedersen.P[2].y, + "03FA0984C931C9E38113E0C0E47E4401562761F92A7A23B45168F4E80FF5B54D", + 16); + + // Pedersen_point_3 x + mpz_set_str( + pedersen.P[3].x, + "04BA4CC166BE8DEC764910F75B45F74B40C690C74709E90F3AA372F0BD2D6997", + 16); + + // Pedersen_point_3 y + mpz_set_str(pedersen.P[3].y, + "040301CF5C1751F4B971E46C4EDE85FCAC5C59A5CE5AE7C48151F27B24B219C", + 16); + + // Pedersen_point_4 x + mpz_set_str( + pedersen.P[4].x, + "054302DCB0E6CC1C6E44CCA8F61A63BB2CA65048D53FB325D36FF12C49A58202", + 16); + + // Pedersen_point_4 y + mpz_set_str( + pedersen.P[4].y, + "01B77B3E37D13504B348046268D8AE25CE98AD783C25561A879DCC77E99C2426", + 16); + + starkPts = &pedersen; +} + +// Set cp2 = cp1 +void stark_point_copy(const stark_point *cp1, stark_point *cp2) { + stark_point_init(cp2); + + mpz_set(cp2->x, cp1->x); + mpz_set(cp2->y, cp1->y); +} + +void stark_point_add(const stark_curve *curve, + const stark_point *cp1, + stark_point *cp2) { + mpz_t lambda, inv, xr, yr; + + mpz_init(lambda); + mpz_init(inv); + mpz_init(xr); + mpz_init(yr); + + if (stark_point_is_infinity(cp1)) { + return; + } + if (stark_point_is_infinity(cp2)) { + stark_point_copy(cp1, cp2); + return; + } + if (stark_point_is_equal(cp1, cp2)) { + stark_point_double(curve, cp2); + return; + } + if (stark_point_is_negative_of(cp1, cp2)) { + stark_point_set_infinity(cp2); + return; + } + + // inv = (cp2->x - cp1->x) mod prime + mpz_sub(inv, cp2->x, cp1->x); + mpz_mod(inv, inv, curve->prime); + + // inv = inv^-1 mod prime + mpz_invert(inv, inv, curve->prime); + + // lambda = (cp2->y - cp1->y) mod prime + mpz_sub(lambda, cp2->y, cp1->y); + mpz_mod(lambda, lambda, curve->prime); + + // lambda = lambda * inv mod prime + mpz_mul(lambda, lambda, inv); + mpz_mod(lambda, lambda, curve->prime); + + // xr = lambda^2 - cp1->x - cp2->x mod prime + mpz_mul(xr, lambda, lambda); + mpz_sub(xr, xr, cp1->x); + mpz_sub(xr, xr, cp2->x); + mpz_mod(xr, xr, curve->prime); + + // yr = lambda * (cp1->x - xr) - cp1->y mod prime + mpz_sub(yr, cp1->x, xr); + mpz_mul(yr, yr, lambda); + mpz_sub(yr, yr, cp1->y); + mpz_mod(yr, yr, curve->prime); + + // Set cp2 to the result + mpz_set(cp2->x, xr); + mpz_set(cp2->y, yr); + + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); +} + +void stark_point_double(const stark_curve *curve, stark_point *cp) { + // Ref: + // https://github.com/starkware-libs/starkex-for-spot-trading/blob/607f0b4ce507e1d95cd018d206a2797f6ba4aab4/src/starkware/crypto/starkware/crypto/signature/math_utils.py + if (mpz_cmp_ui(cp->y, 0) == 0) { + return; + } + + mpz_t lambda, xr, yr, inv; + mpz_init(lambda); + mpz_init(xr); + mpz_init(yr); + mpz_init(inv); + + // lambda = (3 * cp->x^2 + curve->a) * (2 * cp->y)^-1 mod prime + mpz_mul(lambda, cp->x, cp->x); + mpz_mul_ui(lambda, lambda, 3); + mpz_add(lambda, lambda, curve->a); + mpz_mul_ui(inv, cp->y, 2); // using inv to store 2 * y + mpz_invert(inv, inv, curve->prime); + mpz_mul(lambda, lambda, inv); + mpz_mod(lambda, lambda, curve->prime); + + // xr = lambda^2 - 2 * cp->x mod prime + mpz_mul(xr, lambda, lambda); + mpz_submul_ui(xr, cp->x, 2); + mpz_mod(xr, xr, curve->prime); + + // yr = lambda * (cp->x - xr) - cp->y mod prime + mpz_sub(yr, cp->x, xr); + mpz_mul(yr, yr, lambda); + mpz_sub(yr, yr, cp->y); + mpz_mod(yr, yr, curve->prime); + + mpz_set(cp->x, xr); + mpz_set(cp->y, yr); + + mpz_clear(lambda); + mpz_clear(xr); + mpz_clear(yr); + mpz_clear(inv); +} + +// set point to internal representation of point at infinity +void stark_point_set_infinity(stark_point *p) { + mpz_set_ui(p->x, 0); + mpz_set_ui(p->y, 0); +} + +// return true iff p represent point at infinity +// both coords are zero in internal representation +int stark_point_is_infinity(const stark_point *p) { + return mpz_cmp_ui(p->x, 0) == 0 && mpz_cmp_ui(p->y, 0) == 0; +} + +// return true iff both points are equal +int stark_point_is_equal(const stark_point *p, const stark_point *q) { + return (mpz_cmp(p->x, q->x) == 0) && (mpz_cmp(p->y, q->y) == 0); +} + +// returns true iff p == -q +// expects p and q be valid points on curve other than point at infinity +int stark_point_is_negative_of(const stark_point *p, const stark_point *q) { + // if P == (x, y), then -P would be (x, -y) on this curve + if (mpz_cmp(p->x, q->x) != 0) { + return 0; + } + + // we shouldn't hit this for a valid point + if (mpz_cmp_ui(p->y, 0) == 0) { + return 0; + } + + return mpz_cmp(p->y, q->y) != 0; +} + +void stark_point_multiply(const stark_curve *curve, + const mpz_t k, + const stark_point *p, + stark_point *res) { + // Ref: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication + + stark_point temp; + stark_point R; + stark_point_init(&temp); + stark_point_init(&R); + stark_point_set_infinity(&R); // Initialize R to the point at infinity + stark_point_copy(p, &temp); // Copy the input point p to temp + + // Iterate over each bit of k from the least significant to the most + // significant + for (int i = 0; i < 256; i++) { + // If the i-th bit of k is set, add temp to the result R + if (mpz_tstbit(k, i)) { + stark_point_add(curve, &temp, &R); + } + + // Double the current point temp + stark_point_double(curve, &temp); + } + + // Copy the result R to the output parameter res + stark_point_copy(&R, res); + + stark_point_clear(&temp); + stark_point_clear(&R); +} + +int bn_bit_length(const mpz_t k) { + if (mpz_cmp_ui(k, 0) == 0) { + return 0; + } + return mpz_sizeinbase(k, 2); +} + +int bn_is_bit_set(const mpz_t k, int bit_idx) { + return mpz_tstbit(k, bit_idx); +} diff --git a/apps/starknet_app/starknet_crypto.h b/apps/starknet_app/starknet_crypto.h new file mode 100644 index 00000000..937238ef --- /dev/null +++ b/apps/starknet_app/starknet_crypto.h @@ -0,0 +1,71 @@ +/** + * @file starknet_crypto.h + * @author Cypherock X1 Team + * @brief Crypto api definitions for Starknet chain + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef STARKNET_CRYPTO_H +#define STARKNET_CRYPTO_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "mini-gmp.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define STARK_BN_LEN 64 + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +// curve point x and y +typedef struct { + mpz_t x, y; +} stark_point; + +typedef struct { + mpz_t prime; // prime order of the finite field + stark_point G; // initial curve point + mpz_t order; // order of G + mpz_t order_half; // order of G divided by 2 + mpz_t a; // coefficient 'a' of the elliptic curve + mpz_t b; // coefficient 'b' of the elliptic curve +} stark_curve; + +typedef struct { + stark_point P[5]; +} stark_pedersen; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ +extern stark_curve *starkCurve; +extern stark_pedersen *starkPts; + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +void stark_point_init(stark_point *p); +void stark_point_clear(stark_point *p); +void stark_point_copy(const stark_point *cp1, stark_point *cp2); +void stark_point_add(const stark_curve *curve, + const stark_point *cp1, + stark_point *cp2); +void stark_point_double(const stark_curve *curve, stark_point *cp); +void stark_point_multiply(const stark_curve *curve, + const mpz_t k, + const stark_point *p, + stark_point *res); +void stark_point_set_infinity(stark_point *p); +int stark_point_is_infinity(const stark_point *p); +int stark_point_is_equal(const stark_point *p, const stark_point *q); +int stark_point_is_negative_of(const stark_point *p, const stark_point *q); +void stark_pedersen_clear(stark_pedersen *pedersen); +void starknet_init(); +#endif // STARKNET_CRYPTO_H \ No newline at end of file diff --git a/apps/starknet_app/starknet_helpers.c b/apps/starknet_app/starknet_helpers.c new file mode 100644 index 00000000..605125d9 --- /dev/null +++ b/apps/starknet_app/starknet_helpers.c @@ -0,0 +1,220 @@ +/** + * @file starknet_helpers.c + * @author Cypherock X1 Team + * @brief Utilities specific to Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_helpers.h" + +#include + +#include "coin_utils.h" +#include "mini-gmp-helpers.h" +#include "mini-gmp.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_crypto.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Grind starknet private from provided 32-byte seed + */ +static bool grind_key(const uint8_t *grind_seed, uint8_t *out); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +bool grind_key(const uint8_t *grind_seed, uint8_t *out) { + uint8_t key[32] = {0}; + mpz_t strk_limit; + mpz_t strk_key; + mpz_t stark_order; + + // Initialize stark_order + mpz_init_set_str( + stark_order, + "0800000000000010FFFFFFFFFFFFFFFFB781126DCAE7B2321E66A241ADC64D2F", + 16); + + // Initialize strk_limit + mpz_init_set_str( + strk_limit, + "F80000000000020EFFFFFFFFFFFFFFF738A13B4B920E9411AE6DA5F40B0358B1", + 16); + + SHA256_CTX ctx = {0}; + mpz_init(strk_key); + for (uint8_t itr = 0; itr < 200; itr++) { + sha256_Init(&ctx); + sha256_Update(&ctx, grind_seed, 32); + sha256_Update(&ctx, &itr, 1); + sha256_Final(&ctx, key); + + byte_array_to_mpz(strk_key, key, 32); + if (mpz_cmp(strk_key, strk_limit) == -1) { + mpz_t f_key; + mpz_init(f_key); + mpz_mod(f_key, strk_key, stark_order); + mpz_to_byte_array(f_key, out, 32); + return true; + } + } + + starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 0); + LOG_CRITICAL("ERROR: grind 200 iterations failed\n"); + return false; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +bool starknet_derivation_path_guard(const uint32_t *path, uint8_t levels) { + bool status = false; + if (STARKNET_IMPLICIT_ACCOUNT_DEPTH != levels) { + return status; + } + + uint32_t purpose = path[0], layer = path[1], application = path[2], + eth_1 = path[3], eth_2 = path[4], address = path[5]; + + // m/2645'/1195502025'/1148870696'/0'/0'/i + status = + (STARKNET_PURPOSE_INDEX == purpose && STARKNET_LAYER_INDEX == layer && + STARKNET_APPLICATION_INDEX == application && + STARKNET_ETH_1_INDEX == eth_1 && STARKNET_ETH_2_INDEX == eth_2 && + is_non_hardened(address)); + + return status; +} + +bool starknet_derive_key_from_seed(const uint8_t *seed, + const uint32_t *path, + uint32_t path_length, + uint8_t *key_priv, + uint8_t *key_pub) { + HDNode stark_child_node = {0}; + + // derive node at m/2645'/1195502025'/1148870696'/0'/0'/i + if (!derive_hdnode_from_path( + path, path_length, SECP256K1_NAME, seed, &stark_child_node)) { + // send unknown error; unknown failure reason + starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + memzero(&stark_child_node, sizeof(HDNode)); + return false; + } + + uint8_t stark_private_key[32] = {0}; + stark_point p; + stark_point_init(&p); + if (!grind_key(stark_child_node.private_key, stark_private_key)) { + return false; + } + + // copy stark priv key if required + if (key_priv != NULL) { + memzero(key_priv, 32); + memcpy(key_priv, stark_private_key, 32); + } + + // derive stark pub key from stark priv key + mpz_t priv_key; + mpz_init(priv_key); + byte_array_to_mpz(priv_key, stark_private_key, 32); + stark_point_multiply(starkCurve, priv_key, &starkCurve->G, &p); + mpz_clear(priv_key); // clear priv key when no longer required + + uint8_t stark_public_key[32] = {0}; + mpz_to_byte_array(p.x, stark_public_key, STARKNET_PUB_KEY_SIZE); + + // copy stark pub key if required + if (key_pub != NULL) { + memzero(key_pub, STARKNET_PUB_KEY_SIZE); + memcpy(key_pub, stark_public_key, STARKNET_PUB_KEY_SIZE); + } + + // clear mpz variables + stark_point_clear(&p); + + return true; +} diff --git a/apps/starknet_app/starknet_helpers.h b/apps/starknet_app/starknet_helpers.h new file mode 100644 index 00000000..e62918ab --- /dev/null +++ b/apps/starknet_app/starknet_helpers.h @@ -0,0 +1,91 @@ +/** + * @file starknet_helpers.h + * @author Cypherock X1 Team + * @brief Utilities api definitions for Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef STARKNET_HELPERS_H +#define STARKNET_HELPERS_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include +#include + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define LOW_PART_BITS 248 +#define LOW_PART_BYTES (LOW_PART_BITS / 8) +#define LOW_PART_MASK ((1ULL << LOW_PART_BITS) - 1) +#define STARKNET_BIGNUM_SIZE 32 +#define PEDERSEN_HASH_SIZE 32 +#define CALL_DATA_PARAMETER_SIZE 3 +#define STARKNET_SIZE_PUB_KEY (32) +#define STARKNET_ADDR_SIZE 32 +#define STARKNET_ARGENT_CLASS_HASH \ + "036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f" +#define STARKNET_DEPLOYER_VALUE 0 + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Verifies the derivation path. + * @details The function supports checking derivation paths for HD wallets + * Types of derivation: + * m/2645'/1195502025'/1148870696'/0'/0'/i + * + * @param[in] path The derivation path as an uint32 array + * @param[in] depth 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 starknet_derivation_path_guard(const uint32_t *path, uint8_t levels); + +/** + * @brief Derives stark keys (public and/or private) from given seed + * + * @param private_key Stores derived stark private key + * @param public_key Stores derived stark public key + */ +bool starknet_derive_key_from_seed(const uint8_t *seed, + const uint32_t *path, + const uint32_t path_length, + uint8_t *private_key, + uint8_t *public_key); + +/** + * @brief Converts unsigned long int to byte array + */ +void starknet_uli_to_bn_byte_array(const unsigned long int ui, + uint8_t *bn_array); + +/** + * Compute Pedersen hash from data + * + * @param data Array of data to compute Pedersen hash on + * @param num_elem len of data + * @param hash Pedersen hash of elements + */ +void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], + uint8_t num_elem, + uint8_t *hash); + +#endif // STARKNET_HELPERS_H \ No newline at end of file diff --git a/apps/starknet_app/starknet_main.c b/apps/starknet_app/starknet_main.c new file mode 100644 index 00000000..e22eaefb --- /dev/null +++ b/apps/starknet_app/starknet_main.c @@ -0,0 +1,150 @@ +/** + * @file starknet_main.c + * @author Cypherock X1 Team + * @brief A common entry point to various Starknet actions supported. + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_main.h" + +#include "starknet_api.h" +#include "starknet_priv.h" +#include "status_api.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Entry point for the STARKNET application of the X1 vault. It is + * invoked by the X1 vault firmware, as soon as there is a USB request raised + * for the Solana app. + * + * @param usb_evt The USB event which triggered invocation of the bitcoin app + */ +void starknet_main(usb_event_t usb_evt, const void *app_config); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ +static const cy_app_desc_t starknet_app_desc = {.id = 21, + .version = + { + .major = 1, + .minor = 0, + .patch = 0, + }, + .app = starknet_main, + .app_config = NULL}; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +void starknet_main(usb_event_t usb_evt, const void *app_config) { + starknet_query_t query = STARKNET_QUERY_INIT_DEFAULT; + + if (false == decode_starknet_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 STARKNET_QUERY_GET_PUBLIC_KEYS_TAG: + case STARKNET_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG: { + starknet_get_pub_keys(&query); + break; + } + case STARKNET_QUERY_SIGN_TXN_TAG: { + // starknet_sign_transaction(&query); + break; + } + + default: { + /* In case we ever encounter invalid query, convey to the host app */ + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_QUERY); + } break; + } + + return; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +const cy_app_desc_t *get_starknet_app_desc() { + return &starknet_app_desc; +} \ No newline at end of file diff --git a/apps/starknet_app/starknet_main.h b/apps/starknet_app/starknet_main.h new file mode 100644 index 00000000..c8b20d35 --- /dev/null +++ b/apps/starknet_app/starknet_main.h @@ -0,0 +1,44 @@ +/** + * @file starknet_main.h + * @author Cypherock X1 Team + * @brief Header file for a common entry point to various Starknet coin + actions supported. + * @details + + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + */ + +#ifndef STARKNET_MAIN_H +#define STARKNET_MAIN_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "app_registry.h" +#include "events.h" +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Returns the config for Starknet chain app descriptors + * + * @return A const reference to cy_app_desc_t + */ +const cy_app_desc_t *get_starknet_app_desc(); +#endif /* STARKNET_MAIN_H */ diff --git a/apps/starknet_app/starknet_pedersen.c b/apps/starknet_app/starknet_pedersen.c new file mode 100644 index 00000000..89b39b33 --- /dev/null +++ b/apps/starknet_app/starknet_pedersen.c @@ -0,0 +1,212 @@ +/** + * @file starknet_padersen.c + * @author Cypherock X1 Team + * @brief Utilities specific to Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + +#include "coin_utils.h" +#include "mini-gmp-helpers.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_crypto.h" +#include "starknet_helpers.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ +static void process_single_element(mpz_t element, + stark_point *p1, + stark_point *p2, + stark_point *result); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { + ASSERT(NULL != x); + ASSERT(NULL != y); + ASSERT(0 < size); + + // Convert to bn + mpz_t a, b, result; + mpz_init(a); + mpz_init(b); + mpz_init(result); + + mpz_import(a, size, 1, 1, 1, 0, x); // Convert x to mpz_t a + mpz_import(b, size, 1, 1, 1, 0, y); // Convert y to mpz_t b + + // Get shift point + stark_point HASH_SHIFT_POINT, P_1, P_2, P_3, P_4; + stark_point_copy(&starkPts->P[0], &HASH_SHIFT_POINT); + stark_point_copy(&starkPts->P[1], &P_1); + stark_point_copy(&starkPts->P[2], &P_2); + stark_point_copy(&starkPts->P[3], &P_3); + stark_point_copy(&starkPts->P[4], &P_4); + + // Compute the hash using the Starkware Pedersen hash definition + stark_point x_part, y_part, hash_point; + stark_point_init(&x_part); + stark_point_init(&y_part); + stark_point_init(&hash_point); + + process_single_element(a, &P_1, &P_2, &x_part); + process_single_element(b, &P_3, &P_4, &y_part); + + stark_point_add(starkCurve, &HASH_SHIFT_POINT, &x_part); + stark_point_add(starkCurve, &x_part, &y_part); + stark_point_copy(&y_part, &hash_point); + + memzero(hash, 32); + mpz_to_byte_array(hash_point.x, hash, 32); + + return true; +} + +void process_single_element(mpz_t element, + stark_point *p1, + stark_point *p2, + stark_point *result) { + ASSERT(mpz_cmp(element, starkCurve->prime) < 0); + + mpz_t low_part, high_nibble; + mpz_init(low_part); + mpz_init(high_nibble); + + // Extract the low 248 bits and high bits from the element + mpz_t mask; + mpz_init(mask); + // Set mask to (1 << 248) - 1 + mpz_ui_pow_ui(mask, 2, 248); // mask = 2^248 + mpz_sub_ui(mask, mask, 1); // mask = 2^248 - 1 + // Extract the low 248 bits and high bits from the element + mpz_and(low_part, element, mask); + mpz_fdiv_q_2exp(high_nibble, element, LOW_PART_BITS); + + stark_point res1, res2; + stark_point_init(&res1); + stark_point_init(&res2); + + stark_point_multiply(starkCurve, low_part, p1, &res1); // low_part * p1 + stark_point_multiply( + starkCurve, high_nibble, p2, &res2); // high_nibble * p2 + stark_point_add(starkCurve, &res1, &res2); + + stark_point_copy(&res2, result); + + // clear mpz vars + mpz_clear(low_part); + mpz_clear(high_nibble); + mpz_clear(mask); +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +void starknet_uli_to_bn_byte_array(const unsigned long int ui, + uint8_t *bn_array) { + mpz_t bn; + mpz_init(bn); + mpz_set_ui(bn, ui); + + memzero(bn_array, STARKNET_BIGNUM_SIZE); + mpz_to_byte_array(bn, bn_array, STARKNET_BIGNUM_SIZE); +} + +void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], + uint8_t num_elem, + uint8_t *hash) { + uint8_t result[STARKNET_BIGNUM_SIZE]; + memzero(result, STARKNET_BIGNUM_SIZE); + + for (uint8_t index = 0; index < num_elem; index++) { + pederson_hash(result, data[index], STARKNET_BIGNUM_SIZE, result); + } + + uint8_t num_elem_bn[32]; + starknet_uli_to_bn_byte_array(num_elem, num_elem_bn); + + pederson_hash(result, num_elem_bn, STARKNET_BIGNUM_SIZE, result); + + memcpy(hash, result, STARKNET_BIGNUM_SIZE); + return; +} diff --git a/apps/starknet_app/starknet_priv.h b/apps/starknet_app/starknet_priv.h new file mode 100644 index 00000000..bf1163b9 --- /dev/null +++ b/apps/starknet_app/starknet_priv.h @@ -0,0 +1,56 @@ +/** + * @file starknet_priv.h + * @author Cypherock X1 Team + * @brief Support for starknet app internal operations + * This file is defined to separate Starknet's internal use functions, + * flows, common APIs + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef STARKNET_PRIV_H +#define STARKNET_PRIV_H +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +typedef struct { + /** + * The structure holds the wallet information of the transaction. + * @note Populated by starknet_handle_initiate_query() + */ + starknet_sign_txn_initiate_request_t init_info; + + /// remembers the allocated buffer for holding complete unsigned transaction + pb_size_t which_type; + starknet_sign_txn_invoke_txn_t *invoke_txn; + starknet_sign_txn_deploy_account_txn_t *deploy_txn; + +} starknet_txn_context_t; + +/** + * @brief Handler for STARKNET public key derivation. + * @details This flow expects STARKNET_GET_PUBLIC_KEYS_REQUEST_INITIATE_TAG as + * initial query, otherwise the flow is aborted + * + * @param query object for address public key query + */ +void starknet_get_pub_keys(starknet_query_t *query); + +/** + * @brief Handler for signing a transaction on Starknet. + * @details The expected request type is STARKNET_SIGN_TXN_REQUEST_INITIATE_TAG. + * The function controls the complete data exchange with host, user prompts and + * confirmations for signing an Starknet based transaction. + * + * @param query Reference to the decoded query struct from the host app + */ +void starknet_sign_transaction(starknet_query_t *query); + +#endif /* STARKNET_PRIV_H */ \ No newline at end of file diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c new file mode 100644 index 00000000..23a4feec --- /dev/null +++ b/apps/starknet_app/starknet_pub_key.c @@ -0,0 +1,538 @@ +/** + * @file starknet_pub_key.c + * @author Cypherock X1 Team + * @brief Generates public key for Starknet derivations. + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + +#include "assert_conf.h" +#include "mini-gmp-helpers.h" +#include "reconstruct_wallet_flow.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_crypto.h" +#include "starknet_helpers.h" +#include "starknet_priv.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * static FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Checks if the provided query contains expected request. + * @details The function performs the check on the request type and if the check + * fails, then it will send an error to the host STARKNET app and return false. + * + * @param query Reference to an instance of starknet_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 starknet_query_t *query, + pb_size_t which_request); +/** + * @brief Validates all the derivation paths received in the request from host + * @details The function validates each path entry in the request. If any + * invalid path is detected, the function will send an error to the host and + * return false. + * + * @param request Reference to an instance of starknet_get_public_keys_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(starknet_get_public_keys_request_t *request, + const pb_size_t which_request); + +/** + * @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 public_keys. 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 starknet_query_t + * @param public_keys Reference to list of derived public keys to be sent to the + * host + * @param count Number of public key entries in the list of public keys + * + * @return bool Indicating if the public keys were 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(starknet_query_t *query, + const uint8_t public_keys[][STARKNET_PUB_KEY_SIZE], + const size_t count, + const pb_size_t which_request, + const pb_size_t which_response); +/** + * @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); + +/** + * @brief Derives a list of public key corresponding to the provided list of + * derivation paths. + * @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 paths Reference to the list of + * starknet_get_public_keys_derivation_path_t + * @param count Number of derivation paths in the list and consequently, + * sufficient space in memory for storing derived public keys. + * @param seed Reference to a const array containing the seed + * @param public_keys Reference to the location to store all the public keys to + * be derived + * + * @return bool Indicating if the complete public keys list was derived + * @retval true If all the requested public keys were derived. + * @retval false If the public key derivation failed. This could be due to + * invalid derivation path. + */ +static bool fill_starknet_public_keys( + const starknet_get_public_keys_derivation_path_t *paths, + const uint8_t *seed, + uint8_t public_keys[][STARKNET_PUB_KEY_SIZE], + pb_size_t count); + +/** + * @brief Calculates address bound required by @ref + * calculate_contract_address_from_hash + * @details addr_bound = 2^251 - @ref MAX_STORAGE_ITEM_SIZE + * + * @param addr_bound Stores calculated addr bound + */ +static void compute_addr_bound(mpz_t addr_bound); + +/** + * Calculate contract address from class hash + * + * @param pub_key Public key of deployed account + * @param deployer Deployer address of contract deployer + * @param salt Salt to be used for hashing + * @param class_hash Class hash of contract to generate address for + * @param addr Calculated account address + */ +static void calculate_contract_address_from_hash(const uint8_t *pub_key, + const uint8_t *deployer, + const uint8_t *salt, + const uint8_t *class_hash, + char *addr); + +/** + * Prepare ArgentX Call Data values for address calculation + * + * @param pub_key Public key of deployed account + * @param addr Calculated account address + */ +static void starknet_derive_argent_address(const uint8_t *pub_key, char *addr); +/***************************************************************************** + * static VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * static FUNCTIONS + *****************************************************************************/ + +static bool check_which_request(const starknet_query_t *query, + pb_size_t which_request) { + if (which_request != query->get_public_keys.which_request) { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + return false; + } + + return true; +} + +static bool validate_request_data(starknet_get_public_keys_request_t *request, + const pb_size_t which_request) { + bool status = true; + + if (0 == request->initiate.derivation_paths_count) { + // request does not have any derivation paths, invalid request + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + if (STARKNET_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request && + 1 < request->initiate.derivation_paths_count) { + // `STARKNET_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG` request contains more + // than one derivation path which is not expected + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + + starknet_get_public_keys_derivation_path_t *path = NULL; + pb_size_t count = request->initiate.derivation_paths_count; + for (pb_size_t index = 0; index < count; index++) { + path = &request->initiate.derivation_paths[index]; + if (!starknet_derivation_path_guard(path->path, path->path_count)) { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + break; + } + } + + return status; +} + +static bool fill_starknet_public_keys( + const starknet_get_public_keys_derivation_path_t *path, + const uint8_t *seed, + uint8_t public_key_list[][STARKNET_PUB_KEY_SIZE], + pb_size_t count) { + for (pb_size_t index = 0; index < count; index++) { + const starknet_get_public_keys_derivation_path_t *current = &path[index]; + if (!starknet_derive_key_from_seed(seed, + current->path, + current->path_count, + NULL, + public_key_list[index])) { + return false; + } + } + + return true; +} + +static bool send_public_keys(starknet_query_t *query, + const uint8_t public_keys[][STARKNET_PUB_KEY_SIZE], + const size_t count, + const pb_size_t which_request, + const pb_size_t which_response) { + starknet_result_t response = init_starknet_result(which_response); + starknet_get_public_keys_result_response_t *result = + &response.get_public_keys.result; + static const size_t batch_limit = + sizeof(response.get_public_keys.result.public_keys) / + STARKNET_PUB_KEY_SIZE; + size_t remaining = count; + + response.get_public_keys.which_response = + STARKNET_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(response.get_public_keys.result.public_keys, + public_keys[count - remaining], + batch_size * STARKNET_PUB_KEY_SIZE); + + starknet_send_result(&response); + remaining -= batch_size; + if (0 == remaining) { + break; + } + + if (!starknet_get_query(query, which_request) || + !check_which_request(query, + STARKNET_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 (STARKNET_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + snprintf(msg, + sizeof(msg), + UI_TEXT_ADD_ACCOUNT_PROMPT, + starknet_app.name, + wallet_name); + } else { + snprintf(msg, + sizeof(msg), + UI_TEXT_RECEIVE_TOKEN_PROMPT, + starknet_app.lunit2_name, + starknet_app.name, + wallet_name); + } + + return core_scroll_page(NULL, msg, starknet_send_error); +} + +static void compute_addr_bound(mpz_t addr_bound) { + mpz_t max_storage_item_size, base, pow_251; + mpz_init_set_ui(max_storage_item_size, 256); + mpz_init_set_ui(base, 2); + mpz_init(pow_251); + mpz_pow_ui(pow_251, base, 251); + mpz_sub(addr_bound, pow_251, max_storage_item_size); + + // clear mpz variables + mpz_clear(base); + mpz_clear(max_storage_item_size); + mpz_clear(pow_251); +} + +static void calculate_contract_address_from_hash(const uint8_t *pub_key, + const uint8_t *deployer, + const uint8_t *salt, + const uint8_t *class_hash, + char *addr) { + ASSERT(pub_key != NULL && deployer != NULL && salt != NULL && + class_hash != NULL && addr != NULL); + + // prepare array of elements for chain hashing + uint8_t call_data[CALL_DATA_PARAMETER_SIZE][STARKNET_BIGNUM_SIZE] = {0}; + // TODO: Get proper name of parameters and update variables + // ['0x0', stark_key_pub, '0x1'] + uint8_t zero_bn[STARKNET_BIGNUM_SIZE] = {0}; + starknet_uli_to_bn_byte_array(0, zero_bn); + memcpy(call_data[0], zero_bn, STARKNET_BIGNUM_SIZE); + memcpy(call_data[1], pub_key, STARKNET_SIZE_PUB_KEY); + uint8_t one_bn[STARKNET_BIGNUM_SIZE] = {0}; + starknet_uli_to_bn_byte_array(1, one_bn); + memcpy(call_data[2], one_bn, STARKNET_BIGNUM_SIZE); + + // get call data hash + uint8_t call_data_hash[PEDERSEN_HASH_SIZE] = {0}; + compute_hash_on_elements(call_data, CALL_DATA_PARAMETER_SIZE, call_data_hash); + + uint8_t starknet_contract_address_bn[STARKNET_BIGNUM_SIZE] = {0}; + hex_string_to_byte_array( + "00000000000000535441524b4e45545f434f4e54524143545" + "f41444452455353", ///< bn + ///< equivalnet + ///< of + ///< 'STARKNET_CONTRACT_ADDRESS' + STARKNET_BIGNUM_SIZE * 2, + starknet_contract_address_bn); + + // prepare array of elements for chain hashing + uint8_t data[5][STARKNET_BIGNUM_SIZE] = {0}; + memcpy(data[0], starknet_contract_address_bn, STARKNET_BIGNUM_SIZE); + memcpy(data[1], deployer, STARKNET_BIGNUM_SIZE); + memcpy(data[2], salt, STARKNET_BIGNUM_SIZE); + memcpy(data[3], class_hash, STARKNET_BIGNUM_SIZE); + memcpy(data[4], call_data_hash, STARKNET_BIGNUM_SIZE); + + uint8_t final_hash[PEDERSEN_HASH_SIZE] = {0}; + compute_hash_on_elements(data, 5, final_hash); + + mpz_t addr_bound; + mpz_init(addr_bound); + compute_addr_bound(addr_bound); + + mpz_t result_bn; + mpz_init(result_bn); + mpz_import(result_bn, STARKNET_BIGNUM_SIZE, 1, 1, 1, 0, final_hash); + + mpz_mod(result_bn, result_bn, addr_bound); + + mpz_get_str(addr, 16, result_bn); + + // clear mpz variables + mpz_clear(result_bn); + mpz_clear(addr_bound); +} + +static void starknet_derive_argent_address(const uint8_t *pub_key, char *addr) { + ASSERT(pub_key != NULL); + uint8_t deployer[32] = {0}; + starknet_uli_to_bn_byte_array(STARKNET_DEPLOYER_VALUE, deployer); + + uint8_t class_hash[32] = {0}; + hex_string_to_byte_array(STARKNET_ARGENT_CLASS_HASH, 64, class_hash); + + calculate_contract_address_from_hash(pub_key, + deployer, + pub_key, ///< salt = public key + class_hash, + addr); + return; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +void starknet_get_pub_keys(starknet_query_t *query) { + starknet_init(); + + char wallet_name[NAME_SIZE] = ""; + uint8_t seed[64] = {0}; + + const pb_size_t which_request = query->which_request; + starknet_get_public_keys_intiate_request_t *init_req = NULL; + pb_size_t which_response = STARKNET_RESULT_COMMON_ERROR_TAG; + + if (STARKNET_QUERY_GET_PUBLIC_KEYS_TAG == which_request) { + which_response = STARKNET_RESULT_GET_PUBLIC_KEYS_TAG; + init_req = &query->get_public_keys.initiate; + } else { + which_response = STARKNET_RESULT_GET_USER_VERIFIED_PUBLIC_KEY_TAG; + init_req = &query->get_user_verified_public_key.initiate; + } + + uint8_t public_keys[sizeof(init_req->derivation_paths) / + sizeof(starknet_get_public_keys_derivation_path_t)] + [STARKNET_PUB_KEY_SIZE] = {0}; + + if (!check_which_request(query, + STARKNET_GET_PUBLIC_KEYS_REQUEST_INITIATE_TAG) || + !validate_request_data(&query->get_public_keys, which_request) || + !get_wallet_name_by_id(query->get_public_keys.initiate.wallet_id, + (uint8_t *)wallet_name, + starknet_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(STARKNET_GET_PUBLIC_KEYS_STATUS_CONFIRM); + if (!reconstruct_seed(query->get_public_keys.initiate.wallet_id, + &seed[0], + starknet_send_error)) { + memzero(seed, sizeof(seed)); + return; + } + + set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_SEED_GENERATED); + delay_scr_init(ui_text_processing, DELAY_SHORT); + + bool status = fill_starknet_public_keys(init_req->derivation_paths, + seed, + public_keys, + init_req->derivation_paths_count); + + // Clear seed as soon as it is not needed + memzero(seed, sizeof(seed)); + + if (!status) { + // send unknown error; do not know failure reason + starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2); + return; + } + + if (STARKNET_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request) { + char address[100] = "0x"; + + // Calculate to-be account address + starknet_derive_argent_address(&public_keys[0][0], &address[2]); + + if (!core_scroll_page(ui_text_receive_on, address, starknet_send_error)) { + return; + } + + set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_VERIFY); + } + + if (!send_public_keys(query, + public_keys, + init_req->derivation_paths_count, + which_request, + which_response)) { + return; + } + + delay_scr_init(ui_text_check_cysync_app, DELAY_TIME); +} \ No newline at end of file diff --git a/common/core/app_registry.h b/common/core/app_registry.h index a064ea62..243f05b2 100644 --- a/common/core/app_registry.h +++ b/common/core/app_registry.h @@ -23,7 +23,7 @@ * MACROS AND DEFINES *****************************************************************************/ -#define REGISTRY_MAX_APPS 21 +#define REGISTRY_MAX_APPS 22 /***************************************************************************** * TYPEDEFS diff --git a/common/core/core_flow_init.c b/common/core/core_flow_init.c index 31e414f4..1faddf4f 100644 --- a/common/core/core_flow_init.c +++ b/common/core/core_flow_init.c @@ -83,6 +83,7 @@ #include "polygon_app.h" #include "restricted_app.h" #include "solana_main.h" +#include "starknet_main.h" #include "tron_main.h" #include "xrp_main.h" @@ -183,4 +184,5 @@ void core_init_app_registry() { registry_add_app(get_tron_app_desc()); registry_add_app(get_inheritance_app_desc()); registry_add_app(get_xrp_app_desc()); + registry_add_app(get_starknet_app_desc()); } diff --git a/common/cypherock-common b/common/cypherock-common index 19912e8d..0065cf12 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 19912e8d052064c477d6b11ca2e26efa4c483bfe +Subproject commit 0065cf1229d18abf14b0b1b194e681a85c9c9082 diff --git a/common/proto-options/starknet/core.options b/common/proto-options/starknet/core.options new file mode 100644 index 00000000..ea880b80 --- /dev/null +++ b/common/proto-options/starknet/core.options @@ -0,0 +1 @@ +# Options for file common/cypherock-common/proto/starknet/core.proto \ No newline at end of file diff --git a/common/proto-options/starknet/get_public_keys.options b/common/proto-options/starknet/get_public_keys.options new file mode 100644 index 00000000..386b9e2c --- /dev/null +++ b/common/proto-options/starknet/get_public_keys.options @@ -0,0 +1,5 @@ +# Options for file common/cypherock-common/proto/starknet/get_public_keys.proto +starknet.GetPublicKeysDerivationPath.path type:FT_STATIC max_count:6 fixed_length:true +starknet.GetPublicKeysIntiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +starknet.GetPublicKeysIntiateRequest.derivation_paths type:FT_STATIC max_count:100 fixed_length:true +starknet.GetPublicKeysResultResponse.public_keys type:FT_STATIC max_size:32 max_count:10 fixed_length:true \ No newline at end of file diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index 08ba9d38..82936bf2 100644 --- a/utilities/cmake/firmware/firmware.cmake +++ b/utilities/cmake/firmware/firmware.cmake @@ -19,7 +19,7 @@ ELSE() file(GLOB_RECURSE SOURCES "stm32-hal/*.*" "common/*.*" "src/*.*" "apps/*.*") ENDIF(UNIT_TESTS_SWITCH) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c vendor/mini-gmp/mini-gmp-helpers.c vendor/mini-gmp/mini-gmp.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) target_compile_definitions(${EXECUTABLE} PRIVATE -DUSE_HAL_DRIVER -DSTM32L486xx ) add_compile_definitions(USE_SIMULATOR=0 USE_BIP32_CACHE=0 USE_BIP39_CACHE=0 STM32L4 USBD_SOF_DISABLED ENABLE_HID_WEBUSB_COMM=1) IF (DEV_SWITCH) @@ -59,7 +59,7 @@ target_include_directories(${EXECUTABLE} PRIVATE apps/solana_app apps/tron_app apps/inheritance_app - + apps/starknet_app apps/xrp_app @@ -133,6 +133,8 @@ target_include_directories(${EXECUTABLE} PRIVATE common/lvgl/src/lv_objx common/lvgl/src/lv_themes + vendor/mini-gmp + # Device stm32-hal stm32-hal/BSP diff --git a/utilities/cmake/simulator/simulator.cmake b/utilities/cmake/simulator/simulator.cmake index 12894a47..c7a977d5 100644 --- a/utilities/cmake/simulator/simulator.cmake +++ b/utilities/cmake/simulator/simulator.cmake @@ -20,7 +20,7 @@ set(EXECUTABLE ${PROJECT_NAME}) find_package(SDL2 REQUIRED SDL2) include_directories(${SDL2_INCLUDE_DIRS}) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c vendor/mini-gmp/mini-gmp-helpers.c vendor/mini-gmp/mini-gmp.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) if ("${FIRMWARE_TYPE}" STREQUAL "Main") add_compile_definitions(X1WALLET_INITIAL=0 X1WALLET_MAIN=1) @@ -57,6 +57,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE apps/tron_app apps/inheritance_app apps/xrp_app + apps/starknet_app src/ src/menu @@ -128,6 +129,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE common/lvgl/src/lv_objx common/lvgl/src/lv_themes + vendor/mini-gmp + # Simulator simulator simulator/BSP diff --git a/vendor/mini-gmp b/vendor/mini-gmp new file mode 160000 index 00000000..114b80c1 --- /dev/null +++ b/vendor/mini-gmp @@ -0,0 +1 @@ +Subproject commit 114b80c19742a3c9058da81e72e088766154ac53 From bc9dd9c8e0a292bd1fc0dc0a4bce054cf050feed Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Thu, 12 Dec 2024 15:53:09 +0530 Subject: [PATCH 04/26] chore: Add function docs --- apps/starknet_app/starknet_crypto.c | 5 +- apps/starknet_app/starknet_crypto.h | 95 ++++++++++++++++++++++- apps/starknet_app/starknet_helpers.h | 13 +--- apps/starknet_app/starknet_main.c | 1 + apps/starknet_app/starknet_pedersen.c | 19 ++++- apps/starknet_app/starknet_pedersen.h | 104 ++++++++++++++++++++++++++ apps/starknet_app/starknet_priv.h | 2 +- apps/starknet_app/starknet_pub_key.c | 2 + 8 files changed, 224 insertions(+), 17 deletions(-) create mode 100644 apps/starknet_app/starknet_pedersen.h diff --git a/apps/starknet_app/starknet_crypto.c b/apps/starknet_app/starknet_crypto.c index f7a1c156..d549f994 100644 --- a/apps/starknet_app/starknet_crypto.c +++ b/apps/starknet_app/starknet_crypto.c @@ -61,7 +61,6 @@ *****************************************************************************/ #include "starknet_crypto.h" -// #include "bignum_internal.h" #include #include "mini-gmp.h" @@ -117,9 +116,9 @@ void stark_point_clear(stark_point *p) { mpz_clear(p->y); } -void stark_pedersen_clear(stark_pedersen *pedersen) { +void stark_pedersen_clear() { for (int i = 0; i < 5; i++) { - stark_point_clear(&pedersen->P[i]); + stark_point_clear(&starkPts->P[i]); } } diff --git a/apps/starknet_app/starknet_crypto.h b/apps/starknet_app/starknet_crypto.h index 937238ef..e132c602 100644 --- a/apps/starknet_app/starknet_crypto.h +++ b/apps/starknet_app/starknet_crypto.h @@ -51,21 +51,114 @@ extern stark_pedersen *starkPts; /***************************************************************************** * GLOBAL FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Initialize a STARK point. + * @details Allocates and initializes resources for a STARK point. + * + * @param p Pointer to the STARK point to initialize. + */ void stark_point_init(stark_point *p); + +/** + * @brief Clear a STARK point. + * @details Releases resources associated with the STARK point. + * + * @param p Pointer to the STARK point to clear. + */ void stark_point_clear(stark_point *p); + +/** + * @brief Copy a STARK point. + * @details Copies one STARK point to another. + * + * @param cp1 Source STARK point. + * @param cp2 Destination STARK point. + */ void stark_point_copy(const stark_point *cp1, stark_point *cp2); + +/** + * @brief Add two STARK points. + * @details Computes the sum of two STARK points on the given curve. + * + * @param curve Curve defining the group operation. + * @param cp1 First STARK point. + * @param cp2 Second STARK point; result is stored here. + */ void stark_point_add(const stark_curve *curve, const stark_point *cp1, stark_point *cp2); + +/** + * @brief Double a STARK point. + * @details Computes the point doubling operation on the given curve. + * + * @param curve Curve defining the group operation. + * @param cp STARK point to double; result is stored here. + */ void stark_point_double(const stark_curve *curve, stark_point *cp); + +/** + * @brief Multiply a STARK point. + * @details Computes scalar multiplication of a STARK point on the given curve. + * + * @param curve Curve defining the group operation. + * @param k Scalar multiplier. + * @param p STARK point to multiply. + * @param res Resulting STARK point is stored here. + */ void stark_point_multiply(const stark_curve *curve, const mpz_t k, const stark_point *p, stark_point *res); + +/** + * @brief Set a STARK point to infinity. + * @details Configures a STARK point to represent the point at infinity. + * + * @param p STARK point to modify. + */ void stark_point_set_infinity(stark_point *p); + +/** + * @brief Check if a STARK point is at infinity. + * @details Determines if the given STARK point represents the point at + * infinity. + * + * @param p STARK point to check. + * @return Non-zero if the point is at infinity, 0 otherwise. + */ int stark_point_is_infinity(const stark_point *p); + +/** + * @brief Check if two STARK points are equal. + * @details Compares two STARK points for equality. + * + * @param p First STARK point. + * @param q Second STARK point. + * @return Non-zero if points are equal, 0 otherwise. + */ int stark_point_is_equal(const stark_point *p, const stark_point *q); + +/** + * @brief Check if one STARK point is the negative of another. + * @details Determines if the given points are negatives of each other. + * + * @param p First STARK point. + * @param q Second STARK point. + * @return Non-zero if points are negatives, 0 otherwise. + */ int stark_point_is_negative_of(const stark_point *p, const stark_point *q); -void stark_pedersen_clear(stark_pedersen *pedersen); + +/** + * @brief Clear Pedersen resources. + * @details Releases all resources related to Pedersen hash operations. + */ +void stark_pedersen_clear(); + +/** + * @brief Initialize STARKNET context. + * @details Sets up resources and state for STARKNET operations. + */ void starknet_init(); + #endif // STARKNET_CRYPTO_H \ No newline at end of file diff --git a/apps/starknet_app/starknet_helpers.h b/apps/starknet_app/starknet_helpers.h index e62918ab..b9eaf231 100644 --- a/apps/starknet_app/starknet_helpers.h +++ b/apps/starknet_app/starknet_helpers.h @@ -17,20 +17,11 @@ #include #include +#include "starknet_pedersen.h" + /***************************************************************************** * MACROS AND DEFINES *****************************************************************************/ -#define LOW_PART_BITS 248 -#define LOW_PART_BYTES (LOW_PART_BITS / 8) -#define LOW_PART_MASK ((1ULL << LOW_PART_BITS) - 1) -#define STARKNET_BIGNUM_SIZE 32 -#define PEDERSEN_HASH_SIZE 32 -#define CALL_DATA_PARAMETER_SIZE 3 -#define STARKNET_SIZE_PUB_KEY (32) -#define STARKNET_ADDR_SIZE 32 -#define STARKNET_ARGENT_CLASS_HASH \ - "036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f" -#define STARKNET_DEPLOYER_VALUE 0 /***************************************************************************** * TYPEDEFS diff --git a/apps/starknet_app/starknet_main.c b/apps/starknet_app/starknet_main.c index e22eaefb..70c7039f 100644 --- a/apps/starknet_app/starknet_main.c +++ b/apps/starknet_app/starknet_main.c @@ -65,6 +65,7 @@ #include "starknet_api.h" #include "starknet_priv.h" #include "status_api.h" +#include "ui_core_confirm.h" /***************************************************************************** * EXTERN VARIABLES diff --git a/apps/starknet_app/starknet_pedersen.c b/apps/starknet_app/starknet_pedersen.c index 89b39b33..84e31ef5 100644 --- a/apps/starknet_app/starknet_pedersen.c +++ b/apps/starknet_app/starknet_pedersen.c @@ -1,5 +1,5 @@ /** - * @file starknet_padersen.c + * @file starknet_pedersen.c * @author Cypherock X1 Team * @brief Utilities specific to Starknet chains * @copyright Copyright (c) 2023 HODL TECH PTE LTD @@ -60,6 +60,8 @@ * INCLUDES *****************************************************************************/ +#include "starknet_pedersen.h" + #include #include @@ -139,6 +141,15 @@ bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { memzero(hash, 32); mpz_to_byte_array(hash_point.x, hash, 32); + // clear stark points + stark_point_clear(&x_part); + stark_point_clear(&y_part); + stark_point_clear(&hash_point); + + mpz_clear(a); + mpz_clear(b); + mpz_clear(result); + return true; } @@ -177,6 +188,9 @@ void process_single_element(mpz_t element, mpz_clear(low_part); mpz_clear(high_nibble); mpz_clear(mask); + + stark_point_clear(&res1); + stark_point_clear(&res2); } /***************************************************************************** @@ -190,6 +204,9 @@ void starknet_uli_to_bn_byte_array(const unsigned long int ui, memzero(bn_array, STARKNET_BIGNUM_SIZE); mpz_to_byte_array(bn, bn_array, STARKNET_BIGNUM_SIZE); + + // clear mpz vars + mpz_clear(bn); } void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], diff --git a/apps/starknet_app/starknet_pedersen.h b/apps/starknet_app/starknet_pedersen.h new file mode 100644 index 00000000..c41ade73 --- /dev/null +++ b/apps/starknet_app/starknet_pedersen.h @@ -0,0 +1,104 @@ +/** + * @file starknet_pedersen.h + * @author Cypherock X1 Team + * @brief Utilities specific to Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + +#include "coin_utils.h" +#include "mini-gmp-helpers.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_crypto.h" +#include "starknet_helpers.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ +#define LOW_PART_BITS 248 +#define LOW_PART_BYTES (LOW_PART_BITS / 8) +#define LOW_PART_MASK ((1ULL << LOW_PART_BITS) - 1) + +#define STARKNET_BIGNUM_SIZE 32 +#define PEDERSEN_HASH_SIZE 32 + +#define CALL_DATA_PARAMETER_SIZE 3 +#define STARKNET_SIZE_PUB_KEY (32) + +#define STARKNET_ADDR_SIZE 32 +#define STARKNET_ARGENT_CLASS_HASH \ + "036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f" +#define STARKNET_DEPLOYER_VALUE 0 +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ diff --git a/apps/starknet_app/starknet_priv.h b/apps/starknet_app/starknet_priv.h index bf1163b9..4f6ffb87 100644 --- a/apps/starknet_app/starknet_priv.h +++ b/apps/starknet_app/starknet_priv.h @@ -51,6 +51,6 @@ void starknet_get_pub_keys(starknet_query_t *query); * * @param query Reference to the decoded query struct from the host app */ -void starknet_sign_transaction(starknet_query_t *query); +// void starknet_sign_transaction(starknet_query_t *query); #endif /* STARKNET_PRIV_H */ \ No newline at end of file diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c index 23a4feec..86fa38f1 100644 --- a/apps/starknet_app/starknet_pub_key.c +++ b/apps/starknet_app/starknet_pub_key.c @@ -70,6 +70,7 @@ #include "starknet_context.h" #include "starknet_crypto.h" #include "starknet_helpers.h" +#include "starknet_pedersen.h" #include "starknet_priv.h" #include "status_api.h" #include "ui_core_confirm.h" @@ -526,6 +527,7 @@ void starknet_get_pub_keys(starknet_query_t *query) { set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_VERIFY); } + stark_pedersen_clear(); if (!send_public_keys(query, public_keys, init_req->derivation_paths_count, From 5e805ed5df591b82a37b589845eea14b0e016134 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Fri, 13 Dec 2024 20:02:26 +0530 Subject: [PATCH 05/26] chore: Ci fix --- .gitmodules | 4 ++-- CMakeLists.txt | 4 +++- utilities/cmake/firmware/firmware.cmake | 4 +--- utilities/cmake/simulator/simulator.cmake | 4 +--- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index 39795457..222ea7ab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,6 @@ [submodule "nanopb"] path = vendor/nanopb url = ../nanopb -[submodule "vendor/mini-gmp"] +[submodule "mini-gmp"] path = vendor/mini-gmp - url = https://github.com/Cypherock/mini-gmp + url = ../mini-gmp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d5a3e5c..9f88ab9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,8 @@ include(utilities/cmake/version.cmake) file(GLOB_RECURSE PROTO_SRCS "generated/proto/*.*") list(APPEND PROTO_SRCS "vendor/nanopb/pb_common.c" "vendor/nanopb/pb_decode.c" "vendor/nanopb/pb_encode.c" "vendor/nanopb/pb_common.h" "vendor/nanopb/pb_decode.h" "vendor/nanopb/pb_encode.h" "vendor/nanopb/pb.h") +list (APPEND MINI_GMP_SRCS "vendor/mini-gmp/mini-gmp-helpers.c" "vendor/mini-gmp/mini-gmp.c") + OPTION(DEV_SWITCH "Additional features/logs to aid developers" OFF) OPTION(UNIT_TESTS_SWITCH "Compile build for main firmware or unit tests" OFF) @@ -49,7 +51,7 @@ else() endif() # Include nanopb source headers -target_include_directories( ${EXECUTABLE} PRIVATE vendor/nanopb generated/proto ) +target_include_directories( ${EXECUTABLE} PRIVATE vendor/nanopb generated/proto vendor/mini-gmp) # Enable support for dynamically allocated fields in nanopb # Ref: vendor/nanopb/pb.h diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index 82936bf2..38c89ac4 100644 --- a/utilities/cmake/firmware/firmware.cmake +++ b/utilities/cmake/firmware/firmware.cmake @@ -19,7 +19,7 @@ ELSE() file(GLOB_RECURSE SOURCES "stm32-hal/*.*" "common/*.*" "src/*.*" "apps/*.*") ENDIF(UNIT_TESTS_SWITCH) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c vendor/mini-gmp/mini-gmp-helpers.c vendor/mini-gmp/mini-gmp.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) target_compile_definitions(${EXECUTABLE} PRIVATE -DUSE_HAL_DRIVER -DSTM32L486xx ) add_compile_definitions(USE_SIMULATOR=0 USE_BIP32_CACHE=0 USE_BIP39_CACHE=0 STM32L4 USBD_SOF_DISABLED ENABLE_HID_WEBUSB_COMM=1) IF (DEV_SWITCH) @@ -133,8 +133,6 @@ target_include_directories(${EXECUTABLE} PRIVATE common/lvgl/src/lv_objx common/lvgl/src/lv_themes - vendor/mini-gmp - # Device stm32-hal stm32-hal/BSP diff --git a/utilities/cmake/simulator/simulator.cmake b/utilities/cmake/simulator/simulator.cmake index c7a977d5..1e6e7234 100644 --- a/utilities/cmake/simulator/simulator.cmake +++ b/utilities/cmake/simulator/simulator.cmake @@ -20,7 +20,7 @@ set(EXECUTABLE ${PROJECT_NAME}) find_package(SDL2 REQUIRED SDL2) include_directories(${SDL2_INCLUDE_DIRS}) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c vendor/mini-gmp/mini-gmp-helpers.c vendor/mini-gmp/mini-gmp.c ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) if ("${FIRMWARE_TYPE}" STREQUAL "Main") add_compile_definitions(X1WALLET_INITIAL=0 X1WALLET_MAIN=1) @@ -129,8 +129,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE common/lvgl/src/lv_objx common/lvgl/src/lv_themes - vendor/mini-gmp - # Simulator simulator simulator/BSP From a2f86f8c74102fa6122fa8a1bb4dde0a9a0e8ab2 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Tue, 17 Dec 2024 22:27:57 +0530 Subject: [PATCH 06/26] chore: Refactor starknet ecdsa operations --- apps/starknet_app/starknet_crypto.c | 267 +++------------- apps/starknet_app/starknet_crypto.h | 137 +-------- apps/starknet_app/starknet_helpers.c | 8 +- apps/starknet_app/starknet_helpers.h | 17 -- apps/starknet_app/starknet_pedersen.c | 99 +----- apps/starknet_app/starknet_pedersen.h | 27 +- apps/starknet_app/starknet_pub_key.c | 6 +- .../crypto/mpz_operations/mpz_ecdsa.c | 288 ++++++++++++++++++ .../crypto/mpz_operations/mpz_ecdsa.h | 150 +++++++++ .../crypto/mpz_operations/mpz_pedersen.c | 191 ++++++++++++ .../crypto/mpz_operations/mpz_pedersen.h | 119 ++++++++ utilities/cmake/firmware/firmware.cmake | 1 + utilities/cmake/simulator/simulator.cmake | 1 + 13 files changed, 824 insertions(+), 487 deletions(-) create mode 100644 common/libraries/crypto/mpz_operations/mpz_ecdsa.c create mode 100644 common/libraries/crypto/mpz_operations/mpz_ecdsa.h create mode 100644 common/libraries/crypto/mpz_operations/mpz_pedersen.c create mode 100644 common/libraries/crypto/mpz_operations/mpz_pedersen.h diff --git a/apps/starknet_app/starknet_crypto.c b/apps/starknet_app/starknet_crypto.c index d549f994..d844ae51 100644 --- a/apps/starknet_app/starknet_crypto.c +++ b/apps/starknet_app/starknet_crypto.c @@ -64,6 +64,7 @@ #include #include "mini-gmp.h" +#include "mpz_ecdsa.h" /***************************************************************************** * EXTERN VARIABLES @@ -80,7 +81,18 @@ /***************************************************************************** * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Initializes @ref stark_curve instance based on star curve params + ref: + https://github.com/xJonathanLEI/starknet-rs/blob/f31e426a65225b9830bbf3c148f7ea05bf9dc257/starknet-curve/src/curve_params.rs + + */ +static void stark_curve_init(); +/** + * @brief Initializes starknet pedersen points + */ +static void stark_pedersen_init(); /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -88,54 +100,14 @@ /***************************************************************************** * GLOBAL VARIABLES *****************************************************************************/ -stark_curve *starkCurve; -stark_pedersen *starkPts; +mpz_curve *stark_curve; +mpz_pedersen *starknet_pedersen_points; /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ -static void stark_curve_init(); -static void stark_pedersen_init(); - -/***************************************************************************** - * GLOBAL FUNCTIONS - *****************************************************************************/ - -void starknet_init() { - stark_curve_init(); - stark_pedersen_init(); -} - -void stark_point_init(stark_point *p) { - mpz_init(p->x); - mpz_init(p->y); -} - -void stark_point_clear(stark_point *p) { - mpz_clear(p->x); - mpz_clear(p->y); -} - -void stark_pedersen_clear() { - for (int i = 0; i < 5; i++) { - stark_point_clear(&starkPts->P[i]); - } -} - -void stark_curve_init() { - static stark_curve stark256; - // char str[STARK_BN_LEN] = {0}; - - /* stark_curve_params ref: - https://github.com/xJonathanLEI/starknet-rs/blob/f31e426a65225b9830bbf3c148f7ea05bf9dc257/starknet-curve/src/curve_params.rs - - struct bn prime; // prime order of the finite field - stark_point G; // initial curve point - struct bn order; // order of G - struct bn order_half; // order of G divided by 2 - struct bn a; // coefficient 'a' of the elliptic curve OR alpha - struct bn b; // coefficient 'b' of the elliptic curve OR beta - */ +static void stark_curve_init() { + static mpz_curve stark256; // Initialize mpz_t variables in stark256 mpz_init(stark256.prime); @@ -188,14 +160,13 @@ void stark_curve_init() { "06f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89", 16); - starkCurve = &stark256; - // print_stark_curve(); + stark_curve = &stark256; } static void stark_pedersen_init() { // Ref: https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html - static stark_pedersen pedersen; + static mpz_pedersen pedersen; // Initialize all mpz_t variables in the pedersen structure for (int i = 0; i < 5; i++) { mpz_init(pedersen.P[i].x); @@ -261,194 +232,28 @@ static void stark_pedersen_init() { "01B77B3E37D13504B348046268D8AE25CE98AD783C25561A879DCC77E99C2426", 16); - starkPts = &pedersen; -} - -// Set cp2 = cp1 -void stark_point_copy(const stark_point *cp1, stark_point *cp2) { - stark_point_init(cp2); - - mpz_set(cp2->x, cp1->x); - mpz_set(cp2->y, cp1->y); -} - -void stark_point_add(const stark_curve *curve, - const stark_point *cp1, - stark_point *cp2) { - mpz_t lambda, inv, xr, yr; - - mpz_init(lambda); - mpz_init(inv); - mpz_init(xr); - mpz_init(yr); - - if (stark_point_is_infinity(cp1)) { - return; - } - if (stark_point_is_infinity(cp2)) { - stark_point_copy(cp1, cp2); - return; - } - if (stark_point_is_equal(cp1, cp2)) { - stark_point_double(curve, cp2); - return; - } - if (stark_point_is_negative_of(cp1, cp2)) { - stark_point_set_infinity(cp2); - return; - } - - // inv = (cp2->x - cp1->x) mod prime - mpz_sub(inv, cp2->x, cp1->x); - mpz_mod(inv, inv, curve->prime); - - // inv = inv^-1 mod prime - mpz_invert(inv, inv, curve->prime); - - // lambda = (cp2->y - cp1->y) mod prime - mpz_sub(lambda, cp2->y, cp1->y); - mpz_mod(lambda, lambda, curve->prime); - - // lambda = lambda * inv mod prime - mpz_mul(lambda, lambda, inv); - mpz_mod(lambda, lambda, curve->prime); - - // xr = lambda^2 - cp1->x - cp2->x mod prime - mpz_mul(xr, lambda, lambda); - mpz_sub(xr, xr, cp1->x); - mpz_sub(xr, xr, cp2->x); - mpz_mod(xr, xr, curve->prime); - - // yr = lambda * (cp1->x - xr) - cp1->y mod prime - mpz_sub(yr, cp1->x, xr); - mpz_mul(yr, yr, lambda); - mpz_sub(yr, yr, cp1->y); - mpz_mod(yr, yr, curve->prime); - - // Set cp2 to the result - mpz_set(cp2->x, xr); - mpz_set(cp2->y, yr); - - mpz_clear(lambda); - mpz_clear(inv); - mpz_clear(xr); - mpz_clear(yr); -} - -void stark_point_double(const stark_curve *curve, stark_point *cp) { - // Ref: - // https://github.com/starkware-libs/starkex-for-spot-trading/blob/607f0b4ce507e1d95cd018d206a2797f6ba4aab4/src/starkware/crypto/starkware/crypto/signature/math_utils.py - if (mpz_cmp_ui(cp->y, 0) == 0) { - return; - } - - mpz_t lambda, xr, yr, inv; - mpz_init(lambda); - mpz_init(xr); - mpz_init(yr); - mpz_init(inv); - - // lambda = (3 * cp->x^2 + curve->a) * (2 * cp->y)^-1 mod prime - mpz_mul(lambda, cp->x, cp->x); - mpz_mul_ui(lambda, lambda, 3); - mpz_add(lambda, lambda, curve->a); - mpz_mul_ui(inv, cp->y, 2); // using inv to store 2 * y - mpz_invert(inv, inv, curve->prime); - mpz_mul(lambda, lambda, inv); - mpz_mod(lambda, lambda, curve->prime); - - // xr = lambda^2 - 2 * cp->x mod prime - mpz_mul(xr, lambda, lambda); - mpz_submul_ui(xr, cp->x, 2); - mpz_mod(xr, xr, curve->prime); - - // yr = lambda * (cp->x - xr) - cp->y mod prime - mpz_sub(yr, cp->x, xr); - mpz_mul(yr, yr, lambda); - mpz_sub(yr, yr, cp->y); - mpz_mod(yr, yr, curve->prime); - - mpz_set(cp->x, xr); - mpz_set(cp->y, yr); - - mpz_clear(lambda); - mpz_clear(xr); - mpz_clear(yr); - mpz_clear(inv); -} - -// set point to internal representation of point at infinity -void stark_point_set_infinity(stark_point *p) { - mpz_set_ui(p->x, 0); - mpz_set_ui(p->y, 0); -} - -// return true iff p represent point at infinity -// both coords are zero in internal representation -int stark_point_is_infinity(const stark_point *p) { - return mpz_cmp_ui(p->x, 0) == 0 && mpz_cmp_ui(p->y, 0) == 0; -} - -// return true iff both points are equal -int stark_point_is_equal(const stark_point *p, const stark_point *q) { - return (mpz_cmp(p->x, q->x) == 0) && (mpz_cmp(p->y, q->y) == 0); + starknet_pedersen_points = &pedersen; } -// returns true iff p == -q -// expects p and q be valid points on curve other than point at infinity -int stark_point_is_negative_of(const stark_point *p, const stark_point *q) { - // if P == (x, y), then -P would be (x, -y) on this curve - if (mpz_cmp(p->x, q->x) != 0) { - return 0; - } - - // we shouldn't hit this for a valid point - if (mpz_cmp_ui(p->y, 0) == 0) { - return 0; - } - - return mpz_cmp(p->y, q->y) != 0; -} - -void stark_point_multiply(const stark_curve *curve, - const mpz_t k, - const stark_point *p, - stark_point *res) { - // Ref: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication - - stark_point temp; - stark_point R; - stark_point_init(&temp); - stark_point_init(&R); - stark_point_set_infinity(&R); // Initialize R to the point at infinity - stark_point_copy(p, &temp); // Copy the input point p to temp - - // Iterate over each bit of k from the least significant to the most - // significant - for (int i = 0; i < 256; i++) { - // If the i-th bit of k is set, add temp to the result R - if (mpz_tstbit(k, i)) { - stark_point_add(curve, &temp, &R); - } - - // Double the current point temp - stark_point_double(curve, &temp); - } - - // Copy the result R to the output parameter res - stark_point_copy(&R, res); +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ - stark_point_clear(&temp); - stark_point_clear(&R); +void starknet_init() { + stark_curve_init(); + stark_pedersen_init(); } -int bn_bit_length(const mpz_t k) { - if (mpz_cmp_ui(k, 0) == 0) { - return 0; +void stark_clear() { + // clear pedersen points + for (int i = 0; i < 5; i++) { + mpz_curve_point_clear(&starknet_pedersen_points->P[i]); } - return mpz_sizeinbase(k, 2); -} - -int bn_is_bit_set(const mpz_t k, int bit_idx) { - return mpz_tstbit(k, bit_idx); + // clear stark curve points + mpz_clear(stark_curve->a); + mpz_clear(stark_curve->b); + mpz_curve_point_clear(&stark_curve->G); + mpz_clear(stark_curve->order); + mpz_clear(stark_curve->order_half); + mpz_clear(stark_curve->prime); } diff --git a/apps/starknet_app/starknet_crypto.h b/apps/starknet_app/starknet_crypto.h index e132c602..418e5aec 100644 --- a/apps/starknet_app/starknet_crypto.h +++ b/apps/starknet_app/starknet_crypto.h @@ -12,148 +12,24 @@ /***************************************************************************** * INCLUDES *****************************************************************************/ - -#include "mini-gmp.h" - +#include "mpz_ecdsa.h" /***************************************************************************** * MACROS AND DEFINES *****************************************************************************/ -#define STARK_BN_LEN 64 /***************************************************************************** * TYPEDEFS *****************************************************************************/ -// curve point x and y -typedef struct { - mpz_t x, y; -} stark_point; - -typedef struct { - mpz_t prime; // prime order of the finite field - stark_point G; // initial curve point - mpz_t order; // order of G - mpz_t order_half; // order of G divided by 2 - mpz_t a; // coefficient 'a' of the elliptic curve - mpz_t b; // coefficient 'b' of the elliptic curve -} stark_curve; - -typedef struct { - stark_point P[5]; -} stark_pedersen; - /***************************************************************************** * EXPORTED VARIABLES *****************************************************************************/ -extern stark_curve *starkCurve; -extern stark_pedersen *starkPts; +extern mpz_curve *stark_curve; +extern mpz_pedersen *starknet_pedersen_points; /***************************************************************************** * GLOBAL FUNCTION PROTOTYPES *****************************************************************************/ -/** - * @brief Initialize a STARK point. - * @details Allocates and initializes resources for a STARK point. - * - * @param p Pointer to the STARK point to initialize. - */ -void stark_point_init(stark_point *p); - -/** - * @brief Clear a STARK point. - * @details Releases resources associated with the STARK point. - * - * @param p Pointer to the STARK point to clear. - */ -void stark_point_clear(stark_point *p); - -/** - * @brief Copy a STARK point. - * @details Copies one STARK point to another. - * - * @param cp1 Source STARK point. - * @param cp2 Destination STARK point. - */ -void stark_point_copy(const stark_point *cp1, stark_point *cp2); - -/** - * @brief Add two STARK points. - * @details Computes the sum of two STARK points on the given curve. - * - * @param curve Curve defining the group operation. - * @param cp1 First STARK point. - * @param cp2 Second STARK point; result is stored here. - */ -void stark_point_add(const stark_curve *curve, - const stark_point *cp1, - stark_point *cp2); - -/** - * @brief Double a STARK point. - * @details Computes the point doubling operation on the given curve. - * - * @param curve Curve defining the group operation. - * @param cp STARK point to double; result is stored here. - */ -void stark_point_double(const stark_curve *curve, stark_point *cp); - -/** - * @brief Multiply a STARK point. - * @details Computes scalar multiplication of a STARK point on the given curve. - * - * @param curve Curve defining the group operation. - * @param k Scalar multiplier. - * @param p STARK point to multiply. - * @param res Resulting STARK point is stored here. - */ -void stark_point_multiply(const stark_curve *curve, - const mpz_t k, - const stark_point *p, - stark_point *res); - -/** - * @brief Set a STARK point to infinity. - * @details Configures a STARK point to represent the point at infinity. - * - * @param p STARK point to modify. - */ -void stark_point_set_infinity(stark_point *p); - -/** - * @brief Check if a STARK point is at infinity. - * @details Determines if the given STARK point represents the point at - * infinity. - * - * @param p STARK point to check. - * @return Non-zero if the point is at infinity, 0 otherwise. - */ -int stark_point_is_infinity(const stark_point *p); - -/** - * @brief Check if two STARK points are equal. - * @details Compares two STARK points for equality. - * - * @param p First STARK point. - * @param q Second STARK point. - * @return Non-zero if points are equal, 0 otherwise. - */ -int stark_point_is_equal(const stark_point *p, const stark_point *q); - -/** - * @brief Check if one STARK point is the negative of another. - * @details Determines if the given points are negatives of each other. - * - * @param p First STARK point. - * @param q Second STARK point. - * @return Non-zero if points are negatives, 0 otherwise. - */ -int stark_point_is_negative_of(const stark_point *p, const stark_point *q); - -/** - * @brief Clear Pedersen resources. - * @details Releases all resources related to Pedersen hash operations. - */ -void stark_pedersen_clear(); /** * @brief Initialize STARKNET context. @@ -161,4 +37,11 @@ void stark_pedersen_clear(); */ void starknet_init(); +/** + * @brief Clears starknet resources. + * @details Releases all resources related to starkent hash and curve + * operations. + */ +void stark_clear(); + #endif // STARKNET_CRYPTO_H \ No newline at end of file diff --git a/apps/starknet_app/starknet_helpers.c b/apps/starknet_app/starknet_helpers.c index 605125d9..9682b8dd 100644 --- a/apps/starknet_app/starknet_helpers.c +++ b/apps/starknet_app/starknet_helpers.c @@ -185,8 +185,8 @@ bool starknet_derive_key_from_seed(const uint8_t *seed, } uint8_t stark_private_key[32] = {0}; - stark_point p; - stark_point_init(&p); + mpz_curve_point p; + mpz_curve_point_init(&p); if (!grind_key(stark_child_node.private_key, stark_private_key)) { return false; } @@ -201,7 +201,7 @@ bool starknet_derive_key_from_seed(const uint8_t *seed, mpz_t priv_key; mpz_init(priv_key); byte_array_to_mpz(priv_key, stark_private_key, 32); - stark_point_multiply(starkCurve, priv_key, &starkCurve->G, &p); + mpz_curve_point_multiply(stark_curve, priv_key, &stark_curve->G, &p); mpz_clear(priv_key); // clear priv key when no longer required uint8_t stark_public_key[32] = {0}; @@ -214,7 +214,7 @@ bool starknet_derive_key_from_seed(const uint8_t *seed, } // clear mpz variables - stark_point_clear(&p); + mpz_curve_point_clear(&p); return true; } diff --git a/apps/starknet_app/starknet_helpers.h b/apps/starknet_app/starknet_helpers.h index b9eaf231..063e7721 100644 --- a/apps/starknet_app/starknet_helpers.h +++ b/apps/starknet_app/starknet_helpers.h @@ -62,21 +62,4 @@ bool starknet_derive_key_from_seed(const uint8_t *seed, uint8_t *private_key, uint8_t *public_key); -/** - * @brief Converts unsigned long int to byte array - */ -void starknet_uli_to_bn_byte_array(const unsigned long int ui, - uint8_t *bn_array); - -/** - * Compute Pedersen hash from data - * - * @param data Array of data to compute Pedersen hash on - * @param num_elem len of data - * @param hash Pedersen hash of elements - */ -void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], - uint8_t num_elem, - uint8_t *hash); - #endif // STARKNET_HELPERS_H \ No newline at end of file diff --git a/apps/starknet_app/starknet_pedersen.c b/apps/starknet_app/starknet_pedersen.c index 84e31ef5..48cccc0b 100644 --- a/apps/starknet_app/starknet_pedersen.c +++ b/apps/starknet_app/starknet_pedersen.c @@ -1,7 +1,7 @@ /** * @file starknet_pedersen.c * @author Cypherock X1 Team - * @brief Utilities specific to Starknet chains + * @brief Utilities specific to Starknet pedersen hashing * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ @@ -67,9 +67,6 @@ #include "coin_utils.h" #include "mini-gmp-helpers.h" -#include "starknet_api.h" -#include "starknet_context.h" -#include "starknet_crypto.h" #include "starknet_helpers.h" /***************************************************************************** @@ -87,10 +84,6 @@ /***************************************************************************** * STATIC FUNCTION PROTOTYPES *****************************************************************************/ -static void process_single_element(mpz_t element, - stark_point *p1, - stark_point *p2, - stark_point *result); /***************************************************************************** * STATIC VARIABLES @@ -103,95 +96,6 @@ static void process_single_element(mpz_t element, /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ -bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { - ASSERT(NULL != x); - ASSERT(NULL != y); - ASSERT(0 < size); - - // Convert to bn - mpz_t a, b, result; - mpz_init(a); - mpz_init(b); - mpz_init(result); - - mpz_import(a, size, 1, 1, 1, 0, x); // Convert x to mpz_t a - mpz_import(b, size, 1, 1, 1, 0, y); // Convert y to mpz_t b - - // Get shift point - stark_point HASH_SHIFT_POINT, P_1, P_2, P_3, P_4; - stark_point_copy(&starkPts->P[0], &HASH_SHIFT_POINT); - stark_point_copy(&starkPts->P[1], &P_1); - stark_point_copy(&starkPts->P[2], &P_2); - stark_point_copy(&starkPts->P[3], &P_3); - stark_point_copy(&starkPts->P[4], &P_4); - - // Compute the hash using the Starkware Pedersen hash definition - stark_point x_part, y_part, hash_point; - stark_point_init(&x_part); - stark_point_init(&y_part); - stark_point_init(&hash_point); - - process_single_element(a, &P_1, &P_2, &x_part); - process_single_element(b, &P_3, &P_4, &y_part); - - stark_point_add(starkCurve, &HASH_SHIFT_POINT, &x_part); - stark_point_add(starkCurve, &x_part, &y_part); - stark_point_copy(&y_part, &hash_point); - - memzero(hash, 32); - mpz_to_byte_array(hash_point.x, hash, 32); - - // clear stark points - stark_point_clear(&x_part); - stark_point_clear(&y_part); - stark_point_clear(&hash_point); - - mpz_clear(a); - mpz_clear(b); - mpz_clear(result); - - return true; -} - -void process_single_element(mpz_t element, - stark_point *p1, - stark_point *p2, - stark_point *result) { - ASSERT(mpz_cmp(element, starkCurve->prime) < 0); - - mpz_t low_part, high_nibble; - mpz_init(low_part); - mpz_init(high_nibble); - - // Extract the low 248 bits and high bits from the element - mpz_t mask; - mpz_init(mask); - // Set mask to (1 << 248) - 1 - mpz_ui_pow_ui(mask, 2, 248); // mask = 2^248 - mpz_sub_ui(mask, mask, 1); // mask = 2^248 - 1 - // Extract the low 248 bits and high bits from the element - mpz_and(low_part, element, mask); - mpz_fdiv_q_2exp(high_nibble, element, LOW_PART_BITS); - - stark_point res1, res2; - stark_point_init(&res1); - stark_point_init(&res2); - - stark_point_multiply(starkCurve, low_part, p1, &res1); // low_part * p1 - stark_point_multiply( - starkCurve, high_nibble, p2, &res2); // high_nibble * p2 - stark_point_add(starkCurve, &res1, &res2); - - stark_point_copy(&res2, result); - - // clear mpz vars - mpz_clear(low_part); - mpz_clear(high_nibble); - mpz_clear(mask); - - stark_point_clear(&res1); - stark_point_clear(&res2); -} /***************************************************************************** * GLOBAL FUNCTIONS @@ -225,5 +129,6 @@ void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], pederson_hash(result, num_elem_bn, STARKNET_BIGNUM_SIZE, result); memcpy(hash, result, STARKNET_BIGNUM_SIZE); + return; } diff --git a/apps/starknet_app/starknet_pedersen.h b/apps/starknet_app/starknet_pedersen.h index c41ade73..07c88ed0 100644 --- a/apps/starknet_app/starknet_pedersen.h +++ b/apps/starknet_app/starknet_pedersen.h @@ -1,7 +1,7 @@ /** * @file starknet_pedersen.h * @author Cypherock X1 Team - * @brief Utilities specific to Starknet chains + * @brief Utilities specific to Starknet pedersen hashing * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ @@ -63,12 +63,7 @@ #include #include -#include "coin_utils.h" -#include "mini-gmp-helpers.h" -#include "starknet_api.h" -#include "starknet_context.h" -#include "starknet_crypto.h" -#include "starknet_helpers.h" +#include "mpz_pedersen.h" /***************************************************************************** * EXTERN VARIABLES @@ -100,5 +95,21 @@ *****************************************************************************/ /***************************************************************************** - * GLOBAL FUNCTIONS + * GLOBAL FUNCTIONS PROTOTYPES *****************************************************************************/ +/** + * @brief Converts unsigned long int to byte array of size STARKNET_BIGNUM_SIZE + */ +void starknet_uli_to_bn_byte_array(const unsigned long int ui, + uint8_t *bn_array); + +/** + * Computes Pedersen hash from data of size STARKNET_BIGNUM_SIZE + * + * @param data 2D Array of data to compute Pedersen hash on + * @param num_elem len of data + * @param hash Pedersen hash of elements + */ +void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], + uint8_t num_elem, + uint8_t *hash); diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c index 86fa38f1..957367d9 100644 --- a/apps/starknet_app/starknet_pub_key.c +++ b/apps/starknet_app/starknet_pub_key.c @@ -454,8 +454,6 @@ static void starknet_derive_argent_address(const uint8_t *pub_key, char *addr) { * GLOBAL FUNCTIONS *****************************************************************************/ void starknet_get_pub_keys(starknet_query_t *query) { - starknet_init(); - char wallet_name[NAME_SIZE] = ""; uint8_t seed[64] = {0}; @@ -500,6 +498,8 @@ void starknet_get_pub_keys(starknet_query_t *query) { set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_SEED_GENERATED); delay_scr_init(ui_text_processing, DELAY_SHORT); + // initialize starknet context + starknet_init(); bool status = fill_starknet_public_keys(init_req->derivation_paths, seed, public_keys, @@ -526,8 +526,8 @@ void starknet_get_pub_keys(starknet_query_t *query) { set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_VERIFY); } + stark_clear(); - stark_pedersen_clear(); if (!send_public_keys(query, public_keys, init_req->derivation_paths_count, diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c new file mode 100644 index 00000000..6f96c371 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -0,0 +1,288 @@ +/* + * @author Cypherock X1 Team + * @brief ec operations using mpz nums + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "mpz_ecdsa.h" + +#include "mini-gmp.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +void mpz_curve_point_init(mpz_curve_point *p) { + mpz_init(p->x); + mpz_init(p->y); +} + +void mpz_curve_point_clear(mpz_curve_point *p) { + mpz_clear(p->x); + mpz_clear(p->y); +} + +// Set cp2 = cp1 +void mpz_curve_point_copy(const mpz_curve_point *cp1, mpz_curve_point *cp2) { + mpz_curve_point_init(cp2); + + mpz_set(cp2->x, cp1->x); + mpz_set(cp2->y, cp1->y); +} + +void mpz_curve_point_add(const mpz_curve *curve, + const mpz_curve_point *cp1, + mpz_curve_point *cp2) { + mpz_t lambda, inv, xr, yr; + + mpz_init(lambda); + mpz_init(inv); + mpz_init(xr); + mpz_init(yr); + + if (mpz_curve_point_is_infinity(cp1)) { + return; + } + if (mpz_curve_point_is_infinity(cp2)) { + mpz_curve_point_copy(cp1, cp2); + return; + } + if (mpz_curve_point_is_equal(cp1, cp2)) { + mpz_curve_point_double(curve, cp2); + return; + } + if (mpz_curve_point_is_negative_of(cp1, cp2)) { + mpz_curve_point_set_infinity(cp2); + return; + } + + // inv = (cp2->x - cp1->x) mod prime + mpz_sub(inv, cp2->x, cp1->x); + mpz_mod(inv, inv, curve->prime); + + // inv = inv^-1 mod prime + mpz_invert(inv, inv, curve->prime); + + // lambda = (cp2->y - cp1->y) mod prime + mpz_sub(lambda, cp2->y, cp1->y); + mpz_mod(lambda, lambda, curve->prime); + + // lambda = lambda * inv mod prime + mpz_mul(lambda, lambda, inv); + mpz_mod(lambda, lambda, curve->prime); + + // xr = lambda^2 - cp1->x - cp2->x mod prime + mpz_mul(xr, lambda, lambda); + mpz_sub(xr, xr, cp1->x); + mpz_sub(xr, xr, cp2->x); + mpz_mod(xr, xr, curve->prime); + + // yr = lambda * (cp1->x - xr) - cp1->y mod prime + mpz_sub(yr, cp1->x, xr); + mpz_mul(yr, yr, lambda); + mpz_sub(yr, yr, cp1->y); + mpz_mod(yr, yr, curve->prime); + + // Set cp2 to the result + mpz_set(cp2->x, xr); + mpz_set(cp2->y, yr); + + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); +} + +void mpz_curve_point_double(const mpz_curve *curve, mpz_curve_point *cp) { + // Ref: + // https://github.com/starkware-libs/starkex-for-spot-trading/blob/607f0b4ce507e1d95cd018d206a2797f6ba4aab4/src/starkware/crypto/starkware/crypto/signature/math_utils.py + if (mpz_cmp_ui(cp->y, 0) == 0) { + return; + } + + mpz_t lambda, xr, yr, inv; + mpz_init(lambda); + mpz_init(xr); + mpz_init(yr); + mpz_init(inv); + + // lambda = (3 * cp->x^2 + curve->a) * (2 * cp->y)^-1 mod prime + mpz_mul(lambda, cp->x, cp->x); + mpz_mul_ui(lambda, lambda, 3); + mpz_add(lambda, lambda, curve->a); + mpz_mul_ui(inv, cp->y, 2); // using inv to store 2 * y + mpz_invert(inv, inv, curve->prime); + mpz_mul(lambda, lambda, inv); + mpz_mod(lambda, lambda, curve->prime); + + // xr = lambda^2 - 2 * cp->x mod prime + mpz_mul(xr, lambda, lambda); + mpz_submul_ui(xr, cp->x, 2); + mpz_mod(xr, xr, curve->prime); + + // yr = lambda * (cp->x - xr) - cp->y mod prime + mpz_sub(yr, cp->x, xr); + mpz_mul(yr, yr, lambda); + mpz_sub(yr, yr, cp->y); + mpz_mod(yr, yr, curve->prime); + + mpz_set(cp->x, xr); + mpz_set(cp->y, yr); + + mpz_clear(lambda); + mpz_clear(xr); + mpz_clear(yr); + mpz_clear(inv); +} + +// set point to internal representation of point at infinity +void mpz_curve_point_set_infinity(mpz_curve_point *p) { + mpz_set_ui(p->x, 0); + mpz_set_ui(p->y, 0); +} + +// return true iff p represent point at infinity +// both coords are zero in internal representation +int mpz_curve_point_is_infinity(const mpz_curve_point *p) { + return mpz_cmp_ui(p->x, 0) == 0 && mpz_cmp_ui(p->y, 0) == 0; +} + +// return true iff both points are equal +int mpz_curve_point_is_equal(const mpz_curve_point *p, + const mpz_curve_point *q) { + return (mpz_cmp(p->x, q->x) == 0) && (mpz_cmp(p->y, q->y) == 0); +} + +// returns true iff p == -q +// expects p and q be valid points on curve other than point at infinity +int mpz_curve_point_is_negative_of(const mpz_curve_point *p, + const mpz_curve_point *q) { + // if P == (x, y), then -P would be (x, -y) on this curve + if (mpz_cmp(p->x, q->x) != 0) { + return 0; + } + + // we shouldn't hit this for a valid point + if (mpz_cmp_ui(p->y, 0) == 0) { + return 0; + } + + return mpz_cmp(p->y, q->y) != 0; +} + +void mpz_curve_point_multiply(const mpz_curve *curve, + const mpz_t k, + const mpz_curve_point *p, + mpz_curve_point *res) { + // Ref: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication + + mpz_curve_point temp; + mpz_curve_point R; + mpz_curve_point_init(&temp); + mpz_curve_point_init(&R); + mpz_curve_point_set_infinity(&R); // Initialize R to the point at infinity + mpz_curve_point_copy(p, &temp); // Copy the input point p to temp + + // Iterate over each bit of k from the least significant to the most + // significant + for (int i = 0; i < 256; i++) { + // If the i-th bit of k is set, add temp to the result R + if (mpz_tstbit(k, i)) { + mpz_curve_point_add(curve, &temp, &R); + } + + // Double the current point temp + mpz_curve_point_double(curve, &temp); + } + + // Copy the result R to the output parameter res + mpz_curve_point_copy(&R, res); + + mpz_curve_point_clear(&temp); + mpz_curve_point_clear(&R); +} + +int bn_bit_length(const mpz_t k) { + if (mpz_cmp_ui(k, 0) == 0) { + return 0; + } + return mpz_sizeinbase(k, 2); +} + +int bn_is_bit_set(const mpz_t k, int bit_idx) { + return mpz_tstbit(k, bit_idx); +} diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.h b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h new file mode 100644 index 00000000..9b97f4c5 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h @@ -0,0 +1,150 @@ +/** + * @author Cypherock X1 Team + * @brief ec operations using mpz nums + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef MPZ_ECDSA_H +#define MPZ_ECDSA_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "mini-gmp.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +// curve point x and y +typedef struct { + mpz_t x, y; +} mpz_curve_point; + +typedef struct { + mpz_t prime; // prime order of the finite field + mpz_curve_point G; // initial curve point + mpz_t order; // order of G + mpz_t order_half; // order of G divided by 2 + mpz_t a; // coefficient 'a' of the elliptic curve + mpz_t b; // coefficient 'b' of the elliptic curve +} mpz_curve; + +typedef struct { + mpz_curve_point P[5]; +} mpz_pedersen; + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Initialize a mpz curve point. + * @details Allocates and initializes resources for a mpz curve point. + * + * @param p Pointer to the mpz curve point point to initialize. + */ +void mpz_curve_point_init(mpz_curve_point *p); + +/** + * @brief Clear a mpz curve point. + * @details Releases resources associated with the mpz curve point. + * + * @param p Pointer to the mpz curve point to clear. + */ +void mpz_curve_point_clear(mpz_curve_point *p); + +/** + * @brief Copy a mpz curve point. + * @details Copies one curve point to another. + * + * @param cp1 Source curve point. + * @param cp2 Destination curve point. + */ +void mpz_curve_point_copy(const mpz_curve_point *cp1, mpz_curve_point *cp2); + +/** + * @brief Add two curve points. + * @details Computes the sum of two curve points on the given curve. + * + * @param curve Curve defining the group operation. + * @param cp1 First curve point. + * @param cp2 Second curve point; result is stored here. + */ +void mpz_curve_point_add(const mpz_curve *curve, + const mpz_curve_point *cp1, + mpz_curve_point *cp2); + +/** + * @brief Double a curve point. + * @details Computes the point doubling operation on the given curve. + * + * @param curve Curve defining the group operation. + * @param cp curve point to double; result is stored here. + */ +void mpz_curve_point_double(const mpz_curve *curve, mpz_curve_point *cp); + +/** + * @brief Multiply a curve point. + * @details Computes scalar multiplication of a curve point on the given curve. + * + * @param curve Curve defining the group operation. + * @param k Scalar multiplier. + * @param p curve point to multiply. + * @param res Resulting curve point is stored here. + */ +void mpz_curve_point_multiply(const mpz_curve *curve, + const mpz_t k, + const mpz_curve_point *p, + mpz_curve_point *res); + +/** + * @brief Set a curve point to infinity. + * @details Configures a curve point to represent the point at infinity. + * + * @param p curve point to modify. + */ +void mpz_curve_point_set_infinity(mpz_curve_point *p); + +/** + * @brief Check if a curve point is at infinity. + * @details Determines if the given curve point represents the point at + * infinity. + * + * @param p curve point to check. + * @return Non-zero if the point is at infinity, 0 otherwise. + */ +int mpz_curve_point_is_infinity(const mpz_curve_point *p); + +/** + * @brief Check if two curve points are equal. + * @details Compares two curve points for equality. + * + * @param p First curve point. + * @param q Second curve point. + * @return Non-zero if points are equal, 0 otherwise. + */ +int mpz_curve_point_is_equal(const mpz_curve_point *p, + const mpz_curve_point *q); + +/** + * @brief Check if one curve point is the negative of another. + * @details Determines if the given points are negatives of each other. + * + * @param p First curve point. + * @param q Second curve point. + * @return Non-zero if points are negatives, 0 otherwise. + */ +int mpz_curve_point_is_negative_of(const mpz_curve_point *p, + const mpz_curve_point *q); + +#endif // MPZ_ECDSA_H \ No newline at end of file diff --git a/common/libraries/crypto/mpz_operations/mpz_pedersen.c b/common/libraries/crypto/mpz_operations/mpz_pedersen.c new file mode 100644 index 00000000..c5f2e0b5 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_pedersen.c @@ -0,0 +1,191 @@ + +/* + * @author Cypherock X1 Team + * @brief pedersen hashing alogrithms + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + +#include "coin_utils.h" +#include "mini-gmp-helpers.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_crypto.h" +#include "starknet_helpers.h" +#include "starknet_pedersen.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void process_single_element(mpz_t element, + mpz_curve_point *p1, + mpz_curve_point *p2, + mpz_curve_point *result) { + ASSERT(mpz_cmp(element, stark_curve->prime) < 0); + + mpz_t low_part, high_nibble; + mpz_init(low_part); + mpz_init(high_nibble); + + // Extract the low 248 bits and high bits from the element + mpz_t mask; + mpz_init(mask); + // Set mask to (1 << 248) - 1 + mpz_ui_pow_ui(mask, 2, 248); // mask = 2^248 + mpz_sub_ui(mask, mask, 1); // mask = 2^248 - 1 + // Extract the low 248 bits and high bits from the element + mpz_and(low_part, element, mask); + mpz_fdiv_q_2exp(high_nibble, element, LOW_PART_BITS); + + mpz_curve_point res1, res2; + mpz_curve_point_init(&res1); + mpz_curve_point_init(&res2); + + mpz_curve_point_multiply( + stark_curve, low_part, p1, &res1); // low_part * p1 + mpz_curve_point_multiply( + stark_curve, high_nibble, p2, &res2); // high_nibble * p2 + mpz_curve_point_add(stark_curve, &res1, &res2); + + mpz_curve_point_copy(&res2, result); + + // clear mpz vars + mpz_clear(low_part); + mpz_clear(high_nibble); + mpz_clear(mask); + + mpz_curve_point_clear(&res1); + mpz_curve_point_clear(&res2); +} + +bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { + ASSERT(NULL != x); + ASSERT(NULL != y); + ASSERT(0 < size); + + // Convert to bn + mpz_t a, b, result; + mpz_init(a); + mpz_init(b); + mpz_init(result); + + mpz_import(a, size, 1, 1, 1, 0, x); // Convert x to mpz_t a + mpz_import(b, size, 1, 1, 1, 0, y); // Convert y to mpz_t b + + // Get shift point + mpz_curve_point HASH_SHIFT_POINT, P_1, P_2, P_3, P_4; + mpz_curve_point_copy(&starknet_pedersen_points->P[0], &HASH_SHIFT_POINT); + mpz_curve_point_copy(&starknet_pedersen_points->P[1], &P_1); + mpz_curve_point_copy(&starknet_pedersen_points->P[2], &P_2); + mpz_curve_point_copy(&starknet_pedersen_points->P[3], &P_3); + mpz_curve_point_copy(&starknet_pedersen_points->P[4], &P_4); + + // Compute the hash using the Starkware Pedersen hash definition + mpz_curve_point x_part, y_part, hash_point; + mpz_curve_point_init(&x_part); + mpz_curve_point_init(&y_part); + mpz_curve_point_init(&hash_point); + + process_single_element(a, &P_1, &P_2, &x_part); + process_single_element(b, &P_3, &P_4, &y_part); + + mpz_curve_point_add(stark_curve, &HASH_SHIFT_POINT, &x_part); + mpz_curve_point_add(stark_curve, &x_part, &y_part); + mpz_curve_point_copy(&y_part, &hash_point); + + memzero(hash, 32); + mpz_to_byte_array(hash_point.x, hash, 32); + + // clear curve points + mpz_curve_point_clear(&x_part); + mpz_curve_point_clear(&y_part); + mpz_curve_point_clear(&hash_point); + + mpz_clear(a); + mpz_clear(b); + mpz_clear(result); + + return true; +} diff --git a/common/libraries/crypto/mpz_operations/mpz_pedersen.h b/common/libraries/crypto/mpz_operations/mpz_pedersen.h new file mode 100644 index 00000000..326f3d00 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_pedersen.h @@ -0,0 +1,119 @@ +/* + * @author Cypherock X1 Team + * @brief pedersen hashing alogrithms + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + * + ****************************************************************************** + */ +#ifndef MPZ_PEDERSEN_H +#define MPZ_PEDERSEN_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include +#include + +#include "mpz_ecdsa.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +/** + * @brief processes' single element required by @ref pederson_hash + * @details result = element_{low}⋅P1 + element_{high}⋅P2 + */ +void process_single_element(mpz_t element, + mpz_curve_point *p1, + mpz_curve_point *p2, + mpz_curve_point *result); + +/** + * @brief Computes the Pederson hash of input. + * defined as: + H(a,b)= [P0 + x_{low}⋅P1 + x_{high}⋅P2 + y_{low}⋅P3 + y_{high}⋅P4]x + xlow is the 248 low bits of x. + xhigh is the 4 high bits of x(and similarly for y). + P0,P1,P2,P3,P4 are constant points on the elliptic curve, derived from the + decimal digits of π. + ref: + https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/ + */ +bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash); + +#endif // MPZ_PEDERSEN_H diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index 38c89ac4..f8363453 100644 --- a/utilities/cmake/firmware/firmware.cmake +++ b/utilities/cmake/firmware/firmware.cmake @@ -103,6 +103,7 @@ target_include_directories(${EXECUTABLE} PRIVATE common/libraries/atecc/host common/libraries/atecc/jwt common/libraries/crypto + common/libraries/crypto/mpz_operations common/libraries/crypto/aes common/libraries/crypto/chacha20poly1305 common/libraries/crypto/ed25519-donna diff --git a/utilities/cmake/simulator/simulator.cmake b/utilities/cmake/simulator/simulator.cmake index 1e6e7234..b0780449 100644 --- a/utilities/cmake/simulator/simulator.cmake +++ b/utilities/cmake/simulator/simulator.cmake @@ -99,6 +99,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE common/libraries/atecc/host common/libraries/atecc/jwt common/libraries/crypto + common/libraries/crypto/mpz_operations common/libraries/crypto/aes common/libraries/crypto/chacha20poly1305 common/libraries/crypto/ed25519-donna From 83fc2940453255abee4a391a8d7ecb8234020c9d Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Thu, 12 Dec 2024 17:36:49 +0530 Subject: [PATCH 07/26] feat(app): Add sign txn --- .gitmodules | 3 + apps/starknet_app/starknet_main.c | 2 +- apps/starknet_app/starknet_poseidon.c | 552 ++++++++++++++++ apps/starknet_app/starknet_poseidon.h | 129 ++++ apps/starknet_app/starknet_priv.h | 2 +- apps/starknet_app/starknet_sign_txn.c | 600 ++++++++++++++++++ .../proto-options/starknet/sign_txn.options | 33 + vendor/poseidon | 1 + 8 files changed, 1320 insertions(+), 2 deletions(-) create mode 100644 apps/starknet_app/starknet_poseidon.c create mode 100644 apps/starknet_app/starknet_poseidon.h create mode 100644 apps/starknet_app/starknet_sign_txn.c create mode 100644 common/proto-options/starknet/sign_txn.options create mode 160000 vendor/poseidon diff --git a/.gitmodules b/.gitmodules index 222ea7ab..e1e19769 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "mini-gmp"] path = vendor/mini-gmp url = ../mini-gmp +[submodule "vendor/poseidon"] + path = vendor/poseidon + url = https://github.com/Cypherock/poseidon diff --git a/apps/starknet_app/starknet_main.c b/apps/starknet_app/starknet_main.c index 70c7039f..a0283bd9 100644 --- a/apps/starknet_app/starknet_main.c +++ b/apps/starknet_app/starknet_main.c @@ -129,7 +129,7 @@ void starknet_main(usb_event_t usb_evt, const void *app_config) { break; } case STARKNET_QUERY_SIGN_TXN_TAG: { - // starknet_sign_transaction(&query); + starknet_sign_transaction(&query); break; } diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c new file mode 100644 index 00000000..950f86a2 --- /dev/null +++ b/apps/starknet_app/starknet_poseidon.c @@ -0,0 +1,552 @@ +/** + * @file starknet_poseidon.c + * @author Cypherock X1 Team + * @brief Utilities specific to Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "starknet_poseidon.h" + +#include +#include +#include +#include + +#include "coin_utils.h" +#include "f251.h" +#include "mini-gmp-helpers.h" +#include "mini-gmp.h" +#include "poseidon.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief Converts BE hex array to felt + * + */ +static void hex_to_felt_t(const uint8_t hex[], + const uint8_t hex_size, + felt_t felt); + +/** + * @brief Converts mpz num to felt + * + */ +static void mpz_to_felt(felt_t felt, const mpz_t mpz); + +/** + * @brief Clears input felt state to 0 + * + */ +static void clear_state(felt_t *state, int size); + +/** + * @brief Computes Poseidon Hash on array of elements + * + * @param state array of felts to hash on + * @param state_size size of array + * @param res resultant hash + * + */ +static void poseidon_hash_many(const felt_t state[], + const uint8_t state_size, + felt_t res); + +/** + * @brief Encode the L1 gas limits of a V3 transaction + * + * @param bounds object including the limits for L1 + * @param out encoded data + */ +static void encode_resource_bounds_l1(const starknet_resource_bounds_t bounds, + felt_t out); + +/** + * @brief Encode the L2 gas limits of a V3 transaction + * + * @param bounds object including the limits for L2 + * @param out encoded data + */ +static void encode_resource_bounds_l2(const starknet_resource_bounds_t bounds, + felt_t out); + +/** + * @brief Calculates posiedon_many([tip, L1bound, L2Bound]) required for + * @ref calculate_transaction_hash_common + * + * @param bounds object including the limits for L1&L2 + * @param result result hash + */ +static void hash_fee_field(const pb_byte_t tip, + const starknet_resource_bounds_t bounds, + felt_t result); + +/** + * @brief Calculates hash of Data Availability Mode required for + * @ref calculate_transaction_hash_common + * + * @param res result hash + */ +static void hash_DAMode(const pb_byte_t nonce_DAMode, + const pb_byte_t fee_DAMode, + felt_t res); + +/** + * @brief Common hash calculation for starknet txns + * + * @param hash result hash + */ +static void calculate_transaction_hash_common( + const felt_t transaction_hash_prefix, + const pb_byte_t tip[], + const starknet_resource_bounds_t resource_bound, + const pb_byte_t nonce_data_availability_mode[], + const pb_byte_t fee_data_availability_mode[], + const pb_byte_t version[], + const pb_byte_t sender_address[], + const pb_byte_t chain_id[], + const pb_byte_t nonce[], + const felt_t additional_data[], + const uint8_t additional_data_size, + felt_t hash); + +/** + * @brief Prepares additional_data parameter required by + * @ref calculate_transaction_hash_common for DEPLOY txns + * + * @param hash result hash + */ +static void calculate_deploy_transaction_hash( + const starknet_sign_txn_deploy_account_txn_t *txn, + felt_t hash); + +/** + * @brief Prepares additional_data parameter required by + * @ref calculate_transaction_hash_common for INVOKE txns + * + * @param hash result hash + */ +static void calculate_invoke_transaction_hash( + const starknet_sign_txn_invoke_txn_t *txn, + felt_t hash); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static void hex_to_felt_t(const uint8_t hex[], + const uint8_t hex_size, + felt_t felt) { + uint8_t buf[32] = {0}; + memcpy(buf + (32 - hex_size), hex, hex_size); + int offset = 0; + for (int i = 0; i < 4; i++) { + felt[3 - i] = U64_READ_BE_ARRAY(buf + offset); + offset += 8; + } +} + +static void mpz_to_felt(felt_t felt, const mpz_t mpz) { + uint8_t buf[32] = {0}; + mpz_to_byte_array(mpz, buf, 32); + hex_to_felt_t(buf, 32, felt); +} + +static void clear_state(felt_t *state, int size) { + int i; + + for (i = 0; i < size; i++) { + state[i][0] = 0; + state[i][1] = 0; + state[i][2] = 0; + state[i][3] = 0; + } +} + +static void poseidon_hash_many(const felt_t state[], + const uint8_t state_size, + felt_t res) { + ASSERT(state_size + 2 < 16); + + const uint8_t m = 3, rate = 2; + felt_t padded[16]; ///< TODO: Update with macro + clear_state(padded, 16); + + if (state != NULL) { + for (int i = 0; i < state_size; i++) { + f251_copy(padded[i], state[i]); + } + } + uint8_t padded_offset = state_size; + + // padd one to mark end of ip + felt_t felt_one = {1, 0, 0, 0}; + f251_copy(padded[padded_offset++], felt_one); + // padd with zeros to make multiple of rate + felt_t felt_zero = {0, 0, 0, 0}; + while (padded_offset % rate != 0) { + f251_copy(padded[padded_offset++], felt_zero); + } + + felt_t result[m]; + clear_state(result, m); + + for (int i = 0; i < padded_offset; i += rate) { + for (int j = 0; j < rate; j++) { + f251_add(result[j], result[j], padded[i + j]); + } + permutation_3(result); + } + // copt result + f251_copy(res, result[0]); +} + +static void encode_resource_bounds_l1(const starknet_resource_bounds_t bounds, + felt_t out) { + mpz_t temp1, temp2, result; + mpz_init(result); + mpz_init(temp1); + mpz_init(temp2); + + // Perform the encoding: + // (L1_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.level_1.max_amount << + // MAX_PRICE_PER_UNIT_BITS) + bounds.level_1.max_price_per_unit + + // L1_GAS_NAME << RESOURCE_VALUE_OFFSET + mpz_set_ui(result, L1_GAS_NAME); + mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); + + // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS + mpz_import(temp1, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_1.max_amount); + mpz_mul_2exp(temp1, temp1, MAX_PRICE_PER_UNIT_BITS); + + // result += temp1 + mpz_add(result, result, temp1); + + mpz_import( + temp2, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_1.max_price_per_unit); + mpz_add(result, result, temp2); // result += temp2 + + mpz_to_felt(out, result); + mpz_clear(temp1); + mpz_clear(temp2); +} + +static void encode_resource_bounds_l2(const starknet_resource_bounds_t bounds, + felt_t out) { + mpz_t temp1, temp2, result; + + mpz_init(result); + mpz_init(temp1); + mpz_init(temp2); + + // Perform the encoding: + // (L2_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.level_1.max_amount << + // MAX_PRICE_PER_UNIT_BITS) + bounds.level_2.max_price_per_unit + + // L1_GAS_NAME << RESOURCE_VALUE_OFFSET + mpz_set_ui(result, L2_GAS_NAME); + mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); + + // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS + mpz_import(temp1, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_2.max_amount); + mpz_mul_2exp(temp1, temp1, MAX_PRICE_PER_UNIT_BITS); + + // result += temp1 + mpz_add(result, result, temp1); + + mpz_import( + temp2, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_2.max_price_per_unit); + mpz_add(result, result, temp2); // result += temp2 + + mpz_to_felt(out, result); + + mpz_clear(temp1); + mpz_clear(temp2); +} + +static void hash_fee_field(const pb_byte_t tip, + const starknet_resource_bounds_t bounds, + felt_t result) { + felt_t res_l1, res_l2; + encode_resource_bounds_l1(bounds, res_l1); + encode_resource_bounds_l2(bounds, res_l2); + felt_t tip_felt = {tip, 0, 0, 0}; + + felt_t state[3]; + clear_state(state, 3); + f251_copy(state[0], tip_felt); + f251_copy(state[1], res_l1); + f251_copy(state[2], res_l2); + + poseidon_hash_many(state, 3, result); +} + +static void hash_DAMode(const pb_byte_t nonce_DAMode, + const pb_byte_t fee_DAMode, + felt_t res) { + mpz_t temp_nonce, temp_fee, result; + + mpz_init(result); + mpz_init(temp_nonce); + mpz_init(temp_fee); + + // Set the mpz_t variables from the pb_byte_t inputs + mpz_set_ui(temp_nonce, nonce_DAMode); + mpz_set_ui(temp_fee, fee_DAMode); + + // result = (temp_nonce << DATA_AVAILABILITY_MODE_BITS) + temp_fee + mpz_mul_2exp(result, temp_nonce, DATA_AVAILABILITY_MODE_BITS); + mpz_add(result, result, temp_fee); + + mpz_to_felt(res, result); + + mpz_clear(temp_nonce); + mpz_clear(temp_fee); + mpz_clear(result); +} + +static void calculate_transaction_hash_common( + const felt_t transaction_hash_prefix, + const pb_byte_t tip[], + const starknet_resource_bounds_t resource_bound, + const pb_byte_t nonce_data_availability_mode[], + const pb_byte_t fee_data_availability_mode[], + const pb_byte_t version[], + const pb_byte_t sender_address[], + const pb_byte_t chain_id[], + const pb_byte_t nonce[], + const felt_t additional_data[], + const uint8_t additional_data_size, + felt_t hash) { + felt_t fee_field_hash = {0}, DAMode_hash = {0}; + hash_fee_field(tip[0], resource_bound, fee_field_hash); + hash_DAMode(nonce_data_availability_mode[0], + fee_data_availability_mode[0], + DAMode_hash); + + // prepare data to hash array + const uint8_t state_max = 15; + felt_t state[state_max]; + uint8_t offset = 0; + clear_state(state, state_max); + + f251_copy(state[offset++], transaction_hash_prefix); + hex_to_felt_t(version, 1, state[offset++]); + hex_to_felt_t(sender_address, 32, state[offset++]); + f251_copy(state[offset++], fee_field_hash); + felt_t paymaster_data_res = {0}; + poseidon_hash_many( + NULL, + 0, + paymaster_data_res); ///< paymaster_data parameter for future use + ///< refer: + ///< https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#v3_transaction_fields + f251_copy(state[offset++], paymaster_data_res); + hex_to_felt_t(chain_id, 1, state[offset++]); + hex_to_felt_t(nonce, 10, state[offset++]); + f251_copy(state[offset++], DAMode_hash); + if (additional_data != NULL) { + for (uint8_t i = 0; i < additional_data_size; i++) { + f251_copy(state[offset++], additional_data[i]); + ASSERT(offset < state_max); + } + } + + poseidon_hash_many(state, offset, hash); +} + +static void calculate_deploy_transaction_hash( + const starknet_sign_txn_deploy_account_txn_t *txn, + felt_t hash) { + uint8_t hex[14] = DEPLOY_ACCOUNT_PREFIX; + felt_t transaction_hash_prefix = {0}; + hex_to_felt_t(hex, 12, transaction_hash_prefix); + + // prepare additional data array + const uint8_t data_max_count = 3; + felt_t additional_data[data_max_count]; + clear_state(additional_data, data_max_count); + + // copy call data + const uint8_t call_data_max_count = 10; + felt_t call_data_felt[call_data_max_count]; + clear_state(call_data_felt, call_data_max_count); + + uint8_t count = txn->constructor_call_data.value_count; + uint8_t offset; + for (offset = 0; offset < count; offset++) { + hex_to_felt_t(txn->constructor_call_data.value[offset].bytes, + txn->constructor_call_data.value[offset].size, + call_data_felt[offset]); + } + poseidon_hash_many(call_data_felt, offset, additional_data[0]); + + hex_to_felt_t(txn->class_hash, 32, additional_data[1]); + hex_to_felt_t(txn->salt, 32, additional_data[2]); + + calculate_transaction_hash_common(transaction_hash_prefix, + txn->tip, + txn->resource_bounds, + txn->nonce_data_availability_mode, + txn->fee_data_availability_mode, + txn->version, + txn->contract_address, + txn->chain_id, + txn->nonce, + additional_data, + 3, + hash); +} + +static void calculate_invoke_transaction_hash( + const starknet_sign_txn_invoke_txn_t *txn, + felt_t hash) { + uint8_t hex[6] = INVOKE_TXN_PREFIX; + felt_t transaction_hash_prefix = {0}; + hex_to_felt_t(hex, 6, transaction_hash_prefix); + + // prepare additional data array + const uint8_t data_max_count = 2; + felt_t additional_data[data_max_count]; + clear_state(additional_data, data_max_count); + + // Note: currently account_deployment_data is unused + felt_t account_deployment_data = {0}; + poseidon_hash_many(NULL, 0, account_deployment_data); + f251_copy(additional_data[0], account_deployment_data); + + // copy call data + const uint8_t call_data_max_count = 10; + felt_t call_data_felt[call_data_max_count]; + clear_state(call_data_felt, call_data_max_count); + + uint8_t count = txn->calldata.value_count; + uint8_t offset; + for (offset = 0; offset < count; offset++) { + hex_to_felt_t(txn->calldata.value[offset].bytes, + txn->calldata.value[offset].size, + call_data_felt[offset]); + } + poseidon_hash_many(call_data_felt, offset, additional_data[1]); + + calculate_transaction_hash_common(transaction_hash_prefix, + txn->tip, + txn->resource_bound, + txn->nonce_data_availability_mode, + txn->fee_data_availability_mode, + txn->version, + txn->sender_address, + txn->chain_id, + txn->nonce, + additional_data, + 2, + hash); +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void felt_t_to_hex(const felt_t felt, uint8_t hex[32]) { + int offset = 0; + for (int i = 0; i < 4; i++) { + uint64_t value = felt[3 - i]; + + for (int j = 0; j < 8; j++) { + hex[offset + j] = (uint8_t)((value >> (56 - j * 8)) & 0xFF); + } + offset += 8; + } +} + +void calculate_txn_hash(void *txn, pb_size_t type, felt_t hash) { + ASSERT(txn != NULL); + switch (type) { + case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { + calculate_invoke_transaction_hash((starknet_sign_txn_invoke_txn_t *)txn, + hash); + } break; + + case STARKNET_SIGN_TXN_UNSIGNED_TXN_DEPLOY_TXN_TAG: { + calculate_deploy_transaction_hash( + (starknet_sign_txn_deploy_account_txn_t *)txn, hash); + + } break; + } +} \ No newline at end of file diff --git a/apps/starknet_app/starknet_poseidon.h b/apps/starknet_app/starknet_poseidon.h new file mode 100644 index 00000000..b6cc0e95 --- /dev/null +++ b/apps/starknet_app/starknet_poseidon.h @@ -0,0 +1,129 @@ +/** + * @file starknet_poseidon.h + * @author Cypherock X1 Team + * @brief Utilities specific to Starknet chains + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 +#include + +#include "coin_utils.h" +#include "f251.h" +#include "mini-gmp-helpers.h" +#include "poseidon.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ +#define DATA_AVAILABILITY_MODE_BITS 32 // 32 bits for data availability mode +#define MAX_AMOUNT_BITS 64 // 64 bits for max_amount +#define MAX_PRICE_PER_UNIT_BITS 128 // 128 bits for max_price_per_unit +#define RESOURCE_VALUE_OFFSET \ + (MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS) // Combined offset +#define L1_GAS_NAME \ + (unsigned long)0x4c315f474153 // The constant value for L1_GAS_NAME +#define L2_GAS_NAME (unsigned long)0x4c325f474153 +#define INVOKE_TXN_PREFIX \ + { 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65 } // 0x696e766f6b65; 'INKVOKE' +#define DEPLOY_ACCOUNT_PREFIX \ + { \ + 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, \ + 0x6e, 0x74 \ + } // 0x6465706c6f795f6163636f756e74 'DEPLOY_ACCOUNT' + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +/** + * @brief Function to convert Little-Endian felt_t to Big-Endian hex. + * + */ +void felt_t_to_hex(const felt_t felt, uint8_t hex[32]); + +/** + * @brief Calculates txn hash for sign based on txn type + * + * @param txn pointer to input txn + * @param type type of txn (INVOKE or DEPLOY) + * @param hash Calculated hash of txn + * + */ +void calculate_txn_hash(void *txn, pb_size_t type, felt_t hash); diff --git a/apps/starknet_app/starknet_priv.h b/apps/starknet_app/starknet_priv.h index 4f6ffb87..bf1163b9 100644 --- a/apps/starknet_app/starknet_priv.h +++ b/apps/starknet_app/starknet_priv.h @@ -51,6 +51,6 @@ void starknet_get_pub_keys(starknet_query_t *query); * * @param query Reference to the decoded query struct from the host app */ -// void starknet_sign_transaction(starknet_query_t *query); +void starknet_sign_transaction(starknet_query_t *query); #endif /* STARKNET_PRIV_H */ \ No newline at end of file diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c new file mode 100644 index 00000000..f797997c --- /dev/null +++ b/apps/starknet_app/starknet_sign_txn.c @@ -0,0 +1,600 @@ +/** + * @file starknet_txn.c + * @author Cypherock X1 Team + * @brief Source file to handle transaction signing logic for Starknet + *protocol + * + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 "bignum.h" +#include "ecdsa.h" +#include "mini-gmp-helpers.h" +#include "mini-gmp.h" +#include "rand.h" +#include "reconstruct_wallet_flow.h" +#include "rfc6979.h" +#include "starknet_api.h" +#include "starknet_context.h" +#include "starknet_helpers.h" +#include "starknet_poseidon.h" +#include "starknet_priv.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Checks if the provided query contains expected request. + * @details The function performs the check on the request type and if the check + * fails, then it will send an error to the host bitcoin app and return false. + * + * @param query Reference to an instance of starknet_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 starknet_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 starknet_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 starknet_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 + * STARKNET_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 starknet_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 starknet_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 starknet_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(starknet_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(uint8_t *signature_buffer); + +/** + * @brief Sends signature of the STARKNET unsigned txn to the host + * @details The function waits for the host to send a request of type + * STARKNET_SIGN_TXN_REQUEST_SIGNATURE_TAG and sends the response + * + * @param query Reference to buffer of type starknet_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(starknet_query_t *query, const uint8_t *signature); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ +static starknet_txn_context_t *starknet_txn_context = NULL; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +void mpz_to_bn(bignum256 *bn, const mpz_t mpz) { + uint8_t out[32] = {0}; + mpz_to_byte_array(mpz, out, 32); + bn_read_be(out, bn); +} + +void bn_to_mpz(mpz_t mpz, const bignum256 *bn) { + uint8_t in[32] = {0}; + bn_write_be(bn, in); + mpz_import(mpz, 32, 1, 1, 1, 0, in); +} + +// generate K in a deterministic way, according to RFC6979 +// http://tools.ietf.org/html/rfc6979 +void generate_k_rfc6979_mpz(mpz_t k, rfc6979_state *state) { + uint8_t buf[32] = {0}; + generate_rfc6979(buf, state); + mpz_import(k, 32, 1, 1, 1, 0, buf); + memzero(buf, sizeof(buf)); +} + +// generate random K for signing/side-channel noise +static void generate_k_random(bignum256 *k, const bignum256 *prime) { + do { + int i = 0; + for (i = 0; i < 8; i++) { + k->val[i] = random32() & 0x3FFFFFFF; + } + k->val[8] = random32() & 0xFFFF; + // check that k is in range and not zero. + } while (bn_is_zero(k) || !bn_is_less(k, prime)); +} + +void generate_k_random_mpz(mpz_t k, const mpz_t prime) { + bignum256 prime_bn, k_bn = {0}; + mpz_to_bn(&prime_bn, prime); + mpz_to_bn(&k_bn, k); + generate_k_random(&k_bn, &prime_bn); + bn_to_mpz(k, &k_bn); +} + +int starknet_sign_digest(const stark_curve *curve, + const uint8_t *priv_key, + const uint8_t *digest, + uint8_t *sig) { + int i = 0; + stark_point R = {0}; + mpz_t k, z, randk; + stark_point_init(&R); + mpz_t *s = &R.y; + mpz_init(k); + mpz_init(z); + mpz_init(randk); + +#if USE_RFC6979 + rfc6979_state rng = {0}; + init_rfc6979(priv_key, digest, &rng); +#endif + mpz_import(z, 32, 1, 1, 0, 0, digest); + for (i = 0; i < 10000; i++) { +#if USE_RFC6979 + // generate K deterministically + generate_k_rfc6979_mpz(k, &rng); + + // k >> 4 + mpz_fdiv_q_2exp(k, k, 4); + + // if k is too big or too small, we don't like it + if ((mpz_cmp_ui(k, 0) == 0) || !(mpz_cmp(k, curve->order) < 0)) { + continue; + } +#else + // generate random number k + generate_k_random_mpz(k, curve->order); +#endif + // compute k*G + stark_point_multiply(curve, k, &curve->G, &R); + mpz_mod(R.x, R.x, curve->order); + // r = (rx mod n) + if (!(mpz_cmp(R.x, curve->order) < 0)) { + mpz_sub(R.x, R.x, curve->order); + } + // if r is zero, we retry + if (mpz_cmp_ui(R.x, 0) == 0) { + continue; + } + + // randomize operations to counter side-channel attacks + generate_k_random_mpz(randk, curve->order); + + // k = k * rand mod n + mpz_mul(k, k, randk); + mpz_mod(k, k, curve->order); + + // k = (k * rand)^-1 + mpz_invert(k, k, curve->order); + + mpz_import(*s, 32, 1, 1, 1, 0, priv_key); + // R.x*priv + mpz_mul(*s, *s, R.x); + mpz_mod(*s, *s, curve->order); + mpz_add(*s, *s, z); // R.x*priv + z + + // (k*rand)^-1 (R.x*priv + z) + mpz_mul(*s, *s, k); + mpz_mod(*s, *s, curve->order); + + // k^-1 (R.x*priv + z) + mpz_mul(*s, *s, randk); + mpz_mod(*s, *s, curve->order); + + // if s is zero, we retry + if ((mpz_cmp_ui(*s, 0) == 0)) { + continue; + } + + // if S > order/2 => S = -S + if ((mpz_cmp(curve->order_half, *s) < 0)) { + mpz_sub(*s, curve->order, *s); + } + // we are done, R.x and s is the result signature + mpz_to_byte_array(R.x, sig, 32); + mpz_to_byte_array(*s, sig + 32, 32); + + mpz_clear(k); + mpz_clear(randk); + mpz_clear(z); + +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return 0; + } + + // Too many retries without a valid signature + // -> fail with an error + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return -1; +} + +static bool check_which_request(const starknet_query_t *query, + pb_size_t which_request) { + if (which_request != query->sign_txn.which_request) { + starknet_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) { + starknet_result_t result = init_starknet_result(STARKNET_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = which_response; + starknet_send_result(&result); +} + +static bool validate_request_data(const starknet_sign_txn_request_t *request) { + bool status = true; + + if (!starknet_derivation_path_guard( + request->initiate.derivation_path, + request->initiate.derivation_path_count)) { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + status = false; + } + return status; +} + +static bool handle_initiate_query(const starknet_query_t *query) { + char wallet_name[NAME_SIZE] = ""; + char msg[100] = ""; + + if (!check_which_request(query, STARKNET_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, + starknet_send_error)) { + return false; + } + + snprintf(msg, + sizeof(msg), + UI_TEXT_SIGN_TXN_PROMPT, + starknet_app.name, + wallet_name); + // Take user consent to sign transaction for the wallet + if (!core_confirmation(msg, starknet_send_error)) { + return false; + } + + set_app_flow_status(STARKNET_SIGN_TXN_STATUS_CONFIRM); + memcpy(&starknet_txn_context->init_info, + &query->sign_txn.initiate, + sizeof(starknet_sign_txn_initiate_request_t)); + + send_response(STARKNET_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(starknet_query_t *query) { + if (!starknet_get_query(query, STARKNET_QUERY_SIGN_TXN_TAG) && + !check_which_request(query, STARKNET_SIGN_TXN_REQUEST_TXN_TAG)) { + return false; + } + + // Get txn type + starknet_txn_context->which_type = query->sign_txn.txn.which_type; + switch (starknet_txn_context->which_type) { + case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { + starknet_txn_context->invoke_txn = &query->sign_txn.txn.invoke_txn; + } break; + + case STARKNET_SIGN_TXN_UNSIGNED_TXN_DEPLOY_TXN_TAG: { + starknet_txn_context->deploy_txn = &query->sign_txn.txn.deploy_txn; + } break; + + default: { + // should not reach here; + return false; + } + } + + if (1) { + send_response(STARKNET_SIGN_TXN_RESPONSE_UNSIGNED_TXN_ACCEPTED_TAG); + return true; + } else { + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; + } + + return false; +} + +static bool get_invoke_txn_user_verification() { + char address[100] = "0x"; + byte_array_to_hex_string(starknet_txn_context->invoke_txn->sender_address, + 32, + &address[2], + sizeof(address)); + + if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { + return false; + } + // TODO:Verify Call Data + return true; +} + +static bool get_deploy_txn_user_verification() { + char address[100] = "0x"; + byte_array_to_hex_string(starknet_txn_context->deploy_txn->contract_address, + 32, + &address[2], + sizeof(address)); + + if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { + return false; + } + // TODO:Verify Constructor Call Data + return true; +} + +static bool get_user_verification(void) { + bool user_verified = false; + + switch (starknet_txn_context->which_type) { + case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { + user_verified = get_invoke_txn_user_verification(); + + } break; + + case STARKNET_SIGN_TXN_UNSIGNED_TXN_DEPLOY_TXN_TAG: { + user_verified = get_deploy_txn_user_verification(); + + } break; + } + + if (user_verified) { + set_app_flow_status(STARKNET_SIGN_TXN_STATUS_VERIFY); + } + + return user_verified; +} + +static bool sign_txn(uint8_t *signature_buffer) { + uint8_t seed[64] = {0}; + if (!reconstruct_seed(starknet_txn_context->init_info.wallet_id, + seed, + starknet_send_error)) { + memzero(seed, sizeof(seed)); + return false; + } + + set_app_flow_status(STARKNET_SIGN_TXN_STATUS_SEED_GENERATED); + + uint8_t stark_key[32] = {0}; + if (starknet_derive_key_from_seed( + stark_key, + starknet_txn_context->init_info.derivation_path, + starknet_txn_context->init_info.derivation_path_count, + stark_key, + NULL)) { + } else { + starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + } + + // calculate txn hash + felt_t hash_felt = {0}; + switch (starknet_txn_context->which_type) { + case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { + calculate_txn_hash((void *)starknet_txn_context->invoke_txn, + STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG, + hash_felt); + } break; + + case STARKNET_SIGN_TXN_UNSIGNED_TXN_DEPLOY_TXN_TAG: { + calculate_txn_hash((void *)starknet_txn_context->deploy_txn, + STARKNET_SIGN_TXN_UNSIGNED_TXN_DEPLOY_TXN_TAG, + hash_felt); + + } break; + } + uint8_t hash[32] = {0}; + felt_t_to_hex(hash_felt, hash); + + // generate signature + starknet_sign_digest(starkCurve, stark_key, hash, signature_buffer); + + memzero(seed, sizeof(seed)); + memzero(stark_key, sizeof(stark_key)); + + return true; +} + +static bool send_signature(starknet_query_t *query, const uint8_t *signature) { + starknet_result_t result = init_starknet_result(STARKNET_RESULT_SIGN_TXN_TAG); + result.sign_txn.which_response = STARKNET_SIGN_TXN_RESPONSE_SIGNATURE_TAG; + + if (!starknet_get_query(query, STARKNET_QUERY_SIGN_TXN_TAG) || + !check_which_request(query, STARKNET_SIGN_TXN_REQUEST_SIGNATURE_TAG)) { + return false; + } + + memcpy(&result.sign_txn.signature.signature[0], + signature, + sizeof(result.sign_txn.signature.signature)); + starknet_send_result(&result); + return true; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void starknet_sign_transaction(starknet_query_t *query) { + starknet_init(); + starknet_txn_context = + (starknet_txn_context_t *)malloc(sizeof(starknet_txn_context_t)); + memzero(starknet_txn_context, sizeof(starknet_txn_context_t)); + + uint8_t signature[64] = {0}; + memzero(signature, sizeof(signature)); + + if (handle_initiate_query(query) && fetch_valid_input(query) && + get_user_verification() && sign_txn(signature) && + send_signature(query, (const uint8_t *)signature)) { + delay_scr_init(ui_text_check_cysync, DELAY_TIME); + } + + memzero(signature, sizeof(signature)); + + if (starknet_txn_context) { + free(starknet_txn_context); + starknet_txn_context = NULL; + } + + return; +} \ No newline at end of file diff --git a/common/proto-options/starknet/sign_txn.options b/common/proto-options/starknet/sign_txn.options new file mode 100644 index 00000000..17816500 --- /dev/null +++ b/common/proto-options/starknet/sign_txn.options @@ -0,0 +1,33 @@ +# Options for file common/cypherock-common/proto/starknet/sign_txn.proto +starknet.SignTxnInitiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true +starknet.SignTxnInitiateRequest.derivation_path type:FT_STATIC max_count:6 fixed_length:true + +starknet.GasAmount.max_amount type:FT_STATIC max_size:5 fixed_length:true #TODO: Decide on max size +starknet.GasAmount.max_price_per_unit type:FT_STATIC max_size:5 fixed_length:true + +starknet.CompiledCallData.value type:FT_STATIC max_size:32 max_count:6 fixed_length:false + +starknet.SignTxnInvokeTxn.sender_address type:FT_STATIC max_size:32 fixed_length:true +starknet.SignTxnInvokeTxn.version type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnInvokeTxn.chain_id type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnInvokeTxn.nonce type:FT_STATIC max_size:10 fixed_length:true +starknet.SignTxnInvokeTxn.account_deployment_data type:FT_STATIC max_size:1 fixed_length:true #empty +starknet.SignTxnInvokeTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnInvokeTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnInvokeTxn.tip type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnInvokeTxn.paymaster_data type:FT_STATIC max_size:1 fixed_length:true #empty + + +starknet.SignTxnDeployAccountTxn.contract_address type:FT_STATIC max_size:32 fixed_length:true +starknet.SignTxnDeployAccountTxn.class_hash type:FT_STATIC max_size:32 fixed_length:true +starknet.SignTxnDeployAccountTxn.salt type:FT_STATIC max_size:32 fixed_length:true +starknet.SignTxnDeployAccountTxn.version type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnDeployAccountTxn.chain_id type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnDeployAccountTxn.nonce type:FT_STATIC max_size:10 fixed_length:true +starknet.SignTxnDeployAccountTxn.account_deployment_data type:FT_STATIC max_size:1 fixed_length:true #empty +starknet.SignTxnDeployAccountTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnDeployAccountTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnDeployAccountTxn.tip type:FT_STATIC max_size:1 fixed_length:true +starknet.SignTxnDeployAccountTxn.paymaster_data type:FT_STATIC max_size:1 fixed_length:true #empty + +starknet.SignTxnSignatureResponse.signature type:FT_STATIC max_size:64 fixed_length:true \ No newline at end of file diff --git a/vendor/poseidon b/vendor/poseidon new file mode 160000 index 00000000..68a88df9 --- /dev/null +++ b/vendor/poseidon @@ -0,0 +1 @@ +Subproject commit 68a88df9cb894fe1e0ee8b1b1b5825602c83635a From d939931d50013308151b9f882bf8d117132b731f Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Fri, 13 Dec 2024 19:28:03 +0530 Subject: [PATCH 08/26] fix(app): Proto options size --- apps/starknet_app/starknet_poseidon.c | 63 ++++++++++++++----- apps/starknet_app/starknet_poseidon.h | 5 +- apps/starknet_app/starknet_sign_txn.c | 7 ++- .../proto-options/starknet/sign_txn.options | 14 ++--- 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index 950f86a2..325ea304 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -72,6 +72,7 @@ #include "mini-gmp-helpers.h" #include "mini-gmp.h" #include "poseidon.h" +#include "ui_core_confirm.h" /***************************************************************************** * EXTERN VARIABLES @@ -173,7 +174,9 @@ static void calculate_transaction_hash_common( const pb_byte_t version[], const pb_byte_t sender_address[], const pb_byte_t chain_id[], + const pb_size_t chain_id_size, const pb_byte_t nonce[], + const pb_size_t nonce_size, const felt_t additional_data[], const uint8_t additional_data_size, felt_t hash); @@ -288,23 +291,35 @@ static void encode_resource_bounds_l1(const starknet_resource_bounds_t bounds, // MAX_PRICE_PER_UNIT_BITS) + bounds.level_1.max_price_per_unit // L1_GAS_NAME << RESOURCE_VALUE_OFFSET - mpz_set_ui(result, L1_GAS_NAME); + mpz_set_str(result, L1_GAS_NAME, 16); mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS - mpz_import(temp1, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_1.max_amount); + mpz_import(temp1, + bounds.level_1.max_amount.size, + 1, + 1, + 1, + 0, + bounds.level_1.max_amount.bytes); mpz_mul_2exp(temp1, temp1, MAX_PRICE_PER_UNIT_BITS); // result += temp1 mpz_add(result, result, temp1); - mpz_import( - temp2, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_1.max_price_per_unit); + mpz_import(temp2, + bounds.level_1.max_price_per_unit.size, + 1, + 1, + 1, + 0, + bounds.level_1.max_price_per_unit.bytes); mpz_add(result, result, temp2); // result += temp2 mpz_to_felt(out, result); mpz_clear(temp1); mpz_clear(temp2); + mpz_clear(result); } static void encode_resource_bounds_l2(const starknet_resource_bounds_t bounds, @@ -320,24 +335,36 @@ static void encode_resource_bounds_l2(const starknet_resource_bounds_t bounds, // MAX_PRICE_PER_UNIT_BITS) + bounds.level_2.max_price_per_unit // L1_GAS_NAME << RESOURCE_VALUE_OFFSET - mpz_set_ui(result, L2_GAS_NAME); + mpz_set_str(result, L2_GAS_NAME, 16); mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS - mpz_import(temp1, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_2.max_amount); + mpz_import(temp1, + bounds.level_2.max_amount.size, + 1, + 1, + 1, + 0, + bounds.level_2.max_amount.bytes); mpz_mul_2exp(temp1, temp1, MAX_PRICE_PER_UNIT_BITS); // result += temp1 mpz_add(result, result, temp1); - mpz_import( - temp2, 5, 1, sizeof(pb_byte_t), 0, 0, bounds.level_2.max_price_per_unit); + mpz_import(temp2, + bounds.level_2.max_price_per_unit.size, + 1, + 1, + 1, + 0, + bounds.level_2.max_price_per_unit.bytes); mpz_add(result, result, temp2); // result += temp2 mpz_to_felt(out, result); mpz_clear(temp1); mpz_clear(temp2); + mpz_clear(result); } static void hash_fee_field(const pb_byte_t tip, @@ -390,7 +417,9 @@ static void calculate_transaction_hash_common( const pb_byte_t version[], const pb_byte_t sender_address[], const pb_byte_t chain_id[], + const pb_size_t chain_id_size, const pb_byte_t nonce[], + const pb_size_t nonce_size, const felt_t additional_data[], const uint8_t additional_data_size, felt_t hash) { @@ -418,8 +447,8 @@ static void calculate_transaction_hash_common( ///< refer: ///< https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#v3_transaction_fields f251_copy(state[offset++], paymaster_data_res); - hex_to_felt_t(chain_id, 1, state[offset++]); - hex_to_felt_t(nonce, 10, state[offset++]); + hex_to_felt_t(chain_id, chain_id_size, state[offset++]); + hex_to_felt_t(nonce, nonce_size, state[offset++]); f251_copy(state[offset++], DAMode_hash); if (additional_data != NULL) { for (uint8_t i = 0; i < additional_data_size; i++) { @@ -427,7 +456,7 @@ static void calculate_transaction_hash_common( ASSERT(offset < state_max); } } - + print_state(state, offset); poseidon_hash_many(state, offset, hash); } @@ -467,8 +496,10 @@ static void calculate_deploy_transaction_hash( txn->fee_data_availability_mode, txn->version, txn->contract_address, - txn->chain_id, - txn->nonce, + txn->chain_id.bytes, + txn->chain_id.size, + txn->nonce.bytes, + txn->nonce.size, additional_data, 3, hash); @@ -512,8 +543,10 @@ static void calculate_invoke_transaction_hash( txn->fee_data_availability_mode, txn->version, txn->sender_address, - txn->chain_id, - txn->nonce, + txn->chain_id.bytes, + txn->chain_id.size, + txn->nonce.bytes, + txn->nonce.size, additional_data, 2, hash); diff --git a/apps/starknet_app/starknet_poseidon.h b/apps/starknet_app/starknet_poseidon.h index b6cc0e95..ea2514e1 100644 --- a/apps/starknet_app/starknet_poseidon.h +++ b/apps/starknet_app/starknet_poseidon.h @@ -81,9 +81,8 @@ #define MAX_PRICE_PER_UNIT_BITS 128 // 128 bits for max_price_per_unit #define RESOURCE_VALUE_OFFSET \ (MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS) // Combined offset -#define L1_GAS_NAME \ - (unsigned long)0x4c315f474153 // The constant value for L1_GAS_NAME -#define L2_GAS_NAME (unsigned long)0x4c325f474153 +#define L1_GAS_NAME "4c315f474153" // The constant value for L1_GAS_NAME +#define L2_GAS_NAME "4c325f474153" #define INVOKE_TXN_PREFIX \ { 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65 } // 0x696e766f6b65; 'INKVOKE' #define DEPLOY_ACCOUNT_PREFIX \ diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index f797997c..4723fde6 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -328,9 +328,9 @@ int starknet_sign_digest(const stark_curve *curve, } // if S > order/2 => S = -S - if ((mpz_cmp(curve->order_half, *s) < 0)) { - mpz_sub(*s, curve->order, *s); - } + // if ((mpz_cmp(curve->order_half, *s) < 0)) { + // mpz_sub(*s, curve->order, *s); + // } // we are done, R.x and s is the result signature mpz_to_byte_array(R.x, sig, 32); mpz_to_byte_array(*s, sig + 32, 32); @@ -529,6 +529,7 @@ static bool sign_txn(uint8_t *signature_buffer) { // calculate txn hash felt_t hash_felt = {0}; switch (starknet_txn_context->which_type) { + // TODO: Remove switch case and pass starknet_txn_context itself case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { calculate_txn_hash((void *)starknet_txn_context->invoke_txn, STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG, diff --git a/common/proto-options/starknet/sign_txn.options b/common/proto-options/starknet/sign_txn.options index 17816500..fbbbf6ad 100644 --- a/common/proto-options/starknet/sign_txn.options +++ b/common/proto-options/starknet/sign_txn.options @@ -2,15 +2,15 @@ starknet.SignTxnInitiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true starknet.SignTxnInitiateRequest.derivation_path type:FT_STATIC max_count:6 fixed_length:true -starknet.GasAmount.max_amount type:FT_STATIC max_size:5 fixed_length:true #TODO: Decide on max size -starknet.GasAmount.max_price_per_unit type:FT_STATIC max_size:5 fixed_length:true +starknet.GasAmount.max_amount type:FT_STATIC max_size:32 fixed_length:false #TODO: Decide on max size +starknet.GasAmount.max_price_per_unit type:FT_STATIC max_size:32 fixed_length:false -starknet.CompiledCallData.value type:FT_STATIC max_size:32 max_count:6 fixed_length:false +starknet.CompiledCallData.value type:FT_STATIC max_size:32 max_count:10 fixed_length:false starknet.SignTxnInvokeTxn.sender_address type:FT_STATIC max_size:32 fixed_length:true starknet.SignTxnInvokeTxn.version type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnInvokeTxn.chain_id type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnInvokeTxn.nonce type:FT_STATIC max_size:10 fixed_length:true +starknet.SignTxnInvokeTxn.chain_id type:FT_STATIC max_size:10 fixed_length:false +starknet.SignTxnInvokeTxn.nonce type:FT_STATIC max_size:10 fixed_length:false starknet.SignTxnInvokeTxn.account_deployment_data type:FT_STATIC max_size:1 fixed_length:true #empty starknet.SignTxnInvokeTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnInvokeTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true @@ -22,8 +22,8 @@ starknet.SignTxnDeployAccountTxn.contract_address type:FT_STATIC max_size:32 fix starknet.SignTxnDeployAccountTxn.class_hash type:FT_STATIC max_size:32 fixed_length:true starknet.SignTxnDeployAccountTxn.salt type:FT_STATIC max_size:32 fixed_length:true starknet.SignTxnDeployAccountTxn.version type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnDeployAccountTxn.chain_id type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnDeployAccountTxn.nonce type:FT_STATIC max_size:10 fixed_length:true +starknet.SignTxnDeployAccountTxn.chain_id type:FT_STATIC max_size:10 fixed_length:false #TODO: Decide on max size +starknet.SignTxnDeployAccountTxn.nonce type:FT_STATIC max_size:10 fixed_length:false #TODO: Decide on max size starknet.SignTxnDeployAccountTxn.account_deployment_data type:FT_STATIC max_size:1 fixed_length:true #empty starknet.SignTxnDeployAccountTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnDeployAccountTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true From aeef9d9ebd332e3419618063a6a516b5c354f819 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Sat, 14 Dec 2024 14:52:32 +0530 Subject: [PATCH 09/26] chore: Add submodules --- .gitmodules | 4 ++-- CMakeLists.txt | 3 ++- utilities/cmake/firmware/firmware.cmake | 2 +- utilities/cmake/simulator/simulator.cmake | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index e1e19769..98718825 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,6 @@ [submodule "mini-gmp"] path = vendor/mini-gmp url = ../mini-gmp -[submodule "vendor/poseidon"] +[submodule "poseidon"] path = vendor/poseidon - url = https://github.com/Cypherock/poseidon + url = ../poseidon diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f88ab9a..db642fd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ file(GLOB_RECURSE PROTO_SRCS "generated/proto/*.*") list(APPEND PROTO_SRCS "vendor/nanopb/pb_common.c" "vendor/nanopb/pb_decode.c" "vendor/nanopb/pb_encode.c" "vendor/nanopb/pb_common.h" "vendor/nanopb/pb_decode.h" "vendor/nanopb/pb_encode.h" "vendor/nanopb/pb.h") list (APPEND MINI_GMP_SRCS "vendor/mini-gmp/mini-gmp-helpers.c" "vendor/mini-gmp/mini-gmp.c") +list (APPEND POSEIDON_SRCS "vendor/poseidon/sources/f251.c" "vendor/poseidon/sources/poseidon.c" "vendor/poseidon/sources/poseidon_rc.c") OPTION(DEV_SWITCH "Additional features/logs to aid developers" OFF) OPTION(UNIT_TESTS_SWITCH "Compile build for main firmware or unit tests" OFF) @@ -51,7 +52,7 @@ else() endif() # Include nanopb source headers -target_include_directories( ${EXECUTABLE} PRIVATE vendor/nanopb generated/proto vendor/mini-gmp) +target_include_directories( ${EXECUTABLE} PRIVATE vendor/nanopb generated/proto vendor/mini-gmp vendor/poseidon/sources) # Enable support for dynamically allocated fields in nanopb # Ref: vendor/nanopb/pb.h diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index f8363453..f0b8a837 100644 --- a/utilities/cmake/firmware/firmware.cmake +++ b/utilities/cmake/firmware/firmware.cmake @@ -19,7 +19,7 @@ ELSE() file(GLOB_RECURSE SOURCES "stm32-hal/*.*" "common/*.*" "src/*.*" "apps/*.*") ENDIF(UNIT_TESTS_SWITCH) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${POSEIDON_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES} ${LINKER_SCRIPT} ${STARTUP_FILE}) target_compile_definitions(${EXECUTABLE} PRIVATE -DUSE_HAL_DRIVER -DSTM32L486xx ) add_compile_definitions(USE_SIMULATOR=0 USE_BIP32_CACHE=0 USE_BIP39_CACHE=0 STM32L4 USBD_SOF_DISABLED ENABLE_HID_WEBUSB_COMM=1) IF (DEV_SWITCH) diff --git a/utilities/cmake/simulator/simulator.cmake b/utilities/cmake/simulator/simulator.cmake index b0780449..21d5fa8c 100644 --- a/utilities/cmake/simulator/simulator.cmake +++ b/utilities/cmake/simulator/simulator.cmake @@ -20,7 +20,7 @@ set(EXECUTABLE ${PROJECT_NAME}) find_package(SDL2 REQUIRED SDL2) include_directories(${SDL2_INCLUDE_DIRS}) -add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) +add_executable(${EXECUTABLE} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.c ${MINI_GMP_SRCS} ${POSEIDON_SRCS} ${PROTO_SRCS} ${PROTO_HDRS} ${INCLUDES}) if ("${FIRMWARE_TYPE}" STREQUAL "Main") add_compile_definitions(X1WALLET_INITIAL=0 X1WALLET_MAIN=1) From 1b17339a732bdb6196e470ba059ace60e702120f Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Sat, 14 Dec 2024 22:37:00 +0530 Subject: [PATCH 10/26] fix: Sign gen minor bug --- apps/starknet_app/starknet_poseidon.c | 2 +- apps/starknet_app/starknet_sign_txn.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index 325ea304..f6a9a4ba 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -456,7 +456,7 @@ static void calculate_transaction_hash_common( ASSERT(offset < state_max); } } - print_state(state, offset); + poseidon_hash_many(state, offset, hash); } diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 4723fde6..8bb573fe 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -228,7 +228,7 @@ void bn_to_mpz(mpz_t mpz, const bignum256 *bn) { void generate_k_rfc6979_mpz(mpz_t k, rfc6979_state *state) { uint8_t buf[32] = {0}; generate_rfc6979(buf, state); - mpz_import(k, 32, 1, 1, 1, 0, buf); + mpz_import(k, sizeof(buf), 1, 1, 1, 0, buf); memzero(buf, sizeof(buf)); } @@ -517,7 +517,7 @@ static bool sign_txn(uint8_t *signature_buffer) { uint8_t stark_key[32] = {0}; if (starknet_derive_key_from_seed( - stark_key, + seed, starknet_txn_context->init_info.derivation_path, starknet_txn_context->init_info.derivation_path_count, stark_key, From a46aefd758f74a14be25e5314b4bbd7bba19b139 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Sun, 15 Dec 2024 01:08:50 +0530 Subject: [PATCH 11/26] chore: Update submodule --- vendor/mini-gmp | 2 +- vendor/poseidon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/mini-gmp b/vendor/mini-gmp index 114b80c1..cced8f79 160000 --- a/vendor/mini-gmp +++ b/vendor/mini-gmp @@ -1 +1 @@ -Subproject commit 114b80c19742a3c9058da81e72e088766154ac53 +Subproject commit cced8f79a071b37a2d9f185d0c2c893e27a92bbd diff --git a/vendor/poseidon b/vendor/poseidon index 68a88df9..d2148fe3 160000 --- a/vendor/poseidon +++ b/vendor/poseidon @@ -1 +1 @@ -Subproject commit 68a88df9cb894fe1e0ee8b1b1b5825602c83635a +Subproject commit d2148fe3cb2513c39bf25ab50689be5d0683dc02 From 2590c3cbcf62cc0ac0b13a874140668937117d83 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Mon, 16 Dec 2024 20:42:11 +0530 Subject: [PATCH 12/26] fix: Relocate starknet init --- apps/starknet_app/starknet_pub_key.c | 2 +- apps/starknet_app/starknet_sign_txn.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c index 957367d9..a791a217 100644 --- a/apps/starknet_app/starknet_pub_key.c +++ b/apps/starknet_app/starknet_pub_key.c @@ -498,7 +498,7 @@ void starknet_get_pub_keys(starknet_query_t *query) { set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_SEED_GENERATED); delay_scr_init(ui_text_processing, DELAY_SHORT); - // initialize starknet context + // Initialize starknet context starknet_init(); bool status = fill_starknet_public_keys(init_req->derivation_paths, seed, diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 8bb573fe..8b5018fc 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -591,6 +591,7 @@ void starknet_sign_transaction(starknet_query_t *query) { } memzero(signature, sizeof(signature)); + stark_pedersen_clear(); if (starknet_txn_context) { free(starknet_txn_context); From fc02c36030a12e0b6e4f5efb9a03fd691a6f78bd Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Tue, 17 Dec 2024 01:21:49 +0530 Subject: [PATCH 13/26] chore: Add starknet verify amount --- apps/starknet_app/starknet_sign_txn.c | 87 ++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 8b5018fc..33013be8 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -199,6 +199,20 @@ static bool sign_txn(uint8_t *signature_buffer); */ static bool send_signature(starknet_query_t *query, const uint8_t *signature); +/** + * @brief Represents starknet u256 amount in string upto 6 decimal places + * + * @param byte_array Input byte array + * @param size Size of byte array + * @param amount_str String representation of amount + * @param amount_size Size of amount_str + */ + +static void stark_amount_get_decimal_str(const uint8_t *byte_array, + const uint8_t size, + char *amount_str, + uint8_t amount_size); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -454,6 +468,57 @@ static bool fetch_valid_input(starknet_query_t *query) { return false; } +static void stark_amount_get_decimal_str(const uint8_t *byte_array, + const uint8_t size, + char *amount_str, + uint8_t amount_size) { + mpz_t num; + mpz_init(num); + byte_array_to_mpz(num, byte_array, size); + + char str[100] = ""; + mpz_get_str(str, 10, num); + uint8_t len = strlen(str); + + if (len < 18) { + // prepend 0 and decimal point + snprintf(amount_str, amount_size, "0."); + + // prepend 0s after decimal if required + uint8_t i = 2; + for (; i < (18 - len + 2); i++) { + snprintf(amount_str + i, amount_size - i, "0"); + } + uint8_t offset = 0; + for (; i < 6 + 2; i++) { + snprintf(amount_str + i, amount_size - i, "%c", str[offset++]); + } + return; + } else { + uint8_t i = 0; + for (; i < (len - 18); i++) { + snprintf(amount_str + i, amount_size - i, "%c", str[i]); + } + // prepend 0 before decimal if required + uint8_t k = 0; + if (i == 0) { + snprintf(amount_str, amount_size, "0"); + i++; + // set k to 1 incase prepending required + k = 1; + } + snprintf(amount_str + i, amount_size - i, "."); + i++; + // add upto 6 decimal characters + for (uint8_t j = 0; j < 6; j++) { + snprintf((amount_str + i) + j, + (amount_size - i) + j, + "%c", + str[(i - k - 1) + j]); + } + } +} + static bool get_invoke_txn_user_verification() { char address[100] = "0x"; byte_array_to_hex_string(starknet_txn_context->invoke_txn->sender_address, @@ -464,7 +529,27 @@ static bool get_invoke_txn_user_verification() { if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { return false; } - // TODO:Verify Call Data + + // Verify Amount + char amount_str[200] = ""; + stark_amount_get_decimal_str( + starknet_txn_context->invoke_txn->calldata.value[5] + .bytes, ///< index for amount is 5th + starknet_txn_context->invoke_txn->calldata.value[5].size, + amount_str, + sizeof(amount_str)); + + char display[200] = {'\0'}; + snprintf(display, + sizeof(display), + UI_TEXT_VERIFY_AMOUNT, + amount_str, + starknet_app.lunit1_name); + + if (!core_confirmation(display, starknet_send_error)) { + return false; + } + return true; } From 4cc64844abd7f22a9234c57ebc82e78288cd150d Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Tue, 17 Dec 2024 01:29:09 +0530 Subject: [PATCH 14/26] chore: Update starknet utxn proto --- common/cypherock-common | 2 +- common/proto-options/starknet/sign_txn.options | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/cypherock-common b/common/cypherock-common index 0065cf12..bdaa657f 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 0065cf1229d18abf14b0b1b194e681a85c9c9082 +Subproject commit bdaa657ff0ff3199fa613c9893ea7b4457ec9150 diff --git a/common/proto-options/starknet/sign_txn.options b/common/proto-options/starknet/sign_txn.options index fbbbf6ad..d0c9942a 100644 --- a/common/proto-options/starknet/sign_txn.options +++ b/common/proto-options/starknet/sign_txn.options @@ -11,11 +11,11 @@ starknet.SignTxnInvokeTxn.sender_address type:FT_STATIC max_size:32 fixed_length starknet.SignTxnInvokeTxn.version type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnInvokeTxn.chain_id type:FT_STATIC max_size:10 fixed_length:false starknet.SignTxnInvokeTxn.nonce type:FT_STATIC max_size:10 fixed_length:false -starknet.SignTxnInvokeTxn.account_deployment_data type:FT_STATIC max_size:1 fixed_length:true #empty +starknet.SignTxnInvokeTxn.account_deployment_data type:FT_STATIC max_size:1 max_count:1 fixed_length:false #empty starknet.SignTxnInvokeTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnInvokeTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnInvokeTxn.tip type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnInvokeTxn.paymaster_data type:FT_STATIC max_size:1 fixed_length:true #empty +starknet.SignTxnInvokeTxn.paymaster_data type:FT_STATIC max_size:1 max_count:1 fixed_length:false #empty starknet.SignTxnDeployAccountTxn.contract_address type:FT_STATIC max_size:32 fixed_length:true @@ -28,6 +28,6 @@ starknet.SignTxnDeployAccountTxn.account_deployment_data type:FT_STATIC max_size starknet.SignTxnDeployAccountTxn.nonce_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnDeployAccountTxn.fee_data_availability_mode type:FT_STATIC max_size:1 fixed_length:true starknet.SignTxnDeployAccountTxn.tip type:FT_STATIC max_size:1 fixed_length:true -starknet.SignTxnDeployAccountTxn.paymaster_data type:FT_STATIC max_size:1 fixed_length:true #empty +starknet.SignTxnDeployAccountTxn.paymaster_data type:FT_STATIC max_size:1 max_count:1 fixed_length:false #empty starknet.SignTxnSignatureResponse.signature type:FT_STATIC max_size:64 fixed_length:true \ No newline at end of file From f8823f35085abf61e3e6036197228b0d73a4be0d Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 18 Dec 2024 14:10:12 +0530 Subject: [PATCH 15/26] fix: Verify txn data --- apps/starknet_app/starknet_sign_txn.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 33013be8..e6bd9c9b 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -521,10 +521,14 @@ static void stark_amount_get_decimal_str(const uint8_t *byte_array, static bool get_invoke_txn_user_verification() { char address[100] = "0x"; - byte_array_to_hex_string(starknet_txn_context->invoke_txn->sender_address, - 32, - &address[2], - sizeof(address)); + if (starknet_txn_context->invoke_txn->calldata.value[4].size != 32) { + return false; + } + byte_array_to_hex_string( + starknet_txn_context->invoke_txn->calldata.value[4].bytes, + 32, + &address[2], + sizeof(address)); if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { return false; @@ -555,10 +559,14 @@ static bool get_invoke_txn_user_verification() { static bool get_deploy_txn_user_verification() { char address[100] = "0x"; - byte_array_to_hex_string(starknet_txn_context->deploy_txn->contract_address, - 32, - &address[2], - sizeof(address)); + if (starknet_txn_context->invoke_txn->calldata.value[4].size != 32) { + return false; + } + byte_array_to_hex_string( + starknet_txn_context->invoke_txn->calldata.value[4].bytes, + 32, + &address[2], + sizeof(address)); if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { return false; From 266e8271fcec1ac54951a315aec1d3cf97dd8ae6 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 18 Dec 2024 14:21:09 +0530 Subject: [PATCH 16/26] chore: Update base --- apps/starknet_app/starknet_sign_txn.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index e6bd9c9b..1a329640 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -71,6 +71,7 @@ #include "rfc6979.h" #include "starknet_api.h" #include "starknet_context.h" +#include "starknet_crypto.h" #include "starknet_helpers.h" #include "starknet_poseidon.h" #include "starknet_priv.h" @@ -266,14 +267,14 @@ void generate_k_random_mpz(mpz_t k, const mpz_t prime) { bn_to_mpz(k, &k_bn); } -int starknet_sign_digest(const stark_curve *curve, +int starknet_sign_digest(const mpz_curve *curve, const uint8_t *priv_key, const uint8_t *digest, uint8_t *sig) { int i = 0; - stark_point R = {0}; + mpz_curve_point R = {0}; mpz_t k, z, randk; - stark_point_init(&R); + mpz_curve_point_init(&R); mpz_t *s = &R.y; mpz_init(k); mpz_init(z); @@ -301,7 +302,7 @@ int starknet_sign_digest(const stark_curve *curve, generate_k_random_mpz(k, curve->order); #endif // compute k*G - stark_point_multiply(curve, k, &curve->G, &R); + mpz_curve_point_multiply(curve, k, &curve->G, &R); mpz_mod(R.x, R.x, curve->order); // r = (rx mod n) if (!(mpz_cmp(R.x, curve->order) < 0)) { @@ -640,7 +641,7 @@ static bool sign_txn(uint8_t *signature_buffer) { felt_t_to_hex(hash_felt, hash); // generate signature - starknet_sign_digest(starkCurve, stark_key, hash, signature_buffer); + starknet_sign_digest(stark_curve, stark_key, hash, signature_buffer); memzero(seed, sizeof(seed)); memzero(stark_key, sizeof(stark_key)); @@ -684,7 +685,7 @@ void starknet_sign_transaction(starknet_query_t *query) { } memzero(signature, sizeof(signature)); - stark_pedersen_clear(); + stark_clear(); if (starknet_txn_context) { free(starknet_txn_context); From 462c1960dade014fbd02fa836c8d3b7433cdd94b Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 18 Dec 2024 21:07:36 +0530 Subject: [PATCH 17/26] chore: Complete verification data all txns --- apps/starknet_app/starknet_poseidon.c | 2 +- apps/starknet_app/starknet_sign_txn.c | 94 ++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index f6a9a4ba..a5a03fc0 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -465,7 +465,7 @@ static void calculate_deploy_transaction_hash( felt_t hash) { uint8_t hex[14] = DEPLOY_ACCOUNT_PREFIX; felt_t transaction_hash_prefix = {0}; - hex_to_felt_t(hex, 12, transaction_hash_prefix); + hex_to_felt_t(hex, 14, transaction_hash_prefix); // prepare additional data array const uint8_t data_max_count = 3; diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 1a329640..2e33fc8d 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -489,9 +489,13 @@ static void stark_amount_get_decimal_str(const uint8_t *byte_array, uint8_t i = 2; for (; i < (18 - len + 2); i++) { snprintf(amount_str + i, amount_size - i, "0"); + // stop if more than 6 zeros (value too low) + if (i == (2 + 6)) { + break; + } } uint8_t offset = 0; - for (; i < 6 + 2; i++) { + for (; i < 6 + 2; i++) { ///< append till reach 6th char snprintf(amount_str + i, amount_size - i, "%c", str[offset++]); } return; @@ -520,7 +524,27 @@ static void stark_amount_get_decimal_str(const uint8_t *byte_array, } } +static void starknet_get_max_fee(const uint8_t *max_amount_bytes, + const uint8_t max_amount_size, + const uint8_t *unit_price_bytes, + const uint8_t unit_price_size, + uint8_t max_fee[32]) { + // get max fee = max_amount * unit_price + mpz_t max_amount, unit_price; + mpz_init(max_amount); + mpz_init(unit_price); + + byte_array_to_mpz(max_amount, max_amount_bytes, max_amount_size); + byte_array_to_mpz(unit_price, unit_price_bytes, unit_price_size); + mpz_mul(max_amount, max_amount, unit_price); + + mpz_to_byte_array(max_amount, max_fee, 32); + mpz_clear(max_amount); + mpz_clear(unit_price); +} + static bool get_invoke_txn_user_verification() { + // verify address char address[100] = "0x"; if (starknet_txn_context->invoke_txn->calldata.value[4].size != 32) { return false; @@ -555,24 +579,75 @@ static bool get_invoke_txn_user_verification() { return false; } + // Verify gas fee + memzero(amount_str, sizeof(amount_str)); + + // calculate l1 max fee + uint8_t max_fee[32] = {0}; + starknet_get_max_fee( + starknet_txn_context->invoke_txn->resource_bound.level_1.max_amount.bytes, + starknet_txn_context->invoke_txn->resource_bound.level_1.max_amount.size, + starknet_txn_context->invoke_txn->resource_bound.level_1 + .max_price_per_unit.bytes, + starknet_txn_context->invoke_txn->resource_bound.level_1 + .max_price_per_unit.size, + max_fee); + stark_amount_get_decimal_str(max_fee, 32, amount_str, sizeof(amount_str)); + + memzero(display, sizeof(display)); + snprintf(display, + sizeof(display), + "Verify Max Fee\n%s\n%s", + amount_str, + starknet_app.lunit1_name); + + if (!core_confirmation(display, starknet_send_error)) { + return false; + } + return true; } static bool get_deploy_txn_user_verification() { + // verify address char address[100] = "0x"; - if (starknet_txn_context->invoke_txn->calldata.value[4].size != 32) { + + byte_array_to_hex_string(starknet_txn_context->deploy_txn->contract_address, + 32, + &address[2], + sizeof(address)); + + if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { return false; } - byte_array_to_hex_string( - starknet_txn_context->invoke_txn->calldata.value[4].bytes, - 32, - &address[2], - sizeof(address)); - if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { + // Verify gas fee + char amount_str[100] = ""; + + // calculate l1 max fee + uint8_t max_fee[32] = {0}; + starknet_get_max_fee( + starknet_txn_context->deploy_txn->resource_bounds.level_1.max_amount + .bytes, + starknet_txn_context->deploy_txn->resource_bounds.level_1.max_amount.size, + starknet_txn_context->deploy_txn->resource_bounds.level_1 + .max_price_per_unit.bytes, + starknet_txn_context->deploy_txn->resource_bounds.level_1 + .max_price_per_unit.size, + max_fee); + stark_amount_get_decimal_str(max_fee, 32, amount_str, sizeof(amount_str)); + + char display[200] = {'\0'}; + snprintf(display, + sizeof(display), + "Verify Max Fee\n%s\n%s", + amount_str, + starknet_app.lunit1_name); + + if (!core_confirmation(display, starknet_send_error)) { return false; } - // TODO:Verify Constructor Call Data + return true; } @@ -623,7 +698,6 @@ static bool sign_txn(uint8_t *signature_buffer) { // calculate txn hash felt_t hash_felt = {0}; switch (starknet_txn_context->which_type) { - // TODO: Remove switch case and pass starknet_txn_context itself case STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG: { calculate_txn_hash((void *)starknet_txn_context->invoke_txn, STARKNET_SIGN_TXN_UNSIGNED_TXN_INVOKE_TXN_TAG, From ea3033f17c24556ad2cac416cd8567442540ae4c Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Wed, 18 Dec 2024 21:20:42 +0530 Subject: [PATCH 18/26] chore: Refactor sign utilities --- apps/starknet_app/starknet_poseidon.c | 2 +- apps/starknet_app/starknet_poseidon.h | 2 +- apps/starknet_app/starknet_sign_txn.c | 145 +---------------- .../crypto/mpz_operations/mpz_ecdsa.c | 151 +++++++++++++++++- .../crypto/mpz_operations/mpz_ecdsa.h | 11 ++ 5 files changed, 163 insertions(+), 148 deletions(-) diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index a5a03fc0..d8631cfb 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -1,7 +1,7 @@ /** * @file starknet_poseidon.c * @author Cypherock X1 Team - * @brief Utilities specific to Starknet chains + * @brief Utilities specific to Starknet POSEIDON * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ diff --git a/apps/starknet_app/starknet_poseidon.h b/apps/starknet_app/starknet_poseidon.h index ea2514e1..3368d66d 100644 --- a/apps/starknet_app/starknet_poseidon.h +++ b/apps/starknet_app/starknet_poseidon.h @@ -1,7 +1,7 @@ /** * @file starknet_poseidon.h * @author Cypherock X1 Team - * @brief Utilities specific to Starknet chains + * @brief Utilities specific to Starknet POSEIDON * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index 2e33fc8d..cedb2a09 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -66,6 +66,7 @@ #include "ecdsa.h" #include "mini-gmp-helpers.h" #include "mini-gmp.h" +#include "mpz_ecdsa.h" #include "rand.h" #include "reconstruct_wallet_flow.h" #include "rfc6979.h" @@ -226,150 +227,6 @@ static starknet_txn_context_t *starknet_txn_context = NULL; /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ -void mpz_to_bn(bignum256 *bn, const mpz_t mpz) { - uint8_t out[32] = {0}; - mpz_to_byte_array(mpz, out, 32); - bn_read_be(out, bn); -} - -void bn_to_mpz(mpz_t mpz, const bignum256 *bn) { - uint8_t in[32] = {0}; - bn_write_be(bn, in); - mpz_import(mpz, 32, 1, 1, 1, 0, in); -} - -// generate K in a deterministic way, according to RFC6979 -// http://tools.ietf.org/html/rfc6979 -void generate_k_rfc6979_mpz(mpz_t k, rfc6979_state *state) { - uint8_t buf[32] = {0}; - generate_rfc6979(buf, state); - mpz_import(k, sizeof(buf), 1, 1, 1, 0, buf); - memzero(buf, sizeof(buf)); -} - -// generate random K for signing/side-channel noise -static void generate_k_random(bignum256 *k, const bignum256 *prime) { - do { - int i = 0; - for (i = 0; i < 8; i++) { - k->val[i] = random32() & 0x3FFFFFFF; - } - k->val[8] = random32() & 0xFFFF; - // check that k is in range and not zero. - } while (bn_is_zero(k) || !bn_is_less(k, prime)); -} - -void generate_k_random_mpz(mpz_t k, const mpz_t prime) { - bignum256 prime_bn, k_bn = {0}; - mpz_to_bn(&prime_bn, prime); - mpz_to_bn(&k_bn, k); - generate_k_random(&k_bn, &prime_bn); - bn_to_mpz(k, &k_bn); -} - -int starknet_sign_digest(const mpz_curve *curve, - const uint8_t *priv_key, - const uint8_t *digest, - uint8_t *sig) { - int i = 0; - mpz_curve_point R = {0}; - mpz_t k, z, randk; - mpz_curve_point_init(&R); - mpz_t *s = &R.y; - mpz_init(k); - mpz_init(z); - mpz_init(randk); - -#if USE_RFC6979 - rfc6979_state rng = {0}; - init_rfc6979(priv_key, digest, &rng); -#endif - mpz_import(z, 32, 1, 1, 0, 0, digest); - for (i = 0; i < 10000; i++) { -#if USE_RFC6979 - // generate K deterministically - generate_k_rfc6979_mpz(k, &rng); - - // k >> 4 - mpz_fdiv_q_2exp(k, k, 4); - - // if k is too big or too small, we don't like it - if ((mpz_cmp_ui(k, 0) == 0) || !(mpz_cmp(k, curve->order) < 0)) { - continue; - } -#else - // generate random number k - generate_k_random_mpz(k, curve->order); -#endif - // compute k*G - mpz_curve_point_multiply(curve, k, &curve->G, &R); - mpz_mod(R.x, R.x, curve->order); - // r = (rx mod n) - if (!(mpz_cmp(R.x, curve->order) < 0)) { - mpz_sub(R.x, R.x, curve->order); - } - // if r is zero, we retry - if (mpz_cmp_ui(R.x, 0) == 0) { - continue; - } - - // randomize operations to counter side-channel attacks - generate_k_random_mpz(randk, curve->order); - - // k = k * rand mod n - mpz_mul(k, k, randk); - mpz_mod(k, k, curve->order); - - // k = (k * rand)^-1 - mpz_invert(k, k, curve->order); - - mpz_import(*s, 32, 1, 1, 1, 0, priv_key); - // R.x*priv - mpz_mul(*s, *s, R.x); - mpz_mod(*s, *s, curve->order); - mpz_add(*s, *s, z); // R.x*priv + z - - // (k*rand)^-1 (R.x*priv + z) - mpz_mul(*s, *s, k); - mpz_mod(*s, *s, curve->order); - - // k^-1 (R.x*priv + z) - mpz_mul(*s, *s, randk); - mpz_mod(*s, *s, curve->order); - - // if s is zero, we retry - if ((mpz_cmp_ui(*s, 0) == 0)) { - continue; - } - - // if S > order/2 => S = -S - // if ((mpz_cmp(curve->order_half, *s) < 0)) { - // mpz_sub(*s, curve->order, *s); - // } - // we are done, R.x and s is the result signature - mpz_to_byte_array(R.x, sig, 32); - mpz_to_byte_array(*s, sig + 32, 32); - - mpz_clear(k); - mpz_clear(randk); - mpz_clear(z); - -#if USE_RFC6979 - memzero(&rng, sizeof(rng)); -#endif - return 0; - } - - // Too many retries without a valid signature - // -> fail with an error - memzero(&k, sizeof(k)); - memzero(&randk, sizeof(randk)); -#if USE_RFC6979 - memzero(&rng, sizeof(rng)); -#endif - return -1; -} - static bool check_which_request(const starknet_query_t *query, pb_size_t which_request) { if (which_request != query->sign_txn.which_request) { diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c index 6f96c371..9719474e 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -60,7 +60,11 @@ *****************************************************************************/ #include "mpz_ecdsa.h" +#include + +#include "mini-gmp-helpers.h" #include "mini-gmp.h" +#include "rfc6979.h" /***************************************************************************** * EXTERN VARIABLES @@ -79,11 +83,51 @@ *****************************************************************************/ /***************************************************************************** - * STATIC VARIABLES + * STATIC FUNCTIONS *****************************************************************************/ +static void mpz_to_bn(bignum256 *bn, const mpz_t mpz) { + uint8_t out[32] = {0}; + mpz_to_byte_array(mpz, out, 32); + bn_read_be(out, bn); +} + +static void bn_to_mpz(mpz_t mpz, const bignum256 *bn) { + uint8_t in[32] = {0}; + bn_write_be(bn, in); + mpz_import(mpz, 32, 1, 1, 1, 0, in); +} + +// generate K in a deterministic way, according to RFC6979 +// http://tools.ietf.org/html/rfc6979 +static void generate_k_rfc6979_mpz(mpz_t k, rfc6979_state *state) { + uint8_t buf[32] = {0}; + generate_rfc6979(buf, state); + mpz_import(k, sizeof(buf), 1, 1, 1, 0, buf); + memzero(buf, sizeof(buf)); +} + +// generate random K for signing/side-channel noise +static void generate_k_random(bignum256 *k, const bignum256 *prime) { + do { + int i = 0; + for (i = 0; i < 8; i++) { + k->val[i] = random32() & 0x3FFFFFFF; + } + k->val[8] = random32() & 0xFFFF; + // check that k is in range and not zero. + } while (bn_is_zero(k) || !bn_is_less(k, prime)); +} + +static void generate_k_random_mpz(mpz_t k, const mpz_t prime) { + bignum256 prime_bn, k_bn = {0}; + mpz_to_bn(&prime_bn, prime); + mpz_to_bn(&k_bn, k); + generate_k_random(&k_bn, &prime_bn); + bn_to_mpz(k, &k_bn); +} /***************************************************************************** - * GLOBAL VARIABLES + * GLOBAL FUNCTIONS *****************************************************************************/ void mpz_curve_point_init(mpz_curve_point *p) { @@ -286,3 +330,106 @@ int bn_bit_length(const mpz_t k) { int bn_is_bit_set(const mpz_t k, int bit_idx) { return mpz_tstbit(k, bit_idx); } + +int starknet_sign_digest(const mpz_curve *curve, + const uint8_t *priv_key, + const uint8_t *digest, + uint8_t *sig) { + int i = 0; + mpz_curve_point R = {0}; + mpz_t k, z, randk; + mpz_curve_point_init(&R); + mpz_t *s = &R.y; + mpz_init(k); + mpz_init(z); + mpz_init(randk); + +#if USE_RFC6979 + rfc6979_state rng = {0}; + init_rfc6979(priv_key, digest, &rng); +#endif + mpz_import(z, 32, 1, 1, 0, 0, digest); + for (i = 0; i < 10000; i++) { +#if USE_RFC6979 + // generate K deterministically + generate_k_rfc6979_mpz(k, &rng); + + // k >> 4 + mpz_fdiv_q_2exp(k, k, 4); + + // if k is too big or too small, we don't like it + if ((mpz_cmp_ui(k, 0) == 0) || !(mpz_cmp(k, curve->order) < 0)) { + continue; + } +#else + // generate random number k + generate_k_random_mpz(k, curve->order); +#endif + // compute k*G + mpz_curve_point_multiply(curve, k, &curve->G, &R); + mpz_mod(R.x, R.x, curve->order); + // r = (rx mod n) + if (!(mpz_cmp(R.x, curve->order) < 0)) { + mpz_sub(R.x, R.x, curve->order); + } + // if r is zero, we retry + if (mpz_cmp_ui(R.x, 0) == 0) { + continue; + } + + // randomize operations to counter side-channel attacks + generate_k_random_mpz(randk, curve->order); + + // k = k * rand mod n + mpz_mul(k, k, randk); + mpz_mod(k, k, curve->order); + + // k = (k * rand)^-1 + mpz_invert(k, k, curve->order); + + mpz_import(*s, 32, 1, 1, 1, 0, priv_key); + // R.x*priv + mpz_mul(*s, *s, R.x); + mpz_mod(*s, *s, curve->order); + mpz_add(*s, *s, z); // R.x*priv + z + + // (k*rand)^-1 (R.x*priv + z) + mpz_mul(*s, *s, k); + mpz_mod(*s, *s, curve->order); + + // k^-1 (R.x*priv + z) + mpz_mul(*s, *s, randk); + mpz_mod(*s, *s, curve->order); + + // if s is zero, we retry + if ((mpz_cmp_ui(*s, 0) == 0)) { + continue; + } + + // if S > order/2 => S = -S + // if ((mpz_cmp(curve->order_half, *s) < 0)) { + // mpz_sub(*s, curve->order, *s); + // } + // we are done, R.x and s is the result signature + mpz_to_byte_array(R.x, sig, 32); + mpz_to_byte_array(*s, sig + 32, 32); + + mpz_clear(k); + mpz_clear(randk); + mpz_clear(z); + +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return 0; + } + + // Too many retries without a valid signature + // -> fail with an error + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return -1; +} diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.h b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h index 9b97f4c5..a42cdd09 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.h +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h @@ -12,6 +12,8 @@ * INCLUDES *****************************************************************************/ +#include + #include "mini-gmp.h" /***************************************************************************** @@ -147,4 +149,13 @@ int mpz_curve_point_is_equal(const mpz_curve_point *p, int mpz_curve_point_is_negative_of(const mpz_curve_point *p, const mpz_curve_point *q); +/** + * @brief Generates ecdsa signature on mpz curve; currently configured for stark + * curves(f251) + */ +int starknet_sign_digest(const mpz_curve *curve, + const uint8_t *priv_key, + const uint8_t *digest, + uint8_t *sig); + #endif // MPZ_ECDSA_H \ No newline at end of file From bf7396f020585ffdd9809abb4e618cb0803da94c Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Mon, 23 Dec 2024 16:46:58 +0530 Subject: [PATCH 19/26] chore: Review changes --- apps/starknet_app/starkcurve.h | 113 +++++++++++++++++ apps/starknet_app/starknet_crypto.c | 119 +++--------------- apps/starknet_app/starknet_helpers.c | 44 +++---- apps/starknet_app/starknet_pedersen.c | 2 +- apps/starknet_app/starknet_pedersen.h | 3 +- apps/starknet_app/starknet_pub_key.c | 24 ++-- .../crypto/mpz_operations/mpz_ecdsa.c | 28 ++++- .../crypto/mpz_operations/mpz_pedersen.c | 14 ++- .../crypto/mpz_operations/mpz_pedersen.h | 2 +- 9 files changed, 204 insertions(+), 145 deletions(-) create mode 100644 apps/starknet_app/starkcurve.h diff --git a/apps/starknet_app/starkcurve.h b/apps/starknet_app/starkcurve.h new file mode 100644 index 00000000..26021286 --- /dev/null +++ b/apps/starknet_app/starkcurve.h @@ -0,0 +1,113 @@ +/** + * @file starkcurve.h + * @author Cypherock X1 Team + * @brief Constants for Starknet curve. + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 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 + *****************************************************************************/ + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ +#define SIZE_HEX 16 + +// Starknet curve constants +#define STARKNET_CURVE_PRIME \ + "0800000000000011000000000000000000000000000000000000000000000001" +#define STARKNET_CURVE_GX \ + "01EF15C18599971B7BECED415A40F0C7DEACFD9B0D1819E03D723D8BC943CFCA" +#define STARKNET_CURVE_GY \ + "005668060AA49730B7BE4801DF46EC62DE53ECD11ABE43A32873000C36E8DC1F" +#define STARKNET_CURVE_ORDER \ + "0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f" +#define STARKNET_CURVE_ORDER_HALF \ + "04000000000000087fffffffffffffffdbc08936e573d9190f335120d6e32697" +#define STARKNET_CURVE_A \ + "0000000000000000000000000000000000000000000000000000000000000001" +#define STARKNET_CURVE_B \ + "06f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89" + +// starknet pedersen points +// Ref: https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html +#define STARKNET_PEDERSEN_POINT_0_X \ + "049EE3EBA8C1600700EE1B87EB599F16716B0B1022947733551FDE4050CA6804" +#define STARKNET_PEDERSEN_POINT_0_Y \ + "03CA0CFE4B3BC6DDF346D49D06EA0ED34E621062C0E056C1D0405D266E10268A" +#define STARKNET_PEDERSEN_POINT_1_X \ + "0234287DCBAFFE7F969C748655FCA9E58FA8120B6D56EB0C1080D17957EBE47B" +#define STARKNET_PEDERSEN_POINT_1_Y \ + "03B056F100F96FB21E889527D41F4E39940135DD7A6C94CC6ED0268EE89E5615" +#define STARKNET_PEDERSEN_POINT_2_X \ + "04FA56F376C83DB33F9DAB2656558F3399099EC1DE5E3018B7A6932DBA8AA378" +#define STARKNET_PEDERSEN_POINT_2_Y \ + "03FA0984C931C9E38113E0C0E47E4401562761F92A7A23B45168F4E80FF5B54D" +#define STARKNET_PEDERSEN_POINT_3_X \ + "04BA4CC166BE8DEC764910F75B45F74B40C690C74709E90F3AA372F0BD2D6997" +#define STARKNET_PEDERSEN_POINT_3_Y \ + "040301CF5C1751F4B971E46C4EDE85FCAC5C59A5CE5AE7C48151F27B24B219C" +#define STARKNET_PEDERSEN_POINT_4_X \ + "054302DCB0E6CC1C6E44CCA8F61A63BB2CA65048D53FB325D36FF12C49A58202" +#define STARKNET_PEDERSEN_POINT_4_Y \ + "01B77B3E37D13504B348046268D8AE25CE98AD783C25561A879DCC77E99C2426" + +// starknet limit +#define STARKNET_LIMIT \ + "F80000000000020EFFFFFFFFFFFFFFF738A13B4B920E9411AE6DA5F40B0358B1" diff --git a/apps/starknet_app/starknet_crypto.c b/apps/starknet_app/starknet_crypto.c index d844ae51..0d2be1e7 100644 --- a/apps/starknet_app/starknet_crypto.c +++ b/apps/starknet_app/starknet_crypto.c @@ -61,6 +61,7 @@ *****************************************************************************/ #include "starknet_crypto.h" +#include #include #include "mini-gmp.h" @@ -118,54 +119,18 @@ static void stark_curve_init() { mpz_init(stark256.a); mpz_init(stark256.b); - // Prime - mpz_set_str( - stark256.prime, - "0800000000000011000000000000000000000000000000000000000000000001", - 16); - - // Generator_point x - mpz_set_str( - stark256.G.x, - "01EF15C18599971B7BECED415A40F0C7DEACFD9B0D1819E03D723D8BC943CFCA", - 16); - - // Generator_point y - mpz_set_str( - stark256.G.y, - "005668060AA49730B7BE4801DF46EC62DE53ECD11ABE43A32873000C36E8DC1F", - 16); - - // Order - mpz_set_str( - stark256.order, - "0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f", - 16); - - // Order half - mpz_set_str( - stark256.order_half, - "04000000000000087fffffffffffffffdbc08936e573d9190f335120d6e32697", - 16); - - // Alpha - mpz_set_str( - stark256.a, - "0000000000000000000000000000000000000000000000000000000000000001", - 16); - - // Beta - mpz_set_str( - stark256.b, - "06f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89", - 16); + mpz_set_str(stark256.prime, STARKNET_CURVE_PRIME, SIZE_HEX); + mpz_set_str(stark256.G.x, STARKNET_CURVE_GX, SIZE_HEX); + mpz_set_str(stark256.G.y, STARKNET_CURVE_GY, SIZE_HEX); + mpz_set_str(stark256.order, STARKNET_CURVE_ORDER, SIZE_HEX); + mpz_set_str(stark256.order_half, STARKNET_CURVE_ORDER_HALF, SIZE_HEX); + mpz_set_str(stark256.a, STARKNET_CURVE_A, SIZE_HEX); + mpz_set_str(stark256.b, STARKNET_CURVE_B, SIZE_HEX); stark_curve = &stark256; } static void stark_pedersen_init() { - // Ref: https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html - static mpz_pedersen pedersen; // Initialize all mpz_t variables in the pedersen structure for (int i = 0; i < 5; i++) { @@ -173,64 +138,16 @@ static void stark_pedersen_init() { mpz_init(pedersen.P[i].y); } - // Shift_point x - mpz_set_str( - pedersen.P[0].x, - "049EE3EBA8C1600700EE1B87EB599F16716B0B1022947733551FDE4050CA6804", - 16); - - // Shift_point y - mpz_set_str( - pedersen.P[0].y, - "03CA0CFE4B3BC6DDF346D49D06EA0ED34E621062C0E056C1D0405D266E10268A", - 16); - - // Pedersen_point_1 x - mpz_set_str( - pedersen.P[1].x, - "0234287DCBAFFE7F969C748655FCA9E58FA8120B6D56EB0C1080D17957EBE47B", - 16); - - // Pedersen_point_1 y - mpz_set_str( - pedersen.P[1].y, - "03B056F100F96FB21E889527D41F4E39940135DD7A6C94CC6ED0268EE89E5615", - 16); - - // Pedersen_point_2 x - mpz_set_str( - pedersen.P[2].x, - "04FA56F376C83DB33F9DAB2656558F3399099EC1DE5E3018B7A6932DBA8AA378", - 16); - - // Pedersen_point_2 y - mpz_set_str( - pedersen.P[2].y, - "03FA0984C931C9E38113E0C0E47E4401562761F92A7A23B45168F4E80FF5B54D", - 16); - - // Pedersen_point_3 x - mpz_set_str( - pedersen.P[3].x, - "04BA4CC166BE8DEC764910F75B45F74B40C690C74709E90F3AA372F0BD2D6997", - 16); - - // Pedersen_point_3 y - mpz_set_str(pedersen.P[3].y, - "040301CF5C1751F4B971E46C4EDE85FCAC5C59A5CE5AE7C48151F27B24B219C", - 16); - - // Pedersen_point_4 x - mpz_set_str( - pedersen.P[4].x, - "054302DCB0E6CC1C6E44CCA8F61A63BB2CA65048D53FB325D36FF12C49A58202", - 16); - - // Pedersen_point_4 y - mpz_set_str( - pedersen.P[4].y, - "01B77B3E37D13504B348046268D8AE25CE98AD783C25561A879DCC77E99C2426", - 16); + mpz_set_str(pedersen.P[0].x, STARKNET_PEDERSEN_POINT_0_X, SIZE_HEX); + mpz_set_str(pedersen.P[0].y, STARKNET_PEDERSEN_POINT_0_Y, SIZE_HEX); + mpz_set_str(pedersen.P[1].x, STARKNET_PEDERSEN_POINT_1_X, SIZE_HEX); + mpz_set_str(pedersen.P[1].y, STARKNET_PEDERSEN_POINT_1_Y, SIZE_HEX); + mpz_set_str(pedersen.P[2].x, STARKNET_PEDERSEN_POINT_2_X, SIZE_HEX); + mpz_set_str(pedersen.P[2].y, STARKNET_PEDERSEN_POINT_2_Y, SIZE_HEX); + mpz_set_str(pedersen.P[3].x, STARKNET_PEDERSEN_POINT_3_X, SIZE_HEX); + mpz_set_str(pedersen.P[3].y, STARKNET_PEDERSEN_POINT_3_Y, SIZE_HEX); + mpz_set_str(pedersen.P[4].x, STARKNET_PEDERSEN_POINT_4_X, SIZE_HEX); + mpz_set_str(pedersen.P[4].y, STARKNET_PEDERSEN_POINT_4_Y, SIZE_HEX); starknet_pedersen_points = &pedersen; } diff --git a/apps/starknet_app/starknet_helpers.c b/apps/starknet_app/starknet_helpers.c index 9682b8dd..f7f25818 100644 --- a/apps/starknet_app/starknet_helpers.c +++ b/apps/starknet_app/starknet_helpers.c @@ -63,6 +63,7 @@ #include "starknet_helpers.h" #include +#include #include "coin_utils.h" #include "mini-gmp-helpers.h" @@ -105,43 +106,41 @@ static bool grind_key(const uint8_t *grind_seed, uint8_t *out); *****************************************************************************/ bool grind_key(const uint8_t *grind_seed, uint8_t *out) { - uint8_t key[32] = {0}; + uint8_t key[STARKNET_BIGNUM_SIZE] = {0}; mpz_t strk_limit; mpz_t strk_key; - mpz_t stark_order; - // Initialize stark_order - mpz_init_set_str( - stark_order, - "0800000000000010FFFFFFFFFFFFFFFFB781126DCAE7B2321E66A241ADC64D2F", - 16); - - // Initialize strk_limit - mpz_init_set_str( - strk_limit, - "F80000000000020EFFFFFFFFFFFFFFF738A13B4B920E9411AE6DA5F40B0358B1", - 16); + mpz_init_set_str(strk_limit, STARKNET_LIMIT, SIZE_HEX); SHA256_CTX ctx = {0}; mpz_init(strk_key); for (uint8_t itr = 0; itr < 200; itr++) { sha256_Init(&ctx); - sha256_Update(&ctx, grind_seed, 32); + sha256_Update(&ctx, grind_seed, STARKNET_BIGNUM_SIZE); sha256_Update(&ctx, &itr, 1); sha256_Final(&ctx, key); - byte_array_to_mpz(strk_key, key, 32); + byte_array_to_mpz(strk_key, key, STARKNET_BIGNUM_SIZE); if (mpz_cmp(strk_key, strk_limit) == -1) { mpz_t f_key; mpz_init(f_key); - mpz_mod(f_key, strk_key, stark_order); - mpz_to_byte_array(f_key, out, 32); + mpz_mod(f_key, strk_key, stark_curve->order); + mpz_to_byte_array(f_key, out, STARKNET_BIGNUM_SIZE); + + // clear mpz variables + mpz_clear(f_key); + mpz_clear(strk_key); + mpz_clear(strk_limit); return true; } } starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 0); LOG_CRITICAL("ERROR: grind 200 iterations failed\n"); + + // clear mpz variables + mpz_clear(strk_key); + mpz_clear(strk_limit); return false; } @@ -184,27 +183,28 @@ bool starknet_derive_key_from_seed(const uint8_t *seed, return false; } - uint8_t stark_private_key[32] = {0}; + uint8_t stark_private_key[STARKNET_BIGNUM_SIZE] = {0}; mpz_curve_point p; mpz_curve_point_init(&p); if (!grind_key(stark_child_node.private_key, stark_private_key)) { + mpz_curve_point_clear(&p); return false; } // copy stark priv key if required if (key_priv != NULL) { - memzero(key_priv, 32); - memcpy(key_priv, stark_private_key, 32); + memzero(key_priv, STARKNET_BIGNUM_SIZE); + memcpy(key_priv, stark_private_key, STARKNET_BIGNUM_SIZE); } // derive stark pub key from stark priv key mpz_t priv_key; mpz_init(priv_key); - byte_array_to_mpz(priv_key, stark_private_key, 32); + byte_array_to_mpz(priv_key, stark_private_key, STARKNET_BIGNUM_SIZE); mpz_curve_point_multiply(stark_curve, priv_key, &stark_curve->G, &p); mpz_clear(priv_key); // clear priv key when no longer required - uint8_t stark_public_key[32] = {0}; + uint8_t stark_public_key[STARKNET_BIGNUM_SIZE] = {0}; mpz_to_byte_array(p.x, stark_public_key, STARKNET_PUB_KEY_SIZE); // copy stark pub key if required diff --git a/apps/starknet_app/starknet_pedersen.c b/apps/starknet_app/starknet_pedersen.c index 48cccc0b..5fbc6aa6 100644 --- a/apps/starknet_app/starknet_pedersen.c +++ b/apps/starknet_app/starknet_pedersen.c @@ -123,7 +123,7 @@ void compute_hash_on_elements(uint8_t data[][STARKNET_BIGNUM_SIZE], pederson_hash(result, data[index], STARKNET_BIGNUM_SIZE, result); } - uint8_t num_elem_bn[32]; + uint8_t num_elem_bn[STARKNET_BIGNUM_SIZE]; starknet_uli_to_bn_byte_array(num_elem, num_elem_bn); pederson_hash(result, num_elem_bn, STARKNET_BIGNUM_SIZE, result); diff --git a/apps/starknet_app/starknet_pedersen.h b/apps/starknet_app/starknet_pedersen.h index 07c88ed0..ec30f59a 100644 --- a/apps/starknet_app/starknet_pedersen.h +++ b/apps/starknet_app/starknet_pedersen.h @@ -76,7 +76,8 @@ #define LOW_PART_BYTES (LOW_PART_BITS / 8) #define LOW_PART_MASK ((1ULL << LOW_PART_BITS) - 1) -#define STARKNET_BIGNUM_SIZE 32 +#define STARKNET_BIGNUM_SIZE \ + 32 ///< Max byte size of a bignum in starknet context #define PEDERSEN_HASH_SIZE 32 #define CALL_DATA_PARAMETER_SIZE 3 diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c index 957367d9..e8fd4b05 100644 --- a/apps/starknet_app/starknet_pub_key.c +++ b/apps/starknet_app/starknet_pub_key.c @@ -66,6 +66,7 @@ #include "assert_conf.h" #include "mini-gmp-helpers.h" #include "reconstruct_wallet_flow.h" +#include "starkcurve.h" #include "starknet_api.h" #include "starknet_context.h" #include "starknet_crypto.h" @@ -76,7 +77,6 @@ #include "ui_core_confirm.h" #include "ui_screens.h" #include "wallet_list.h" - /***************************************************************************** * EXTERN VARIABLES *****************************************************************************/ @@ -84,6 +84,11 @@ /***************************************************************************** * PRIVATE MACROS AND DEFINES *****************************************************************************/ +#define STARKNET_CONTRACT_ADDRESS \ + "00000000000000535441524b4e45545f434f4e54524143545f41444452455353" ///< bn + ///< equivalnet + ///< of + ///< 'STARKNET_CONTRACT_ADDRESS' /***************************************************************************** * PRIVATE TYPEDEFS @@ -397,14 +402,9 @@ static void calculate_contract_address_from_hash(const uint8_t *pub_key, compute_hash_on_elements(call_data, CALL_DATA_PARAMETER_SIZE, call_data_hash); uint8_t starknet_contract_address_bn[STARKNET_BIGNUM_SIZE] = {0}; - hex_string_to_byte_array( - "00000000000000535441524b4e45545f434f4e54524143545" - "f41444452455353", ///< bn - ///< equivalnet - ///< of - ///< 'STARKNET_CONTRACT_ADDRESS' - STARKNET_BIGNUM_SIZE * 2, - starknet_contract_address_bn); + hex_string_to_byte_array(STARKNET_CONTRACT_ADDRESS, + STARKNET_BIGNUM_SIZE * 2, + starknet_contract_address_bn); // prepare array of elements for chain hashing uint8_t data[5][STARKNET_BIGNUM_SIZE] = {0}; @@ -427,7 +427,7 @@ static void calculate_contract_address_from_hash(const uint8_t *pub_key, mpz_mod(result_bn, result_bn, addr_bound); - mpz_get_str(addr, 16, result_bn); + mpz_get_str(addr, SIZE_HEX, result_bn); // clear mpz variables mpz_clear(result_bn); @@ -436,10 +436,10 @@ static void calculate_contract_address_from_hash(const uint8_t *pub_key, static void starknet_derive_argent_address(const uint8_t *pub_key, char *addr) { ASSERT(pub_key != NULL); - uint8_t deployer[32] = {0}; + uint8_t deployer[STARKNET_BIGNUM_SIZE] = {0}; starknet_uli_to_bn_byte_array(STARKNET_DEPLOYER_VALUE, deployer); - uint8_t class_hash[32] = {0}; + uint8_t class_hash[STARKNET_BIGNUM_SIZE] = {0}; hex_string_to_byte_array(STARKNET_ARGENT_CLASS_HASH, 64, class_hash); calculate_contract_address_from_hash(pub_key, diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c index 6f96c371..da6a4bb1 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -60,6 +60,9 @@ *****************************************************************************/ #include "mpz_ecdsa.h" +#include + +#include "assert_conf.h" #include "mini-gmp.h" /***************************************************************************** @@ -98,8 +101,6 @@ void mpz_curve_point_clear(mpz_curve_point *p) { // Set cp2 = cp1 void mpz_curve_point_copy(const mpz_curve_point *cp1, mpz_curve_point *cp2) { - mpz_curve_point_init(cp2); - mpz_set(cp2->x, cp1->x); mpz_set(cp2->y, cp1->y); } @@ -115,18 +116,37 @@ void mpz_curve_point_add(const mpz_curve *curve, mpz_init(yr); if (mpz_curve_point_is_infinity(cp1)) { + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); return; } if (mpz_curve_point_is_infinity(cp2)) { mpz_curve_point_copy(cp1, cp2); + // clear mpz vars + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); return; } if (mpz_curve_point_is_equal(cp1, cp2)) { mpz_curve_point_double(curve, cp2); + // clear mpz vars + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); return; } if (mpz_curve_point_is_negative_of(cp1, cp2)) { mpz_curve_point_set_infinity(cp2); + // clear mpz vars + mpz_clear(lambda); + mpz_clear(inv); + mpz_clear(xr); + mpz_clear(yr); return; } @@ -170,9 +190,7 @@ void mpz_curve_point_add(const mpz_curve *curve, void mpz_curve_point_double(const mpz_curve *curve, mpz_curve_point *cp) { // Ref: // https://github.com/starkware-libs/starkex-for-spot-trading/blob/607f0b4ce507e1d95cd018d206a2797f6ba4aab4/src/starkware/crypto/starkware/crypto/signature/math_utils.py - if (mpz_cmp_ui(cp->y, 0) == 0) { - return; - } + ASSERT(mpz_cmp_ui(cp->y, 0) != 0); mpz_t lambda, xr, yr, inv; mpz_init(lambda); diff --git a/common/libraries/crypto/mpz_operations/mpz_pedersen.c b/common/libraries/crypto/mpz_operations/mpz_pedersen.c index c5f2e0b5..5bc15308 100644 --- a/common/libraries/crypto/mpz_operations/mpz_pedersen.c +++ b/common/libraries/crypto/mpz_operations/mpz_pedersen.c @@ -140,7 +140,7 @@ void process_single_element(mpz_t element, mpz_curve_point_clear(&res2); } -bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { +void pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { ASSERT(NULL != x); ASSERT(NULL != y); ASSERT(0 < size); @@ -156,6 +156,12 @@ bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { // Get shift point mpz_curve_point HASH_SHIFT_POINT, P_1, P_2, P_3, P_4; + mpz_curve_point_init(&HASH_SHIFT_POINT); + mpz_curve_point_init(&P_1); + mpz_curve_point_init(&P_2); + mpz_curve_point_init(&P_3); + mpz_curve_point_init(&P_4); + mpz_curve_point_copy(&starknet_pedersen_points->P[0], &HASH_SHIFT_POINT); mpz_curve_point_copy(&starknet_pedersen_points->P[1], &P_1); mpz_curve_point_copy(&starknet_pedersen_points->P[2], &P_2); @@ -187,5 +193,9 @@ bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash) { mpz_clear(b); mpz_clear(result); - return true; + mpz_curve_point_clear(&HASH_SHIFT_POINT); + mpz_curve_point_clear(&P_1); + mpz_curve_point_clear(&P_2); + mpz_curve_point_clear(&P_3); + mpz_curve_point_clear(&P_4); } diff --git a/common/libraries/crypto/mpz_operations/mpz_pedersen.h b/common/libraries/crypto/mpz_operations/mpz_pedersen.h index 326f3d00..4de420d6 100644 --- a/common/libraries/crypto/mpz_operations/mpz_pedersen.h +++ b/common/libraries/crypto/mpz_operations/mpz_pedersen.h @@ -114,6 +114,6 @@ void process_single_element(mpz_t element, ref: https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/ */ -bool pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash); +void pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash); #endif // MPZ_PEDERSEN_H From 9cc6e7209696655ca89269c160fa4859c5a5d86d Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Mon, 23 Dec 2024 18:33:25 +0530 Subject: [PATCH 20/26] chore: Review changes --- apps/starknet_app/starknet_context.h | 2 + apps/starknet_app/starknet_pedersen.c | 1 + apps/starknet_app/starknet_pedersen.h | 2 +- apps/starknet_app/starknet_poseidon.c | 14 ++++++ apps/starknet_app/starknet_poseidon.h | 16 +------ apps/starknet_app/starknet_sign_txn.c | 46 ++++++++++--------- .../crypto/mpz_operations/mpz_ecdsa.c | 26 ++++++++--- .../crypto/mpz_operations/mpz_ecdsa.h | 2 +- 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/apps/starknet_app/starknet_context.h b/apps/starknet_app/starknet_context.h index 5bd731d2..0589ba5a 100644 --- a/apps/starknet_app/starknet_context.h +++ b/apps/starknet_app/starknet_context.h @@ -32,6 +32,8 @@ /// this makes length of 5 with a termination NULL byte #define STARKNET_LONG_NAME_MAX_SIZE 9 +#define STARKNET_BIGNUM_SIZE 32 + /***************************************************************************** * TYPEDEFS *****************************************************************************/ diff --git a/apps/starknet_app/starknet_pedersen.c b/apps/starknet_app/starknet_pedersen.c index 48cccc0b..85125ce3 100644 --- a/apps/starknet_app/starknet_pedersen.c +++ b/apps/starknet_app/starknet_pedersen.c @@ -67,6 +67,7 @@ #include "coin_utils.h" #include "mini-gmp-helpers.h" +#include "starknet_context.h" #include "starknet_helpers.h" /***************************************************************************** diff --git a/apps/starknet_app/starknet_pedersen.h b/apps/starknet_app/starknet_pedersen.h index 07c88ed0..1c16dc3b 100644 --- a/apps/starknet_app/starknet_pedersen.h +++ b/apps/starknet_app/starknet_pedersen.h @@ -64,6 +64,7 @@ #include #include "mpz_pedersen.h" +#include "starknet_context.h" /***************************************************************************** * EXTERN VARIABLES @@ -76,7 +77,6 @@ #define LOW_PART_BYTES (LOW_PART_BITS / 8) #define LOW_PART_MASK ((1ULL << LOW_PART_BITS) - 1) -#define STARKNET_BIGNUM_SIZE 32 #define PEDERSEN_HASH_SIZE 32 #define CALL_DATA_PARAMETER_SIZE 3 diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index d8631cfb..d034de18 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -81,6 +81,20 @@ /***************************************************************************** * PRIVATE MACROS AND DEFINES *****************************************************************************/ +#define DATA_AVAILABILITY_MODE_BITS 32 // 32 bits for data availability mode +#define MAX_AMOUNT_BITS 64 // 64 bits for max_amount +#define MAX_PRICE_PER_UNIT_BITS 128 // 128 bits for max_price_per_unit +#define RESOURCE_VALUE_OFFSET \ + (MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS) // Combined offset +#define L1_GAS_NAME "4c315f474153" // The constant value for L1_GAS_NAME +#define L2_GAS_NAME "4c325f474153" +#define INVOKE_TXN_PREFIX \ + { 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65 } // 0x696e766f6b65; 'INKVOKE' +#define DEPLOY_ACCOUNT_PREFIX \ + { \ + 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, \ + 0x6e, 0x74 \ + } // 0x6465706c6f795f6163636f756e74 'DEPLOY_ACCOUNT' /***************************************************************************** * PRIVATE TYPEDEFS diff --git a/apps/starknet_app/starknet_poseidon.h b/apps/starknet_app/starknet_poseidon.h index 3368d66d..b3c356e1 100644 --- a/apps/starknet_app/starknet_poseidon.h +++ b/apps/starknet_app/starknet_poseidon.h @@ -74,22 +74,8 @@ *****************************************************************************/ /***************************************************************************** - * PRIVATE MACROS AND DEFINES + * MACROS AND DEFINES *****************************************************************************/ -#define DATA_AVAILABILITY_MODE_BITS 32 // 32 bits for data availability mode -#define MAX_AMOUNT_BITS 64 // 64 bits for max_amount -#define MAX_PRICE_PER_UNIT_BITS 128 // 128 bits for max_price_per_unit -#define RESOURCE_VALUE_OFFSET \ - (MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS) // Combined offset -#define L1_GAS_NAME "4c315f474153" // The constant value for L1_GAS_NAME -#define L2_GAS_NAME "4c325f474153" -#define INVOKE_TXN_PREFIX \ - { 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65 } // 0x696e766f6b65; 'INKVOKE' -#define DEPLOY_ACCOUNT_PREFIX \ - { \ - 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, \ - 0x6e, 0x74 \ - } // 0x6465706c6f795f6163636f756e74 'DEPLOY_ACCOUNT' /***************************************************************************** * PRIVATE TYPEDEFS diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c index cedb2a09..d1559339 100644 --- a/apps/starknet_app/starknet_sign_txn.c +++ b/apps/starknet_app/starknet_sign_txn.c @@ -310,20 +310,15 @@ static bool fetch_valid_input(starknet_query_t *query) { default: { // should not reach here; + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + return false; } } - if (1) { - send_response(STARKNET_SIGN_TXN_RESPONSE_UNSIGNED_TXN_ACCEPTED_TAG); - return true; - } else { - starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, - ERROR_DATA_FLOW_INVALID_DATA); - return false; - } - - return false; + send_response(STARKNET_SIGN_TXN_RESPONSE_UNSIGNED_TXN_ACCEPTED_TAG); + return true; } static void stark_amount_get_decimal_str(const uint8_t *byte_array, @@ -385,7 +380,7 @@ static void starknet_get_max_fee(const uint8_t *max_amount_bytes, const uint8_t max_amount_size, const uint8_t *unit_price_bytes, const uint8_t unit_price_size, - uint8_t max_fee[32]) { + uint8_t max_fee[STARKNET_BIGNUM_SIZE]) { // get max fee = max_amount * unit_price mpz_t max_amount, unit_price; mpz_init(max_amount); @@ -395,7 +390,7 @@ static void starknet_get_max_fee(const uint8_t *max_amount_bytes, byte_array_to_mpz(unit_price, unit_price_bytes, unit_price_size); mpz_mul(max_amount, max_amount, unit_price); - mpz_to_byte_array(max_amount, max_fee, 32); + mpz_to_byte_array(max_amount, max_fee, STARKNET_BIGNUM_SIZE); mpz_clear(max_amount); mpz_clear(unit_price); } @@ -403,12 +398,13 @@ static void starknet_get_max_fee(const uint8_t *max_amount_bytes, static bool get_invoke_txn_user_verification() { // verify address char address[100] = "0x"; - if (starknet_txn_context->invoke_txn->calldata.value[4].size != 32) { + if (starknet_txn_context->invoke_txn->calldata.value[4].size != + STARKNET_BIGNUM_SIZE) { return false; } byte_array_to_hex_string( starknet_txn_context->invoke_txn->calldata.value[4].bytes, - 32, + STARKNET_BIGNUM_SIZE, &address[2], sizeof(address)); @@ -440,7 +436,7 @@ static bool get_invoke_txn_user_verification() { memzero(amount_str, sizeof(amount_str)); // calculate l1 max fee - uint8_t max_fee[32] = {0}; + uint8_t max_fee[STARKNET_BIGNUM_SIZE] = {0}; starknet_get_max_fee( starknet_txn_context->invoke_txn->resource_bound.level_1.max_amount.bytes, starknet_txn_context->invoke_txn->resource_bound.level_1.max_amount.size, @@ -449,7 +445,8 @@ static bool get_invoke_txn_user_verification() { starknet_txn_context->invoke_txn->resource_bound.level_1 .max_price_per_unit.size, max_fee); - stark_amount_get_decimal_str(max_fee, 32, amount_str, sizeof(amount_str)); + stark_amount_get_decimal_str( + max_fee, STARKNET_BIGNUM_SIZE, amount_str, sizeof(amount_str)); memzero(display, sizeof(display)); snprintf(display, @@ -470,7 +467,7 @@ static bool get_deploy_txn_user_verification() { char address[100] = "0x"; byte_array_to_hex_string(starknet_txn_context->deploy_txn->contract_address, - 32, + STARKNET_BIGNUM_SIZE, &address[2], sizeof(address)); @@ -482,7 +479,7 @@ static bool get_deploy_txn_user_verification() { char amount_str[100] = ""; // calculate l1 max fee - uint8_t max_fee[32] = {0}; + uint8_t max_fee[STARKNET_BIGNUM_SIZE] = {0}; starknet_get_max_fee( starknet_txn_context->deploy_txn->resource_bounds.level_1.max_amount .bytes, @@ -492,7 +489,8 @@ static bool get_deploy_txn_user_verification() { starknet_txn_context->deploy_txn->resource_bounds.level_1 .max_price_per_unit.size, max_fee); - stark_amount_get_decimal_str(max_fee, 32, amount_str, sizeof(amount_str)); + stark_amount_get_decimal_str( + max_fee, STARKNET_BIGNUM_SIZE, amount_str, sizeof(amount_str)); char display[200] = {'\0'}; snprintf(display, @@ -541,7 +539,7 @@ static bool sign_txn(uint8_t *signature_buffer) { set_app_flow_status(STARKNET_SIGN_TXN_STATUS_SEED_GENERATED); - uint8_t stark_key[32] = {0}; + uint8_t stark_key[STARKNET_BIGNUM_SIZE] = {0}; if (starknet_derive_key_from_seed( seed, starknet_txn_context->init_info.derivation_path, @@ -568,11 +566,15 @@ static bool sign_txn(uint8_t *signature_buffer) { } break; } - uint8_t hash[32] = {0}; + uint8_t hash[STARKNET_BIGNUM_SIZE] = {0}; felt_t_to_hex(hash_felt, hash); // generate signature - starknet_sign_digest(stark_curve, stark_key, hash, signature_buffer); + if (starknet_sign_digest(stark_curve, stark_key, hash, signature_buffer) != + 0) { + starknet_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); + return false; + } memzero(seed, sizeof(seed)); memzero(stark_key, sizeof(stark_key)); diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c index 9719474e..afe1ed96 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -61,6 +61,7 @@ #include "mpz_ecdsa.h" #include +#include #include "mini-gmp-helpers.h" #include "mini-gmp.h" @@ -86,21 +87,21 @@ * STATIC FUNCTIONS *****************************************************************************/ static void mpz_to_bn(bignum256 *bn, const mpz_t mpz) { - uint8_t out[32] = {0}; - mpz_to_byte_array(mpz, out, 32); + uint8_t out[STARKNET_BIGNUM_SIZE] = {0}; + mpz_to_byte_array(mpz, out, STARKNET_BIGNUM_SIZE); bn_read_be(out, bn); } static void bn_to_mpz(mpz_t mpz, const bignum256 *bn) { - uint8_t in[32] = {0}; + uint8_t in[STARKNET_BIGNUM_SIZE] = {0}; bn_write_be(bn, in); - mpz_import(mpz, 32, 1, 1, 1, 0, in); + mpz_import(mpz, STARKNET_BIGNUM_SIZE, 1, 1, 1, 0, in); } // generate K in a deterministic way, according to RFC6979 // http://tools.ietf.org/html/rfc6979 static void generate_k_rfc6979_mpz(mpz_t k, rfc6979_state *state) { - uint8_t buf[32] = {0}; + uint8_t buf[STARKNET_BIGNUM_SIZE] = {0}; generate_rfc6979(buf, state); mpz_import(k, sizeof(buf), 1, 1, 1, 0, buf); memzero(buf, sizeof(buf)); @@ -348,7 +349,7 @@ int starknet_sign_digest(const mpz_curve *curve, rfc6979_state rng = {0}; init_rfc6979(priv_key, digest, &rng); #endif - mpz_import(z, 32, 1, 1, 0, 0, digest); + mpz_import(z, STARKNET_BIGNUM_SIZE, 1, 1, 0, 0, digest); for (i = 0; i < 10000; i++) { #if USE_RFC6979 // generate K deterministically @@ -387,7 +388,7 @@ int starknet_sign_digest(const mpz_curve *curve, // k = (k * rand)^-1 mpz_invert(k, k, curve->order); - mpz_import(*s, 32, 1, 1, 1, 0, priv_key); + mpz_import(*s, STARKNET_BIGNUM_SIZE, 1, 1, 1, 0, priv_key); // R.x*priv mpz_mul(*s, *s, R.x); mpz_mod(*s, *s, curve->order); @@ -414,9 +415,14 @@ int starknet_sign_digest(const mpz_curve *curve, mpz_to_byte_array(R.x, sig, 32); mpz_to_byte_array(*s, sig + 32, 32); + // clear all the temporary variables + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); + mpz_clear(k); mpz_clear(randk); mpz_clear(z); + mpz_curve_point_clear(&R); #if USE_RFC6979 memzero(&rng, sizeof(rng)); @@ -428,6 +434,12 @@ int starknet_sign_digest(const mpz_curve *curve, // -> fail with an error memzero(&k, sizeof(k)); memzero(&randk, sizeof(randk)); + + mpz_clear(k); + mpz_clear(randk); + mpz_clear(z); + mpz_curve_point_clear(&R); + #if USE_RFC6979 memzero(&rng, sizeof(rng)); #endif diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.h b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h index a42cdd09..b0234509 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.h +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h @@ -151,7 +151,7 @@ int mpz_curve_point_is_negative_of(const mpz_curve_point *p, /** * @brief Generates ecdsa signature on mpz curve; currently configured for stark - * curves(f251) + * curves(f252) */ int starknet_sign_digest(const mpz_curve *curve, const uint8_t *priv_key, From f625d0a5bed4a5de619a3a8007e71934436a4996 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Fri, 3 Jan 2025 16:43:03 +0530 Subject: [PATCH 21/26] chore: Update submodule and review changes --- apps/starknet_app/starknet_poseidon.c | 50 +++++++++++-------- common/cypherock-common | 2 +- .../crypto/mpz_operations/mpz_ecdsa.c | 4 +- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/apps/starknet_app/starknet_poseidon.c b/apps/starknet_app/starknet_poseidon.c index d034de18..581d8c9d 100644 --- a/apps/starknet_app/starknet_poseidon.c +++ b/apps/starknet_app/starknet_poseidon.c @@ -72,6 +72,8 @@ #include "mini-gmp-helpers.h" #include "mini-gmp.h" #include "poseidon.h" +#include "starkcurve.h" +#include "starknet_context.h" #include "ui_core_confirm.h" /***************************************************************************** @@ -86,10 +88,15 @@ #define MAX_PRICE_PER_UNIT_BITS 128 // 128 bits for max_price_per_unit #define RESOURCE_VALUE_OFFSET \ (MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS) // Combined offset + #define L1_GAS_NAME "4c315f474153" // The constant value for L1_GAS_NAME #define L2_GAS_NAME "4c325f474153" + +#define INVOKE_TXN_PREFIX_BYTES_SIZE 6 #define INVOKE_TXN_PREFIX \ { 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65 } // 0x696e766f6b65; 'INKVOKE' + +#define DEPLOY_ACCOUNT_PREFIX_BYTES_SIZE 14 #define DEPLOY_ACCOUNT_PREFIX \ { \ 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, \ @@ -229,8 +236,8 @@ static void calculate_invoke_transaction_hash( static void hex_to_felt_t(const uint8_t hex[], const uint8_t hex_size, felt_t felt) { - uint8_t buf[32] = {0}; - memcpy(buf + (32 - hex_size), hex, hex_size); + uint8_t buf[STARKNET_BIGNUM_SIZE] = {0}; + memcpy(buf + (STARKNET_BIGNUM_SIZE - hex_size), hex, hex_size); int offset = 0; for (int i = 0; i < 4; i++) { felt[3 - i] = U64_READ_BE_ARRAY(buf + offset); @@ -239,9 +246,9 @@ static void hex_to_felt_t(const uint8_t hex[], } static void mpz_to_felt(felt_t felt, const mpz_t mpz) { - uint8_t buf[32] = {0}; - mpz_to_byte_array(mpz, buf, 32); - hex_to_felt_t(buf, 32, felt); + uint8_t buf[STARKNET_BIGNUM_SIZE] = {0}; + mpz_to_byte_array(mpz, buf, STARKNET_BIGNUM_SIZE); + hex_to_felt_t(buf, STARKNET_BIGNUM_SIZE, felt); } static void clear_state(felt_t *state, int size) { @@ -258,11 +265,12 @@ static void clear_state(felt_t *state, int size) { static void poseidon_hash_many(const felt_t state[], const uint8_t state_size, felt_t res) { - ASSERT(state_size + 2 < 16); + const uint8_t padded_max_size = 16; ///< max size of buffer to hold states + ASSERT(state_size + 2 < padded_max_size); const uint8_t m = 3, rate = 2; - felt_t padded[16]; ///< TODO: Update with macro - clear_state(padded, 16); + felt_t padded[padded_max_size]; + clear_state(padded, padded_max_size); if (state != NULL) { for (int i = 0; i < state_size; i++) { @@ -305,7 +313,7 @@ static void encode_resource_bounds_l1(const starknet_resource_bounds_t bounds, // MAX_PRICE_PER_UNIT_BITS) + bounds.level_1.max_price_per_unit // L1_GAS_NAME << RESOURCE_VALUE_OFFSET - mpz_set_str(result, L1_GAS_NAME, 16); + mpz_set_str(result, L1_GAS_NAME, SIZE_HEX); mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS @@ -349,7 +357,7 @@ static void encode_resource_bounds_l2(const starknet_resource_bounds_t bounds, // MAX_PRICE_PER_UNIT_BITS) + bounds.level_2.max_price_per_unit // L1_GAS_NAME << RESOURCE_VALUE_OFFSET - mpz_set_str(result, L2_GAS_NAME, 16); + mpz_set_str(result, L2_GAS_NAME, SIZE_HEX); mpz_mul_2exp(result, result, RESOURCE_VALUE_OFFSET); // bounds.level_1.max_amount << MAX_PRICE_PER_UNIT_BITS @@ -388,14 +396,14 @@ static void hash_fee_field(const pb_byte_t tip, encode_resource_bounds_l1(bounds, res_l1); encode_resource_bounds_l2(bounds, res_l2); felt_t tip_felt = {tip, 0, 0, 0}; - - felt_t state[3]; - clear_state(state, 3); + const uint8_t state_size = 3; + felt_t state[state_size]; + clear_state(state, state_size); f251_copy(state[0], tip_felt); f251_copy(state[1], res_l1); f251_copy(state[2], res_l2); - poseidon_hash_many(state, 3, result); + poseidon_hash_many(state, state_size, result); } static void hash_DAMode(const pb_byte_t nonce_DAMode, @@ -451,7 +459,7 @@ static void calculate_transaction_hash_common( f251_copy(state[offset++], transaction_hash_prefix); hex_to_felt_t(version, 1, state[offset++]); - hex_to_felt_t(sender_address, 32, state[offset++]); + hex_to_felt_t(sender_address, STARKNET_BIGNUM_SIZE, state[offset++]); f251_copy(state[offset++], fee_field_hash); felt_t paymaster_data_res = {0}; poseidon_hash_many( @@ -477,9 +485,9 @@ static void calculate_transaction_hash_common( static void calculate_deploy_transaction_hash( const starknet_sign_txn_deploy_account_txn_t *txn, felt_t hash) { - uint8_t hex[14] = DEPLOY_ACCOUNT_PREFIX; + uint8_t hex[DEPLOY_ACCOUNT_PREFIX_BYTES_SIZE] = DEPLOY_ACCOUNT_PREFIX; felt_t transaction_hash_prefix = {0}; - hex_to_felt_t(hex, 14, transaction_hash_prefix); + hex_to_felt_t(hex, DEPLOY_ACCOUNT_PREFIX_BYTES_SIZE, transaction_hash_prefix); // prepare additional data array const uint8_t data_max_count = 3; @@ -500,8 +508,8 @@ static void calculate_deploy_transaction_hash( } poseidon_hash_many(call_data_felt, offset, additional_data[0]); - hex_to_felt_t(txn->class_hash, 32, additional_data[1]); - hex_to_felt_t(txn->salt, 32, additional_data[2]); + hex_to_felt_t(txn->class_hash, STARKNET_BIGNUM_SIZE, additional_data[1]); + hex_to_felt_t(txn->salt, STARKNET_BIGNUM_SIZE, additional_data[2]); calculate_transaction_hash_common(transaction_hash_prefix, txn->tip, @@ -522,9 +530,9 @@ static void calculate_deploy_transaction_hash( static void calculate_invoke_transaction_hash( const starknet_sign_txn_invoke_txn_t *txn, felt_t hash) { - uint8_t hex[6] = INVOKE_TXN_PREFIX; + uint8_t hex[INVOKE_TXN_PREFIX_BYTES_SIZE] = INVOKE_TXN_PREFIX; felt_t transaction_hash_prefix = {0}; - hex_to_felt_t(hex, 6, transaction_hash_prefix); + hex_to_felt_t(hex, INVOKE_TXN_PREFIX_BYTES_SIZE, transaction_hash_prefix); // prepare additional data array const uint8_t data_max_count = 2; diff --git a/common/cypherock-common b/common/cypherock-common index bdaa657f..482a0270 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit bdaa657ff0ff3199fa613c9893ea7b4457ec9150 +Subproject commit 482a0270138f9ad5ecdc4c397bf9738882ca9a56 diff --git a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c index 8be0937a..5b04a85c 100644 --- a/common/libraries/crypto/mpz_operations/mpz_ecdsa.c +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -430,8 +430,8 @@ int starknet_sign_digest(const mpz_curve *curve, // mpz_sub(*s, curve->order, *s); // } // we are done, R.x and s is the result signature - mpz_to_byte_array(R.x, sig, 32); - mpz_to_byte_array(*s, sig + 32, 32); + mpz_to_byte_array(R.x, sig, STARKNET_BIGNUM_SIZE); + mpz_to_byte_array(*s, sig + STARKNET_BIGNUM_SIZE, STARKNET_BIGNUM_SIZE); // clear all the temporary variables memzero(&k, sizeof(k)); From 1bff1dd7b2931178c09d167a29b9d75681750946 Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Fri, 3 Jan 2025 17:47:13 +0530 Subject: [PATCH 22/26] chore: Remove dev build ci --- .github/workflows/ci-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index fadbf883..6be62848 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -88,7 +88,7 @@ jobs: fail-fast: false matrix: firmware: [main] - target: [release, dev] + target: [release] platform: [device, simulator] runs-on: ubuntu-latest From d99c888d239c84b97ef1223eea3c3a1bfd7068ce Mon Sep 17 00:00:00 2001 From: Vaibhav Sethia Date: Fri, 3 Jan 2025 17:56:55 +0530 Subject: [PATCH 23/26] chore: Version bump(v0.6.1538++) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 8117b775..f5562768 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:005:002 +firmware version=000:006:006:002 hardware version=000:001:000:000 magic number=45227A01 From 41d8d786462576f8e7c4e50b461155eae53b2c4a Mon Sep 17 00:00:00 2001 From: Tejasv Sharma Date: Thu, 9 Jan 2025 13:26:04 +0530 Subject: [PATCH 24/26] fix: Apped zeros when converting mpz to string --- apps/starknet_app/starknet_pub_key.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/starknet_app/starknet_pub_key.c b/apps/starknet_app/starknet_pub_key.c index 5e3cbb38..3fce5327 100644 --- a/apps/starknet_app/starknet_pub_key.c +++ b/apps/starknet_app/starknet_pub_key.c @@ -517,8 +517,21 @@ void starknet_get_pub_keys(starknet_query_t *query) { if (STARKNET_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG == which_request) { char address[100] = "0x"; - // Calculate to-be account address - starknet_derive_argent_address(&public_keys[0][0], &address[2]); + { + char raw_address[100] = {0}; + // Calculate to-be account address + starknet_derive_argent_address(&public_keys[0][0], raw_address); + + size_t len = 64 - strnlen(raw_address, sizeof(raw_address)); + if (len < 0) { + len = 0; + } + uint8_t index = 2; + while (len--) { + sprintf(address + index++, "0"); + } + snprintf(address + index, sizeof(address) - index, raw_address); + } if (!core_scroll_page(ui_text_receive_on, address, starknet_send_error)) { return; @@ -537,4 +550,4 @@ void starknet_get_pub_keys(starknet_query_t *query) { } delay_scr_init(ui_text_check_cysync_app, DELAY_TIME); -} \ No newline at end of file +} From 33ca59fa76156f0f59eed77d019a8d3a78fb7e31 Mon Sep 17 00:00:00 2001 From: Tejasv Sharma Date: Thu, 9 Jan 2025 13:29:20 +0530 Subject: [PATCH 25/26] chore: Version bump (patch++) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index f5562768..6903c3fb 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:006:002 +firmware version=000:006:006:003 hardware version=000:001:000:000 magic number=45227A01 From 0eab18034b6c7923c6801aadb002c0317e3db83e Mon Sep 17 00:00:00 2001 From: Tejasv Sharma Date: Thu, 9 Jan 2025 13:56:33 +0530 Subject: [PATCH 26/26] chore: Fix ci - remove initial release ci code --- .github/workflows/build.yml | 54 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93ad88b5..79e0dde2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ on: branches: - release** paths: - - 'version.txt' + - "version.txt" jobs: build-firmwares: @@ -25,30 +25,28 @@ jobs: runs-on: ubuntu-latest if: ${{ github.ref_type }} == 'tag' steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Download artifacts - uses: actions/download-artifact@v3 - with: - path: ./ - - name: Publish a release - env: - TAG_NAME: ${{ github.ref_name }} - auth_token: ${{ secrets.GITHUB_TOKEN }} - REPOSITORY: ${{ github.repository }} - run: | - chkmain=$(sha256sum Main-Release-outputs/Cypherock-Main.bin | cut -f -1 -d ' ') - chkinit=$(sha256sum Initial-Release-outputs/Cypherock-Initial.bin | cut -f -1 -d ' ') - APP_VERSION=$(cat version.txt | grep firmware | cut -f 2-2 -d '=' | awk -F ':' '{ print 0+$1 "." 0+$2 "." $3*2**8 + $4 }') - HW_VERSION=$(cat version.txt | grep hardware | cut -f 2-2 -d '=' | awk -F ':' '{ print 0+$1 "." 0+$2 "." $3*2**8 + $4 }') - echo ${APP_VERSION}:${HW_VERSION} - curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/${REPOSITORY}/releases -d '{"tag_name":"'${TAG_NAME}'","target_commitish":"main","name":"'${TAG_NAME}'","body":"Application version: '${APP_VERSION}'\r\nHardware version: '${HW_VERSION}'\r\n## SHA256 of binaries:\r\n**Cypherock-Initial.bin** : '${chkinit}' \r\n**Cypherock-Main.bin** : '${chkmain}'","draft":true,"prerelease":false,"generate_release_notes":true}' > output.txt - echo "upload_url=$(cat output.txt | grep "\"upload_url\":" | cut -f 4-4 -d '"' | cut -f 1-1 -d '{')" >> $GITHUB_ENV - - name: Upload assets - env: - auth_token: ${{ secrets.GITHUB_TOKEN }} - run: | - content_type=$(file -b --mime-type Main-Release-outputs/Cypherock-Main.bin) - curl -X POST -H "Content-Type: ${content_type}" -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" ${upload_url}?name=Cypherock-Main.bin --data-binary @Main-Release-outputs/Cypherock-Main.bin - curl -X POST -H "Content-Type: ${content_type}" -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" ${upload_url}?name=Cypherock-Initial.bin --data-binary @Initial-Release-outputs/Cypherock-Initial.bin - curl -X POST -H "Content-Type: ${content_type}" -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" ${upload_url}?name=version.txt --data-binary @version.txt + - name: Checkout + uses: actions/checkout@v3 + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + path: ./ + - name: Publish a release + env: + TAG_NAME: ${{ github.ref_name }} + auth_token: ${{ secrets.GITHUB_TOKEN }} + REPOSITORY: ${{ github.repository }} + run: | + chkmain=$(sha256sum Main-Release-outputs/Cypherock-Main.bin | cut -f -1 -d ' ') + APP_VERSION=$(cat version.txt | grep firmware | cut -f 2-2 -d '=' | awk -F ':' '{ print 0+$1 "." 0+$2 "." $3*2**8 + $4 }') + HW_VERSION=$(cat version.txt | grep hardware | cut -f 2-2 -d '=' | awk -F ':' '{ print 0+$1 "." 0+$2 "." $3*2**8 + $4 }') + echo ${APP_VERSION}:${HW_VERSION} + curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/${REPOSITORY}/releases -d '{"tag_name":"'${TAG_NAME}'","target_commitish":"main","name":"'${TAG_NAME}'","body":"Application version: '${APP_VERSION}'\r\nHardware version: '${HW_VERSION}'\r\n## SHA256 of binaries:\r\n**Cypherock-Main.bin** : '${chkmain}'","draft":true,"prerelease":false,"generate_release_notes":true}' > output.txt + echo "upload_url=$(cat output.txt | grep "\"upload_url\":" | cut -f 4-4 -d '"' | cut -f 1-1 -d '{')" >> $GITHUB_ENV + - name: Upload assets + env: + auth_token: ${{ secrets.GITHUB_TOKEN }} + run: | + content_type=$(file -b --mime-type Main-Release-outputs/Cypherock-Main.bin) + curl -X POST -H "Content-Type: ${content_type}" -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" ${upload_url}?name=Cypherock-Main.bin --data-binary @Main-Release-outputs/Cypherock-Main.bin + curl -X POST -H "Content-Type: ${content_type}" -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${auth_token}" -H "X-GitHub-Api-Version: 2022-11-28" ${upload_url}?name=version.txt --data-binary @version.txt