diff --git a/.clang-format b/.clang-format index a9d6f7a3..570f6354 100644 --- a/.clang-format +++ b/.clang-format @@ -6,6 +6,7 @@ Language: Cpp ColumnLimit: 100 PointerAlignment: Right AlignAfterOpenBracket: Align +AllowShortFunctionsOnASingleLine: None AlignConsecutiveMacros: true AllowAllParametersOfDeclarationOnNextLine: false SortIncludes: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..f2fb620f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +# Checklist + +- [ ] App update process has been followed +- [ ] Target branch is `develop` +- [ ] Application version has been bumped + + diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml new file mode 100644 index 00000000..946c039e --- /dev/null +++ b/.github/workflows/build_and_functional_tests.yml @@ -0,0 +1,33 @@ +name: Build and run functional tests using ragger through reusable workflow + +# This workflow will build the app and then run functional tests using the Ragger framework upon Speculos emulation. +# It calls a reusable workflow developed by Ledger's internal developer team to build the application and upload the +# resulting binaries. +# It then calls another reusable workflow to run the Ragger tests on the compiled application binary. +# +# While this workflow is optional, having functional testing on your application is mandatory and this workflow and +# tooling environment is meant to be easy to use and adapt after forking your application + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + flags: "DEBUG=1" + + ragger_tests: + name: Run ragger tests using the reusable workflow + needs: build_application + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1 + with: + download_app_binaries_artifact: "compiled_app_binaries" diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml deleted file mode 100644 index 53003417..00000000 --- a/.github/workflows/ci-workflow.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Compilation - -on: - push: - branches: - - master - - develop - pull_request: - branches: - - master - - develop - -jobs: - job_build_debug: - name: Build debug - runs-on: ubuntu-latest - - container: - image: docker://ledgerhq/ledger-app-builder:1.6.0 - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Build - run: | - make DEBUG=1 - - - name: Upload app binary - uses: actions/upload-artifact@v2 - with: - name: monero-app-debug - path: bin - - job_test: - name: Test - needs: job_build_debug - runs-on: ubuntu-latest - - container: - image: docker://ledgerhq/speculos:latest - ports: - - 1234:1234 - - 9999:9999 - - 40000:40000 - - 41000:41000 - - 42000:42000 - - 43000:43000 - options: --entrypoint /bin/bash - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Download app binary - uses: actions/download-artifact@v2 - with: - name: monero-app-debug - path: bin - - - name: Run test - run: | - nohup bash -c "python /speculos/speculos.py bin/app.elf --sdk 1.6 --seed \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\" --apdu-port 9999 --button-port 42000 --automation-port 43000 --display headless" > speculos.log 2<&1 & - pip install pytest - pytest - - - name: Upload Speculos log - uses: actions/upload-artifact@v2 - with: - name: speculos-log - path: speculos.log diff --git a/.github/workflows/guidelines_enforcer.yml b/.github/workflows/guidelines_enforcer.yml new file mode 100644 index 00000000..fdaf9f27 --- /dev/null +++ b/.github/workflows/guidelines_enforcer.yml @@ -0,0 +1,23 @@ +name: Ensure compliance with Ledger guidelines + +# This workflow is mandatory in all applications +# It calls a reusable workflow guidelines_enforcer developed by Ledger's internal developer team. +# The successful completion of the reusable workflow is a mandatory step for an app to be available on the Ledger +# application store. +# +# More information on the guidelines can be found in the repository: +# LedgerHQ/ledger-app-workflows/ + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + guidelines_enforcer: + name: Call Ledger guidelines_enforcer + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_guidelines_enforcer.yml@v1 diff --git a/.github/workflows/lint-workflow.yml b/.github/workflows/lint-workflow.yml index 67a20249..3a64caff 100644 --- a/.github/workflows/lint-workflow.yml +++ b/.github/workflows/lint-workflow.yml @@ -1,6 +1,14 @@ name: Code style check -on: [push, pull_request] +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop jobs: job_lint: diff --git a/.gitignore b/.gitignore index a8488aad..30351c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ debug/ dep/ obj/ - # Editors .vscode/ .idea/ @@ -18,3 +17,15 @@ __pycache__/ *.egg-info/ .eggs/ .python-version + +# Pyinstaller +build/ +dist/ +*.manifest +*.spec + +# Temporary directory with snapshots taken during test runs +tests/snapshots-tmp/ + +# Virtual env for sideload (macOS and Windows) +ledger/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eb171d0..5843b37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,40 @@ All notable changes to this project will be documented in this file. +## 2.0.0 - 2024-05-01 + +- Support of Flex device + +## 1.7.6 - 2021-03-30 + +- Support of Monero client version `0.17.2.*` + +## 1.7.5 - 2020-11-16 + +- Support of firmware 1.2.4-5 + +## 1.7.4 - 2020-10-13 + +- Support of Monero client version `0.17.1.*` + +## 1.7.3 - 2020-10-06 + +- Fix garbage when displaying destination address on Nano + +## 1.7.2 - 2020-10-02 + +- Fix behavior without `DEBUG_HWDEVICE` flag + + +## 1.7.1 - 2020-09-24 + +- Fix `clsag_hash()` behavior which is different than MLSAG + +## 1.7.0 - 2020-09-13 + +- Update to protocol v4 to support both MLSAG and CLSAG +- Add CLSAG signature algorithm with `INS_CLSAG` +- Update InProofv1 to InProofv2 ## 1.6.0 - 2020-06-04 diff --git a/Makefile b/Makefile index 0eb34f20..5d012402 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #******************************************************************************* -# Ledger Nano S -# (c) 2016-2019 Ledger -# +# Ledger App Monero +# (c) 2023 Ledger SAS. +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -15,184 +15,82 @@ # limitations under the License. #******************************************************************************* -TARGET_NAME := TARGET_NANOX - --include Makefile.env ifeq ($(BOLOS_SDK),) $(error Environment variable BOLOS_SDK is not set) endif include $(BOLOS_SDK)/Makefile.defines -#Monero /44'/128' -APP_LOAD_PARAMS= --path "2147483692/2147483776" --curve secp256k1 $(COMMON_LOAD_PARAMS) --appFlags 0x240 +######################################## +# Mandatory configuration # +######################################## +# Application name APPNAME = "Monero" -ifeq ($(TARGET_NAME),TARGET_BLUE) -ICONNAME = images/icon_monero_blue.gif -else ifeq ($(TARGET_NAME),TARGET_NANOX) -ICONNAME = images/icon_monero_nanox.gif -else -ICONNAME = images/icon_monero.gif -endif +# Application version +APPVERSION_M = 2 +APPVERSION_N = 0 +APPVERSION_P = 0 +APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" -#DEFINES += MONERO_ALPHA -#DEFINES += MONERO_BETA +SPECVERSION = "1.0" + +# Application source files +APP_SOURCE_PATH += src -APPVERSION_M=1 -APPVERSION_N=6 -APPVERSION_P=0 +# Application icons following guidelines: +# https://developers.ledger.com/docs/embedded-app/design-requirements/#device-icon +ICON_NANOS = icons/app_monero_16px.gif +ICON_NANOX = icons/app_monero_14px.gif +ICON_NANOSP = icons/app_monero_14px.gif +ICON_STAX = icons/app_monero_32px.gif +ICON_FLEX = icons/app_monero_40px.gif -APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) -SPECVERSION="1.0" +# Application allowed derivation curves. +CURVE_APP_LOAD_PARAMS = secp256k1 + +# Application allowed derivation paths. +PATH_APP_LOAD_PARAMS = "44'/128'" + +VARIANT_PARAM = COIN +VARIANT_VALUES = monero + +ENABLE_BLUETOOTH = 1 +ENABLE_NBGL_QRCODE = 1 DEFINES += $(MONERO_CONFIG) DEFINES += MONERO_VERSION_MAJOR=$(APPVERSION_M) MONERO_VERSION_MINOR=$(APPVERSION_N) MONERO_VERSION_MICRO=$(APPVERSION_P) DEFINES += MONERO_VERSION=$(APPVERSION) DEFINES += MONERO_NAME=$(APPNAME) -DEFINES += SPEC_VERSION=$(SPECVERSION) - -ifeq ($(TARGET_NAME),TARGET_NANOX) -DEFINES += UI_NANO_X -TARGET_UI := FLOW -else ifeq ($(TARGET_NAME),TARGET_BLUE) -DEFINES += UI_BLUE -else -DEFINES += UI_NANO_S -endif -#DEFINES += IOCRYPT +DEFINES += SPEC_VERSION=$(SPECVERSION) -################ -# Default rule # -################ +# Enable StageNet by default +#DEFINES += MONERO_ALPHA // This will also disable mainnet +#DEFINES += MONERO_BETA -all: default ############ # Platform # ############ -ifneq ($(NO_CONSENT),) -DEFINES += NO_CONSENT -endif - -DEFINES += OS_IO_SEPROXYHAL -DEFINES += HAVE_BAGL HAVE_SPRINTF -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=4 IO_HID_EP_LENGTH=64 HAVE_USB_APDU DEFINES += CUSTOM_IO_APDU_BUFFER_SIZE=\(255+5+64\) DEFINES += HAVE_LEGACY_PID -DEFINES += USB_SEGMENT_SIZE=64 DEFINES += U2F_PROXY_MAGIC=\"MOON\" DEFINES += HAVE_IO_U2F HAVE_U2F -DEFINES += UNUSED\(x\)=\(void\)x -DEFINES += APPVERSION=\"$(APPVERSION)\" - -ifeq ($(TARGET_NAME),TARGET_NANOX) -# DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 -# DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE - -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 -DEFINES += HAVE_GLO096 -DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 -DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 -DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE -else -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 -endif - -ifeq ($(TARGET_UI),FLOW) -DEFINES += HAVE_UX_FLOW -endif - # Enabling debug PRINTF -DEBUG = 0 ifneq ($(DEBUG),0) - - ifeq ($(TARGET_NAME),TARGET_NANOX) - DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf - else - DEFINES += HAVE_PRINTF PRINTF=screen_printf - endif - DEFINES += PLINE="PRINTF(\"FILE:%s..LINE:%d\n\",__FILE__,__LINE__)" # Debug options DEFINES += DEBUG_HWDEVICE + DEFINES += BYPASS_COMMITMENT_FOR_TESTS DEFINES += IODUMMYCRYPT # or IONOCRYPT # Stagenet network by default DEFINES += MONERO_BETA -else - - DEFINES += PRINTF\(...\)= - DEFINES += PLINE\(...\)= - -endif - - -############## -# Compiler # -############## -ifneq ($(BOLOS_ENV),) -$(info BOLOS_ENV=$(BOLOS_ENV)) -CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ -GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ -else -$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH) endif -ifeq ($(CLANGPATH),) -$(info CLANGPATH is not set: clang will be used from PATH) -endif -ifeq ($(GCCPATH),) -$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) -endif -CC := $(CLANGPATH)clang - -#CFLAGS += -O0 -gdwarf-2 -gstrict-dwarf -CFLAGS += -O3 -Os -#CFLAGS += -fno-jump-tables -fno-lookup-tables -fsave-optimization-record -#$(info $(CFLAGS)) - -AS := $(GCCPATH)arm-none-eabi-gcc - -LD := $(GCCPATH)arm-none-eabi-gcc -#SCRIPT_LD:=script.ld -#LDFLAGS += -O0 -gdwarf-2 -gstrict-dwarf -LDFLAGS += -O3 -Os -LDLIBS += -lm -lgcc -lc - -# import rules to compile glyphs(/pone) -include $(BOLOS_SDK)/Makefile.glyphs ### variables processed by the common makefile.rules of the SDK to grab source files and include dirs -APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f - -ifeq ($(TARGET_UI),FLOW) -SDK_SOURCE_PATH += lib_ux -endif - -ifeq ($(TARGET_NAME),TARGET_NANOX) -SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -endif - - -load: all - python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) - -delete: - python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) - -# import generic rules from the user and SDK --include Makefile.rules -include $(BOLOS_SDK)/Makefile.rules - -#add dependency on custom makefile filename -dep/%.d: %.c Makefile +SDK_SOURCE_PATH += lib_u2f -listvariants: - @echo VARIANTS COIN monero +include $(BOLOS_SDK)/Makefile.standard_app diff --git a/README.md b/README.md index dc9d137b..08fb891d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Monero Ledger App -Monero wallet application for Ledger Nano S and Nano X. +Monero wallet application for all Ledger devices. ## Install diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..034e8480 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/doc/developer/blue-app-commands.pdf b/doc/developer/blue-app-commands.pdf index 72a9b60b..37d5471d 100644 Binary files a/doc/developer/blue-app-commands.pdf and b/doc/developer/blue-app-commands.pdf differ diff --git a/doc/developer/blue-app-commands.rst b/doc/developer/blue-app-commands.rst index fde67f27..db46f4ab 100644 --- a/doc/developer/blue-app-commands.rst +++ b/doc/developer/blue-app-commands.rst @@ -104,6 +104,8 @@ .. |eDRVout| replace:: :math:`\widetilde{\mathfrak{D}_\mathrm{out}}` .. |AKout| replace:: :math:`\mathcal{AK}_\mathrm{amount}` .. |eAKout| replace:: :math:`\widetilde{\mathcal{AK}_\mathrm{amount}}` +.. |vtf| replace:: :math:`\mathit{view\_tag\_full}` +.. |vt| replace:: :math:`\mathit{view\_tag}` .. |ctH| replace:: :math:`\mathcal{H}_\mathrm{commitment}` @@ -198,6 +200,7 @@ To summarize, the signature process is: - compute the range proof - blind the amount + - compute the view tag . Compute the final confidential ring signature @@ -1142,6 +1145,54 @@ return |Img|. +--------+-----------------------------------------------------------------+ +Derive View Tag +~~~~~~~~~~~~~~~~~~ + +**Monero** + +crypto::derive_view_tag. + +**Description** + +Derive the view tag of an output. + + | compute |Drv| = |dec|[|spk|](|eDrv|) + | compute |vtf| = |Hs|("view_tag" \|, |Drv|, |idx|) + | compute |vt| = |vtf|[0:1] + +return |vt|. + +**Command** + ++-----+-----+-----+-----+----------+ +| CLA | INS | P1 | P2 | LC | ++=====+=====+=====+=====+==========+ +| 03 | 3B | 00 | 00 | 25 or 45 | ++-----+-----+-----+-----+----------+ + +**Command data** + ++--------+-----------------------------------------------------------------+ +| Length | Value | ++========+=================================================================+ +| 01 | 00 | ++--------+-----------------------------------------------------------------+ +| 20 | encrypted key derivation |eDrv| | ++--------+-----------------------------------------------------------------+ +| 20 | ephemeral hmac (optional, only during active transaction) | ++--------+-----------------------------------------------------------------+ +| 04 | index | ++--------+-----------------------------------------------------------------+ + +**Response data** + ++--------+-----------------------------------------------------------------+ +| Length | Value | ++========+=================================================================+ +| 01 | view tag |vt| | ++--------+-----------------------------------------------------------------+ + + Generate Keypair ~~~~~~~~~~~~~~~~ diff --git a/glyphs/Monero_64px.gif b/glyphs/Monero_64px.gif new file mode 100644 index 00000000..d7bdae59 Binary files /dev/null and b/glyphs/Monero_64px.gif differ diff --git a/icons/app_monero_14px.gif b/icons/app_monero_14px.gif new file mode 100644 index 00000000..fe64cd2a Binary files /dev/null and b/icons/app_monero_14px.gif differ diff --git a/icons/app_monero_16px.gif b/icons/app_monero_16px.gif new file mode 100644 index 00000000..760ccfeb Binary files /dev/null and b/icons/app_monero_16px.gif differ diff --git a/icons/app_monero_32px.gif b/icons/app_monero_32px.gif new file mode 100644 index 00000000..0b9408b4 Binary files /dev/null and b/icons/app_monero_32px.gif differ diff --git a/icons/app_monero_40px.gif b/icons/app_monero_40px.gif new file mode 100644 index 00000000..0cc57ad9 Binary files /dev/null and b/icons/app_monero_40px.gif differ diff --git a/images/icon_monero_stax.gif b/images/icon_monero_stax.gif new file mode 100644 index 00000000..0b9408b4 Binary files /dev/null and b/images/icon_monero_stax.gif differ diff --git a/ledger_app.toml b/ledger_app.toml new file mode 100644 index 00000000..7329320d --- /dev/null +++ b/ledger_app.toml @@ -0,0 +1,7 @@ +[app] +build_directory = "./" +sdk = "C" +devices = ["nanos", "nanox", "nanos+", "stax", "flex"] + +[tests] +pytest_directory = "./tests/" diff --git a/src/monero_api.h b/src/monero_api.h index a86aa4f1..2238d803 100644 --- a/src/monero_api.h +++ b/src/monero_api.h @@ -19,22 +19,22 @@ #ifndef MONERO_API_H #define MONERO_API_H +#include "monero_vars.h" + +void __attribute__((noreturn)) send_error_and_kill_app(int sw); int monero_apdu_reset(void); -int monero_apdu_lock(void); -void monero_lock_and_throw(int sw); void monero_install(unsigned char netId); -void monero_init(void); -void monero_init_private_key(void); -void monero_wipe_private_key(void); +unsigned int monero_init(void); +int monero_init_private_key(void); -void monero_init_ux(void); +int monero_init_ux(void); int monero_dispatch(void); int monero_apdu_put_key(void); int monero_apdu_get_key(void); int monero_apdu_display_address(void); -int monero_apdu_manage_seedwords(); +int monero_apdu_manage_seedwords(void); int monero_apdu_verify_key(void); int monero_apdu_get_chacha8_prekey(void); int monero_apdu_sc_add(void); @@ -47,6 +47,7 @@ int monero_apdu_derivation_to_scalar(void); int monero_apdu_derive_public_key(void); int monero_apdu_derive_secret_key(void); int monero_apdu_generate_key_image(void); +int monero_apdu_derive_view_tag(void); int monero_apdu_derive_subaddress_public_key(void); int monero_apdu_get_subaddress(void); int monero_apdu_get_subaddress_spend_public_key(void); @@ -56,7 +57,7 @@ int monero_apdu_get_tx_proof(void); int monero_apdu_open_tx(void); int monero_apdu_open_tx_cont(void); -void monero_reset_tx(int reset_tx_cnt); +int monero_reset_tx(int reset_tx_cnt); int monero_apdu_open_subtx(void); int monero_apdu_set_signature_mode(void); int monero_apdu_stealth(void); @@ -68,10 +69,14 @@ int monero_apdu_mlsag_prehash_init(void); int monero_apdu_mlsag_prehash_update(void); int monero_apdu_mlsag_prehash_finalize(void); +int monero_apdu_clsag_prepare(void); +int monero_apdu_clsag_hash(void); +int monero_apdu_clsag_sign(void); + int monero_apu_generate_txout_keys(void); -int monero_apdu_prefix_hash_init(); -int monero_apdu_prefix_hash_update(); +int monero_apdu_prefix_hash_init(void); +int monero_apdu_prefix_hash_update(void); int monero_apdu_mlsag_prepare(void); int monero_apdu_mlsag_hash(void); @@ -80,15 +85,17 @@ int monero_apdu_close_tx(void); void ui_init(void); void ui_menu_lock_display(void); -void ui_menu_main_display(unsigned int value); -void ui_menu_info_display(unsigned int value); -void ui_menu_info_display2(unsigned int value, char *line1, char *line2); +void ui_menu_main_display(void); +void ui_menu_show_security_error(void); +void ui_menu_show_tx_aborted(void); void ui_export_viewkey_display(unsigned int value); -void ui_menu_any_pubaddr_display(unsigned int value, unsigned char *pub_view, - unsigned char *pub_spend, unsigned char is_subbadress, - unsigned char *paymanetID); +int ui_menu_any_pubaddr_display(unsigned int value, unsigned char *pub_view, + unsigned char *pub_spend, unsigned char is_subbadress, + unsigned char *paymanetID); void ui_menu_pubaddr_display(unsigned int value); +unsigned int ui_menu_transaction_start(void); +unsigned int ui_menu_transaction_signed(void); /* ----------------------------------------------------------------------- */ /* --- MISC ---- */ /* ----------------------------------------------------------------------- */ @@ -100,39 +107,47 @@ int monero_base58_public_key(char *str_b58, unsigned char *view, unsigned char * /** unsigned varint amount to uint64 */ uint64_t monero_vamount2uint64(unsigned char *binary); /** binary little endian unsigned int amount to uint64 */ -uint64_t monero_bamount2uint64(unsigned char *binary); +uint64_t monero_bamount2uint64(unsigned char *binary, size_t binary_len); /** unsigned varint amount to str */ int monero_vamount2str(unsigned char *binary, char *str, unsigned int str_len); /** binary little endian unsigned int amount to str */ -int monero_bamount2str(unsigned char *binary, char *str, unsigned int str_len); +int monero_bamount2str(unsigned char *binary, char *str, size_t binary_len, unsigned int str_len); /** uint64 amount to str */ -int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len); +unsigned int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len); /** uint64 amount to str */ -void monero_uint642str(uint64_t val, char *str, unsigned int str_len); +unsigned int monero_uint642str(uint64_t val, char *str, unsigned int str_len); -int monero_abort_tx(); +int monero_abort_tx(void); int monero_unblind(unsigned char *v, unsigned char *k, unsigned char *AKout, - unsigned int short_amount); + unsigned int short_amount, size_t v_len, size_t k_len, size_t AKout_len); void ui_menu_validation_display(unsigned int value); +void ui_menu_validation_display_last(unsigned int value); void ui_menu_fee_validation_display(unsigned int value); void ui_menu_change_validation_display(unsigned int value); +void ui_menu_change_validation_display_last(unsigned int value); void ui_menu_timelock_validation_display(unsigned int value); void ui_menu_opentx_display(unsigned int value); + +void display_account(void); /* ----------------------------------------------------------------------- */ /* --- KEYS & ADDRESS ---- */ /* ----------------------------------------------------------------------- */ -extern const unsigned char C_FAKE_SEC_VIEW_KEY[32]; -extern const unsigned char C_FAKE_SEC_SPEND_KEY[32]; +extern const unsigned char C_FAKE_SEC_VIEW_KEY[KEY_SIZE]; +extern const unsigned char C_FAKE_SEC_SPEND_KEY[KEY_SIZE]; int is_fake_view_key(unsigned char *s); int is_fake_spend_key(unsigned char *s); +int monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes, size_t ge_len, + size_t bytes_len); void monero_sc_add(unsigned char *r, unsigned char *s1, unsigned char *s2); -void monero_hash_to_scalar(unsigned char *scalar, unsigned char *raw, unsigned int len); -void monero_hash_to_ec(unsigned char *ec, unsigned char *ec_pub); -void monero_generate_keypair(unsigned char *ec_pub, unsigned char *ec_priv); +int monero_hash_to_scalar(unsigned char *scalar, unsigned char *raw, size_t scalar_len, + unsigned int len); +int monero_hash_to_ec(unsigned char *ec, unsigned char *ec_pub, size_t ec_len); +int monero_generate_keypair(unsigned char *ec_pub, unsigned char *ec_priv, size_t ec_pub_len, + size_t ec_priv_len); /* * compute s = 8 * (k*P) * @@ -140,10 +155,10 @@ void monero_generate_keypair(unsigned char *ec_pub, unsigned char *ec_priv); * P [in] point in 02 y or 04 x y format * k [in] 32 bytes scalar */ -void monero_generate_key_derivation(unsigned char *drv_data, unsigned char *P, - unsigned char *scalar); -void monero_derivation_to_scalar(unsigned char *scalar, unsigned char *drv_data, - unsigned int out_idx); +int monero_generate_key_derivation(unsigned char *drv_data, unsigned char *P, unsigned char *scalar, + size_t drv_data_len, size_t P_len, size_t scalar_len); +int monero_derivation_to_scalar(unsigned char *scalar, unsigned char *drv_data, + unsigned int out_idx, size_t scalar_len, size_t drv_data_len); /* * compute x = Hps(drv_data,out_idx) + ec_pv * @@ -151,8 +166,9 @@ void monero_derivation_to_scalar(unsigned char *scalar, unsigned char *drv_data, * drv_data [in] 32 bytes derivation data (point) * ec_pv [in] 32 bytes private key */ -void monero_derive_secret_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, - unsigned char *ec_priv); +int monero_derive_secret_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, + unsigned char *ec_priv, size_t x_len, size_t drv_data_len, + size_t ec_priv_len); /* * compute x = Hps(drv_data,out_idx)*G + ec_pub * @@ -160,188 +176,220 @@ void monero_derive_secret_key(unsigned char *x, unsigned char *drv_data, unsigne * drv_data [in] 32 bytes derivation data (point) * ec_pub [in] 32 bytes public key */ -void monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, - unsigned char *ec_pub); -void monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv); -void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x); - -void monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, - unsigned char *drv_data, unsigned int index); -void monero_get_subaddress_spend_public_key(unsigned char *x, unsigned char *index); -void monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *index); -void monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, unsigned char *index); - -void monero_clear_words(); +int monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, + unsigned char *ec_pub, size_t x_len, size_t drv_data_len, + size_t ec_pub_len); +int monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv, + size_t ec_pub_len, size_t ec_priv_len); +int monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x, + size_t img_len, size_t x_len); +int monero_derive_view_tag(unsigned char *view_tag, const unsigned char drv_data[static 32], + unsigned int out_idx); + +int monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, + unsigned char *drv_data, unsigned int index, size_t x_len, + size_t pub_len, size_t drv_data_len); +int monero_get_subaddress_spend_public_key(unsigned char *x, unsigned char *index, size_t x_len, + size_t index_len); +int monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *index, size_t C_len, + size_t D_len, size_t index_len); +int monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, unsigned char *index, + size_t sub_s_len, size_t s_len, size_t index_len); + +void monero_clear_words(void); /* ----------------------------------------------------------------------- */ /* --- CRYPTO ---- */ /* ----------------------------------------------------------------------- */ extern const unsigned char C_ED25519_ORDER[]; -void monero_aes_derive(cx_aes_key_t *sk, unsigned char *seed32, unsigned char *a, unsigned char *b); +int monero_aes_derive(cx_aes_key_t *sk, unsigned char *seed32, unsigned char *a, unsigned char *b); void monero_aes_generate(cx_aes_key_t *sk); /* Compute Monero-Hash of data*/ -void monero_hash_init_keccak(cx_hash_t *hasher); +int monero_hash_init_keccak(cx_hash_t *hasher); void monero_hash_init_sha256(cx_hash_t *hasher); -void monero_hash_update(cx_hash_t *hasher, unsigned char *buf, unsigned int len); +int monero_hash_update(cx_hash_t *hasher, const unsigned char *buf, unsigned int len); int monero_hash_final(cx_hash_t *hasher, unsigned char *out); -int monero_hash(unsigned int algo, cx_hash_t *hasher, unsigned char *buf, unsigned int len, +int monero_hash(unsigned int algo, cx_hash_t *hasher, const unsigned char *buf, unsigned int len, unsigned char *out); -#define monero_keccak_init_F() monero_hash_init_keccak((cx_hash_t *)&G_monero_vstate.keccakF) -#define monero_keccak_update_F(buf, len) \ - monero_hash_update((cx_hash_t *)&G_monero_vstate.keccakF, (buf), (len)) -#define monero_keccak_final_F(out) monero_hash_final((cx_hash_t *)&G_monero_vstate.keccakF, (out)) -#define monero_keccak_F(buf, len, out) \ - monero_hash(CX_KECCAK, (cx_hash_t *)&G_monero_vstate.keccakF, (buf), (len), (out)) - -#define monero_keccak_init_H() monero_hash_init_keccak((cx_hash_t *)&G_monero_vstate.keccakH) -#define monero_keccak_update_H(buf, len) \ - monero_hash_update((cx_hash_t *)&G_monero_vstate.keccakH, (buf), (len)) -#define monero_keccak_final_H(out) monero_hash_final((cx_hash_t *)&G_monero_vstate.keccakH, (out)) -#define monero_keccak_H(buf, len, out) \ - monero_hash(CX_KECCAK, (cx_hash_t *)&G_monero_vstate.keccakH, (buf), (len), (out)) - -#define monero_sha256_commitment_init() \ - monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_commitment) -#define monero_sha256_commitment_update(buf, len) \ - monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_commitment, (buf), (len)) -#define monero_sha256_commitment_final(out) \ - monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_commitment, \ - (out) ? (out) : G_monero_vstate.C) - -#define monero_sha256_outkeys_init() \ - monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_out_keys) -#define monero_sha256_outkeys_update(buf, len) \ - monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_out_keys, (buf), (len)) -#define monero_sha256_outkeys_final(out) \ - monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_out_keys, (out)) +static inline int monero_keccak_F(unsigned char *buf, size_t len, unsigned char *out) { + return monero_hash(CX_KECCAK, (cx_hash_t *)&G_monero_vstate.keccakF, buf, len, out); +} + +static inline int monero_keccak_init_H(void) { + return monero_hash_init_keccak((cx_hash_t *)&G_monero_vstate.keccakH); +} + +static inline int monero_keccak_update_H(const unsigned char *buf, size_t len) { + return monero_hash_update((cx_hash_t *)&G_monero_vstate.keccakH, buf, len); +} + +static inline int monero_keccak_final_H(unsigned char *out) { + return monero_hash_final((cx_hash_t *)&G_monero_vstate.keccakH, out); +} + +static inline int monero_keccak_H(const unsigned char *buf, size_t len, unsigned char *out) { + return monero_hash(CX_KECCAK, (cx_hash_t *)&G_monero_vstate.keccakH, buf, len, out); +} + +static inline void monero_sha256_commitment_init(void) { + monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_commitment); +} + +static inline int monero_sha256_commitment_update(const unsigned char *buf, size_t len) { + return monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_commitment, buf, len); +} + +static inline int monero_sha256_commitment_final(unsigned char *out) { + unsigned char *digest = out ? out : G_monero_vstate.C; + return monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_commitment, digest); +} + +static inline void monero_sha256_outkeys_init(void) { + monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_out_keys); +} + +static inline int monero_sha256_outkeys_update(const unsigned char *buf, size_t len) { + return monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_out_keys, buf, len); +} + +static inline int monero_sha256_outkeys_final(unsigned char *out) { + return monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_out_keys, out); +} /* * check 1, Ledger SAS. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#include "os.h" +#include "cx.h" +#include "monero_types.h" +#include "monero_api.h" +#include "monero_vars.h" + +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +/* + bool device_default::clsag_prepare(const rct::key &p, + const rct::key &z, + rct::key &I, rct::key &D, + const rct::key &H, + rct::key &a, rct::key &aG, + rct::key &aH) { + rct::skpkGen(a, aG); // aG = a*G + rct::scalarmultKey(aH, H, a); // aH = a*H + rct::scalarmultKey(I, H, p); // I = p*H + rct::scalarmultKey(D, H, z); // D = z*H + return true; + } +*/ +int monero_apdu_clsag_prepare() { + unsigned char a[32]; + unsigned char p[32]; + unsigned char z[32]; + unsigned char H[32]; + unsigned char W[32]; + int err = 0; + + G_monero_vstate.tx_sign_cnt++; + if (G_monero_vstate.tx_sign_cnt == 0) { + return SW_SECURITY_MAX_SIGNATURE_REACHED; + } + + err = monero_io_fetch_decrypt(p, 32, TYPE_SCALAR); + if (err) { + return err; + } + + monero_io_fetch(z, 32); + monero_io_fetch(H, 32); + monero_io_discard(1); + + // a + err = monero_rng_mod_order(a, sizeof(a)); + if (err) { + return err; + } + + monero_io_insert_encrypt(a, 32, TYPE_ALPHA); + // a.G + err = monero_ecmul_G(W, a, sizeof(W), sizeof(a)); + if (err) { + return err; + } + + monero_io_insert(W, 32); + // a.H + err = monero_ecmul_k(W, H, a, sizeof(W), sizeof(H), sizeof(a)); + if (err) { + return err; + } + + monero_io_insert(W, 32); + // I = p.H + err = monero_ecmul_k(W, H, p, sizeof(W), sizeof(H), sizeof(p)); + if (err) { + return err; + } + monero_io_insert(W, 32); + + // D = z.H + err = monero_ecmul_k(W, H, z, sizeof(W), sizeof(H), sizeof(z)); + if (err) { + return err; + } + monero_io_insert(W, 32); + + return SW_OK; +} + +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +/* + bool device_default::clsag_hash(const rct::keyV &data, + rct::key &hash) { + hash = rct::hash_to_scalar(data); + return true; + } +*/ +int monero_apdu_clsag_hash() { + unsigned char msg[32]; + unsigned char c[32]; + int err; + + if (G_monero_vstate.io_p2 == 1) { + if (monero_keccak_init_H()) { + return SW_WRONG_DATA; + } + } + + monero_io_fetch(msg, 32); + monero_io_discard(1); + + err = monero_keccak_update_H(msg, 32); + if (err) { + return err; + } + + if ((G_monero_vstate.options & 0x80) == 0) { + err = monero_keccak_final_H(c); + if (err) { + return err; + } + + err = monero_reduce(c, c, sizeof(c), sizeof(c)); + if (err) { + return err; + } + monero_io_insert(c, 32); + memcpy(G_monero_vstate.c, c, 32); + } + return SW_OK; +} + +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +/* +bool device_default::clsag_sign(const rct::key &c, + const rct::key &a, + const rct::key &p, const + rct::key &z, + const rct::key &mu_P, + const rct::key &mu_C, + rct::key &s) { + rct::key s0_p_mu_P; + sc_mul(s0_p_mu_P.bytes, mu_P.bytes, p.bytes); + rct::key s0_add_z_mu_C; + sc_muladd(s0_add_z_mu_C.bytes, mu_C.bytes, z.bytes, s0_p_mu_P.bytes); + sc_mulsub(s.bytes, c.bytes, s0_add_z_mu_C.bytes, a.bytes); + + return true; + } +*/ +int monero_apdu_clsag_sign() { + unsigned char s[32]; + unsigned char a[32]; + unsigned char p[32]; + unsigned char z[32]; + unsigned char mu_P[32]; + unsigned char mu_C[32]; + int err = 0; + + if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_FAKE) { + monero_io_fetch(a, 32); + monero_io_fetch(p, 32); + monero_io_fetch(z, 32); + monero_io_fetch(mu_P, 32); + monero_io_fetch(mu_C, 32); + } else if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { + err = monero_io_fetch_decrypt(a, 32, TYPE_ALPHA); + if (err) { + return err; + } + + err = monero_io_fetch_decrypt(p, 32, TYPE_SCALAR); + if (err) { + return err; + } + monero_io_fetch(z, 32); + monero_io_fetch(mu_P, 32); + monero_io_fetch(mu_C, 32); + } else { + return SW_SECURITY_INTERNAL; + } + + monero_io_discard(1); + + err = monero_check_scalar_not_null(a); + if (err) { + return err; + } + + err = monero_check_scalar_not_null(p); + if (err) { + return err; + } + + err = monero_check_scalar_not_null(z); + if (err) { + return err; + } + + err = monero_reduce(a, a, sizeof(a), sizeof(a)); + if (err) { + return err; + } + + err = monero_reduce(p, p, sizeof(p), sizeof(p)); + if (err) { + return err; + } + + err = monero_reduce(z, z, sizeof(z), sizeof(z)); + if (err) { + return err; + } + + err = monero_reduce(mu_P, mu_P, sizeof(mu_P), sizeof(mu_P)); + if (err) { + return err; + } + + err = monero_reduce(mu_C, mu_C, sizeof(mu_C), sizeof(mu_C)); + if (err) { + return err; + } + + err = monero_reduce(G_monero_vstate.c, G_monero_vstate.c, sizeof(G_monero_vstate.c), + sizeof(G_monero_vstate.c)); + if (err) { + return err; + } + + // s0_p_mu_P = mu_P*p + // s0_add_z_mu_C = mu_C*z + s0_p_mu_P + // + // s = a - c*s0_add_z_mu_C + // = a - c*(mu_C*z + mu_P*p) + + // s = p*mu_P + err = monero_multm(s, p, mu_P, sizeof(s), sizeof(p), sizeof(mu_P)); + if (err) { + return err; + } + // mu_P = mu_C*z + err = monero_multm(mu_P, mu_C, z, sizeof(mu_P), sizeof(mu_C), sizeof(z)); + if (err) { + return err; + } + // s = p*mu_P + mu_C*z + err = monero_addm(s, s, mu_P, sizeof(s), sizeof(s), sizeof(mu_P)); + if (err) { + return err; + } + // mu_P = c * (p*mu_P + mu_C*z) + err = monero_multm(mu_P, G_monero_vstate.c, s, sizeof(mu_P), sizeof(G_monero_vstate.c), + sizeof(s)); + if (err) { + return err; + } + // s = a - c*(p*mu_P + mu_C*z) + err = monero_subm(s, a, mu_P, sizeof(s), sizeof(a), sizeof(mu_P)); + if (err) { + return err; + } + + monero_io_insert(s, 32); + + return SW_OK; +} diff --git a/src/monero_crypto.c b/src/monero_crypto.c index 07cd8974..f986f933 100644 --- a/src/monero_crypto.c +++ b/src/monero_crypto.c @@ -22,6 +22,8 @@ #include "monero_api.h" #include "monero_vars.h" +#define PXY_SIZE 65 + /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ @@ -56,115 +58,176 @@ unsigned char const C_EIGHT[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0 /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_aes_derive(cx_aes_key_t *sk, unsigned char *seed32, unsigned char *a, - unsigned char *b) { - unsigned char h1[32]; +int monero_aes_derive(cx_aes_key_t *sk, unsigned char *seed32, unsigned char *a, unsigned char *b) { + unsigned char h1[KEY_SIZE]; + int error; - monero_keccak_init_H(); - monero_keccak_update_H(seed32, 32); - monero_keccak_update_H(a, 32); - monero_keccak_update_H(b, 32); - monero_keccak_final_H(h1); + error = monero_keccak_init_H(); + if (error) { + return error; + } - monero_keccak_H(h1, 32, h1); + error = monero_keccak_update_H(seed32, KEY_SIZE); + if (error) { + return error; + } - cx_aes_init_key(h1, 16, sk); -} + error = monero_keccak_update_H(a, KEY_SIZE); + if (error) { + return error; + } -/* ----------------------------------------------------------------------- */ -/* --- --- */ -/* ----------------------------------------------------------------------- */ -void monero_aes_generate(cx_aes_key_t *sk) { - unsigned char h1[16]; - cx_rng(h1, 16); - cx_aes_init_key(h1, 16, sk); + error = monero_keccak_update_H(b, KEY_SIZE); + if (error) { + return error; + } + + error = monero_keccak_final_H(h1); + if (error) { + return error; + } + + error = monero_keccak_H(h1, KEY_SIZE, h1); + if (error) { + return error; + } + + error = cx_aes_init_key_no_throw(h1, 16, sk); + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- assert: max_len>0 --- */ /* ----------------------------------------------------------------------- */ -unsigned int monero_encode_varint(unsigned char *varint, unsigned int max_len, uint64_t value) { - unsigned int len; - len = 0; +unsigned int monero_encode_varint(unsigned char *varint, unsigned int max_len, uint64_t value, + unsigned int *out_len) { + if (!varint || !out_len) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA; + } + + *out_len = 0; while (value >= 0x80) { - if (len == (max_len - 1)) { - THROW(SW_WRONG_DATA_RANGE); + if (*out_len == (max_len - 1)) { + return SW_WRONG_DATA_RANGE; } - varint[len] = (value & 0x7F) | 0x80; + varint[*out_len] = (value & 0x7F) | 0x80; value = value >> 7; - len++; + *out_len = *out_len + 1; } - varint[len] = value; - return len + 1; + varint[*out_len] = value; + *out_len = *out_len + 1; + return 0; } /* ----------------------------------------------------------------------- */ /* --- assert: max_len>0 --- */ /* ----------------------------------------------------------------------- */ -unsigned int monero_decode_varint(unsigned char *varint, unsigned int max_len, uint64_t *value) { +unsigned int monero_decode_varint(const unsigned char *varint, size_t max_len, uint64_t *value, + unsigned int *out_len) { uint64_t v; - int len; + size_t len; v = 0; len = 0; + if (!varint || !out_len) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA; + } while ((varint[len]) & 0x80) { if (len == (max_len - 1)) { - THROW(SW_WRONG_DATA_RANGE); + return SW_WRONG_DATA_RANGE; } - v = v + (((varint[len]) & 0x7f) << (len * 7)); + v = v + ((uint64_t)((varint[len]) & 0x7f) << (len * 7)); len++; } - v = v + (((varint[len]) & 0x7f) << (len * 7)); + v = v + ((uint64_t)((varint[len]) & 0x7f) << (len * 7)); *value = v; - return len + 1; + *out_len = len + 1; + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_reverse32(unsigned char *rscal, unsigned char *scal) { +int monero_reverse32(unsigned char *rscal, unsigned char *scal, size_t rscal_len, size_t scal_len) { unsigned char x; unsigned int i; + if (!rscal || !scal) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA; + } + if (rscal_len < 32 || scal_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } for (i = 0; i < 16; i++) { x = scal[i]; rscal[i] = scal[31 - i]; rscal[31 - i] = x; } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_hash_init_sha256(cx_hash_t *hasher) { cx_sha256_init((cx_sha256_t *)hasher); } +void monero_hash_init_sha256(cx_hash_t *hasher) { + cx_sha256_init((cx_sha256_t *)hasher); +} -void monero_hash_init_keccak(cx_hash_t *hasher) { cx_keccak_init((cx_sha3_t *)hasher, 256); } +int monero_hash_init_keccak(cx_hash_t *hasher) { + int error = cx_keccak_init_no_throw((cx_sha3_t *)hasher, 256); + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; +} /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_hash_update(cx_hash_t *hasher, unsigned char *buf, unsigned int len) { - cx_hash(hasher, 0, buf, len, NULL, 0); +int monero_hash_update(cx_hash_t *hasher, const unsigned char *buf, unsigned int len) { + int error = cx_hash_no_throw(hasher, 0, buf, len, NULL, 0); + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_hash_final(cx_hash_t *hasher, unsigned char *out) { - return cx_hash(hasher, CX_LAST, NULL, 0, out, 32); + int error = cx_hash_no_throw(hasher, CX_LAST, NULL, 0, out, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_hash(unsigned int algo, cx_hash_t *hasher, unsigned char *buf, unsigned int len, +int monero_hash(unsigned int algo, cx_hash_t *hasher, const unsigned char *buf, unsigned int len, unsigned char *out) { - hasher->algo = algo; + int err = 0; if (algo == CX_SHA256) { cx_sha256_init((cx_sha256_t *)hasher); } else { - cx_keccak_init((cx_sha3_t *)hasher, 256); + err = cx_keccak_init_no_throw((cx_sha3_t *)hasher, 256); + if (err) { + return SW_SECURITY_INTERNAL; + } } - return cx_hash(hasher, CX_LAST, buf, len, out, 32); + err = cx_hash_no_throw(hasher, CX_LAST, buf, len, out, 32); + if (err) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ @@ -275,40 +338,23 @@ const unsigned char C_fe_qm5div8[] = { 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}; -static void monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes) { +int monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes, size_t ge_len, + size_t bytes_len) { + int error = 0; #define MOD (unsigned char *)C_ED25519_FIELD, 32 #define fe_isnegative(f) (f[31] & 1) -#if 0 - unsigned char u[32], v[32], w[32], x[32], y[32], z[32]; - unsigned char rX[32], rY[32], rZ[32]; - union { - struct { - unsigned char _uv7[32]; - unsigned char _v3[32]; - }; - unsigned char _Pxy[65]; +#define u (G_monero_vstate.io_buffer + 0 * 32) +#define v (G_monero_vstate.io_buffer + 1 * 32) +#define w (G_monero_vstate.io_buffer + 2 * 32) +#define x (G_monero_vstate.io_buffer + 3 * 32) +#define y (G_monero_vstate.io_buffer + 4 * 32) +#define z (G_monero_vstate.io_buffer + 5 * 32) +#define rX (G_monero_vstate.io_buffer + 6 * 32) +#define rY (G_monero_vstate.io_buffer + 7 * 32) +#define rZ (G_monero_vstate.io_buffer + 8 * 32) - } uv; - -#define uv7 uv._uv7 -#define v3 uv._v3 - -#define Pxy uv._Pxy -#else -#define u (G_monero_vstate.io_buffer + 0 * 32) -#define v (G_monero_vstate.io_buffer + 1 * 32) -#define w (G_monero_vstate.io_buffer + 2 * 32) -#define x (G_monero_vstate.io_buffer + 3 * 32) -#define y (G_monero_vstate.io_buffer + 4 * 32) -#define z (G_monero_vstate.io_buffer + 5 * 32) -#define rX (G_monero_vstate.io_buffer + 6 * 32) -#define rY (G_monero_vstate.io_buffer + 7 * 32) -#define rZ (G_monero_vstate.io_buffer + 8 * 32) - - //#define uv7 (G_monero_vstate.io_buffer+9*32) - //#define v3 (G_monero_vstate.io_buffer+10*32) union { - unsigned char _Pxy[65]; + unsigned char _Pxy[PXY_SIZE]; struct { unsigned char _uv7[32]; unsigned char _v3[32]; @@ -323,69 +369,77 @@ static void monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes) #if MONERO_IO_BUFFER_LENGTH < (9 * 32) #error MONERO_IO_BUFFER_LENGTH is too small -#endif #endif unsigned char sign; + if (!ge) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA; + } + if (ge_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } // cx works in BE - monero_reverse32(u, bytes); - cx_math_modm(u, 32, (unsigned char *)C_ED25519_FIELD, 32); + error |= monero_reverse32(u, bytes, 32, bytes_len); + error |= cx_math_modm_no_throw(u, 32, (unsigned char *)C_ED25519_FIELD, 32); // go on - cx_math_multm(v, u, u, MOD); /* 2 * u^2 */ - cx_math_addm(v, v, v, MOD); + error |= cx_math_multm_no_throw(v, u, u, MOD); /* 2 * u^2 */ + error |= cx_math_addm_no_throw(v, v, v, MOD); - os_memset(w, 0, 32); - w[31] = 1; /* w = 1 */ - cx_math_addm(w, v, w, MOD); /* w = 2 * u^2 + 1 */ - cx_math_multm(x, w, w, MOD); /* w^2 */ - cx_math_multm(y, (unsigned char *)C_fe_ma2, v, MOD); /* -2 * A^2 * u^2 */ - cx_math_addm(x, x, y, MOD); /* x = w^2 - 2 * A^2 * u^2 */ + explicit_bzero(w, 32); + w[31] = 1; /* w = 1 */ + error |= cx_math_addm_no_throw(w, v, w, MOD); /* w = 2 * u^2 + 1 */ + error |= cx_math_multm_no_throw(x, w, w, MOD); /* w^2 */ + error |= cx_math_multm_no_throw(y, (unsigned char *)C_fe_ma2, v, MOD); /* -2 * A^2 * u^2 */ + error |= cx_math_addm_no_throw(x, x, y, MOD); /* x = w^2 - 2 * A^2 * u^2 */ // inline fe_divpowm1(r->X, w, x); // (w / x)^(m + 1) => fe_divpowm1(r,u,v) #define _u w #define _v x - cx_math_multm(v3, _v, _v, MOD); - cx_math_multm(v3, v3, _v, MOD); /* v3 = v^3 */ - cx_math_multm(uv7, v3, v3, MOD); - cx_math_multm(uv7, uv7, _v, MOD); - cx_math_multm(uv7, uv7, _u, MOD); /* uv7 = uv^7 */ - cx_math_powm(uv7, uv7, (unsigned char *)C_fe_qm5div8, 32, MOD); /* (uv^7)^((q-5)/8)*/ - cx_math_multm(uv7, uv7, v3, MOD); - cx_math_multm(rX, uv7, w, MOD); /* u^(m+1)v^(-(m+1)) */ + error |= cx_math_multm_no_throw(v3, _v, _v, MOD); + error |= cx_math_multm_no_throw(v3, v3, _v, MOD); /* v3 = v^3 */ + error |= cx_math_multm_no_throw(uv7, v3, v3, MOD); + error |= cx_math_multm_no_throw(uv7, uv7, _v, MOD); + error |= cx_math_multm_no_throw(uv7, uv7, _u, MOD); /* uv7 = uv^7 */ + error |= cx_math_powm_no_throw(uv7, uv7, (unsigned char *)C_fe_qm5div8, 32, + MOD); /* (uv^7)^((q-5)/8)*/ + error |= cx_math_multm_no_throw(uv7, uv7, v3, MOD); + error |= cx_math_multm_no_throw(rX, uv7, w, MOD); /* u^(m+1)v^(-(m+1)) */ #undef _u #undef _v - cx_math_multm(y, rX, rX, MOD); - cx_math_multm(x, y, x, MOD); - cx_math_subm(y, w, x, MOD); - os_memmove(z, C_fe_ma, 32); + error |= cx_math_multm_no_throw(y, rX, rX, MOD); + error |= cx_math_multm_no_throw(x, y, x, MOD); + error |= cx_math_subm_no_throw(y, w, x, MOD); + memcpy(z, C_fe_ma, 32); if (!cx_math_is_zero(y, 32)) { - cx_math_addm(y, w, x, MOD); + error |= cx_math_addm_no_throw(y, w, x, MOD); if (!cx_math_is_zero(y, 32)) { goto negative; } else { - cx_math_multm(rX, rX, (unsigned char *)C_fe_fffb1, MOD); + error |= cx_math_multm_no_throw(rX, rX, (unsigned char *)C_fe_fffb1, MOD); } } else { - cx_math_multm(rX, rX, (unsigned char *)C_fe_fffb2, MOD); + error |= cx_math_multm_no_throw(rX, rX, (unsigned char *)C_fe_fffb2, MOD); } - cx_math_multm(rX, rX, u, MOD); // u * sqrt(2 * A * (A + 2) * w / x) - cx_math_multm(z, z, v, MOD); // -2 * A * u^2 + error |= cx_math_multm_no_throw(rX, rX, u, MOD); // u * sqrt(2 * A * (A + 2) * w / x) + error |= cx_math_multm_no_throw(z, z, v, MOD); // -2 * A * u^2 sign = 0; goto setsign; negative: - cx_math_multm(x, x, (unsigned char *)C_fe_sqrtm1, MOD); - cx_math_subm(y, w, x, MOD); + error |= cx_math_multm_no_throw(x, x, (unsigned char *)C_fe_sqrtm1, MOD); + error |= cx_math_subm_no_throw(y, w, x, MOD); if (!cx_math_is_zero(y, 32)) { - cx_math_addm(y, w, x, MOD); - cx_math_multm(rX, rX, (unsigned char *)C_fe_fffb3, MOD); + error |= cx_math_addm_no_throw(y, w, x, MOD); + error |= cx_math_multm_no_throw(rX, rX, (unsigned char *)C_fe_fffb3, MOD); } else { - cx_math_multm(rX, rX, (unsigned char *)C_fe_fffb4, MOD); + error |= cx_math_multm_no_throw(rX, rX, (unsigned char *)C_fe_fffb4, MOD); } // r->X = sqrt(A * (A + 2) * w / x) // z = -A @@ -393,20 +447,24 @@ static void monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes) setsign: if (fe_isnegative(rX) != sign) { - // fe_neg(r->X, r->X); - cx_math_sub(rX, (unsigned char *)C_ED25519_FIELD, rX, 32); + error |= cx_math_sub(rX, (unsigned char *)C_ED25519_FIELD, rX, 32); } - cx_math_addm(rZ, z, w, MOD); - cx_math_subm(rY, z, w, MOD); - cx_math_multm(rX, rX, rZ, MOD); + error |= cx_math_addm_no_throw(rZ, z, w, MOD); + error |= cx_math_subm_no_throw(rY, z, w, MOD); + error |= cx_math_multm_no_throw(rX, rX, rZ, MOD); // back to monero y-affine - cx_math_invprimem(u, rZ, MOD); + error |= cx_math_invprimem_no_throw(u, rZ, MOD); Pxy[0] = 0x04; - cx_math_multm(&Pxy[1], rX, u, MOD); - cx_math_multm(&Pxy[1 + 32], rY, u, MOD); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(ge, &Pxy[1], 32); + error |= cx_math_multm_no_throw(&Pxy[1], rX, u, MOD); + error |= cx_math_multm_no_throw(&Pxy[1 + 32], rY, u, MOD); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(ge, &Pxy[1], 32); + + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; #undef u #undef v @@ -431,93 +489,203 @@ static void monero_ge_fromfe_frombytes(unsigned char *ge, unsigned char *bytes) /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_hash_to_scalar(unsigned char *scalar, unsigned char *raw, unsigned int raw_len) { - monero_keccak_F(raw, raw_len, scalar); - monero_reduce(scalar, scalar); +int monero_hash_to_scalar(unsigned char *scalar, unsigned char *raw, size_t scalar_len, + unsigned int raw_len) { + int error; + + error = monero_keccak_F(raw, raw_len, scalar); + if (error) { + return error; + } + + error = monero_reduce(scalar, scalar, scalar_len, scalar_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_hash_to_ec(unsigned char *ec, unsigned char *ec_pub) { - monero_keccak_F(ec_pub, 32, ec); - monero_ge_fromfe_frombytes(ec, ec); - monero_ecmul_8(ec, ec); +int monero_hash_to_ec(unsigned char *ec, unsigned char *ec_pub, size_t ec_len) { + int error; + error = monero_keccak_F(ec_pub, 32, ec); + if (error) { + return error; + } + + error = monero_ge_fromfe_frombytes(ec, ec, ec_len, ec_len); + if (error) { + return error; + } + + error = monero_ecmul_8(ec, ec, ec_len, ec_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_generate_keypair(unsigned char *ec_pub, unsigned char *ec_priv) { - monero_rng_mod_order(ec_priv); - monero_ecmul_G(ec_pub, ec_priv); +int monero_generate_keypair(unsigned char *ec_pub, unsigned char *ec_priv, size_t ec_pub_len, + size_t ec_priv_len) { + int error; + error = monero_rng_mod_order(ec_priv, ec_priv_len); + if (error) { + return error; + } + + error = monero_ecmul_G(ec_pub, ec_priv, ec_pub_len, ec_priv_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- ok --- */ /* ----------------------------------------------------------------------- */ -void monero_generate_key_derivation(unsigned char *drv_data, unsigned char *P, - unsigned char *scalar) { - monero_ecmul_8k(drv_data, P, scalar); +int monero_generate_key_derivation(unsigned char *drv_data, unsigned char *P, unsigned char *scalar, + size_t drv_data_len, size_t P_len, size_t scalar_len) { + return monero_ecmul_8k(drv_data, P, scalar, drv_data_len, P_len, scalar_len); } /* ----------------------------------------------------------------------- */ /* --- ok --- */ /* ----------------------------------------------------------------------- */ -void monero_derivation_to_scalar(unsigned char *scalar, unsigned char *drv_data, - unsigned int out_idx) { +int monero_derivation_to_scalar(unsigned char *scalar, unsigned char *drv_data, + unsigned int out_idx, size_t scalar_len, size_t drv_data_len) { unsigned char varint[32 + 8]; unsigned int len_varint; + int error = 0; - os_memmove(varint, drv_data, 32); - len_varint = monero_encode_varint(varint + 32, 8, out_idx); + if (drv_data_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + memcpy(varint, drv_data, 32); + error = monero_encode_varint(varint + 32, 8, out_idx, &len_varint); + if (error) { + return error; + } len_varint += 32; - monero_keccak_F(varint, len_varint, varint); - monero_reduce(scalar, varint); + + error = monero_keccak_F(varint, len_varint, varint); + if (error) { + return error; + } + + error = monero_reduce(scalar, varint, scalar_len, sizeof(varint)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_derive_secret_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, - unsigned char *ec_priv) { +int monero_derive_secret_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, + unsigned char *ec_priv, size_t x_len, size_t drv_data_len, + size_t ec_priv_len) { unsigned char tmp[32]; + int error; // derivation to scalar - monero_derivation_to_scalar(tmp, drv_data, out_idx); + error = monero_derivation_to_scalar(tmp, drv_data, out_idx, sizeof(tmp), drv_data_len); + if (error) { + return error; + } // generate - monero_addm(x, tmp, ec_priv); + error = monero_addm(x, tmp, ec_priv, x_len, sizeof(tmp), ec_priv_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, - unsigned char *ec_pub) { +int monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigned int out_idx, + unsigned char *ec_pub, size_t x_len, size_t drv_data_len, + size_t ec_pub_len) { unsigned char tmp[32]; // derivation to scalar - monero_derivation_to_scalar(tmp, drv_data, out_idx); + int error = monero_derivation_to_scalar(tmp, drv_data, out_idx, sizeof(tmp), drv_data_len); + if (error) { + return error; + } // generate - monero_ecmul_G(tmp, tmp); - monero_ecadd(x, tmp, ec_pub); + error = monero_ecmul_G(tmp, tmp, sizeof(tmp), sizeof(tmp)); + if (error) { + return error; + } + + error = monero_ecadd(x, tmp, ec_pub, x_len, sizeof(tmp), ec_pub_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv) { - monero_ecmul_G(ec_pub, ec_priv); +int monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv, + size_t ec_pub_len, size_t ec_priv_len) { + return monero_ecmul_G(ec_pub, ec_priv, ec_pub_len, ec_priv_len); } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x) { +int monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x, + size_t img_len, size_t x_len) { unsigned char I[32]; - monero_hash_to_ec(I, P); - monero_ecmul_k(img, I, x); + int error; + error = monero_hash_to_ec(I, P, sizeof(I)); + if (error) { + return error; + } + + error = monero_ecmul_k(img, I, x, img_len, sizeof(I), x_len); + if (error) { + return error; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +int monero_derive_view_tag(unsigned char *view_tag, const unsigned char drv_data[static 32], + unsigned int out_idx) { + unsigned char varint[8 + 32 + 8]; + unsigned int len_varint; + int error = 0; + + memcpy(varint, "view_tag", 8); + memcpy(varint + 8, drv_data, 32); + error = monero_encode_varint(varint + 8 + 32, 8, out_idx, &len_varint); + if (error) { + return error; + } + len_varint += 8 + 32; + + error = monero_keccak_F(varint, len_varint, varint); + if (error) { + return error; + } + + *view_tag = varint[0]; + return 0; } /* ======================================================================= */ @@ -527,35 +695,70 @@ void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned ch /* ----------------------------------------------------------------------- */ /* --- ok --- */ /* ----------------------------------------------------------------------- */ -void monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, - unsigned char *drv_data, unsigned int index) { +int monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, + unsigned char *drv_data, unsigned int index, size_t x_len, + size_t pub_len, size_t drv_data_len) { unsigned char scalarG[32]; + int error; - monero_derivation_to_scalar(scalarG, drv_data, index); - monero_ecmul_G(scalarG, scalarG); - monero_ecsub(x, pub, scalarG); + error = monero_derivation_to_scalar(scalarG, drv_data, index, sizeof(scalarG), drv_data_len); + if (error) { + return error; + } + error = monero_ecmul_G(scalarG, scalarG, sizeof(scalarG), sizeof(scalarG)); + if (error) { + return error; + } + error = monero_ecsub(x, pub, scalarG, x_len, pub_len, sizeof(scalarG)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- ok --- */ /* ----------------------------------------------------------------------- */ -void monero_get_subaddress_spend_public_key(unsigned char *x, unsigned char *index) { +int monero_get_subaddress_spend_public_key(unsigned char *x, unsigned char *index, size_t x_len, + size_t index_len) { + int error; // m = Hs(a || index_major || index_minor) - monero_get_subaddress_secret_key(x, G_monero_vstate.a, index); + error = monero_get_subaddress_secret_key(x, G_monero_vstate.a, index, x_len, + sizeof(G_monero_vstate.a), index_len); + if (error) { + return error; + } + // M = m*G - monero_secret_key_to_public_key(x, x); + error = monero_secret_key_to_public_key(x, x, x_len, x_len); + if (error) { + return error; + } + // D = B + M - monero_ecadd(x, x, G_monero_vstate.B); + error = monero_ecadd(x, x, G_monero_vstate.B, x_len, x_len, sizeof(G_monero_vstate.B)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *index) { +int monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *index, size_t C_len, + size_t D_len, size_t index_len) { // retrieve D - monero_get_subaddress_spend_public_key(D, index); + int error = monero_get_subaddress_spend_public_key(D, index, D_len, index_len); + if (error) { + return error; + } // C = a*D - monero_ecmul_k(C, D, G_monero_vstate.a); + error = monero_ecmul_k(C, D, G_monero_vstate.a, C_len, D_len, sizeof(G_monero_vstate.a)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ @@ -563,16 +766,36 @@ void monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *in /* ----------------------------------------------------------------------- */ static const char C_sub_address_prefix[] = {'S', 'u', 'b', 'A', 'd', 'd', 'r', 0}; -void monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, - unsigned char *index) { +int monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, unsigned char *index, + size_t sub_s_len, size_t s_len, size_t index_len) { unsigned char in[sizeof(C_sub_address_prefix) + 32 + 8]; + int error; + + if (!s || s_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!index || index_len < 8) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } - os_memmove(in, C_sub_address_prefix, sizeof(C_sub_address_prefix)), - os_memmove(in + sizeof(C_sub_address_prefix), s, 32); - os_memmove(in + sizeof(C_sub_address_prefix) + 32, index, 8); + memcpy(in, C_sub_address_prefix, sizeof(C_sub_address_prefix)); + memcpy(in + sizeof(C_sub_address_prefix), s, 32); + memcpy(in + sizeof(C_sub_address_prefix) + 32, index, 8); // hash_to_scalar with more that 32bytes: - monero_keccak_F(in, sizeof(in), sub_s); - monero_reduce(sub_s, sub_s); + + error = monero_keccak_F(in, sizeof(in), sub_s); + if (error) { + return error; + } + + error = monero_reduce(sub_s, sub_s, sub_s_len, sub_s_len); + if (error) { + return error; + } + return 0; } /* ======================================================================= */ @@ -582,139 +805,267 @@ void monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_check_scalar_range_1N(unsigned char *s) { +unsigned int monero_check_scalar_range_1N(unsigned char *s, size_t s_len) { unsigned char x[32]; - monero_reverse32(x, s); - if (cx_math_is_zero(x, 32) || cx_math_cmp(x, C_ED25519_ORDER, 32) >= 0) { - THROW(SW_WRONG_DATA_RANGE); + int diff; + int error = monero_reverse32(x, s, sizeof(x), s_len); + if (error) { + return error; + } + if (cx_math_cmp_no_throw(x, C_ED25519_ORDER, 32, &diff)) { + return SW_SECURITY_INTERNAL; } + if (cx_math_is_zero(x, 32) || diff >= 0) { + return SW_WRONG_DATA_RANGE; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_check_scalar_not_null(unsigned char *s) { +unsigned int monero_check_scalar_not_null(unsigned char *s) { if (cx_math_is_zero(s, 32)) { - THROW(SW_WRONG_DATA_RANGE); + return SW_WRONG_DATA_RANGE; } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecmul_G(unsigned char *W, unsigned char *scalar32) { - unsigned char Pxy[65]; +int monero_ecmul_G(unsigned char *W, unsigned char *scalar32, size_t W_len, size_t scalar32_len) { + unsigned char Pxy[PXY_SIZE]; unsigned char s[32]; - monero_reverse32(s, scalar32); - os_memmove(Pxy, C_ED25519_G, 65); - cx_ecfp_scalar_mult(CX_CURVE_Ed25519, Pxy, sizeof(Pxy), s, 32); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(W, &Pxy[1], 32); + int error; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + error = monero_reverse32(s, scalar32, sizeof(s), scalar32_len); + if (error) { + return error; + } + memcpy(Pxy, C_ED25519_G, PXY_SIZE); + error = cx_ecfp_scalar_mult_no_throw(CX_CURVE_Ed25519, Pxy, s, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + error = cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + if (error) { + return SW_SECURITY_INTERNAL; + } + memcpy(W, &Pxy[1], 32); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecmul_H(unsigned char *W, unsigned char *scalar32) { - unsigned char Pxy[65]; +int monero_ecmul_H(unsigned char *W, unsigned char *scalar32, size_t W_len, size_t scalar32_len) { + unsigned char Pxy[PXY_SIZE]; unsigned char s[32]; - monero_reverse32(s, scalar32); + int error = 0; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + error = monero_reverse32(s, scalar32, sizeof(s), scalar32_len); + if (error) { + return error; + } Pxy[0] = 0x02; - os_memmove(&Pxy[1], C_ED25519_Hy, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(&Pxy[1], C_ED25519_Hy, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + + error |= cx_ecfp_scalar_mult_no_throw(CX_CURVE_Ed25519, Pxy, s, 32); - cx_ecfp_scalar_mult(CX_CURVE_Ed25519, Pxy, sizeof(Pxy), s, 32); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(W, &Pxy[1], 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + + memcpy(W, &Pxy[1], 32); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecmul_k(unsigned char *W, unsigned char *P, unsigned char *scalar32) { - unsigned char Pxy[65]; +int monero_ecmul_k(unsigned char *W, unsigned char *P, unsigned char *scalar32, size_t W_len, + size_t P_len, size_t scalar32_len) { + unsigned char Pxy[PXY_SIZE]; unsigned char s[32]; + int error = 0; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } - monero_reverse32(s, scalar32); + if (!P || P_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + error = monero_reverse32(s, scalar32, sizeof(s), scalar32_len); + if (error) { + return error; + } Pxy[0] = 0x02; - os_memmove(&Pxy[1], P, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(&Pxy[1], P, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - cx_ecfp_scalar_mult(CX_CURVE_Ed25519, Pxy, sizeof(Pxy), s, 32); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + error |= cx_ecfp_scalar_mult_no_throw(CX_CURVE_Ed25519, Pxy, s, 32); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + if (error) { + return SW_SECURITY_INTERNAL; + } - os_memmove(W, &Pxy[1], 32); + memcpy(W, &Pxy[1], 32); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecmul_8k(unsigned char *W, unsigned char *P, unsigned char *scalar32) { +int monero_ecmul_8k(unsigned char *W, unsigned char *P, unsigned char *scalar32, size_t W_len, + size_t P_len, size_t scalar32_len) { unsigned char s[32]; - monero_multm_8(s, scalar32); - monero_ecmul_k(W, P, s); + int error = 0; + error = monero_multm_8(s, scalar32, sizeof(s), scalar32_len); + if (error) { + return error; + } + + error = monero_ecmul_k(W, P, s, W_len, P_len, sizeof(s)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecmul_8(unsigned char *W, unsigned char *P) { - unsigned char Pxy[65]; +int monero_ecmul_8(unsigned char *W, unsigned char *P, size_t W_len, size_t P_len) { + unsigned char Pxy[PXY_SIZE]; + int error = 0; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!P || P_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } Pxy[0] = 0x02; - os_memmove(&Pxy[1], P, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - cx_ecfp_add_point(CX_CURVE_Ed25519, Pxy, Pxy, Pxy, sizeof(Pxy)); - cx_ecfp_add_point(CX_CURVE_Ed25519, Pxy, Pxy, Pxy, sizeof(Pxy)); - cx_ecfp_add_point(CX_CURVE_Ed25519, Pxy, Pxy, Pxy, sizeof(Pxy)); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(W, &Pxy[1], 32); + memcpy(&Pxy[1], P, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + error |= cx_ecfp_add_point_no_throw(CX_CURVE_Ed25519, Pxy, Pxy, Pxy); + error |= cx_ecfp_add_point_no_throw(CX_CURVE_Ed25519, Pxy, Pxy, Pxy); + error |= cx_ecfp_add_point_no_throw(CX_CURVE_Ed25519, Pxy, Pxy, Pxy); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + if (error) { + return SW_SECURITY_INTERNAL; + } + memcpy(W, &Pxy[1], 32); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecadd(unsigned char *W, unsigned char *P, unsigned char *Q) { - unsigned char Pxy[65]; - unsigned char Qxy[65]; +int monero_ecadd(unsigned char *W, unsigned char *P, unsigned char *Q, size_t W_len, size_t P_len, + size_t Q_len) { + unsigned char Pxy[PXY_SIZE]; + unsigned char Qxy[PXY_SIZE]; + int error = 0; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!P || P_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!Q || Q_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } Pxy[0] = 0x02; - os_memmove(&Pxy[1], P, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(&Pxy[1], P, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); Qxy[0] = 0x02; - os_memmove(&Qxy[1], Q, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Qxy, sizeof(Qxy)); + memcpy(&Qxy[1], Q, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Qxy, sizeof(Qxy)); - cx_ecfp_add_point(CX_CURVE_Ed25519, Pxy, Pxy, Qxy, sizeof(Pxy)); + error |= cx_ecfp_add_point_no_throw(CX_CURVE_Ed25519, Pxy, Pxy, Qxy); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(W, &Pxy[1], 32); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(W, &Pxy[1], 32); + + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_ecsub(unsigned char *W, unsigned char *P, unsigned char *Q) { - unsigned char Pxy[65]; - unsigned char Qxy[65]; +int monero_ecsub(unsigned char *W, unsigned char *P, unsigned char *Q, size_t W_len, size_t P_len, + size_t Q_len) { + unsigned char Pxy[PXY_SIZE]; + unsigned char Qxy[PXY_SIZE]; + int error = 0; + + if (!W || W_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!P || P_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + if (!Q || Q_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } Pxy[0] = 0x02; - os_memmove(&Pxy[1], P, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(&Pxy[1], P, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); Qxy[0] = 0x02; - os_memmove(&Qxy[1], Q, 32); - cx_edward_decompress_point(CX_CURVE_Ed25519, Qxy, sizeof(Qxy)); + memcpy(&Qxy[1], Q, 32); + error |= cx_edwards_decompress_point_no_throw(CX_CURVE_Ed25519, Qxy, sizeof(Qxy)); - cx_math_sub(Qxy + 1, (unsigned char *)C_ED25519_FIELD, Qxy + 1, 32); - cx_ecfp_add_point(CX_CURVE_Ed25519, Pxy, Pxy, Qxy, sizeof(Pxy)); + error |= cx_math_sub(Qxy + 1, (unsigned char *)C_ED25519_FIELD, Qxy + 1, 32); + error |= cx_ecfp_add_point_no_throw(CX_CURVE_Ed25519, Pxy, Pxy, Qxy); - cx_edward_compress_point(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); - os_memmove(W, &Pxy[1], 32); + error |= cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, Pxy, sizeof(Pxy)); + memcpy(W, &Pxy[1], 32); + + if (error) { + return SW_SECURITY_INTERNAL; + } + return 0; } /* ----------------------------------------------------------------------- */ @@ -731,11 +1082,19 @@ void monero_ecsub(unsigned char *W, unsigned char *P, unsigned char *Q) { return hash; } */ -void monero_ecdhHash(unsigned char *x, unsigned char *k) { +int monero_ecdhHash(unsigned char *x, unsigned char *k, size_t k_len) { unsigned char data[38]; - os_memmove(data, "amount", 6); - os_memmove(data + 6, k, 32); - monero_keccak_F(data, 38, x); + int error; + + if (!k || k_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA; + } + + memcpy(data, "amount", 6); + memcpy(data + 6, k, 32); + error = monero_keccak_F(data, 38, x); + return error; } /* ----------------------------------------------------------------------- */ @@ -752,94 +1111,188 @@ void monero_ecdhHash(unsigned char *x, unsigned char *k) { return scalar; } */ -void monero_genCommitmentMask(unsigned char *c, unsigned char *sk) { +int monero_genCommitmentMask(unsigned char *c, unsigned char *sk, size_t c_len, size_t sk_len) { unsigned char data[15 + 32]; - os_memmove(data, "commitment_mask", 15); - os_memmove(data + 15, sk, 32); - monero_hash_to_scalar(c, data, 15 + 32); + int error; + + if (!sk || sk_len < 32) { + PRINTF("Buffer Error: %s:%d \n", __LINE__); + return SW_WRONG_DATA_RANGE; + } + + memcpy(data, "commitment_mask", 15); + memcpy(data + 15, sk, 32); + error = monero_hash_to_scalar(c, data, c_len, 15 + 32); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_addm(unsigned char *r, unsigned char *a, unsigned char *b) { +int monero_addm(unsigned char *r, unsigned char *a, unsigned char *b, size_t r_len, size_t a_len, + size_t b_len) { unsigned char ra[32]; unsigned char rb[32]; + int error; - monero_reverse32(ra, a); - monero_reverse32(rb, b); - cx_math_addm(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, r); + error = monero_reverse32(ra, a, sizeof(ra), a_len); + if (error) { + return error; + } + error = monero_reverse32(rb, b, sizeof(rb), b_len); + if (error) { + return error; + } + error = cx_math_addm_no_throw(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + + error = monero_reverse32(r, r, r_len, r_len); + if (error) { + return error; + } + + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_subm(unsigned char *r, unsigned char *a, unsigned char *b) { +int monero_subm(unsigned char *r, unsigned char *a, unsigned char *b, size_t r_len, size_t a_len, + size_t b_len) { unsigned char ra[32]; unsigned char rb[32]; + int error; - monero_reverse32(ra, a); - monero_reverse32(rb, b); - cx_math_subm(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, r); + error = monero_reverse32(ra, a, sizeof(ra), a_len); + if (error) { + return error; + } + error = monero_reverse32(rb, b, sizeof(rb), b_len); + if (error) { + return error; + } + error = cx_math_subm_no_throw(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + + error = monero_reverse32(r, r, r_len, r_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_multm(unsigned char *r, unsigned char *a, unsigned char *b) { +int monero_multm(unsigned char *r, unsigned char *a, unsigned char *b, size_t r_len, size_t a_len, + size_t b_len) { unsigned char ra[32]; unsigned char rb[32]; + int error; - monero_reverse32(ra, a); - monero_reverse32(rb, b); - cx_math_multm(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, r); + error = monero_reverse32(ra, a, sizeof(ra), a_len); + if (error) { + return error; + } + error = monero_reverse32(rb, b, sizeof(rb), b_len); + if (error) { + return error; + } + error = cx_math_multm_no_throw(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + + error = monero_reverse32(r, r, r_len, r_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_multm_8(unsigned char *r, unsigned char *a) { +int monero_multm_8(unsigned char *r, unsigned char *a, size_t r_len, size_t a_len) { unsigned char ra[32]; unsigned char rb[32]; + int error; - monero_reverse32(ra, a); - os_memset(rb, 0, 32); + error = monero_reverse32(ra, a, sizeof(ra), a_len); + if (error) { + return error; + } + explicit_bzero(rb, 32); rb[31] = 8; - cx_math_multm(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, r); + error = cx_math_multm_no_throw(r, ra, rb, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + error = monero_reverse32(r, r, r_len, r_len); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_reduce(unsigned char *r, unsigned char *a) { +int monero_reduce(unsigned char *r, unsigned char *a, size_t r_len, size_t a_len) { unsigned char ra[32]; - monero_reverse32(ra, a); - cx_math_modm(ra, 32, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, ra); + int error; + + error = monero_reverse32(ra, a, sizeof(ra), a_len); + if (error) { + return error; + } + error = cx_math_modm_no_throw(ra, 32, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + error = monero_reverse32(r, ra, r_len, sizeof(ra)); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_rng_mod_order(unsigned char *r) { +int monero_rng_mod_order(unsigned char *r, size_t r_len) { unsigned char rnd[32 + 8]; + int error; cx_rng(rnd, 32 + 8); - cx_math_modm(rnd, 32 + 8, (unsigned char *)C_ED25519_ORDER, 32); - monero_reverse32(r, rnd + 8); + error = cx_math_modm_no_throw(rnd, 32 + 8, (unsigned char *)C_ED25519_ORDER, 32); + if (error) { + return SW_SECURITY_INTERNAL; + } + error = monero_reverse32(r, rnd + 8, r_len, 32); + if (error) { + return error; + } + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -/* return 0 if ok, 1 if missing decimal */ -void monero_uint642str(uint64_t val, char *str, unsigned int str_len) { +unsigned int monero_uint642str(uint64_t val, char *str, unsigned int str_len) { char stramount[22]; unsigned int offset, len; - os_memset(str, 0, str_len); + if (!str) { + PRINTF("%d \n\n", __LINE__); + return SW_WRONG_DATA; + } + explicit_bzero(str, str_len); offset = 22; while (val) { @@ -849,28 +1302,32 @@ void monero_uint642str(uint64_t val, char *str, unsigned int str_len) { } len = sizeof(stramount) - offset; if (len > str_len) { - THROW(SW_WRONG_DATA_RANGE); + return SW_WRONG_DATA_RANGE; } - os_memmove(str, stramount + offset, len); + memcpy(str, stramount + offset, len); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -/* return 0 if ok, 1 if missing decimal */ -int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len) { +unsigned int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len) { // max uint64 is 18446744073709551616, aka 20 char, plus dot char stramount[22]; - unsigned int offset, len, ov; + unsigned int offset, len; - os_memset(str, 0, str_len); + if (!str) { + PRINTF("%d \n\n", __LINE__); + return SW_WRONG_DATA; + } + explicit_bzero(str, str_len); - os_memset(stramount, '0', sizeof(stramount)); + memset(stramount, '0', sizeof(stramount)); stramount[21] = 0; // special case if (xmr == 0) { str[0] = '0'; - return 1; + return 0; } // uint64 units to str @@ -887,7 +1344,7 @@ int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len) { // offset: 0-7 | 8 | 9-20 |21 // ---------------------- // value: xmr | . | units| 0 - os_memmove(stramount, stramount + 1, 8); + memmove(stramount, stramount + 1, 8); stramount[8] = '.'; offset = 0; while ((stramount[offset] == '0') && (stramount[offset] != '.')) { @@ -901,21 +1358,23 @@ int monero_amount2str(uint64_t xmr, char *str, unsigned int str_len) { len--; } len = len - offset + 1; - ov = 0; if (len > (str_len - 1)) { len = str_len - 1; - ov = 1; } - os_memmove(str, stramount + offset, len); - return ov; + memcpy(str, stramount + offset, len); + return 0; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -uint64_t monero_bamount2uint64(unsigned char *binary) { +uint64_t monero_bamount2uint64(unsigned char *binary, size_t binary_len) { uint64_t xmr; int i; + if (!binary || binary_len < 8) { + PRINTF("%d \n\n", __LINE__); + return 0; + } xmr = 0; for (i = 7; i >= 0; i--) { xmr = xmr * 256 + binary[i]; @@ -926,16 +1385,19 @@ uint64_t monero_bamount2uint64(unsigned char *binary) { /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_bamount2str(unsigned char *binary, char *str, unsigned int str_len) { - return monero_amount2str(monero_bamount2uint64(binary), str, str_len); +int monero_bamount2str(unsigned char *binary, char *str, size_t binary_len, unsigned int str_len) { + return monero_amount2str(monero_bamount2uint64(binary, binary_len), str, str_len); } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_vamount2str(unsigned char *binary, char *str, unsigned int str_len) { - // return monero_amount2str(monero_vamount2uint64(binary), str,str_len); uint64_t amount; - monero_decode_varint(binary, 8, &amount); + unsigned int out_len; + unsigned int error = monero_decode_varint(binary, 8, &amount, &out_len); + if (error) { + return error; + } return monero_amount2str(amount, str, str_len); } diff --git a/src/monero_dispatch.c b/src/monero_dispatch.c index fcbea880..07665604 100644 --- a/src/monero_dispatch.c +++ b/src/monero_dispatch.c @@ -22,34 +22,24 @@ #include "monero_api.h" #include "monero_vars.h" -void update_protocol() { +static void update_protocol(void) { G_monero_vstate.tx_state_ins = G_monero_vstate.io_ins; G_monero_vstate.tx_state_p1 = G_monero_vstate.io_p1; G_monero_vstate.tx_state_p2 = G_monero_vstate.io_p2; } -void clear_protocol() { +static void clear_protocol(void) { G_monero_vstate.tx_state_ins = 0; G_monero_vstate.tx_state_p1 = 0; G_monero_vstate.tx_state_p2 = 0; } -int check_potocol() { - /* if locked and pin is veririfed, unlock */ - if ((G_monero_vstate.protocol_barrier == PROTOCOL_LOCKED_UNLOCKABLE) && - (os_global_pin_is_validated() == PIN_VERIFIED)) { - G_monero_vstate.protocol_barrier = PROTOCOL_UNLOCKED; - } - - /* if we are locked, deny all command! */ - if (G_monero_vstate.protocol_barrier != PROTOCOL_UNLOCKED) { - return SW_SECURITY_LOCKED; - } - +static int check_protocol(void) { /* the first command enforce the protocol version until application quits */ switch (G_monero_vstate.io_protocol_version) { case 0x00: /* the first one: PCSC epoch */ case 0x03: /* protocol V3 */ + case 0x04: /* protocol V4 */ if (G_monero_vstate.protocol == 0xff) { G_monero_vstate.protocol = G_monero_vstate.io_protocol_version; } @@ -57,7 +47,7 @@ int check_potocol() { break; } // unknown protocol or hot protocol switch is not allowed - // FALL THROUGH + __attribute__((fallthrough)); default: return SW_PROTOCOL_NOT_SUPPORTED; @@ -71,7 +61,6 @@ int check_ins_access() { } switch (G_monero_vstate.io_ins) { - case INS_LOCK_DISPLAY: case INS_RESET: case INS_PUT_KEY: case INS_GET_KEY: @@ -83,6 +72,7 @@ int check_ins_access() { case INS_DERIVE_PUBLIC_KEY: case INS_DERIVE_SECRET_KEY: case INS_GEN_KEY_IMAGE: + case INS_DERIVE_VIEW_TAG: case INS_SECRET_KEY_TO_PUBLIC_KEY: case INS_SECRET_KEY_ADD: case INS_GENERATE_KEYPAIR: @@ -101,9 +91,6 @@ int check_ins_access() { case INS_OPEN_TX: case INS_SET_SIGNATURE_MODE: - if (os_global_pin_is_validated() != PIN_VERIFIED) { - return SW_SECURITY_PIN_LOCKED; - } return SW_OK; case INS_GEN_TXOUT_KEYS: @@ -111,14 +98,15 @@ int check_ins_access() { case INS_BLIND: case INS_VALIDATE: case INS_MLSAG: + case INS_CLSAG: case INS_GEN_COMMITMENT_MASK: - if (os_global_pin_is_validated() != PIN_VERIFIED) { - return SW_SECURITY_PIN_LOCKED; - } if (G_monero_vstate.tx_in_progress != 1) { return SW_COMMAND_NOT_ALLOWED; } return SW_OK; + case INS_LOCK_DISPLAY: + // Deprecated command + return SW_INS_NOT_SUPPORTED; } return SW_INS_NOT_SUPPORTED; @@ -127,7 +115,14 @@ int check_ins_access() { int monero_dispatch() { int sw; - if (((sw = check_potocol()) != SW_OK) || ((sw = check_ins_access() != SW_OK))) { + sw = check_protocol(); + if (sw != SW_OK) { + monero_io_discard(0); + return sw; + } + + sw = check_ins_access(); + if (sw != SW_OK) { monero_io_discard(0); return sw; } @@ -139,13 +134,6 @@ int monero_dispatch() { return sw; } - if (G_monero_vstate.io_ins == INS_LOCK_DISPLAY) { - sw = monero_apdu_lock(); - return sw; - } - - sw = 0x6F01; - switch (G_monero_vstate.io_ins) { /* --- KEYS --- */ case INS_PUT_KEY: @@ -186,6 +174,9 @@ int monero_dispatch() { case INS_GEN_KEY_IMAGE: sw = monero_apdu_generate_key_image(); break; + case INS_DERIVE_VIEW_TAG: + sw = monero_apdu_derive_view_tag(); + break; case INS_SECRET_KEY_ADD: sw = monero_apdu_sc_add(); break; @@ -232,10 +223,13 @@ int monero_dispatch() { case INS_OPEN_TX: // state machine check if (G_monero_vstate.tx_state_ins != 0) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // 2. command process sw = monero_apdu_open_tx(); + if (sw != 0 && sw != SW_OK) { + return sw; + } update_protocol(); break; @@ -249,7 +243,7 @@ int monero_dispatch() { // 1. state machine check if (G_monero_vstate.tx_in_progress != 0) { // Change sig mode during transacation is not allowed - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // 2. command process sw = monero_apdu_set_signature_mode(); @@ -261,14 +255,17 @@ int monero_dispatch() { if (G_monero_vstate.tx_in_progress == 1) { if ((G_monero_vstate.tx_state_ins != INS_OPEN_TX) && (G_monero_vstate.tx_state_ins != INS_STEALTH)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } if ((G_monero_vstate.io_p1 != 0) || (G_monero_vstate.io_p2 != 0)) { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } } // 2. command process sw = monero_apdu_stealth(); + if (sw != 0 && sw != SW_OK) { + return sw; + } if (G_monero_vstate.tx_in_progress == 1) { update_protocol(); } @@ -280,20 +277,23 @@ int monero_dispatch() { if ((G_monero_vstate.tx_state_ins != INS_OPEN_TX) && (G_monero_vstate.tx_state_ins != INS_GEN_TXOUT_KEYS) && (G_monero_vstate.tx_state_ins != INS_STEALTH)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } if (G_monero_vstate.protocol == 3) { if ((G_monero_vstate.tx_state_ins != INS_OPEN_TX) && (G_monero_vstate.tx_state_ins != INS_GEN_TXOUT_KEYS) && (G_monero_vstate.tx_state_ins != INS_STEALTH)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } } if ((G_monero_vstate.io_p1 != 0) || (G_monero_vstate.io_p2 != 0)) { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } // 2. command process sw = monero_apu_generate_txout_keys(); + if (sw != 0 && sw != SW_OK) { + return sw; + } update_protocol(); break; @@ -302,7 +302,7 @@ int monero_dispatch() { // 1. state machine check if ((G_monero_vstate.tx_state_ins != INS_GEN_TXOUT_KEYS) && (G_monero_vstate.tx_state_ins != INS_PREFIX_HASH)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // init prefixhash state machine if (G_monero_vstate.tx_state_ins == INS_GEN_TXOUT_KEYS) { @@ -313,19 +313,19 @@ int monero_dispatch() { // check new state is allowed if (G_monero_vstate.tx_state_p1 == 0) { if (1 != G_monero_vstate.io_p1) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else if (G_monero_vstate.tx_state_p1 == 1) { if ((G_monero_vstate.io_p1 != 2) || (G_monero_vstate.io_p2 != 1)) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else if (G_monero_vstate.tx_state_p1 == 2) { if ((G_monero_vstate.io_p1 != 2) || (G_monero_vstate.io_p2 - 1 != G_monero_vstate.tx_state_p2)) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // 2. command process if (G_monero_vstate.io_p1 == 1) { @@ -333,7 +333,10 @@ int monero_dispatch() { } else if (G_monero_vstate.io_p1 == 2) { sw = monero_apdu_prefix_hash_update(); } else { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; + } + if (sw != 0 && sw != SW_OK) { + return sw; } update_protocol(); break; @@ -344,15 +347,18 @@ int monero_dispatch() { if (G_monero_vstate.protocol == 3) { if ((G_monero_vstate.tx_state_ins != INS_PREFIX_HASH) && (G_monero_vstate.tx_state_ins != INS_GEN_COMMITMENT_MASK)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } } if ((G_monero_vstate.io_p1 != 0) || (G_monero_vstate.io_p2 != 0)) { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } // 2. command process sw = monero_apdu_gen_commitment_mask(); + if (sw != 0 && sw != SW_OK) { + return sw; + } update_protocol(); break; @@ -363,22 +369,25 @@ int monero_dispatch() { if (G_monero_vstate.protocol == 3) { if ((G_monero_vstate.tx_state_ins != INS_PREFIX_HASH) && (G_monero_vstate.tx_state_ins != INS_BLIND)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } } } else if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { if ((G_monero_vstate.tx_state_ins != INS_GEN_COMMITMENT_MASK) && (G_monero_vstate.tx_state_ins != INS_BLIND)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } } else { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // 2. command process if ((G_monero_vstate.io_p1 != 0) || (G_monero_vstate.io_p2 != 0)) { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } sw = monero_apdu_blind(); + if (sw != 0 && sw != SW_OK) { + return sw; + } update_protocol(); break; @@ -387,7 +396,7 @@ int monero_dispatch() { // 1. state machine check if ((G_monero_vstate.tx_state_ins != INS_BLIND) && (G_monero_vstate.tx_state_ins != INS_VALIDATE)) { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // init PREHASH state machine if (G_monero_vstate.tx_state_ins == INS_BLIND) { @@ -395,20 +404,20 @@ int monero_dispatch() { G_monero_vstate.tx_state_p1 = 1; G_monero_vstate.tx_state_p2 = 0; if ((G_monero_vstate.io_p1 != 1) || (G_monero_vstate.io_p2 != 1)) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } // check new state is allowed if (G_monero_vstate.tx_state_p1 == G_monero_vstate.io_p1) { if (G_monero_vstate.tx_state_p2 != G_monero_vstate.io_p2 - 1) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else if (G_monero_vstate.tx_state_p1 == G_monero_vstate.io_p1 - 1) { if (1 != G_monero_vstate.io_p2) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } // 2. command process if (G_monero_vstate.io_p1 == 1) { @@ -418,7 +427,10 @@ int monero_dispatch() { } else if (G_monero_vstate.io_p1 == 3) { sw = monero_apdu_mlsag_prehash_finalize(); } else { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; + } + if (sw != 0 && sw != SW_OK) { + return sw; } update_protocol(); break; @@ -426,30 +438,31 @@ int monero_dispatch() { /* --- MLSAG --- */ case INS_MLSAG: // 1. state machine check - if ((G_monero_vstate.tx_state_ins != INS_VALIDATE) && - (G_monero_vstate.tx_state_ins != INS_MLSAG)) { - THROW(SW_COMMAND_NOT_ALLOWED); + if ((G_monero_vstate.tx_state_ins != INS_VALIDATE) && // + (G_monero_vstate.tx_state_ins != INS_MLSAG) && // + (G_monero_vstate.protocol != 3 && G_monero_vstate.protocol != 4)) { + return SW_COMMAND_NOT_ALLOWED; } if (G_monero_vstate.tx_state_ins == INS_VALIDATE) { if ((G_monero_vstate.tx_state_p1 != 3) || (G_monero_vstate.io_p1 != 1) || (G_monero_vstate.io_p2 != 0)) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else { if (G_monero_vstate.tx_state_p1 == 1) { if (2 != G_monero_vstate.io_p1) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else if (G_monero_vstate.tx_state_p1 == 2) { if ((2 != G_monero_vstate.io_p1) && (3 != G_monero_vstate.io_p1)) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else if (G_monero_vstate.tx_state_p1 == 3) { if (1 != G_monero_vstate.io_p1) { - THROW(SW_SUBCOMMAND_NOT_ALLOWED); + return SW_SUBCOMMAND_NOT_ALLOWED; } } else { - THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } } @@ -461,7 +474,57 @@ int monero_dispatch() { } else if (G_monero_vstate.io_p1 == 3) { sw = monero_apdu_mlsag_sign(); } else { - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; + } + if (sw != 0 && sw != SW_OK) { + return sw; + } + update_protocol(); + break; + + /* --- CLSAG --- */ + case INS_CLSAG: + // 1. state machine check + if ((G_monero_vstate.tx_state_ins != INS_VALIDATE) && // + (G_monero_vstate.tx_state_ins != INS_CLSAG) && // + (G_monero_vstate.protocol != 4)) { + return SW_COMMAND_NOT_ALLOWED; + } + if (G_monero_vstate.tx_state_ins == INS_VALIDATE) { + if ((G_monero_vstate.tx_state_p1 != 3) || (G_monero_vstate.io_p1 != 1) || + (G_monero_vstate.io_p2 != 0)) { + return SW_SUBCOMMAND_NOT_ALLOWED; + } + } else { + if (G_monero_vstate.tx_state_p1 == 1) { + if (2 != G_monero_vstate.io_p1) { + return SW_SUBCOMMAND_NOT_ALLOWED; + } + } else if (G_monero_vstate.tx_state_p1 == 2) { + if ((2 != G_monero_vstate.io_p1) && (3 != G_monero_vstate.io_p1)) { + return SW_SUBCOMMAND_NOT_ALLOWED; + } + } else if (G_monero_vstate.tx_state_p1 == 3) { + if (1 != G_monero_vstate.io_p1) { + return SW_SUBCOMMAND_NOT_ALLOWED; + } + } else { + return SW_COMMAND_NOT_ALLOWED; + } + } + + // 2. command process + if (G_monero_vstate.io_p1 == 1) { + sw = monero_apdu_clsag_prepare(); + } else if (G_monero_vstate.io_p1 == 2) { + sw = monero_apdu_clsag_hash(); + } else if (G_monero_vstate.io_p1 == 3) { + sw = monero_apdu_clsag_sign(); + } else { + return SW_WRONG_P1P2; + } + if (sw != 0 && sw != SW_OK) { + return sw; } update_protocol(); break; @@ -469,8 +532,7 @@ int monero_dispatch() { /* --- KEYS --- */ default: - THROW(SW_INS_NOT_SUPPORTED); - break; + return SW_INS_NOT_SUPPORTED; } return sw; } diff --git a/src/monero_init.c b/src/monero_init.c index 45d643d1..f22d905f 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -27,21 +27,21 @@ /* ----------------------*/ const unsigned char C_MAGIC[8] = {'M', 'O', 'N', 'E', 'R', 'O', 'H', 'W'}; -const unsigned char C_FAKE_SEC_VIEW_KEY[32] = { +const unsigned char C_FAKE_SEC_VIEW_KEY[KEY_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -const unsigned char C_FAKE_SEC_SPEND_KEY[32] = { +const unsigned char C_FAKE_SEC_SPEND_KEY[KEY_SIZE] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* ----------------------------------------------------------------------- */ /* --- Boot --- */ /* ----------------------------------------------------------------------- */ -void monero_init() { - os_memset(&G_monero_vstate, 0, sizeof(monero_v_state_t)); +unsigned int monero_init() { + explicit_bzero(&G_monero_vstate, sizeof(monero_v_state_t)); // first init ? - if (os_memcmp((void*)N_monero_pstate->magic, (void*)C_MAGIC, sizeof(C_MAGIC)) != 0) { + if (memcmp((void*)N_monero_pstate->magic, (void*)C_MAGIC, sizeof(C_MAGIC)) != 0) { #if defined(MONERO_ALPHA) || defined(MONERO_BETA) monero_install(STAGENET); #else @@ -50,32 +50,30 @@ void monero_init() { } G_monero_vstate.protocol = 0xff; - G_monero_vstate.protocol_barrier = PROTOCOL_UNLOCKED; // load key - monero_init_private_key(); + unsigned error = monero_init_private_key(); + if (error) { + return error; + } // ux conf - monero_init_ux(); + error = monero_init_ux(); + if (error) { + return error; + } // Let's go! G_monero_vstate.state = STATE_IDLE; + return 0; } /* ----------------------------------------------------------------------- */ /* --- init private keys --- */ /* ----------------------------------------------------------------------- */ -void monero_wipe_private_key() { - os_memset(G_monero_vstate.a, 0, 32); - os_memset(G_monero_vstate.b, 0, 32); - os_memset(G_monero_vstate.A, 0, 32); - os_memset(G_monero_vstate.B, 0, 32); - os_memset(&G_monero_vstate.spk, 0, sizeof(G_monero_vstate.spk)); - G_monero_vstate.key_set = 0; -} - -void monero_init_private_key() { +int monero_init_private_key(void) { unsigned int path[5]; - unsigned char seed[32]; - unsigned char chain[32]; + unsigned char seed[64]; + unsigned char chain[KEY_SIZE]; + int error; // generate account keys @@ -86,72 +84,95 @@ void monero_init_private_key() { path[2] = 0x80000000 | N_monero_pstate->account_id; path[3] = 0x00000000; path[4] = 0x00000000; - os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 5, seed, chain); + if (os_derive_bip32_no_throw(CX_CURVE_SECP256K1, path, 5, seed, chain)) { + return SW_SECURITY_INTERNAL; + } switch (N_monero_pstate->key_mode) { case KEY_MODE_SEED: - monero_keccak_F(seed, 32, G_monero_vstate.b); - monero_reduce(G_monero_vstate.b, G_monero_vstate.b); - monero_keccak_F(G_monero_vstate.b, 32, G_monero_vstate.a); - monero_reduce(G_monero_vstate.a, G_monero_vstate.a); + error = monero_keccak_F(seed, KEY_SIZE, G_monero_vstate.b); + if (error) { + return error; + } + + error = monero_reduce(G_monero_vstate.b, G_monero_vstate.b, sizeof(G_monero_vstate.b), + sizeof(G_monero_vstate.b)); + if (error) { + return error; + } + + error = monero_keccak_F(G_monero_vstate.b, KEY_SIZE, G_monero_vstate.a); + if (error) { + return error; + } + + error = monero_reduce(G_monero_vstate.a, G_monero_vstate.a, sizeof(G_monero_vstate.a), + sizeof(G_monero_vstate.a)); + if (error) { + return error; + } break; case KEY_MODE_EXTERNAL: - os_memmove(G_monero_vstate.a, (void*)N_monero_pstate->a, 32); - os_memmove(G_monero_vstate.b, (void*)N_monero_pstate->b, 32); + memcpy(G_monero_vstate.a, (void*)N_monero_pstate->a, KEY_SIZE); + memcpy(G_monero_vstate.b, (void*)N_monero_pstate->b, KEY_SIZE); break; default: - THROW(SW_SECURITY_LOAD_KEY); - return; + return SW_SECURITY_LOAD_KEY; + } + error = monero_ecmul_G(G_monero_vstate.A, G_monero_vstate.a, sizeof(G_monero_vstate.A), + sizeof(G_monero_vstate.a)); + if (error) { + return error; + } + error = monero_ecmul_G(G_monero_vstate.B, G_monero_vstate.b, sizeof(G_monero_vstate.B), + sizeof(G_monero_vstate.b)); + if (error) { + return error; } - monero_ecmul_G(G_monero_vstate.A, G_monero_vstate.a); - monero_ecmul_G(G_monero_vstate.B, G_monero_vstate.b); // generate key protection - monero_aes_derive(&G_monero_vstate.spk, chain, G_monero_vstate.a, G_monero_vstate.b); + error = monero_aes_derive(&G_monero_vstate.spk, chain, G_monero_vstate.a, G_monero_vstate.b); + if (error) { + return error; + } G_monero_vstate.key_set = 1; + return 0; } /* ----------------------------------------------------------------------- */ /* --- Set up ui/ux --- */ /* ----------------------------------------------------------------------- */ -void monero_init_ux() { - monero_base58_public_key(G_monero_vstate.ux_address, G_monero_vstate.A, G_monero_vstate.B, 0, - NULL); - os_memset(G_monero_vstate.ux_wallet_public_short_address, '.', - sizeof(G_monero_vstate.ux_wallet_public_short_address)); +int monero_init_ux() { + int error = monero_base58_public_key(G_monero_vstate.ux_address, G_monero_vstate.A, + G_monero_vstate.B, 0, NULL); + if (error) { + return error; + } -#ifdef HAVE_UX_FLOW + memset(G_monero_vstate.ux_wallet_public_short_address, '.', + sizeof(G_monero_vstate.ux_wallet_public_short_address)); -#ifdef UI_NANO_X +#ifndef TARGET_NANOS snprintf(G_monero_vstate.ux_wallet_account_name, sizeof(G_monero_vstate.ux_wallet_account_name), "XMR / %d", N_monero_pstate->account_id); - os_memmove(G_monero_vstate.ux_wallet_public_short_address, G_monero_vstate.ux_address, 5); - os_memmove(G_monero_vstate.ux_wallet_public_short_address + 7, - G_monero_vstate.ux_address + 95 - 5, 5); + memcpy(G_monero_vstate.ux_wallet_public_short_address, G_monero_vstate.ux_address, 5); + memcpy(G_monero_vstate.ux_wallet_public_short_address + 7, G_monero_vstate.ux_address + 95 - 5, + 5); G_monero_vstate.ux_wallet_public_short_address[12] = 0; #else snprintf(G_monero_vstate.ux_wallet_account_name, sizeof(G_monero_vstate.ux_wallet_account_name), " XMR / %d", N_monero_pstate->account_id); - os_memmove(G_monero_vstate.ux_wallet_public_short_address, G_monero_vstate.ux_address, 4); - os_memmove(G_monero_vstate.ux_wallet_public_short_address + 6, - G_monero_vstate.ux_address + 95 - 4, 4); + memcpy(G_monero_vstate.ux_wallet_public_short_address, G_monero_vstate.ux_address, 4); + memcpy(G_monero_vstate.ux_wallet_public_short_address + 6, G_monero_vstate.ux_address + 95 - 4, + 4); G_monero_vstate.ux_wallet_public_short_address[10] = 0; #endif -#else - - snprintf(G_monero_vstate.ux_wallet_account_name, sizeof(G_monero_vstate.ux_wallet_account_name), - "XMR / %d", N_monero_pstate->account_id); - os_memmove(G_monero_vstate.ux_wallet_public_short_address, G_monero_vstate.ux_address, 5); - os_memmove(G_monero_vstate.ux_wallet_public_short_address + 7, - G_monero_vstate.ux_address + 95 - 5, 5); - G_monero_vstate.ux_wallet_public_short_address[12] = 0; - -#endif + return 0; } /* ----------------------------------------------------------------------- */ @@ -177,57 +198,55 @@ void monero_install(unsigned char netId) { /* ----------------------------------------------------------------------- */ /* --- Reset --- */ /* ----------------------------------------------------------------------- */ -#define MONERO_SUPPORTED_CLIENT_SIZE 1 -const char* const monero_supported_client[MONERO_SUPPORTED_CLIENT_SIZE] = { - "0.16.0.", -}; +// Accept the following versions and their derivates +const char* const supported_clients[] = {"0.18."}; +#define MONERO_SUPPORTED_CLIENT_SIZE (sizeof(supported_clients) / sizeof(supported_clients[0])) + +// Explicitly refuse the following versions +const char* const refused_clients[] = {"0.18.0.0."}; +#define MONERO_REFUSED_CLIENT_SIZE (sizeof(refused_clients) / sizeof(refused_clients[0])) + +static bool is_client_version_valid(const char* client_version) { + // Check if version is explicitly refused + for (uint32_t i = 0; i < MONERO_REFUSED_CLIENT_SIZE; ++i) { + if (strcmp(PIC(refused_clients[i]), client_version) == 0) { + return false; + } + } + // Check if version is supported + for (uint32_t i = 0; i < MONERO_SUPPORTED_CLIENT_SIZE; ++i) { + // Use strncmp to allow supported version prefixing client version + unsigned int supported_clients_len = strlen(PIC(supported_clients[i])); + if (strncmp(PIC(supported_clients[i]), client_version, supported_clients_len) == 0) { + return true; + } + } + return false; +} int monero_apdu_reset() { unsigned int client_version_len; char client_version[16]; + memset(client_version, '\0', 16); client_version_len = G_monero_vstate.io_length - G_monero_vstate.io_offset; if (client_version_len > 14) { - THROW(SW_CLIENT_NOT_SUPPORTED + 1); + return SW_CLIENT_NOT_SUPPORTED + 1; } monero_io_fetch((unsigned char*)&client_version[0], client_version_len); + // Add '.' suffix to avoid 'X.1' prefixing 'X.10' client_version[client_version_len] = '.'; - client_version_len++; - client_version[client_version_len] = 0; - unsigned int i = 0; - while (i < MONERO_SUPPORTED_CLIENT_SIZE) { - unsigned int monero_supported_client_len = strlen((char*)PIC(monero_supported_client[i])); - if ((monero_supported_client_len <= client_version_len) && - (os_memcmp((char*)PIC(monero_supported_client[i]), client_version, - monero_supported_client_len) == 0)) { - break; - } - i++; - } - if (i == MONERO_SUPPORTED_CLIENT_SIZE) { - THROW(SW_CLIENT_NOT_SUPPORTED); + + if (!is_client_version_valid(client_version)) { + return SW_CLIENT_NOT_SUPPORTED; } monero_io_discard(0); - monero_init(); + unsigned int error = monero_init(); + if (error) { + return error; + } monero_io_insert_u8(MONERO_VERSION_MAJOR); monero_io_insert_u8(MONERO_VERSION_MINOR); monero_io_insert_u8(MONERO_VERSION_MICRO); return SW_OK; } - -/* ----------------------------------------------------------------------- */ -/* --- LOCK --- */ -/* ----------------------------------------------------------------------- */ -int monero_apdu_lock() { - monero_io_discard(0); - monero_lock_and_throw(SW_SECURITY_LOCKED); - return SW_SECURITY_LOCKED; -} - -void monero_lock_and_throw(int sw) { - G_monero_vstate.protocol_barrier = PROTOCOL_LOCKED; - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Security Err"); - snprintf(G_monero_vstate.ux_info2, sizeof(G_monero_vstate.ux_info2), "%x", sw); - ui_menu_info_display(0); - THROW(sw); -} diff --git a/src/monero_io.c b/src/monero_io.c index 65c9b5a4..de06eb3d 100644 --- a/src/monero_io.c +++ b/src/monero_io.c @@ -21,6 +21,8 @@ #include "monero_types.h" #include "monero_api.h" #include "monero_vars.h" +#include "os_utils.h" +#include "read.h" #if defined(IODUMMYCRYPT) #warning IODUMMYCRYPT activated @@ -38,20 +40,10 @@ /* ----------------------------------------------------------------------- */ /* MISC */ /* ----------------------------------------------------------------------- */ -void monero_io_set_offset(unsigned int offset) { - if (offset == IO_OFFSET_END) { - G_monero_vstate.io_offset = G_monero_vstate.io_length; - } else if (offset == IO_OFFSET_MARK) { - G_monero_vstate.io_offset = G_monero_vstate.io_mark; - } else if (offset < G_monero_vstate.io_length) { - G_monero_vstate.io_offset = offset; - } else { - THROW(ERROR_IO_OFFSET); - } +void monero_io_mark(void) { + G_monero_vstate.io_mark = G_monero_vstate.io_offset; } -void monero_io_mark() { G_monero_vstate.io_mark = G_monero_vstate.io_offset; } - void monero_io_inserted(unsigned int len) { G_monero_vstate.io_offset += len; G_monero_vstate.io_length += len; @@ -66,7 +58,9 @@ void monero_io_discard(int clear) { } } -void monero_io_clear() { os_memset(G_monero_vstate.io_buffer, 0, MONERO_IO_BUFFER_LENGTH); } +void monero_io_clear(void) { + explicit_bzero(G_monero_vstate.io_buffer, MONERO_IO_BUFFER_LENGTH); +} /* ----------------------------------------------------------------------- */ /* INSERT data to be sent */ @@ -74,29 +68,35 @@ void monero_io_clear() { os_memset(G_monero_vstate.io_buffer, 0, MONERO_IO_BUFFE void monero_io_hole(unsigned int sz) { if ((G_monero_vstate.io_length + sz) > MONERO_IO_BUFFER_LENGTH) { - THROW(ERROR_IO_FULL); + send_error_and_kill_app(ERROR_IO_FULL); } - os_memmove(G_monero_vstate.io_buffer + G_monero_vstate.io_offset + sz, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.io_length - G_monero_vstate.io_offset); + memmove(G_monero_vstate.io_buffer + G_monero_vstate.io_offset + sz, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + G_monero_vstate.io_length - G_monero_vstate.io_offset); G_monero_vstate.io_length += sz; } void monero_io_insert(unsigned char const* buff, unsigned int len) { + if (!buff) { + send_error_and_kill_app(ERROR_IO_FULL); + } monero_io_hole(len); - os_memmove(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, buff, len); + memcpy(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, buff, len); G_monero_vstate.io_offset += len; } void monero_io_insert_hmac_for(unsigned char* buffer, int len, int type) { + if (!buffer) { + send_error_and_kill_app(ERROR_IO_FULL); + } // for now, only 32bytes block are allowed if (len != 32) { - THROW(SW_WRONG_DATA); + send_error_and_kill_app(ERROR_IO_FULL); } unsigned char hmac[32 + 1 + 4]; - os_memmove(hmac, buffer, 32); + memcpy(hmac, buffer, 32); hmac[32] = type; if (type == TYPE_ALPHA) { hmac[33] = (G_monero_vstate.tx_sign_cnt >> 0) & 0xFF; @@ -113,23 +113,29 @@ void monero_io_insert_hmac_for(unsigned char* buffer, int len, int type) { monero_io_insert(hmac, 32); } -void monero_io_insert_encrypt(unsigned char* buffer, int len, int type) { +void monero_io_insert_encrypt(unsigned char* buffer, size_t len, int type) { + if (!buffer) { + send_error_and_kill_app(ERROR_IO_FULL); + } + // for now, only 32bytes block are allowed if (len != 32) { - THROW(SW_WRONG_DATA); + send_error_and_kill_app(ERROR_IO_FULL); } monero_io_hole(len); #if defined(IODUMMYCRYPT) - for (int i = 0; i < len; i++) { + for (unsigned int i = 0; i < len; i++) { G_monero_vstate.io_buffer[G_monero_vstate.io_offset + i] = buffer[i] ^ 0x55; } #elif defined(IONOCRYPT) - os_memmove(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, buffer, len); + memcpy(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, buffer, len); #else - cx_aes(&G_monero_vstate.spk, CX_ENCRYPT | CX_CHAIN_CBC | CX_LAST | CX_PAD_NONE, buffer, len, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len); + if (cx_aes_no_throw(&G_monero_vstate.spk, CX_ENCRYPT | CX_CHAIN_CBC | CX_LAST | CX_PAD_NONE, + buffer, len, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, &len)) { + send_error_and_kill_app(SW_SECURITY_INTERNAL); + } #endif G_monero_vstate.io_offset += len; if (G_monero_vstate.tx_in_progress) { @@ -140,25 +146,13 @@ void monero_io_insert_encrypt(unsigned char* buffer, int len, int type) { void monero_io_insert_u32(unsigned int v32) { monero_io_hole(4); - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] = v32 >> 24; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] = v32 >> 16; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 2] = v32 >> 8; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 3] = v32 >> 0; + U4BE_ENCODE(G_monero_vstate.io_buffer, G_monero_vstate.io_offset, v32); G_monero_vstate.io_offset += 4; } -void monero_io_insert_u24(unsigned int v24) { - monero_io_hole(3); - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] = v24 >> 16; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] = v24 >> 8; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 2] = v24 >> 0; - G_monero_vstate.io_offset += 3; -} - void monero_io_insert_u16(unsigned int v16) { monero_io_hole(2); - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] = v16 >> 8; - G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] = v16 >> 0; + U2BE_ENCODE(G_monero_vstate.io_buffer, G_monero_vstate.io_offset, v16); G_monero_vstate.io_offset += 2; } @@ -196,31 +190,42 @@ void monero_io_insert_tlv(unsigned int T, unsigned int L, unsigned char const* V /* ----------------------------------------------------------------------- */ /* FECTH data from received buffer */ /* ----------------------------------------------------------------------- */ -int monero_io_fetch_available() { return G_monero_vstate.io_length - G_monero_vstate.io_offset; } +int monero_io_fetch_available(void) { + return G_monero_vstate.io_length - G_monero_vstate.io_offset; +} void monero_io_assert_available(int sz) { if ((G_monero_vstate.io_length - G_monero_vstate.io_offset) < sz) { - THROW(SW_WRONG_LENGTH + (sz & 0xFF)); + send_error_and_kill_app(ERROR_IO_FULL); } } +void monero_io_skip(int len) { + monero_io_assert_available(len); + G_monero_vstate.io_offset += len; +} + int monero_io_fetch(unsigned char* buffer, int len) { monero_io_assert_available(len); - if (buffer) { - os_memmove(buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len); + if (!buffer) { + send_error_and_kill_app(ERROR_IO_FULL); } + memcpy(buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len); G_monero_vstate.io_offset += len; return len; } -static void monero_io_verify_hmac_for(const unsigned char* buffer, int len, - unsigned char* expected_hmac, int type) { +static int monero_io_verify_hmac_for(const unsigned char* buffer, int len, + unsigned char* expected_hmac, int type) { + if (!buffer || !expected_hmac) { + return SW_WRONG_DATA; + } // for now, only 32bytes block allowed if (len != 32) { - THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } unsigned char hmac[37]; - os_memmove(hmac, buffer, 32); + memcpy(hmac, buffer, 32); hmac[32] = type; if (type == TYPE_ALPHA) { hmac[33] = (G_monero_vstate.tx_sign_cnt >> 0) & 0xFF; @@ -234,36 +239,42 @@ static void monero_io_verify_hmac_for(const unsigned char* buffer, int len, hmac[36] = 0; } cx_hmac_sha256(G_monero_vstate.hmac_key, 32, hmac, 37, hmac, 32); - if (os_memcmp(hmac, expected_hmac, 32)) { - monero_lock_and_throw(SW_SECURITY_HMAC); + if (memcmp(hmac, expected_hmac, 32) != 0) { + return SW_SECURITY_HMAC; } + return 0; } -int monero_io_fetch_decrypt(unsigned char* buffer, int len, int type) { +int monero_io_fetch_decrypt(unsigned char* buffer, size_t len, int type) { + int error = 0; // for now, only 32bytes block allowed if (len != 32) { - THROW(SW_WRONG_LENGTH); + return SW_WRONG_LENGTH; } if (G_monero_vstate.tx_in_progress) { monero_io_assert_available(len + 32); - monero_io_verify_hmac_for(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset + len, - type); + error = monero_io_verify_hmac_for( + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset + len, type); + if (error) { + return error; + } } else { monero_io_assert_available(len); } if (buffer) { #if defined(IODUMMYCRYPT) - for (int i = 0; i < len; i++) { + for (unsigned int i = 0; i < len; i++) { buffer[i] = G_monero_vstate.io_buffer[G_monero_vstate.io_offset + i] ^ 0x55; } #elif defined(IONOCRYPT) - os_memmove(buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len); + memcpy(buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len); #else // IOCRYPT - cx_aes(&G_monero_vstate.spk, CX_DECRYPT | CX_CHAIN_CBC | CX_LAST | CX_PAD_NONE, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len, buffer, len); + error = cx_aes_no_throw( + &G_monero_vstate.spk, CX_DECRYPT | CX_CHAIN_CBC | CX_LAST | CX_PAD_NONE, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, len, buffer, &len); #endif } G_monero_vstate.io_offset += len; @@ -273,57 +284,68 @@ int monero_io_fetch_decrypt(unsigned char* buffer, int len, int type) { if (buffer) { switch (type) { case TYPE_SCALAR: - monero_check_scalar_range_1N(buffer); - break; + return monero_check_scalar_range_1N(buffer, len); case TYPE_AMOUNT_KEY: case TYPE_DERIVATION: case TYPE_ALPHA: - monero_check_scalar_not_null(buffer); - break; + return monero_check_scalar_not_null(buffer); default: - THROW(SW_SECURITY_INTERNAL); + return SW_SECURITY_INTERNAL; } } - return len; + return error; } -int monero_io_fetch_decrypt_key(unsigned char* buffer) { +int monero_io_fetch_decrypt_key(unsigned char* buffer, size_t buffer_size) { unsigned char* k; + int error = 0; + if (!buffer) { + return SW_WRONG_DATA; + } + if (buffer_size > 32) { + return SW_WRONG_DATA_RANGE; + } monero_io_assert_available(32); k = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; // view? - if (os_memcmp(k, C_FAKE_SEC_VIEW_KEY, 32) == 0) { + if (memcmp(k, C_FAKE_SEC_VIEW_KEY, 32) == 0) { G_monero_vstate.io_offset += 32; if (G_monero_vstate.tx_in_progress) { monero_io_assert_available(32); - monero_io_verify_hmac_for(C_FAKE_SEC_VIEW_KEY, 32, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - TYPE_SCALAR); + error = monero_io_verify_hmac_for(C_FAKE_SEC_VIEW_KEY, 32, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + TYPE_SCALAR); + if (error) { + return error; + } G_monero_vstate.io_offset += 32; } - os_memmove(buffer, G_monero_vstate.a, 32); - return 32; + memcpy(buffer, G_monero_vstate.a, 32); + return 0; } // spend? - else if (os_memcmp(k, C_FAKE_SEC_SPEND_KEY, 32) == 0) { + else if (memcmp(k, C_FAKE_SEC_SPEND_KEY, 32) == 0) { switch (G_monero_vstate.io_ins) { case INS_VERIFY_KEY: case INS_DERIVE_SECRET_KEY: // case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: break; default: - THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } G_monero_vstate.io_offset += 32; if (G_monero_vstate.tx_in_progress) { monero_io_assert_available(32); - monero_io_verify_hmac_for(C_FAKE_SEC_SPEND_KEY, 32, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - TYPE_SCALAR); + error = monero_io_verify_hmac_for(C_FAKE_SEC_SPEND_KEY, 32, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + TYPE_SCALAR); + if (error) { + return error; + } } - os_memmove(buffer, G_monero_vstate.b, 32); - return 32; + memcpy(buffer, G_monero_vstate.b, 32); + return 0; } // else else { @@ -331,45 +353,35 @@ int monero_io_fetch_decrypt_key(unsigned char* buffer) { } } -uint64_t monero_io_fetch_varint() { - uint64_t v64; - G_monero_vstate.io_offset += - monero_decode_varint(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - MIN(8, G_monero_vstate.io_length - G_monero_vstate.io_offset), &v64); - return v64; +int monero_io_fetch_varint(uint64_t* out_v64) { + if (!out_v64) { + return SW_WRONG_DATA; + } + unsigned int out_len = 0; + unsigned int error = monero_decode_varint( + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + MIN(8, G_monero_vstate.io_length - G_monero_vstate.io_offset), out_v64, &out_len); + G_monero_vstate.io_offset += out_len; + return error; } -unsigned int monero_io_fetch_u32() { +unsigned int monero_io_fetch_u32(void) { unsigned int v32; monero_io_assert_available(4); - v32 = ((G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] << 24) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] << 16) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 2] << 8) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 3] << 0)); + v32 = read_u32_be(G_monero_vstate.io_buffer, G_monero_vstate.io_offset); G_monero_vstate.io_offset += 4; return v32; } -unsigned int monero_io_fetch_u24() { - unsigned int v24; - monero_io_assert_available(3); - v24 = ((G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] << 16) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] << 8) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 2] << 0)); - G_monero_vstate.io_offset += 3; - return v24; -} - -unsigned int monero_io_fetch_u16() { +unsigned int monero_io_fetch_u16(void) { unsigned int v16; monero_io_assert_available(2); - v16 = ((G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 0] << 8) | - (G_monero_vstate.io_buffer[G_monero_vstate.io_offset + 1] << 0)); + v16 = read_u16_be(G_monero_vstate.io_buffer, G_monero_vstate.io_offset); G_monero_vstate.io_offset += 2; return v16; } -unsigned int monero_io_fetch_u8() { +unsigned int monero_io_fetch_u8(void) { unsigned int v8; monero_io_assert_available(1); v8 = G_monero_vstate.io_buffer[G_monero_vstate.io_offset]; @@ -430,10 +442,10 @@ int monero_io_do(unsigned int io_flags) { else { G_monero_vstate.io_offset = 0; if (G_monero_vstate.io_length > MAX_OUT) { - THROW(SW_IO_FULL); + return SW_IO_FULL; } - os_memmove(G_io_apdu_buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.io_length); + memcpy(G_io_apdu_buffer, G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + G_monero_vstate.io_length); if (io_flags & IO_RETURN_AFTER_TX) { monero_io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, G_monero_vstate.io_length); @@ -450,10 +462,9 @@ int monero_io_do(unsigned int io_flags) { G_monero_vstate.io_ins = G_io_apdu_buffer[1]; G_monero_vstate.io_p1 = G_io_apdu_buffer[2]; G_monero_vstate.io_p2 = G_io_apdu_buffer[3]; - G_monero_vstate.io_lc = 0; G_monero_vstate.io_le = 0; G_monero_vstate.io_lc = G_io_apdu_buffer[4]; - os_memmove(G_monero_vstate.io_buffer, G_io_apdu_buffer + 5, G_monero_vstate.io_lc); + memcpy(G_monero_vstate.io_buffer, G_io_apdu_buffer + 5, G_monero_vstate.io_lc); G_monero_vstate.io_length = G_monero_vstate.io_lc; return 0; diff --git a/src/monero_key.c b/src/monero_key.c index f80a4d5e..36b3edd5 100644 --- a/src/monero_key.c +++ b/src/monero_key.c @@ -73,7 +73,7 @@ static unsigned long monero_crc32(unsigned long inCrc32, const void *buf, size_t return (crc32 ^ 0xFFFFFFFF); } -void monero_clear_words() { +void monero_clear_words(void) { monero_nvm_write((void *)N_monero_pstate->words_list, NULL, sizeof(N_monero_pstate->words_list)); } @@ -85,13 +85,13 @@ void monero_clear_words() { * word_list * len : word_list length */ -static void monero_set_word(unsigned int n, unsigned int idx, unsigned int w_start, - unsigned char *word_list, int len) { +static int monero_set_word(unsigned int n, unsigned int idx, unsigned int w_start, + unsigned char *word_list, int len) { while (w_start < idx) { len -= 1 + word_list[0]; if (len < 0) { monero_clear_words(); - THROW(SW_WRONG_DATA + 1); + return SW_WRONG_DATA + 1; } word_list += 1 + word_list[0]; w_start++; @@ -99,19 +99,21 @@ static void monero_set_word(unsigned int n, unsigned int idx, unsigned int w_sta if ((w_start != idx) || (word_list[0] > (len - 1)) || (word_list[0] > 19)) { monero_clear_words(); - THROW(SW_WRONG_DATA + 2); + return SW_WRONG_DATA + 2; } len = word_list[0]; word_list++; monero_nvm_write((void *)N_monero_pstate->words[n], word_list, len); + return 0; } #define word_list_length 1626 #define seed G_monero_vstate.b -int monero_apdu_manage_seedwords() { +int monero_apdu_manage_seedwords(void) { unsigned int w_start, w_end; unsigned short wc[4]; + int err = 0; switch (G_monero_vstate.io_p1) { // SETUP @@ -119,7 +121,7 @@ int monero_apdu_manage_seedwords() { w_start = monero_io_fetch_u32(); w_end = w_start + monero_io_fetch_u32(); if ((w_start >= word_list_length) || (w_end > word_list_length) || (w_start > w_end)) { - THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } for (int i = 0; i < 8; i++) { unsigned int val = (seed[i * 4 + 0] << 0) | (seed[i * 4 + 1] << 8) | @@ -130,9 +132,12 @@ int monero_apdu_manage_seedwords() { for (int wi = 0; wi < 3; wi++) { if ((wc[wi] >= w_start) && (wc[wi] < w_end)) { - monero_set_word(i * 3 + wi, wc[wi], w_start, - G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - MONERO_IO_BUFFER_LENGTH - G_monero_vstate.io_offset); + err = monero_set_word(i * 3 + wi, wc[wi], w_start, + G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + MONERO_IO_BUFFER_LENGTH - G_monero_vstate.io_offset); + if (err) { + return err; + } } } } @@ -150,19 +155,17 @@ int monero_apdu_manage_seedwords() { monero_nvm_write((void *)N_monero_pstate->words[24], (void *)N_monero_pstate->words[w_start], WORDS_MAX_LENGTH); -#ifdef HAVE_UX_FLOW // transform to list ready to display unsigned char word[21]; w_start = 0; for (int i = 0; i < 24; i++) { w_end = N_monero_pstate->words[i][0]; - os_memmove(word, &N_monero_pstate->words[i][1], w_end); + memcpy(word, (char *)&N_monero_pstate->words[i][1], w_end); word[w_end] = (i == 23) ? 0 : ' '; w_end++; - monero_nvm_write(N_monero_pstate->words_list + w_start, word, w_end); + monero_nvm_write((char *)N_monero_pstate->words_list + w_start, word, w_end); w_start += w_end; } -#endif } break; @@ -197,8 +200,9 @@ int monero_apdu_display_address() { unsigned int minor; unsigned char index[8]; unsigned char payment_id[8]; - unsigned char C[32]; - unsigned char D[32]; + unsigned char C[KEY_SIZE]; + unsigned char D[KEY_SIZE]; + int error = 0; // fetch monero_io_fetch(index, 8); @@ -208,15 +212,18 @@ int monero_apdu_display_address() { major = (index[0] << 0) | (index[1] << 8) | (index[2] << 16) | (index[3] << 24); minor = (index[4] << 0) | (index[5] << 8) | (index[6] << 16) | (index[7] << 24); if ((minor | major) && (G_monero_vstate.io_p1 == 1)) { - THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } // retrieve pub keys if (minor | major) { - monero_get_subaddress(C, D, index); + error = monero_get_subaddress(C, D, index, sizeof(C), sizeof(D), sizeof(index)); + if (error) { + return error; + } } else { - os_memmove(C, G_monero_vstate.A, 32); - os_memmove(D, G_monero_vstate.B, 32); + memcpy(C, G_monero_vstate.A, KEY_SIZE); + memcpy(D, G_monero_vstate.B, KEY_SIZE); } // prepare UI @@ -233,51 +240,61 @@ int monero_apdu_display_address() { } } - ui_menu_any_pubaddr_display(0, C, D, (minor | major) ? 1 : 0, - (G_monero_vstate.io_p1 == 1) ? payment_id : NULL); - return 0; + error = ui_menu_any_pubaddr_display(0, C, D, (minor | major) ? 1 : 0, + (G_monero_vstate.io_p1 == 1) ? payment_id : NULL); + return error; } /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -int is_fake_view_key(unsigned char *s) { return os_memcmp(s, C_FAKE_SEC_VIEW_KEY, 32) == 0; } +int is_fake_view_key(unsigned char *s) { + return memcmp(s, C_FAKE_SEC_VIEW_KEY, KEY_SIZE) == 0; +} -int is_fake_spend_key(unsigned char *s) { return os_memcmp(s, C_FAKE_SEC_SPEND_KEY, 32) == 0; } +int is_fake_spend_key(unsigned char *s) { + return memcmp(s, C_FAKE_SEC_SPEND_KEY, KEY_SIZE) == 0; +} /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_put_key() { - unsigned char raw[32]; - unsigned char pub[32]; - unsigned char sec[32]; + unsigned char raw[KEY_SIZE]; + unsigned char pub[KEY_SIZE]; + unsigned char sec[KEY_SIZE]; + int err; // option + priv/pub view key + priv/pub spend key + base58 address - if (G_monero_vstate.io_length != (1 + 32 * 2 + 32 * 2 + 95)) { - THROW(SW_WRONG_LENGTH); + if (G_monero_vstate.io_length != (1 + KEY_SIZE * 2 + KEY_SIZE * 2 + 95)) { return SW_WRONG_LENGTH; } // view key - monero_io_fetch(sec, 32); - monero_io_fetch(pub, 32); - monero_ecmul_G(raw, sec); - if (os_memcmp(pub, raw, 32)) { - THROW(SW_WRONG_DATA); + monero_io_fetch(sec, KEY_SIZE); + monero_io_fetch(pub, KEY_SIZE); + err = monero_ecmul_G(raw, sec, sizeof(raw), sizeof(sec)); + if (err) { + return err; + } + + if (memcmp(pub, raw, KEY_SIZE) != 0) { return SW_WRONG_DATA; } - nvm_write((void *)N_monero_pstate->a, sec, 32); + nvm_write((void *)N_monero_pstate->a, sec, KEY_SIZE); // spend key - monero_io_fetch(sec, 32); - monero_io_fetch(pub, 32); - monero_ecmul_G(raw, sec); - if (os_memcmp(pub, raw, 32)) { - THROW(SW_WRONG_DATA); + monero_io_fetch(sec, KEY_SIZE); + monero_io_fetch(pub, KEY_SIZE); + err = monero_ecmul_G(raw, sec, sizeof(raw), sizeof(sec)); + if (err) { + return err; + } + + if (memcmp(pub, raw, KEY_SIZE) != 0) { return SW_WRONG_DATA; } - nvm_write((void *)N_monero_pstate->b, sec, 32); + nvm_write((void *)N_monero_pstate->b, sec, KEY_SIZE); // change mode unsigned char key_mode = KEY_MODE_EXTERNAL; @@ -297,12 +314,16 @@ int monero_apdu_get_key() { // get pub case 1: // view key - monero_io_insert(G_monero_vstate.A, 32); + monero_io_insert(G_monero_vstate.A, KEY_SIZE); // spend key - monero_io_insert(G_monero_vstate.B, 32); + monero_io_insert(G_monero_vstate.B, KEY_SIZE); // public base address - monero_base58_public_key((char *)G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.A, G_monero_vstate.B, 0, NULL); + int error = monero_base58_public_key( + (char *)G_monero_vstate.io_buffer + G_monero_vstate.io_offset, G_monero_vstate.A, + G_monero_vstate.B, 0, NULL); + if (error) { + return 0; + } monero_io_inserted(95); break; @@ -310,18 +331,18 @@ int monero_apdu_get_key() { case 2: // view key if (G_monero_vstate.export_view_key == EXPORT_VIEW_KEY) { - monero_io_insert(G_monero_vstate.a, 32); + monero_io_insert(G_monero_vstate.a, KEY_SIZE); } else { ui_export_viewkey_display(0); return 0; } break; -#if DEBUG_HWDEVICE +#ifdef DEBUG_HWDEVICE // get info case 3: { unsigned int path[5]; - unsigned char seed[32]; + unsigned char seed[64]; // m/44'/128'/0'/0/0 path[0] = 0x8000002C; @@ -330,24 +351,26 @@ int monero_apdu_get_key() { path[3] = 0x00000000; path[4] = 0x00000000; - os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 5, seed, G_monero_vstate.a); - monero_io_insert(seed, 32); + if (os_derive_bip32_no_throw(CX_CURVE_SECP256K1, path, 5, seed, G_monero_vstate.a)) { + return SW_WRONG_DATA; + } + monero_io_insert(seed, KEY_SIZE); - monero_io_insert(G_monero_vstate.b, 32); - monero_io_insert(G_monero_vstate.a, 32); + monero_io_insert(G_monero_vstate.b, KEY_SIZE); + monero_io_insert(G_monero_vstate.a, KEY_SIZE); break; } // get info case 4: - monero_io_insert(G_monero_vstate.a, 32); - monero_io_insert(G_monero_vstate.b, 32); + monero_io_insert(G_monero_vstate.a, KEY_SIZE); + monero_io_insert(G_monero_vstate.b, KEY_SIZE); break; #endif default: - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } return SW_OK; } @@ -356,27 +379,44 @@ int monero_apdu_get_key() { /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_verify_key() { - unsigned char pub[32]; - unsigned char priv[32]; - unsigned char computed_pub[32]; + unsigned char pub[KEY_SIZE]; + unsigned char computed_pub[KEY_SIZE]; unsigned int verified = 0; + int err = 0; - monero_io_fetch_decrypt_key(priv); - monero_io_fetch(pub, 32); switch (G_monero_vstate.io_p1) { - case 0: - monero_secret_key_to_public_key(computed_pub, priv); + case 0: { + unsigned char priv[KEY_SIZE]; + err = monero_io_fetch_decrypt_key(priv, sizeof(priv)); + if (err) { + explicit_bzero(priv, sizeof(priv)); + return err; + } + + err = monero_secret_key_to_public_key(computed_pub, priv, sizeof(computed_pub), + sizeof(priv)); + explicit_bzero(priv, sizeof(priv)); + + if (err) { + return err; + } break; + } case 1: - os_memmove(computed_pub, G_monero_vstate.A, 32); + monero_io_skip(KEY_SIZE); + memcpy(computed_pub, G_monero_vstate.A, KEY_SIZE); break; case 2: - os_memmove(computed_pub, G_monero_vstate.B, 32); + monero_io_skip(KEY_SIZE); + memcpy(computed_pub, G_monero_vstate.B, KEY_SIZE); break; default: - THROW(SW_WRONG_P1P2); + return SW_WRONG_P1P2; } - if (os_memcmp(computed_pub, pub, 32) == 0) { + + monero_io_fetch(pub, sizeof(pub)); + + if (memcmp(computed_pub, pub, sizeof(pub)) == 0) { verified = 1; } @@ -391,13 +431,16 @@ int monero_apdu_verify_key() { #define CHACHA8_KEY_TAIL 0x8c int monero_apdu_get_chacha8_prekey(/*char *prekey*/) { unsigned char abt[65]; - unsigned char pre[32]; + unsigned char pre[KEY_SIZE]; monero_io_discard(0); - os_memmove(abt, G_monero_vstate.a, 32); - os_memmove(abt + 32, G_monero_vstate.b, 32); + memcpy(abt, G_monero_vstate.a, KEY_SIZE); + memcpy(abt + KEY_SIZE, G_monero_vstate.b, KEY_SIZE); abt[64] = CHACHA8_KEY_TAIL; - monero_keccak_F(abt, 65, pre); + int error = monero_keccak_F(abt, 65, pre); + if (error) { + return error; + } monero_io_insert((unsigned char *)G_monero_vstate.keccakF.acc, 200); return SW_OK; } @@ -407,12 +450,22 @@ int monero_apdu_get_chacha8_prekey(/*char *prekey*/) { /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_sc_add(/*unsigned char *r, unsigned char *s1, unsigned char *s2*/) { - unsigned char s1[32]; - unsigned char s2[32]; - unsigned char r[32]; + unsigned char s1[KEY_SIZE]; + unsigned char s2[KEY_SIZE]; + unsigned char r[KEY_SIZE]; + int err; // fetch - monero_io_fetch_decrypt(s1, 32, TYPE_SCALAR); - monero_io_fetch_decrypt(s2, 32, TYPE_SCALAR); + err = monero_io_fetch_decrypt(s1, KEY_SIZE, TYPE_SCALAR); + if (err) { + explicit_bzero(s1, sizeof(s1)); + return err; + } + err = monero_io_fetch_decrypt(s2, KEY_SIZE, TYPE_SCALAR); + if (err) { + explicit_bzero(s1, sizeof(s1)); + explicit_bzero(s2, sizeof(s2)); + return err; + } monero_io_discard(0); if (G_monero_vstate.tx_in_progress) { // During a transaction, only "last_derive_secret_key+last_get_subaddress_secret_key" @@ -420,13 +473,24 @@ int monero_apdu_sc_add(/*unsigned char *r, unsigned char *s1, unsigned char *s2* // https://github.com/monero-project/monero/blob/v0.15.0.5/src/cryptonote_basic/cryptonote_format_utils.cpp#L331 // // hwdev.sc_secret_add(scalar_step2, scalar_step1,subaddr_sk); - if ((os_memcmp(s1, G_monero_vstate.last_derive_secret_key, 32) != 0) || - (os_memcmp(s2, G_monero_vstate.last_get_subaddress_secret_key, 32) != 0)) { - monero_lock_and_throw(SW_WRONG_DATA); + if ((memcmp(s1, G_monero_vstate.last_derive_secret_key, KEY_SIZE) != 0) || + (memcmp(s2, G_monero_vstate.last_get_subaddress_secret_key, KEY_SIZE) != 0)) { + explicit_bzero(s1, sizeof(s1)); + explicit_bzero(s2, sizeof(s2)); + return SW_WRONG_DATA; } } - monero_addm(r, s1, s2); - monero_io_insert_encrypt(r, 32, TYPE_SCALAR); + err = monero_addm(r, s1, s2, sizeof(r), sizeof(s1), sizeof(s2)); + if (err) { + explicit_bzero(s1, sizeof(s1)); + explicit_bzero(s2, sizeof(s2)); + explicit_bzero(r, sizeof(r)); + return err; + } + monero_io_insert_encrypt(r, KEY_SIZE, TYPE_SCALAR); + explicit_bzero(s1, sizeof(s1)); + explicit_bzero(s2, sizeof(s2)); + explicit_bzero(r, sizeof(r)); return SW_OK; } @@ -434,16 +498,27 @@ int monero_apdu_sc_add(/*unsigned char *r, unsigned char *s1, unsigned char *s2* /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_scal_mul_key(/*const rct::key &pub, const rct::key &sec, rct::key mulkey*/) { - unsigned char pub[32]; - unsigned char sec[32]; - unsigned char r[32]; + unsigned char pub[KEY_SIZE]; + unsigned char sec[KEY_SIZE]; + unsigned char r[KEY_SIZE]; + int err = 0; + // fetch - monero_io_fetch(pub, 32); - monero_io_fetch_decrypt_key(sec); + monero_io_fetch(pub, KEY_SIZE); + err = monero_io_fetch_decrypt_key(sec, sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); - monero_ecmul_k(r, pub, sec); - monero_io_insert(r, 32); + err = monero_ecmul_k(r, pub, sec, sizeof(r), sizeof(pub), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } + monero_io_insert(r, KEY_SIZE); + explicit_bzero(sec, sizeof(sec)); return SW_OK; } @@ -451,14 +526,25 @@ int monero_apdu_scal_mul_key(/*const rct::key &pub, const rct::key &sec, rct::ke /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_scal_mul_base(/*const rct::key &sec, rct::key mulkey*/) { - unsigned char sec[32]; - unsigned char r[32]; + unsigned char sec[KEY_SIZE]; + unsigned char r[KEY_SIZE]; + int err; // fetch - monero_io_fetch_decrypt(sec, 32, TYPE_SCALAR); + err = monero_io_fetch_decrypt(sec, KEY_SIZE, TYPE_SCALAR); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); - monero_ecmul_G(r, sec); - monero_io_insert(r, 32); + err = monero_ecmul_G(r, sec, sizeof(r), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } + + monero_io_insert(r, KEY_SIZE); + explicit_bzero(sec, sizeof(sec)); return SW_OK; } @@ -466,13 +552,18 @@ int monero_apdu_scal_mul_base(/*const rct::key &sec, rct::key mulkey*/) { /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_generate_keypair(/*crypto::public_key &pub, crypto::secret_key &sec*/) { - unsigned char sec[32]; - unsigned char pub[32]; + unsigned char sec[KEY_SIZE]; + unsigned char pub[KEY_SIZE]; monero_io_discard(0); - monero_generate_keypair(pub, sec); - monero_io_insert(pub, 32); - monero_io_insert_encrypt(sec, 32, TYPE_SCALAR); + int error = monero_generate_keypair(pub, sec, sizeof(pub), sizeof(sec)); + if (error) { + explicit_bzero(sec, sizeof(sec)); + return error; + } + monero_io_insert(pub, KEY_SIZE); + monero_io_insert_encrypt(sec, KEY_SIZE, TYPE_SCALAR); + explicit_bzero(sec, sizeof(sec)); return SW_OK; } @@ -481,15 +572,25 @@ int monero_apdu_generate_keypair(/*crypto::public_key &pub, crypto::secret_key & /* ----------------------------------------------------------------------- */ int monero_apdu_secret_key_to_public_key( /*const crypto::secret_key &sec, crypto::public_key &pub*/) { - unsigned char sec[32]; - unsigned char pub[32]; + unsigned char sec[KEY_SIZE]; + unsigned char pub[KEY_SIZE]; // fetch - monero_io_fetch_decrypt(sec, 32, TYPE_SCALAR); + int err = monero_io_fetch_decrypt(sec, KEY_SIZE, TYPE_SCALAR); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); // pub - monero_ecmul_G(pub, sec); + err = monero_ecmul_G(pub, sec, sizeof(pub), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } + // pub key - monero_io_insert(pub, 32); + monero_io_insert(pub, KEY_SIZE); + explicit_bzero(sec, sizeof(sec)); return SW_OK; } @@ -497,19 +598,32 @@ int monero_apdu_secret_key_to_public_key( /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_generate_key_derivation(/*const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation*/) { - unsigned char pub[32]; - unsigned char sec[32]; - unsigned char drv[32]; + unsigned char pub[KEY_SIZE]; + unsigned char sec[KEY_SIZE]; + unsigned char drv[KEY_SIZE]; + int err = 0; + // fetch - monero_io_fetch(pub, 32); - monero_io_fetch_decrypt_key(sec); + monero_io_fetch(pub, KEY_SIZE); + err = monero_io_fetch_decrypt_key(sec, sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); // Derive and keep - monero_generate_key_derivation(drv, pub, sec); + err = monero_generate_key_derivation(drv, pub, sec, sizeof(drv), sizeof(pub), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(drv, sizeof(drv)); + return err; + } - monero_io_insert_encrypt(drv, 32, TYPE_DERIVATION); + monero_io_insert_encrypt(drv, KEY_SIZE, TYPE_DERIVATION); + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(drv, sizeof(drv)); return SW_OK; } @@ -518,20 +632,32 @@ int monero_apdu_generate_key_derivation(/*const crypto::public_key &pub, const c /* ----------------------------------------------------------------------- */ int monero_apdu_derivation_to_scalar( /*const crypto::key_derivation &derivation, const size_t output_index, ec_scalar &res*/) { - unsigned char derivation[32]; + unsigned char derivation[KEY_SIZE]; unsigned int output_index; - unsigned char res[32]; + unsigned char res[KEY_SIZE]; // fetch - monero_io_fetch_decrypt(derivation, 32, TYPE_DERIVATION); + int err = monero_io_fetch_decrypt(derivation, KEY_SIZE, TYPE_DERIVATION); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } output_index = monero_io_fetch_u32(); monero_io_discard(0); // pub - monero_derivation_to_scalar(res, derivation, output_index); + err = + monero_derivation_to_scalar(res, derivation, output_index, sizeof(res), sizeof(derivation)); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + explicit_bzero(res, sizeof(res)); + return err; + } // pub key - monero_io_insert_encrypt(res, 32, TYPE_SCALAR); + monero_io_insert_encrypt(res, KEY_SIZE, TYPE_SCALAR); + explicit_bzero(derivation, sizeof(derivation)); + explicit_bzero(res, sizeof(res)); return SW_OK; } @@ -539,22 +665,32 @@ int monero_apdu_derivation_to_scalar( /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_derive_public_key(/*const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, public_key &derived_pub*/) { - unsigned char derivation[32]; + unsigned char derivation[KEY_SIZE]; unsigned int output_index; - unsigned char pub[32]; - unsigned char drvpub[32]; + unsigned char pub[KEY_SIZE]; + unsigned char drvpub[KEY_SIZE]; // fetch - monero_io_fetch_decrypt(derivation, 32, TYPE_DERIVATION); + int err = monero_io_fetch_decrypt(derivation, KEY_SIZE, TYPE_DERIVATION); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } output_index = monero_io_fetch_u32(); - monero_io_fetch(pub, 32); + monero_io_fetch(pub, KEY_SIZE); monero_io_discard(0); // pub - monero_derive_public_key(drvpub, derivation, output_index, pub); + int error = monero_derive_public_key(drvpub, derivation, output_index, pub, sizeof(drvpub), + sizeof(derivation), sizeof(pub)); + if (error) { + explicit_bzero(derivation, sizeof(derivation)); + return error; + } // pub key - monero_io_insert(drvpub, 32); + monero_io_insert(drvpub, KEY_SIZE); + explicit_bzero(derivation, sizeof(derivation)); return SW_OK; } @@ -562,23 +698,44 @@ int monero_apdu_derive_public_key(/*const crypto::key_derivation &derivation, co /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_derive_secret_key(/*const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, secret_key &derived_sec*/){ - unsigned char derivation[32]; + unsigned char derivation[KEY_SIZE]; unsigned int output_index; - unsigned char sec[32]; - unsigned char drvsec[32]; + unsigned char sec[KEY_SIZE]; + unsigned char drvsec[KEY_SIZE]; + int err = 0; // fetch - monero_io_fetch_decrypt(derivation, 32, TYPE_DERIVATION); + err = monero_io_fetch_decrypt(derivation, KEY_SIZE, TYPE_DERIVATION); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } output_index = monero_io_fetch_u32(); - monero_io_fetch_decrypt_key(sec); + err = monero_io_fetch_decrypt_key(sec, sizeof(sec)); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); // pub - monero_derive_secret_key(drvsec, derivation, output_index, sec); + err = monero_derive_secret_key(drvsec, derivation, output_index, sec, sizeof(drvsec), + sizeof(derivation), sizeof(sec)); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(drvsec, sizeof(drvsec)); + return err; + } // sec key - os_memmove(G_monero_vstate.last_derive_secret_key, drvsec, 32); - monero_io_insert_encrypt(drvsec, 32, TYPE_SCALAR); + memcpy(G_monero_vstate.last_derive_secret_key, drvsec, KEY_SIZE); + monero_io_insert_encrypt(drvsec, KEY_SIZE, TYPE_SCALAR); + + explicit_bzero(derivation, sizeof(derivation)); + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(drvsec, sizeof(drvsec)); return SW_OK; } @@ -587,20 +744,60 @@ int monero_apdu_derive_secret_key(/*const crypto::key_derivation &derivation, co /* ----------------------------------------------------------------------- */ int monero_apdu_generate_key_image( /*const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image*/) { - unsigned char pub[32]; - unsigned char sec[32]; - unsigned char image[32]; + unsigned char pub[KEY_SIZE]; + unsigned char sec[KEY_SIZE]; + unsigned char image[KEY_SIZE]; // fetch - monero_io_fetch(pub, 32); - monero_io_fetch_decrypt(sec, 32, TYPE_SCALAR); + monero_io_fetch(pub, KEY_SIZE); + int err = monero_io_fetch_decrypt(sec, KEY_SIZE, TYPE_SCALAR); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_discard(0); // pub - monero_generate_key_image(image, pub, sec); + err = monero_generate_key_image(image, pub, sec, sizeof(image), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } // pub key - monero_io_insert(image, 32); + monero_io_insert(image, KEY_SIZE); + explicit_bzero(sec, sizeof(sec)); + return SW_OK; +} + +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +int monero_apdu_derive_view_tag( + /*const crypto::key_derivation &derivation, const size_t output_index, crypto::view_tag &view_tag*/) { + unsigned char derivation[KEY_SIZE]; + unsigned int output_index; + unsigned char res[1]; + int err; + + // fetch + err = monero_io_fetch_decrypt(derivation, KEY_SIZE, TYPE_DERIVATION); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } + output_index = monero_io_fetch_u32(); + monero_io_discard(0); + + // derive and keep + err = monero_derive_view_tag(res, derivation, output_index); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } + + monero_io_insert(res, 1); + explicit_bzero(derivation, sizeof(derivation)); return SW_OK; } @@ -608,21 +805,31 @@ int monero_apdu_generate_key_image( /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_derive_subaddress_public_key(/*const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, public_key &derived_pub*/) { - unsigned char pub[32]; - unsigned char derivation[32]; + unsigned char pub[KEY_SIZE]; + unsigned char derivation[KEY_SIZE]; unsigned int output_index; - unsigned char sub_pub[32]; + unsigned char sub_pub[KEY_SIZE]; // fetch - monero_io_fetch(pub, 32); - monero_io_fetch_decrypt(derivation, 32, TYPE_DERIVATION); + monero_io_fetch(pub, KEY_SIZE); + int err = monero_io_fetch_decrypt(derivation, KEY_SIZE, TYPE_DERIVATION); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } output_index = monero_io_fetch_u32(); monero_io_discard(0); // pub - monero_derive_subaddress_public_key(sub_pub, pub, derivation, output_index); + err = monero_derive_subaddress_public_key(sub_pub, pub, derivation, output_index, + sizeof(sub_pub), sizeof(pub), sizeof(derivation)); + if (err) { + explicit_bzero(derivation, sizeof(derivation)); + return err; + } // pub key - monero_io_insert(sub_pub, 32); + monero_io_insert(sub_pub, KEY_SIZE); + explicit_bzero(derivation, sizeof(derivation)); return SW_OK; } @@ -632,19 +839,22 @@ int monero_apdu_derive_subaddress_public_key(/*const crypto::public_key &pub, co int monero_apdu_get_subaddress( /*const cryptonote::subaddress_index& index, cryptonote::account_public_address &address*/) { unsigned char index[8]; - unsigned char C[32]; - unsigned char D[32]; + unsigned char C[KEY_SIZE]; + unsigned char D[KEY_SIZE]; // fetch monero_io_fetch(index, 8); monero_io_discard(0); // pub - monero_get_subaddress(C, D, index); + int error = monero_get_subaddress(C, D, index, sizeof(C), sizeof(D), sizeof(index)); + if (error) { + return error; + } // pub key - monero_io_insert(C, 32); - monero_io_insert(D, 32); + monero_io_insert(C, KEY_SIZE); + monero_io_insert(D, KEY_SIZE); return SW_OK; } @@ -654,17 +864,20 @@ int monero_apdu_get_subaddress( int monero_apdu_get_subaddress_spend_public_key( /*const cryptonote::subaddress_index& index, crypto::public_key D*/) { unsigned char index[8]; - unsigned char D[32]; + unsigned char D[KEY_SIZE]; // fetch monero_io_fetch(index, 8); monero_io_discard(1); // pub - monero_get_subaddress_spend_public_key(D, index); + int error = monero_get_subaddress_spend_public_key(D, index, sizeof(D), sizeof(index)); + if (error) { + return error; + } // pub key - monero_io_insert(D, 32); + monero_io_insert(D, KEY_SIZE); return SW_OK; } @@ -673,18 +886,31 @@ int monero_apdu_get_subaddress_spend_public_key( /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_get_subaddress_secret_key(/*const crypto::secret_key& sec, const cryptonote::subaddress_index& index, crypto::secret_key &sub_sec*/) { - unsigned char sec[32]; + unsigned char sec[KEY_SIZE]; unsigned char index[8]; - unsigned char sub_sec[32]; + unsigned char sub_sec[KEY_SIZE]; + int err = 0; - monero_io_fetch_decrypt_key(sec); + err = monero_io_fetch_decrypt_key(sec, sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } monero_io_fetch(index, 8); monero_io_discard(0); - monero_get_subaddress_secret_key(sub_sec, sec, index); + err = monero_get_subaddress_secret_key(sub_sec, sec, index, sizeof(sub_sec), sizeof(sec), + sizeof(index)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(sub_sec, sizeof(sec)); + return err; + } - os_memmove(G_monero_vstate.last_get_subaddress_secret_key, sub_sec, 32); - monero_io_insert_encrypt(sub_sec, 32, TYPE_SCALAR); + memcpy(G_monero_vstate.last_get_subaddress_secret_key, sub_sec, KEY_SIZE); + monero_io_insert_encrypt(sub_sec, KEY_SIZE, TYPE_SCALAR); + explicit_bzero(sec, sizeof(sec)); + explicit_bzero(sub_sec, sizeof(sec)); return SW_OK; } @@ -692,10 +918,9 @@ int monero_apdu_get_subaddress_secret_key(/*const crypto::secret_key& sec, const /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_sec, crypto::public_key Aout, crypto::public_key Bout, size_t output_index, bool is_change, bool is_subaddress, bool need_additional_key*/) { +int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_sec, crypto::public_key Aout, crypto::public_key Bout, size_t output_index, bool is_change, bool is_subaddress, bool need_additional_key, bool use_view_tags*/) { // IN - unsigned int tx_version; - unsigned char tx_key[32]; + unsigned char tx_key[KEY_SIZE]; unsigned char *txkey_pub; unsigned char *Aout; unsigned char *Bout; @@ -703,79 +928,170 @@ int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_se unsigned char is_change; unsigned char is_subaddress; unsigned char need_additional_txkeys; - unsigned char additional_txkey_sec[32]; + unsigned char additional_txkey_sec[KEY_SIZE]; + unsigned char use_view_tags; // OUT - unsigned char additional_txkey_pub[32]; + unsigned char additional_txkey_pub[KEY_SIZE]; #define amount_key tx_key #define out_eph_public_key additional_txkey_sec + unsigned char view_tag[1]; // TMP - unsigned char derivation[32]; - - tx_version = monero_io_fetch_u32(); - monero_io_fetch_decrypt_key(tx_key); + unsigned char derivation[KEY_SIZE]; + int err = 0; + + monero_io_fetch_u32(); // skip tx_version + err = monero_io_fetch_decrypt_key(tx_key, sizeof(tx_key)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + return err; + } txkey_pub = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(KEY_SIZE); Aout = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(KEY_SIZE); Bout = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(KEY_SIZE); output_index = monero_io_fetch_u32(); is_change = monero_io_fetch_u8(); is_subaddress = monero_io_fetch_u8(); need_additional_txkeys = monero_io_fetch_u8(); if (need_additional_txkeys) { - monero_io_fetch_decrypt_key(additional_txkey_sec); + err = monero_io_fetch_decrypt_key(additional_txkey_sec, sizeof(additional_txkey_sec)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } else { - monero_io_fetch(NULL, 32); + monero_io_skip(KEY_SIZE); } + use_view_tags = monero_io_fetch_u8(); // update outkeys hash control if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { if (G_monero_vstate.io_protocol_version >= 2) { - monero_sha256_outkeys_update(Aout, 32); - monero_sha256_outkeys_update(Bout, 32); - monero_sha256_outkeys_update(&is_change, 1); + err = monero_sha256_outkeys_update(Aout, KEY_SIZE); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } + err = monero_sha256_outkeys_update(Bout, KEY_SIZE); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } + err = monero_sha256_outkeys_update(&is_change, 1); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } } // make additional tx pubkey if necessary if (need_additional_txkeys) { if (is_subaddress) { - monero_ecmul_k(additional_txkey_pub, Bout, additional_txkey_sec); + err = monero_ecmul_k(additional_txkey_pub, Bout, additional_txkey_sec, + sizeof(additional_txkey_pub), KEY_SIZE, + sizeof(additional_txkey_sec)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } else { - monero_ecmul_G(additional_txkey_pub, additional_txkey_sec); + err = monero_ecmul_G(additional_txkey_pub, additional_txkey_sec, + sizeof(additional_txkey_pub), sizeof(additional_txkey_sec)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } } else { - os_memset(additional_txkey_pub, 0, 32); + explicit_bzero(additional_txkey_pub, KEY_SIZE); } // derivation if (is_change) { - monero_generate_key_derivation(derivation, txkey_pub, G_monero_vstate.a); + err = + monero_generate_key_derivation(derivation, txkey_pub, G_monero_vstate.a, + sizeof(derivation), KEY_SIZE, sizeof(G_monero_vstate.a)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } else { - monero_generate_key_derivation( + err = monero_generate_key_derivation( derivation, Aout, - (is_subaddress && need_additional_txkeys) ? additional_txkey_sec : tx_key); + (is_subaddress && need_additional_txkeys) ? additional_txkey_sec : tx_key, + sizeof(derivation), KEY_SIZE, KEY_SIZE); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + return err; + } } // compute amount key AKout (scalar1), version is always greater than 1 - monero_derivation_to_scalar(amount_key, derivation, output_index); + err = monero_derivation_to_scalar(amount_key, derivation, output_index, sizeof(amount_key), + sizeof(derivation)); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + explicit_bzero(amount_key, sizeof(amount_key)); + return err; + } if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { if (G_monero_vstate.io_protocol_version >= 2) { - monero_sha256_outkeys_update(amount_key, 32); + err = monero_sha256_outkeys_update(amount_key, KEY_SIZE); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + explicit_bzero(amount_key, sizeof(amount_key)); + return err; + } } } // compute ephemeral output key - monero_derive_public_key(out_eph_public_key, derivation, output_index, Bout); + err = monero_derive_public_key(out_eph_public_key, derivation, output_index, Bout, + sizeof(out_eph_public_key), sizeof(derivation), KEY_SIZE); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + explicit_bzero(amount_key, sizeof(amount_key)); + return err; + } + + // compute view tag + if (use_view_tags) { + err = monero_derive_view_tag(view_tag, derivation, output_index); + if (err) { + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + explicit_bzero(amount_key, sizeof(amount_key)); + return err; + } + } // send all monero_io_discard(0); - monero_io_insert_encrypt(amount_key, 32, TYPE_AMOUNT_KEY); - monero_io_insert(out_eph_public_key, 32); + monero_io_insert_encrypt(amount_key, KEY_SIZE, TYPE_AMOUNT_KEY); + monero_io_insert(out_eph_public_key, KEY_SIZE); if (need_additional_txkeys) { - monero_io_insert(additional_txkey_pub, 32); + monero_io_insert(additional_txkey_pub, KEY_SIZE); + } + if (use_view_tags) { + monero_io_insert(view_tag, 1); } G_monero_vstate.tx_output_cnt++; + explicit_bzero(tx_key, sizeof(tx_key)); + explicit_bzero(additional_txkey_sec, sizeof(additional_txkey_sec)); + explicit_bzero(amount_key, sizeof(amount_key)); return SW_OK; } diff --git a/src/monero_main.c b/src/monero_main.c index d94ac5e4..a52f60f3 100644 --- a/src/monero_main.c +++ b/src/monero_main.c @@ -27,204 +27,50 @@ #include "os_io_seproxyhal.h" #include "string.h" #include "glyphs.h" +#include "io.h" -#ifdef HAVE_UX_FLOW #include "ux.h" -#endif -#ifdef TARGET_NANOX -#include "balenos_ble.h" -#endif /* ----------------------------------------------------------------------- */ /* --- Application Entry --- */ /* ----------------------------------------------------------------------- */ +void __attribute__((noreturn)) app_exit(void); -void monero_main(void) { - unsigned int io_flags, cont; - io_flags = 0; - cont = 1; - while (cont) { - volatile unsigned short sw = 0; - BEGIN_TRY { - TRY { - monero_io_do(io_flags); - sw = monero_dispatch(); - } - CATCH(EXCEPTION_IO_RESET) { - sw = 0; - cont = 0; - monero_io_discard(1); - // THROW(EXCEPTION_IO_RESET); - } - CATCH_OTHER(e) { - monero_reset_tx(1); - if (((e & 0xF000) == 0x9000) || ((e & 0xFF00) == 0x6400)) { - sw = e; - } else { - monero_io_discard(1); - if ((e & 0xFFFF0000) || ((e & 0xF000) != 0x6000)) { - monero_io_insert_u32(e); - sw = 0x6f42; - } else { - sw = e; - } - } - } - FINALLY { - if (sw) { - monero_io_insert_u16(sw); - io_flags = 0; - } else { - io_flags = IO_ASYNCH_REPLY; - } - } - } - END_TRY; - } +void __attribute__((noreturn)) send_error_and_kill_app(int sw) { + monero_io_discard(1); + monero_io_insert_u16(sw); + monero_io_do(IO_RETURN_AFTER_TX); + explicit_bzero(&G_monero_vstate, sizeof(G_monero_vstate)); + app_exit(); } -unsigned char io_event(unsigned char channel) { - unsigned int s_before; - unsigned int s_after; - - s_before = os_global_pin_is_validated(); - - // nothing done with the event, throw an error on the transport layer if - // needed - // can't have more than one tag in the reply, not supported yet. - switch (G_io_seproxyhal_spi_buffer[0]) { - case SEPROXYHAL_TAG_FINGER_EVENT: - UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); - break; - // power off if long push, else pass to the application callback if any - case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S - UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); - break; - - // other events are propagated to the UX just in case - default: - UX_DEFAULT_EVENT(); - break; - - case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: - UX_DISPLAYED_EVENT({}); - break; - case SEPROXYHAL_TAG_TICKER_EVENT: - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { - // only allow display when not locked of overlayed by an OS UX. - if (UX_ALLOWED) { - UX_REDISPLAY(); - } - }); - break; - } +void app_main(void) { + unsigned int io_flags; + unsigned int error; + io_flags = 0; - // close the event if not done previously (by a display or whatever) - if (!io_seproxyhal_spi_is_status_sent()) { - io_seproxyhal_general_status(); + error = monero_init(); + if (error) { + explicit_bzero(&G_monero_vstate, sizeof(G_monero_vstate)); + app_exit(); } - s_after = os_global_pin_is_validated(); + // set up initial screen + ui_init(); - if (s_before != s_after) { - if (s_after == PIN_VERIFIED) { - monero_init_private_key(); + for (;;) { + volatile unsigned short sw = 0; + monero_io_do(io_flags); + sw = monero_dispatch(); + if (sw == 0) { + io_flags = IO_ASYNCH_REPLY; + } else if (sw == SW_OK) { + monero_io_insert_u16(sw); + io_flags = 0; } else { - ; // do nothing, allowing TX parsing in lock mode - // monero_wipe_private_key(); - } - } - - // command has been processed, DO NOT reset the current APDU transport - return 1; -} - -unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { - switch (channel & ~(IO_FLAGS)) { - case CHANNEL_KEYBOARD: - break; - - // multiplexed io exchange over a SPI channel and TLV encapsulated protocol - case CHANNEL_SPI: - if (tx_len) { - io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - - if (channel & IO_RESET_AFTER_REPLIED) { - reset(); - } - return 0; // nothing received from the master so far (it's a tx - // transaction) - } else { - return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); - } - - default: - THROW(INVALID_PARAMETER); - return 0; - } - return 0; -} - -void app_exit(void) { - BEGIN_TRY_L(exit) { - TRY_L(exit) { os_sched_exit(-1); } - FINALLY_L(exit) {} - } - END_TRY_L(exit); -} - -/* -------------------------------------------------------------- */ - -__attribute__((section(".boot"))) int main(void) { - // exit critical section - __asm volatile("cpsie i"); - unsigned int cont = 1; - - // ensure exception will work as planned - os_boot(); - while (cont) { - UX_INIT(); - - BEGIN_TRY { - TRY { - // start communication with MCU - io_seproxyhal_init(); - - USB_power(0); - USB_power(1); - -#ifdef HAVE_USB_CLASS_CCID - io_usb_ccid_set_card_inserted(1); -#endif - -#ifdef TARGET_NANOX - G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); - BLE_power(0, NULL); - BLE_power(1, "Nano X - Monero"); -#endif - - monero_init(); - - // set up initial screen - ui_init(); - - // start the application - // the first exchange will: - // - display the initial screen - // - send the ATR - // - receive the first command - monero_main(); - } - CATCH(EXCEPTION_IO_RESET) { - // reset IO and UX - ; - } - CATCH_OTHER(e) { cont = 0; } - FINALLY {} + send_error_and_kill_app(sw); } - END_TRY; } - app_exit(); } #endif diff --git a/src/monero_mlsag.c b/src/monero_mlsag.c index 415cc938..d9eecec6 100644 --- a/src/monero_mlsag.c +++ b/src/monero_mlsag.c @@ -31,10 +31,11 @@ int monero_apdu_mlsag_prepare() { unsigned char xin[32]; unsigned char alpha[32]; unsigned char mul[32]; + int err; G_monero_vstate.tx_sign_cnt++; if (G_monero_vstate.tx_sign_cnt == 0) { - monero_lock_and_throw(SW_SECURITY_MAX_SIGNATURE_REACHED); + return SW_SECURITY_MAX_SIGNATURE_REACHED; } if (G_monero_vstate.io_length > 1) { @@ -42,7 +43,10 @@ int monero_apdu_mlsag_prepare() { if (G_monero_vstate.options & 0x40) { monero_io_fetch(xin, 32); } else { - monero_io_fetch_decrypt(xin, 32, TYPE_SCALAR); + err = monero_io_fetch_decrypt(xin, 32, TYPE_SCALAR); + if (err) { + return err; + } } options = 1; } else { @@ -52,20 +56,38 @@ int monero_apdu_mlsag_prepare() { monero_io_discard(1); // ai - monero_rng_mod_order(alpha); - monero_reduce(alpha, alpha); + err = monero_rng_mod_order(alpha, sizeof(alpha)); + if (err) { + return err; + } + + err = monero_reduce(alpha, alpha, sizeof(alpha), sizeof(alpha)); + if (err) { + return err; + } + monero_io_insert_encrypt(alpha, 32, TYPE_ALPHA); // ai.G - monero_ecmul_G(mul, alpha); + err = monero_ecmul_G(mul, alpha, sizeof(mul), sizeof(alpha)); + if (err) { + return err; + } + monero_io_insert(mul, 32); if (options) { // ai.Hi - monero_ecmul_k(mul, Hi, alpha); + err = monero_ecmul_k(mul, Hi, alpha, sizeof(mul), sizeof(Hi), sizeof(alpha)); + if (err) { + return err; + } monero_io_insert(mul, 32); // IIi = xin.Hi - monero_ecmul_k(mul, Hi, xin); + err = monero_ecmul_k(mul, Hi, xin, sizeof(mul), sizeof(Hi), sizeof(xin)); + if (err) { + return err; + } monero_io_insert(mul, 32); } return SW_OK; @@ -77,20 +99,34 @@ int monero_apdu_mlsag_prepare() { int monero_apdu_mlsag_hash() { unsigned char msg[32]; unsigned char c[32]; + int err; if (G_monero_vstate.io_p2 == 1) { - monero_keccak_init_H(); - os_memmove(msg, G_monero_vstate.mlsagH, 32); + if (monero_keccak_init_H()) { + return SW_WRONG_DATA; + } + memcpy(msg, G_monero_vstate.mlsagH, 32); } else { monero_io_fetch(msg, 32); } monero_io_discard(1); - monero_keccak_update_H(msg, 32); + err = monero_keccak_update_H(msg, 32); + if (err) { + return err; + } + if ((G_monero_vstate.options & 0x80) == 0) { - monero_keccak_final_H(c); - monero_reduce(c, c); + err = monero_keccak_final_H(c); + if (err) { + return err; + } + + err = monero_reduce(c, c, sizeof(c), sizeof(c)); + if (err) { + return err; + } monero_io_insert(c, 32); - os_memmove(G_monero_vstate.c, c, 32); + memcpy(G_monero_vstate.c, c, 32); } return SW_OK; } @@ -103,29 +139,53 @@ int monero_apdu_mlsag_sign() { unsigned char alpha[32]; unsigned char ss[32]; unsigned char ss2[32]; + int err; if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_FAKE) { monero_io_fetch(xin, 32); monero_io_fetch(alpha, 32); } else if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - monero_io_fetch_decrypt(xin, 32, TYPE_SCALAR); - monero_io_fetch_decrypt(alpha, 32, TYPE_ALPHA); + err = monero_io_fetch_decrypt(xin, 32, TYPE_SCALAR); + if (err) { + return err; + } + err = monero_io_fetch_decrypt(alpha, 32, TYPE_ALPHA); + if (err) { + return err; + } } else { - monero_lock_and_throw(SW_SECURITY_INTERNAL); + return SW_SECURITY_INTERNAL; } monero_io_discard(1); // check xin and alpha are not null if (cx_math_is_zero(xin, 32) || cx_math_is_zero(alpha, 32)) { - monero_lock_and_throw(SW_SECURITY_RANGE_VALUE); + return SW_SECURITY_RANGE_VALUE; } - monero_reduce(ss, G_monero_vstate.c); - monero_reduce(xin, xin); - monero_multm(ss, ss, xin); + err = monero_reduce(ss, G_monero_vstate.c, sizeof(ss), sizeof(G_monero_vstate.c)); + if (err) { + return err; + } - monero_reduce(alpha, alpha); - monero_subm(ss2, alpha, ss); + err = monero_reduce(xin, xin, sizeof(xin), sizeof(xin)); + if (err) { + return err; + } + + err = monero_multm(ss, ss, xin, sizeof(ss), sizeof(ss), sizeof(xin)); + if (err) { + return err; + } + + err = monero_reduce(alpha, alpha, sizeof(alpha), sizeof(alpha)); + if (err) { + return err; + } + err = monero_subm(ss2, alpha, ss, sizeof(ss2), sizeof(alpha), sizeof(ss)); + if (err) { + return err; + } monero_io_insert(ss2, 32); monero_io_insert_u32(G_monero_vstate.tx_sig_mode); diff --git a/src/monero_monero.c b/src/monero_monero.c index 1f753bd0..a6b585de 100644 --- a/src/monero_monero.c +++ b/src/monero_monero.c @@ -17,6 +17,7 @@ *****************************************************************************/ #include "os.h" +#include "cx.h" #include "monero_types.h" #include "monero_api.h" #include "monero_vars.h" @@ -64,30 +65,39 @@ const unsigned int encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11}; #define FULL_BLOCK_SIZE 8 //(sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1) #define FULL_ENCODED_BLOCK_SIZE 11 // encoded_block_sizes[full_block_size]; #define ADDR_CHECKSUM_SIZE 4 +#define ADDR_LEN 95 +#define INTEGRATED_ADDR_LEN 106 static uint64_t uint_8be_to_64(const unsigned char* data, size_t size) { uint64_t res = 0; switch (9 - size) { case 1: res |= *data++; + __attribute__((fallthrough)); case 2: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 3: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 4: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 5: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 6: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 7: res <<= 8; res |= *data++; + __attribute__((fallthrough)); case 8: res <<= 8; res |= *data; @@ -112,8 +122,8 @@ int monero_base58_public_key(char* str_b58, unsigned char* view, unsigned char* unsigned char data[72 + 8]; unsigned int offset; unsigned int prefix; + int error = 0; - // data[0] = N_monero_pstate->network_id; switch (N_monero_pstate->network_id) { case TESTNET: if (paymanetID) { @@ -144,18 +154,27 @@ int monero_base58_public_key(char* str_b58, unsigned char* view, unsigned char* } break; #endif + default: + str_b58[0] = 0; + return 0; + } + error = monero_encode_varint(data, 8, prefix, &offset); + if (error) { + return error; } - offset = monero_encode_varint(data, 8, prefix); - os_memmove(data + offset, spend, 32); - os_memmove(data + offset + 32, view, 32); + memcpy(data + offset, spend, KEY_SIZE); + memcpy(data + offset + KEY_SIZE, view, KEY_SIZE); offset += 64; if (paymanetID) { - os_memmove(data + offset, paymanetID, 8); + memcpy(data + offset, paymanetID, 8); offset += 8; } - monero_keccak_F(data, offset, G_monero_vstate.mlsagH); - os_memmove(data + offset, G_monero_vstate.mlsagH, 4); + error = monero_keccak_F(data, offset, G_monero_vstate.mlsagH); + if (error) { + return error; + } + memcpy(data + offset, G_monero_vstate.mlsagH, 4); offset += 4; unsigned int full_block_count = (offset) / FULL_BLOCK_SIZE; @@ -170,5 +189,11 @@ int monero_base58_public_key(char* str_b58, unsigned char* view, unsigned char* &str_b58[full_block_count * FULL_ENCODED_BLOCK_SIZE]); } + if (paymanetID) { + str_b58[INTEGRATED_ADDR_LEN] = '\0'; + } else { + str_b58[ADDR_LEN] = '\0'; + } + return 0; } diff --git a/src/monero_nvram.c b/src/monero_nvram.c index c13a4b54..c12ce79e 100644 --- a/src/monero_nvram.c +++ b/src/monero_nvram.c @@ -22,8 +22,8 @@ #include "monero_api.h" #include "monero_vars.h" -#ifdef TARGET_NANOX -const monero_nv_state_t N_state_pic; -#else +#if defined(TARGET_NANOS) monero_nv_state_t N_state_pic; +#else +const monero_nv_state_t N_state_pic; #endif diff --git a/src/monero_open_tx.c b/src/monero_open_tx.c index 093eec06..97afc80e 100644 --- a/src/monero_open_tx.c +++ b/src/monero_open_tx.c @@ -25,12 +25,16 @@ /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -void monero_reset_tx(int reset_tx_cnt) { - os_memset(G_monero_vstate.r, 0, 32); - os_memset(G_monero_vstate.R, 0, 32); - cx_rng(G_monero_vstate.hmac_key, 32); - - monero_keccak_init_H(); +int monero_reset_tx(int reset_tx_cnt) { + int error; + explicit_bzero(G_monero_vstate.r, sizeof(G_monero_vstate.r)); + explicit_bzero(G_monero_vstate.R, sizeof(G_monero_vstate.R)); + cx_rng(G_monero_vstate.hmac_key, KEY_SIZE); + + error = monero_keccak_init_H(); + if (error) { + return error; + } monero_sha256_commitment_init(); monero_sha256_outkeys_init(); G_monero_vstate.tx_in_progress = 0; @@ -38,6 +42,7 @@ void monero_reset_tx(int reset_tx_cnt) { if (reset_tx_cnt) { G_monero_vstate.tx_cnt = 0; } + return 0; } /* ----------------------------------------------------------------------- */ @@ -47,39 +52,47 @@ void monero_reset_tx(int reset_tx_cnt) { * HD wallet not yet supported : account is assumed to be zero */ int monero_apdu_open_tx() { - unsigned int account; - - account = monero_io_fetch_u32(); + int error; + monero_io_fetch_u32(); // skip account monero_io_discard(1); - monero_reset_tx(0); + error = monero_reset_tx(0); + if (error) { + return error; + } G_monero_vstate.tx_cnt++; ui_menu_opentx_display(0); - if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - // return 0; - } return monero_apdu_open_tx_cont(); } int monero_apdu_open_tx_cont() { + int error; G_monero_vstate.tx_in_progress = 1; #ifdef DEBUG_HWDEVICE - os_memset(G_monero_vstate.hmac_key, 0xab, 32); + memset(G_monero_vstate.hmac_key, 0xab, sizeof(G_monero_vstate.hmac_key)); #else - cx_rng(G_monero_vstate.hmac_key, 32); + cx_rng(G_monero_vstate.hmac_key, KEY_SIZE); #endif - monero_rng_mod_order(G_monero_vstate.r); - monero_ecmul_G(G_monero_vstate.R, G_monero_vstate.r); + error = monero_rng_mod_order(G_monero_vstate.r, sizeof(G_monero_vstate.r)); + if (error) { + return error; + } + + error = monero_ecmul_G(G_monero_vstate.R, G_monero_vstate.r, sizeof(G_monero_vstate.R), + sizeof(G_monero_vstate.r)); + if (error) { + return error; + } - monero_io_insert(G_monero_vstate.R, 32); - monero_io_insert_encrypt(G_monero_vstate.r, 32, TYPE_SCALAR); - monero_io_insert(C_FAKE_SEC_VIEW_KEY, 32); - monero_io_insert_hmac_for((void*)C_FAKE_SEC_VIEW_KEY, 32, TYPE_SCALAR); - monero_io_insert(C_FAKE_SEC_SPEND_KEY, 32); - monero_io_insert_hmac_for((void*)C_FAKE_SEC_SPEND_KEY, 32, TYPE_SCALAR); + monero_io_insert(G_monero_vstate.R, KEY_SIZE); + monero_io_insert_encrypt(G_monero_vstate.r, KEY_SIZE, TYPE_SCALAR); + monero_io_insert(C_FAKE_SEC_VIEW_KEY, KEY_SIZE); + monero_io_insert_hmac_for((void*)C_FAKE_SEC_VIEW_KEY, KEY_SIZE, TYPE_SCALAR); + monero_io_insert(C_FAKE_SEC_SPEND_KEY, KEY_SIZE); + monero_io_insert_hmac_for((void*)C_FAKE_SEC_SPEND_KEY, KEY_SIZE, TYPE_SCALAR); return SW_OK; } @@ -87,9 +100,15 @@ int monero_apdu_open_tx_cont() { /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_close_tx() { + int error; monero_io_discard(1); - monero_reset_tx(G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL); - ui_menu_main_display(0); + error = monero_reset_tx(G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL); + if (error) { + return error; + } +#ifdef HAVE_BAGL + ui_menu_main_display(); +#endif return SW_OK; } @@ -98,7 +117,7 @@ int monero_apdu_close_tx() { /* ----------------------------------------------------------------------- */ int monero_abort_tx() { monero_reset_tx(1); - ui_menu_info_display2(0, "TX", "Aborted"); + ui_menu_show_tx_aborted(); return 0; } @@ -117,7 +136,7 @@ int monero_apdu_set_signature_mode() { case TRANSACTION_CREATE_FAKE: break; default: - monero_lock_and_throw(SW_WRONG_DATA); + return SW_WRONG_DATA; } G_monero_vstate.tx_sig_mode = sig_mode; diff --git a/src/monero_prefix.c b/src/monero_prefix.c index de662c57..0d811003 100644 --- a/src/monero_prefix.c +++ b/src/monero_prefix.c @@ -29,26 +29,41 @@ /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_apdu_prefix_hash_init() { - int timelock; +int monero_apdu_prefix_hash_init(void) { + uint64_t timelock; + int error = 0; - monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.io_length - G_monero_vstate.io_offset); + error = monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + G_monero_vstate.io_length - G_monero_vstate.io_offset); + if (error) { + return error; + } if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - monero_io_fetch_varint(); - timelock = monero_io_fetch_varint(); + error = monero_io_fetch_varint(&timelock); // DUmmy call TODO + if (error) { + return error; + } + + error = monero_io_fetch_varint(&timelock); + if (error) { + return error; + } + if (monero_io_fetch_available() != 0) { - THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } // ask user monero_io_discard(1); if (timelock != 0) { - monero_uint642str(timelock, G_monero_vstate.ux_amount, 15); + error = monero_uint642str(timelock, G_monero_vstate.ux_amount, 15); + if (error) { + return error; + } ui_menu_timelock_validation_display(0); return 0; } else { - return SW_OK; + return ui_menu_transaction_start(); } } else { monero_io_discard(1); @@ -59,12 +74,20 @@ int monero_apdu_prefix_hash_init() { /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_apdu_prefix_hash_update() { - monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.io_length - G_monero_vstate.io_offset); +int monero_apdu_prefix_hash_update(void) { + int error; + error = monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + G_monero_vstate.io_length - G_monero_vstate.io_offset); + if (error) { + return error; + } + monero_io_discard(0); if ((G_monero_vstate.options & 0x80) == 0x00) { - monero_keccak_final_H(G_monero_vstate.prefixH); + error = monero_keccak_final_H(G_monero_vstate.prefixH); + if (error) { + return error; + } monero_io_insert(G_monero_vstate.prefixH, 32); } diff --git a/src/monero_prehash.c b/src/monero_prehash.c index 231ffcc9..c609b639 100644 --- a/src/monero_prehash.c +++ b/src/monero_prehash.c @@ -30,22 +30,35 @@ /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_mlsag_prehash_init() { + int error = 0; if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { if (G_monero_vstate.io_p2 == 1) { - monero_sha256_outkeys_final(G_monero_vstate.OUTK); + error = monero_sha256_outkeys_final(G_monero_vstate.OUTK); + if (error) { + return error; + } monero_sha256_outkeys_init(); monero_sha256_commitment_init(); - monero_keccak_init_H(); + error = monero_keccak_init_H(); + if (error) { + return error; + } } } - monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, - G_monero_vstate.io_length - G_monero_vstate.io_offset); + error = monero_keccak_update_H(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, + G_monero_vstate.io_length - G_monero_vstate.io_offset); + if (error) { + return error; + } if ((G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) && (G_monero_vstate.io_p2 == 1)) { // skip type monero_io_fetch_u8(); // fee str monero_vamount2str(G_monero_vstate.io_buffer + G_monero_vstate.io_offset, G_monero_vstate.ux_amount, 15); + + snprintf(G_monero_vstate.ux_amount + strlen(G_monero_vstate.ux_amount), + sizeof(G_monero_vstate.ux_amount) - strlen(G_monero_vstate.ux_amount), " XMR"); // ask user monero_io_discard(1); ui_menu_fee_validation_display(0); @@ -64,13 +77,14 @@ int monero_apdu_mlsag_prehash_update() { unsigned char *Aout; unsigned char *Bout; unsigned char is_change; - unsigned char AKout[32]; + unsigned char AKout[KEY_SIZE]; unsigned char C[32]; unsigned char v[32]; unsigned char k[32]; #define aH AKout unsigned char kG[32]; + int err = 0; // fetch destination is_subaddress = monero_io_fetch_u8(); @@ -80,10 +94,13 @@ int monero_apdu_mlsag_prehash_update() { is_change = 0; } Aout = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(32); Bout = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); - monero_io_fetch_decrypt(AKout, 32, TYPE_AMOUNT_KEY); + monero_io_skip(32); + err = monero_io_fetch_decrypt(AKout, KEY_SIZE, TYPE_AMOUNT_KEY); + if (err) { + return err; + } monero_io_fetch(C, 32); monero_io_fetch(k, 32); monero_io_fetch(v, 32); @@ -92,63 +109,127 @@ int monero_apdu_mlsag_prehash_update() { // update MLSAG prehash if ((G_monero_vstate.options & 0x03) == 0x02) { - monero_keccak_update_H(v, 8); + err = monero_keccak_update_H(v, 8); + if (err) { + return err; + } + } else { - monero_keccak_update_H(k, 32); - monero_keccak_update_H(v, 32); + err = monero_keccak_update_H(k, 32); + if (err) { + return err; + } + + err = monero_keccak_update_H(v, 32); + if (err) { + return err; + } } if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { if (is_change == 0) { // encode dest adress - monero_base58_public_key(&G_monero_vstate.ux_address[0], Aout, Bout, is_subaddress, - NULL); + err = monero_base58_public_key(&G_monero_vstate.ux_address[0], Aout, Bout, + is_subaddress, NULL); + if (err) { + return err; + } } // update destination hash control if (G_monero_vstate.io_protocol_version >= 2) { - monero_sha256_outkeys_update(Aout, 32); - monero_sha256_outkeys_update(Bout, 32); - monero_sha256_outkeys_update(&is_change, 1); - monero_sha256_outkeys_update(AKout, 32); + err = monero_sha256_outkeys_update(Aout, KEY_SIZE); + if (err) { + return err; + } + err = monero_sha256_outkeys_update(Bout, KEY_SIZE); + if (err) { + return err; + } + err = monero_sha256_outkeys_update(&is_change, 1); + if (err) { + return err; + } + err = monero_sha256_outkeys_update(AKout, KEY_SIZE); + if (err) { + return err; + } } // check C = aH+kG - monero_unblind(v, k, AKout, G_monero_vstate.options & 0x03); - monero_ecmul_G(kG, k); + err = monero_unblind(v, k, AKout, G_monero_vstate.options & 0x03, sizeof(v), sizeof(k), + sizeof(AKout)); + if (err) { + return err; + } + + err = monero_ecmul_G(kG, k, sizeof(kG), sizeof(k)); + if (err) { + return err; + } + if (!cx_math_is_zero(v, 32)) { - monero_ecmul_H(aH, v); - monero_ecadd(aH, kG, aH); + err = monero_ecmul_H(aH, v, sizeof(aH), sizeof(v)); + if (err) { + return err; + } + + err = monero_ecadd(aH, kG, aH, sizeof(aH), sizeof(kG), sizeof(aH)); + if (err) { + return err; + } } else { - os_memmove(aH, kG, 32); + memcpy(aH, kG, 32); } - if (os_memcmp(C, aH, 32)) { - monero_lock_and_throw(SW_SECURITY_COMMITMENT_CONTROL); + if (memcmp(C, aH, 32) != 0) { +#ifndef BYPASS_COMMITMENT_FOR_TESTS + return SW_SECURITY_COMMITMENT_CHAIN_CONTROL; +#endif } // update commitment hash control - monero_sha256_commitment_update(C, 32); + err = monero_sha256_commitment_update(C, 32); + if (err) { + return err; + } if ((G_monero_vstate.options & IN_OPTION_MORE_COMMAND) == 0) { if (G_monero_vstate.io_protocol_version >= 2) { // finalize and check destination hash_control - monero_sha256_outkeys_final(k); - if (os_memcmp(k, G_monero_vstate.OUTK, 32)) { - monero_lock_and_throw(SW_SECURITY_OUTKEYS_CHAIN_CONTROL); + err = monero_sha256_outkeys_final(k); + if (err) { + return err; + } + if (memcmp(k, G_monero_vstate.OUTK, KEY_SIZE) != 0) { + return SW_SECURITY_COMMITMENT_CHAIN_CONTROL; } } // finalize commitment hash control - monero_sha256_commitment_final(NULL); + err = monero_sha256_commitment_final(NULL); + if (err) { + return err; + } monero_sha256_commitment_init(); } // ask user uint64_t amount; - amount = monero_bamount2uint64(v); + amount = monero_bamount2uint64(v, sizeof(v)); if (amount) { monero_amount2str(amount, G_monero_vstate.ux_amount, 15); - if (!is_change) { - ui_menu_validation_display(0); + snprintf(G_monero_vstate.ux_amount + strlen(G_monero_vstate.ux_amount), + sizeof(G_monero_vstate.ux_amount) - strlen(G_monero_vstate.ux_amount), " XMR"); + + if ((G_monero_vstate.options & IN_OPTION_MORE_COMMAND) == 0) { + if (!is_change) { + ui_menu_validation_display_last(0); + } else { + ui_menu_change_validation_display_last(0); + } } else { - ui_menu_change_validation_display(0); + if (!is_change) { + ui_menu_validation_display(0); + } else { + ui_menu_change_validation_display(0); + } } return 0; } @@ -165,45 +246,80 @@ int monero_apdu_mlsag_prehash_finalize() { unsigned char message[32]; unsigned char proof[32]; unsigned char H[32]; + int error; if (G_monero_vstate.options & IN_OPTION_MORE_COMMAND) { // accumulate monero_io_fetch(H, 32); monero_io_discard(1); - monero_keccak_update_H(H, 32); - monero_sha256_commitment_update(H, 32); -#ifdef DEBUG_HWDEVICE - monero_io_insert(H, 32); -#endif + error = monero_keccak_update_H(H, 32); + if (error) { + return error; + } + error = monero_sha256_commitment_update(H, 32); + if (error) { + return error; + } } else { // Finalize and check commitment hash control if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - monero_sha256_commitment_final(H); - if (os_memcmp(H, G_monero_vstate.C, 32)) { - monero_lock_and_throw(SW_SECURITY_COMMITMENT_CHAIN_CONTROL); + error = monero_sha256_commitment_final(H); + if (error) { + return error; + } + if (memcmp(H, G_monero_vstate.C, 32) != 0) { +#ifndef BYPASS_COMMITMENT_FOR_TESTS + return SW_SECURITY_COMMITMENT_CHAIN_CONTROL; +#endif } } // compute last H - monero_keccak_final_H(H); + error = monero_keccak_final_H(H); + if (error) { + return error; + } // compute last prehash monero_io_fetch(message, 32); monero_io_fetch(proof, 32); monero_io_discard(1); - monero_keccak_init_H(); - if (G_monero_vstate.io_protocol_version == 3) { - if (os_memcmp(message, G_monero_vstate.prefixH, 32) != 0) { - monero_lock_and_throw(SW_SECURITY_PREFIX_HASH); + error = monero_keccak_init_H(); + if (error) { + return error; + } + + if (G_monero_vstate.io_protocol_version >= 3) { + if (memcmp(message, G_monero_vstate.prefixH, 32) != 0) { +#ifndef BYPASS_COMMITMENT_FOR_TESTS + return SW_SECURITY_PREFIX_HASH; +#endif } } - monero_keccak_update_H(message, 32); - monero_keccak_update_H(H, 32); - monero_keccak_update_H(proof, 32); - monero_keccak_final_H(G_monero_vstate.mlsagH); -#ifdef DEBUG_HWDEVICE + error = monero_keccak_update_H(message, 32); + if (error) { + return error; + } + + error = monero_keccak_update_H(H, 32); + if (error) { + return error; + } + + error = monero_keccak_update_H(proof, 32); + if (error) { + return error; + } + + error = monero_keccak_final_H(G_monero_vstate.mlsagH); + if (error) { + return error; + } + monero_io_insert(G_monero_vstate.mlsagH, 32); - monero_io_insert(H, 32); -#endif + + if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { + ui_menu_transaction_signed(); + } } return SW_OK; diff --git a/src/monero_proof.c b/src/monero_proof.c index 30eb0287..0c89b31a 100644 --- a/src/monero_proof.c +++ b/src/monero_proof.c @@ -25,16 +25,6 @@ /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ -/* - * pick random k - * if B: - * compute X = k*B - * else: - * compute X = k*G - * compute Y = k*A - * sig.c = Hs(Msg || D || X || Y) - * sig.r = k - sig.c*r - */ int monero_apdu_get_tx_proof() { unsigned char *msg; unsigned char *R; @@ -45,43 +35,105 @@ int monero_apdu_get_tx_proof() { unsigned char XY[32]; unsigned char sig_c[32]; unsigned char sig_r[32]; -#define k (G_monero_vstate.tmp + 256) + unsigned char sep[32]; + int err = 0; +#define k (G_monero_vstate.tmp + 256) +#define k_len (sizeof(G_monero_vstate.tmp) - 256) msg = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(32); R = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(32); A = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(32); B = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); + monero_io_skip(32); D = G_monero_vstate.io_buffer + G_monero_vstate.io_offset; - monero_io_fetch(NULL, 32); - monero_io_fetch_decrypt_key(r); + monero_io_skip(32); + err = monero_io_fetch_decrypt_key(r, sizeof(r)); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } monero_io_discard(0); - monero_rng_mod_order(k); - os_memmove(G_monero_vstate.tmp + 32 * 0, msg, 32); - os_memmove(G_monero_vstate.tmp + 32 * 1, D, 32); + // Generate random k + err = monero_rng_mod_order(k, k_len); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } + + // tmp = msg + memcpy(G_monero_vstate.tmp + 32 * 0, msg, 32); + // tmp = msg || D + memcpy(G_monero_vstate.tmp + 32 * 1, D, 32); if (G_monero_vstate.options & 1) { - monero_ecmul_k(XY, B, k); + // X = kB + err = monero_ecmul_k(XY, B, k, sizeof(XY), 32, k_len); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } } else { - monero_ecmul_G(XY, k); + // X = kG + err = monero_ecmul_G(XY, k, sizeof(XY), k_len); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } } - os_memmove(G_monero_vstate.tmp + 32 * 2, XY, 32); + // tmp = msg || D || X + memcpy(G_monero_vstate.tmp + 32 * 2, XY, 32); - monero_ecmul_k(XY, A, k); - os_memmove(G_monero_vstate.tmp + 32 * 3, XY, 32); + // Y = kA + err = monero_ecmul_k(XY, A, k, sizeof(XY), 32, k_len); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } + // tmp = msg || D || X || Y + memcpy(G_monero_vstate.tmp + 32 * 3, XY, 32); + err = monero_keccak_H((unsigned char *)"TXPROOF_V2", 10, sep); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } + // tmp = msg || D || X || Y || sep + memcpy(G_monero_vstate.tmp + 32 * 4, sep, 32); + // tmp = msg || D || X || Y || sep || R + memcpy(G_monero_vstate.tmp + 32 * 5, R, 32); + // tmp = msg || D || X || Y || sep || R || A + memcpy(G_monero_vstate.tmp + 32 * 6, A, 32); + // tmp = msg || D || X || Y || sep || R || B or [0] + memcpy(G_monero_vstate.tmp + 32 * 7, B, 32); - monero_hash_to_scalar(sig_c, &G_monero_vstate.tmp[0], 32 * 4); + // sig_c = H_n(tmp) + err = monero_hash_to_scalar(sig_c, &G_monero_vstate.tmp[0], sizeof(sig_c), 32 * 8); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } + + // sig_c*r + err = monero_multm(XY, sig_c, r, sizeof(XY), sizeof(sig_c), sizeof(r)); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } - monero_multm(XY, sig_c, r); - monero_subm(sig_r, k, XY); + // sig_r = k - sig_c*r + err = monero_subm(sig_r, k, XY, sizeof(sig_r), k_len, sizeof(r)); + if (err) { + explicit_bzero(r, sizeof(r)); + return err; + } monero_io_insert(sig_c, 32); monero_io_insert(sig_r, 32); + explicit_bzero(r, sizeof(r)); return SW_OK; } diff --git a/src/monero_ram.c b/src/monero_ram.c index 7f7fd491..542fe723 100644 --- a/src/monero_ram.c +++ b/src/monero_ram.c @@ -24,20 +24,10 @@ #include "os_io_seproxyhal.h" -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -#ifdef HAVE_UX_FLOW -/* --- "NANO-X" and "NANO-S flow" config --- */ +extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; #include "ux.h" -ux_state_t G_ux; -bolos_ux_params_t G_ux_params; - -#else -/* --- "NANO-S legacy" config --- */ - -ux_state_t ux; - -#endif +extern ux_state_t G_ux; +extern bolos_ux_params_t G_ux_params; monero_v_state_t G_monero_vstate; diff --git a/src/monero_stealth.c b/src/monero_stealth.c index 7d82e2aa..9c4936e0 100644 --- a/src/monero_stealth.c +++ b/src/monero_stealth.c @@ -27,26 +27,39 @@ /* ----------------------------------------------------------------------- */ int monero_apdu_stealth() { int i; - unsigned char pub[32]; - unsigned char sec[32]; + unsigned char pub[KEY_SIZE]; + unsigned char sec[KEY_SIZE]; unsigned char drv[33]; unsigned char payID[8]; + int err = 0; // fetch pub - monero_io_fetch(pub, 32); + monero_io_fetch(pub, KEY_SIZE); // fetch sec - monero_io_fetch_decrypt_key(sec); + err = monero_io_fetch_decrypt_key(sec, sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } // fetch paymentID monero_io_fetch(payID, 8); monero_io_discard(0); // Compute Dout - monero_generate_key_derivation(drv, pub, sec); + err = monero_generate_key_derivation(drv, pub, sec, sizeof(drv), sizeof(pub), sizeof(sec)); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } // compute mask - drv[32] = ENCRYPTED_PAYMENT_ID_TAIL; - monero_keccak_F(drv, 33, sec); + drv[KEY_SIZE] = ENCRYPTED_PAYMENT_ID_TAIL; + err = monero_keccak_F(drv, 33, sec); + if (err) { + explicit_bzero(sec, sizeof(sec)); + return err; + } // stealth! for (i = 0; i < 8; i++) { @@ -55,5 +68,7 @@ int monero_apdu_stealth() { monero_io_insert(payID, 8); + explicit_bzero(sec, sizeof(sec)); + return SW_OK; } \ No newline at end of file diff --git a/src/monero_types.h b/src/monero_types.h index b98d7898..7119fb6a 100644 --- a/src/monero_types.h +++ b/src/monero_types.h @@ -21,19 +21,10 @@ #include "os_io_seproxyhal.h" -#if CX_APILEVEL == 8 -#define PIN_VERIFIED (!0) -#elif CX_APILEVEL == 9 || CX_APILEVEL == 10 - -#define PIN_VERIFIED BOLOS_UX_OK -#else -#error CX_APILEVEL not supported -#endif - /* cannot send more that F0 bytes in CCID, why? do not know for now - * So set up length to F0 minus 2 bytes for SW + * So set up length to F0 */ -#define MONERO_APDU_LENGTH 0xFE +#define MONERO_APDU_LENGTH 0xF0 /* big private DO */ #define MONERO_EXT_PRIVATE_DO_LENGTH 512 @@ -55,6 +46,8 @@ #define TESTNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX 54 #define TESTNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX 63 +#define KEY_SIZE 32 + enum network_type { #ifndef MONERO_ALPHA MAINNET = 0, @@ -80,9 +73,9 @@ struct monero_nv_state_s { unsigned int account_id; /* spend key */ - unsigned char b[32]; + unsigned char b[KEY_SIZE]; /* view key */ - unsigned char a[32]; + unsigned char a[KEY_SIZE]; /*words*/ #define WORDS_MAX_LENGTH 20 @@ -148,8 +141,8 @@ struct monero_v_state_s { unsigned int tx_sign_cnt; /* sc_add control */ - unsigned char last_derive_secret_key[32]; - unsigned char last_get_subaddress_secret_key[32]; + unsigned char last_derive_secret_key[KEY_SIZE]; + unsigned char last_get_subaddress_secret_key[KEY_SIZE]; /* ------------------------------------------ */ /* --- Crypo --- */ @@ -161,11 +154,11 @@ struct monero_v_state_s { /* SPK */ cx_aes_key_t spk; - unsigned char hmac_key[32]; + unsigned char hmac_key[KEY_SIZE]; /* Tx key */ - unsigned char R[32]; - unsigned char r[32]; + unsigned char R[KEY_SIZE]; + unsigned char r[KEY_SIZE]; /* prefix/mlsag hash */ cx_sha3_t keccakF; @@ -176,7 +169,7 @@ struct monero_v_state_s { /* -- track tx-in/out and commitment -- */ cx_sha256_t sha256_out_keys; - unsigned char OUTK[32]; + unsigned char OUTK[KEY_SIZE]; cx_sha256_t sha256_commitment; unsigned char C[32]; @@ -253,6 +246,7 @@ typedef struct monero_v_state_s monero_v_state_t; #define INS_DERIVE_PUBLIC_KEY 0x36 #define INS_DERIVE_SECRET_KEY 0x38 #define INS_GEN_KEY_IMAGE 0x3A +#define INS_DERIVE_VIEW_TAG 0x3B #define INS_SECRET_KEY_ADD 0x3C #define INS_GENERATE_KEYPAIR 0x40 #define INS_SECRET_SCAL_MUL_KEY 0x42 @@ -274,6 +268,7 @@ typedef struct monero_v_state_s monero_v_state_t; #define INS_PREFIX_HASH 0x7D #define INS_VALIDATE 0x7C #define INS_MLSAG 0x7E +#define INS_CLSAG 0x7F #define INS_CLOSE_TX 0x80 #define INS_GET_TX_PROOF 0xA0 diff --git a/src/monero_ux_msg.c b/src/monero_ux_msg.c deleted file mode 100644 index e9948a15..00000000 --- a/src/monero_ux_msg.c +++ /dev/null @@ -1,20 +0,0 @@ -/***************************************************************************** - * Ledger Monero App. - * (c) 2017-2020 Cedric Mesnil , Ledger SAS. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -const char* const C_OK = "OK"; -const char* const C_NOK = "NOK"; diff --git a/src/monero_ux_msg.h b/src/monero_ux_msg.h deleted file mode 100644 index bafdf484..00000000 --- a/src/monero_ux_msg.h +++ /dev/null @@ -1,30 +0,0 @@ -/***************************************************************************** - * Ledger Monero App. - * (c) 2017-2020 Cedric Mesnil , Ledger SAS. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#ifndef MONERO_UX_MSG_H -#define MONERO_UX_MSG_H - -extern const char* const C_OK; -extern const char* const C_NOK; - -#define PICSTR(x) ((char*)PIC(x)) - -#define OK PICSTR(C_OK) -#define NOK PICSTR(C_NOK) - -#endif diff --git a/src/monero_ux_nano.c b/src/monero_ux_nano.c index 8d3d7b66..d48e37b1 100644 --- a/src/monero_ux_nano.c +++ b/src/monero_ux_nano.c @@ -16,7 +16,7 @@ * limitations under the License. *****************************************************************************/ -#if defined(UI_NANO_X) || defined(UI_NANO_SX) +#ifdef HAVE_BAGL #include "os.h" #include "cx.h" @@ -24,7 +24,6 @@ #include "monero_api.h" #include "monero_vars.h" -#include "monero_ux_msg.h" #include "os_io_seproxyhal.h" #include "string.h" #include "glyphs.h" @@ -36,31 +35,8 @@ #define ACCEPT 0xACCE #define REJECT ~ACCEPT -void ui_menu_main_display(unsigned int value); - -/* -------------------------------------- LOCK--------------------------------------- */ - -void ui_menu_pinlock_display() { - struct { - bolos_ux_t ux_id; - // length of parameters in the u union to be copied during the syscall - unsigned int len; - union { - struct { - unsigned int cancellable; - } validate_pin; - } u; - - } ux_params; - - os_global_pin_invalidate(); - G_monero_vstate.protocol_barrier = PROTOCOL_LOCKED_UNLOCKABLE; - ux_params.ux_id = BOLOS_UX_VALIDATE_PIN; - ux_params.len = sizeof(ux_params.u.validate_pin); - ux_params.u.validate_pin.cancellable = 0; - os_ux((bolos_ux_params_t*)&ux_params); - ui_menu_main_display(0); -} +void __attribute__((noreturn)) app_exit(void); +void ui_menu_main_display(); /* -------------------------------------- 25 WORDS --------------------------------------- */ void ui_menu_words_display(unsigned int value); @@ -68,7 +44,7 @@ void ui_menu_words_clear(unsigned int value); void ui_menu_words_back(unsigned int value); UX_STEP_NOCB(ux_menu_words_1_step, -#ifdef UI_NANO_X +#ifndef TARGET_NANOS bnnn_paging, #else bn_paging, @@ -85,20 +61,24 @@ UX_STEP_CB(ux_menu_words_3_step, pb, ui_menu_words_back(0), {&C_icon_back, "back UX_FLOW(ux_flow_words, &ux_menu_words_1_step, &ux_menu_words_2_step, &ux_menu_words_3_step); -void ui_menu_words_clear(unsigned int value) { +void ui_menu_words_clear(unsigned int value __attribute__((unused))) { monero_clear_words(); - ui_menu_main_display(0); + ui_menu_main_display(); } -void ui_menu_words_back(unsigned int value) { ui_menu_main_display(1); } +void ui_menu_words_back(unsigned int value __attribute__((unused))) { + ui_menu_main_display(); +} -void ui_menu_words_display(unsigned int value) { ux_flow_init(0, ux_flow_words, NULL); } +void ui_menu_words_display(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_words, NULL); +} -void settings_show_25_words(void) { ui_menu_words_display(0); } +void settings_show_25_words(void) { + ui_menu_words_display(0); +} /* -------------------------------- INFO UX --------------------------------- */ -unsigned int ui_menu_info_action(unsigned int value); - -UX_STEP_CB(ux_menu_info_1_step, nn, ui_menu_info_action(0), +UX_STEP_CB(ux_menu_info_1_step, nn, (void)ui_menu_main_display(), { G_monero_vstate.ux_info1, G_monero_vstate.ux_info2, @@ -106,24 +86,34 @@ UX_STEP_CB(ux_menu_info_1_step, nn, ui_menu_info_action(0), UX_FLOW(ux_flow_info, &ux_menu_info_1_step); -unsigned int ui_menu_info_action(unsigned int value) { - if (G_monero_vstate.protocol_barrier == PROTOCOL_LOCKED) { - ui_menu_pinlock_display(); - } else { - ui_menu_main_display(0); - } - return 0; -} - -void ui_menu_info_display2(unsigned int value, char* line1, char* line2) { +static void ui_menu_info_display2(unsigned int value __attribute__((unused)), const char* line1, + const char* line2) { snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "%s", line1); snprintf(G_monero_vstate.ux_info2, sizeof(G_monero_vstate.ux_info2), "%s", line2); ux_flow_init(0, ux_flow_info, NULL); } -void ui_menu_info_display(unsigned int value) { ux_flow_init(0, ux_flow_info, NULL); } +static void ui_menu_info_display(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_info, NULL); +} + +void ui_menu_show_tx_aborted(void) { + ui_menu_info_display2(0, "TX", "Aborted"); +} + +void ui_menu_show_security_error(void) { + ui_menu_info_display(0); +} /* -------------------------------- OPEN TX UX --------------------------------- */ +unsigned int ui_menu_transaction_start(void) { + return SW_OK; +} + +unsigned int ui_menu_transaction_signed(void) { + return SW_OK; +} + unsigned int ui_menu_opentx_action(unsigned int value); UX_STEP_NOCB(ux_menu_opentx_1_step, nn, {"Process", "new TX ?"}); @@ -135,38 +125,24 @@ UX_STEP_CB(ux_menu_opentx_3_step, pb, ui_menu_opentx_action(REJECT), {&C_icon_cr UX_FLOW(ux_flow_opentx, &ux_menu_opentx_1_step, &ux_menu_opentx_2_step, &ux_menu_opentx_3_step); unsigned int ui_menu_opentx_action(unsigned int value) { - unsigned int sw; - unsigned char x[32]; + unsigned int sw = SW_OK; monero_io_discard(0); - os_memset(x, 0, 32); - sw = SW_OK; if (value == ACCEPT) { sw = monero_apdu_open_tx_cont(); ui_menu_info_display2(0, "Processing TX", "..."); } else { - monero_abort_tx(0); + monero_abort_tx(); sw = SW_DENY; - ui_menu_info_display2(0, "Tansaction", "aborted"); + ui_menu_info_display2(0, "Transaction", "aborted"); } monero_io_insert_u16(sw); monero_io_do(IO_RETURN_AFTER_TX); return 0; } -#if 0 -void ui_menu_opentx_display(unsigned int value) { - if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - ux_flow_init(0, ux_flow_opentx,NULL); - } else { - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Prepare new"); - snprintf(G_monero_vstate.ux_info2, sizeof(G_monero_vstate.ux_info2), "TX / %d", G_monero_vstate.tx_cnt); - ui_menu_info_display(0); - } -} -#else -void ui_menu_opentx_display(unsigned int value) { +void ui_menu_opentx_display(unsigned int value __attribute__((unused))) { uint32_t i; if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Processing TX"); @@ -179,7 +155,6 @@ void ui_menu_opentx_display(unsigned int value) { G_monero_vstate.ux_info2[i] = 0; ui_menu_info_display(0); } -#endif /* ----------------- FEE/CHANGE/TIMELOCK VALIDATION ----------------- */ @@ -206,7 +181,7 @@ UX_STEP_NOCB(ux_menu_validation_timelock_1_step, bn, UX_STEP_CB(ux_menu_validation_cf_2_step, pb, ui_menu_amount_validation_action(ACCEPT), { &C_icon_validate_14, - "Accept", + "Sign transaction", }); UX_STEP_CB(ux_menu_validation_cf_3_step, pb, ui_menu_amount_validation_action(REJECT), @@ -229,7 +204,7 @@ void ui_menu_amount_validation_action(unsigned int value) { if (value == ACCEPT) { sw = SW_OK; } else { - monero_abort_tx(0); + monero_abort_tx(); sw = SW_DENY; } monero_io_insert_u16(sw); @@ -237,22 +212,28 @@ void ui_menu_amount_validation_action(unsigned int value) { ui_menu_info_display2(0, "Processing TX", "..."); } -void ui_menu_fee_validation_display(unsigned int value) { ux_flow_init(0, ux_flow_fee, NULL); } +void ui_menu_fee_validation_display(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_fee, NULL); +} -void ui_menu_change_validation_display(unsigned int value) { +void ui_menu_change_validation_display(unsigned int value __attribute__((unused))) { ux_flow_init(0, ux_flow_change, NULL); } -void ui_menu_timelock_validation_display(unsigned int value) { +void ui_menu_change_validation_display_last(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_change, NULL); +} + +void ui_menu_timelock_validation_display(unsigned int value __attribute__((unused))) { ux_flow_init(0, ux_flow_timelock, NULL); } /* ----------------------------- USER DEST/AMOUNT VALIDATION ----------------------------- */ void ui_menu_validation_action(unsigned int value); -UX_STEP_NOCB(ux_menu_validation_1_step, bn, {"Amout", G_monero_vstate.ux_amount}); +UX_STEP_NOCB(ux_menu_validation_1_step, bn, {"Amount", G_monero_vstate.ux_amount}); UX_STEP_NOCB(ux_menu_validation_2_step, -#ifdef UI_NANO_X +#ifndef TARGET_NANOS bnnn_paging, #else bn_paging, @@ -260,7 +241,7 @@ UX_STEP_NOCB(ux_menu_validation_2_step, {"Destination", G_monero_vstate.ux_address}); UX_STEP_CB(ux_menu_validation_3_step, pb, ui_menu_validation_action(ACCEPT), - {&C_icon_validate_14, "Accept"}); + {&C_icon_validate_14, "Sign transaction"}); UX_STEP_CB(ux_menu_validation_4_step, pb, ui_menu_validation_action(REJECT), {&C_icon_crossmark, "Reject"}); @@ -268,14 +249,20 @@ UX_STEP_CB(ux_menu_validation_4_step, pb, ui_menu_validation_action(REJECT), UX_FLOW(ux_flow_validation, &ux_menu_validation_1_step, &ux_menu_validation_2_step, &ux_menu_validation_3_step, &ux_menu_validation_4_step); -void ui_menu_validation_display(unsigned int value) { ux_flow_init(0, ux_flow_validation, NULL); } +void ui_menu_validation_display(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_validation, NULL); +} + +void ui_menu_validation_display_last(unsigned int value __attribute__((unused))) { + ux_flow_init(0, ux_flow_validation, NULL); +} void ui_menu_validation_action(unsigned int value) { unsigned short sw; if (value == ACCEPT) { sw = SW_OK; } else { - monero_abort_tx(0); + monero_abort_tx(); sw = SW_DENY; } monero_io_insert_u16(sw); @@ -297,28 +284,28 @@ UX_STEP_CB(ux_menu_export_viewkey_3_step, pb, ui_menu_export_viewkey_action(REJE UX_FLOW(ux_flow_export_viewkey, &ux_menu_export_viewkey_1_step, &ux_menu_export_viewkey_2_step, &ux_menu_export_viewkey_3_step); -void ui_export_viewkey_display(unsigned int value) { +void ui_export_viewkey_display(unsigned int value __attribute__((unused))) { ux_flow_init(0, ux_flow_export_viewkey, NULL); } unsigned int ui_menu_export_viewkey_action(unsigned int value) { unsigned int sw; - unsigned char x[32]; + unsigned char x[KEY_SIZE]; monero_io_discard(0); - os_memset(x, 0, 32); + explicit_bzero(x, sizeof(x)); sw = SW_OK; if (value == ACCEPT) { - monero_io_insert(G_monero_vstate.a, 32); + monero_io_insert(G_monero_vstate.a, KEY_SIZE); G_monero_vstate.export_view_key = EXPORT_VIEW_KEY; } else { - monero_io_insert(x, 32); + monero_io_insert(x, KEY_SIZE); G_monero_vstate.export_view_key = 0; } monero_io_insert_u16(sw); monero_io_do(IO_RETURN_AFTER_TX); - ui_menu_main_display(0); + ui_menu_main_display(); return 0; } @@ -340,34 +327,38 @@ const char* account_submenu_getter(unsigned int idx) { } } -void account_back(void) { ui_menu_main_display(0); } +void account_back(void) { + ui_menu_main_display(); +} void account_submenu_selector(unsigned int idx) { if (idx <= 9) { monero_nvm_write((void*)&N_monero_pstate->account_id, &idx, sizeof(unsigned int)); monero_init(); } - ui_menu_main_display(0); + ui_menu_main_display(); } -void ui_menu_account_display(unsigned int value) { +void ui_menu_account_display(void) { ux_menulist_init(G_ux.stack_count - 1, account_submenu_getter, account_submenu_selector); } -void settings_change_account(void) { ui_menu_account_display(0); } +void settings_change_account(void) { + ui_menu_account_display(); +} /* -------------------------------- NETWORK UX --------------------------------- */ const char* const network_submenu_getter_values[] = { #ifdef MONERO_ALPHA - "Unvailable", + "Unavailable", #else "Main Network", #endif "Stage Network", "Test Network", "Abort"}; const char* const network_submenu_getter_values_selected[] = { #ifdef MONERO_ALPHA - "Unvailable", + "Unavailable", #else "Main Network +", #endif @@ -403,7 +394,9 @@ const char* network_submenu_getter(unsigned int idx) { } } -void network_back(void) { ui_menu_main_display(0); } +void network_back(void) { + ui_menu_main_display(); +} static void network_set_net(unsigned int network) { monero_install(network); @@ -426,16 +419,18 @@ void network_submenu_selector(unsigned int idx) { default: break; } - ui_menu_main_display(0); + ui_menu_main_display(); } -void ui_menu_network_display(unsigned int value) { +void ui_menu_network_display(void) { ux_menulist_init(G_ux.stack_count - 1, network_submenu_getter, network_submenu_selector); } -void settings_change_network(void) { ui_menu_network_display(0); } +void settings_change_network(void) { + ui_menu_network_display(); +} /* -------------------------------- RESET UX --------------------------------- */ -void ui_menu_reset_display(unsigned int value); +void ui_menu_reset_display(void); void ui_menu_reset_action(unsigned int value); UX_STEP_NOCB(ux_menu_reset_1_step, nn, @@ -458,9 +453,13 @@ UX_STEP_CB(ux_menu_reset_3_step, pb, ui_menu_reset_action(ACCEPT), UX_FLOW(ux_flow_reset, &ux_menu_reset_1_step, &ux_menu_reset_2_step, &ux_menu_reset_3_step); -void ui_menu_reset_display(unsigned int value) { ux_flow_init(0, ux_flow_reset, 0); } +void ui_menu_reset_display(void) { + ux_flow_init(0, ux_flow_reset, 0); +} -void settings_reset(void) { ui_menu_reset_display(0); } +void settings_reset(void) { + ui_menu_reset_display(); +} void ui_menu_reset_action(unsigned int value) { if (value == ACCEPT) { @@ -472,7 +471,7 @@ void ui_menu_reset_action(unsigned int value) { monero_nvm_write((void*)N_monero_pstate->magic, magic, 4); monero_init(); } - ui_menu_main_display(0); + ui_menu_main_display(); } /* ------------------------------- SETTINGS UX ------------------------------- */ @@ -487,7 +486,9 @@ const char* settings_submenu_getter(unsigned int idx) { return NULL; } -void settings_back(void) { ui_menu_main_display(0); } +void settings_back(void) { + ui_menu_main_display(); +} void settings_submenu_selector(unsigned int idx) { switch (idx) { @@ -512,7 +513,7 @@ void settings_submenu_selector(unsigned int idx) { #define STR(x) #x #define XSTR(x) STR(x) -#ifdef UI_NANO_X +#ifndef TARGET_NANOS UX_STEP_NOCB(ux_menu_about_1_step, bnnn, { "Monero", @@ -535,21 +536,23 @@ UX_STEP_NOCB(ux_menu_about_1b_step, nn, #endif -UX_STEP_CB(ux_menu_about_2_step, pb, ui_menu_main_display(0), +UX_STEP_CB(ux_menu_about_2_step, pb, ui_menu_main_display(), { &C_icon_back, "Back", }); UX_FLOW(ux_flow_about, -#ifdef UI_NANO_X +#ifndef TARGET_NANOS &ux_menu_about_1_step, #else &ux_menu_about_1a_step, &ux_menu_about_1b_step, #endif &ux_menu_about_2_step); -void ui_menu_about_display(unsigned int value) { ux_flow_init(0, ux_flow_about, NULL); } +void ui_menu_about_display(void) { + ux_flow_init(0, ux_flow_about, NULL); +} #undef STR #undef XSTR @@ -583,55 +586,62 @@ UX_STEP_CB(ux_menu_pubaddr_2_step, pb, ui_menu_pubaddr_action(0), {&C_icon_back, UX_FLOW(ux_flow_pubaddr, &ux_menu_pubaddr_01_step, &ux_menu_pubaddr_02_step, &ux_menu_pubaddr_1_step, &ux_menu_pubaddr_2_step); -void ui_menu_pubaddr_action(unsigned int value) { +void ui_menu_pubaddr_action(unsigned int value __attribute__((unused))) { if (G_monero_vstate.disp_addr_mode) { monero_io_insert_u16(SW_OK); monero_io_do(IO_RETURN_AFTER_TX); } G_monero_vstate.disp_addr_mode = 0; - ui_menu_main_display(0); + ui_menu_main_display(); } /** * */ -void ui_menu_any_pubaddr_display(unsigned int value, unsigned char* pub_view, - unsigned char* pub_spend, unsigned char is_subbadress, - unsigned char* paymanetID) { - memset(G_monero_vstate.ux_address, 0, sizeof(G_monero_vstate.ux_address)); +int ui_menu_any_pubaddr_display(unsigned int value __attribute__((unused)), unsigned char* pub_view, + unsigned char* pub_spend, unsigned char is_subbadress, + unsigned char* paymanetID) { + explicit_bzero(G_monero_vstate.ux_address, sizeof(G_monero_vstate.ux_address)); switch (G_monero_vstate.disp_addr_mode) { case 0: case DISP_MAIN: - os_memmove(ADDR_TYPE, "Main", 4); - os_memmove(ADDR_MAJOR, "Major: 0", 8); - os_memmove(ADDR_MINOR, "minor: 0", 8); + memcpy(ADDR_TYPE, "Main", sizeof("Main")); + memcpy(ADDR_MAJOR, "Major: 0", sizeof("Major: 0")); + memcpy(ADDR_MINOR, "minor: 0", sizeof("minor: 0")); break; case DISP_SUB: - os_memmove(ADDR_TYPE, "Sub", 3); + memcpy(ADDR_TYPE, "Sub", sizeof("Sub")); snprintf(ADDR_MAJOR, 16, "Major: %d", G_monero_vstate.disp_addr_M); snprintf(ADDR_MINOR, 16, "minor: %d", G_monero_vstate.disp_addr_m); break; case DISP_INTEGRATED: - os_memmove(ADDR_TYPE, "Integrated", 10); - os_memmove(ADDR_IDSTR, "Payment ID", 10); - os_memmove(ADDR_ID, G_monero_vstate.payment_id, 16); + memcpy(ADDR_TYPE, "Integrated", sizeof("Integrated")); + memcpy(ADDR_IDSTR, "Payment ID", sizeof("Payment ID")); + strncpy(ADDR_ID, G_monero_vstate.payment_id, 16); break; } - monero_base58_public_key(G_monero_vstate.ux_address + strlen(G_monero_vstate.ux_address), - pub_view, pub_spend, is_subbadress, paymanetID); + int error = monero_base58_public_key(G_monero_vstate.ux_address, pub_view, pub_spend, + is_subbadress, paymanetID); + if (error) { + return error; + } ux_layout_bnnn_paging_reset(); ux_flow_init(0, ux_flow_pubaddr, NULL); + return 0; } void ui_menu_pubaddr_display(unsigned int value) { G_monero_vstate.disp_addr_mode = 0; G_monero_vstate.disp_addr_M = 0; - G_monero_vstate.disp_addr_M = 0; - ui_menu_any_pubaddr_display(value, G_monero_vstate.A, G_monero_vstate.B, 0, NULL); + G_monero_vstate.disp_addr_m = 0; + int error = ui_menu_any_pubaddr_display(value, G_monero_vstate.A, G_monero_vstate.B, 0, NULL); + if (error) { + app_exit(); + } } #undef ADDR_TYPE @@ -651,14 +661,14 @@ UX_STEP_CB(ux_menu_main_2_step, pb, settings_submenu_selector), {&C_icon_coggle, "Settings"}); -UX_STEP_CB(ux_menu_main_3_step, pb, ui_menu_about_display(0), {&C_icon_certificate, "About"}); +UX_STEP_CB(ux_menu_main_3_step, pb, ui_menu_about_display(), {&C_icon_certificate, "About"}); -UX_STEP_CB(ux_menu_main_4_step, pb, os_sched_exit(0), {&C_icon_dashboard_x, "Quit app"}); +UX_STEP_CB(ux_menu_main_4_step, pb, app_exit(), {&C_icon_dashboard_x, "Quit app"}); UX_FLOW(ux_flow_main, &ux_menu_main_1_step, &ux_menu_main_2_step, &ux_menu_main_3_step, &ux_menu_main_4_step); -void ui_menu_main_display(unsigned int value) { +void ui_menu_main_display(void) { // reserve a display stack slot if none yet if (G_ux.stack_count == 0) { ux_stack_push(); @@ -667,7 +677,9 @@ void ui_menu_main_display(unsigned int value) { } /* --- INIT --- */ -void ui_init(void) { ui_menu_main_display(0); } +void ui_init(void) { + ui_menu_main_display(); +} void io_seproxyhal_display(const bagl_element_t* element) { io_seproxyhal_display_default((bagl_element_t*)element); diff --git a/src/monero_ux_nanos.c b/src/monero_ux_nanos.c deleted file mode 100644 index ea3777d8..00000000 --- a/src/monero_ux_nanos.c +++ /dev/null @@ -1,782 +0,0 @@ -/***************************************************************************** - * Ledger Monero App. - * (c) 2017-2020 Cedric Mesnil , Ledger SAS. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#ifdef UI_NANO_S - -#include "os.h" -#include "cx.h" -#include "monero_types.h" -#include "monero_api.h" -#include "monero_vars.h" - -#include "monero_ux_msg.h" -#include "os_io_seproxyhal.h" -#include "string.h" -#include "glyphs.h" - -/* ----------------------------------------------------------------------- */ -/* --- NanoS UI layout --- */ -/* ----------------------------------------------------------------------- */ - -const ux_menu_entry_t ui_menu_reset[]; -void ui_menu_reset_action(unsigned int value); - -const ux_menu_entry_t ui_menu_settings[]; - -const ux_menu_entry_t ui_menu_main[]; -void ui_menu_main_display(unsigned int value); -const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element); - -void ui_menu_settings_display(unsigned int value); - -/* -------------------------------------- LOCK--------------------------------------- */ - -void ui_menu_pinlock_display() { - struct { - bolos_ux_t ux_id; - // length of parameters in the u union to be copied during the syscall - unsigned int len; - union { - struct { - unsigned int cancellable; - } validate_pin; - } u; - } ux_params; - - os_global_pin_invalidate(); - G_monero_vstate.protocol_barrier = PROTOCOL_LOCKED_UNLOCKABLE; - ux_params.ux_id = BOLOS_UX_VALIDATE_PIN; - ux_params.len = sizeof(ux_params.u.validate_pin); - ux_params.u.validate_pin.cancellable = 0; - os_ux((bolos_ux_params_t*)&ux_params); - ui_menu_main_display(0); -} - -/* -------------------------------------- 25 WORDS --------------------------------------- */ -void ui_menu_words_clear(unsigned int value); -void ui_menu_words_back(unsigned int value); -#define WORDS N_monero_pstate->words -const ux_menu_entry_t ui_menu_words[] = { - {NULL, ui_menu_words_back, 0, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 2, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 4, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 6, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 8, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 10, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 12, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 14, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 16, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 18, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 20, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 22, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_back, 24, NULL, "", "", 0, 0}, - {NULL, ui_menu_words_clear, -1, NULL, "CLEAR WORDS", "(NO WIPE)", 0, 0}, - UX_MENU_END}; - -const bagl_element_t* ui_menu_words_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - if ((entry->userid >= 0) && (entry->userid < 25)) { - if (element->component.userid == 0x21) { - element->text = N_monero_pstate->words[entry->userid]; - } - - if ((element->component.userid == 0x22) && (entry->userid < 24)) { - element->text = N_monero_pstate->words[entry->userid + 1]; - } - } - - return element; -} - -void ui_menu_words_display(unsigned int value) { - UX_MENU_DISPLAY(0, ui_menu_words, ui_menu_words_preprocessor); -} - -void ui_menu_words_clear(unsigned int value) { - monero_clear_words(); - ui_menu_main_display(0); -} - -void ui_menu_words_back(unsigned int value) { ui_menu_settings_display(1); } - -/* -------------------------------- INFO UX --------------------------------- */ -unsigned int ui_menu_info_button(unsigned int button_mask, unsigned int button_mask_counter); - -const bagl_element_t ui_menu_info[] = { - // type userid x y w h str rad fill fg bg - // font_id icon_id - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL}, - - {{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - G_monero_vstate.ux_info1}, - - {{BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - G_monero_vstate.ux_info2}, -}; - -unsigned int ui_menu_info_button(unsigned int button_mask, unsigned int button_mask_counter) { - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - if (G_monero_vstate.protocol_barrier == PROTOCOL_LOCKED) { - ui_menu_pinlock_display(); - } else { - ui_menu_main_display(0); - } - break; - - default: - return 0; - } - return 0; -} - -void ui_menu_info_display2(unsigned int value, char* line1, char* line2) { - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "%s", line1); - snprintf(G_monero_vstate.ux_info2, sizeof(G_monero_vstate.ux_info2), "%s", line2); - UX_DISPLAY(ui_menu_info, NULL); -} - -void ui_menu_info_display(unsigned int value) { UX_DISPLAY(ui_menu_info, NULL); } - -/* -------------------------------- OPEN TX UX --------------------------------- */ - -unsigned int ui_menu_opentx_button(unsigned int button_mask, unsigned int button_mask_counter); - -const bagl_element_t ui_menu_opentx[] = { - // type userid x y w h str rad fill fg bg - // font_id icon_id - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL}, - - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, NULL}, - - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, NULL}, - - {{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Process"}, - - {{BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "new TX ?"}, - -}; - -unsigned int ui_menu_opentx_button(unsigned int button_mask, unsigned int button_mask_counter) { - unsigned int sw; - unsigned char x[32]; - - monero_io_discard(0); - os_memset(x, 0, 32); - sw = SW_OK; - - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_LEFT: // CANCEL - monero_abort_tx(); - sw = SW_DENY; - break; - - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: // OK - sw = monero_apdu_open_tx_cont(); - break; - - default: - return 0; - } - monero_io_insert_u16(sw); - monero_io_do(IO_RETURN_AFTER_TX); - if (sw == SW_OK) { - ui_menu_info_display2(0, "Processing TX", "..."); - } else { - ui_menu_info_display2(0, "Tansaction", "aborted"); - } - return 0; -} -#if 0 -void ui_menu_opentx_display(unsigned int value) { - if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - UX_DISPLAY(ui_menu_opentx, (void*)NULL); - } else { - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Prepare new"); - snprintf(G_monero_vstate.ux_info2, sizeof(G_monero_vstate.ux_info2), "TX / %d", G_monero_vstate.tx_cnt); - ui_menu_info_display(0); - } -} -#else -void ui_menu_opentx_display(unsigned int value) { - uint32_t i; - if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Processing TX"); - } else { - snprintf(G_monero_vstate.ux_info1, sizeof(G_monero_vstate.ux_info1), "Preparing TX"); - } - for (i = 0; (i < G_monero_vstate.tx_cnt) && (i < 12); i++) { - G_monero_vstate.ux_info2[i] = '.'; - } - G_monero_vstate.ux_info2[i] = 0; - ui_menu_info_display(0); -} -#endif - -/* ----------------- FEE/CHANGE/TIMELOCK VALIDATION ----------------- */ - -#define ACCEPT 0xACCE -#define REJECT ~ACCEPT - -void ui_menu_amount_validation_action(unsigned int value); - -const ux_menu_entry_t ui_menu_fee_validation[] = { - {NULL, NULL, 1, NULL, " Fee", "?xmr?", 0, 0}, - {NULL, ui_menu_amount_validation_action, REJECT, NULL, "Reject", "Fee", 0, 0}, - {NULL, ui_menu_amount_validation_action, ACCEPT, NULL, "Accept", "Fee", 0, 0}, - UX_MENU_END}; -const ux_menu_entry_t ui_menu_change_validation[] = { - {NULL, NULL, 1, NULL, " Change", "?xmr?", 0, 0}, - {NULL, ui_menu_amount_validation_action, REJECT, NULL, "Reject", "Change", 0, 0}, - {NULL, ui_menu_amount_validation_action, ACCEPT, NULL, "Accept", "Change", 0, 0}, - UX_MENU_END}; -const ux_menu_entry_t ui_menu_timelock_validation[] = { - {NULL, NULL, 1, NULL, " Timelock", "?...?", 0, 0}, - {NULL, ui_menu_amount_validation_action, REJECT, NULL, "Reject", "Timelock", 0, 0}, - {NULL, ui_menu_amount_validation_action, ACCEPT, NULL, "Accept", "Timelock", 0, 0}, - UX_MENU_END}; -const bagl_element_t* ui_menu_amount_validation_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - /* --- Amount --- */ - if ((entry == &ui_menu_fee_validation[0]) || (entry == &ui_menu_change_validation[0]) || - (entry == &ui_menu_timelock_validation[0])) { - if (element->component.userid == 0x22) { - element->text = G_monero_vstate.ux_amount; - } - } - return element; -} - -void ui_menu_amount_validation_action(unsigned int value) { - unsigned short sw; - if (value == ACCEPT) { - sw = SW_OK; - } else { - sw = SW_DENY; - monero_abort_tx(); - } - monero_io_insert_u16(sw); - monero_io_do(IO_RETURN_AFTER_TX); - ui_menu_info_display2(0, "Processing TX", "..."); -} - -void ui_menu_fee_validation_display(unsigned int value) { - UX_MENU_DISPLAY(0, ui_menu_fee_validation, ui_menu_amount_validation_preprocessor); -} -void ui_menu_change_validation_display(unsigned int value) { - UX_MENU_DISPLAY(0, ui_menu_change_validation, ui_menu_amount_validation_preprocessor); -} -void ui_menu_timelock_validation_display(unsigned int value) { - UX_MENU_DISPLAY(0, ui_menu_timelock_validation, ui_menu_amount_validation_preprocessor); -} - -/* ----------------------------- USER DEST/AMOUNT VALIDATION ----------------------------- */ -void ui_menu_validation_action(unsigned int value); - -const ux_menu_entry_t ui_menu_validation[] = { - {NULL, NULL, 1, NULL, " Amount", "?xmr?", 0, 0}, - {NULL, NULL, 3, NULL, "Destination", "?dest.1?", 0, 0}, - {NULL, NULL, 4, NULL, "?dest.2?", "?dest.2?", 0, 0}, - {NULL, NULL, 5, NULL, "?dest.3?", "?dest.3?", 0, 0}, - {NULL, NULL, 6, NULL, "?dest.4?", "?dest.4?", 0, 0}, - {NULL, NULL, 7, NULL, "?dest.5?", "?dest.5?", 0, 0}, - {NULL, ui_menu_validation_action, REJECT, NULL, "Reject", "TX", 0, 0}, - {NULL, ui_menu_validation_action, ACCEPT, NULL, "Accept", "TX", 0, 0}, - UX_MENU_END}; - -const bagl_element_t* ui_menu_validation_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - /* --- Amount --- */ - if (entry == &ui_menu_validation[0]) { - if (element->component.userid == 0x22) { - element->text = G_monero_vstate.ux_amount; - } - } - - /* --- Destination --- */ - if (entry == &ui_menu_validation[1]) { - if (element->component.userid == 0x22) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 0, 11); - element->text = G_monero_vstate.ux_menu; - } - } - if (entry == &ui_menu_validation[2]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 1, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 2, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_validation[3]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 3, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 4, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_validation[4]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 5, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 6, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_validation[5]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 7, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 8, 7); - } - element->text = G_monero_vstate.ux_menu; - } - - return element; -} - -void ui_menu_validation_display(unsigned int value) { - UX_MENU_DISPLAY(0, ui_menu_validation, ui_menu_validation_preprocessor); -} - -void ui_menu_validation_action(unsigned int value) { - unsigned short sw; - if (value == ACCEPT) { - sw = SW_OK; - } else { - sw = SW_DENY; - monero_abort_tx(); - } - monero_io_insert_u16(sw); - monero_io_do(IO_RETURN_AFTER_TX); - ui_menu_info_display2(0, "Processing TX", "..."); -} - -/* -------------------------------- EXPORT VIEW KEY UX --------------------------------- */ -unsigned int ui_export_viewkey_prepro(const bagl_element_t* element); -unsigned int ui_export_viewkey_button(unsigned int button_mask, unsigned int button_mask_counter); - -const bagl_element_t ui_export_viewkey[] = { - // type userid x y w h str rad fill fg bg - // font_id icon_id - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL}, - - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, NULL}, - - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, NULL}, - - {{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - G_monero_vstate.ux_menu}, - - {{BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - G_monero_vstate.ux_menu}, -}; - -void ui_export_viewkey_display(unsigned int value) { - UX_DISPLAY(ui_export_viewkey, (void*)ui_export_viewkey_prepro); -} - -unsigned int ui_export_viewkey_prepro(const bagl_element_t* element) { - if (element->component.userid == 1) { - snprintf(G_monero_vstate.ux_menu, sizeof(G_monero_vstate.ux_menu), "Export"); - return 1; - } - if (element->component.userid == 2) { - snprintf(G_monero_vstate.ux_menu, sizeof(G_monero_vstate.ux_menu), "View Key"); - return 1; - } - snprintf(G_monero_vstate.ux_menu, sizeof(G_monero_vstate.ux_menu), "Please Cancel"); - return 1; -} - -unsigned int ui_export_viewkey_button(unsigned int button_mask, unsigned int button_mask_counter) { - unsigned int sw; - unsigned char x[32]; - - monero_io_discard(0); - os_memset(x, 0, 32); - sw = SW_OK; - - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_LEFT: // CANCEL - monero_io_insert(x, 32); - G_monero_vstate.export_view_key = 0; - break; - - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: // OK - monero_io_insert(G_monero_vstate.a, 32); - G_monero_vstate.export_view_key = EXPORT_VIEW_KEY; - break; - - default: - return 0; - } - monero_io_insert_u16(sw); - monero_io_do(IO_RETURN_AFTER_TX); - ui_menu_main_display(0); - return 0; -} - -/* -------------------------------- ACCOUNT UX --------------------------------- */ - -void ui_menu_account_action(unsigned int value); -const ux_menu_entry_t ui_menu_account[] = { - {NULL, NULL, 0, NULL, "It will reset", "the application!", 0, 0}, - {NULL, ui_menu_main_display, 0, &C_badge_back, "Abort", NULL, 61, 40}, - {NULL, ui_menu_account_action, 0, NULL, "0", NULL, 0, 0}, - {NULL, ui_menu_account_action, 1, NULL, "1", NULL, 0, 0}, - {NULL, ui_menu_account_action, 2, NULL, "2", NULL, 0, 0}, - {NULL, ui_menu_account_action, 3, NULL, "3", NULL, 0, 0}, - {NULL, ui_menu_account_action, 4, NULL, "4", NULL, 0, 0}, - {NULL, ui_menu_account_action, 5, NULL, "5", NULL, 0, 0}, - {NULL, ui_menu_account_action, 6, NULL, "6", NULL, 0, 0}, - {NULL, ui_menu_account_action, 7, NULL, "7", NULL, 0, 0}, - {NULL, ui_menu_account_action, 8, NULL, "8", NULL, 0, 0}, - {NULL, ui_menu_account_action, 9, NULL, "9", NULL, 0, 0}, - UX_MENU_END}; - -const bagl_element_t* ui_menu_account_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - for (unsigned int i = 2; i < 12; i++) { - if ((entry == &ui_menu_account[i]) && (element->component.userid == 0x20) && - (N_monero_pstate->account_id == (i - 2))) { - G_monero_vstate.ux_menu[0] = '0' + (i - 2); - G_monero_vstate.ux_menu[1] = ' '; - G_monero_vstate.ux_menu[2] = '+'; - element->text = G_monero_vstate.ux_menu; - } - } - return element; -} - -void ui_menu_account_action(unsigned int value) { - monero_nvm_write((void*)&N_monero_pstate->account_id, &value, sizeof(unsigned int)); - monero_init(); - ui_menu_main_display(0); -} - -void ui_menu_account_display(unsigned int value) { - UX_MENU_DISPLAY(value, ui_menu_account, ui_menu_account_preprocessor); -} - -/* -------------------------------- NETWORK UX --------------------------------- */ -void ui_menu_network_action(unsigned int value); -const ux_menu_entry_t ui_menu_network[] = { - {NULL, NULL, 0, NULL, "It will reset", "the application!", 0, 0}, - {NULL, ui_menu_main_display, 0, &C_badge_back, "Abort", NULL, 61, 40}, - {NULL, ui_menu_network_action, TESTNET, NULL, "Test Network ", NULL, 0, 0}, - {NULL, ui_menu_network_action, STAGENET, NULL, "Stage Network", NULL, 0, 0}, -#ifndef MONERO_ALPHA - {NULL, ui_menu_network_action, MAINNET, NULL, "Main Network", NULL, 0, 0}, -#endif - UX_MENU_END}; - -const bagl_element_t* ui_menu_network_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if ((entry == &ui_menu_network[2]) && (element->component.userid == 0x20) && - (N_monero_pstate->network_id == TESTNET)) { - os_memmove(G_monero_vstate.ux_menu, "Test Network ", 14); - G_monero_vstate.ux_menu[13] = '+'; - element->text = G_monero_vstate.ux_menu; - } - if ((entry == &ui_menu_network[3]) && (element->component.userid == 0x20) && - (N_monero_pstate->network_id == STAGENET)) { - os_memmove(G_monero_vstate.ux_menu, "Stage Network ", 14); - G_monero_vstate.ux_menu[13] = '+'; - element->text = G_monero_vstate.ux_menu; - } -#ifndef MONERO_ALPHA - if ((entry == &ui_menu_network[4]) && (element->component.userid == 0x20) && - (N_monero_pstate->network_id == MAINNET)) { - os_memmove(G_monero_vstate.ux_menu, "Main Network ", 14); - G_monero_vstate.ux_menu[13] = '+'; - element->text = G_monero_vstate.ux_menu; - } -#endif - return element; -} - -void ui_menu_network_action(unsigned int value) { - monero_install(value); - monero_init(); - ui_menu_main_display(0); -} - -void ui_menu_network_display(unsigned int value) { - UX_MENU_DISPLAY(value, ui_menu_network, ui_menu_network_preprocessor); -} - -/* -------------------------------- RESET UX --------------------------------- */ - -const ux_menu_entry_t ui_menu_reset[] = { - {NULL, NULL, 0, NULL, "Really Reset ?", NULL, 0, 0}, - {NULL, ui_menu_main_display, 0, &C_badge_back, "No", NULL, 61, 40}, - {NULL, ui_menu_reset_action, 0, NULL, "Yes", NULL, 0, 0}, - UX_MENU_END}; - -void ui_menu_reset_action(unsigned int value) { - unsigned char magic[4]; - magic[0] = 0; - magic[1] = 0; - magic[2] = 0; - magic[3] = 0; - monero_nvm_write(N_monero_pstate->magic, magic, 4); - monero_init(); - ui_menu_main_display(0); -} -/* ------------------------------- SETTINGS UX ------------------------------- */ - -const ux_menu_entry_t ui_menu_settings[] = { - {NULL, ui_menu_account_display, 0, NULL, "Select Account", NULL, 0, 0}, - {NULL, ui_menu_network_display, 0, NULL, "Select Network", NULL, 0, 0}, - {NULL, ui_menu_words_display, 0, NULL, "Show 25 words", NULL, 0, 0}, - {ui_menu_reset, NULL, 0, NULL, "Reset", NULL, 0, 0}, - {NULL, ui_menu_main_display, 2, &C_badge_back, "Back", NULL, 61, 40}, - UX_MENU_END}; - -void ui_menu_settings_display(unsigned int value) { - UX_MENU_DISPLAY(value, ui_menu_settings, NULL); -} - -/* --------------------------------- INFO UX --------------------------------- */ - -#define STR(x) #x -#define XSTR(x) STR(x) - -const ux_menu_entry_t ui_menu_about[] = { - {NULL, NULL, -1, NULL, "Monero", NULL, 0, 0}, - {NULL, NULL, -1, NULL, "(c) Ledger SAS", NULL, 0, 0}, - {NULL, NULL, -1, NULL, "Spec " XSTR(SPEC_VERSION), NULL, 0, 0}, - {NULL, NULL, -1, NULL, "App " XSTR(MONERO_VERSION), NULL, 0, 0}, - {NULL, ui_menu_main_display, 3, &C_badge_back, "Back", NULL, 61, 40}, - UX_MENU_END}; - -#undef STR -#undef XSTR - -/* ---------------------------- PUBLIC ADDRESS UX ---------------------------- */ - -void ui_menu_pubaddr_action(unsigned int value); - -const ux_menu_entry_t ui_menu_pubaddr[] = { - {NULL, NULL, 3, NULL, "t1.1", "t1.2", 0, 0}, - - {NULL, NULL, 3, NULL, "i1.1", "i1.2", 0, 0}, - - {NULL, NULL, 5, NULL, "l1.1", "l1.2", 0, 0}, - {NULL, NULL, 6, NULL, "l2.1", "l2.2", 0, 0}, - {NULL, NULL, 7, NULL, "l3.1", "l3.2", 0, 0}, - {NULL, NULL, 6, NULL, "l4.1", "l4.2", 0, 0}, - {NULL, NULL, 7, NULL, "l5.1", "l5.2", 0, 0}, - //{NULL, ui_menu_main_display, 0, &C_badge_back, "Back", NULL, 61, 40}, - {NULL, ui_menu_pubaddr_action, 0, &C_badge_back, "Ok", NULL, 61, 40}, - UX_MENU_END}; - -const bagl_element_t* ui_menu_pubaddr_preprocessor(const ux_menu_entry_t* entry, - bagl_element_t* element) { - /* --- address --- */ - if (entry == &ui_menu_pubaddr[0]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - switch (G_monero_vstate.disp_addr_mode) { - case 0: - case DISP_MAIN: - os_memmove(G_monero_vstate.ux_menu, "Main", 4); - break; - case DISP_SUB: - os_memmove(G_monero_vstate.ux_menu, "Sub", 3); - break; - case DISP_INTEGRATED: - os_memmove(G_monero_vstate.ux_menu, "Integrated", 10); - break; - } - element->text = G_monero_vstate.ux_menu; - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, "Address", 7); - element->text = G_monero_vstate.ux_menu; - } - } - - if (entry == &ui_menu_pubaddr[1]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - switch (G_monero_vstate.disp_addr_mode) { - case 0: - case DISP_MAIN: - case DISP_SUB: - snprintf(G_monero_vstate.ux_menu, sizeof(G_monero_vstate.ux_menu), "Major: %d", - G_monero_vstate.disp_addr_M); - break; - case DISP_INTEGRATED: - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.payment_id, 8); - break; - } - element->text = G_monero_vstate.ux_menu; - } - if (element->component.userid == 0x22) { - switch (G_monero_vstate.disp_addr_mode) { - case 0: - case DISP_MAIN: - case DISP_SUB: - snprintf(G_monero_vstate.ux_menu, sizeof(G_monero_vstate.ux_menu), "minor: %d", - G_monero_vstate.disp_addr_m); - break; - case DISP_INTEGRATED: - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.payment_id + 8, 8); - break; - } - element->text = G_monero_vstate.ux_menu; - } - element->text = G_monero_vstate.ux_menu; - } - - if (entry == &ui_menu_pubaddr[2]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 0, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 1, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_pubaddr[3]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 2, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 3, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_pubaddr[4]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 4, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 5, 11); - } - element->text = G_monero_vstate.ux_menu; - } - if (entry == &ui_menu_pubaddr[5]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 6, 11); - } - if (element->component.userid == 0x22) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 7, 11); - } - element->text = G_monero_vstate.ux_menu; - } - - if (entry == &ui_menu_pubaddr[6]) { - os_memset(G_monero_vstate.ux_menu, 0, sizeof(G_monero_vstate.ux_menu)); - if (element->component.userid == 0x21) { - if (G_monero_vstate.disp_addr_mode == DISP_INTEGRATED) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 8, 11); - } else { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 8, 7); - } - } - if (element->component.userid == 0x22) { - if (G_monero_vstate.disp_addr_mode == DISP_INTEGRATED) { - os_memmove(G_monero_vstate.ux_menu, G_monero_vstate.ux_address + 11 * 9, 7); - } - } - element->text = G_monero_vstate.ux_menu; - } - - return element; -} - -void ui_menu_pubaddr_action(unsigned int value) { - if (G_monero_vstate.disp_addr_mode) { - monero_io_insert_u16(SW_OK); - monero_io_do(IO_RETURN_AFTER_TX); - } - G_monero_vstate.disp_addr_mode = 0; - G_monero_vstate.disp_addr_M = 0; - G_monero_vstate.disp_addr_m = 0; - ui_menu_main_display(0); -} - -void ui_menu_any_pubaddr_display(unsigned int value, unsigned char* pub_view, - unsigned char* pub_spend, unsigned char is_subbadress, - unsigned char* paymanetID) { - monero_base58_public_key(G_monero_vstate.ux_address, pub_view, pub_spend, is_subbadress, - paymanetID); - UX_MENU_DISPLAY(value, ui_menu_pubaddr, ui_menu_pubaddr_preprocessor); -} - -void ui_menu_pubaddr_display(unsigned int value) { - monero_base58_public_key(G_monero_vstate.ux_address, G_monero_vstate.A, G_monero_vstate.B, 0, - NULL); - G_monero_vstate.disp_addr_mode = 0; - G_monero_vstate.disp_addr_M = 0; - G_monero_vstate.disp_addr_m = 0; - UX_MENU_DISPLAY(value, ui_menu_pubaddr, ui_menu_pubaddr_preprocessor); -} - -/* --------------------------------- MAIN UX --------------------------------- */ - -const ux_menu_entry_t ui_menu_main[] = { - {NULL, ui_menu_pubaddr_display, 0, NULL, G_monero_vstate.ux_wallet_account_name, - G_monero_vstate.ux_wallet_public_short_address, 0, 0}, - {ui_menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0}, - {ui_menu_about, NULL, 0, NULL, "About", NULL, 0, 0}, - {NULL, (void*)os_sched_exit, 0, &C_icon_dashboard, "Quit app", NULL, 50, 29}, - UX_MENU_END}; - -void ui_menu_main_display(unsigned int value) { UX_MENU_DISPLAY(value, ui_menu_main, NULL); } - -void ui_init(void) { - ui_menu_main_display(0); - // setup the first screen changing - UX_CALLBACK_SET_INTERVAL(1000); -} - -void io_seproxyhal_display(const bagl_element_t* element) { - io_seproxyhal_display_default((bagl_element_t*)element); -} - -#endif diff --git a/src/monero_ux_nbgl.c b/src/monero_ux_nbgl.c new file mode 100644 index 00000000..de0dfd83 --- /dev/null +++ b/src/monero_ux_nbgl.c @@ -0,0 +1,431 @@ +/***************************************************************************** + * Ledger Monero App. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include "os.h" +#include "ux.h" +#include "cx.h" +#include "monero_types.h" +#include "monero_api.h" +#include "monero_vars.h" + +#include "os_io_seproxyhal.h" +#include "string.h" +#include "glyphs.h" + +#include "nbgl_use_case.h" + +#define QUIT_TOKEN 0 +#define CONTINUE_TOKEN 1 +/* ----------------------------------------------------------------------- */ +/* --- Stax UI layout --- */ +/* ----------------------------------------------------------------------- */ + +typedef struct { + nbgl_layoutTagValue_t tagValuePair[6]; + nbgl_layoutTagValueList_t tagValueList; + nbgl_pageInfoLongPress_t infoLongPress; + char timelockBuffer[23]; + char feeBuffer[23]; + char changeBuffer[23]; + nbgl_pageContent_t content; + nbgl_pageNavigationInfo_t nav; +} TransactionContext_t; + +static TransactionContext_t transactionContext; +static nbgl_page_t* pageContext; + +static void release_context(void) { + if (pageContext != NULL) { + nbgl_pageRelease(pageContext); + pageContext = NULL; + } +} + +/* -------------------------------- INFO UX --------------------------------- */ + +void ui_menu_show_tx_aborted(void) { + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_menu_main_display); +} + +void ui_menu_show_security_error(void) { + nbgl_useCaseStatus("Security Error", false, ui_menu_main_display); +} + +/* -------------------------------- OPEN TX UX --------------------------------- */ +static void ui_menu_validation_action_cancel(void) { + monero_abort_tx(); + monero_io_insert_u16(SW_DENY); + monero_io_do(IO_RETURN_AFTER_TX); +} + +static void ui_menu_validation_action_confirm(void) { + monero_io_discard(0); + monero_io_insert_u16(SW_OK); + monero_io_do(IO_RETURN_AFTER_TX); + nbgl_useCaseSpinner("Processing TX"); +} + +static void ui_menu_validation_action_cancel_prompt(void) { + nbgl_useCaseConfirm("Reject transaction?", "", "Yes, Reject", "Go back to transaction", + ui_menu_validation_action_cancel); +} + +static void ui_menu_validation_action(bool value) { + if (value) { + transactionContext.tagValueList.nbPairs = 0; + ui_menu_validation_action_confirm(); + } else { + ui_menu_validation_action_cancel_prompt(); + } +} + +unsigned int ui_menu_transaction_start(void) { + transactionContext.tagValueList.nbPairs = 0; + nbgl_useCaseReviewStart(&C_Monero_64px, "Review Transaction\nto send Monero", "", + "Reject transaction", ui_menu_validation_action_confirm, + ui_menu_validation_action_cancel_prompt); + return 0; +} + +unsigned int ui_menu_transaction_signed(void) { + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_menu_main_display); + return 0; +} + +void ui_menu_opentx_display(unsigned int value __attribute__((unused))) { + if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { + nbgl_useCaseSpinner("Processing TX"); + } else { + nbgl_useCaseSpinner("Preparing TX"); + } +} + +/* ----------------- FEE/CHANGE/TIMELOCK VALIDATION ----------------- */ + +void ui_menu_fee_validation_display(unsigned int value __attribute__((unused))) { + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + strncpy(transactionContext.feeBuffer, G_monero_vstate.ux_amount, + sizeof(transactionContext.feeBuffer)); + + transactionContext.tagValuePair[nbPairs].item = "Fee"; + transactionContext.tagValuePair[nbPairs].value = transactionContext.feeBuffer; + + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; + transactionContext.tagValueList.nbPairs++; + + ui_menu_validation_action_confirm(); +} + +void ui_menu_change_validation_display(unsigned int value __attribute__((unused))) { + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + strncpy(transactionContext.changeBuffer, G_monero_vstate.ux_amount, + sizeof(transactionContext.changeBuffer)); + + transactionContext.tagValuePair[nbPairs].item = "Change"; + transactionContext.tagValuePair[nbPairs].value = transactionContext.changeBuffer; + + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; + transactionContext.tagValueList.nbPairs++; + + ui_menu_validation_action_confirm(); +} + +void ui_menu_change_validation_display_last(unsigned int value __attribute__((unused))) { + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + strncpy(transactionContext.changeBuffer, G_monero_vstate.ux_amount, + sizeof(transactionContext.changeBuffer)); + + transactionContext.tagValuePair[nbPairs].item = "Change"; + transactionContext.tagValuePair[nbPairs].value = transactionContext.changeBuffer; + + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; + transactionContext.tagValueList.nbPairs++; + + transactionContext.infoLongPress.icon = &C_Monero_64px; + transactionContext.infoLongPress.longPressText = "Hold to sign"; + transactionContext.infoLongPress.longPressToken = 0; + transactionContext.infoLongPress.tuneId = TUNE_TAP_CASUAL; + transactionContext.infoLongPress.text = "Sign transaction"; + + nbgl_useCaseStaticReview(&transactionContext.tagValueList, &transactionContext.infoLongPress, + "Reject transaction", ui_menu_validation_action); +} + +static void timelock_callback(void) { + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + strncpy(transactionContext.timelockBuffer, G_monero_vstate.ux_amount, + sizeof(transactionContext.timelockBuffer)); + + transactionContext.tagValuePair[nbPairs].item = "Timelock"; + transactionContext.tagValuePair[nbPairs].value = transactionContext.timelockBuffer; + + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; + transactionContext.tagValueList.nbPairs++; + + ui_menu_validation_action_confirm(); +} + +void ui_menu_timelock_validation_display(unsigned int value __attribute__((unused))) { + transactionContext.tagValueList.nbPairs = 0; + + nbgl_useCaseReviewStart(&C_Monero_64px, "Review Transaction\nto send Monero", NULL, + "Reject transaction", timelock_callback, + ui_menu_validation_action_cancel_prompt); +} + +/* ----------------------------- USER DEST/AMOUNT VALIDATION ----------------------------- */ + +#ifdef TARGET_FLEX +#define NEXT_PAGE_TEXT ("Swipe to continue") +#else +#define NEXT_PAGE_TEXT ("Tap to continue") +#endif + +static void fill_amount_and_destination(void) { + transactionContext.tagValuePair[0].item = "Amount"; + transactionContext.tagValuePair[0].value = G_monero_vstate.ux_amount; + + transactionContext.tagValuePair[1].item = "Destination"; + transactionContext.tagValuePair[1].value = G_monero_vstate.ux_address; + + transactionContext.tagValueList.nbPairs = 2; + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; +} + +static void page_callback(int token, unsigned char index) { + (void)index; + release_context(); + + ui_menu_validation_action(token); +} + +static void continue_display(int token, unsigned char index) { + (void)index; + + if (token == QUIT_TOKEN) { + ui_menu_validation_action_cancel_prompt(); + } + + fill_amount_and_destination(); + + nbgl_pageNavigationInfo_t info = {.activePage = 0, + .nbPages = 0, + .navType = NAV_WITH_TAP, + .progressIndicator = true, + .navWithTap.backButton = false, + .navWithTap.nextPageText = NEXT_PAGE_TEXT, + .navWithTap.nextPageToken = CONTINUE_TOKEN, + .navWithTap.quitText = "Reject transaction", + .quitToken = QUIT_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + + nbgl_pageContent_t content = { + .type = TAG_VALUE_LIST, + .tagValueList.nbPairs = transactionContext.tagValueList.nbPairs, + .tagValueList.pairs = (nbgl_layoutTagValue_t*)transactionContext.tagValueList.pairs}; + + pageContext = nbgl_pageDrawGenericContent(page_callback, &info, &content); +} + +static void continue_display_last(int token, unsigned char index) { + (void)index; + + if (token == QUIT_TOKEN) { + ui_menu_validation_action_cancel_prompt(); + } + + fill_amount_and_destination(); + transactionContext.infoLongPress.icon = &C_Monero_64px; + transactionContext.infoLongPress.longPressText = "Hold to sign"; + transactionContext.infoLongPress.longPressToken = 0; + transactionContext.infoLongPress.tuneId = TUNE_TAP_CASUAL; + transactionContext.infoLongPress.text = "Sign transaction"; + + nbgl_useCaseStaticReview(&transactionContext.tagValueList, &transactionContext.infoLongPress, + "Reject transaction", ui_menu_validation_action); +} + +static void display_previous_infos(bool last) { + release_context(); + + nbgl_pageNavigationInfo_t info = {.activePage = 0, + .nbPages = 0, + .navType = NAV_WITH_TAP, + .progressIndicator = true, + .navWithTap.backButton = false, + .navWithTap.nextPageText = NEXT_PAGE_TEXT, + .navWithTap.nextPageToken = CONTINUE_TOKEN, + .navWithTap.quitText = "Reject transaction", + .quitToken = QUIT_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + + nbgl_pageContent_t content = { + .type = TAG_VALUE_LIST, + .tagValueList.nbPairs = transactionContext.tagValueList.nbPairs, + .tagValueList.pairs = (nbgl_layoutTagValue_t*)transactionContext.tagValueList.pairs}; + + if (last) { + pageContext = nbgl_pageDrawGenericContent(continue_display_last, &info, &content); + } else { + pageContext = nbgl_pageDrawGenericContent(continue_display, &info, &content); + } +} + +void ui_menu_validation_display(unsigned int value __attribute__((unused))) { + const bool last = 0; + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + if (nbPairs > 0) { + display_previous_infos(last); + } else { + continue_display(CONTINUE_TOKEN, 0); + } +} + +void ui_menu_validation_display_last(unsigned int value __attribute__((unused))) { + const bool last = 1; + uint8_t nbPairs = transactionContext.tagValueList.nbPairs; + + if (nbPairs > 0) { + display_previous_infos(last); + } else { + continue_display_last(CONTINUE_TOKEN, 0); + } +} +/* ---------------------------- PUBLIC ADDRESS UX ---------------------------- */ +#define ADDR_MAJOR G_monero_vstate.ux_address + 124 +#define ADDR_MINOR G_monero_vstate.ux_address + 140 +#define ADDR_ID G_monero_vstate.ux_address + 140 + +static void ui_menu_pubaddr_action_cancelled(void) { + if (G_monero_vstate.disp_addr_mode) { + monero_io_insert_u16(SW_OK); + monero_io_do(IO_RETURN_AFTER_TX); + } + G_monero_vstate.disp_addr_mode = 0; + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, ui_menu_main_display); +} + +void ui_menu_pubaddr_action(bool confirm) { + if (confirm) { + if (G_monero_vstate.disp_addr_mode) { + monero_io_insert_u16(SW_OK); + monero_io_do(IO_RETURN_AFTER_TX); + } + G_monero_vstate.disp_addr_mode = 0; + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_menu_main_display); + } else { + ui_menu_pubaddr_action_cancelled(); + } +} + +void display_account(void) { + switch (G_monero_vstate.disp_addr_mode) { + case 0: + case DISP_MAIN: + transactionContext.tagValuePair[0].item = "Type"; + transactionContext.tagValuePair[0].value = "Main address"; + + transactionContext.tagValueList.nbPairs = 1; + break; + + case DISP_SUB: + transactionContext.tagValuePair[0].item = "Type"; + transactionContext.tagValuePair[0].value = "Sub address"; + + snprintf(ADDR_MAJOR, 16, "%d", G_monero_vstate.disp_addr_M); + snprintf(ADDR_MINOR, 16, "%d", G_monero_vstate.disp_addr_m); + + transactionContext.tagValuePair[1].item = "Major"; + transactionContext.tagValuePair[1].value = ADDR_MAJOR; + + transactionContext.tagValuePair[2].item = "Minor"; + transactionContext.tagValuePair[2].value = ADDR_MINOR; + + transactionContext.tagValueList.nbPairs = 3; + break; + + case DISP_INTEGRATED: + transactionContext.tagValuePair[0].item = "Type"; + transactionContext.tagValuePair[0].value = "Integrated address"; + + memcpy(ADDR_ID, G_monero_vstate.payment_id, 16); + + transactionContext.tagValuePair[1].item = "Payment ID"; + transactionContext.tagValuePair[1].value = ADDR_ID; + + transactionContext.tagValueList.nbPairs = 2; + break; + } + + transactionContext.tagValueList.pairs = transactionContext.tagValuePair; + + nbgl_useCaseAddressReview(G_monero_vstate.ux_address, &transactionContext.tagValueList, + &C_Monero_64px, "Verify Monero\naddress", NULL, + ui_menu_pubaddr_action); +} + +int ui_menu_any_pubaddr_display(unsigned int value __attribute__((unused)), unsigned char* pub_view, + unsigned char* pub_spend, unsigned char is_subbadress, + unsigned char* paymanetID) { + int error; + explicit_bzero(G_monero_vstate.ux_address, sizeof(G_monero_vstate.ux_address)); + + error = monero_base58_public_key(G_monero_vstate.ux_address, pub_view, pub_spend, is_subbadress, + paymanetID); + if (error) { + return error; + } + + display_account(); + + return 0; +} + +/* -------------------------------- EXPORT VIEW KEY UX --------------------------------- */ + +static void ui_menu_export_viewkey_action(bool value) { + unsigned int sw; + unsigned char x[KEY_SIZE]; + + monero_io_discard(0); + explicit_bzero(x, sizeof(x)); + sw = SW_OK; + + if (value) { + monero_io_insert(G_monero_vstate.a, KEY_SIZE); + G_monero_vstate.export_view_key = EXPORT_VIEW_KEY; + } else { + monero_io_insert(x, KEY_SIZE); + G_monero_vstate.export_view_key = 0; + } + monero_io_insert_u16(sw); + monero_io_do(IO_RETURN_AFTER_TX); + nbgl_useCaseStatus("VIEW KEY\nEXPORTED", true, ui_menu_main_display); +} + +void ui_export_viewkey_display(unsigned int value __attribute__((unused))) { + nbgl_useCaseChoice(&C_Monero_64px, "Export\nview key", "", "Accept", "Reject if not sure", + ui_menu_export_viewkey_action); +} + +#endif diff --git a/src/monero_ux_nbgl_menu.c b/src/monero_ux_nbgl_menu.c new file mode 100644 index 00000000..758d43dd --- /dev/null +++ b/src/monero_ux_nbgl_menu.c @@ -0,0 +1,279 @@ +/***************************************************************************** + * Ledger Monero App. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include "os.h" +#include "ux.h" +#include "cx.h" +#include "monero_types.h" +#include "monero_api.h" +#include "monero_vars.h" + +#include "os_io_seproxyhal.h" +#include "string.h" +#include "glyphs.h" + +#include "nbgl_use_case.h" + +#define STR(x) #x +#define XSTR(x) STR(x) + +#define PAGE_START 0 +#define NB_PAGE_SETTING 3 +#define IS_TOUCHABLE true + +void __attribute__((noreturn)) app_exit(void); + +/* ----------------------------------------------------------------------- */ +/* --- Stax UI layout --- */ +/* ----------------------------------------------------------------------- */ + +typedef struct { + char buffer[20]; + nbgl_pageContent_t content; + nbgl_pageNavigationInfo_t nav; +} TransactionContext_t; + +static TransactionContext_t transactionContext; + +enum { + ACCOUNT_TOKEN = FIRST_USER_TOKEN, + NETWORK_TOKEN, + RESET_TOKEN, + ACCOUNT_CHOICE, + ACCOUNT_CHOICE_2, + ACCOUNT_CHOICE_3, + NETWORK_CHOICE +}; + +enum { MAIN_NET, STAGE_NET, TEST_NET, MAX_NET }; + +#define SETTING_INFO_NB 4 +static const char* const infoTypes[] = {"Spec", "Version", "Developer", "Copyright"}; +static const char* const infoContents[] = {XSTR(SPEC_VERSION), APPVERSION, "Ledger", + "(c) 2024 Ledger"}; + +static const char* const barTexts[] = {"Select Account", "Select Network", "Reset"}; +static const uint8_t tokens[] = {ACCOUNT_TOKEN, NETWORK_TOKEN, RESET_TOKEN}; + +static const char* const accountNames[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; +#ifdef MONERO_ALPHA +static const char* const networkNames[] = {"Unavailable", "Stage network", "Test network"}; +#else +static const char* const networkNames[] = {"Main network", "Stage network", "Test network"}; +#endif + +static void display_settings_menu(void); + +static void update_account(void) { + G_monero_vstate.disp_addr_mode = 0; + G_monero_vstate.disp_addr_M = 0; + G_monero_vstate.disp_addr_m = 0; +} + +static bool account_settings_navigation_cb(uint8_t page, nbgl_pageContent_t* content) { + content->type = CHOICES_LIST; + content->choicesList.nbChoices = 4; + content->choicesList.localized = false; + content->choicesList.tuneId = TUNE_TAP_CASUAL; + + if (page == 0) { + content->choicesList.initChoice = N_monero_pstate->account_id; + content->choicesList.names = accountNames; + content->choicesList.token = ACCOUNT_CHOICE; + } else if (page == 1) { + if (N_monero_pstate->account_id > 3) { + content->choicesList.initChoice = N_monero_pstate->account_id - 4; + } else { + content->choicesList.initChoice = 4; + } + content->choicesList.names = accountNames + 4; + content->choicesList.token = ACCOUNT_CHOICE_2; + } else if (page == 2) { + content->choicesList.nbChoices = 2; + if (N_monero_pstate->account_id > 7) { + content->choicesList.initChoice = N_monero_pstate->account_id - 8; + } else { + content->choicesList.initChoice = 8; + } + content->choicesList.names = accountNames + 8; + content->choicesList.token = ACCOUNT_CHOICE_3; + } else { + return false; + } + return true; +} + +static bool network_settings_navigation_cb(uint8_t page, nbgl_pageContent_t* content) { + if (page == 0) { + content->type = CHOICES_LIST; + content->choicesList.nbChoices = 3; + content->choicesList.localized = false; + content->choicesList.tuneId = TUNE_TAP_CASUAL; + + if (N_monero_pstate->network_id == MAINNET) { + content->choicesList.initChoice = MAIN_NET; + } else if (N_monero_pstate->network_id == TESTNET) { + content->choicesList.initChoice = TEST_NET; + } else if (N_monero_pstate->network_id == STAGENET) { + content->choicesList.initChoice = STAGE_NET; + } + content->choicesList.names = networkNames; + content->choicesList.token = NETWORK_CHOICE; + } else { + return false; + } + return true; +} + +static void resetCallback(void) { + unsigned char magic[4]; + magic[0] = 0; + magic[1] = 0; + magic[2] = 0; + magic[3] = 0; + monero_nvm_write((void*)N_monero_pstate->magic, magic, 4); + monero_init(); + nbgl_useCaseStatus("ACCOUNT INFOS\nRESETTED", true, ui_menu_main_display); +} + +static void account_settings_control_cb(int token, uint8_t index) { + switch (token) { + case ACCOUNT_CHOICE_3: + case ACCOUNT_CHOICE_2: + case ACCOUNT_CHOICE: + index = (token - ACCOUNT_CHOICE) * 4 + index; + if (index <= 9) { + monero_nvm_write((void*)&N_monero_pstate->account_id, &index, sizeof(uint8_t)); + monero_init(); + } + display_settings_menu(); + break; + default: + PRINTF("Should not happen !"); + break; + } +} + +static void network_settings_control_cb(int token, uint8_t index) { + UNUSED(index); + switch (token) { + case NETWORK_CHOICE: +#ifdef MONERO_ALPHA + if (index == 0) { + display_settings_menu(); + } else +#endif + if (index < MAX_NET) { + if (index == MAIN_NET) { + monero_install(MAINNET); + } else if (index == TEST_NET) { + monero_install(TESTNET); + } else if (index == STAGE_NET) { + monero_install(STAGENET); + } + monero_init(); + } + display_settings_menu(); + break; + default: + PRINTF("Should not happen !"); + break; + } +} + +static void settings_control_cb(int token, uint8_t index, int page) { + UNUSED(index); + UNUSED(page); + switch (token) { + case ACCOUNT_TOKEN: + nbgl_useCaseSettings("Select account", 0, 3, IS_TOUCHABLE, display_settings_menu, + account_settings_navigation_cb, account_settings_control_cb); + break; + case NETWORK_TOKEN: + nbgl_useCaseSettings("Select network", 0, 2, IS_TOUCHABLE, display_settings_menu, + network_settings_navigation_cb, network_settings_control_cb); + break; + case RESET_TOKEN: + nbgl_useCaseConfirm("Reset account\ninformations ?", "", "Yes, Reset", "Go back", + resetCallback); + break; + + default: + PRINTF("Should not happen !"); + break; + } +} + +// info menu definition +static const nbgl_contentInfoList_t infoList = { + .nbInfos = SETTING_INFO_NB, + .infoTypes = infoTypes, + .infoContents = infoContents, +}; + +// settings menu definition +#define SETTING_CONTENTS_NB 1 +static const nbgl_content_t contents[SETTING_CONTENTS_NB] = { + {.type = BARS_LIST, + .content.barsList.barTexts = barTexts, + .content.barsList.tokens = tokens, + .content.barsList.nbBars = 3, + .content.barsList.tuneId = TUNE_TAP_CASUAL, + .contentActionCallback = settings_control_cb}}; + +static const nbgl_genericContents_t settingContents = {.callbackCallNeeded = false, + .contentsList = contents, + .nbContents = SETTING_CONTENTS_NB}; +static nbgl_homeAction_t homeAction; + +static void display_home_and_settings(bool displayHome) { + update_account(); + + explicit_bzero(G_monero_vstate.ux_address, sizeof(G_monero_vstate.ux_address)); + + snprintf(transactionContext.buffer, sizeof(transactionContext.buffer), "Show %s", + G_monero_vstate.ux_wallet_account_name); + + monero_base58_public_key(G_monero_vstate.ux_address, G_monero_vstate.A, G_monero_vstate.B, 0, + NULL); + + homeAction.callback = display_account; + homeAction.icon = NULL; + homeAction.text = transactionContext.buffer; + + nbgl_useCaseHomeAndSettings(APPNAME, &C_Monero_64px, NULL, displayHome ? INIT_HOME_PAGE : 0, + &settingContents, &infoList, &homeAction, app_exit); +} + +static void display_settings_menu(void) { + // display settings page + display_home_and_settings(false); +} + +void ui_menu_main_display(void) { + // display home page + display_home_and_settings(true); +} + +/* --- INIT --- */ + +void ui_init(void) { + ui_menu_main_display(); +} + +#endif diff --git a/src/monero_vars.h b/src/monero_vars.h index 6d047f0b..161475a9 100644 --- a/src/monero_vars.h +++ b/src/monero_vars.h @@ -21,18 +21,19 @@ #include "os.h" #include "cx.h" +#include "ux.h" #include "os_io_seproxyhal.h" #include "monero_types.h" #include "monero_api.h" extern monero_v_state_t G_monero_vstate; -#ifdef TARGET_NANOX -extern const monero_nv_state_t N_state_pic; -#define N_monero_pstate ((volatile monero_nv_state_t *)PIC(&N_state_pic)) -#else +#if defined(TARGET_NANOS) extern monero_nv_state_t N_state_pic; #define N_monero_pstate ((WIDE monero_nv_state_t *)PIC(&N_state_pic)) +#else +extern const monero_nv_state_t N_state_pic; +#define N_monero_pstate ((volatile monero_nv_state_t *)PIC(&N_state_pic)) #endif #ifdef MONERO_DEBUG_MAIN diff --git a/tests/conftest.py b/tests/conftest.py index 975d8c54..04588632 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,30 +1,30 @@ from typing import Dict, Tuple import pytest -from monero_client.io.button import Button, FakeButton from monero_client.monero_cmd import MoneroCmd -SPECULOS: bool = True +from ragger.conftest import configuration +########################### +### CONFIGURATION START ### +########################### +MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" -@pytest.fixture(scope="module") -def monero(): - monero_client = MoneroCmd(debug=True, - speculos=SPECULOS) +configuration.OPTIONAL.CUSTOM_SEED = MNEMONIC - yield monero_client - - monero_client.device.close() +######################### +### CONFIGURATION END ### +######################### +# Pull all features from the base ragger conftest using the overridden configuration +pytest_plugins = ("ragger.conftest.base_conftest", ) -@pytest.fixture(scope="module") -def button(): - button_client = (Button(server="127.0.0.1", port=42000) - if SPECULOS else FakeButton()) - yield button_client +@pytest.fixture() +def monero(backend, debug=False): + monero_client = MoneroCmd(debug, backend) - button_client.close() + yield monero_client _test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {} diff --git a/tests/monero_client/io/button.py b/tests/monero_client/io/button.py deleted file mode 100644 index 954150f6..00000000 --- a/tests/monero_client/io/button.py +++ /dev/null @@ -1,33 +0,0 @@ -import socket - - -class FakeButton: - def right_click(self): - pass - - def left_click(self): - pass - - def both_click(self): - pass - - def close(self): - pass - - -class Button: - def __init__(self, server: str, port: int) -> None: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.connect((server, port)) - - def right_click(self): - self.socket.sendall(b"Rr") - - def left_click(self): - self.socket.sendall(b"Ll") - - def both_click(self): - self.socket.sendall(b"LRlr") - - def close(self): - self.socket.close() diff --git a/tests/monero_client/io/comm.py b/tests/monero_client/io/comm.py deleted file mode 100644 index 6d4d11fd..00000000 --- a/tests/monero_client/io/comm.py +++ /dev/null @@ -1,20 +0,0 @@ -from abc import ABCMeta, abstractmethod -from typing import Tuple - - -class Comm(metaclass=ABCMeta): - @abstractmethod - def send(self, apdus: bytes) -> None: - raise NotImplementedError - - @abstractmethod - def recv(self) -> Tuple[int, bytes]: - raise NotImplementedError - - @abstractmethod - def exchange(self, apdus: bytes) -> Tuple[int, bytes]: - raise NotImplementedError - - @abstractmethod - def close(self) -> None: - raise NotImplementedError diff --git a/tests/monero_client/io/display.py b/tests/monero_client/io/display.py deleted file mode 100644 index 1d69188e..00000000 --- a/tests/monero_client/io/display.py +++ /dev/null @@ -1,72 +0,0 @@ -import json -import queue -import socket -from typing import Optional - - -class FakeDisplay: - def listen(self): - pass - - def close(self): - pass - - -class Display: - def __init__(self, server: str, port: int) -> None: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.connect((server, port)) - self.q: queue.Queue = queue.Queue() - - def listen(self): - while True: - text: Optional[bytes] = json.loads( - self.socket.recv(1000) - ).get("text") - - if text: - self.q.put(text) - - def close(self): - self.socket.close() - - -# class DisplayClientProtocol(asyncio.Protocol): -# def __init__(self, q, on_con_lost): -# self.on_con_lost = on_con_lost -# self.q = q -# self.transport = None -# -# def connection_made(self, transport): -# self.transport = transport -# -# def data_received(self, data): -# text: Optional[bytes] = json.loads(data).get("text") -# if text: -# logging.debug("+ %s", text) -# self.q.put(text) -# -# def connection_lost(self, exc): -# print('The server closed the connection') -# self.on_con_lost.set_result(True) -# -# -# async def main(): -# loop = asyncio.get_running_loop() -# -# on_con_lost = loop.create_future() -# q = queue.Queue() -# -# transport, protocol = await loop.create_connection( -# lambda: DisplayClientProtocol(q, on_con_lost), -# "127.0.0.1", -# 43000 -# ) -# -# try: -# await on_con_lost -# finally: -# transport.close() -# -# -# asyncio.run(main()) diff --git a/tests/monero_client/io/hid_device.py b/tests/monero_client/io/hid_device.py deleted file mode 100644 index a52508e5..00000000 --- a/tests/monero_client/io/hid_device.py +++ /dev/null @@ -1,87 +0,0 @@ -import logging -from typing import List, Tuple - -try: - import hid -except ImportError: - hid = None - -from monero_client.io.comm import Comm - -LEDGER_VENDOR_ID: int = 0x2C97 - - -class HID(Comm): - def __init__(self) -> None: - if hid is None: - raise ImportError("hidapi is not installed!") - - self.device = hid.device() - self.path = HID.enumerate_devices()[0] - self.device.open_path(self.path) - self.device.set_nonblocking(True) - self.__opened: bool = True - - @staticmethod - def enumerate_devices() -> List[bytes]: - devices: List[bytes] = [] - - for hid_device in hid.enumerate(LEDGER_VENDOR_ID, 0): - if (hid_device.get("interface_number") == 0 or - hid_device.get("usage_page") == 0xffa0): - devices.append(hid_device["path"]) - - assert len(devices) != 0, f"Can't find device with vendor_id {LEDGER_VENDOR_ID}" - - return devices - - def send(self, apdus: bytes) -> None: - data: bytes = int.to_bytes(len(apdus), 2, byteorder="big") + apdus - offset: int = 0 - seq_idx: int = 0 - - while offset < len(data): - # Header: channel (0x101), tag (0x05), sequence index - header: bytes = (b"\x01\x01\x05" + - seq_idx.to_bytes(2, byteorder="big")) - data_chunk: bytes = (header + - data[offset:offset + 64 - len(header)]) - - self.device.write(b"\x00" + data_chunk) - logging.debug("=> %s", data_chunk.hex()) - offset += 64 - len(header) - seq_idx += 1 - - def recv(self) -> Tuple[int, bytes]: - seq_idx: int = 0 - self.device.set_nonblocking(False) - data_chunk: bytes = bytes(self.device.read(64 + 1)) - self.device.set_nonblocking(True) - - assert data_chunk[:2] == b"\x01\x01" - assert data_chunk[2] == 5 - assert data_chunk[3:5] == seq_idx.to_bytes(2, byteorder="big") - - data_len: int = int.from_bytes(data_chunk[5:7], byteorder="big") - data: bytes = data_chunk[7:] - - while len(data) < data_len: - read_bytes = bytes(self.device.read(64 + 1, timeout_ms=1000)) - data += read_bytes[5:] - - logging.debug("<= %s", data[:data_len].hex()) - - sw: int = int.from_bytes(data[data_len - 2:data_len], byteorder="big") - data = data[:data_len - 2] - - return sw, data - - def exchange(self, apdus: bytes) -> Tuple[int, bytes]: - self.send(apdus) - - return self.recv() - - def close(self): - if self.__opened: - self.device.close() - self.__opened = False diff --git a/tests/monero_client/io/tcp_client.py b/tests/monero_client/io/tcp_client.py deleted file mode 100644 index a25341cf..00000000 --- a/tests/monero_client/io/tcp_client.py +++ /dev/null @@ -1,39 +0,0 @@ -import logging -import socket -from typing import Tuple - -from monero_client.io.comm import Comm - - -class TCPClient(Comm): - def __init__(self, server: str, port: int) -> None: - self.server: str = server - self.port: int = port - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.connect((self.server, self.port)) - self.__opened: bool = True - - def send(self, apdus: bytes) -> None: - logging.debug("=> %s", apdus.hex()) - length: bytes = int.to_bytes(len(apdus), 4, byteorder="big") - self.socket.send(length + apdus) - - def recv(self) -> Tuple[int, bytes]: - length: int = int.from_bytes(self.socket.recv(4), byteorder="big") - data: bytes = self.socket.recv(length) - sw: int = int.from_bytes(self.socket.recv(2), byteorder="big") - - logging.debug("<= %s %s", data.hex(), hex(sw)[2:]) - - return sw, data - - def exchange(self, apdus: bytes) -> Tuple[int, bytes]: - self.send(apdus) - sw, response = self.recv() # type: int, bytes - - return sw, response - - def close(self): - if self.__opened: - self.socket.close() - self.__opened = False diff --git a/tests/monero_client/io/transport.py b/tests/monero_client/io/transport.py index 855b7578..147badd1 100644 --- a/tests/monero_client/io/transport.py +++ b/tests/monero_client/io/transport.py @@ -2,18 +2,18 @@ import logging import struct from typing import Union, Tuple - -from monero_client.io.tcp_client import TCPClient -from monero_client.io.hid_device import HID +from ragger.backend.interface import BackendInterface, RAPDU +from contextlib import contextmanager class Transport: - def __init__(self, debug: bool = False, speculos: bool = False) -> None: + backend: BackendInterface + + def __init__(self, backend, debug) -> None: if debug: logging.basicConfig(format="%(message)s", level=logging.DEBUG) - self.com: Union[TCPClient, HID] = (TCPClient(server="127.0.0.1", port=9999) - if speculos else HID()) + self._client = backend def exchange(self, cla: int, @@ -30,7 +30,7 @@ def exchange(self, 1 + len(payload), option) - return self.com.exchange(header + payload) + return self._client.exchange(header + payload) def send(self, cla: int, @@ -46,10 +46,30 @@ def send(self, p2, 1 + len(payload), option) - self.com.send(header + payload) + self._client.send_raw(header + payload) + + @contextmanager + def send_async(self, + cla: int, + ins: enum.IntEnum, + p1: int = 0, + p2: int = 0, + option: int = 0, + payload: bytes = b"") -> None: + header: bytes = struct.pack("BBBBBB", + cla, + ins.value, + p1, + p2, + 1 + len(payload), + option) + with self._client.exchange_async_raw(header + payload): + yield def recv(self) -> Tuple[int, bytes]: - return self.com.recv() + response = self._client.receive() + return (response.status, response.data) - def close(self) -> None: - self.com.close() + def async_response(self) -> Tuple[int, bytes]: + response = self._client.last_async_response + return (response.status, response.data) diff --git a/tests/monero_client/monero_cmd.py b/tests/monero_client/monero_cmd.py index 41230f67..0b08c1f6 100644 --- a/tests/monero_client/monero_cmd.py +++ b/tests/monero_client/monero_cmd.py @@ -24,15 +24,19 @@ from .monero_types import InsType, Type, SigType from .crypto.hmac import hmac_sha256 from .exception.device_error import DeviceError -from .io.button import Button from .utils.varint import encode_varint +from .utils.utils import get_nano_review_instructions +from pathlib import Path +from ragger.navigator import NavInsID, NavIns PROTOCOL_VERSION: int = 3 +TESTS_ROOT_DIR = Path(__file__).parent.parent class MoneroCmd(MoneroCryptoCmd): - def __init__(self, debug: bool = False, speculos: bool = False) -> None: - MoneroCryptoCmd.__init__(self, debug, speculos) + def __init__(self, debug, backend) -> None: + self.backend = backend + MoneroCryptoCmd.__init__(self, backend, debug) def reset_and_get_version(self, monero_client_version: bytes @@ -76,6 +80,8 @@ def set_signature_mode(self, sig_type: SigType) -> int: sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -103,6 +109,9 @@ def open_tx(self) -> Tuple[bytes, bytes, bytes, bytes]: sw, response = self.device.recv() # type: int, bytes + # Wait for internal backend screen to be up to date before continuing + self.backend.wait_for_screen_change() + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -144,6 +153,9 @@ def close_tx(self) -> None: sw, response = self.device.recv() # type: int, bytes + # Wait for internal backend screen to be up to date before continuing + self.backend.wait_for_screen_change() + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -174,6 +186,7 @@ def gen_txout_keys(self, b"\x01" if is_change_addr else b"\x00", b"\x01" if is_subaddress else b"\x00", b"\x00" * 33, # additional_txkeys + b"\x00" * 33, # use_view_tags )) self.device.send(cla=PROTOCOL_VERSION, @@ -185,6 +198,8 @@ def gen_txout_keys(self, sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -200,7 +215,7 @@ def gen_txout_keys(self, return _ak_amount, out_ephemeral_pub_key - def prefix_hash_init(self, button: Button, version: int, timelock: int) -> None: + def prefix_hash_init(self, backend, test_name, firmware, navigator, version: int, timelock: int) -> None: ins: InsType = InsType.INS_PREFIX_HASH payload: bytes = b"".join([ @@ -208,21 +223,27 @@ def prefix_hash_init(self, button: Button, version: int, timelock: int) -> None: encode_varint(timelock) ]) - self.device.send(cla=PROTOCOL_VERSION, - ins=ins, - p1=1, - p2=0, - option=0, - payload=payload) - - # "Timelock" -> go down - button.right_click() - # "Reject Timelock" -> go down - button.right_click() - # "Accept Timelock" -> accept - button.both_click() - - sw, response = self.device.recv() # type: int, bytes + if firmware.device == "nanos": + instructions = get_nano_review_instructions(1) + elif firmware.device.startswith("nano"): + instructions = get_nano_review_instructions(1) + else: + instructions = [ + NavIns(NavInsID.SWIPE_CENTER_TO_LEFT) + ] + with self.device.send_async(cla=PROTOCOL_VERSION, + ins=ins, + p1=1, + p2=0, + option=0, + payload=payload): + navigator.navigate_and_compare(TESTS_ROOT_DIR, + test_name + "_hash_init", + instructions) + + sw, response = self.device.async_response() # type: int, bytes + + # Screen change already waited in navigate_and_compare() above if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins, message="P1=1 (init)") @@ -241,6 +262,8 @@ def prefix_hash_update(self, index: int, payload: bytes, is_last: bool) -> bytes sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins, message="P1=2 (update)") @@ -269,6 +292,8 @@ def gen_commitment_mask(self, _ak_amount: bytes) -> bytes: sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -301,6 +326,8 @@ def blind(self, sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -335,6 +362,8 @@ def unblind(self, sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins) @@ -345,7 +374,10 @@ def unblind(self, return mask, amount def validate_prehash_init(self, - button: Button, + backend, + test_name, + firmware, + navigator, index: int, txntype: int, txnfee: int) -> None: @@ -354,21 +386,32 @@ def validate_prehash_init(self, # txntype is skipped in the app payload: bytes = struct.pack("B", txntype) + encode_varint(txnfee) - self.device.send(cla=PROTOCOL_VERSION, - ins=ins, - p1=1, - p2=index, - option=0, - payload=payload) + if firmware.device == "nanos": + instructions = get_nano_review_instructions(1) + elif firmware.device.startswith("nano"): + instructions = get_nano_review_instructions(1) + else: + instructions = [ + NavIns(NavInsID.USE_CASE_REVIEW_TAP) + ] - # "Fee" -> go down - button.right_click() - # "Reject Fee" -> go down - button.right_click() - # "Accept Fee" -> accept - button.both_click() + with self.device.send_async(cla=PROTOCOL_VERSION, + ins=ins, + p1=1, + p2=index, + option=0, + payload=payload): - sw, response = self.device.recv() # type: int, bytes + if firmware.device.startswith("nano"): + navigator.navigate_and_compare(TESTS_ROOT_DIR, + test_name + "_prehash_init", + instructions) + else: + pass + + sw, response = self.device.async_response() # type: int, bytes + + # Screen change already waited in navigate_and_compare() above if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins, message="P1=1 (init)") @@ -376,6 +419,10 @@ def validate_prehash_init(self, assert len(response) == 0 def validate_prehash_update(self, + backend, + test_name, + firmware, + navigator, index: int, is_short: bool, is_change_addr: bool, @@ -403,14 +450,35 @@ def validate_prehash_update(self, blinded_amount )) - self.device.send(cla=PROTOCOL_VERSION, - ins=ins, - p1=2, - p2=index, - option=(0 if is_last else 0x80) | (0x02 if is_short else 0), - payload=payload) + if firmware.device == "nanos": + instructions = get_nano_review_instructions(7) + elif firmware.device.startswith("nano"): + instructions = get_nano_review_instructions(3) + else: + instructions = [ - sw, response = self.device.recv() # type: int, bytes + NavIns(NavInsID.SWIPE_CENTER_TO_LEFT), + NavIns(NavInsID.USE_CASE_REVIEW_TAP), + NavIns(NavInsID.USE_CASE_REVIEW_CONFIRM) + ] + + backend.wait_for_text_on_screen("Processing") + with self.device.send_async(cla=PROTOCOL_VERSION, + ins=ins, + p1=2, + p2=index, + option=(0 if is_last else 0x80) | ( + 0x02 if is_short else 0), + payload=payload): + + navigator.navigate_and_compare(TESTS_ROOT_DIR, + test_name + "_prehash_update", + instructions, + screen_change_after_last_instruction=False, timeout=10000) + + sw, response = self.device.async_response() # type: int, bytes + + # Screen change already waited in navigate_and_compare() above if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins, message="P1=2 (update)") @@ -454,7 +522,9 @@ def validate_prehash_finalize(self, sw, response = self.device.recv() # type: int, bytes + # No screen change expected + if not sw & 0x9000: raise DeviceError(error_code=sw, ins=ins, message="P1=3 (finalize)") - assert len(response) == 0 + assert len(response) == 32 diff --git a/tests/monero_client/monero_crypto_cmd.py b/tests/monero_client/monero_crypto_cmd.py index 33cd5126..2066a7ba 100644 --- a/tests/monero_client/monero_crypto_cmd.py +++ b/tests/monero_client/monero_crypto_cmd.py @@ -6,15 +6,19 @@ from .monero_types import InsType from .monero_types import Type from .io.transport import Transport +from pathlib import Path +from ragger.navigator import NavInsID, NavIns +from .utils.utils import get_nano_review_instructions PROTOCOL_VERSION: int = 3 +TESTS_ROOT_DIR = Path(__file__).parent.parent class MoneroCryptoCmd: HMAC_KEY: bytes = b"\xab" * 32 - def __init__(self, debug: bool = False, speculos: bool = False) -> None: - self.device = Transport(debug=debug, speculos=speculos) + def __init__(self, backend, debug: bool = False) -> None: + self.device = Transport(backend, debug=debug) self.is_in_tx_mode = False @staticmethod @@ -102,19 +106,68 @@ def get_public_keys(self) -> Tuple[bytes, bytes, str]: return view_pub_key, spend_pub_key, base58_address - def get_private_view_key(self, button) -> bytes: + def display_address(self, test_name, firmware, navigator, derivation: bytes, output_index: bytes) -> bytes: + ins: InsType = InsType.INS_DISPLAY_ADDRESS + + payload: bytes = b"".join([ + derivation, + output_index, + ]) + + if firmware.device == "nanos": + instructions = get_nano_review_instructions(8) + elif firmware.device.startswith("nano"): + instructions = get_nano_review_instructions(4) + else: + instructions = [ + NavIns(NavInsID.SWIPE_CENTER_TO_LEFT), + NavIns(NavInsID.TOUCH, (200, 350 if firmware.device.startswith("flex") else 410)), + NavIns(NavInsID.USE_CASE_ADDRESS_CONFIRMATION_EXIT_QR), + NavIns(NavInsID.SWIPE_CENTER_TO_LEFT), + NavIns(NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM), + NavIns(NavInsID.USE_CASE_STATUS_DISMISS) + ] + with self.device.send_async(cla=PROTOCOL_VERSION, + ins=ins, + p1=0, + p2=0, + option=0, + payload=payload): + + navigator.navigate_and_compare(TESTS_ROOT_DIR, + test_name, + instructions) + + sw, response = self.device.async_response() # type: int, bytes + + if not sw & 0x9000: + raise DeviceError(sw, ins, "P1=2") + + return response # priv_view_key + + def get_private_view_key(self, test_name, firmware, navigator) -> bytes: ins: InsType = InsType.INS_GET_KEY - self.device.send(cla=PROTOCOL_VERSION, - ins=ins, - p1=2, - p2=0, - option=0) + if firmware.device == "nanos": + instructions = get_nano_review_instructions(1) + elif firmware.device.startswith("nano"): + instructions = get_nano_review_instructions(1) + else: + instructions = [ + NavIns(NavInsID.USE_CASE_CHOICE_CONFIRM) + ] - # "Export View Key" - button.right_click() + with self.device.send_async(cla=PROTOCOL_VERSION, + ins=ins, + p1=2, + p2=0, + option=0): - sw, response = self.device.recv() # type: int, bytes + navigator.navigate_and_compare(TESTS_ROOT_DIR, + test_name, + instructions) + + sw, response = self.device.async_response() # type: int, bytes if not sw & 0x9000: raise DeviceError(sw, ins, "P1=2") @@ -259,3 +312,27 @@ def gen_key_derivation(self, pub_key: bytes, _priv_key: bytes) -> bytes: assert len(response) == 32 return _d_in + + def derive_view_tag(self, derivation: bytes, output_index: bytes) -> int: + ins: InsType = InsType.INS_DERIVE_VIEW_TAG + + payload: bytes = b"".join([ + derivation, + output_index, + ]) + + self.device.send(cla=PROTOCOL_VERSION, + ins=ins, + p1=0, + p2=0, + option=0, + payload=payload) + + sw, response = self.device.recv() # type: int, bytes + + if not sw & 0x9000: + raise DeviceError(sw, ins) + + assert len(response) == 1 + + return int.from_bytes(response, "big") diff --git a/tests/monero_client/monero_types.py b/tests/monero_client/monero_types.py index 9a85be28..8dcd2587 100644 --- a/tests/monero_client/monero_types.py +++ b/tests/monero_client/monero_types.py @@ -45,6 +45,7 @@ class InsType(enum.IntEnum): INS_DERIVE_PUBLIC_KEY = 0x36 INS_DERIVE_SECRET_KEY = 0x38 INS_GEN_KEY_IMAGE = 0x3A + INS_DERIVE_VIEW_TAG = 0x3B INS_SECRET_KEY_ADD = 0x3C INS_GENERATE_KEYPAIR = 0x40 INS_SECRET_SCAL_MUL_KEY = 0x42 @@ -66,6 +67,7 @@ class InsType(enum.IntEnum): INS_PREFIX_HASH = 0x7D INS_VALIDATE = 0x7C INS_MLSAG = 0x7E + INS_CLSAG = 0x7F INS_CLOSE_TX = 0x80 INS_GET_TX_PROOF = 0xA0 diff --git a/tests/monero_client/utils/utils.py b/tests/monero_client/utils/utils.py new file mode 100644 index 00000000..5c6dc8b9 --- /dev/null +++ b/tests/monero_client/utils/utils.py @@ -0,0 +1,50 @@ +from bip32 import HARDENED_INDEX + +from ragger.backend import SpeculosBackend +from ragger.navigator import NavInsID, NavIns +from ragger.backend.interface import RAPDU + + +def get_nano_review_instructions(num_screen_skip): + instructions = [NavIns(NavInsID.RIGHT_CLICK)] * num_screen_skip + instructions.append(NavIns(NavInsID.BOTH_CLICK)) + return instructions + + +def get_stax_review_instructions(num_screen_skip): + instructions = [NavIns(NavInsID.USE_CASE_CHOICE_CONFIRM)] + + for i in range(num_screen_skip): + instructions.append(NavIns(NavInsID.USE_CASE_REVIEW_TAP)) + + instructions.append(NavIns(NavInsID.USE_CASE_REVIEW_CONFIRM)) + instructions.append(NavIns(NavInsID.USE_CASE_STATUS_DISMISS)) + return instructions + + +def get_stax_review_instructions_with_warning(num_screen_skip): + instructions = [NavIns(NavInsID.USE_CASE_CHOICE_CONFIRM)] + instructions.append(NavIns(NavInsID.USE_CASE_CHOICE_CONFIRM)) + + for i in range(num_screen_skip): + instructions.append(NavIns(NavInsID.USE_CASE_REVIEW_TAP)) + + instructions.append(NavIns(NavInsID.USE_CASE_REVIEW_CONFIRM)) + instructions.append(NavIns(NavInsID.USE_CASE_STATUS_DISMISS)) + return instructions + + +def get_async_response(backend: SpeculosBackend) -> RAPDU: + return backend.last_async_response + + +def pack_derivation_path(derivation_path: str) -> bytes: + split = derivation_path.split("/") + assert split.pop(0) == "m", "master expected" + derivation_path: bytes = (len(split)).to_bytes(1, byteorder='big') + for i in split: + if (i[-1] == "'"): + derivation_path += (int(i[:-1]) | HARDENED_INDEX).to_bytes(4, byteorder='big') + else: + derivation_path += int(i).to_bytes(4, byteorder='big') + return derivation_path diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..82e60530 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,3 @@ +base58 +bip32 +ragger[tests,speculos] diff --git a/tests/snapshots/flex/test_display_address/00000.png b/tests/snapshots/flex/test_display_address/00000.png new file mode 100644 index 00000000..b824ee50 Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00000.png differ diff --git a/tests/snapshots/flex/test_display_address/00001.png b/tests/snapshots/flex/test_display_address/00001.png new file mode 100644 index 00000000..075ca11e Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00001.png differ diff --git a/tests/snapshots/flex/test_display_address/00002.png b/tests/snapshots/flex/test_display_address/00002.png new file mode 100644 index 00000000..c99cda9c Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00002.png differ diff --git a/tests/snapshots/flex/test_display_address/00003.png b/tests/snapshots/flex/test_display_address/00003.png new file mode 100644 index 00000000..075ca11e Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00003.png differ diff --git a/tests/snapshots/flex/test_display_address/00004.png b/tests/snapshots/flex/test_display_address/00004.png new file mode 100644 index 00000000..27b70e05 Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00004.png differ diff --git a/tests/snapshots/flex/test_display_address/00005.png b/tests/snapshots/flex/test_display_address/00005.png new file mode 100644 index 00000000..4321e601 Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00005.png differ diff --git a/tests/snapshots/flex/test_display_address/00006.png b/tests/snapshots/flex/test_display_address/00006.png new file mode 100644 index 00000000..876e018f Binary files /dev/null and b/tests/snapshots/flex/test_display_address/00006.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00000.png b/tests/snapshots/flex/test_display_subaddress/00000.png new file mode 100644 index 00000000..b824ee50 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00000.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00001.png b/tests/snapshots/flex/test_display_subaddress/00001.png new file mode 100644 index 00000000..f9bc6495 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00001.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00002.png b/tests/snapshots/flex/test_display_subaddress/00002.png new file mode 100644 index 00000000..1775cfc5 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00002.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00003.png b/tests/snapshots/flex/test_display_subaddress/00003.png new file mode 100644 index 00000000..f9bc6495 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00003.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00004.png b/tests/snapshots/flex/test_display_subaddress/00004.png new file mode 100644 index 00000000..409120c8 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00004.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00005.png b/tests/snapshots/flex/test_display_subaddress/00005.png new file mode 100644 index 00000000..4321e601 Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00005.png differ diff --git a/tests/snapshots/flex/test_display_subaddress/00006.png b/tests/snapshots/flex/test_display_subaddress/00006.png new file mode 100644 index 00000000..876e018f Binary files /dev/null and b/tests/snapshots/flex/test_display_subaddress/00006.png differ diff --git a/tests/snapshots/flex/test_prefix_hash_hash_init/00000.png b/tests/snapshots/flex/test_prefix_hash_hash_init/00000.png new file mode 100644 index 00000000..60911aed Binary files /dev/null and b/tests/snapshots/flex/test_prefix_hash_hash_init/00000.png differ diff --git a/tests/snapshots/flex/test_prefix_hash_hash_init/00001.png b/tests/snapshots/flex/test_prefix_hash_hash_init/00001.png new file mode 100644 index 00000000..92bcce3e Binary files /dev/null and b/tests/snapshots/flex/test_prefix_hash_hash_init/00001.png differ diff --git a/tests/snapshots/flex/test_private_view_key/00000.png b/tests/snapshots/flex/test_private_view_key/00000.png new file mode 100644 index 00000000..146a5f57 Binary files /dev/null and b/tests/snapshots/flex/test_private_view_key/00000.png differ diff --git a/tests/snapshots/flex/test_private_view_key/00001.png b/tests/snapshots/flex/test_private_view_key/00001.png new file mode 100644 index 00000000..d08de444 Binary files /dev/null and b/tests/snapshots/flex/test_private_view_key/00001.png differ diff --git a/tests/snapshots/flex/test_validate_prehash_update/00000.png b/tests/snapshots/flex/test_validate_prehash_update/00000.png new file mode 100644 index 00000000..fa42052b Binary files /dev/null and b/tests/snapshots/flex/test_validate_prehash_update/00000.png differ diff --git a/tests/snapshots/flex/test_validate_prehash_update/00001.png b/tests/snapshots/flex/test_validate_prehash_update/00001.png new file mode 100644 index 00000000..5dfb8a9b Binary files /dev/null and b/tests/snapshots/flex/test_validate_prehash_update/00001.png differ diff --git a/tests/snapshots/flex/test_validate_prehash_update/00002.png b/tests/snapshots/flex/test_validate_prehash_update/00002.png new file mode 100644 index 00000000..0e3eac5c Binary files /dev/null and b/tests/snapshots/flex/test_validate_prehash_update/00002.png differ diff --git a/tests/snapshots/nanos/test_display_address/00000.png b/tests/snapshots/nanos/test_display_address/00000.png new file mode 100644 index 00000000..8392c006 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00000.png differ diff --git a/tests/snapshots/nanos/test_display_address/00001.png b/tests/snapshots/nanos/test_display_address/00001.png new file mode 100644 index 00000000..beed0ae3 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00001.png differ diff --git a/tests/snapshots/nanos/test_display_address/00002.png b/tests/snapshots/nanos/test_display_address/00002.png new file mode 100644 index 00000000..a6026d1a Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00002.png differ diff --git a/tests/snapshots/nanos/test_display_address/00003.png b/tests/snapshots/nanos/test_display_address/00003.png new file mode 100644 index 00000000..1dc79da5 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00003.png differ diff --git a/tests/snapshots/nanos/test_display_address/00004.png b/tests/snapshots/nanos/test_display_address/00004.png new file mode 100644 index 00000000..37dbedf9 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00004.png differ diff --git a/tests/snapshots/nanos/test_display_address/00005.png b/tests/snapshots/nanos/test_display_address/00005.png new file mode 100644 index 00000000..6f53639b Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00005.png differ diff --git a/tests/snapshots/nanos/test_display_address/00006.png b/tests/snapshots/nanos/test_display_address/00006.png new file mode 100644 index 00000000..d9f596f1 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00006.png differ diff --git a/tests/snapshots/nanos/test_display_address/00007.png b/tests/snapshots/nanos/test_display_address/00007.png new file mode 100644 index 00000000..3b1e3278 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00007.png differ diff --git a/tests/snapshots/nanos/test_display_address/00008.png b/tests/snapshots/nanos/test_display_address/00008.png new file mode 100644 index 00000000..bb6d52f8 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00008.png differ diff --git a/tests/snapshots/nanos/test_display_address/00009.png b/tests/snapshots/nanos/test_display_address/00009.png new file mode 100644 index 00000000..925ee999 Binary files /dev/null and b/tests/snapshots/nanos/test_display_address/00009.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00000.png b/tests/snapshots/nanos/test_display_subaddress/00000.png new file mode 100644 index 00000000..b4ba0791 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00000.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00001.png b/tests/snapshots/nanos/test_display_subaddress/00001.png new file mode 100644 index 00000000..f5eefd43 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00001.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00002.png b/tests/snapshots/nanos/test_display_subaddress/00002.png new file mode 100644 index 00000000..6b9a13dc Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00002.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00003.png b/tests/snapshots/nanos/test_display_subaddress/00003.png new file mode 100644 index 00000000..36022914 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00003.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00004.png b/tests/snapshots/nanos/test_display_subaddress/00004.png new file mode 100644 index 00000000..571575f3 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00004.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00005.png b/tests/snapshots/nanos/test_display_subaddress/00005.png new file mode 100644 index 00000000..11691f51 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00005.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00006.png b/tests/snapshots/nanos/test_display_subaddress/00006.png new file mode 100644 index 00000000..c47d4d63 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00006.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00007.png b/tests/snapshots/nanos/test_display_subaddress/00007.png new file mode 100644 index 00000000..2606d53f Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00007.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00008.png b/tests/snapshots/nanos/test_display_subaddress/00008.png new file mode 100644 index 00000000..bb6d52f8 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00008.png differ diff --git a/tests/snapshots/nanos/test_display_subaddress/00009.png b/tests/snapshots/nanos/test_display_subaddress/00009.png new file mode 100644 index 00000000..925ee999 Binary files /dev/null and b/tests/snapshots/nanos/test_display_subaddress/00009.png differ diff --git a/tests/snapshots/nanos/test_prefix_hash_hash_init/00000.png b/tests/snapshots/nanos/test_prefix_hash_hash_init/00000.png new file mode 100644 index 00000000..b9a2e0dd Binary files /dev/null and b/tests/snapshots/nanos/test_prefix_hash_hash_init/00000.png differ diff --git a/tests/snapshots/nanos/test_prefix_hash_hash_init/00001.png b/tests/snapshots/nanos/test_prefix_hash_hash_init/00001.png new file mode 100644 index 00000000..e66f6628 Binary files /dev/null and b/tests/snapshots/nanos/test_prefix_hash_hash_init/00001.png differ diff --git a/tests/snapshots/nanos/test_prefix_hash_hash_init/00002.png b/tests/snapshots/nanos/test_prefix_hash_hash_init/00002.png new file mode 100644 index 00000000..2360dfba Binary files /dev/null and b/tests/snapshots/nanos/test_prefix_hash_hash_init/00002.png differ diff --git a/tests/snapshots/nanos/test_private_view_key/00000.png b/tests/snapshots/nanos/test_private_view_key/00000.png new file mode 100644 index 00000000..0384a5a2 Binary files /dev/null and b/tests/snapshots/nanos/test_private_view_key/00000.png differ diff --git a/tests/snapshots/nanos/test_private_view_key/00001.png b/tests/snapshots/nanos/test_private_view_key/00001.png new file mode 100644 index 00000000..1e284f08 Binary files /dev/null and b/tests/snapshots/nanos/test_private_view_key/00001.png differ diff --git a/tests/snapshots/nanos/test_private_view_key/00002.png b/tests/snapshots/nanos/test_private_view_key/00002.png new file mode 100644 index 00000000..925ee999 Binary files /dev/null and b/tests/snapshots/nanos/test_private_view_key/00002.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_init/00000.png b/tests/snapshots/nanos/test_validate_prehash_init/00000.png new file mode 100644 index 00000000..e9bb91ad Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_init/00000.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_init/00001.png b/tests/snapshots/nanos/test_validate_prehash_init/00001.png new file mode 100644 index 00000000..e66f6628 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_init/00001.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_init/00002.png b/tests/snapshots/nanos/test_validate_prehash_init/00002.png new file mode 100644 index 00000000..2360dfba Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_init/00002.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00000.png b/tests/snapshots/nanos/test_validate_prehash_update/00000.png new file mode 100644 index 00000000..b16b0c5f Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00000.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00001.png b/tests/snapshots/nanos/test_validate_prehash_update/00001.png new file mode 100644 index 00000000..05135fd1 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00001.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00002.png b/tests/snapshots/nanos/test_validate_prehash_update/00002.png new file mode 100644 index 00000000..178c13d1 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00002.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00003.png b/tests/snapshots/nanos/test_validate_prehash_update/00003.png new file mode 100644 index 00000000..24848774 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00003.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00004.png b/tests/snapshots/nanos/test_validate_prehash_update/00004.png new file mode 100644 index 00000000..6146bbd3 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00004.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00005.png b/tests/snapshots/nanos/test_validate_prehash_update/00005.png new file mode 100644 index 00000000..145db6e0 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00005.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00006.png b/tests/snapshots/nanos/test_validate_prehash_update/00006.png new file mode 100644 index 00000000..3084b7ca Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00006.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00007.png b/tests/snapshots/nanos/test_validate_prehash_update/00007.png new file mode 100644 index 00000000..e66f6628 Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00007.png differ diff --git a/tests/snapshots/nanos/test_validate_prehash_update/00008.png b/tests/snapshots/nanos/test_validate_prehash_update/00008.png new file mode 100644 index 00000000..2360dfba Binary files /dev/null and b/tests/snapshots/nanos/test_validate_prehash_update/00008.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00000.png b/tests/snapshots/nanosp/test_display_address/00000.png new file mode 100644 index 00000000..658e5946 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00000.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00001.png b/tests/snapshots/nanosp/test_display_address/00001.png new file mode 100644 index 00000000..a074d01a Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00001.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00002.png b/tests/snapshots/nanosp/test_display_address/00002.png new file mode 100644 index 00000000..224e7fdf Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00002.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00003.png b/tests/snapshots/nanosp/test_display_address/00003.png new file mode 100644 index 00000000..c4ba4822 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00003.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00004.png b/tests/snapshots/nanosp/test_display_address/00004.png new file mode 100644 index 00000000..2eca2713 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00004.png differ diff --git a/tests/snapshots/nanosp/test_display_address/00005.png b/tests/snapshots/nanosp/test_display_address/00005.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_address/00005.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00000.png b/tests/snapshots/nanosp/test_display_subaddress/00000.png new file mode 100644 index 00000000..4b4d3d97 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00000.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00001.png b/tests/snapshots/nanosp/test_display_subaddress/00001.png new file mode 100644 index 00000000..ab2a5889 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00001.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00002.png b/tests/snapshots/nanosp/test_display_subaddress/00002.png new file mode 100644 index 00000000..d7e39756 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00002.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00003.png b/tests/snapshots/nanosp/test_display_subaddress/00003.png new file mode 100644 index 00000000..3aa67551 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00003.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00004.png b/tests/snapshots/nanosp/test_display_subaddress/00004.png new file mode 100644 index 00000000..2eca2713 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00004.png differ diff --git a/tests/snapshots/nanosp/test_display_subaddress/00005.png b/tests/snapshots/nanosp/test_display_subaddress/00005.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanosp/test_display_subaddress/00005.png differ diff --git a/tests/snapshots/nanosp/test_prefix_hash_hash_init/00000.png b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00000.png new file mode 100644 index 00000000..b02564fa Binary files /dev/null and b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00000.png differ diff --git a/tests/snapshots/nanosp/test_prefix_hash_hash_init/00001.png b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00001.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00001.png differ diff --git a/tests/snapshots/nanosp/test_prefix_hash_hash_init/00002.png b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00002.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanosp/test_prefix_hash_hash_init/00002.png differ diff --git a/tests/snapshots/nanosp/test_private_view_key/00000.png b/tests/snapshots/nanosp/test_private_view_key/00000.png new file mode 100644 index 00000000..0128f842 Binary files /dev/null and b/tests/snapshots/nanosp/test_private_view_key/00000.png differ diff --git a/tests/snapshots/nanosp/test_private_view_key/00001.png b/tests/snapshots/nanosp/test_private_view_key/00001.png new file mode 100644 index 00000000..0a22ed9c Binary files /dev/null and b/tests/snapshots/nanosp/test_private_view_key/00001.png differ diff --git a/tests/snapshots/nanosp/test_private_view_key/00002.png b/tests/snapshots/nanosp/test_private_view_key/00002.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanosp/test_private_view_key/00002.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_init/00000.png b/tests/snapshots/nanosp/test_validate_prehash_init/00000.png new file mode 100644 index 00000000..d83aca87 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_init/00000.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_init/00001.png b/tests/snapshots/nanosp/test_validate_prehash_init/00001.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_init/00001.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_init/00002.png b/tests/snapshots/nanosp/test_validate_prehash_init/00002.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_init/00002.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_update/00000.png b/tests/snapshots/nanosp/test_validate_prehash_update/00000.png new file mode 100644 index 00000000..6a2a0062 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_update/00000.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_update/00001.png b/tests/snapshots/nanosp/test_validate_prehash_update/00001.png new file mode 100644 index 00000000..62047757 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_update/00001.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_update/00002.png b/tests/snapshots/nanosp/test_validate_prehash_update/00002.png new file mode 100644 index 00000000..bac1f06c Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_update/00002.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_update/00003.png b/tests/snapshots/nanosp/test_validate_prehash_update/00003.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_update/00003.png differ diff --git a/tests/snapshots/nanosp/test_validate_prehash_update/00004.png b/tests/snapshots/nanosp/test_validate_prehash_update/00004.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanosp/test_validate_prehash_update/00004.png differ diff --git a/tests/snapshots/nanox/test_display_address/00000.png b/tests/snapshots/nanox/test_display_address/00000.png new file mode 100644 index 00000000..658e5946 Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00000.png differ diff --git a/tests/snapshots/nanox/test_display_address/00001.png b/tests/snapshots/nanox/test_display_address/00001.png new file mode 100644 index 00000000..a074d01a Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00001.png differ diff --git a/tests/snapshots/nanox/test_display_address/00002.png b/tests/snapshots/nanox/test_display_address/00002.png new file mode 100644 index 00000000..224e7fdf Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00002.png differ diff --git a/tests/snapshots/nanox/test_display_address/00003.png b/tests/snapshots/nanox/test_display_address/00003.png new file mode 100644 index 00000000..c4ba4822 Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00003.png differ diff --git a/tests/snapshots/nanox/test_display_address/00004.png b/tests/snapshots/nanox/test_display_address/00004.png new file mode 100644 index 00000000..2eca2713 Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00004.png differ diff --git a/tests/snapshots/nanox/test_display_address/00005.png b/tests/snapshots/nanox/test_display_address/00005.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanox/test_display_address/00005.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00000.png b/tests/snapshots/nanox/test_display_subaddress/00000.png new file mode 100644 index 00000000..4b4d3d97 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00000.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00001.png b/tests/snapshots/nanox/test_display_subaddress/00001.png new file mode 100644 index 00000000..ab2a5889 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00001.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00002.png b/tests/snapshots/nanox/test_display_subaddress/00002.png new file mode 100644 index 00000000..d7e39756 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00002.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00003.png b/tests/snapshots/nanox/test_display_subaddress/00003.png new file mode 100644 index 00000000..3aa67551 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00003.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00004.png b/tests/snapshots/nanox/test_display_subaddress/00004.png new file mode 100644 index 00000000..2eca2713 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00004.png differ diff --git a/tests/snapshots/nanox/test_display_subaddress/00005.png b/tests/snapshots/nanox/test_display_subaddress/00005.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanox/test_display_subaddress/00005.png differ diff --git a/tests/snapshots/nanox/test_prefix_hash_hash_init/00000.png b/tests/snapshots/nanox/test_prefix_hash_hash_init/00000.png new file mode 100644 index 00000000..b02564fa Binary files /dev/null and b/tests/snapshots/nanox/test_prefix_hash_hash_init/00000.png differ diff --git a/tests/snapshots/nanox/test_prefix_hash_hash_init/00001.png b/tests/snapshots/nanox/test_prefix_hash_hash_init/00001.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanox/test_prefix_hash_hash_init/00001.png differ diff --git a/tests/snapshots/nanox/test_prefix_hash_hash_init/00002.png b/tests/snapshots/nanox/test_prefix_hash_hash_init/00002.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanox/test_prefix_hash_hash_init/00002.png differ diff --git a/tests/snapshots/nanox/test_private_view_key/00000.png b/tests/snapshots/nanox/test_private_view_key/00000.png new file mode 100644 index 00000000..0128f842 Binary files /dev/null and b/tests/snapshots/nanox/test_private_view_key/00000.png differ diff --git a/tests/snapshots/nanox/test_private_view_key/00001.png b/tests/snapshots/nanox/test_private_view_key/00001.png new file mode 100644 index 00000000..0a22ed9c Binary files /dev/null and b/tests/snapshots/nanox/test_private_view_key/00001.png differ diff --git a/tests/snapshots/nanox/test_private_view_key/00002.png b/tests/snapshots/nanox/test_private_view_key/00002.png new file mode 100644 index 00000000..960d4c51 Binary files /dev/null and b/tests/snapshots/nanox/test_private_view_key/00002.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_init/00000.png b/tests/snapshots/nanox/test_validate_prehash_init/00000.png new file mode 100644 index 00000000..d83aca87 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_init/00000.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_init/00001.png b/tests/snapshots/nanox/test_validate_prehash_init/00001.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_init/00001.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_init/00002.png b/tests/snapshots/nanox/test_validate_prehash_init/00002.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_init/00002.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_update/00000.png b/tests/snapshots/nanox/test_validate_prehash_update/00000.png new file mode 100644 index 00000000..6a2a0062 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_update/00000.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_update/00001.png b/tests/snapshots/nanox/test_validate_prehash_update/00001.png new file mode 100644 index 00000000..62047757 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_update/00001.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_update/00002.png b/tests/snapshots/nanox/test_validate_prehash_update/00002.png new file mode 100644 index 00000000..bac1f06c Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_update/00002.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_update/00003.png b/tests/snapshots/nanox/test_validate_prehash_update/00003.png new file mode 100644 index 00000000..63db7647 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_update/00003.png differ diff --git a/tests/snapshots/nanox/test_validate_prehash_update/00004.png b/tests/snapshots/nanox/test_validate_prehash_update/00004.png new file mode 100644 index 00000000..b1a8fd91 Binary files /dev/null and b/tests/snapshots/nanox/test_validate_prehash_update/00004.png differ diff --git a/tests/snapshots/stax/test_display_address/00000.png b/tests/snapshots/stax/test_display_address/00000.png new file mode 100644 index 00000000..ef48b90a Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00000.png differ diff --git a/tests/snapshots/stax/test_display_address/00001.png b/tests/snapshots/stax/test_display_address/00001.png new file mode 100644 index 00000000..b331e020 Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00001.png differ diff --git a/tests/snapshots/stax/test_display_address/00002.png b/tests/snapshots/stax/test_display_address/00002.png new file mode 100644 index 00000000..4380f090 Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00002.png differ diff --git a/tests/snapshots/stax/test_display_address/00003.png b/tests/snapshots/stax/test_display_address/00003.png new file mode 100644 index 00000000..b331e020 Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00003.png differ diff --git a/tests/snapshots/stax/test_display_address/00004.png b/tests/snapshots/stax/test_display_address/00004.png new file mode 100644 index 00000000..3a3a98e2 Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00004.png differ diff --git a/tests/snapshots/stax/test_display_address/00005.png b/tests/snapshots/stax/test_display_address/00005.png new file mode 100644 index 00000000..3f906b2b Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00005.png differ diff --git a/tests/snapshots/stax/test_display_address/00006.png b/tests/snapshots/stax/test_display_address/00006.png new file mode 100644 index 00000000..02f4af83 Binary files /dev/null and b/tests/snapshots/stax/test_display_address/00006.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00000.png b/tests/snapshots/stax/test_display_subaddress/00000.png new file mode 100644 index 00000000..ef48b90a Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00000.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00001.png b/tests/snapshots/stax/test_display_subaddress/00001.png new file mode 100644 index 00000000..e6bce053 Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00001.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00002.png b/tests/snapshots/stax/test_display_subaddress/00002.png new file mode 100644 index 00000000..ce343464 Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00002.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00003.png b/tests/snapshots/stax/test_display_subaddress/00003.png new file mode 100644 index 00000000..e6bce053 Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00003.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00004.png b/tests/snapshots/stax/test_display_subaddress/00004.png new file mode 100644 index 00000000..58f51b54 Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00004.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00005.png b/tests/snapshots/stax/test_display_subaddress/00005.png new file mode 100644 index 00000000..3f906b2b Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00005.png differ diff --git a/tests/snapshots/stax/test_display_subaddress/00006.png b/tests/snapshots/stax/test_display_subaddress/00006.png new file mode 100644 index 00000000..02f4af83 Binary files /dev/null and b/tests/snapshots/stax/test_display_subaddress/00006.png differ diff --git a/tests/snapshots/stax/test_prefix_hash_hash_init/00000.png b/tests/snapshots/stax/test_prefix_hash_hash_init/00000.png new file mode 100644 index 00000000..b77c0a4d Binary files /dev/null and b/tests/snapshots/stax/test_prefix_hash_hash_init/00000.png differ diff --git a/tests/snapshots/stax/test_prefix_hash_hash_init/00001.png b/tests/snapshots/stax/test_prefix_hash_hash_init/00001.png new file mode 100644 index 00000000..cfb99420 Binary files /dev/null and b/tests/snapshots/stax/test_prefix_hash_hash_init/00001.png differ diff --git a/tests/snapshots/stax/test_private_view_key/00000.png b/tests/snapshots/stax/test_private_view_key/00000.png new file mode 100644 index 00000000..5e2de951 Binary files /dev/null and b/tests/snapshots/stax/test_private_view_key/00000.png differ diff --git a/tests/snapshots/stax/test_private_view_key/00001.png b/tests/snapshots/stax/test_private_view_key/00001.png new file mode 100644 index 00000000..b802c1cc Binary files /dev/null and b/tests/snapshots/stax/test_private_view_key/00001.png differ diff --git a/tests/snapshots/stax/test_validate_prehash_update/00000.png b/tests/snapshots/stax/test_validate_prehash_update/00000.png new file mode 100644 index 00000000..bc5b2d0e Binary files /dev/null and b/tests/snapshots/stax/test_validate_prehash_update/00000.png differ diff --git a/tests/snapshots/stax/test_validate_prehash_update/00001.png b/tests/snapshots/stax/test_validate_prehash_update/00001.png new file mode 100644 index 00000000..17d0bfba Binary files /dev/null and b/tests/snapshots/stax/test_validate_prehash_update/00001.png differ diff --git a/tests/snapshots/stax/test_validate_prehash_update/00002.png b/tests/snapshots/stax/test_validate_prehash_update/00002.png new file mode 100644 index 00000000..f1e88e79 Binary files /dev/null and b/tests/snapshots/stax/test_validate_prehash_update/00002.png differ diff --git a/tests/snapshots/stax/test_validate_prehash_update/00003.png b/tests/snapshots/stax/test_validate_prehash_update/00003.png new file mode 100644 index 00000000..264824aa Binary files /dev/null and b/tests/snapshots/stax/test_validate_prehash_update/00003.png differ diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 18ab9b5d..409ef581 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -11,8 +11,8 @@ def test_public_keys(monero): "p5pSacMdSg7A3b71RejLzB8EkGbfjp5PELVHCRUaE") -def test_private_view_key(monero, button): - view_priv_key: bytes = monero.get_private_view_key(button) +def test_private_view_key(monero, navigator, firmware, test_name): + view_priv_key: bytes = monero.get_private_view_key(test_name, firmware, navigator) assert view_priv_key == bytes.fromhex("0f3fe25d0c6d4c94dde0c0bcc214b233" "e9c72927f813728b0f01f28f9d5e1201") @@ -77,3 +77,157 @@ def test_gen_key_derivation(monero): ) assert expected == monero.xor_cipher(_d_in, b"\x55") # decrypt _d_in + + +class Derivation_Test: + _derivation: bytes + _output_index: bytes + _expected_view_tag: int + + def __init__(self, derivation, output_index, expected_view_tag): + self._derivation = bytes.fromhex(derivation) + self._output_index = (output_index).to_bytes(4, byteorder='big') + self._expected_view_tag = expected_view_tag + + def do_test(self, monero): + encrypted_derivation = monero.xor_cipher( + self._derivation, b"\x55") # encrypt with dummy key 0x55 + assert self._expected_view_tag == monero.derive_view_tag( + encrypted_derivation, self._output_index) + + +DERIVATION_TESTS = [ + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 0, 0x76), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 1, 0xd6), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 2, 0x87), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 3, 0x1b), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 12, 0xd6), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 13, 0xe9), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 14, 0x12), + Derivation_Test( + "0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 15, 0x26), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 0, 0x70), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 1, 0x81), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 2, 0xa0), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 3, 0xec), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 12, 0x22), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 13, 0x0a), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 14, 0x87), + Derivation_Test( + "a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 15, 0x76), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 0, 0x93), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 1, 0x67), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 2, 0x9d), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 3, 0x2d), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 12, 0x63), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 13, 0xcf), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 14, 0xef), + Derivation_Test( + "7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 15, 0x10), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 0, 0x90), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 1, 0x5a), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 2, 0xde), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 3, 0x21), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 12, 0x57), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 13, 0x52), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 14, 0x6f), + Derivation_Test( + "fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 15, 0xeb), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 0, 0xc6), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 1, 0x60), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 2, 0xf0), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 3, 0x71), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 12, 0x0e), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 13, 0x42), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 14, 0xb2), + Derivation_Test( + "ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 15, 0x61), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 0, 0x4c), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 1, 0x9b), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 2, 0x64), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 3, 0xff), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 12, 0xe3), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 13, 0x24), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 14, 0xea), + Derivation_Test( + "25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 15, 0x3b), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 0, 0x74), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 1, 0x77), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 2, 0xa9), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 3, 0x44), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 12, 0x75), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 13, 0x05), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 14, 0xca), + Derivation_Test( + "8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 15, 0x00), +] + + +def test_derive_view_tag(monero): + for test in DERIVATION_TESTS: + test.do_test(monero) + + +def test_display_address(monero, navigator, firmware, test_name): + major: bytes = (0).to_bytes(4, byteorder='little') + minor: bytes = (0).to_bytes(4, byteorder='little') + payment_id: bytes = bytes.fromhex(8*"00") + monero.display_address(test_name, firmware, navigator, major + + minor + payment_id, bytes.fromhex("00")) + + +def test_display_subaddress(monero, navigator, firmware, test_name): + major: bytes = (0).to_bytes(4, byteorder='little') + minor: bytes = (1).to_bytes(4, byteorder='little') + payment_id: bytes = bytes.fromhex(8*"00") + monero.display_address(test_name, firmware, navigator, major + + minor + payment_id, bytes.fromhex("00")) diff --git a/tests/test_sig.py b/tests/test_sig.py index 03cd4c2b..e1606e19 100644 --- a/tests/test_sig.py +++ b/tests/test_sig.py @@ -1,6 +1,7 @@ import pytest from monero_client.monero_types import SigType, Keys +from monero_client.exception import ClientNotSupported @pytest.mark.incremental @@ -37,7 +38,7 @@ def state(): return {"sender": sender, "receiver": receiver, - "amount": 10**12, # 1.0 XMR + "amount": 11**3, "tx_pub_key": None, "_tx_priv_key": None, "_ak_amount": [], @@ -47,10 +48,9 @@ def state(): @staticmethod def test_set_sig(monero): - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.16.0.0" - ) # type: int, int, int - assert (major, minor, patch) == (1, 6, 0) # version of the Monero app + monero.reset_and_get_version( + monero_client_version=b"0.18" + ) sig_mode: SigType = monero.set_signature_mode(sig_type=SigType.REAL) assert sig_mode == SigType.REAL @@ -83,11 +83,12 @@ def test_gen_txout_keys(monero, state): state["_ak_amount"].append(_ak_amount) # _ak_amount_t @staticmethod - def test_prefix_hash(monero, button): - expected: bytes = bytes.fromhex("49d03a195e239b52779866b33024210f" - "c7dc66e9c2998975c0aa45c1702549d5") + def test_prefix_hash(monero, backend, navigator, firmware, test_name): + expected: bytes = bytes.fromhex("9a259973bf721120aceae3d8d40696c0" + "7470331e386028753123f37fee36926b") # should ask for timelock validation - monero.prefix_hash_init(button=button, version=0, timelock=1) + monero.prefix_hash_init(backend, test_name, firmware, + navigator=navigator, version=0, timelock=2147483650) result: bytes = monero.prefix_hash_update( index=1, payload=b"", @@ -123,39 +124,61 @@ def test_blind(monero, state): ) # type: bytes, bytes assert state["y"][0] == mask - assert state["amount"] == int.from_bytes(amount, byteorder="big") + # assert state["amount"] == int.from_bytes(amount, byteorder="big") state["blinded_mask"].append(blinded_mask) state["blinded_amount"].append(blinded_amount) - @staticmethod - def test_validate(monero, button, state): assert len(state["y"]) != 0 assert len(state["_ak_amount"]) != 0 assert len(state["blinded_amount"]) != 0 assert len(state["blinded_mask"]) != 0 + @staticmethod + def test_validate(monero, backend, navigator, firmware, test_name, state): + fee: int = 100000000 # 0.0001 XMR # should ask for fee validation - monero.validate_prehash_init(button=button, + monero.validate_prehash_init(backend, + navigator=navigator, + firmware=firmware, + test_name=test_name, index=1, # start at 1 txntype=0, txnfee=fee) - # monero_client.validate_prehash_update( - # index=1, - # is_short=False, - # is_change_addr=False, - # is_subaddress=False, - # dst_pub_view_key=state["receiver"].public_view_key, - # dst_pub_spend_key=state["receiver"].public_spend_key, - # _ak_amount=state["_ak_amount"][0], - # commitment=..., - # blinded_amount=state["blinded_amount"][0], - # blinded_mask=state["blinded_mask"][0], - # is_last=True - # ) + monero.validate_prehash_update( + backend, + test_name, + firmware, + navigator, + index=1, + is_short=False, + is_change_addr=False, + is_subaddress=False, + dst_pub_view_key=state["receiver"].public_view_key, + dst_pub_spend_key=state["receiver"].public_spend_key, + _ak_amount=state["_ak_amount"][0], + commitment=bytes.fromhex(32*"00"), + blinded_amount=state["blinded_amount"][0], + blinded_mask=state["blinded_mask"][0], + is_last=True + ) + + monero.validate_prehash_finalize( + index=1, + is_short=False, + is_change_addr=False, + is_subaddress=False, + dst_pub_view_key=state["receiver"].public_view_key, + dst_pub_spend_key=state["receiver"].public_spend_key, + _ak_amount=state["_ak_amount"][0], + commitment=bytes.fromhex(32*"00"), + blinded_amount=state["blinded_amount"][0], + blinded_mask=state["blinded_mask"][0], + is_last=True + ) @staticmethod def test_close_tx(monero): diff --git a/tests/test_version.py b/tests/test_version.py index 4eeb9cfc..a47549ba 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,18 +1,59 @@ +from typing import List +import re import pytest # pylint: disable=wildcard-import, unused-wildcard-import -from monero_client.exception import * +from monero_client.exception import ClientNotSupported -def test_version(monero): +def check_accepted_version(monero, valid_version: bytes): major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.16.0.0" + monero_client_version=valid_version ) # type: int, int, int + _verify_version(f"{major}.{minor}.{patch}") + + +def check_refused_version(monero, invalid_version: bytes): + with pytest.raises(ClientNotSupported) as excinfo: + monero.reset_and_get_version(invalid_version) + + +def test_version(monero): + # Monero does not have any consensus changes with minor point upgrades, we support the entire 0.18. range, except 0.18.0.0 + check_accepted_version(monero, b"0.18") + check_accepted_version(monero, b"0.18.0.1") + check_accepted_version(monero, b"0.18.0.10") + check_accepted_version(monero, b"0.18.1.1") + check_accepted_version(monero, b"0.18.9.0") + check_accepted_version(monero, b"0.18.18.0") + + +def _verify_version(version: str) -> None: + """Verify the app version, based on defines in Makefile + + Args: + Version (str): Version to be checked + """ + + vers_dict = {} + vers_str = "" + lines = _read_makefile() + version_re = re.compile(r"^APPVERSION_(?P\w)\s?=\s?(?P\d*)", re.I) + for line in lines: + info = version_re.match(line) + if info: + dinfo = info.groupdict() + vers_dict[dinfo["part"]] = dinfo["val"] + try: + vers_str = f"{vers_dict['M']}.{vers_dict['N']}.{vers_dict['P']}" + except KeyError: + pass + assert version == vers_str - assert (major, minor, patch) == (1, 6, 0) # version of the Monero app +def _read_makefile() -> List[str]: + """Read lines from the parent Makefile """ -@pytest.mark.xfail(raises=ClientNotSupported) -def test_old_client_version(monero): - # should raise ClientNotSupported[0x6a30] - monero.reset_and_get_version(b"0.15.0.0") + with open("Makefile", "r", encoding="utf-8") as f_p: + lines = f_p.readlines() + return lines \ No newline at end of file diff --git a/tools/python/README.md b/tools/python/README.md index 8d16cfb7..b6f1670a 100644 --- a/tools/python/README.md +++ b/tools/python/README.md @@ -65,11 +65,11 @@ On Mac/Linux In tools/python/ directory run the command: - PYTHONPATH=`pwd`/src python3 -m ledger.monero.seedconv.py offline + PYTHONPATH=`pwd`/src python3 src/ledger/monero/seedconv.py offline Example: - $ PYTHONPATH="$(pwd)/src" python3 -m ledger.monero.seedconv offline + $ PYTHONPATH="$(pwd)/src" python3 src/ledger/monero/seedconv.py offline ============================================================= Monero Seed Converter v0.9. Copyright (c) Ledger SAS 20018. @@ -113,11 +113,11 @@ Example: In tools/python/ directory run the command: - PYTHONPATH=`pwd`/src python3 -m ledger.monero.seedconv.py online + PYTHONPATH=`pwd`/src python3 src/ledger/monero/seedconv.py online Example: - $ PYTHONPATH=`pwd`/src python3 -m ledger.monero.seedconv.py online + $ PYTHONPATH=`pwd`/src python3 src/ledger/monero/seedconv.py online ============================================================= Monero Seed Converter v0.9. Copyright (c) Ledger SAS 20018. diff --git a/tools/python/src/ledger/monero/seedconv.py b/tools/python/src/ledger/monero/seedconv.py index 0c592c86..4d26495f 100644 --- a/tools/python/src/ledger/monero/seedconv.py +++ b/tools/python/src/ledger/monero/seedconv.py @@ -25,7 +25,7 @@ -from .dictionaries.languages import monero_langs +from ledger.monero.dictionaries.languages import monero_langs # =========================================================================================