diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93ad88b5b..79e0dde27 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 diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index c7596c29c..6be628488 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 @@ -88,65 +88,70 @@ jobs: fail-fast: false matrix: firmware: [main] - target: [release, dev] + target: [release] platform: [device, simulator] 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 diff --git a/.gitmodules b/.gitmodules index d1c67791a..987188256 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "nanopb"] path = vendor/nanopb url = ../nanopb +[submodule "mini-gmp"] + path = vendor/mini-gmp + url = ../mini-gmp +[submodule "poseidon"] + path = vendor/poseidon + url = ../poseidon diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d5a3e5cc..db642fd01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,9 @@ 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") +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) @@ -49,7 +52,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 vendor/poseidon/sources) # Enable support for dynamically allocated fields in nanopb # Ref: vendor/nanopb/pb.h diff --git a/apps/btc_family/btc_txn.c b/apps/btc_family/btc_txn.c index 267e72d87..570a8cf71 100644 --- a/apps/btc_family/btc_txn.c +++ b/apps/btc_family/btc_txn.c @@ -447,9 +447,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 772cf2acb..47f793907 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 3555b0139..fb4893ffc 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/apps/starknet_app/starkcurve.h b/apps/starknet_app/starkcurve.h new file mode 100644 index 000000000..260212861 --- /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_api.c b/apps/starknet_app/starknet_api.c new file mode 100644 index 000000000..c2f4bd815 --- /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 000000000..5f2bbceaf --- /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 000000000..dc4a77d49 --- /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 000000000..0589ba5a6 --- /dev/null +++ b/apps/starknet_app/starknet_context.h @@ -0,0 +1,61 @@ +/** + * @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 + +#define STARKNET_BIGNUM_SIZE 32 + +/***************************************************************************** + * 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 000000000..0d2be1e7b --- /dev/null +++ b/apps/starknet_app/starknet_crypto.c @@ -0,0 +1,176 @@ +/** + * @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 +#include + +#include "mini-gmp.h" +#include "mpz_ecdsa.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * 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 + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ +mpz_curve *stark_curve; +mpz_pedersen *starknet_pedersen_points; + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static void stark_curve_init() { + static mpz_curve stark256; + + // 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); + + 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() { + 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); + mpz_init(pedersen.P[i].y); + } + + 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; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void starknet_init() { + stark_curve_init(); + stark_pedersen_init(); +} + +void stark_clear() { + // clear pedersen points + for (int i = 0; i < 5; i++) { + mpz_curve_point_clear(&starknet_pedersen_points->P[i]); + } + // 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 new file mode 100644 index 000000000..418e5aec0 --- /dev/null +++ b/apps/starknet_app/starknet_crypto.h @@ -0,0 +1,47 @@ +/** + * @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 "mpz_ecdsa.h" +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ +extern mpz_curve *stark_curve; +extern mpz_pedersen *starknet_pedersen_points; + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Initialize STARKNET context. + * @details Sets up resources and state for STARKNET operations. + */ +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 new file mode 100644 index 000000000..f7f258189 --- /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 + +#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[STARKNET_BIGNUM_SIZE] = {0}; + mpz_t strk_limit; + mpz_t strk_key; + + 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, STARKNET_BIGNUM_SIZE); + sha256_Update(&ctx, &itr, 1); + sha256_Final(&ctx, key); + + 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_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; +} + +/***************************************************************************** + * 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[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, 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, 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[STARKNET_BIGNUM_SIZE] = {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 + mpz_curve_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 000000000..063e7721b --- /dev/null +++ b/apps/starknet_app/starknet_helpers.h @@ -0,0 +1,65 @@ +/** + * @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 + +#include "starknet_pedersen.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * 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); + +#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 000000000..a0283bd9f --- /dev/null +++ b/apps/starknet_app/starknet_main.c @@ -0,0 +1,151 @@ +/** + * @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" +#include "ui_core_confirm.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 000000000..c8b20d352 --- /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 000000000..b05ab2074 --- /dev/null +++ b/apps/starknet_app/starknet_pedersen.c @@ -0,0 +1,135 @@ +/** + * @file starknet_pedersen.c + * @author Cypherock X1 Team + * @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/ + * + ****************************************************************************** + * @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_pedersen.h" + +#include +#include + +#include "coin_utils.h" +#include "mini-gmp-helpers.h" +#include "starknet_context.h" +#include "starknet_helpers.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * 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); + + // clear mpz vars + mpz_clear(bn); +} + +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[STARKNET_BIGNUM_SIZE]; + 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_pedersen.h b/apps/starknet_app/starknet_pedersen.h new file mode 100644 index 000000000..1c16dc3be --- /dev/null +++ b/apps/starknet_app/starknet_pedersen.h @@ -0,0 +1,115 @@ +/** + * @file starknet_pedersen.h + * @author Cypherock X1 Team + * @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/ + * + ****************************************************************************** + * @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 "mpz_pedersen.h" +#include "starknet_context.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 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 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_poseidon.c b/apps/starknet_app/starknet_poseidon.c new file mode 100644 index 000000000..581d8c9de --- /dev/null +++ b/apps/starknet_app/starknet_poseidon.c @@ -0,0 +1,607 @@ +/** + * @file starknet_poseidon.c + * @author Cypherock X1 Team + * @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/ + * + ****************************************************************************** + * @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" +#include "starkcurve.h" +#include "starknet_context.h" +#include "ui_core_confirm.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 "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, \ + 0x6e, 0x74 \ + } // 0x6465706c6f795f6163636f756e74 'DEPLOY_ACCOUNT' + +/***************************************************************************** + * 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_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); + +/** + * @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[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); + offset += 8; + } +} + +static void mpz_to_felt(felt_t felt, const mpz_t mpz) { + 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) { + 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) { + 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[padded_max_size]; + clear_state(padded, padded_max_size); + + 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_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 + 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, + 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, + 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_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 + 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, + 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, + 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}; + 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, state_size, 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_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) { + 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, STARKNET_BIGNUM_SIZE, 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, 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++) { + 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[DEPLOY_ACCOUNT_PREFIX_BYTES_SIZE] = DEPLOY_ACCOUNT_PREFIX; + felt_t transaction_hash_prefix = {0}; + hex_to_felt_t(hex, DEPLOY_ACCOUNT_PREFIX_BYTES_SIZE, 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, 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, + txn->resource_bounds, + txn->nonce_data_availability_mode, + txn->fee_data_availability_mode, + txn->version, + txn->contract_address, + txn->chain_id.bytes, + txn->chain_id.size, + txn->nonce.bytes, + txn->nonce.size, + additional_data, + 3, + hash); +} + +static void calculate_invoke_transaction_hash( + const starknet_sign_txn_invoke_txn_t *txn, + felt_t hash) { + uint8_t hex[INVOKE_TXN_PREFIX_BYTES_SIZE] = INVOKE_TXN_PREFIX; + felt_t transaction_hash_prefix = {0}; + hex_to_felt_t(hex, INVOKE_TXN_PREFIX_BYTES_SIZE, 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.bytes, + txn->chain_id.size, + txn->nonce.bytes, + txn->nonce.size, + 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 000000000..b3c356e10 --- /dev/null +++ b/apps/starknet_app/starknet_poseidon.h @@ -0,0 +1,114 @@ +/** + * @file starknet_poseidon.h + * @author Cypherock X1 Team + * @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/ + * + ****************************************************************************** + * @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 + *****************************************************************************/ + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * 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 new file mode 100644 index 000000000..bf1163b9f --- /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 000000000..3fce53277 --- /dev/null +++ b/apps/starknet_app/starknet_pub_key.c @@ -0,0 +1,553 @@ +/** + * @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 "starkcurve.h" +#include "starknet_api.h" +#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" +#include "ui_screens.h" +#include "wallet_list.h" +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ +#define STARKNET_CONTRACT_ADDRESS \ + "00000000000000535441524b4e45545f434f4e54524143545f41444452455353" ///< bn + ///< equivalnet + ///< of + ///< 'STARKNET_CONTRACT_ADDRESS' + +/***************************************************************************** + * 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(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, SIZE_HEX, 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[STARKNET_BIGNUM_SIZE] = {0}; + starknet_uli_to_bn_byte_array(STARKNET_DEPLOYER_VALUE, deployer); + + 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, + deployer, + pub_key, ///< salt = public key + class_hash, + addr); + return; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ +void starknet_get_pub_keys(starknet_query_t *query) { + 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); + + // Initialize starknet context + starknet_init(); + 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"; + + { + 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; + } + + set_app_flow_status(STARKNET_GET_PUBLIC_KEYS_STATUS_VERIFY); + } + stark_clear(); + + 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); +} diff --git a/apps/starknet_app/starknet_sign_txn.c b/apps/starknet_app/starknet_sign_txn.c new file mode 100644 index 000000000..d1559339a --- /dev/null +++ b/apps/starknet_app/starknet_sign_txn.c @@ -0,0 +1,629 @@ +/** + * @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 "mpz_ecdsa.h" +#include "rand.h" +#include "reconstruct_wallet_flow.h" +#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" +#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); + +/** + * @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 + *****************************************************************************/ +static starknet_txn_context_t *starknet_txn_context = NULL; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +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; + starknet_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); + + 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, + 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"); + // stop if more than 6 zeros (value too low) + if (i == (2 + 6)) { + break; + } + } + uint8_t offset = 0; + for (; i < 6 + 2; i++) { ///< append till reach 6th char + 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 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[STARKNET_BIGNUM_SIZE]) { + // 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, STARKNET_BIGNUM_SIZE); + 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 != + STARKNET_BIGNUM_SIZE) { + return false; + } + byte_array_to_hex_string( + starknet_txn_context->invoke_txn->calldata.value[4].bytes, + STARKNET_BIGNUM_SIZE, + &address[2], + sizeof(address)); + + if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { + return false; + } + + // 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; + } + + // Verify gas fee + memzero(amount_str, sizeof(amount_str)); + + // calculate l1 max fee + 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, + 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, STARKNET_BIGNUM_SIZE, 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"; + + byte_array_to_hex_string(starknet_txn_context->deploy_txn->contract_address, + STARKNET_BIGNUM_SIZE, + &address[2], + sizeof(address)); + + if (!core_scroll_page(ui_text_verify_address, address, starknet_send_error)) { + return false; + } + + // Verify gas fee + char amount_str[100] = ""; + + // calculate l1 max fee + uint8_t max_fee[STARKNET_BIGNUM_SIZE] = {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, STARKNET_BIGNUM_SIZE, 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; + } + + 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[STARKNET_BIGNUM_SIZE] = {0}; + if (starknet_derive_key_from_seed( + seed, + 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[STARKNET_BIGNUM_SIZE] = {0}; + felt_t_to_hex(hash_felt, hash); + + // generate signature + 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)); + + 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)); + stark_clear(); + + if (starknet_txn_context) { + free(starknet_txn_context); + starknet_txn_context = NULL; + } + + return; +} \ No newline at end of file diff --git a/common/core/app_registry.h b/common/core/app_registry.h index a064ea624..243f05b21 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 31e414f4b..1faddf4f6 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 19912e8d0..482a02701 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 19912e8d052064c477d6b11ca2e26efa4c483bfe +Subproject commit 482a0270138f9ad5ecdc4c397bf9738882ca9a56 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 000000000..5b04a85c8 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.c @@ -0,0 +1,465 @@ +/* + * @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 +#include + +#include "mini-gmp-helpers.h" +#include + +#include "assert_conf.h" +#include "mini-gmp.h" +#include "rfc6979.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static void mpz_to_bn(bignum256 *bn, const mpz_t mpz) { + 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[STARKNET_BIGNUM_SIZE] = {0}; + bn_write_be(bn, 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[STARKNET_BIGNUM_SIZE] = {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 FUNCTIONS + *****************************************************************************/ + +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_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)) { + 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; + } + + // 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 + ASSERT(mpz_cmp_ui(cp->y, 0) != 0); + + 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); +} + +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, STARKNET_BIGNUM_SIZE, 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, STARKNET_BIGNUM_SIZE, 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, STARKNET_BIGNUM_SIZE); + mpz_to_byte_array(*s, sig + STARKNET_BIGNUM_SIZE, STARKNET_BIGNUM_SIZE); + + // 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)); +#endif + return 0; + } + + // Too many retries without a valid signature + // -> 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 + return -1; +} 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 000000000..b02345091 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_ecdsa.h @@ -0,0 +1,161 @@ +/** + * @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 + +#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); + +/** + * @brief Generates ecdsa signature on mpz curve; currently configured for stark + * curves(f252) + */ +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 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 000000000..5bc153089 --- /dev/null +++ b/common/libraries/crypto/mpz_operations/mpz_pedersen.c @@ -0,0 +1,201 @@ + +/* + * @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); +} + +void 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_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); + 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); + + 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 new file mode 100644 index 000000000..4de420d61 --- /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/ + */ +void pederson_hash(uint8_t *x, uint8_t *y, uint8_t size, uint8_t *hash); + +#endif // MPZ_PEDERSEN_H diff --git a/common/proto-options/starknet/core.options b/common/proto-options/starknet/core.options new file mode 100644 index 000000000..ea880b80d --- /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 000000000..386b9e2cc --- /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/common/proto-options/starknet/sign_txn.options b/common/proto-options/starknet/sign_txn.options new file mode 100644 index 000000000..d0c9942ac --- /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: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: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: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 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 max_count:1 fixed_length:false #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: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 +starknet.SignTxnDeployAccountTxn.tip type:FT_STATIC max_size:1 fixed_length:true +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 diff --git a/tests/apps/btc_app/btc_txn_helpers_tests.c b/tests/apps/btc_app/btc_txn_helpers_tests.c index 18d6a6bb9..95ef429d0 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 572649652..4b0f93f24 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); diff --git a/utilities/cmake/firmware/firmware.cmake b/utilities/cmake/firmware/firmware.cmake index 08ba9d38f..f0b8a837d 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 ${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) @@ -59,7 +59,7 @@ target_include_directories(${EXECUTABLE} PRIVATE apps/solana_app apps/tron_app apps/inheritance_app - + apps/starknet_app apps/xrp_app @@ -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 12894a478..21d5fa8c4 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 ${MINI_GMP_SRCS} ${POSEIDON_SRCS} ${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 @@ -98,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 diff --git a/vendor/mini-gmp b/vendor/mini-gmp new file mode 160000 index 000000000..cced8f79a --- /dev/null +++ b/vendor/mini-gmp @@ -0,0 +1 @@ +Subproject commit cced8f79a071b37a2d9f185d0c2c893e27a92bbd diff --git a/vendor/poseidon b/vendor/poseidon new file mode 160000 index 000000000..d2148fe3c --- /dev/null +++ b/vendor/poseidon @@ -0,0 +1 @@ +Subproject commit d2148fe3cb2513c39bf25ab50689be5d0683dc02 diff --git a/version.txt b/version.txt index 8117b7754..6903c3fba 100755 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -firmware version=000:006:005:002 +firmware version=000:006:006:003 hardware version=000:001:000:000 magic number=45227A01