diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 3e15b79..4c9118a 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -171,7 +171,6 @@ jobs: with: upload_app_binaries_artifact: "ragger_elfs" flags: "DEBUG=1 CAL_CI_KEY=1 DOMAIN_NAME_TEST_KEY=1" - run_for_devices: '["nanos", "nanox", "nanosp"]' jobs-ragger-tests: name: Run Ragger tests @@ -180,4 +179,3 @@ jobs: with: download_app_binaries_artifact: "ragger_elfs" test_dir: tests/ragger - run_for_devices: '["nanos", "nanox", "nanosp"]' diff --git a/.gitignore b/.gitignore index 9443644..6779287 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ bin/ debug/ dep/ obj/ +build/ # Python *.pyc diff --git a/Makefile b/Makefile index a32c240..20e4d69 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ include ./makefile_conf/chain/$(CHAIN).mk else $(error Unsupported CHAIN - use $(SUPPORTED_CHAINS)) endif +CFLAGS += -DAPPNAME=\"$(APPNAME)\" ######### # Other # @@ -65,6 +66,12 @@ DEFINES += $(DEFINES_LIB) #prepare hsm generation ifeq ($(TARGET_NAME),TARGET_NANOS) ICONNAME=icons/nanos_app_$(CHAIN).gif +else ifeq ($(TARGET_NAME),TARGET_STAX) +ICONNAME=icons/stax_app_$(CHAIN).gif +DEFINES += ICONGLYPH=C_stax_$(CHAIN)_64px +DEFINES += ICONBITMAP=C_stax_$(CHAIN)_64px_bitmap +DEFINES += ICONGLYPH_SMALL=C_stax_$(CHAIN) +GLYPH_FILES += $(ICONNAME) else ICONNAME=icons/nanox_app_$(CHAIN).gif endif @@ -79,9 +86,10 @@ all: default ############ DEFINES += OS_IO_SEPROXYHAL -DEFINES += HAVE_BAGL HAVE_SPRINTF HAVE_SNPRINTF_FORMAT_U +DEFINES += HAVE_SPRINTF HAVE_SNPRINTF_FORMAT_U DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=4 IO_HID_EP_LENGTH=64 HAVE_USB_APDU DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) +DEFINES += BUILD_YEAR=\"$(shell date +%Y)\" # U2F DEFINES += HAVE_U2F HAVE_IO_U2F @@ -90,30 +98,41 @@ DEFINES += USB_SEGMENT_SIZE=64 DEFINES += BLE_SEGMENT_SIZE=32 #max MTU, min 20 DEFINES += UNUSED\(x\)=\(void\)x DEFINES += APPVERSION=\"$(APPVERSION)\" -DEFINES += HAVE_UX_FLOW #WEBUSB_URL = www.ledgerwallet.com #DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=$(shell echo -n $(WEBUSB_URL) | wc -c) WEBUSB_URL=$(shell echo -n $(WEBUSB_URL) | sed -e "s/./\\\'\0\\\',/g") DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" -ifeq ($(TARGET_NAME),TARGET_NANOX) +ifneq (,$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX)) DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE +SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl endif ifeq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72 -DEFINES += HAVE_WALLET_ID_SDK +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 else DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 +endif + +ifeq ($(TARGET_NAME),TARGET_STAX) +DEFINES += NBGL_QRCODE +else +DEFINES += HAVE_BAGL +DEFINES += HAVE_UX_FLOW +ifeq ($(TARGET_NAME),TARGET_NANOS) +DEFINES += HAVE_WALLET_ID_SDK +DEFINES += BAGL_WIDTH=128 BAGL_HEIGHT=32 +else DEFINES += HAVE_GLO096 -DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 +DEFINES += 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 endif +endif # Enables direct data signing without having to specify it in the settings. Useful when testing with speculos. ALLOW_DATA:=0 @@ -167,7 +186,6 @@ endif endif # Enabling debug PRINTF -DEBUG:=0 ifneq ($(DEBUG),0) DEFINES += HAVE_STACK_OVERFLOW_CHECK ifeq ($(TARGET_NAME),TARGET_NANOS) @@ -204,13 +222,11 @@ endif CC := $(CLANGPATH)clang -#CFLAGS += -O0 -CFLAGS += -Oz -Wno-format-invalid-specifier -Wno-format-extra-args +CFLAGS += -Wno-format-invalid-specifier -Wno-format-extra-args AS := $(GCCPATH)arm-none-eabi-gcc LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -O3 -Os LDLIBS += -lm -lgcc -lc # import rules to compile glyphs(/pone) @@ -219,11 +235,12 @@ 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_common src src_features src_plugins SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f +ifeq ($(TARGET_NAME),TARGET_STAX) +APP_SOURCE_PATH += src_nbgl +else SDK_SOURCE_PATH += lib_ux -ifeq ($(TARGET_NAME),TARGET_NANOX) -SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -endif APP_SOURCE_PATH += src_bagl +endif ### initialize plugin SDK submodule if needed, rebuild it, and warn if a difference is noticed ifeq ($(CHAIN),ethereum) diff --git a/glyphs/stax_akroma_64px.gif b/glyphs/stax_akroma_64px.gif new file mode 100644 index 0000000..d092851 Binary files /dev/null and b/glyphs/stax_akroma_64px.gif differ diff --git a/glyphs/stax_apothemnetwork_64px.gif b/glyphs/stax_apothemnetwork_64px.gif new file mode 100644 index 0000000..974f8bb Binary files /dev/null and b/glyphs/stax_apothemnetwork_64px.gif differ diff --git a/glyphs/stax_artis_sigma1_64px.gif b/glyphs/stax_artis_sigma1_64px.gif new file mode 100644 index 0000000..05c4f22 Binary files /dev/null and b/glyphs/stax_artis_sigma1_64px.gif differ diff --git a/glyphs/stax_artis_tau1_64px.gif b/glyphs/stax_artis_tau1_64px.gif new file mode 100644 index 0000000..87b527d Binary files /dev/null and b/glyphs/stax_artis_tau1_64px.gif differ diff --git a/glyphs/stax_astar_64px.gif b/glyphs/stax_astar_64px.gif new file mode 100644 index 0000000..60fb1e8 Binary files /dev/null and b/glyphs/stax_astar_64px.gif differ diff --git a/glyphs/stax_atheios_64px.gif b/glyphs/stax_atheios_64px.gif new file mode 100644 index 0000000..051e9c3 Binary files /dev/null and b/glyphs/stax_atheios_64px.gif differ diff --git a/glyphs/stax_bsc_64px.gif b/glyphs/stax_bsc_64px.gif new file mode 100644 index 0000000..956f73d Binary files /dev/null and b/glyphs/stax_bsc_64px.gif differ diff --git a/glyphs/stax_bttc_64px.gif b/glyphs/stax_bttc_64px.gif new file mode 100644 index 0000000..99e51ff Binary files /dev/null and b/glyphs/stax_bttc_64px.gif differ diff --git a/glyphs/stax_callisto_64px.gif b/glyphs/stax_callisto_64px.gif new file mode 100644 index 0000000..44a6b88 Binary files /dev/null and b/glyphs/stax_callisto_64px.gif differ diff --git a/glyphs/stax_conflux_espace_64px.gif b/glyphs/stax_conflux_espace_64px.gif new file mode 100644 index 0000000..d6f790d Binary files /dev/null and b/glyphs/stax_conflux_espace_64px.gif differ diff --git a/glyphs/stax_cube_64px.gif b/glyphs/stax_cube_64px.gif new file mode 100644 index 0000000..8186722 Binary files /dev/null and b/glyphs/stax_cube_64px.gif differ diff --git a/glyphs/stax_dexon_64px.gif b/glyphs/stax_dexon_64px.gif new file mode 100644 index 0000000..d37f6b2 Binary files /dev/null and b/glyphs/stax_dexon_64px.gif differ diff --git a/glyphs/stax_ellaism_64px.gif b/glyphs/stax_ellaism_64px.gif new file mode 100644 index 0000000..b785d48 Binary files /dev/null and b/glyphs/stax_ellaism_64px.gif differ diff --git a/glyphs/stax_energywebchain_64px.gif b/glyphs/stax_energywebchain_64px.gif new file mode 100644 index 0000000..5cd4c4d Binary files /dev/null and b/glyphs/stax_energywebchain_64px.gif differ diff --git a/glyphs/stax_ether1_64px.gif b/glyphs/stax_ether1_64px.gif new file mode 100644 index 0000000..ec41146 Binary files /dev/null and b/glyphs/stax_ether1_64px.gif differ diff --git a/glyphs/stax_ethereum_64px.gif b/glyphs/stax_ethereum_64px.gif new file mode 100644 index 0000000..48fa1ac Binary files /dev/null and b/glyphs/stax_ethereum_64px.gif differ diff --git a/glyphs/stax_ethereum_classic_64px.gif b/glyphs/stax_ethereum_classic_64px.gif new file mode 100644 index 0000000..5aeed55 Binary files /dev/null and b/glyphs/stax_ethereum_classic_64px.gif differ diff --git a/glyphs/stax_ethergem_64px.gif b/glyphs/stax_ethergem_64px.gif new file mode 100644 index 0000000..8bdb8e5 Binary files /dev/null and b/glyphs/stax_ethergem_64px.gif differ diff --git a/glyphs/stax_ethersocial_64px.gif b/glyphs/stax_ethersocial_64px.gif new file mode 100644 index 0000000..530fe55 Binary files /dev/null and b/glyphs/stax_ethersocial_64px.gif differ diff --git a/glyphs/stax_expanse_64px.gif b/glyphs/stax_expanse_64px.gif new file mode 100644 index 0000000..61698c8 Binary files /dev/null and b/glyphs/stax_expanse_64px.gif differ diff --git a/glyphs/stax_flare_64px.gif b/glyphs/stax_flare_64px.gif new file mode 100644 index 0000000..c834fcb Binary files /dev/null and b/glyphs/stax_flare_64px.gif differ diff --git a/glyphs/stax_flare_coston_64px.gif b/glyphs/stax_flare_coston_64px.gif new file mode 100644 index 0000000..28822f3 Binary files /dev/null and b/glyphs/stax_flare_coston_64px.gif differ diff --git a/glyphs/stax_gochain_64px.gif b/glyphs/stax_gochain_64px.gif new file mode 100644 index 0000000..aec56d0 Binary files /dev/null and b/glyphs/stax_gochain_64px.gif differ diff --git a/glyphs/stax_goerli_64px.gif b/glyphs/stax_goerli_64px.gif new file mode 100644 index 0000000..3c48394 Binary files /dev/null and b/glyphs/stax_goerli_64px.gif differ diff --git a/glyphs/stax_hpb_64px.gif b/glyphs/stax_hpb_64px.gif new file mode 100644 index 0000000..392151a Binary files /dev/null and b/glyphs/stax_hpb_64px.gif differ diff --git a/glyphs/stax_id4good_64px.gif b/glyphs/stax_id4good_64px.gif new file mode 100644 index 0000000..6d04b22 Binary files /dev/null and b/glyphs/stax_id4good_64px.gif differ diff --git a/glyphs/stax_kardiachain_64px.gif b/glyphs/stax_kardiachain_64px.gif new file mode 100644 index 0000000..66b6e6f Binary files /dev/null and b/glyphs/stax_kardiachain_64px.gif differ diff --git a/glyphs/stax_kusd_64px.gif b/glyphs/stax_kusd_64px.gif new file mode 100644 index 0000000..b187306 Binary files /dev/null and b/glyphs/stax_kusd_64px.gif differ diff --git a/glyphs/stax_meter_64px.gif b/glyphs/stax_meter_64px.gif new file mode 100644 index 0000000..ee0804b Binary files /dev/null and b/glyphs/stax_meter_64px.gif differ diff --git a/glyphs/stax_mix_64px.gif b/glyphs/stax_mix_64px.gif new file mode 100644 index 0000000..e3a3e57 Binary files /dev/null and b/glyphs/stax_mix_64px.gif differ diff --git a/glyphs/stax_moonbeam_64px.gif b/glyphs/stax_moonbeam_64px.gif new file mode 100644 index 0000000..e8485ee Binary files /dev/null and b/glyphs/stax_moonbeam_64px.gif differ diff --git a/glyphs/stax_moonriver_64px.gif b/glyphs/stax_moonriver_64px.gif new file mode 100644 index 0000000..1809857 Binary files /dev/null and b/glyphs/stax_moonriver_64px.gif differ diff --git a/glyphs/stax_multivac_64px.gif b/glyphs/stax_multivac_64px.gif new file mode 100644 index 0000000..e3ea716 Binary files /dev/null and b/glyphs/stax_multivac_64px.gif differ diff --git a/glyphs/stax_musicoin_64px.gif b/glyphs/stax_musicoin_64px.gif new file mode 100644 index 0000000..d9dce6c Binary files /dev/null and b/glyphs/stax_musicoin_64px.gif differ diff --git a/glyphs/stax_oasys_64px.gif b/glyphs/stax_oasys_64px.gif new file mode 100644 index 0000000..81c0e5f Binary files /dev/null and b/glyphs/stax_oasys_64px.gif differ diff --git a/glyphs/stax_okc_64px.gif b/glyphs/stax_okc_64px.gif new file mode 100644 index 0000000..f377521 Binary files /dev/null and b/glyphs/stax_okc_64px.gif differ diff --git a/glyphs/stax_pirl_64px.gif b/glyphs/stax_pirl_64px.gif new file mode 100644 index 0000000..7c97331 Binary files /dev/null and b/glyphs/stax_pirl_64px.gif differ diff --git a/glyphs/stax_poa_64px.gif b/glyphs/stax_poa_64px.gif new file mode 100644 index 0000000..d857c17 Binary files /dev/null and b/glyphs/stax_poa_64px.gif differ diff --git a/glyphs/stax_polygon_64px.gif b/glyphs/stax_polygon_64px.gif new file mode 100644 index 0000000..40b99e1 Binary files /dev/null and b/glyphs/stax_polygon_64px.gif differ diff --git a/glyphs/stax_reosc_64px.gif b/glyphs/stax_reosc_64px.gif new file mode 100644 index 0000000..fa727e3 Binary files /dev/null and b/glyphs/stax_reosc_64px.gif differ diff --git a/glyphs/stax_ropsten_64px.gif b/glyphs/stax_ropsten_64px.gif new file mode 100644 index 0000000..6025aff Binary files /dev/null and b/glyphs/stax_ropsten_64px.gif differ diff --git a/glyphs/stax_rsk_64px.gif b/glyphs/stax_rsk_64px.gif new file mode 100644 index 0000000..5e25ff6 Binary files /dev/null and b/glyphs/stax_rsk_64px.gif differ diff --git a/glyphs/stax_rsk_testnet_64px.gif b/glyphs/stax_rsk_testnet_64px.gif new file mode 100644 index 0000000..5e25ff6 Binary files /dev/null and b/glyphs/stax_rsk_testnet_64px.gif differ diff --git a/glyphs/stax_shiden_64px.gif b/glyphs/stax_shiden_64px.gif new file mode 100644 index 0000000..6bbe136 Binary files /dev/null and b/glyphs/stax_shiden_64px.gif differ diff --git a/glyphs/stax_shyft_64px.gif b/glyphs/stax_shyft_64px.gif new file mode 100644 index 0000000..78a7ea9 Binary files /dev/null and b/glyphs/stax_shyft_64px.gif differ diff --git a/glyphs/stax_songbird_64px.gif b/glyphs/stax_songbird_64px.gif new file mode 100644 index 0000000..e635b95 Binary files /dev/null and b/glyphs/stax_songbird_64px.gif differ diff --git a/glyphs/stax_tecracoin_64px.gif b/glyphs/stax_tecracoin_64px.gif new file mode 100644 index 0000000..4fa9b49 Binary files /dev/null and b/glyphs/stax_tecracoin_64px.gif differ diff --git a/glyphs/stax_tecratestnet_64px.gif b/glyphs/stax_tecratestnet_64px.gif new file mode 100644 index 0000000..4fa9b49 Binary files /dev/null and b/glyphs/stax_tecratestnet_64px.gif differ diff --git a/glyphs/stax_thundercore_64px.gif b/glyphs/stax_thundercore_64px.gif new file mode 100644 index 0000000..76134be Binary files /dev/null and b/glyphs/stax_thundercore_64px.gif differ diff --git a/glyphs/stax_tobalaba_64px.gif b/glyphs/stax_tobalaba_64px.gif new file mode 100644 index 0000000..0125e6b Binary files /dev/null and b/glyphs/stax_tobalaba_64px.gif differ diff --git a/glyphs/stax_tomochain_64px.gif b/glyphs/stax_tomochain_64px.gif new file mode 100644 index 0000000..6abef33 Binary files /dev/null and b/glyphs/stax_tomochain_64px.gif differ diff --git a/glyphs/stax_ubiq_64px.gif b/glyphs/stax_ubiq_64px.gif new file mode 100644 index 0000000..740b7e9 Binary files /dev/null and b/glyphs/stax_ubiq_64px.gif differ diff --git a/glyphs/stax_volta_64px.gif b/glyphs/stax_volta_64px.gif new file mode 100644 index 0000000..83cfb91 Binary files /dev/null and b/glyphs/stax_volta_64px.gif differ diff --git a/glyphs/stax_wanchain_64px.gif b/glyphs/stax_wanchain_64px.gif new file mode 100644 index 0000000..27a5582 Binary files /dev/null and b/glyphs/stax_wanchain_64px.gif differ diff --git a/glyphs/stax_webchain_64px.gif b/glyphs/stax_webchain_64px.gif new file mode 100644 index 0000000..585832f Binary files /dev/null and b/glyphs/stax_webchain_64px.gif differ diff --git a/glyphs/stax_wethio_64px.gif b/glyphs/stax_wethio_64px.gif new file mode 100644 index 0000000..8a66b12 Binary files /dev/null and b/glyphs/stax_wethio_64px.gif differ diff --git a/glyphs/stax_xdcnetwork_64px.gif b/glyphs/stax_xdcnetwork_64px.gif new file mode 100644 index 0000000..b990ab3 Binary files /dev/null and b/glyphs/stax_xdcnetwork_64px.gif differ diff --git a/icons/stax_app_akroma.gif b/icons/stax_app_akroma.gif new file mode 100644 index 0000000..7976866 Binary files /dev/null and b/icons/stax_app_akroma.gif differ diff --git a/icons/stax_app_apothemnetwork.gif b/icons/stax_app_apothemnetwork.gif new file mode 100644 index 0000000..381f070 Binary files /dev/null and b/icons/stax_app_apothemnetwork.gif differ diff --git a/icons/stax_app_artis_sigma1.gif b/icons/stax_app_artis_sigma1.gif new file mode 100644 index 0000000..8d769db Binary files /dev/null and b/icons/stax_app_artis_sigma1.gif differ diff --git a/icons/stax_app_artis_tau1.gif b/icons/stax_app_artis_tau1.gif new file mode 100644 index 0000000..555be4a Binary files /dev/null and b/icons/stax_app_artis_tau1.gif differ diff --git a/icons/stax_app_astar.gif b/icons/stax_app_astar.gif new file mode 100644 index 0000000..458e519 Binary files /dev/null and b/icons/stax_app_astar.gif differ diff --git a/icons/stax_app_atheios.gif b/icons/stax_app_atheios.gif new file mode 100644 index 0000000..10a3e3b Binary files /dev/null and b/icons/stax_app_atheios.gif differ diff --git a/icons/stax_app_bsc.gif b/icons/stax_app_bsc.gif new file mode 100644 index 0000000..311d31a Binary files /dev/null and b/icons/stax_app_bsc.gif differ diff --git a/icons/stax_app_bttc.gif b/icons/stax_app_bttc.gif new file mode 100644 index 0000000..cc8a3f9 Binary files /dev/null and b/icons/stax_app_bttc.gif differ diff --git a/icons/stax_app_callisto.gif b/icons/stax_app_callisto.gif new file mode 100644 index 0000000..890dd9f Binary files /dev/null and b/icons/stax_app_callisto.gif differ diff --git a/icons/stax_app_conflux_espace.gif b/icons/stax_app_conflux_espace.gif new file mode 100644 index 0000000..e39f442 Binary files /dev/null and b/icons/stax_app_conflux_espace.gif differ diff --git a/icons/stax_app_cube.gif b/icons/stax_app_cube.gif new file mode 100644 index 0000000..1a5b3c8 Binary files /dev/null and b/icons/stax_app_cube.gif differ diff --git a/icons/stax_app_dexon.gif b/icons/stax_app_dexon.gif new file mode 100644 index 0000000..692537f Binary files /dev/null and b/icons/stax_app_dexon.gif differ diff --git a/icons/stax_app_ellaism.gif b/icons/stax_app_ellaism.gif new file mode 100644 index 0000000..ac5ef85 Binary files /dev/null and b/icons/stax_app_ellaism.gif differ diff --git a/icons/stax_app_energywebchain.gif b/icons/stax_app_energywebchain.gif new file mode 100644 index 0000000..3f8a574 Binary files /dev/null and b/icons/stax_app_energywebchain.gif differ diff --git a/icons/stax_app_ether1.gif b/icons/stax_app_ether1.gif new file mode 100644 index 0000000..1f46988 Binary files /dev/null and b/icons/stax_app_ether1.gif differ diff --git a/icons/stax_app_ethereum.gif b/icons/stax_app_ethereum.gif new file mode 100644 index 0000000..81db45c Binary files /dev/null and b/icons/stax_app_ethereum.gif differ diff --git a/icons/stax_app_ethereum_classic.gif b/icons/stax_app_ethereum_classic.gif new file mode 100644 index 0000000..10a661d Binary files /dev/null and b/icons/stax_app_ethereum_classic.gif differ diff --git a/icons/stax_app_ethergem.gif b/icons/stax_app_ethergem.gif new file mode 100644 index 0000000..0ed7b1c Binary files /dev/null and b/icons/stax_app_ethergem.gif differ diff --git a/icons/stax_app_ethersocial.gif b/icons/stax_app_ethersocial.gif new file mode 100644 index 0000000..e8fc4dd Binary files /dev/null and b/icons/stax_app_ethersocial.gif differ diff --git a/icons/stax_app_expanse.gif b/icons/stax_app_expanse.gif new file mode 100644 index 0000000..9dfd870 Binary files /dev/null and b/icons/stax_app_expanse.gif differ diff --git a/icons/stax_app_flare.gif b/icons/stax_app_flare.gif new file mode 100644 index 0000000..4d1a0f8 Binary files /dev/null and b/icons/stax_app_flare.gif differ diff --git a/icons/stax_app_flare_coston.gif b/icons/stax_app_flare_coston.gif new file mode 100644 index 0000000..2f77dee Binary files /dev/null and b/icons/stax_app_flare_coston.gif differ diff --git a/icons/stax_app_gochain.gif b/icons/stax_app_gochain.gif new file mode 100644 index 0000000..04bb08d Binary files /dev/null and b/icons/stax_app_gochain.gif differ diff --git a/icons/stax_app_goerli.gif b/icons/stax_app_goerli.gif new file mode 100644 index 0000000..95c819d Binary files /dev/null and b/icons/stax_app_goerli.gif differ diff --git a/icons/stax_app_hpb.gif b/icons/stax_app_hpb.gif new file mode 100644 index 0000000..4c4e50d Binary files /dev/null and b/icons/stax_app_hpb.gif differ diff --git a/icons/stax_app_id4good.gif b/icons/stax_app_id4good.gif new file mode 100644 index 0000000..ae3fae4 Binary files /dev/null and b/icons/stax_app_id4good.gif differ diff --git a/icons/stax_app_kardiachain.gif b/icons/stax_app_kardiachain.gif new file mode 100644 index 0000000..0c092ff Binary files /dev/null and b/icons/stax_app_kardiachain.gif differ diff --git a/icons/stax_app_kusd.gif b/icons/stax_app_kusd.gif new file mode 100644 index 0000000..18b1a1c Binary files /dev/null and b/icons/stax_app_kusd.gif differ diff --git a/icons/stax_app_meter.gif b/icons/stax_app_meter.gif new file mode 100644 index 0000000..d07813d Binary files /dev/null and b/icons/stax_app_meter.gif differ diff --git a/icons/stax_app_mix.gif b/icons/stax_app_mix.gif new file mode 100644 index 0000000..f82aafc Binary files /dev/null and b/icons/stax_app_mix.gif differ diff --git a/icons/stax_app_moonbeam.gif b/icons/stax_app_moonbeam.gif new file mode 100644 index 0000000..c8d2c9b Binary files /dev/null and b/icons/stax_app_moonbeam.gif differ diff --git a/icons/stax_app_moonriver.gif b/icons/stax_app_moonriver.gif new file mode 100644 index 0000000..69466a7 Binary files /dev/null and b/icons/stax_app_moonriver.gif differ diff --git a/icons/stax_app_multivac.gif b/icons/stax_app_multivac.gif new file mode 100644 index 0000000..ad33059 Binary files /dev/null and b/icons/stax_app_multivac.gif differ diff --git a/icons/stax_app_musicoin.gif b/icons/stax_app_musicoin.gif new file mode 100644 index 0000000..eced2ed Binary files /dev/null and b/icons/stax_app_musicoin.gif differ diff --git a/icons/stax_app_oasys.gif b/icons/stax_app_oasys.gif new file mode 100644 index 0000000..83995c6 Binary files /dev/null and b/icons/stax_app_oasys.gif differ diff --git a/icons/stax_app_okc.gif b/icons/stax_app_okc.gif new file mode 100644 index 0000000..38cfa3c Binary files /dev/null and b/icons/stax_app_okc.gif differ diff --git a/icons/stax_app_pirl.gif b/icons/stax_app_pirl.gif new file mode 100644 index 0000000..6ac7154 Binary files /dev/null and b/icons/stax_app_pirl.gif differ diff --git a/icons/stax_app_poa.gif b/icons/stax_app_poa.gif new file mode 100644 index 0000000..afa1186 Binary files /dev/null and b/icons/stax_app_poa.gif differ diff --git a/icons/stax_app_polygon.gif b/icons/stax_app_polygon.gif new file mode 100644 index 0000000..eba2eaa Binary files /dev/null and b/icons/stax_app_polygon.gif differ diff --git a/icons/stax_app_reosc.gif b/icons/stax_app_reosc.gif new file mode 100644 index 0000000..50fe3fb Binary files /dev/null and b/icons/stax_app_reosc.gif differ diff --git a/icons/stax_app_ropsten.gif b/icons/stax_app_ropsten.gif new file mode 100644 index 0000000..4c7a5a0 Binary files /dev/null and b/icons/stax_app_ropsten.gif differ diff --git a/icons/stax_app_rsk.gif b/icons/stax_app_rsk.gif new file mode 100644 index 0000000..a576134 Binary files /dev/null and b/icons/stax_app_rsk.gif differ diff --git a/icons/stax_app_rsk_testnet.gif b/icons/stax_app_rsk_testnet.gif new file mode 100644 index 0000000..a576134 Binary files /dev/null and b/icons/stax_app_rsk_testnet.gif differ diff --git a/icons/stax_app_shiden.gif b/icons/stax_app_shiden.gif new file mode 100644 index 0000000..1322309 Binary files /dev/null and b/icons/stax_app_shiden.gif differ diff --git a/icons/stax_app_shyft.gif b/icons/stax_app_shyft.gif new file mode 100644 index 0000000..0741edd Binary files /dev/null and b/icons/stax_app_shyft.gif differ diff --git a/icons/stax_app_songbird.gif b/icons/stax_app_songbird.gif new file mode 100644 index 0000000..811d72a Binary files /dev/null and b/icons/stax_app_songbird.gif differ diff --git a/icons/stax_app_tecracoin.gif b/icons/stax_app_tecracoin.gif new file mode 100644 index 0000000..a1ccb2d Binary files /dev/null and b/icons/stax_app_tecracoin.gif differ diff --git a/icons/stax_app_tecratestnet.gif b/icons/stax_app_tecratestnet.gif new file mode 100644 index 0000000..a1ccb2d Binary files /dev/null and b/icons/stax_app_tecratestnet.gif differ diff --git a/icons/stax_app_thundercore.gif b/icons/stax_app_thundercore.gif new file mode 100644 index 0000000..4d730a1 Binary files /dev/null and b/icons/stax_app_thundercore.gif differ diff --git a/icons/stax_app_tobalaba.gif b/icons/stax_app_tobalaba.gif new file mode 100644 index 0000000..cd3e946 Binary files /dev/null and b/icons/stax_app_tobalaba.gif differ diff --git a/icons/stax_app_tomochain.gif b/icons/stax_app_tomochain.gif new file mode 100644 index 0000000..eb8d01a Binary files /dev/null and b/icons/stax_app_tomochain.gif differ diff --git a/icons/stax_app_ubiq.gif b/icons/stax_app_ubiq.gif new file mode 100644 index 0000000..afd0577 Binary files /dev/null and b/icons/stax_app_ubiq.gif differ diff --git a/icons/stax_app_volta.gif b/icons/stax_app_volta.gif new file mode 100644 index 0000000..ea4c024 Binary files /dev/null and b/icons/stax_app_volta.gif differ diff --git a/icons/stax_app_wanchain.gif b/icons/stax_app_wanchain.gif new file mode 100644 index 0000000..cf62804 Binary files /dev/null and b/icons/stax_app_wanchain.gif differ diff --git a/icons/stax_app_webchain.gif b/icons/stax_app_webchain.gif new file mode 100644 index 0000000..3deecb3 Binary files /dev/null and b/icons/stax_app_webchain.gif differ diff --git a/icons/stax_app_wethio.gif b/icons/stax_app_wethio.gif new file mode 100644 index 0000000..4b54030 Binary files /dev/null and b/icons/stax_app_wethio.gif differ diff --git a/icons/stax_app_xdcnetwork.gif b/icons/stax_app_xdcnetwork.gif new file mode 100644 index 0000000..2cd15b2 Binary files /dev/null and b/icons/stax_app_xdcnetwork.gif differ diff --git a/src/common_ui.h b/src/common_ui.h index 9b2b0dc..e14f57b 100644 --- a/src/common_ui.h +++ b/src/common_ui.h @@ -9,7 +9,6 @@ void ui_display_public_eth2(void); void ui_display_privacy_public_key(void); void ui_display_privacy_shared_secret(void); void ui_display_public_key(void); -void ui_display_sign(void); void ui_sign_712_v0(void); void ui_display_stark_public(void); void ui_confirm_selector(void); diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index 3aa6883..29e965f 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -64,7 +64,7 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI, } queryContractUI->screenIndex = screenIndex; - strlcpy(queryContractUI->network_ticker, get_network_ticker(), MAX_TICKER_LEN); + strlcpy(queryContractUI->network_ticker, get_tx_network_ticker(), MAX_TICKER_LEN); queryContractUI->title = title; queryContractUI->titleLength = titleLength; queryContractUI->msg = msg; diff --git a/src/eth_plugin_internal.h b/src/eth_plugin_internal.h index 692c9b6..2d2ca03 100644 --- a/src/eth_plugin_internal.h +++ b/src/eth_plugin_internal.h @@ -7,7 +7,6 @@ #define SELECTOR_SIZE 4 #define PARAMETER_LENGTH 32 -#define RUN_APPLICATION 1 void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size); diff --git a/src/handle_swap_sign_transaction.c b/src/handle_swap_sign_transaction.c index 39268f5..7eb0e27 100644 --- a/src/handle_swap_sign_transaction.c +++ b/src/handle_swap_sign_transaction.c @@ -67,7 +67,13 @@ void handle_swap_sign_transaction(chain_config_t* config) { nvm_write((void*) &N_storage, (void*) &storage, sizeof(internalStorage_t)); } +#ifdef HAVE_BAGL UX_INIT(); +#endif // HAVE_BAGL +#ifdef HAVE_NBGL + nbgl_objInit(); +#endif // HAVE_NBGL + USB_power(0); USB_power(1); // ui_idle(); diff --git a/src/main.c b/src/main.c index d66dbb1..08113ae 100644 --- a/src/main.c +++ b/src/main.c @@ -69,7 +69,8 @@ bolos_ux_params_t G_ux_params; const internalStorage_t N_storage_real; -chain_config_t *chainConfig; +caller_app_t *caller_app = NULL; +chain_config_t *chainConfig = NULL; void reset_app_context() { // PRINTF("!!RESET_APP_CONTEXT\n"); @@ -877,9 +878,11 @@ void app_main(void) { } // override point, but nothing more to do +#ifdef HAVE_BAGL void io_seproxyhal_display(const bagl_element_t *element) { io_seproxyhal_display_default((bagl_element_t *) element); } +#endif unsigned char io_event(__attribute__((unused)) unsigned char channel) { // nothing done with the event, throw an error on the transport layer if @@ -890,10 +893,11 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { case SEPROXYHAL_TAG_FINGER_EVENT: UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); break; - +#ifdef HAVE_BAGL case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); break; +#endif // HAVE_BAGL case SEPROXYHAL_TAG_STATUS_EVENT: if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && @@ -901,22 +905,23 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { THROW(EXCEPTION_IO_RESET); } - // no break is intentional + __attribute__((fallthrough)); default: UX_DEFAULT_EVENT(); break; case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: +#ifdef HAVE_BAGL UX_DISPLAYED_EVENT({}); +#endif // HAVE_BAGL +#ifdef HAVE_NBGL + UX_DEFAULT_EVENT(); +#endif // HAVE_NBGL break; -#if 0 - case SEPROXYHAL_TAG_TICKER_EVENT: - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, - { - }); - break; -#endif + case SEPROXYHAL_TAG_TICKER_EVENT: + UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {}); + break; } // close the event if not done previously (by a display or whatever) @@ -946,19 +951,35 @@ void init_coin_config(chain_config_t *coin_config) { coin_config->kind = CHAIN_KIND; } -void coin_main(chain_config_t *coin_config) { +void coin_main(libargs_t *args) { chain_config_t config; - if (coin_config == NULL) { + if (args) { + if (args->chain_config != NULL) { + chainConfig = args->chain_config; + } + if ((caller_app = args->caller_app) != NULL) { + if (chainConfig != NULL) { + caller_app->type = CALLER_TYPE_CLONE; + } else { + caller_app->type = CALLER_TYPE_PLUGIN; + } + } + } + if (chainConfig == NULL) { init_coin_config(&config); chainConfig = &config; - } else { - chainConfig = coin_config; } + reset_app_context(); tmpCtx.transactionContext.currentItemIndex = 0; for (;;) { +#ifdef HAVE_BAGL UX_INIT(); +#endif // HAVE_BAGL +#ifdef HAVE_NBGL + nbgl_objInit(); +#endif // HAVE_NBGL BEGIN_TRY { TRY { @@ -1022,18 +1043,7 @@ void coin_main(chain_config_t *coin_config) { app_exit(); } -struct libargs_s { - unsigned int id; - unsigned int command; - chain_config_t *chain_config; - union { - check_address_parameters_t *check_address; - create_transaction_parameters_t *create_transaction; - get_printable_amount_parameters_t *get_printable_amount; - }; -}; - -static void library_main_helper(struct libargs_s *args) { +static void library_main_helper(libargs_t *args) { check_api_level(CX_COMPAT_APILEVEL); PRINTF("Inside a library \n"); switch (args->command) { @@ -1061,7 +1071,7 @@ static void library_main_helper(struct libargs_s *args) { } } -void library_main(struct libargs_s *args) { +void library_main(libargs_t *args) { chain_config_t coin_config; if (args->chain_config == NULL) { init_coin_config(&coin_config); @@ -1093,6 +1103,7 @@ __attribute__((section(".boot"))) int main(int arg0) { unsigned int libcall_params[5]; chain_config_t local_chainConfig; init_coin_config(&local_chainConfig); + PRINTF("Hello from Eth-clone\n"); check_api_level(CX_COMPAT_APILEVEL); // delegate to Ethereum app/lib @@ -1100,7 +1111,22 @@ __attribute__((section(".boot"))) int main(int arg0) { libcall_params[1] = 0x100; libcall_params[2] = RUN_APPLICATION; libcall_params[3] = (unsigned int) &local_chainConfig; - libcall_params[4] = 0; +#ifdef HAVE_NBGL + const char app_name[] = APPNAME; + caller_app_t capp; + nbgl_icon_details_t icon_details; + uint8_t bitmap[sizeof(ICONBITMAP)]; + + memcpy(&icon_details, &ICONGLYPH, sizeof(ICONGLYPH)); + memcpy(&bitmap, &ICONBITMAP, sizeof(bitmap)); + icon_details.bitmap = (const uint8_t *) bitmap; + capp.name = app_name; + capp.icon = &icon_details; + libcall_params[4] = (unsigned int) &capp; +#else + libcall_params[4] = NULL; +#endif // HAVE_NBGL + if (arg0) { // call as a library libcall_params[2] = ((unsigned int *) arg0)[1]; @@ -1132,7 +1158,7 @@ __attribute__((section(".boot"))) int main(int arg0) { return 0; } - struct libargs_s *args = (struct libargs_s *) arg0; + libargs_t *args = (libargs_t *) arg0; if (args->id != 0x100) { app_exit(); return 0; @@ -1140,7 +1166,7 @@ __attribute__((section(".boot"))) int main(int arg0) { switch (args->command) { case RUN_APPLICATION: // called as ethereum from altcoin or plugin - coin_main(args->chain_config); + coin_main(args); break; default: // called as ethereum or altcoin library diff --git a/src/shared_context.h b/src/shared_context.h index ce907dd..c2fc66a 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -7,6 +7,9 @@ #include "tokens.h" #include "chainConfig.h" #include "nft.h" +#ifdef HAVE_NBGL +#include "nbgl_types.h" +#endif #define MAX_BIP32_PATH 10 @@ -221,6 +224,16 @@ typedef enum { extern pluginType_t pluginType; +typedef enum { CALLER_TYPE_CLONE, CALLER_TYPE_PLUGIN } e_caller_type; + +typedef struct caller_app_t { + const char *name; +#ifdef HAVE_NBGL + const nbgl_icon_details_t *icon; +#endif + char type; // does not have to be set by the caller app +} caller_app_t; + extern uint8_t appState; #ifdef HAVE_STARKWARE extern bool quantumSet; @@ -229,6 +242,8 @@ extern bool quantumSet; extern uint32_t eth2WithdrawalIndex; #endif +extern caller_app_t *caller_app; + void reset_app_context(void); const uint8_t *parseBip32(const uint8_t *dataBuffer, uint8_t *dataLength, bip32_path_t *bip32); diff --git a/src/swap_lib_calls.h b/src/swap_lib_calls.h index 9bdb115..4fd0284 100644 --- a/src/swap_lib_calls.h +++ b/src/swap_lib_calls.h @@ -2,6 +2,8 @@ #define _SWAP_LIB_CALLS_H_ #include "stdbool.h" +#include "chainConfig.h" +#include "shared_context.h" #define RUN_APPLICATION 1 @@ -52,4 +54,16 @@ typedef struct create_transaction_parameters_s { const char* const destination_address_extra_id; } create_transaction_parameters_t; +typedef struct libargs_s { + unsigned int id; + unsigned int command; + chain_config_t* chain_config; + union { + check_address_parameters_t* check_address; + create_transaction_parameters_t* create_transaction; + get_printable_amount_parameters_t* get_printable_amount; + caller_app_t* caller_app; + }; +} libargs_t; + #endif // _SWAP_LIB_CALLS_H_ diff --git a/src/ui_callbacks.h b/src/ui_callbacks.h index 2035c27..041f717 100644 --- a/src/ui_callbacks.h +++ b/src/ui_callbacks.h @@ -4,6 +4,10 @@ #include "shared_context.h" #include "ux.h" +#ifdef HAVE_NBGL +typedef int bagl_element_t; +#endif + unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e); unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e); unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e); diff --git a/src_bagl/ui_flow_signTx.c b/src_bagl/ui_flow_signTx.c index 2b55922..c60f46d 100644 --- a/src_bagl/ui_flow_signTx.c +++ b/src_bagl/ui_flow_signTx.c @@ -220,7 +220,7 @@ void ux_approve_tx(bool fromPlugin) { // We're in a regular transaction, just show the amount and the address ux_approval_tx_flow[step++] = &ux_approval_amount_step; #ifdef HAVE_DOMAIN_NAME - uint64_t chain_id = get_chain_id(); + uint64_t chain_id = get_tx_chain_id(); if (has_domain_name(&chain_id, tmpContent.txContent.destination)) { ux_approval_tx_flow[step++] = &ux_domain_name_step; if (N_storage.verbose_domain_name) { @@ -238,7 +238,7 @@ void ux_approve_tx(bool fromPlugin) { ux_approval_tx_flow[step++] = &ux_approval_nonce_step; } - uint64_t chain_id = get_chain_id(); + uint64_t chain_id = get_tx_chain_id(); if (chainConfig->chainId == ETHEREUM_MAINNET_CHAINID && chain_id != chainConfig->chainId) { ux_approval_tx_flow[step++] = &ux_approval_network_step; } diff --git a/src_common/network.c b/src_common/network.c index f0dc4d1..dbb8f6b 100644 --- a/src_common/network.c +++ b/src_common/network.c @@ -7,6 +7,8 @@ #include "shared_context.h" #include "utils.h" +typedef enum { APP, TX } e_net_type; + // Mappping of chain ids to networks. static const network_info_t NETWORK_MAPPING[] = { {.chain_id = 1, .name = "Ethereum", .ticker = "ETH"}, @@ -64,7 +66,7 @@ static const network_info_t NETWORK_MAPPING[] = { {.chain_id = 39797, .name = "Energi", .ticker = "NRG"}, {.chain_id = 248, .name = "Oasys", .ticker = "OAS"}}; -uint64_t get_chain_id(void) { +uint64_t get_tx_chain_id(void) { uint64_t chain_id = 0; switch (txContext.txType) { @@ -83,8 +85,16 @@ uint64_t get_chain_id(void) { return chain_id; } -const network_info_t *get_network(void) { - uint64_t chain_id = get_chain_id(); +uint64_t get_app_chain_id(void) { + return chainConfig->chainId; +} + +static uint64_t get_chain_id(e_net_type type) { + return (type == APP) ? get_app_chain_id() : get_tx_chain_id(); +} + +static const network_info_t *get_network(e_net_type type) { + uint64_t chain_id = get_chain_id(type); for (size_t i = 0; i < sizeof(NETWORK_MAPPING) / sizeof(*NETWORK_MAPPING); i++) { if (NETWORK_MAPPING[i].chain_id == chain_id) { return (const network_info_t *) PIC(&NETWORK_MAPPING[i]); @@ -93,8 +103,8 @@ const network_info_t *get_network(void) { return NULL; } -const char *get_network_name(void) { - const network_info_t *network = get_network(); +static const char *get_network_name(e_net_type type) { + const network_info_t *network = get_network(type); if (network == NULL) { return NULL; } else { @@ -102,11 +112,27 @@ const char *get_network_name(void) { } } -const char *get_network_ticker(void) { - const network_info_t *network = get_network(); +const char *get_app_network_name(void) { + return get_network_name(APP); +} + +const char *get_tx_network_name(void) { + return get_network_name(TX); +} + +static const char *get_network_ticker(e_net_type type) { + const network_info_t *network = get_network(type); if (network == NULL) { return chainConfig->coinName; } else { return (char *) PIC(network->ticker); } } + +const char *get_app_network_ticker(void) { + return get_network_ticker(APP); +} + +const char *get_tx_network_ticker(void) { + return get_network_ticker(TX); +} diff --git a/src_common/network.h b/src_common/network.h index b9ce7f3..a2ce262 100644 --- a/src_common/network.h +++ b/src_common/network.h @@ -11,13 +11,15 @@ typedef struct network_info_s { uint64_t chain_id; } network_info_t; -// Returns the current chain id. Defaults to 0 if txType was not found. -uint64_t get_chain_id(void); -// Returns a pointer to the network struct, or NULL if there is none. -const network_info_t *get_network(void); +// Returns the chain ID. Defaults to 0 if txType was not found (For TX). +uint64_t get_tx_chain_id(void); +uint64_t get_app_chain_id(void); // Returns a pointer to the network name, or NULL if there is none. -const char *get_network_name(void); +const char *get_tx_network_name(void); +const char *get_app_network_name(void); + // Returns a pointer to the network ticker, or chainConfig->coinName if there is none. -const char *get_network_ticker(void); +const char *get_tx_network_ticker(void); +const char *get_app_network_ticker(void); #endif // _NETWORK_H_ diff --git a/src_features/signMessageEIP712/context_712.c b/src_features/signMessageEIP712/context_712.c index 82abd09..57d5be6 100644 --- a/src_features/signMessageEIP712/context_712.c +++ b/src_features/signMessageEIP712/context_712.c @@ -72,7 +72,6 @@ void eip712_context_deinit(void) { mem_reset(); eip712_context = NULL; reset_app_context(); - ui_idle(); } #endif diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 199ccbb..c3074db 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -195,7 +195,7 @@ static void address_to_string(uint8_t *in, } static void raw_fee_to_string(uint256_t *rawFee, char *displayBuffer, uint32_t displayBufferSize) { - const char *feeTicker = get_network_ticker(); + const char *feeTicker = get_tx_network_ticker(); uint8_t tickerOffset = 0; uint32_t i; @@ -262,10 +262,10 @@ static void nonce_to_string(const txInt256_t *nonce, char *out, size_t out_size) } static void get_network_as_string(char *out, size_t out_size) { - const char *name = get_network_name(); + const char *name = get_tx_network_name(); if (name == NULL) { // No network name found so simply copy the chain ID as the network name. - uint64_t chain_id = get_chain_id(); + uint64_t chain_id = get_tx_chain_id(); u64_to_string(chain_id, out, out_size); } else { // Network name found, simply copy it. @@ -312,13 +312,13 @@ static int strcasecmp_workaround(const char *str1, const char *str2) { void finalizeParsing(bool direct) { char displayBuffer[50]; uint8_t decimals = WEI_TO_ETHER; - const char *ticker = get_network_ticker(); + const char *ticker = get_tx_network_ticker(); ethPluginFinalize_t pluginFinalize; bool use_standard_UI = true; // Verify the chain if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) { - uint64_t id = get_chain_id(); + uint64_t id = get_tx_chain_id(); if (chainConfig->chainId != id) { PRINTF("Invalid chainID %u expected %u\n", id, chainConfig->chainId); diff --git a/src_nbgl/ui_712_common.c b/src_nbgl/ui_712_common.c new file mode 100644 index 0000000..a1b13e3 --- /dev/null +++ b/src_nbgl/ui_712_common.c @@ -0,0 +1,48 @@ +#include "ui_nbgl.h" +#include "ui_712_common.h" +#include "common_712.h" + +static void (*g_resume_func)(void) = NULL; + +void nbgl_712_review_approve(void) { + ui_712_approve_cb(NULL); +} + +void nbgl_712_review_reject(void) { + ui_712_reject_cb(NULL); +} + +void nbgl_712_confirm_rejection_cb(bool confirm) { + if (confirm) { + nbgl_useCaseStatus("Message signing\ncancelled", false, nbgl_712_review_reject); + } else { + (*g_resume_func)(); + } +} + +void nbgl_712_confirm_rejection(void) { + nbgl_useCaseChoice(&C_warning64px, + "Reject message?", + NULL, + "Yes, reject", + "Go back to message", + nbgl_712_confirm_rejection_cb); +} + +void nbgl_712_review_choice(bool confirm) { + if (confirm) { + nbgl_useCaseStatus("MESSAGE\nSIGNED", true, nbgl_712_review_approve); + } else { + nbgl_712_confirm_rejection(); + } +} + +void nbgl_712_start(void (*resume_func)(void), const char *title) { + g_resume_func = resume_func; + nbgl_useCaseReviewStart(&C_Message_64px, + title, + NULL, + "Reject", + resume_func, + nbgl_712_confirm_rejection); +} diff --git a/src_nbgl/ui_712_common.h b/src_nbgl/ui_712_common.h new file mode 100644 index 0000000..ab8bd4f --- /dev/null +++ b/src_nbgl/ui_712_common.h @@ -0,0 +1,13 @@ +#ifndef UI_712_COMMON_H_ +#define UI_712_COMMON_H_ + +#include + +void nbgl_712_approve(void); +void nbgl_712_reject(void); +void nbgl_712_confirm_rejection_cb(bool confirm); +void nbgl_712_confirm_rejection(void); +void nbgl_712_review_choice(bool confirm); +void nbgl_712_start(void (*resume_func)(void), const char *title); + +#endif // UI_712_COMMON_H_ diff --git a/src_nbgl/ui_approve_tx.c b/src_nbgl/ui_approve_tx.c new file mode 100644 index 0000000..377166c --- /dev/null +++ b/src_nbgl/ui_approve_tx.c @@ -0,0 +1,257 @@ + +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" +#include "network.h" +#include "plugins.h" +#include "domain_name.h" + +// 1 more than actually displayed on screen, because of calculations in StaticReview +#define MAX_PLUGIN_ITEMS_PER_SCREEN 4 +#define TAG_MAX_LEN 43 +#define VALUE_MAX_LEN 79 +enum { + REJECT_TOKEN, + START_REVIEW_TOKEN, +}; + +static nbgl_layoutTagValue_t pair; +// these buffers are used as circular +static char title_buffer[MAX_PLUGIN_ITEMS_PER_SCREEN][TAG_MAX_LEN]; +static char msg_buffer[MAX_PLUGIN_ITEMS_PER_SCREEN][VALUE_MAX_LEN]; +static nbgl_layoutTagValueList_t useCaseTagValueList; +static nbgl_pageInfoLongPress_t infoLongPress; + +struct tx_approval_context_t { + bool fromPlugin; + bool blindSigning; + bool displayNetwork; +#ifdef HAVE_DOMAIN_NAME + bool domain_name_match; +#endif +}; + +static struct tx_approval_context_t tx_approval_context; + +static void reviewContinueCommon(void); + +static void reviewReject(void) { + io_seproxyhal_touch_tx_cancel(NULL); + memset(&tx_approval_context, 0, sizeof(tx_approval_context)); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_tx_ok(NULL); + memset(&tx_approval_context, 0, sizeof(tx_approval_context)); +} + +static void onConfirmAbandon(void) { + nbgl_useCaseStatus("Transaction rejected", false, reviewReject); +} + +static void rejectTransactionQuestion(void) { + nbgl_useCaseConfirm("Reject transaction?", + NULL, + "Yes, reject", + "Go back to transaction", + onConfirmAbandon); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + nbgl_useCaseStatus("TRANSACTION\nSIGNED", true, confirmTransation); + } else { + rejectTransactionQuestion(); + } +} + +// called by NBGL to get the tag/value pair corresponding to pairIndex +static nbgl_layoutTagValue_t *getTagValuePair(uint8_t pairIndex) { + static int counter = 0; + + if (tx_approval_context.fromPlugin) { + if (pairIndex < dataContext.tokenContext.pluginUiMaxItems) { + // for the next dataContext.tokenContext.pluginUiMaxItems items, get tag/value from + // plugin_ui_get_item_internal() + dataContext.tokenContext.pluginUiCurrentItem = pairIndex; + plugin_ui_get_item_internal((uint8_t *) title_buffer[counter], + TAG_MAX_LEN, + (uint8_t *) msg_buffer[counter], + VALUE_MAX_LEN); + pair.item = title_buffer[counter]; + pair.value = msg_buffer[counter]; + } else { + pairIndex -= dataContext.tokenContext.pluginUiMaxItems; + // for the last 1 (or 2), tags are fixed + if (tx_approval_context.displayNetwork && (pairIndex == 0)) { + pair.item = "Network"; + pair.value = strings.common.network_name; + } else { + pair.item = "Max fees"; + pair.value = strings.common.maxFee; + } + } + } else { + uint8_t target_index = 0; + + if (pairIndex == target_index++) { + pair.item = "Amount"; + pair.value = strings.common.fullAmount; + } +#ifdef HAVE_DOMAIN_NAME + if (tx_approval_context.domain_name_match) { + if (pairIndex == target_index++) { + pair.item = "Domain"; + pair.value = g_domain_name; + } + } + if (!tx_approval_context.domain_name_match || N_storage.verbose_domain_name) { +#endif // HAVE_DOMAIN_NAME + if (pairIndex == target_index++) { + pair.item = "Address"; + pair.value = strings.common.fullAddress; + } +#ifdef HAVE_DOMAIN_NAME + } +#endif // HAVE_DOMAIN_NAME + if (N_storage.displayNonce) { + if (pairIndex == target_index++) { + pair.item = "Nonce"; + pair.value = strings.common.nonce; + } + } + if (pairIndex == target_index++) { + pair.item = "Max fees"; + pair.value = strings.common.maxFee; + } + if (pairIndex == target_index++) { + pair.item = "Network"; + pair.value = strings.common.network_name; + } + } + // counter is used as index to circular buffer + counter++; + if (counter == MAX_PLUGIN_ITEMS_PER_SCREEN) { + counter = 0; + } + return &pair; +} + +static void pageCallback(int token, uint8_t index) { + (void) index; + nbgl_pageRelease(pageContext); + if (token == REJECT_TOKEN) { + reviewReject(); + } else if (token == START_REVIEW_TOKEN) { + reviewContinueCommon(); + } +} + +static void reviewContinue(void) { + if (tx_approval_context.blindSigning) { + nbgl_pageInfoDescription_t info = { + .centeredInfo.icon = &C_round_warning_64px, + .centeredInfo.text1 = "Blind Signing", + .centeredInfo.text2 = + "This transaction cannot be\nsecurely interpreted by Ledger\nStax. It might put " + "your assets\nat risk.", + .centeredInfo.text3 = NULL, + .centeredInfo.style = LARGE_CASE_INFO, + .centeredInfo.offsetY = -32, + .footerText = "Reject transaction", + .footerToken = REJECT_TOKEN, + .tapActionText = "Tap to continue", + .tapActionToken = START_REVIEW_TOKEN, + .topRightStyle = NO_BUTTON_STYLE, + .actionButtonText = NULL, + .tuneId = TUNE_TAP_CASUAL}; + + if (pageContext != NULL) { + nbgl_pageRelease(pageContext); + pageContext = NULL; + } + pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info); + } else { + reviewContinueCommon(); + } +} + +static void reviewContinueCommon(void) { + uint8_t nbPairs = 0; + + if (tx_approval_context.fromPlugin) { + // plugin id + max items + fees + nbPairs += dataContext.tokenContext.pluginUiMaxItems + 1; + if (tx_approval_context.displayNetwork) { + nbPairs++; + } + } else { + nbPairs += 3; + if (N_storage.displayNonce) { + nbPairs++; + } +#ifdef HAVE_DOMAIN_NAME + uint64_t chain_id = get_tx_chain_id(); + tx_approval_context.domain_name_match = + has_domain_name(&chain_id, tmpContent.txContent.destination); + if (tx_approval_context.domain_name_match && N_storage.verbose_domain_name) { + nbPairs += 1; + } +#endif // HAVE_DOMAIN_NAME + if (tx_approval_context.displayNetwork) { + nbPairs++; + } + } + + useCaseTagValueList.pairs = NULL; + useCaseTagValueList.callback = getTagValuePair; + useCaseTagValueList.startIndex = 0; + useCaseTagValueList.nbPairs = nbPairs; ///< number of pairs in pairs array + useCaseTagValueList.smallCaseForValue = false; + useCaseTagValueList.wrapping = false; + infoLongPress.icon = get_app_icon(true); + infoLongPress.text = tx_approval_context.fromPlugin ? staxSharedBuffer : "Review transaction"; + infoLongPress.longPressText = "Hold to sign"; + nbgl_useCaseStaticReview(&useCaseTagValueList, + &infoLongPress, + "Reject transaction", + reviewChoice); +} + +static void buildFirstPage(void) { + if (tx_approval_context.fromPlugin) { + plugin_ui_get_id(); + SPRINTF(staxSharedBuffer, + "Review %s\ntransaction:\n%s", + strings.common.fullAddress, + strings.common.fullAmount); + nbgl_useCaseReviewStart(get_app_icon(true), + staxSharedBuffer, + NULL, + "Reject transaction", + reviewContinue, + rejectTransactionQuestion); + } else { + nbgl_useCaseReviewStart(get_app_icon(true), + "Review transaction", + NULL, + "Reject transaction", + reviewContinue, + rejectTransactionQuestion); + } +} + +void ux_approve_tx(bool fromPlugin) { + tx_approval_context.blindSigning = + !fromPlugin && tmpContent.txContent.dataPresent && !N_storage.contractDetails; + tx_approval_context.fromPlugin = fromPlugin; + tx_approval_context.displayNetwork = false; + + uint64_t chain_id = get_tx_chain_id(); + if (chainConfig->chainId == ETHEREUM_MAINNET_CHAINID && chain_id != chainConfig->chainId) { + tx_approval_context.displayNetwork = true; + } + + buildFirstPage(); +} diff --git a/src_nbgl/ui_confirm_parameter_selector.c b/src_nbgl/ui_confirm_parameter_selector.c new file mode 100644 index 0000000..43f93fc --- /dev/null +++ b/src_nbgl/ui_confirm_parameter_selector.c @@ -0,0 +1,77 @@ +#include "common_ui.h" +#include "ui_nbgl.h" +#include "network.h" + +typedef enum { PARAMETER_CONFIRMATION, SELECTOR_CONFIRMATION } e_confirmation_type; + +static nbgl_layoutTagValue_t pair; +static e_confirmation_type confirm_type; + +static void reviewReject(void) { + io_seproxyhal_touch_data_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_data_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + confirmTransation(); + } else { + reviewReject(); + } +} + +static bool displayTransactionPage(uint8_t page, nbgl_pageContent_t *content) { + if (page == 0) { + pair.item = (confirm_type == PARAMETER_CONFIRMATION) ? "Parameter" : "Selector"; + pair.value = strings.tmp.tmp; + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 1; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) &pair; + } else if (page == 1) { + snprintf(staxSharedBuffer, + sizeof(staxSharedBuffer), + "Confirm %s", + (confirm_type == PARAMETER_CONFIRMATION) ? "parameter" : "selector"); + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(true); + content->infoLongPress.text = staxSharedBuffer; + content->infoLongPress.longPressText = "Hold to confirm"; + } else { + return false; + } + // valid page so return true + return true; +} + +static void reviewContinue(void) { + snprintf(staxSharedBuffer, + sizeof(staxSharedBuffer), + "Reject %s", + (confirm_type == PARAMETER_CONFIRMATION) ? "parameter" : "selector"); + nbgl_useCaseRegularReview(0, 2, staxSharedBuffer, NULL, displayTransactionPage, reviewChoice); +} + +static void buildScreen(void) { + snprintf(staxSharedBuffer, + sizeof(staxSharedBuffer), + "Verify %s", + (confirm_type == PARAMETER_CONFIRMATION) ? "parameter" : "selector"); + nbgl_useCaseReviewStart(get_app_icon(true), + staxSharedBuffer, + NULL, + "Reject", + reviewContinue, + reviewReject); +} + +void ui_confirm_parameter(void) { + confirm_type = PARAMETER_CONFIRMATION; + buildScreen(); +} + +void ui_confirm_selector(void) { + confirm_type = SELECTOR_CONFIRMATION; + buildScreen(); +} diff --git a/src_nbgl/ui_display_privacy.c b/src_nbgl/ui_display_privacy.c new file mode 100644 index 0000000..fb014ba --- /dev/null +++ b/src_nbgl/ui_display_privacy.c @@ -0,0 +1,68 @@ +#include "common_ui.h" +#include "ui_nbgl.h" +#include "ui_callbacks.h" +#include "nbgl_use_case.h" +#include "network.h" + +static nbgl_layoutTagValue_t pairs[2]; +static char *review_string; + +static void reviewReject(void) { + io_seproxyhal_touch_privacy_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_privacy_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + confirmTransation(); + } else { + reviewReject(); + } +} + +static bool displayTransactionPage(uint8_t page, nbgl_pageContent_t *content) { + if (page == 0) { + pairs[0].item = "Address"; + pairs[0].value = strings.common.fullAddress; + pairs[1].item = "Key"; + pairs[1].value = strings.common.fullAmount; + + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 2; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + } else if (page == 1) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(true); + content->infoLongPress.text = review_string; + content->infoLongPress.longPressText = "Hold to approve"; + } else { + return false; + } + // valid page so return true + return true; +} + +static void reviewContinue(void) { + nbgl_useCaseRegularReview(0, 2, "Reject", NULL, displayTransactionPage, reviewChoice); +} + +static void buildFirstPage(void) { + nbgl_useCaseReviewStart(get_app_icon(true), + review_string, + NULL, + "Reject", + reviewContinue, + reviewReject); +} + +void ui_display_privacy_public_key(void) { + review_string = (char *) "Provide public\nprivacy key"; + buildFirstPage(); +} + +void ui_display_privacy_shared_secret(void) { + review_string = (char *) "Provide public\nsecret key"; + buildFirstPage(); +} diff --git a/src_nbgl/ui_get_eth2_public_key.c b/src_nbgl/ui_get_eth2_public_key.c new file mode 100644 index 0000000..9f388df --- /dev/null +++ b/src_nbgl/ui_get_eth2_public_key.c @@ -0,0 +1,30 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" + +static void reviewReject(void) { + io_seproxyhal_touch_address_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_address_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + // display a status page and go back to main + nbgl_useCaseStatus("ADDRESS\nVERIFIED", true, confirmTransation); + } else { + nbgl_useCaseStatus("Address verification\ncancelled", false, reviewReject); + } +} + +static void buildScreen(void) { + snprintf(strings.tmp.tmp, 100, "0x%.*H", 48, tmpCtx.publicKeyContext.publicKey.W); + nbgl_useCaseAddressConfirmation(strings.tmp.tmp, reviewChoice); +} + +void ui_display_public_eth2(void) { + buildScreen(); +} \ No newline at end of file diff --git a/src_nbgl/ui_get_public_key.c b/src_nbgl/ui_get_public_key.c new file mode 100644 index 0000000..5966737 --- /dev/null +++ b/src_nbgl/ui_get_public_key.c @@ -0,0 +1,28 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" + +static void reviewReject(void) { + io_seproxyhal_touch_address_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_address_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + // display a status page and go back to main + nbgl_useCaseStatus("ADDRESS\nVERIFIED", true, confirmTransation); + } else { + nbgl_useCaseStatus("Address verification\ncancelled", false, reviewReject); + } +} + +static void buildScreen(void) { + nbgl_useCaseAddressConfirmation(strings.common.fullAddress, reviewChoice); +} +void ui_display_public_key(void) { + buildScreen(); +} \ No newline at end of file diff --git a/src_nbgl/ui_get_stark_public_key.c b/src_nbgl/ui_get_stark_public_key.c new file mode 100644 index 0000000..ce57787 --- /dev/null +++ b/src_nbgl/ui_get_stark_public_key.c @@ -0,0 +1,29 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" + +static void reviewReject(void) { + io_seproxyhal_touch_address_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_stark_pubkey_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + // display a status page and go back to main + nbgl_useCaseStatus("ADDRESS\nVERIFIED", true, confirmTransation); + } else { + nbgl_useCaseStatus("Address verification\ncancelled", false, reviewReject); + } +} + +static void buildScreen(void) { + nbgl_useCaseAddressConfirmation(strings.tmp.tmp, reviewChoice); +} + +void ui_display_stark_public(void) { + buildScreen(); +} \ No newline at end of file diff --git a/src_nbgl/ui_idle.c b/src_nbgl/ui_idle.c new file mode 100644 index 0000000..b14ca9b --- /dev/null +++ b/src_nbgl/ui_idle.c @@ -0,0 +1,59 @@ +#include "common_ui.h" +#include "shared_context.h" +#include "ui_nbgl.h" +#include "nbgl_use_case.h" +#include "glyphs.h" +#include "network.h" + +char staxSharedBuffer[SHARED_BUFFER_SIZE] = {0}; +nbgl_page_t *pageContext; + +#define FORMAT_PLUGIN "This app enables clear\nsigning transactions for\nthe %s dApp." + +void releaseContext(void) { + if (pageContext != NULL) { + nbgl_pageRelease(pageContext); + pageContext = NULL; + } +} + +void app_quit(void) { + // exit app here + os_sched_exit(-1); +} + +const nbgl_icon_details_t *get_app_icon(bool caller_icon) { + const nbgl_icon_details_t *icon = NULL; + + if (caller_icon && caller_app) { + if (caller_app->icon) { + icon = caller_app->icon; + } + } else { + icon = &ICONGLYPH; + } + return icon; +} + +void ui_idle(void) { + const char *app_name = NULL; + const char *tagline = NULL; + + if (caller_app) { + app_name = caller_app->name; + + if (caller_app->type == CALLER_TYPE_PLUGIN) { + snprintf(staxSharedBuffer, sizeof(staxSharedBuffer), FORMAT_PLUGIN, app_name); + tagline = staxSharedBuffer; + } + } else { // Ethereum app + app_name = get_app_network_name(); + } + + nbgl_useCaseHome((char *) app_name, + get_app_icon(true), + tagline, + true, + ui_menu_settings, + app_quit); +} diff --git a/src_nbgl/ui_nbgl.h b/src_nbgl/ui_nbgl.h new file mode 100644 index 0000000..d7db175 --- /dev/null +++ b/src_nbgl/ui_nbgl.h @@ -0,0 +1,21 @@ +#ifndef _UI_NBGL_H_ +#define _UI_NBGL_H_ + +#include +#include +#include + +#define SHARED_BUFFER_SIZE SHARED_CTX_FIELD_1_SIZE +extern char staxSharedBuffer[SHARED_BUFFER_SIZE]; + +extern nbgl_page_t* pageContext; + +void releaseContext(void); + +const nbgl_icon_details_t* get_app_icon(bool caller_icon); + +void ui_idle(void); +void ui_menu_settings(void); +void ui_menu_about(void); + +#endif // _UI_NBGL_H_ diff --git a/src_nbgl/ui_settings.c b/src_nbgl/ui_settings.c new file mode 100644 index 0000000..7981356 --- /dev/null +++ b/src_nbgl/ui_settings.c @@ -0,0 +1,120 @@ +#include "common_ui.h" +#include "ui_nbgl.h" +#include "nbgl_use_case.h" + +static const char* const infoTypes[] = {"Version", APPNAME " App"}; +static const char* const infoContents[] = {APPVERSION, "(c) " BUILD_YEAR " Ledger"}; + +enum { + BLIND_SIGNING_TOKEN = FIRST_USER_TOKEN, + DEBUG_TOKEN, + NONCE_TOKEN, +#ifdef HAVE_EIP712_FULL_SUPPORT + EIP712_VERBOSE_TOKEN, +#endif // HAVE_EIP712_FULL_SUPPORT +#ifdef HAVE_DOMAIN_NAME + DOMAIN_NAME_VERBOSE_TOKEN +#endif // HAVE_DOMAIN_NAME +}; + +static nbgl_layoutSwitch_t switches[3]; + +static bool navCallback(uint8_t page, nbgl_pageContent_t* content) { + uint8_t index = 0; + + switch (page) { + case 0: + switches[index++] = + (nbgl_layoutSwitch_t){.initState = N_storage.dataAllowed ? ON_STATE : OFF_STATE, + .text = "Blind signing", + .subText = "Enable transaction blind\nsigning", + .token = BLIND_SIGNING_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + switches[index++] = + (nbgl_layoutSwitch_t){.initState = N_storage.contractDetails ? ON_STATE : OFF_STATE, + .text = "Debug", + .subText = "Display contract data\ndetails", + .token = DEBUG_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + switches[index++] = + (nbgl_layoutSwitch_t){.initState = N_storage.displayNonce ? ON_STATE : OFF_STATE, + .text = "Nonce", + .subText = "Display account nonce\nin transaction", + .token = NONCE_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + + content->type = SWITCHES_LIST; + content->switchesList.nbSwitches = index; + content->switchesList.switches = (nbgl_layoutSwitch_t*) switches; + break; + + case 1: + switches[index++] = + (nbgl_layoutSwitch_t){.initState = N_storage.verbose_eip712 ? ON_STATE : OFF_STATE, + .text = "Verbose EIP712", + .subText = "Ignore filtering and\ndisplay raw content", + .token = EIP712_VERBOSE_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; +#ifdef HAVE_DOMAIN_NAME + switches[index++] = (nbgl_layoutSwitch_t){ + .initState = N_storage.verbose_domain_name ? ON_STATE : OFF_STATE, + .text = "Verbose domains", + .subText = "Show resolved address", + .token = DOMAIN_NAME_VERBOSE_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; +#endif // HAVE_DOMAIN_NAME + + content->type = SWITCHES_LIST; + content->switchesList.nbSwitches = index; + content->switchesList.switches = (nbgl_layoutSwitch_t*) switches; + break; + + case 2: + content->type = INFOS_LIST; + content->infosList.nbInfos = 2; + content->infosList.infoTypes = (const char**) infoTypes; + content->infosList.infoContents = (const char**) infoContents; + break; + + default: + return false; + break; + } + + return true; +} + +static void controlsCallback(int token, uint8_t index) { + (void) index; + uint8_t value; + switch (token) { + case BLIND_SIGNING_TOKEN: + value = (N_storage.dataAllowed ? 0 : 1); + nvm_write((void*) &N_storage.dataAllowed, (void*) &value, sizeof(uint8_t)); + break; + case DEBUG_TOKEN: + value = (N_storage.contractDetails ? 0 : 1); + nvm_write((void*) &N_storage.contractDetails, (void*) &value, sizeof(uint8_t)); + break; + case NONCE_TOKEN: + value = (N_storage.displayNonce ? 0 : 1); + nvm_write((void*) &N_storage.displayNonce, (void*) &value, sizeof(uint8_t)); + break; +#ifdef HAVE_EIP712_FULL_SUPPORT + case EIP712_VERBOSE_TOKEN: + value = (N_storage.verbose_eip712 ? 0 : 1); + nvm_write((void*) &N_storage.verbose_eip712, (void*) &value, sizeof(uint8_t)); + break; +#endif // HAVE_EIP712_FULL_SUPPORT +#ifdef HAVE_DOMAIN_NAME + case DOMAIN_NAME_VERBOSE_TOKEN: + value = (N_storage.verbose_domain_name ? 0 : 1); + nvm_write((void*) &N_storage.verbose_domain_name, (void*) &value, sizeof(uint8_t)); + break; +#endif // HAVE_DOMAIN_NAME + } +} + +void ui_menu_settings(void) { + nbgl_useCaseSettings(APPNAME " settings", 0, 3, true, ui_idle, navCallback, controlsCallback); +} diff --git a/src_nbgl/ui_sign_712.c b/src_nbgl/ui_sign_712.c new file mode 100644 index 0000000..bfe53fd --- /dev/null +++ b/src_nbgl/ui_sign_712.c @@ -0,0 +1,80 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "common_ui.h" +#include "ui_nbgl.h" +#include "ui_logic.h" +#include "common_712.h" +#include "nbgl_use_case.h" +#include "network.h" +#include "ui_712_common.h" + +static nbgl_layoutTagValue_t pair; + +static bool display_sign_page(uint8_t page, nbgl_pageContent_t *content) { + (void) page; + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(true); + content->infoLongPress.text = "Sign typed message"; + content->infoLongPress.longPressText = "Hold to sign"; + return true; +} + +static bool display_review_page(uint8_t page, nbgl_pageContent_t *content) { + bool ret; + uint16_t len; + + switch (page) { + case 0: + // limit the value to one page + nbgl_getTextMaxLenInNbLines(BAGL_FONT_INTER_MEDIUM_32px, + strings.tmp.tmp, + SCREEN_WIDTH - (2 * BORDER_MARGIN), + 9, + &len); + strings.tmp.tmp[len] = '\0'; + + pair.item = strings.tmp.tmp2; + pair.value = strings.tmp.tmp; + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 1; + content->tagValueList.pairs = &pair; + content->tagValueList.wrapping = false; + content->tagValueList.nbMaxLinesForValue = 0; + ret = true; + break; + + case 1: + switch (ui_712_next_field()) { + case EIP712_NO_MORE_FIELD: + return display_sign_page(page, content); + break; + case EIP712_FIELD_INCOMING: + case EIP712_FIELD_LATER: + default: + break; + } + __attribute__((fallthrough)); + + default: + ret = false; + break; + } + return ret; +} + +static void handle_display(nbgl_navCallback_t cb) { + nbgl_useCaseRegularReview(0, 0, "Reject", NULL, cb, nbgl_712_review_choice); +} + +void ui_712_start(void) { + nbgl_712_start(&ui_712_switch_to_message, "Review typed message"); +} + +void ui_712_switch_to_message(void) { + handle_display(display_review_page); +} + +void ui_712_switch_to_sign(void) { + handle_display(display_sign_page); +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_nbgl/ui_sign_712_v0.c b/src_nbgl/ui_sign_712_v0.c new file mode 100644 index 0000000..e5dc92e --- /dev/null +++ b/src_nbgl/ui_sign_712_v0.c @@ -0,0 +1,51 @@ +#include "common_ui.h" +#include "ui_nbgl.h" +#include "common_712.h" +#include "network.h" +#include "ethUtils.h" +#include "ui_712_common.h" + +static nbgl_layoutTagValue_t pairs[2]; + +static void start_review(void); // forward declaration + +static char *format_hash(const uint8_t *hash, char *buffer, size_t buffer_size, size_t offset) { + snprintf(buffer + offset, buffer_size - offset, "0x%.*H", KECCAK256_HASH_BYTESIZE, hash); + return buffer + offset; +} + +static bool display_review_page(uint8_t page, nbgl_pageContent_t *content) { + if (page == 0) { + pairs[0].item = "Domain hash"; + pairs[0].value = format_hash(tmpCtx.messageSigningContext712.domainHash, + strings.tmp.tmp, + sizeof(strings.tmp.tmp), + 0); + pairs[1].item = "Message hash"; + pairs[1].value = format_hash(tmpCtx.messageSigningContext712.messageHash, + strings.tmp.tmp, + sizeof(strings.tmp.tmp), + 70); + + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 2; + content->tagValueList.nbMaxLinesForValue = 0; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + } else if (page == 1) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(true); + content->infoLongPress.text = "Sign typed message"; + content->infoLongPress.longPressText = "Hold to sign"; + } else { + return false; + } + // valid page so return true + return true; +} + +static void start_review(void) { + nbgl_useCaseRegularReview(0, 2, "Reject", NULL, display_review_page, nbgl_712_review_choice); +} + +void ui_sign_712_v0(void) { + nbgl_712_start(&start_review, "Sign typed message"); +} diff --git a/src_nbgl/ui_sign_message.c b/src_nbgl/ui_sign_message.c new file mode 100644 index 0000000..5ea2c89 --- /dev/null +++ b/src_nbgl/ui_sign_message.c @@ -0,0 +1,188 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" +#include "sign_message.h" +#include "glyphs.h" +#include "nbgl_use_case.h" +#include "common_ui.h" + +typedef enum { + UI_191_NBGL_START_REVIEW_DISPLAYED = 0, + UI_191_NBGL_GO_TO_NEXT_CONTENT, + UI_191_NBGL_BACK_FROM_REJECT_CANCEL, + UI_191_NBGL_GO_TO_SIGN, + UI_191_NBGL_SIGN_DISPLAYED, +} e_ui_nbgl_191_state; + +static e_ui_nbgl_191_state state; +static e_ui_nbgl_191_state state_before_reject_cancel; +static bool skip_message; + +static nbgl_layoutTagValue_t pair; + +// +static uint32_t eip191MessageIdx = 0; +static uint32_t stringsTmpTmpIdx = 0; + +static void reject_message(void) { + io_seproxyhal_touch_signMessage_cancel(); +} + +static void sign_message(void) { + io_seproxyhal_touch_signMessage_ok(); +} + +static bool display_message(nbgl_pageContent_t *content) { + uint16_t len = 0; + bool reached; + + if (state != UI_191_NBGL_BACK_FROM_REJECT_CANCEL) { + strncpy(staxSharedBuffer + eip191MessageIdx, + strings.tmp.tmp + stringsTmpTmpIdx, + SHARED_BUFFER_SIZE - eip191MessageIdx); + reached = nbgl_getTextMaxLenInNbLines(BAGL_FONT_INTER_MEDIUM_32px, + (char *) staxSharedBuffer, + SCREEN_WIDTH - (2 * BORDER_MARGIN), + 9, + &len); + + stringsTmpTmpIdx = len - eip191MessageIdx; + eip191MessageIdx = len; + staxSharedBuffer[eip191MessageIdx] = '\0'; + + if (!reached && eip191MessageIdx < SHARED_BUFFER_SIZE) { + stringsTmpTmpIdx = 0; + question_switcher(); + + if (state != UI_191_NBGL_GO_TO_SIGN) { + return false; + } + } else if (reached || eip191MessageIdx == SHARED_BUFFER_SIZE) { + eip191MessageIdx = 0; + } + } + + pair.value = staxSharedBuffer; + pair.item = "Message"; + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 1; + content->tagValueList.pairs = &pair; + content->tagValueList.smallCaseForValue = false; + content->tagValueList.nbMaxLinesForValue = 9; + content->tagValueList.wrapping = false; + + if (state == UI_191_NBGL_BACK_FROM_REJECT_CANCEL) { + // We come back from Reject screen. + // The previously displayed content must be redisplayed. + // Do not call question_switcher() to avoid replacing + // string.tmp.tmp content. + state = state_before_reject_cancel; + } else if (stringsTmpTmpIdx >= strlen(strings.tmp.tmp)) { + // Fetch the next content to display into strings.tmp.tmp buffer. + stringsTmpTmpIdx = 0; + question_switcher(); + } + return true; +} + +static void display_sign(nbgl_pageContent_t *content) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = &C_Message_64px; + content->infoLongPress.text = "Sign Message?"; + content->infoLongPress.longPressText = "Hold to sign"; + state = UI_191_NBGL_SIGN_DISPLAYED; +} + +static bool nav_callback(uint8_t page, nbgl_pageContent_t *content) { + bool ret = true; + + if (page == LAST_PAGE_FOR_REVIEW) { // was skipped + skip_message = true; + skip_rest_of_message(); + } + if ((state != UI_191_NBGL_GO_TO_SIGN) && (state != UI_191_NBGL_SIGN_DISPLAYED)) { + if (skip_message) { + // do not refresh when this callback triggers after user validation + ret = false; + } else { + ret = display_message(content); + } + } else { + // the last page must contain a long press button + display_sign(content); + } + return ret; +} + +static void choice_callback(bool confirm) { + if (confirm) { + nbgl_useCaseStatus("MESSAGE\nSIGNED", true, sign_message); + sign_message(); + } +} + +static void continue_review(void) { + nbgl_useCaseForwardOnlyReview("Reject", NULL, nav_callback, choice_callback); +} + +static void confirm_transaction_rejection_choice(bool confirm) { + if (confirm) { + reject_message(); + } else { + // Go to previous screen accordingly + if (state == UI_191_NBGL_START_REVIEW_DISPLAYED) { + ui_191_start(); + } else { + if (state != UI_191_NBGL_SIGN_DISPLAYED) { + state_before_reject_cancel = state; + state = UI_191_NBGL_BACK_FROM_REJECT_CANCEL; + } + continue_review(); + } + } +} + +static void confirm_transaction_rejection() { + nbgl_useCaseChoice(&C_warning64px, + "Reject message?", + NULL, + "Yes, Reject", + "Go back to message", + confirm_transaction_rejection_choice); +} + +void ui_191_start(void) { + state = UI_191_NBGL_START_REVIEW_DISPLAYED; + skip_message = false; + eip191MessageIdx = 0; + stringsTmpTmpIdx = 0; + + nbgl_useCaseReviewStart(&C_Message_64px, + "Review message", + NULL, + "Reject", + continue_review, + confirm_transaction_rejection); +} + +void ui_191_switch_to_message(void) { + // No question mechanism on Stax: + // Message is already displayed + state = UI_191_NBGL_GO_TO_NEXT_CONTENT; + continue_review(); +} + +void ui_191_switch_to_sign(void) { + // Next nav_callback callback must display + // the hold to approve screen + state = UI_191_NBGL_GO_TO_SIGN; + if (skip_message) { + continue_review(); // to force screen refresh + } +} + +void ui_191_switch_to_question(void) { + // No question mechanism on Stax: + // Always display the next message chunk. + continue_displaying_message(); +} diff --git a/src_nbgl/ui_stark_limit_order.c b/src_nbgl/ui_stark_limit_order.c new file mode 100644 index 0000000..4c164c5 --- /dev/null +++ b/src_nbgl/ui_stark_limit_order.c @@ -0,0 +1,67 @@ +#include "common_ui.h" +#include "ui_nbgl.h" +#include "ui_callbacks.h" +#include "nbgl_use_case.h" +#include "network.h" + +#ifdef HAVE_STARKWARE + +static nbgl_layoutTagValue_t pairs[3]; + +static void reviewReject(void) { + io_seproxyhal_touch_tx_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_stark_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + confirmTransation(); + } else { + reviewReject(); + } +} + +static bool displayTransactionPage(uint8_t page, nbgl_pageContent_t *content) { + if (page == 0) { + pairs[0].item = "Sell"; + pairs[0].value = strings.common.fullAmount; + pairs[1].item = "Buy"; + pairs[1].value = strings.common.maxFee; + pairs[2].item = "Token amount"; + pairs[2].value = strings.common.fullAddress; + + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 3; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + } else if (page == 1) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(false); + content->infoLongPress.text = "Review stark limit order"; + content->infoLongPress.longPressText = "Hold to sign"; + } else { + return false; + } + // valid page so return true + return true; +} + +static void reviewContinue(void) { + nbgl_useCaseRegularReview(0, 2, "Reject", NULL, displayTransactionPage, reviewChoice); +} + +static void buildFirstPage(void) { + nbgl_useCaseReviewStart(get_app_icon(false), + "Review stark limit order", + NULL, + "Reject", + reviewContinue, + reviewReject); +} + +void ui_stark_limit_order(void) { + buildFirstPage(); +} + +#endif diff --git a/src_nbgl/ui_stark_transfer.c b/src_nbgl/ui_stark_transfer.c new file mode 100644 index 0000000..0b752eb --- /dev/null +++ b/src_nbgl/ui_stark_transfer.c @@ -0,0 +1,127 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" +#include "starkDisplayUtils.h" +#include "ethUtils.h" +#include "network.h" + +#ifdef HAVE_STARKWARE + +static nbgl_layoutTagValue_t pairs[3]; +static char condAddressBuffer[43]; +struct stark_transfer_context { + bool selfTransfer; + bool conditional; +}; + +static struct stark_transfer_context context; + +static void reviewReject(void) { + io_seproxyhal_touch_tx_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_stark_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + confirmTransation(); + } else { + reviewReject(); + } +} + +static bool displayTransactionPage(uint8_t page, nbgl_pageContent_t *content) { + uint8_t count = 0; + if (page == 0) { + pairs[count].item = "Amount"; + pairs[count].value = tmpContent.tmp; + count++; + + if (context.selfTransfer == false && context.conditional == false) { + pairs[count].item = "Master Account"; + pairs[count].value = strings.tmp.tmp; + count++; + } + if (context.conditional) { + stark_sign_display_master_account(); + pairs[count].item = "Master Account"; + pairs[count].value = strings.tmp.tmp; + count++; + } + pairs[count].item = "Token Account"; + pairs[count].value = strings.tmp.tmp2; + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = count; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + + return true; + } + if (page == 1) { + if (context.conditional) { + getEthDisplayableAddress(dataContext.starkContext.conditionAddress, + condAddressBuffer, + sizeof(condAddressBuffer), + &global_sha3, + chainConfig->chainId), + pairs[0].item = "Cond. Address"; + pairs[0].value = condAddressBuffer; + + stark_sign_display_condition_fact(); + pairs[1].item = "Cond. Address"; + pairs[1].value = strings.tmp.tmp; + + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 2; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + + } else { + page++; + } + } + if (page == 2) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(false); + content->infoLongPress.text = "Review transaction"; + content->infoLongPress.longPressText = "Hold to sign"; + } + + return false; +} + +static void reviewContinue(void) { + nbgl_useCaseRegularReview(0, + context.conditional ? 3 : 2, + "Reject", + NULL, + displayTransactionPage, + reviewChoice); +} + +void ui_stark_transfer(bool selfTransfer, bool conditional) { + context.selfTransfer = selfTransfer; + context.conditional = conditional; + char *subTitle; + if (conditional) { + if (selfTransfer) { + subTitle = (char *) "Conditionnal self transfer"; + } else { + subTitle = (char *) "Conditionnal transfer"; + } + } else { + if (selfTransfer) { + subTitle = (char *) "self transfer"; + } else { + subTitle = (char *) "Transfer"; + } + } + nbgl_useCaseReviewStart(get_app_icon(false), + "Review stark transaction", + subTitle, + "Reject", + reviewContinue, + reviewReject); +} + +#endif // #ifdef HAVE_STARKWARE diff --git a/src_nbgl/ui_stark_unsafe_sign.c b/src_nbgl/ui_stark_unsafe_sign.c new file mode 100644 index 0000000..70579bb --- /dev/null +++ b/src_nbgl/ui_stark_unsafe_sign.c @@ -0,0 +1,70 @@ + +#include "common_ui.h" +#include "ui_nbgl.h" +#include "ui_callbacks.h" +#include "nbgl_use_case.h" +#include "network.h" + +#ifdef HAVE_STARKWARE + +static nbgl_layoutTagValue_t pairs[2]; +static char from_account[64]; +static char message_hash[64]; + +static void reviewReject(void) { + io_seproxyhal_touch_tx_cancel(NULL); +} + +static void confirmTransation(void) { + io_seproxyhal_touch_stark_unsafe_sign_ok(NULL); +} + +static void reviewChoice(bool confirm) { + if (confirm) { + confirmTransation(); + } else { + reviewReject(); + } +} + +static bool displayTransactionPage(uint8_t page, nbgl_pageContent_t *content) { + snprintf(from_account, sizeof(from_account), "0x%.*H", 32, dataContext.starkContext.w1); + snprintf(message_hash, sizeof(message_hash), "0x%.*H", 32, dataContext.starkContext.w2); + + if (page == 0) { + pairs[0].item = "From Account"; + pairs[0].value = from_account; + pairs[1].item = "Hash"; + pairs[1].value = message_hash; + content->type = TAG_VALUE_LIST; + content->tagValueList.nbPairs = 2; + content->tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + } else if (page == 1) { + content->type = INFO_LONG_PRESS, content->infoLongPress.icon = get_app_icon(false); + content->infoLongPress.text = "Unsafe Stark Sign"; + content->infoLongPress.longPressText = "Hold to sign"; + } else { + return false; + } + // valid page so return true + return true; +} + +static void reviewContinue(void) { + nbgl_useCaseRegularReview(0, 2, "Reject", NULL, displayTransactionPage, reviewChoice); +} + +static void buildFirstPage(void) { + nbgl_useCaseReviewStart(get_app_icon(false), + "Unsafe Stark Sign", + NULL, + "Reject", + reviewContinue, + reviewReject); +} + +void ui_stark_unsafe_sign(void) { + buildFirstPage(); +} + +#endif // HAVE_STARKWARE diff --git a/src_nbgl/ui_warning_contract_data.c b/src_nbgl/ui_warning_contract_data.c new file mode 100644 index 0000000..2619dce --- /dev/null +++ b/src_nbgl/ui_warning_contract_data.c @@ -0,0 +1,21 @@ +#include +#include "shared_context.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" + +static void ui_warning_contract_data_choice(bool confirm) { + if (confirm) { + ui_idle(); + } else { + ui_menu_settings(); + } +} + +void ui_warning_contract_data(void) { + nbgl_useCaseChoice(&C_warning64px, + "This message cannot\nbe clear-signed", + "Enable blind-signing in\nthe settings to sign\nthis transaction.", + "Exit", + "Go to settings", + ui_warning_contract_data_choice); +} diff --git a/src_plugins/eth2/eth2_plugin.c b/src_plugins/eth2/eth2_plugin.c index 29ea299..c93c82e 100644 --- a/src_plugins/eth2/eth2_plugin.c +++ b/src_plugins/eth2/eth2_plugin.c @@ -212,7 +212,7 @@ void eth2_plugin_call(int message, void *parameters) { strlcpy(msg->title, "Validator", msg->titleLength); strlcpy(msg->msg, context->deposit_address, msg->msgLength); msg->result = ETH_PLUGIN_RESULT_OK; - } + } break; default: break; } diff --git a/tests/ragger/app/client.py b/tests/ragger/app/client.py index 9732c81..616670c 100644 --- a/tests/ragger/app/client.py +++ b/tests/ragger/app/client.py @@ -2,14 +2,9 @@ from enum import IntEnum, auto from typing import Optional from ragger.backend import BackendInterface from ragger.utils import RAPDU -from ragger.navigator import NavInsID, NavIns, NanoNavigator -from .command_builder import EthereumCmdBuilder -from .setting import SettingType, SettingImpl +from .command_builder import CommandBuilder from .eip712 import EIP712FieldType -from .response_parser import EthereumRespParser from .tlv import format_tlv -import signal -import time from pathlib import Path import keychain import rlp @@ -41,58 +36,19 @@ class DOMAIN_NAME_TAG(IntEnum): ADDRESS = 0x22 -class EthereumClient: - _settings: dict[SettingType, SettingImpl] = { - SettingType.BLIND_SIGNING: SettingImpl( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.DEBUG_DATA: SettingImpl( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.NONCE: SettingImpl( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.VERBOSE_EIP712: SettingImpl( - [ "nanox", "nanosp" ] - ), - SettingType.VERBOSE_ENS: SettingImpl( - [ "nanox", "nanosp" ] - ) - } - _click_delay = 1/4 - _eip712_filtering = False - - def __init__(self, client: BackendInterface, golden_run: bool): +class EthAppClient: + def __init__(self, client: BackendInterface): self._client = client - self._chain_id = 1 - self._cmd_builder = EthereumCmdBuilder() - self._resp_parser = EthereumRespParser() - self._nav = NanoNavigator(client, client.firmware, golden_run) - signal.signal(signal.SIGALRM, self._click_signal_timeout) - for setting in self._settings.values(): - setting.value = False + self._cmd_builder = CommandBuilder() def _send(self, payload: bytearray): return self._client.exchange_async_raw(payload) - def _recv(self) -> RAPDU: + def response(self) -> RAPDU: return self._client._last_async_response - def _click_signal_timeout(self, _signum: int, _frame): - self._client.right_click() - - def _enable_click_until_response(self): - signal.setitimer(signal.ITIMER_REAL, - self._click_delay, - self._click_delay) - - def _disable_click_until_response(self): - signal.setitimer(signal.ITIMER_REAL, 0, 0) - def eip712_send_struct_def_struct_name(self, name: str): - with self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)): - pass - return self._recv().status == 0x9000 + return self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)) def eip712_send_struct_def_struct_field(self, field_type: EIP712FieldType, @@ -100,98 +56,45 @@ class EthereumClient: type_size: int, array_levels: [], key_name: str): - with self._send(self._cmd_builder.eip712_send_struct_def_struct_field( - field_type, - type_name, - type_size, - array_levels, - key_name)): - pass - return self._recv() + return self._send(self._cmd_builder.eip712_send_struct_def_struct_field( + field_type, + type_name, + type_size, + array_levels, + key_name)) def eip712_send_struct_impl_root_struct(self, name: str): - with self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)): - self._enable_click_until_response() - self._disable_click_until_response() - return self._recv() + return self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)) def eip712_send_struct_impl_array(self, size: int): - with self._send(self._cmd_builder.eip712_send_struct_impl_array(size)): - pass - return self._recv() + return self._send(self._cmd_builder.eip712_send_struct_impl_array(size)) def eip712_send_struct_impl_struct_field(self, raw_value: bytes): - for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value): - with self._send(apdu): - self._enable_click_until_response() - self._disable_click_until_response() - assert self._recv().status == 0x9000 + chunks = self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value) + for chunk in chunks[:-1]: + with self._send(chunk): + pass + return self._send(chunks[-1]) - def eip712_sign_new(self, bip32_path: str): - with self._send(self._cmd_builder.eip712_sign_new(bip32_path)): - time.sleep(0.5) # tight on timing, needed by the CI otherwise might fail sometimes - if not self._settings[SettingType.VERBOSE_EIP712].value and \ - not self._eip712_filtering: # need to skip the message hash - self._client.right_click() - self._client.right_click() - self._client.both_click() # approve signature - resp = self._recv() - assert resp.status == 0x9000 - return self._resp_parser.sign(resp.data) + def eip712_sign_new(self, bip32_path: str, verbose: bool): + return self._send(self._cmd_builder.eip712_sign_new(bip32_path)) def eip712_sign_legacy(self, bip32_path: str, domain_hash: bytes, message_hash: bytes): - with self._send(self._cmd_builder.eip712_sign_legacy(bip32_path, - domain_hash, - message_hash)): - self._client.right_click() # sign typed message screen - for _ in range(2): # two hashes (domain + message) - if self._client.firmware.device == "nanos": - screens_per_hash = 4 - else: - screens_per_hash = 2 - for _ in range(screens_per_hash): - self._client.right_click() - self._client.both_click() # approve signature - - resp = self._recv() - - assert resp.status == 0x9000 - return self._resp_parser.sign(resp.data) - - def settings_set(self, new_values: dict[SettingType, bool]): - # Go to settings - for _ in range(2): - self._client.right_click() - self._client.both_click() - - for enum in self._settings.keys(): - if self._client.firmware.device in self._settings[enum].devices: - if enum in new_values.keys(): - if new_values[enum] != self._settings[enum].value: - self._client.both_click() - self._settings[enum].value = new_values[enum] - self._client.right_click() - self._client.both_click() + return self._send(self._cmd_builder.eip712_sign_legacy(bip32_path, + domain_hash, + message_hash)) def eip712_filtering_activate(self): - with self._send(self._cmd_builder.eip712_filtering_activate()): - pass - self._eip712_filtering = True - assert self._recv().status == 0x9000 + return self._send(self._cmd_builder.eip712_filtering_activate()) def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes): - with self._send(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)): - self._enable_click_until_response() - self._disable_click_until_response() - assert self._recv().status == 0x9000 + return self._send(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)) def eip712_filtering_show_field(self, name: str, sig: bytes): - with self._send(self._cmd_builder.eip712_filtering_show_field(name, sig)): - pass - assert self._recv().status == 0x9000 + return self._send(self._cmd_builder.eip712_filtering_show_field(name, sig)) def send_fund(self, bip32_path: str, @@ -200,8 +103,7 @@ class EthereumClient: gas_limit: int, to: bytes, amount: float, - chain_id: int, - screenshot_collection: str = None): + chain_id: int): data = list() data.append(nonce) data.append(gas_price) @@ -213,27 +115,14 @@ class EthereumClient: data.append(bytes()) data.append(bytes()) - for chunk in self._cmd_builder.sign(bip32_path, rlp.encode(data)): + chunks = self._cmd_builder.sign(bip32_path, rlp.encode(data)) + for chunk in chunks[:-1]: with self._send(chunk): - nav_ins = NavIns(NavInsID.RIGHT_CLICK) - final_ins = [ NavIns(NavInsID.BOTH_CLICK) ] - target_text = "and send" - if screenshot_collection: - self._nav.navigate_until_text_and_compare(nav_ins, - final_ins, - target_text, - ROOT_SCREENSHOT_PATH, - screenshot_collection) - else: - self._nav.navigate_until_text(nav_ins, - final_ins, - target_text) + pass + return self._send(chunks[-1]) - def get_challenge(self) -> int: - with self._send(self._cmd_builder.get_challenge()): - pass - resp = self._recv() - return self._resp_parser.challenge(resp.data) + def get_challenge(self): + return self._send(self._cmd_builder.get_challenge()) def provide_domain_name(self, challenge: int, name: str, addr: bytes): payload = format_tlv(DOMAIN_NAME_TAG.STRUCTURE_TYPE, 3) # TrustedDomainName @@ -247,6 +136,8 @@ class EthereumClient: payload += format_tlv(DOMAIN_NAME_TAG.SIGNATURE, keychain.sign_data(keychain.Key.DOMAIN_NAME, payload)) - for chunk in self._cmd_builder.provide_domain_name(payload): + chunks = self._cmd_builder.provide_domain_name(payload) + for chunk in chunks[:-1]: with self._send(chunk): pass + return self._send(chunks[-1]) diff --git a/tests/ragger/app/command_builder.py b/tests/ragger/app/command_builder.py index aac10d0..ae3f730 100644 --- a/tests/ragger/app/command_builder.py +++ b/tests/ragger/app/command_builder.py @@ -29,7 +29,7 @@ class P2Type(IntEnum): FILTERING_CONTRACT_NAME = 0x0f FILTERING_FIELD_NAME = 0xff -class EthereumCmdBuilder: +class CommandBuilder: _CLA: int = 0xE0 def _serialize(self, @@ -103,6 +103,7 @@ class EthereumCmdBuilder: data) def eip712_send_struct_impl_struct_field(self, data: bytearray) -> Iterator[bytes]: + chunks = list() # Add a 16-bit integer with the data's byte length (network byte order) data_w_length = bytearray() data_w_length.append((len(data) & 0xff00) >> 8) @@ -110,11 +111,12 @@ class EthereumCmdBuilder: data_w_length += data while len(data_w_length) > 0: p1 = P1Type.PARTIAL_SEND if len(data_w_length) > 0xff else P1Type.COMPLETE_SEND - yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, - p1, - P2Type.STRUCT_FIELD, - data_w_length[:0xff]) + chunks.append(self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + p1, + P2Type.STRUCT_FIELD, + data_w_length[:0xff])) data_w_length = data_w_length[0xff:] + return chunks def eip712_sign_new(self, bip32_path: str) -> bytes: data = pack_derivation_path(bip32_path) @@ -167,29 +169,33 @@ class EthereumCmdBuilder: P2Type.FILTERING_FIELD_NAME, self._eip712_filtering_send_name(name, sig)) - def sign(self, bip32_path: str, rlp_data: bytes) -> Iterator[bytes]: + def sign(self, bip32_path: str, rlp_data: bytes) -> list[bytes]: + apdus = list() payload = pack_derivation_path(bip32_path) payload += rlp_data p1 = P1Type.SIGN_FIRST_CHUNK while len(payload) > 0: - yield self._serialize(InsType.SIGN, - p1, - 0x00, - payload[:0xff]) + apdus.append(self._serialize(InsType.SIGN, + p1, + 0x00, + payload[:0xff])) payload = payload[0xff:] p1 = P1Type.SIGN_SUBSQT_CHUNK + return apdus def get_challenge(self) -> bytes: return self._serialize(InsType.GET_CHALLENGE, 0x00, 0x00) - def provide_domain_name(self, tlv_payload: bytes) -> bytes: + def provide_domain_name(self, tlv_payload: bytes) -> list[bytes]: + chunks = list() payload = struct.pack(">H", len(tlv_payload)) payload += tlv_payload p1 = 1 while len(payload) > 0: - yield self._serialize(InsType.PROVIDE_DOMAIN_NAME, - p1, - 0x00, - payload[:0xff]) + chunks.append(self._serialize(InsType.PROVIDE_DOMAIN_NAME, + p1, + 0x00, + payload[:0xff])) payload = payload[0xff:] p1 = 0 + return chunks diff --git a/tests/ragger/app/response_parser.py b/tests/ragger/app/response_parser.py index 242f4cf..5e73df4 100644 --- a/tests/ragger/app/response_parser.py +++ b/tests/ragger/app/response_parser.py @@ -1,18 +1,14 @@ -class EthereumRespParser: - def sign(self, data: bytes): - assert len(data) == (1 + 32 + 32) +def signature(data: bytes) -> tuple[bytes, bytes, bytes]: + assert len(data) == (1 + 32 + 32) - v = data[0:1] - data = data[1:] + v = data[0:1] + data = data[1:] + r = data[0:32] + data = data[32:] + s = data[0:32] - r = data[0:32] - data = data[32:] + return v, r, s - s = data[0:32] - data = data[32:] - - return v, r, s - - def challenge(self, data: bytes) -> int: - assert len(data) == 4 - return int.from_bytes(data, "big") +def challenge(data: bytes) -> int: + assert len(data) == 4 + return int.from_bytes(data, "big") diff --git a/tests/ragger/app/setting.py b/tests/ragger/app/setting.py deleted file mode 100644 index 7e79da7..0000000 --- a/tests/ragger/app/setting.py +++ /dev/null @@ -1,16 +0,0 @@ -from enum import IntEnum, auto -from typing import List - -class SettingType(IntEnum): - BLIND_SIGNING = 0, - DEBUG_DATA = auto() - NONCE = auto() - VERBOSE_EIP712 = auto() - VERBOSE_ENS = auto() - -class SettingImpl: - devices: List[str] - value: bool - - def __init__(self, devs: List[str]): - self.devices = devs diff --git a/tests/ragger/app/settings.py b/tests/ragger/app/settings.py new file mode 100644 index 0000000..48d78cc --- /dev/null +++ b/tests/ragger/app/settings.py @@ -0,0 +1,62 @@ +from enum import Enum, auto +from typing import List +from ragger.firmware import Firmware +from ragger.navigator import Navigator, NavInsID, NavIns + +class SettingID(Enum): + BLIND_SIGNING = auto() + DEBUG_DATA = auto() + NONCE = auto() + VERBOSE_EIP712 = auto() + VERBOSE_ENS = auto() + +def get_device_settings(device: str) -> list[SettingID]: + if device == "nanos": + return [ + SettingID.BLIND_SIGNING, + SettingID.DEBUG_DATA, + SettingID.NONCE + ] + if (device == "nanox") or (device == "nanosp") or (device == "stax"): + return [ + SettingID.BLIND_SIGNING, + SettingID.DEBUG_DATA, + SettingID.NONCE, + SettingID.VERBOSE_EIP712, + SettingID.VERBOSE_ENS + ] + return [] + +settings_per_page = 3 + +def get_setting_position(device: str, setting: NavInsID) -> tuple[int, int]: + screen_height = 672 # px + header_height = 85 # px + footer_height = 124 # px + usable_height = screen_height - (header_height + footer_height) + setting_height = usable_height // settings_per_page + index_in_page = get_device_settings(device).index(setting) % settings_per_page + return 350, header_height + (setting_height * index_in_page) + (setting_height // 2) + +def settings_toggle(fw: Firmware, nav: Navigator, to_toggle: list[SettingID]): + moves = list() + settings = get_device_settings(fw.device) + # Assume the app is on the home page + if fw.device.startswith("nano"): + moves += [NavInsID.RIGHT_CLICK] * 2 + moves += [NavInsID.BOTH_CLICK] + for setting in settings: + if setting in to_toggle: + moves += [NavInsID.BOTH_CLICK] + moves += [NavInsID.RIGHT_CLICK] + moves += [NavInsID.BOTH_CLICK] # Back + else: + moves += [NavInsID.USE_CASE_HOME_SETTINGS] + for setting in settings: + setting_idx = settings.index(setting) + if (setting_idx > 0) and (setting_idx % settings_per_page) == 0: + moves += [NavInsID.USE_CASE_SETTINGS_NEXT] + if setting in to_toggle: + moves += [NavIns(NavInsID.TOUCH, get_setting_position(fw.device, setting))] + moves += [NavInsID.EXIT_HEADER_TAP] + nav.navigate(moves, screen_change_before_first_instruction=False) diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py index 68799b2..6dd6f4b 100644 --- a/tests/ragger/conftest.py +++ b/tests/ragger/conftest.py @@ -1,12 +1,4 @@ -import pytest from ragger.conftest import configuration -from ragger.backend import BackendInterface -from app.client import EthereumClient - -# This final fixture will return the properly configured app client, to be used in tests -@pytest.fixture -def app_client(backend: BackendInterface, golden_run: bool) -> EthereumClient: - return EthereumClient(backend, golden_run) # Pull all features from the base ragger conftest using the overridden configuration pytest_plugins = ("ragger.conftest.base_conftest", ) diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 02a96eb..6dd6471 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -4,15 +4,19 @@ import json import sys import re import hashlib -from app.client import EthereumClient, EIP712FieldType +from app.client import EthAppClient, EIP712FieldType import keychain +from typing import Callable +import signal # global variables -app_client: EthereumClient = None +app_client: EthAppClient = None filtering_paths = None current_path = list() sig_ctx = {} +autonext_handler: Callable = None + @@ -97,11 +101,12 @@ def send_struct_def_field(typename, keyname): type_enum = EIP712FieldType.CUSTOM typesize = None - app_client.eip712_send_struct_def_struct_field(type_enum, - typename, - typesize, - array_lvls, - keyname) + with app_client.eip712_send_struct_def_struct_field(type_enum, + typename, + typesize, + array_lvls, + keyname): + pass return (typename, type_enum, typesize, array_lvls) @@ -191,7 +196,9 @@ def send_struct_impl_field(value, field): if path in filtering_paths.keys(): send_filtering_show_field(filtering_paths[path]) - app_client.eip712_send_struct_impl_struct_field(data) + with app_client.eip712_send_struct_impl_struct_field(data): + enable_autonext() + disable_autonext() @@ -201,7 +208,8 @@ def evaluate_field(structs, data, field, lvls_left, new_level = True): if new_level: current_path.append(field["name"]) if len(array_lvls) > 0 and lvls_left > 0: - app_client.eip712_send_struct_impl_array(len(data)) + with app_client.eip712_send_struct_impl_array(len(data)): + pass idx = 0 for subdata in data: current_path.append("[]") @@ -252,7 +260,9 @@ def send_filtering_message_info(display_name: str, filters_count: int): to_sign.append(ord(char)) sig = keychain.sign_data(keychain.Key.CAL, to_sign) - app_client.eip712_filtering_message_info(display_name, filters_count, sig) + with app_client.eip712_filtering_message_info(display_name, filters_count, sig): + enable_autonext() + disable_autonext() # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures def send_filtering_show_field(display_name): @@ -270,7 +280,8 @@ def send_filtering_show_field(display_name): for char in display_name: to_sign.append(ord(char)) sig = keychain.sign_data(keychain.Key.CAL, to_sign) - app_client.eip712_filtering_show_field(display_name, sig) + with app_client.eip712_filtering_show_field(display_name, sig): + pass def read_filtering_file(domain, message, filtering_file_path): data_json = None @@ -309,9 +320,29 @@ def init_signature_context(types, domain): schema_hash = hashlib.sha224(schema_str.encode()) sig_ctx["schema_hash"] = bytearray.fromhex(schema_hash.hexdigest()) -def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_path = None) -> bool: + +def next_timeout(_signum: int, _frame): + autonext_handler() + +def enable_autonext(): + seconds = 1/4 + if app_client._client.firmware.device == 'stax': # Stax Speculos is slow + interval = seconds * 3 + else: + interval = seconds + signal.setitimer(signal.ITIMER_REAL, seconds, interval) + +def disable_autonext(): + signal.setitimer(signal.ITIMER_REAL, 0, 0) + + +def process_file(aclient: EthAppClient, + input_file_path: str, + filtering_file_path = None, + autonext: Callable = None) -> bool: global sig_ctx global app_client + global autonext_handler app_client = aclient with open(input_file_path, "r") as data: @@ -322,23 +353,31 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_p domain = data_json["domain"] message = data_json["message"] + if autonext: + autonext_handler = autonext + signal.signal(signal.SIGALRM, next_timeout) + if filtering_file_path: init_signature_context(types, domain) filtr = read_filtering_file(domain, message, filtering_file_path) # send types definition for key in types.keys(): - app_client.eip712_send_struct_def_struct_name(key) + with app_client.eip712_send_struct_def_struct_name(key): + pass for f in types[key]: (f["type"], f["enum"], f["typesize"], f["array_lvls"]) = \ send_struct_def_field(f["type"], f["name"]) if filtering_file_path: - app_client.eip712_filtering_activate() + with app_client.eip712_filtering_activate(): + pass prepare_filtering(filtr, message) # send domain implementation - app_client.eip712_send_struct_impl_root_struct(domain_typename) + with app_client.eip712_send_struct_impl_root_struct(domain_typename): + enable_autonext() + disable_autonext() if not send_struct_impl(types, domain, domain_typename): return False @@ -349,7 +388,9 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_p send_filtering_message_info(domain["name"], len(filtering_paths)) # send message implementation - app_client.eip712_send_struct_impl_root_struct(message_typename) + with app_client.eip712_send_struct_impl_root_struct(message_typename): + enable_autonext() + disable_autonext() if not send_struct_impl(types, message, message_typename): return False diff --git a/tests/ragger/requirements.txt b/tests/ragger/requirements.txt index e408ead..818749b 100644 --- a/tests/ragger/requirements.txt +++ b/tests/ragger/requirements.txt @@ -1,4 +1,4 @@ -ragger[speculos]>=1.6.0,<1.7.0 +ragger[speculos]>=1.7.0,<1.8.0 pytest ecdsa simple-rlp diff --git a/tests/ragger/snapshots/stax/domain_name_non_mainnet/00000.png b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00000.png new file mode 100644 index 0000000..a23d917 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00000.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_non_mainnet/00001.png b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00001.png new file mode 100644 index 0000000..2527859 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00001.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_non_mainnet/00002.png b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00002.png new file mode 100644 index 0000000..ed2045a Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00002.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_non_mainnet/00003.png b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00003.png new file mode 100644 index 0000000..50df349 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00003.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_non_mainnet/00004.png b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00004.png new file mode 100644 index 0000000..7a05bf7 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_non_mainnet/00004.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_False/00000.png b/tests/ragger/snapshots/stax/domain_name_verbose_False/00000.png new file mode 100644 index 0000000..a23d917 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_False/00000.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_False/00001.png b/tests/ragger/snapshots/stax/domain_name_verbose_False/00001.png new file mode 100644 index 0000000..5e0c0a9 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_False/00001.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_False/00002.png b/tests/ragger/snapshots/stax/domain_name_verbose_False/00002.png new file mode 100644 index 0000000..2d6b095 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_False/00002.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_False/00003.png b/tests/ragger/snapshots/stax/domain_name_verbose_False/00003.png new file mode 100644 index 0000000..7a05bf7 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_False/00003.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_True/00000.png b/tests/ragger/snapshots/stax/domain_name_verbose_True/00000.png new file mode 100644 index 0000000..a23d917 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_True/00000.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_True/00001.png b/tests/ragger/snapshots/stax/domain_name_verbose_True/00001.png new file mode 100644 index 0000000..ad8f077 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_True/00001.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_True/00002.png b/tests/ragger/snapshots/stax/domain_name_verbose_True/00002.png new file mode 100644 index 0000000..e64dacc Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_True/00002.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_True/00003.png b/tests/ragger/snapshots/stax/domain_name_verbose_True/00003.png new file mode 100644 index 0000000..50df349 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_True/00003.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_verbose_True/00004.png b/tests/ragger/snapshots/stax/domain_name_verbose_True/00004.png new file mode 100644 index 0000000..7a05bf7 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_verbose_True/00004.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_wrong_addr/00000.png b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00000.png new file mode 100644 index 0000000..a23d917 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00000.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_wrong_addr/00001.png b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00001.png new file mode 100644 index 0000000..3e505d3 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00001.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_wrong_addr/00002.png b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00002.png new file mode 100644 index 0000000..2d6b095 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00002.png differ diff --git a/tests/ragger/snapshots/stax/domain_name_wrong_addr/00003.png b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00003.png new file mode 100644 index 0000000..7a05bf7 Binary files /dev/null and b/tests/ragger/snapshots/stax/domain_name_wrong_addr/00003.png differ diff --git a/tests/ragger/test_domain_name.py b/tests/ragger/test_domain_name.py index 317f72d..be08ebc 100644 --- a/tests/ragger/test_domain_name.py +++ b/tests/ragger/test_domain_name.py @@ -1,7 +1,11 @@ import pytest from ragger.error import ExceptionRAPDU -from app.client import EthereumClient, StatusWord -from app.setting import SettingType +from ragger.firmware import Firmware +from ragger.backend import BackendInterface +from ragger.navigator import Navigator, NavInsID +from app.client import EthAppClient, StatusWord, ROOT_SCREENSHOT_PATH +from app.settings import SettingID, settings_toggle +import app.response_parser as ResponseParser import struct # Values used across all tests @@ -20,108 +24,182 @@ AMOUNT = 1.22 def verbose(request) -> bool: return request.param -def common(app_client: EthereumClient) -> int: +def common(app_client: EthAppClient) -> int: if app_client._client.firmware.device == "nanos": pytest.skip("Not supported on LNS") - return app_client.get_challenge() + with app_client.get_challenge(): + pass + return ResponseParser.challenge(app_client.response().data) -def test_send_fund(app_client: EthereumClient, verbose: bool): +def test_send_fund(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + verbose: bool): + app_client = EthAppClient(backend) challenge = common(app_client) if verbose: - app_client.settings_set({ - SettingType.VERBOSE_ENS: True - }) + settings_toggle(firmware, navigator, [SettingID.VERBOSE_ENS]) - app_client.provide_domain_name(challenge, NAME, ADDR) + with app_client.provide_domain_name(challenge, NAME, ADDR): + pass - app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - ADDR, - AMOUNT, - CHAIN_ID, - "domain_name_verbose_" + str(verbose)) + with app_client.send_fund(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + ADDR, + AMOUNT, + CHAIN_ID): + moves = list() + if firmware.device.startswith("nano"): + moves += [ NavInsID.RIGHT_CLICK ] * 4 + if verbose: + moves += [ NavInsID.RIGHT_CLICK ] + moves += [ NavInsID.BOTH_CLICK ] + else: + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] * 2 + if verbose: + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] + moves += [ NavInsID.USE_CASE_REVIEW_CONFIRM ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + "domain_name_verbose_" + str(verbose), + moves) -def test_send_fund_wrong_challenge(app_client: EthereumClient): + +def test_send_fund_wrong_challenge(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) caught = False challenge = common(app_client) try: - app_client.provide_domain_name(~challenge & 0xffffffff, NAME, ADDR) + with app_client.provide_domain_name(~challenge & 0xffffffff, NAME, ADDR): + pass except ExceptionRAPDU as e: assert e.status == StatusWord.INVALID_DATA else: assert False # An exception should have been raised -def test_send_fund_wrong_addr(app_client: EthereumClient): + +def test_send_fund_wrong_addr(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str): + app_client = EthAppClient(backend) challenge = common(app_client) - app_client.provide_domain_name(challenge, NAME, ADDR) + with app_client.provide_domain_name(challenge, NAME, ADDR): + pass addr = bytearray(ADDR) addr.reverse() - app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - addr, - AMOUNT, - CHAIN_ID, - "domain_name_wrong_addr") + with app_client.send_fund(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + addr, + AMOUNT, + CHAIN_ID): + moves = list() + if firmware.device.startswith("nano"): + moves += [ NavInsID.RIGHT_CLICK ] * 4 + moves += [ NavInsID.BOTH_CLICK ] + else: + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] * 2 + moves += [ NavInsID.USE_CASE_REVIEW_CONFIRM ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + "domain_name_wrong_addr", + moves) -def test_send_fund_non_mainnet(app_client: EthereumClient): + +def test_send_fund_non_mainnet(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str): + app_client = EthAppClient(backend) challenge = common(app_client) - app_client.provide_domain_name(challenge, NAME, ADDR) + with app_client.provide_domain_name(challenge, NAME, ADDR): + pass - app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - ADDR, - AMOUNT, - 5, - "domain_name_non_mainnet") + with app_client.send_fund(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + ADDR, + AMOUNT, + 5): + moves = list() + if firmware.device.startswith("nano"): + moves += [ NavInsID.RIGHT_CLICK ] * 5 + moves += [ NavInsID.BOTH_CLICK ] + else: + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] * 3 + moves += [ NavInsID.USE_CASE_REVIEW_CONFIRM ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + "domain_name_non_mainnet", + moves) -def test_send_fund_domain_too_long(app_client: EthereumClient): + +def test_send_fund_domain_too_long(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) challenge = common(app_client) try: - app_client.provide_domain_name(challenge, "ledger" + "0"*25 + ".eth", ADDR) + with app_client.provide_domain_name(challenge, "ledger" + "0"*25 + ".eth", ADDR): + pass except ExceptionRAPDU as e: assert e.status == StatusWord.INVALID_DATA else: assert False # An exception should have been raised -def test_send_fund_domain_invalid_character(app_client: EthereumClient): + +def test_send_fund_domain_invalid_character(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) challenge = common(app_client) try: - app_client.provide_domain_name(challenge, "l\xe8dger.eth", ADDR) + with app_client.provide_domain_name(challenge, "l\xe8dger.eth", ADDR): + pass except ExceptionRAPDU as e: assert e.status == StatusWord.INVALID_DATA else: assert False # An exception should have been raised -def test_send_fund_uppercase(app_client: EthereumClient): + +def test_send_fund_uppercase(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) challenge = common(app_client) try: - app_client.provide_domain_name(challenge, NAME.upper(), ADDR) + with app_client.provide_domain_name(challenge, NAME.upper(), ADDR): + pass except ExceptionRAPDU as e: assert e.status == StatusWord.INVALID_DATA else: assert False # An exception should have been raised -def test_send_fund_domain_non_ens(app_client: EthereumClient): + +def test_send_fund_domain_non_ens(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) challenge = common(app_client) try: - app_client.provide_domain_name(challenge, "ledger.hte", ADDR) + with app_client.provide_domain_name(challenge, "ledger.hte", ADDR): + pass except ExceptionRAPDU as e: assert e.status == StatusWord.INVALID_DATA else: diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 29cb843..6ff0bc7 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -2,10 +2,17 @@ import pytest import os import fnmatch from typing import List -from app.client import EthereumClient, SettingType +from ragger.firmware import Firmware +from ragger.backend import BackendInterface +from ragger.navigator import Navigator, NavInsID +from app.client import EthAppClient +from app.settings import SettingID, settings_toggle from eip712 import InputData from pathlib import Path from configparser import ConfigParser +import app.response_parser as ResponseParser +from functools import partial +import time BIP32_PATH = "m/44'/60'/0'/0/0" @@ -30,19 +37,52 @@ def filtering(request) -> bool: return request.param -def test_eip712_legacy(app_client: EthereumClient): - v, r, s = app_client.eip712_sign_legacy( - BIP32_PATH, - bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'), - bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8') - ) +def test_eip712_legacy(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator): + app_client = EthAppClient(backend) + with app_client.eip712_sign_legacy( + BIP32_PATH, + bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'), + bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8')): + moves = list() + if firmware.device.startswith("nano"): + moves += [ NavInsID.RIGHT_CLICK ] + if firmware.device == "nanos": + screens_per_hash = 4 + else: + screens_per_hash = 2 + moves += [ NavInsID.RIGHT_CLICK ] * screens_per_hash * 2 + moves += [ NavInsID.BOTH_CLICK ] + else: + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] * 2 + moves += [ NavInsID.USE_CASE_REVIEW_CONFIRM ] + navigator.navigate(moves) + + v, r, s = ResponseParser.signature(app_client.response().data) assert v == bytes.fromhex("1c") assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64") assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") -def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, filtering: bool): - if app_client._client.firmware.device == "nanos": + +def autonext(fw: Firmware, nav: Navigator): + moves = list() + if fw.device.startswith("nano"): + moves = [ NavInsID.RIGHT_CLICK ] + else: + moves = [ NavInsID.USE_CASE_REVIEW_TAP ] + nav.navigate(moves, screen_change_before_first_instruction=False, screen_change_after_last_instruction=False) + + +def test_eip712_new(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + input_file: Path, + verbose: bool, + filtering: bool): + app_client = EthAppClient(backend) + if firmware.device == "nanos": pytest.skip("Not supported on LNS") else: test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1])) @@ -63,12 +103,25 @@ def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, if not filtering or Path(filter_file).is_file(): if verbose: - app_client.settings_set({ - SettingType.VERBOSE_EIP712: True - }) + settings_toggle(firmware, navigator, [SettingID.VERBOSE_EIP712]) - assert InputData.process_file(app_client, input_file, filter_file) == True - v, r, s = app_client.eip712_sign_new(BIP32_PATH) + assert InputData.process_file(app_client, + input_file, + filter_file, + partial(autonext, firmware, navigator)) == True + with app_client.eip712_sign_new(BIP32_PATH, verbose): + time.sleep(0.5) # tight on timing, needed by the CI otherwise might fail sometimes + moves = list() + if firmware.device.startswith("nano"): + if not verbose and not filtering: # need to skip the message hash + moves = [ NavInsID.RIGHT_CLICK ] * 2 + moves += [ NavInsID.BOTH_CLICK ] + else: + if not verbose and not filtering: # need to skip the message hash + moves += [ NavInsID.USE_CASE_REVIEW_TAP ] + moves += [ NavInsID.USE_CASE_REVIEW_CONFIRM ] + navigator.navigate(moves) + v, r, s = ResponseParser.signature(app_client.response().data) #print("[signature]") #print("v = %s" % (v.hex())) #print("r = %s" % (r.hex())) diff --git a/tools/build_sdk.py b/tools/build_sdk.py index a808e4a..552bb25 100755 --- a/tools/build_sdk.py +++ b/tools/build_sdk.py @@ -82,10 +82,13 @@ def extract_from_c_files(sources, nodes_to_extract): def merge_headers(sources, nodes_to_extract): includes = [ + '#include ', + '#include ', '#include "os.h"', '#include "cx.h"', - '#include ', - '#include ' + '#ifdef HAVE_NBGL', + '#include "nbgl_types.h"', + '#endif' ] body = extract_from_headers(sources, nodes_to_extract) @@ -156,11 +159,12 @@ if __name__ == "__main__": "src/shared_context.h", "src/eth_plugin_internal.h", "src/nft.h", + "src/swap_lib_calls.h" ] nodes_to_extract = { "#define": ["MAX_TICKER_LEN", "ADDRESS_LENGTH", "INT256_LENGTH", "WEI_TO_ETHER", "SELECTOR_SIZE", "PARAMETER_LENGTH", "RUN_APPLICATION", "COLLECTION_NAME_MAX_LEN"], "typedef enum": [], - "typedef struct": ["tokenDefinition_t", "txInt256_t", "txContent_t", "nftInfo_t"], + "typedef struct": ["tokenDefinition_t", "txInt256_t", "txContent_t", "nftInfo_t", "caller_app_t"], "typedef union": ["extraInfo_t"], "__attribute__((no_instrument_function)) inline": ["int allzeroes"], "const": ["HEXDIGITS"],