From 8d0544bf68906f2a47a9013409792bf986272028 Mon Sep 17 00:00:00 2001 From: BTChip github Date: Sat, 27 Jun 2020 13:24:04 +0200 Subject: [PATCH] Merge Starkware branch --- Makefile | 35 +- blue_app_ropsten.gif | Bin 0 -> 405 bytes doc/eth_starkware_extensions.asc | 207 ++ glyphs/blue_badge_ropsten.gif | Bin 0 -> 347 bytes glyphs/nanos_badge_ropsten.gif | Bin 0 -> 1132 bytes nanos_app_ropsten.gif | Bin 0 -> 1125 bytes nanox_app_ropsten.gif | Bin 0 -> 1116 bytes ropsten.png | Bin 0 -> 2117 bytes src/apdu_constants.h | 58 + src/main.c | 2305 +---------------- src/poorstream.c | 43 + src/poorstream.h | 21 + src/shared_context.h | 169 ++ src/stark_crypto.c | 85 + src/stark_crypto.h | 25 + src/stark_utils.c | 141 + src/stark_utils.h | 25 + src/tokens.c | 15 + src/tokens.h | 13 +- src/ui_blue.c | 564 ++++ src/ui_blue.h | 45 + src/ui_callbacks.h | 20 + src/ui_flow.c | 131 + src/ui_flow.h | 46 + src_common/ethUtils.c | 4 +- src_common/ethUtils.h | 20 +- .../erc20_approval/ui_flow_erc20_approval.c | 73 + .../cmd_getAppConfiguration.c | 29 + src_features/getPublicKey/cmd_getPublicKey.c | 76 + .../getPublicKey/feature_getPublicKey.h | 4 + .../getPublicKey/logic_getPublicKey.c | 17 + .../getPublicKey/ui_common_getPublicKey.c | 27 + .../getPublicKey/ui_flow_getPublicKey.c | 47 + .../cmd_provideTokenInfo.c | 179 ++ src_features/signMessage/cmd_signMessage.c | 105 + .../signMessage/ui_common_signMessage.c | 53 + .../signMessage/ui_flow_signMessage.c | 45 + src_features/signTx/cmd_signTx.c | 81 + src_features/signTx/feature_signTx.h | 5 + src_features/signTx/logic_signTx.c | 484 ++++ src_features/signTx/ui_common_signTx.c | 103 + src_features/signTx/ui_flow_signTx.c | 165 ++ .../ui_flow_stark_deposit.c | 126 + .../ui_flow_stark_escape.c | 136 + .../ui_flow_stark_register.c | 124 + .../ui_flow_stark_verify_escape.c | 77 + .../ui_flow_stark_verifyVaultId.c | 116 + .../ui_flow_stark_withdrawal.c | 110 + .../cmd_stark_getPublicKey.c | 65 + .../feature_stark_getPublicKey.h | 4 + .../logic_stark_getPublicKey.c | 15 + .../ui_common_stark_getPublicKey.c | 20 + .../ui_flow_stark_getPublicKey.c | 49 + .../cmd_stark_provideQuantum.c | 42 + src_features/stark_sign/cmd_stark_sign.c | 140 + .../stark_sign/ui_common_stark_sign.c | 28 + src_features/stark_sign/ui_flow_stark_sign.c | 165 ++ 57 files changed, 4439 insertions(+), 2243 deletions(-) create mode 100644 blue_app_ropsten.gif create mode 100644 doc/eth_starkware_extensions.asc create mode 100644 glyphs/blue_badge_ropsten.gif create mode 100644 glyphs/nanos_badge_ropsten.gif create mode 100644 nanos_app_ropsten.gif create mode 100644 nanox_app_ropsten.gif create mode 100755 ropsten.png create mode 100644 src/apdu_constants.h create mode 100644 src/poorstream.c create mode 100644 src/poorstream.h create mode 100644 src/shared_context.h create mode 100644 src/stark_crypto.c create mode 100644 src/stark_crypto.h create mode 100644 src/stark_utils.c create mode 100644 src/stark_utils.h create mode 100644 src/ui_blue.c create mode 100644 src/ui_blue.h create mode 100644 src/ui_callbacks.h create mode 100644 src/ui_flow.c create mode 100644 src/ui_flow.h create mode 100644 src_features/erc20_approval/ui_flow_erc20_approval.c create mode 100644 src_features/getAppConfiguration/cmd_getAppConfiguration.c create mode 100644 src_features/getPublicKey/cmd_getPublicKey.c create mode 100644 src_features/getPublicKey/feature_getPublicKey.h create mode 100644 src_features/getPublicKey/logic_getPublicKey.c create mode 100644 src_features/getPublicKey/ui_common_getPublicKey.c create mode 100644 src_features/getPublicKey/ui_flow_getPublicKey.c create mode 100644 src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c create mode 100644 src_features/signMessage/cmd_signMessage.c create mode 100644 src_features/signMessage/ui_common_signMessage.c create mode 100644 src_features/signMessage/ui_flow_signMessage.c create mode 100644 src_features/signTx/cmd_signTx.c create mode 100644 src_features/signTx/feature_signTx.h create mode 100644 src_features/signTx/logic_signTx.c create mode 100644 src_features/signTx/ui_common_signTx.c create mode 100644 src_features/signTx/ui_flow_signTx.c create mode 100644 src_features/stark_contract_deposit/ui_flow_stark_deposit.c create mode 100644 src_features/stark_contract_escape/ui_flow_stark_escape.c create mode 100644 src_features/stark_contract_register/ui_flow_stark_register.c create mode 100644 src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c create mode 100644 src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c create mode 100644 src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c create mode 100644 src_features/stark_getPublicKey/cmd_stark_getPublicKey.c create mode 100644 src_features/stark_getPublicKey/feature_stark_getPublicKey.h create mode 100644 src_features/stark_getPublicKey/logic_stark_getPublicKey.c create mode 100644 src_features/stark_getPublicKey/ui_common_stark_getPublicKey.c create mode 100644 src_features/stark_getPublicKey/ui_flow_stark_getPublicKey.c create mode 100644 src_features/stark_provideQuantum/cmd_stark_provideQuantum.c create mode 100644 src_features/stark_sign/cmd_stark_sign.c create mode 100644 src_features/stark_sign/ui_common_stark_sign.c create mode 100644 src_features/stark_sign/ui_flow_stark_sign.c diff --git a/Makefile b/Makefile index 10112a2..0c204fa 100755 --- a/Makefile +++ b/Makefile @@ -25,11 +25,11 @@ APP_LOAD_PARAMS= --curve secp256k1 $(COMMON_LOAD_PARAMS) # Allow the app to use path 45 for multi-sig (see BIP45). APP_LOAD_PARAMS += --path "45'" # Samsung temporary implementation for wallet ID on 0xda7aba5e/0xc1a551c5 -APP_LOAD_PARAMS += --path "1517992542'/1101353413'" +#APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 -APPVERSION_N=2 -APPVERSION_P=13 +APPVERSION_N=3 +APPVERSION_P=7 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) @@ -41,9 +41,25 @@ ifeq ($(CHAIN),ethereum) # Lock the application on its standard path for 1.5. Please complain if non compliant APP_LOAD_PARAMS += --path "44'/60'" DEFINES += CHAINID_UPCASE=\"ETHEREUM\" CHAINID_COINNAME=\"ETH\" CHAIN_KIND=CHAIN_KIND_ETHEREUM CHAIN_ID=0 +# Starkware integration +APP_LOAD_PARAMS += --path "2645'/579218131'" +DEFINES += HAVE_STARKWARE +DEFINES += STARK_BIP32_PATH_0=0x80000A55 STARK_BIP32_PATH_1=0xA2862AD3 APPNAME = "Ethereum" DEFINES_LIB= APP_LOAD_FLAGS=--appFlags 0xa40 +else ifeq ($(CHAIN),ropsten) +APP_LOAD_PARAMS += --path "44'/60'" +DEFINES += CHAINID_UPCASE=\"ETHEREUM\" CHAINID_COINNAME=\"ETH\" CHAIN_KIND=CHAIN_KIND_ETHEREUM CHAIN_ID=3 +# Starkware integration +APP_LOAD_PARAMS += --path "2645'/579218131'" +DEFINES += HAVE_STARKWARE +# Keep for Starkware Ropsten tests +DEFINES += HAVE_TOKENS_EXTRA_LIST +DEFINES += STARK_BIP32_PATH_0=0x80000A55 STARK_BIP32_PATH_1=0xA2862AD3 +APPNAME = "Eth Ropsten" +DEFINES_LIB= +APP_LOAD_FLAGS=--appFlags 0xa40 else ifeq ($(CHAIN),ellaism) APP_LOAD_PARAMS += --path "44'/163'" DEFINES += CHAINID_UPCASE=\"ELLA\" CHAINID_COINNAME=\"ELLA\" CHAIN_KIND=CHAIN_KIND_ELLAISM CHAIN_ID=64 @@ -167,7 +183,7 @@ DEFINES += CHAINID_UPCASE=\"THUNDERCORE\" CHAINID_COINNAME=\"TT\" CHAIN_KIND=CHA APPNAME = "ThunderCore" else ifeq ($(filter clean,$(MAKECMDGOALS)),) -$(error Unsupported CHAIN - use ethereum, ethereum_classic, expanse, poa, artis_sigma1, artis_tau1, rsk, rsk_testnet, ubiq, wanchain, kusd, musicoin, pirl, akroma, atheios, callisto, ethersocial, ellaism, ether1, ethergem, gochain, mix, reosc, hpb, tomochain, tobalaba, dexon, volta, ewc, webchain, thundercore) +$(error Unsupported CHAIN - use ethereum, ropsten, ethereum_classic, expanse, poa, artis_sigma1, artis_tau1, rsk, rsk_testnet, ubiq, wanchain, kusd, musicoin, pirl, akroma, atheios, callisto, ethersocial, ellaism, ether1, ethergem, gochain, mix, reosc, hpb, tomochain, tobalaba, dexon, volta, ewc, webchain, thundercore) endif endif @@ -196,7 +212,7 @@ all: default DEFINES += OS_IO_SEPROXYHAL DEFINES += HAVE_BAGL HAVE_SPRINTF -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU +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) # U2F @@ -225,12 +241,13 @@ DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX DEFINES += HAVE_UX_FLOW else -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72 endif # Enabling debug PRINTF -DEBUG = 0 +DEBUG:=0 ifneq ($(DEBUG),0) +DEFINES += HAVE_STACK_OVERFLOW_CHECK ifeq ($(TARGET_NAME),TARGET_NANOX) DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf else @@ -278,7 +295,7 @@ LDLIBS += -lm -lgcc -lc 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 +APP_SOURCE_PATH += src_common src src_features SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f ifeq ($(TARGET_NAME),TARGET_NANOX) SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl @@ -310,4 +327,4 @@ include $(BOLOS_SDK)/Makefile.rules dep/%.d: %.c Makefile listvariants: - @echo VARIANTS CHAIN ethereum ethereum_classic expanse poa artis_sigma1 artis_tau1 rsk rsk_testnet ubiq wanchain kusd pirl akroma atheios callisto ethersocial ether1 gochain musicoin ethergem mix ellaism reosc hpb tomochain tobalaba dexon volta ewc webchain thundercore + @echo VARIANTS CHAIN ethereum ropsten ethereum_classic expanse poa artis_sigma1 artis_tau1 rsk rsk_testnet ubiq wanchain kusd pirl akroma atheios callisto ethersocial ether1 gochain musicoin ethergem mix ellaism reosc hpb tomochain tobalaba dexon volta ewc webchain thundercore diff --git a/blue_app_ropsten.gif b/blue_app_ropsten.gif new file mode 100644 index 0000000000000000000000000000000000000000..c3d5c4e1dc70b9a674bb9e74c88711501e93b7e2 GIT binary patch literal 405 zcmV;G0c!q7Nk%w1VKM+R0J8u9^ZELJ+~uw4?$hz~=l1sw!_t@G={?NZ0Kd;}+2c~t z-7v}50Km}s`T76<{{X$uEC2ui05SkF000F4u*pdq5kakjp8p_-6pIoFM5V3`U>spA z&)#7G>}nKr?|TJ9ZZd%Vj=jSmH{c16oa1f**9fP_ zzyzX%BcuK)qLzcE3h0?N}2 z36~4CMIQ>-vH{;73b7ALZqpvLhr|T}mD21$&JNKC0rv~_AqtHE+;foNQx{a6R$Up< z#TcbzYWj6y)aVhE5@UFI5@Vu|r9lcxKhf)g!rze;7EOZq(nckXGNoLCSTkXrOb=xy zr1Zy +Application version 1.3.0 - 15th of February 2020 + +## 1.3.0 + - Initial release + +## About + +This specification describes the APDU messages interface implementing the Starkware extensions for the Ethereum appilcation + +## Modified general purpose APDUs + +### GET APP CONFIGURATION + +#### Description + +This command returns specific application configuration + +It is modified to notify Stark extensions support on flag 0x04 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 06 | 00 | 00 | 00 | 04 +|============================================================================================================================== + +'Input data' + +None + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Flags + 0x01 : arbitrary data signature enabled by user + + 0x02 : ERC 20 Token information needs to be provided externally + + 0x04 : Stark extensions are supported + | 01 +| Application major version | 01 +| Application minor version | 01 +| Application patch version | 01 +|============================================================================================================================== + + +## Additional APDUs + +Additional APDUs use the APDU CLA F0 + +### GET STARK PUBLIC KEY + +#### Description + +This command returns the public Stark key (X and Y coordinates) for the given BIP 32 path. + +The key can be optionally checked on the device before being returned - in that case, only the X coordinate is displayed, as this is what is used in the contract + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| F0 | 02 | 00 : return address + + 01 : display address and confirm before returning + | 00 | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +|============================================================================================================================== + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Stark key | 65 +|============================================================================================================================== + +### SIGN STARK MESSAGE + +#### Description + +This command signs an order or a transfer on the Starkware curve. + +The contract addressed associated to the token shall have be provisioned previously with the PROVIDE ERC 20 TOKEN INFORMATION command or this command will fail. + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| F0 | 04 | + 01 : sign a Stark Order + + 02 : sign a Stark Transfer + + | 00 | variable | variable +|============================================================================================================================== + +'Input data for a Stark Order' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| Contract address of the token to be sold (or 00..00 for ETH) | 20 +| Quantization of the token to be sold (big endian) | 32 +| Contract address of the token to be bought (or 00..00 for ETH) | 20 +| Quantization of the token to be bought (big endian) | 32 +| ID of the source vault (big endian encoded) | 4 +| ID of the destination vault (big endian encoded) | 4 +| Amount to be sold (big endian encoded) | 8 +| Amount to buy (big endian encoded) | 8 +| Transaction nonce (big endian encoded) | 4 +| Transaction timestamp (big endian encoded) | 4 +|============================================================================================================================== + +'Input data for a Stark Transfer' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| Contract address of the token to be transferred (or 00..00 for ETH) | 20 +| Quantization of the token to be transferred (big endian) | 32 +| Token target public key | 32 +| ID of the source vault (big endian encoded) | 4 +| ID of the destination vault (big endian encoded) | 4 +| Amount to be transferred (big endian encoded) | 8 +| Transaction nonce (big endian encoded) | 4 +| Transaction timestamp (big endian encoded) | 4 +|============================================================================================================================== + + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| RFU (00) | 1 +| r | 32 +| s | 32 +|============================================================================================================================== + + +### PROVIDES QUANTUM + +#### Description + +This command provides quantization data used to compute a tokenId and provide additional information to the user before signing a transaction performing a deposit or withdrawal call on a Stark powered smart contract. + +It shall be called following a PROVIDE ERC 20 TOKEN INFORMATION command called for the associated contract + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| F0 | 08 | + 00 + + | 00 | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Contract address used in the next transaction | 20 +| Quantization to be used in the next transaction | 32 +|============================================================================================================================== + + +'Output data' + +None diff --git a/glyphs/blue_badge_ropsten.gif b/glyphs/blue_badge_ropsten.gif new file mode 100644 index 0000000000000000000000000000000000000000..5174214f342c9938ccb2b1ebf96701f5d698edfb GIT binary patch literal 347 zcmV-h0i^y%Nk%w1VKM+R0FeLy-QC^o?d{CW%=!8GA^8LV00000EC2ui05SkF00092 zoR4`C00N^6s@j`e++5;sHQhJ@6bL@tbQ+AdqF1oCpSaobphD)p=m`KoUapv+d1O5z zveHCFpO)#Rr=up973EO-!CJWBYXo{OgUADK_U7EV5vus^VVC+4kvC>8muB=;w2_yx zH_%rkB;Xfv=2hgiQ3%11xI=P?p@PF{M)Igo=Ltk22%;p$gqnHN5$Jj;6I(m`ieZaF zi8{*1!H5bv#__9w!=@@hE7h|4eB9+eyhIT>_Cr6{}d=wluJ#9R0zBjq{0eKK!x@aF0`n{N~eP?Pc()( tqJ`2!I)>^s6f$y4$$5=TN;G7`#*0S!z$C*}gQ7s394XFB7ZXVV06SD}p<)04 literal 0 HcmV?d00001 diff --git a/glyphs/nanos_badge_ropsten.gif b/glyphs/nanos_badge_ropsten.gif new file mode 100644 index 0000000000000000000000000000000000000000..776f6454b3f247e94570e722dd801ddd5f5f5ced GIT binary patch literal 1132 zcmZ?wbhEHbh+i z#(Mch>H3D2mX`VkM*2oZx|Z5PFSp>IEf;+ybD@E~!PCWvMA{Mftf3V2@j6VsVR^ zk&}_JqpJzfJY(!`f#^-a?G^)^ZqWxiMjsTtNYM=w0;VAl6P|d19C-3i%>$zB`;K7M%r?(Lh`uU@`*{_N?K$B!O9xPR~No!hr=-nf44>Xplv zE?zi)?(CVz2)%Hf~tIZta@Yt5&X9zHI4| z#fug$m_KjsoY}Kx&X_)J>XgZoCQj(@>+R|8>g;H5Yi((6YHX;ltF5W7s;nq4D=jH5 zDlEv)%gxEo%FIYlOHD~mN=%54i;annii`*k3k?Yl3Jmb~^Y!ue^7L?bb9Hfca&)k_ zv$e6dva~QaGc_?bGBnWF)78<|($r8_Q&mw`QdE$ala-N{l9Uh^6BQ8_5)|O)#mZm}0O8n;egFUf literal 0 HcmV?d00001 diff --git a/nanos_app_ropsten.gif b/nanos_app_ropsten.gif new file mode 100644 index 0000000000000000000000000000000000000000..8f8fa310bac0bf66875ff867705033cf5c9331ca GIT binary patch literal 1125 zcmZ?wbhEHb6krfwXkcLY|NlP&1B2p!?g-xi1((Eh+i z#(Mch>H3D2mX`VkM*2oZx|Z5PB_f>IEf;+ybD@E~!PCWvMA{Mftf3V2@j6VsVS1 zi=~sRi7RgNpn6lVxCN@$5vN=9fsWA!MK4lx!-Rlo2*iXZULXga{8RIQX}$=UxEcTd z`}^njub)4@fBX97^QVs=-oJbM=Jl(WFP=Yp`sDGWhY#-GyL;#Mt(!NlU%Ptc@}-Lx z&YwGb=JctPCypOGdgSn-g9rBS+q-A?uAMu!Z`-%OrteG>WPn$Yr@}!9q`ulo&y1P0%+S^)NnwuIM>g#H2s;eq1%F9Yiii-*h z^7C?Yva>QX($i8?l9Lh>;^SgtqN5@s!oxyCf`bAB{QZ1=yuCa<+}&JVoShsU?CorA ztgS39%*{+qjExKp^!0Rgw6!!f)YVi~l$8_}tP)JEENd(gW?JEirle1Gx6p~WY zGxKbf-tXS8q>!0ns}yePYv5bpoSKp8QB{;0T;&&%T$P<{nWAKGr(jcI1=O2clBiIT zo0C^;Rbi`?n3A8AY6WEHrj{h?D=C0glw{i~If5hXl~0)0b01CWOxKFuxg^~J9=Hy5lL z7!<`NL8%DWVl}roq_QAYKPa_0zqBYh6{uVpWK)5ab5UwyNq$jCetr%t2m>`A zMVV!(DQ-pixe8!^TV=xC0(Xj`i=~sRi7U)BRHuMl0@0d+@{(ZdJ#@7=v~`_|1H*RNf@a{1E53+K6ZM9qnzcEzM1h4fS=kHPuy>73F26CB;RB1^IcoIoVm68R==MDalER3Gs2U zG0{A;Cd`0selzKHgrQ9`0_gF3wJl4)%7oHr7^_7UpKACdNjF2KsusI@(&A z8tQ7QD#}WV3i5KYGSX6#65?W_BEmv~0{ncuJUrZ7oE+?ItSrn-z=A>tR6u}A4F)Fh emj0Ei1$$&n{0{w+*v5UoB6PBO+T}_y25SKJ#)+{2 literal 0 HcmV?d00001 diff --git a/ropsten.png b/ropsten.png new file mode 100755 index 0000000000000000000000000000000000000000..60e111ce0b9f3377252836376f1fe9820c8ca1c9 GIT binary patch literal 2117 zcmV-L2)g%)P)L{S^M7{t z_uBa+lTLScW@p}a-|xNO@BQAq9WP(Lq+U^8TplN)5#FjqRN-x`XBQDplZaZptq{=~ zHm6=&>eU5wO_1{9G75d1x2m8*PfL?Uf;dY~y|&hkFUeI97U3KbUD^TyL;-<5BcfY* zij#{VY>K9c=xi=mWgiV9n#;qqWfFvim?5ImRu^Y}+(vn15hj%&Y?|h_agB%qHD)S} zDJvyCpuD(zi5s^ zX104N^$=fKcL>6@qX-oCj?-M`ZS)#4wMB1SG96{!ZbP_d^=P}7PM&>7iXL(8_=vk%j;n$7 zf_!rL0F4grayRT-Oot#<8}H+dS6`uzhF@?0kA1vmDXs>dAXbLh=Z}xjo`Ie1|FJ)B zujz6%DCwS}Euc1>tPJ|D7O$zb#wMUeiSumjy6}OWq3s3UD=Rg}B1WtfL^oAB7Mm_t z5UU#WGy5wvv9EKeW`5&WcT>S<@L9{KmUudV6~GM5A3M}>sT=?NOIIH~b~o&I2zBg` zZKkVYl^|#vbW?TZ{?Ft*d+u)7SJ@NM)(FHE#ELHO$-P4~sr8<2KK`BV{_$7)f9&Hm z6&! zhBc2~GU5e6BzIbC2Nr&F=<_GRX|i|^pHaa%yr{5b#0#QFaa~jOU?cK4e8y+GEbvW4 z3j#AUs{N|byOnJ%0g-ysokg1}#xJ#w()wbjk1zV;vq zICfPVZ^w5?^i9MGVlq`98yvu-=;x4XxQ&p_wT<=zWcTz1p*T4nl+=_$4pAb0qcgo)?~_7EFBiuq&O2 zq=;zLO->tVJZbqt9wxkG>nL!phGAeUFXuB4#Izx3Ows77rmHrzo8JHCzU#Q9F-5v* zgCFwMyT>{{lehDk6%i)O1A^Q*@n*+s`1R_^3}KCRSmO_`T=#b_-1{kXIJnn{5(M0Q z`z>F$G|awx4LML7cpXzag>Lov?|WDY# zX*_8Ll=Ne8&ThuZvLaRxQyNdw*-@rhR&mpa`i1DcETr-1j$zz19#a|-&XpvMs2_;W zT)bjP=d#_Rnh>iIO6@F=&=}y(KF^mo;d1 zRM0FdNs-(bv<7Sv#G4(ZMugQgp18BD1k)<<1Cy=6G&>3*byFIVq_BESR!Y1yoK={@ zDk{>$BO$C=DTou%HsC$WOqIu)_lQ4HVky-n=0}&@C31{VfR?qJaFtwj^sw*YTyadv$bCH#+x0Lb(WO?Tn)T5AZrC7OQN_j za5YdLHNH|TZ`3!MPQBLg5KQ8Qb_@DLyX;j#ZWUtmjdt0$1>vXjH?~zV$e^LFMWv62 z7}rHA=qep$-XRFDhjhCr|DJKXSc6Iy;cHOQ|FxuOOS(_Pv=DyKnEDhiept{QoS^l- zjDm2hUn~&TJ*G^nd{S7}iBqq^S-EcCy^7~Lm#vY=1@4QGO0Kj+BtgW<%Nn z -#include -#include - -#include "os.h" -#include "cx.h" -#include "ethUstream.h" -#include "ethUtils.h" -#include "uint256.h" -#include "tokens.h" -#include "chainConfig.h" +#include "shared_context.h" +#include "apdu_constants.h" +#include "ui_callbacks.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif #include "os_io_seproxyhal.h" #include "glyphs.h" #include "utils.h" +#ifdef HAVE_STARKWARE +#include "stark_crypto.h" +#endif + unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; -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); -unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e); void ui_idle(void); uint32_t set_result_get_publicKey(void); void finalizeParsing(bool); -#define MAX_BIP32_PATH 10 - -#define APP_FLAG_DATA_ALLOWED 0x01 -#define APP_FLAG_EXTERNAL_TOKEN_NEEDED 0x02 - -#define CLA 0xE0 -#define INS_GET_PUBLIC_KEY 0x02 -#define INS_SIGN 0x04 -#define INS_GET_APP_CONFIGURATION 0x06 -#define INS_SIGN_PERSONAL_MESSAGE 0x08 -#define INS_PROVIDE_ERC20_TOKEN_INFORMATION 0x0A -#define P1_CONFIRM 0x01 -#define P1_NON_CONFIRM 0x00 -#define P2_NO_CHAINCODE 0x00 -#define P2_CHAINCODE 0x01 -#define P1_FIRST 0x00 -#define P1_MORE 0x80 - -#define COMMON_CLA 0xB0 -#define COMMON_INS_GET_WALLET_ID 0x04 - -#define OFFSET_CLA 0 -#define OFFSET_INS 1 -#define OFFSET_P1 2 -#define OFFSET_P2 3 -#define OFFSET_LC 4 -#define OFFSET_CDATA 5 - -#define WEI_TO_ETHER 18 - -static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb }; - -static const uint8_t const TOKEN_SIGNATURE_PUBLIC_KEY[] = { -// production key 2019-01-11 03:07PM (erc20signer) - 0x04, - - 0x5e,0x6c,0x10,0x20,0xc1,0x4d,0xc4,0x64, - 0x42,0xfe,0x89,0xf9,0x7c,0x0b,0x68,0xcd, - 0xb1,0x59,0x76,0xdc,0x24,0xf2,0x4c,0x31, - 0x6e,0x7b,0x30,0xfe,0x4e,0x8c,0xc7,0x6b, - - 0x14,0x89,0x15,0x0c,0x21,0x51,0x4e,0xbf, - 0x44,0x0f,0xf5,0xde,0xa5,0x39,0x3d,0x83, - 0xde,0x53,0x58,0xcd,0x09,0x8f,0xce,0x8f, - 0xd0,0xf8,0x1d,0xaa,0x94,0x97,0x91,0x83 -}; - -typedef struct tokenContext_t { - uint8_t data[4 + 32 + 32]; - uint32_t dataFieldPos; -} tokenContext_t; - -typedef struct rawDataContext_t { - uint8_t data[32]; - uint8_t fieldIndex; - uint8_t fieldOffset; -} rawDataContext_t; - -typedef struct publicKeyContext_t { - cx_ecfp_public_key_t publicKey; - uint8_t address[41]; - uint8_t chainCode[32]; - bool getChaincode; -} publicKeyContext_t; - -typedef struct transactionContext_t { - uint8_t pathLength; - uint32_t bip32Path[MAX_BIP32_PATH]; - uint8_t hash[32]; - tokenDefinition_t currentToken; -} transactionContext_t; - -typedef struct messageSigningContext_t { - uint8_t pathLength; - uint32_t bip32Path[MAX_BIP32_PATH]; - uint8_t hash[32]; - uint32_t remainingLength; -} messageSigningContext_t; - -union { - publicKeyContext_t publicKeyContext; - transactionContext_t transactionContext; - messageSigningContext_t messageSigningContext; -} tmpCtx; +tmpCtx_t tmpCtx; txContext_t txContext; - -union { - txContent_t txContent; - cx_sha256_t sha2; -} tmpContent; +tmpContent_t tmpContent; +dataContext_t dataContext; +strings_t strings; cx_sha3_t sha3; -union { - tokenContext_t tokenContext; - rawDataContext_t rawDataContext; -} dataContext; - -typedef enum { - APP_STATE_IDLE, - APP_STATE_SIGNING_TX, - APP_STATE_SIGNING_MESSAGE -} app_state_t; - - -volatile uint8_t dataAllowed; -volatile uint8_t contractDetails; -volatile uint8_t appState; -volatile char addressSummary[32]; -volatile bool dataPresent; -volatile bool tokenProvisioned; -volatile bool currentTokenSet; - -bagl_element_t tmp_element; +uint8_t dataAllowed; +uint8_t contractDetails; +uint8_t appState; +#ifdef TARGET_BLUE +char addressSummary[32]; +#endif +bool dataPresent; +contract_call_t contractProvisioned; +#ifdef HAVE_STARKWARE +bool quantumSet; +#endif #ifdef HAVE_UX_FLOW #include "ux.h" @@ -176,1278 +73,27 @@ unsigned int ux_step; unsigned int ux_step_count; #endif // HAVE_UX_FLOW - -typedef struct internalStorage_t { - unsigned char dataAllowed; - unsigned char contractDetails; - uint8_t initialized; -} internalStorage_t; - -typedef struct strData_t { - char fullAddress[43]; - char fullAmount[50]; - char maxFee[50]; -} strData_t; - -typedef struct strDataTmp_t { - char tmp[100]; - char tmp2[40]; -} strDataTmp_t; - -union { - strData_t common; - strDataTmp_t tmp; -} strings; - const internalStorage_t N_storage_real; -#define N_storage (*(volatile internalStorage_t*) PIC(&N_storage_real)) +#ifdef TARGET_BLUE static const char const CONTRACT_ADDRESS[] = "New contract"; +#endif -static const char const SIGN_MAGIC[] = "\x19" - "Ethereum Signed Message:\n"; chain_config_t *chainConfig; -const bagl_element_t* ui_menu_item_out_over(const bagl_element_t* e) { - // the selection rectangle is after the none|touchable - e = (const bagl_element_t*)(((unsigned int)e)+sizeof(bagl_element_t)); - return e; -} - void reset_app_context() { + PRINTF("!!RESET_APP_CONTEXT\n"); appState = APP_STATE_IDLE; - currentTokenSet = false; + os_memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN); + contractProvisioned = CONTRACT_NONE; +#ifdef HAVE_STARKWARE + quantumSet = false; +#endif os_memset((uint8_t*)&txContext, 0, sizeof(txContext)); os_memset((uint8_t*)&tmpContent, 0, sizeof(tmpContent)); } - -#define BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH 10 -#define BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH 8 -#define MAX_CHAR_PER_LINE 25 - -#define COLOR_BG_1 0xF9F9F9 -#define COLOR_APP 0x0ebdcf -#define COLOR_APP_LIGHT 0x87dee6 - -#if defined(TARGET_BLUE) - -unsigned int map_color(unsigned int color) { - switch(color) { - case COLOR_APP: - return chainConfig->color_header; - - case COLOR_APP_LIGHT: - return chainConfig->color_dashboard; - } - return color; -} -void copy_element_and_map_coin_colors(const bagl_element_t* element) { - os_memmove(&tmp_element, element, sizeof(bagl_element_t)); - tmp_element.component.fgcolor = map_color(tmp_element.component.fgcolor); - tmp_element.component.bgcolor = map_color(tmp_element.component.bgcolor); - tmp_element.overfgcolor = map_color(tmp_element.overfgcolor); - tmp_element.overbgcolor = map_color(tmp_element.overbgcolor); -} - -const bagl_element_t *ui_idle_blue_prepro(const bagl_element_t *element) { - copy_element_and_map_coin_colors(element); - if (element->component.userid == 0x01) { - tmp_element.text = chainConfig->header_text; - } - return &tmp_element; -} - -const bagl_element_t ui_idle_blue[] = { - // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, CHAINID_UPCASE, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_SETTINGS, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_settings, NULL, NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 0, 270, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Open your wallet", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x00, 0, 308, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Connect your Ledger Blue and open your", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x00, 0, 331, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "preferred wallet to view your accounts.", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 0, 450, 320, 14, 0, 0, 0 , 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Validation requests will show automatically.", 10, 0, COLOR_BG_1, NULL, NULL, NULL }, -}; - -unsigned int ui_idle_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} -#endif // #if defined(TARGET_BLUE) - - -#if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - - -const ux_menu_entry_t menu_main[]; -const ux_menu_entry_t menu_settings[]; -//const ux_menu_entry_t menu_settings_browser[]; -const ux_menu_entry_t menu_settings_data[]; -const ux_menu_entry_t menu_settings_details[]; - -#ifdef HAVE_U2F - -// change the setting -void menu_settings_data_change(unsigned int enabled) { - dataAllowed = enabled; - nvm_write(&N_storage.dataAllowed, (void*)&dataAllowed, sizeof(uint8_t)); - // go back to the menu entry - UX_MENU_DISPLAY(0, menu_settings, NULL); -} - -void menu_settings_details_change(unsigned int enabled) { - contractDetails = enabled; - nvm_write(&N_storage.contractDetails, (void*)&contractDetails, sizeof(uint8_t)); - // go back to the menu entry - UX_MENU_DISPLAY(0, menu_settings, NULL); -} - -// show the currently activated entry -void menu_settings_data_init(unsigned int ignored) { - UNUSED(ignored); - UX_MENU_DISPLAY(N_storage.dataAllowed?1:0, menu_settings_data, NULL); -} - -void menu_settings_details_init(unsigned int ignored) { - UNUSED(ignored); - UX_MENU_DISPLAY(N_storage.contractDetails?1:0, menu_settings_details, NULL); -} - -const ux_menu_entry_t menu_settings_data[] = { - {NULL, menu_settings_data_change, 0, NULL, "No", NULL, 0, 0}, - {NULL, menu_settings_data_change, 1, NULL, "Yes", NULL, 0, 0}, - UX_MENU_END -}; - -const ux_menu_entry_t menu_settings_details[] = { - {NULL, menu_settings_details_change, 0, NULL, "No", NULL, 0, 0}, - {NULL, menu_settings_details_change, 1, NULL, "Yes", NULL, 0, 0}, - UX_MENU_END -}; - -const ux_menu_entry_t menu_settings[] = { - {NULL, menu_settings_data_init, 0, NULL, "Contract data", NULL, 0, 0}, - {NULL, menu_settings_details_init, 0, NULL, "Display data", NULL, 0, 0}, - {menu_main, NULL, 1, &C_icon_back, "Back", NULL, 61, 40}, - UX_MENU_END -}; -#endif // HAVE_U2F - -const ux_menu_entry_t menu_about[] = { - {NULL, NULL, 0, NULL, "Version", APPVERSION , 0, 0}, - {menu_main, NULL, 2, &C_icon_back, "Back", NULL, 61, 40}, - UX_MENU_END -}; - -const ux_menu_entry_t menu_main[] = { - {NULL, NULL, 0, NULL, "Use wallet to", "view accounts", 0, 0}, - {menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0}, - {menu_about, NULL, 0, NULL, "About", NULL, 0, 0}, - {NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app", NULL, 50, 29}, - UX_MENU_END -}; - -#endif // #if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - -#if defined(TARGET_BLUE) -const bagl_element_t * ui_settings_blue_toggle_data(const bagl_element_t * e) { - // swap setting and request redraw of settings elements - uint8_t setting = N_storage.dataAllowed?0:1; - nvm_write(&N_storage.dataAllowed, (void*)&setting, sizeof(uint8_t)); - - // only refresh settings mutable drawn elements - UX_REDISPLAY_IDX(7); - - // won't redisplay the bagl_none - return 0; -} - -const bagl_element_t * ui_settings_blue_toggle_details(const bagl_element_t * e) { - // swap setting and request redraw of settings elements - uint8_t setting = N_storage.contractDetails?0:1; - nvm_write(&N_storage.contractDetails, (void*)&setting, sizeof(uint8_t)); - - // only refresh settings mutable drawn elements - UX_REDISPLAY_IDX(7); - - // won't redisplay the bagl_none - return 0; -} - - -// don't perform any draw/color change upon finger event over settings -const bagl_element_t* ui_settings_out_over(const bagl_element_t* e) { - return NULL; -} - -unsigned int ui_settings_back_callback(const bagl_element_t* e) { - // go back to idle - ui_idle(); - return 0; -} - -const bagl_element_t ui_settings_blue[] = { - // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "SETTINGS", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_LEFT, 0, COLOR_APP, 0xFFFFFF, ui_settings_back_callback, NULL, NULL}, - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - - {{BAGL_LABELINE , 0x00, 30, 105, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, "Contract data", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x00, 30, 126, 260, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0 }, "Allow contract data in transactions", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 78, 320, 68, 0, 0, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_settings_blue_toggle_data, ui_settings_out_over, ui_settings_out_over }, - - {{BAGL_RECTANGLE, 0x00, 30, 146, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x00, 30, 174, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, "Display data", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x00, 30, 195, 260, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0}, "Display contract data details", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 147, 320, 68, 0, 0, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, NULL, 0, 0xEEEEEE, 0x000000, ui_settings_blue_toggle_details, ui_settings_out_over, ui_settings_out_over}, - - {{BAGL_ICON, 0x02, 258, 167, 32, 18, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_ICON , 0x01, 258, 98, 32, 18, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, 0, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, -}; - -const bagl_element_t * ui_settings_blue_prepro(const bagl_element_t * e) { - copy_element_and_map_coin_colors(e); - // none elements are skipped - if ((e->component.type&(~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { - return 0; - } - // swap icon buffer to be displayed depending on if corresponding setting is enabled or not. - if (e->component.userid) { - switch(e->component.userid) { - case 0x01: - // swap icon content - if (N_storage.dataAllowed) { - tmp_element.text = &C_icon_toggle_set; - } - else { - tmp_element.text = &C_icon_toggle_reset; - } - break; - case 0x02: - // swap icon content - if (N_storage.contractDetails) { - tmp_element.text = &C_icon_toggle_set; - } - else { - tmp_element.text = &C_icon_toggle_reset; - } - break; - } - } - return &tmp_element; -} - -unsigned int ui_settings_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} -#endif // #if defined(TARGET_BLUE) - - - - -#if defined(TARGET_BLUE) -// reuse addressSummary for each line content -const char* ui_details_title; -const char* ui_details_content; -typedef void (*callback_t)(void); -callback_t ui_details_back_callback; - -const bagl_element_t* ui_details_blue_back_callback(const bagl_element_t* element) { - ui_details_back_callback(); - return 0; -} - - -const bagl_element_t ui_details_blue[] = { - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_LEFT, 0, COLOR_APP, 0xFFFFFF, ui_details_blue_back_callback, NULL, NULL}, - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "VALUE", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x12, 30, 182, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x13, 30, 205, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x14, 30, 228, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x15, 30, 251, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x16, 30, 274, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x17, 30, 297, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x18, 30, 320, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - //"..." at the end if too much - {{BAGL_LABELINE , 0x19, 30, 343, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 0, 450, 320, 14, 0, 0, 0 , 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Review the whole value before continuing.", 10, 0, COLOR_BG_1, NULL, NULL, NULL }, -}; - -const bagl_element_t* ui_details_blue_prepro(const bagl_element_t* element) { - copy_element_and_map_coin_colors(element); - if (element->component.userid == 1) { - tmp_element.text = ui_details_title; - return &tmp_element; - } - else if(element->component.userid > 0) { - unsigned int length = strlen(ui_details_content); - if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { - os_memset(addressSummary, 0, MAX_CHAR_PER_LINE+1); - os_memmove(addressSummary, ui_details_content+(element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); - return &tmp_element; - } - // nothing to draw for this line - return 0; - } - return &tmp_element; -} - -unsigned int ui_details_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} - -void ui_details_init(const char* title, const char* content, callback_t back_callback) { - ui_details_title = title; - ui_details_content = content; - ui_details_back_callback = back_callback; - UX_DISPLAY(ui_details_blue, ui_details_blue_prepro); -} - -void ui_approval_blue_init(void); - -bagl_element_callback_t ui_approval_blue_ok; -bagl_element_callback_t ui_approval_blue_cancel; - -const bagl_element_t* ui_approval_blue_ok_callback(const bagl_element_t* e) { - return ui_approval_blue_ok(e); -} - -const bagl_element_t* ui_approval_blue_cancel_callback(const bagl_element_t* e) { - return ui_approval_blue_cancel(e); -} - -typedef enum { - APPROVAL_TRANSACTION, - APPROVAL_MESSAGE, -} ui_approval_blue_state_t; -ui_approval_blue_state_t G_ui_approval_blue_state; -// pointer to value to be displayed -const char* ui_approval_blue_values[3]; -// variable part of the structure -const char* const ui_approval_blue_details_name[][5] = { - /*APPROVAL_TRANSACTION*/ - {"AMOUNT", "ADDRESS", "MAX FEES","CONFIRM TRANSACTION","Transaction details",}, - - /*APPROVAL_MESSAGE*/ - {"HASH", NULL, NULL, "SIGN MESSAGE", "Message signature", }, -}; - -const bagl_element_t* ui_approval_blue_1_details(const bagl_element_t* e) { - if (strlen(ui_approval_blue_values[0])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >= 160) { - // display details screen - ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][0], ui_approval_blue_values[0], ui_approval_blue_init); - } - return 0; -}; - -const bagl_element_t* ui_approval_blue_2_details(const bagl_element_t* e) { - if (strlen(ui_approval_blue_values[1])*BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >= 160) { - ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][1], ui_approval_blue_values[1], ui_approval_blue_init); - } - return 0; -}; - -const bagl_element_t* ui_approval_blue_3_details(const bagl_element_t* e) { - if (strlen(ui_approval_blue_values[2])*BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >= 160) { - ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][2], ui_approval_blue_values[2], ui_approval_blue_init); - } - return 0; -}; - -const bagl_element_t ui_approval_blue[] = { - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x60, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - // BADGE_TRANSACTION.GIF - {{BAGL_ICON , 0x40, 30, 98, 50, 50, 0, 0, BAGL_FILL, 0 , COLOR_BG_1, 0 , 0 } , &C_badge_transaction, 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE , 0x50, 100, 117, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 100, 138, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0 }, "Check and confirm values", 0, 0, 0, NULL, NULL, NULL}, - - - {{BAGL_LABELINE , 0x70, 30, 196, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // AMOUNT - // x-18 when ... - {{BAGL_LABELINE , 0x10, 130, 200, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // fullAmount - {{BAGL_LABELINE , 0x20, 284, 196, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 168, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_1_details, ui_menu_item_out_over, ui_menu_item_out_over }, - {{BAGL_RECTANGLE , 0x20, 0, 168, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, - - {{BAGL_RECTANGLE , 0x31, 30, 216, 260, 1, 1, 0, 0 , 0xEEEEEE, COLOR_BG_1, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - - {{BAGL_LABELINE , 0x71, 30, 245, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // ADDRESS - // x-18 when ... - {{BAGL_LABELINE , 0x11, 130, 245, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // fullAddress - {{BAGL_LABELINE , 0x21, 284, 245, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 217, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_2_details, ui_menu_item_out_over, ui_menu_item_out_over }, - {{BAGL_RECTANGLE , 0x21, 0, 217, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, - - {{BAGL_RECTANGLE , 0x32, 30, 265, 260, 1, 1, 0, 0 , 0xEEEEEE, COLOR_BG_1, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - - {{BAGL_LABELINE , 0x72, 30, 294, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // MAX FEES - // x-18 when ... - {{BAGL_LABELINE , 0x12, 130, 294, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, //maxFee - {{BAGL_LABELINE , 0x22, 284, 294, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 266, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_3_details, ui_menu_item_out_over, ui_menu_item_out_over }, - {{BAGL_RECTANGLE , 0x22, 0, 266, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, - - {{BAGL_RECTANGLE, 0x90, 30, 314, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x90, 30, 343, 120, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, "CONTRACT DATA", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x90, 133, 343, 140, 30, 0, 0, BAGL_FILL, 0x666666, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, "Present", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_ICON, 0x90, 278, 333, 12, 12, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0}, &C_icon_warning, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, ui_approval_blue_cancel_callback, NULL, NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, ui_approval_blue_ok_callback, NULL, NULL}, - -}; - -const bagl_element_t* ui_approval_blue_prepro(const bagl_element_t* element) { - copy_element_and_map_coin_colors(element); - if (element->component.userid == 0) { - return &tmp_element; - } - // none elements are skipped - if ((element->component.type&(~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { - return 0; - } - else { - switch(element->component.userid&0xF0) { - - // icon - case 0x40: - return &tmp_element; - break; - - // TITLE - case 0x60: - tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][3]; - return &tmp_element; - break; - - // SUBLINE - case 0x50: - tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][4]; - return &tmp_element; - - // details label - case 0x70: - if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { - return NULL; - } - tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]; - return &tmp_element; - - // detail value - case 0x10: - // won't display - if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { - return NULL; - } - // always display the value - tmp_element.text = ui_approval_blue_values[(element->component.userid&0xF)]; - - // x -= 18 when overflow is detected - if (strlen(ui_approval_blue_values[(element->component.userid&0xF)])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >= 160) { - tmp_element.component.x -= 18; - } - return &tmp_element; - break; - - // right arrow and left selection rectangle - case 0x20: - if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { - return NULL; - } - if (strlen(ui_approval_blue_values[(element->component.userid&0xF)])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH < 160) { - return NULL; - } - - // horizontal delimiter - case 0x30: - return ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]!=NULL?&tmp_element:NULL; - - case 0x90: - return (dataPresent && !N_storage.contractDetails); - } - } - return &tmp_element; -} -unsigned int ui_approval_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} - -#endif // #if defined(TARGET_BLUE) - -#if defined(TARGET_BLUE) -const bagl_element_t ui_address_blue[] = { - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM ACCOUNT", 0, 0, 0, NULL, NULL, NULL}, - - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "ACCOUNT", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_address_cancel, NULL, NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_address_ok, NULL, NULL}, -}; - -unsigned int ui_address_blue_prepro(const bagl_element_t* element) { - copy_element_and_map_coin_colors(element); - if(element->component.userid > 0) { - unsigned int length = strlen(strings.common.fullAddress); - if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { - os_memset(addressSummary, 0, MAX_CHAR_PER_LINE+1); - os_memmove(addressSummary, strings.common.fullAddress+(element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); - return &tmp_element; - } - // nothing to draw for this line - return 0; - } - return &tmp_element; -} - -unsigned int ui_address_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} -#endif // #if defined(TARGET_BLUE) - -#if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - -const bagl_element_t ui_address_nanos[] = { - // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_ICON , 0x00, 3, 12, 7, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_ICON , 0x00, 117, 13, 8, 6, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - //{{BAGL_ICON , 0x01, 31, 9, 14, 14, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_EYE_BADGE }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 12, 128, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Confirm", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 26, 128, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "address", 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE , 0x02, 0, 12, 128, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Address", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x02, 23, 26, 82, 12, 0x80|10, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 26 }, (char*)strings.common.fullAddress, 0, 0, 0, NULL, NULL, NULL }, -}; - -unsigned int ui_address_prepro(const bagl_element_t* element) { - if (element->component.userid > 0) { - unsigned int display = (ux_step == element->component.userid-1); - if(display) { - switch(element->component.userid) { - case 1: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 2: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000+bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - } - return display; - } - return 1; -} - -unsigned int ui_address_nanos_button(unsigned int button_mask, unsigned int button_mask_counter); -#endif // #if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - - -#if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) -const bagl_element_t ui_approval_nanos[] = { - // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_ICON , 0x00, 3, 12, 7, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_ICON , 0x00, 117, 13, 8, 6, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - //{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Confirm", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "transaction", 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE, 0x02, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, "WARNING", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, "Data present", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x03, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Amount", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x03, 23, 26, 82, 12, 0x80|10, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 26 }, (char*)strings.common.fullAmount, 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE , 0x04, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Address", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x04, 23, 26, 82, 12, 0x80|10, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 50 }, (char*)strings.common.fullAddress, 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE , 0x05, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Maximum fees", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x05, 23, 26, 82, 12, 0x80|10, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 26 }, (char*)strings.common.maxFee, 0, 0, 0, NULL, NULL, NULL }, - -}; - -unsigned int ui_approval_prepro(const bagl_element_t* element) { - unsigned int display = 1; - if (element->component.userid > 0) { - display = (ux_step == element->component.userid-1); - if(display) { - switch(element->component.userid) { - case 1: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 2: - if (dataPresent && !N_storage.contractDetails) { - UX_CALLBACK_SET_INTERVAL(3000); - } - else { - display = 0; - ux_step++; // display the next step - } - break; - case 3: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000+bagl_label_roundtrip_duration_ms(element, 7))); - break; - case 4: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000+bagl_label_roundtrip_duration_ms(element, 7))); - break; - case 5: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000+bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - } - } - return display; -} - -unsigned int ui_approval_nanos_button(unsigned int button_mask, unsigned int button_mask_counter); - -const bagl_element_t ui_approval_signMessage_nanos[] = { - // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_ICON , 0x00, 3, 12, 7, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_ICON , 0x00, 117, 13, 8, 6, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - //{{BAGL_ICON , 0x01, 28, 9, 14, 14, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Sign the", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x01, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "message", 0, 0, 0, NULL, NULL, NULL }, - - {{BAGL_LABELINE , 0x02, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Message hash", 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x02, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, strings.common.fullAddress, 0, 0, 0, NULL, NULL, NULL }, - -}; - -unsigned int -ui_approval_signMessage_nanos_button(unsigned int button_mask, unsigned int button_mask_counter); - -unsigned int ui_approval_signMessage_prepro(const bagl_element_t *element) { - if (element->component.userid > 0) { - switch (element->component.userid) { - case 1: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 2: - UX_CALLBACK_SET_INTERVAL(3000); - break; - } - return (ux_step == element->component.userid - 1); - } - return 1; -} - -#endif // #if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - -#if defined(TARGET_BLUE) -const bagl_element_t ui_data_selector_blue[] = { - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM SELECTOR", 0, 0, 0, NULL, NULL, NULL}, - - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "SELECTOR", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, strings.tmp.tmp, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_data_cancel, NULL, NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_data_ok, NULL, NULL}, -}; - -unsigned int ui_data_selector_blue_prepro(const bagl_element_t* element) { - copy_element_and_map_coin_colors(element); - if(element->component.userid > 0) { - unsigned int length = strlen(strings.tmp.tmp); - unsigned int offset = (element->component.userid & 0xF) * 24; - if (length >= offset) { - unsigned int copyLength = ((offset + 24) > length ? length - offset : 24); - os_memset(addressSummary, 0, 25); - os_memmove(addressSummary, strings.tmp.tmp + offset, copyLength); - return &tmp_element; - } - // nothing to draw for this line - return 0; - } - return &tmp_element; -} - -unsigned int ui_data_selector_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} -#endif // #if defined(TARGET_BLUE) - - -#if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) -const bagl_element_t ui_data_selector_nanos[] = { - // type userid x y w h str rad - // fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, NULL, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, "Confirm", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, "selector", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, "Selector", 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, (char *)strings.tmp.tmp, 0, 0, 0, NULL, NULL, NULL}, -}; - -unsigned int ui_data_selector_prepro(const bagl_element_t *element) { - if (element->component.userid > 0) { - unsigned int display = (ux_step == element->component.userid - 1); - if (display) { - switch (element->component.userid) { - case 1: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 2: - UX_CALLBACK_SET_INTERVAL(MAX( - 3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - } - return display; - } - return 1; -} - -unsigned int ui_data_selector_nanos_button(unsigned int button_mask, - unsigned int button_mask_counter); -#endif // #if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - -#if defined(TARGET_BLUE) -const bagl_element_t ui_data_parameter_blue[] = { - {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, - - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM PARAMETER", 0, 0, 0, NULL, NULL, NULL}, - - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "PARAMETER", 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_LABELINE , 0x00, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, strings.tmp.tmp2, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x10, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x11, 30, 182, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x12, 30, 205, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - {{BAGL_LABELINE , 0x13, 30, 228, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_data_cancel, NULL, NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_data_ok, NULL, NULL}, -}; - -unsigned int ui_data_parameter_blue_prepro(const bagl_element_t* element) { - copy_element_and_map_coin_colors(element); - if(element->component.userid > 0) { - unsigned int pos = (element->component.userid & 0xF); - unsigned int i; - unsigned int offset = 0; - unsigned int copyLength; - for (i=0; icomponent.userid > 0) { - unsigned int display = (ux_step == element->component.userid - 1); - if (display) { - switch (element->component.userid) { - case 1: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 2: - UX_CALLBACK_SET_INTERVAL(MAX( - 3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - } - return display; - } - return 1; -} - -unsigned int ui_data_parameter_nanos_button(unsigned int button_mask, - unsigned int button_mask_counter); -#endif // #if defined(TARGET_NANOS) && !defined(HAVE_UX_FLOW) - -#if defined(HAVE_UX_FLOW) - -void display_settings(void); -void switch_settings_contract_data(void); -void switch_settings_display_data(void); - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB( - ux_idle_flow_1_step, - nn, //pnn, - { - //"", //&C_icon_dashboard, - "Application", - "is ready", - }); -UX_FLOW_DEF_NOCB( - ux_idle_flow_2_step, - bn, - { - "Version", - APPVERSION, - }); -UX_FLOW_DEF_VALID( - ux_idle_flow_3_step, - pb, - display_settings(), - { - &C_icon_eye, - "Settings", - }); -UX_FLOW_DEF_VALID( - ux_idle_flow_4_step, - pb, - os_sched_exit(-1), - { - &C_icon_dashboard_x, - "Quit", - }); -const ux_flow_step_t * const ux_idle_flow [] = { - &ux_idle_flow_1_step, - &ux_idle_flow_2_step, - &ux_idle_flow_3_step, - &ux_idle_flow_4_step, - FLOW_END_STEP, -}; - -#if defined(TARGET_NANOS) - -UX_FLOW_DEF_VALID( - ux_settings_flow_1_step, - bnnn_paging, - switch_settings_contract_data(), - { - .title = "Contract data", - .text = strings.common.fullAddress, - }); - -UX_FLOW_DEF_VALID( - ux_settings_flow_2_step, - bnnn_paging, - switch_settings_display_data(), - { - .title = "Debug data", - .text = strings.common.fullAddress + 20 - }); - -#else - -UX_FLOW_DEF_VALID( - ux_settings_flow_1_step, - bnnn, - switch_settings_contract_data(), - { - "Contract data", - "Allow contract data", - "in transactions", - strings.common.fullAddress, - }); - -UX_FLOW_DEF_VALID( - ux_settings_flow_2_step, - bnnn, - switch_settings_display_data(), - { - "Debug data", - "Display contract data", - "details", - strings.common.fullAddress + 20 - }); - -#endif - -UX_FLOW_DEF_VALID( - ux_settings_flow_3_step, - pb, - ui_idle(), - { - &C_icon_back_x, - "Back", - }); - -const ux_flow_step_t * const ux_settings_flow [] = { - &ux_settings_flow_1_step, - &ux_settings_flow_2_step, - &ux_settings_flow_3_step, - FLOW_END_STEP, -}; - -void display_settings() { - strcpy(strings.common.fullAddress, (N_storage.dataAllowed ? "Allowed" : "NOT Allowed")); - strcpy(strings.common.fullAddress + 20, (N_storage.contractDetails ? "Displayed" : "NOT Displayed")); - ux_flow_init(0, ux_settings_flow, NULL); -} - -void switch_settings_contract_data() { - uint8_t value = (N_storage.dataAllowed ? 0 : 1); - nvm_write(&N_storage.dataAllowed, (void*)&value, sizeof(uint8_t)); - display_settings(); -} - -void switch_settings_display_data() { - uint8_t value = (N_storage.contractDetails ? 0 : 1); - nvm_write(&N_storage.contractDetails, (void*)&value, sizeof(uint8_t)); - display_settings(); -} - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB( - ux_display_public_flow_1_step, - pnn, - { - &C_icon_eye, - "Verify", - "address", - }); -UX_FLOW_DEF_NOCB( - ux_display_public_flow_2_step, - bnnn_paging, - { - .title = "Address", - .text = strings.common.fullAddress, - }); -UX_FLOW_DEF_VALID( - ux_display_public_flow_3_step, - pb, - io_seproxyhal_touch_address_ok(NULL), - { - &C_icon_validate_14, - "Approve", - }); -UX_FLOW_DEF_VALID( - ux_display_public_flow_4_step, - pb, - io_seproxyhal_touch_address_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -const ux_flow_step_t * const ux_display_public_flow [] = { - &ux_display_public_flow_1_step, - &ux_display_public_flow_2_step, - &ux_display_public_flow_3_step, - &ux_display_public_flow_4_step, - FLOW_END_STEP, -}; - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB( - ux_confirm_selector_flow_1_step, - pnn, - { - &C_icon_eye, - "Verify", - "selector", - }); - -UX_FLOW_DEF_NOCB( - ux_confirm_selector_flow_2_step, - bn, - { - "Selector", - strings.tmp.tmp - }); -UX_FLOW_DEF_VALID( - ux_confirm_selector_flow_3_step, - pb, - io_seproxyhal_touch_data_ok(NULL), - { - &C_icon_validate_14, - "Approve", - }); -UX_FLOW_DEF_VALID( - ux_confirm_selector_flow_4_step, - pb, - io_seproxyhal_touch_data_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -const ux_flow_step_t * const ux_confirm_selector_flow [] = { - &ux_confirm_selector_flow_1_step, - &ux_confirm_selector_flow_2_step, - &ux_confirm_selector_flow_3_step, - &ux_confirm_selector_flow_4_step, - FLOW_END_STEP, -}; - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB( - ux_confirm_parameter_flow_1_step, - pnn, - { - &C_icon_eye, - "Verify", - strings.tmp.tmp2 - }); -UX_FLOW_DEF_NOCB( - ux_confirm_parameter_flow_2_step, - bnnn_paging, - { - .title = "Parameter", - .text = strings.tmp.tmp, - }); -UX_FLOW_DEF_VALID( - ux_confirm_parameter_flow_3_step, - pb, - io_seproxyhal_touch_data_ok(NULL), - { - &C_icon_validate_14, - "Approve", - }); -UX_FLOW_DEF_VALID( - ux_confirm_parameter_flow_4_step, - pb, - io_seproxyhal_touch_data_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -const ux_flow_step_t * const ux_confirm_parameter_flow [] = { - &ux_confirm_parameter_flow_1_step, - &ux_confirm_parameter_flow_2_step, - &ux_confirm_parameter_flow_3_step, - &ux_confirm_parameter_flow_4_step, - FLOW_END_STEP, -}; - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB(ux_approval_tx_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); -UX_FLOW_DEF_NOCB( - ux_approval_tx_2_step, - bnnn_paging, - { - .title = "Amount", - .text = strings.common.fullAmount - }); -UX_FLOW_DEF_NOCB( - ux_approval_tx_3_step, - bnnn_paging, - { - .title = "Address", - .text = strings.common.fullAddress, - }); -UX_FLOW_DEF_NOCB( - ux_approval_tx_4_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); -UX_FLOW_DEF_VALID( - ux_approval_tx_5_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); -UX_FLOW_DEF_VALID( - ux_approval_tx_6_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW_DEF_NOCB(ux_approval_tx_data_warning_step, - pbb, - { - &C_icon_warning, - "Data", - "Present", - }); - - -const ux_flow_step_t * const ux_approval_tx_flow [] = { - &ux_approval_tx_1_step, - &ux_approval_tx_2_step, - &ux_approval_tx_3_step, - &ux_approval_tx_4_step, - &ux_approval_tx_5_step, - &ux_approval_tx_6_step, - FLOW_END_STEP, -}; - -const ux_flow_step_t * const ux_approval_tx_data_warning_flow [] = { - &ux_approval_tx_1_step, - &ux_approval_tx_data_warning_step, - &ux_approval_tx_2_step, - &ux_approval_tx_3_step, - &ux_approval_tx_4_step, - &ux_approval_tx_5_step, - &ux_approval_tx_6_step, - FLOW_END_STEP, -}; - -////////////////////////////////////////////////////////////////////// -UX_FLOW_DEF_NOCB( - ux_sign_flow_1_step, - pnn, - { - &C_icon_certificate, - "Sign", - "message", - }); -UX_FLOW_DEF_NOCB( - ux_sign_flow_2_step, - bnnn_paging, - { - .title = "Message hash", - .text = strings.common.fullAddress, - }); -UX_FLOW_DEF_VALID( - ux_sign_flow_3_step, - pbb, - io_seproxyhal_touch_signMessage_ok(NULL), - { - &C_icon_validate_14, - "Sign", - "message", - }); -UX_FLOW_DEF_VALID( - ux_sign_flow_4_step, - pbb, - io_seproxyhal_touch_signMessage_cancel(NULL), - { - &C_icon_crossmark, - "Cancel", - "signature", - }); - -const ux_flow_step_t * const ux_sign_flow [] = { - &ux_sign_flow_1_step, - &ux_sign_flow_2_step, - &ux_sign_flow_3_step, - &ux_sign_flow_4_step, - FLOW_END_STEP, -}; - - -#endif // #if defined(HAVE_UX_FLOW) - - void ui_idle(void) { #if defined(TARGET_BLUE) UX_DISPLAY(ui_idle_blue, ui_idle_blue_prepro); @@ -1457,8 +103,6 @@ void ui_idle(void) { ux_stack_push(); } ux_flow_init(0, ux_idle_flow, NULL); -#elif defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_main, NULL); #endif // #if TARGET_ID } @@ -1475,29 +119,6 @@ unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e) { return 0; // do not redraw the widget } -unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e) { - uint32_t tx = set_result_get_publicKey(); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - reset_app_context(); - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - -unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e) { - G_io_apdu_buffer[0] = 0x69; - G_io_apdu_buffer[1] = 0x85; - reset_app_context(); - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - #if defined(TARGET_NANOS) unsigned int ui_address_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) { switch(button_mask) { @@ -1542,157 +163,6 @@ void format_signature_out(const uint8_t* signature) { memmove(G_io_apdu_buffer+offset+32-xlength, signature+xoffset, xlength); } -unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e) { - uint8_t privateKeyData[32]; - uint8_t signature[100]; - uint8_t signatureLength; - cx_ecfp_private_key_t privateKey; - uint32_t tx = 0; - uint32_t v = getV(&tmpContent.txContent); - io_seproxyhal_io_heartbeat(); - os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path, - tmpCtx.transactionContext.pathLength, - privateKeyData, NULL); - cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, - &privateKey); - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - unsigned int info = 0; - io_seproxyhal_io_heartbeat(); - signatureLength = - cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256, - tmpCtx.transactionContext.hash, - sizeof(tmpCtx.transactionContext.hash), signature, sizeof(signature), &info); - os_memset(&privateKey, 0, sizeof(privateKey)); - // Parity is present in the sequence tag in the legacy API - if (tmpContent.txContent.vLength == 0) { - // Legacy API - G_io_apdu_buffer[0] = 27; - } - else { - // New API - // Note that this is wrong for a large v, but the client can always recover - G_io_apdu_buffer[0] = (v * 2) + 35; - } - if (info & CX_ECCINFO_PARITY_ODD) { - G_io_apdu_buffer[0]++; - } - if (info & CX_ECCINFO_xGTn) { - G_io_apdu_buffer[0] += 2; - } - format_signature_out(signature); - tx = 65; - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - reset_app_context(); - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - -unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e) { - reset_app_context(); - G_io_apdu_buffer[0] = 0x69; - G_io_apdu_buffer[1] = 0x85; - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - - -unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e) { - uint8_t privateKeyData[32]; - uint8_t signature[100]; - uint8_t signatureLength; - cx_ecfp_private_key_t privateKey; - uint32_t tx = 0; - io_seproxyhal_io_heartbeat(); - os_perso_derive_node_bip32( - CX_CURVE_256K1, tmpCtx.messageSigningContext.bip32Path, - tmpCtx.messageSigningContext.pathLength, privateKeyData, NULL); - io_seproxyhal_io_heartbeat(); - cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - unsigned int info = 0; - io_seproxyhal_io_heartbeat(); - signatureLength = - cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256, - tmpCtx.messageSigningContext.hash, - sizeof(tmpCtx.messageSigningContext.hash), signature, sizeof(signature), &info); - os_memset(&privateKey, 0, sizeof(privateKey)); - G_io_apdu_buffer[0] = 27; - if (info & CX_ECCINFO_PARITY_ODD) { - G_io_apdu_buffer[0]++; - } - if (info & CX_ECCINFO_xGTn) { - G_io_apdu_buffer[0] += 2; - } - format_signature_out(signature); - tx = 65; - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - reset_app_context(); - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - -unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e) { - reset_app_context(); - G_io_apdu_buffer[0] = 0x69; - G_io_apdu_buffer[1] = 0x85; - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - -unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e) { - parserStatus_e txResult = USTREAM_FINISHED; - txResult = continueTx(&txContext); - switch (txResult) { - case USTREAM_SUSPENDED: - break; - case USTREAM_FINISHED: - break; - case USTREAM_PROCESSING: - io_seproxyhal_send_status(0x9000); - ui_idle(); - break; - case USTREAM_FAULT: - reset_app_context(); - io_seproxyhal_send_status(0x6A80); - ui_idle(); - break; - default: - PRINTF("Unexpected parser status\n"); - reset_app_context(); - io_seproxyhal_send_status(0x6A80); - ui_idle(); - } - - if (txResult == USTREAM_FINISHED) { - finalizeParsing(false); - } - - return 0; -} - - -unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e) { - reset_app_context(); - io_seproxyhal_send_status(0x6985); - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - #if defined(TARGET_BLUE) void ui_approval_blue_init(void) { UX_DISPLAY(ui_approval_blue, ui_approval_blue_prepro); @@ -1806,41 +276,7 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { return 0; } -uint32_t set_result_get_publicKey() { - uint32_t tx = 0; - G_io_apdu_buffer[tx++] = 65; - os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.publicKey.W, 65); - tx += 65; - G_io_apdu_buffer[tx++] = 40; - os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.address, 40); - tx += 40; - if (tmpCtx.publicKeyContext.getChaincode) { - os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.chainCode, 32); - tx += 32; - } - return tx; -} - -uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) { - uint32_t i; - for (i=0; i<8; i++) { - if (parameter[i] != 0x00) { - break; - } - } - if (i == 8) { - result[0] = '0'; - result[1] = '0'; - result[2] = '\0'; - return 2; - } - else { - array_hexstr(result, parameter + i, 8 - i); - return ((8 - i) * 2); - } -} - -tokenDefinition_t* getKnownToken() { +tokenDefinition_t* getKnownToken(uint8_t *contractAddress) { tokenDefinition_t *currentToken = NULL; #ifdef HAVE_TOKENS_LIST uint32_t numTokens = 0; @@ -2035,628 +471,45 @@ tokenDefinition_t* getKnownToken() { } } #endif - - if ((currentTokenSet || tokenProvisioned) && (os_memcmp(tmpCtx.transactionContext.currentToken.address, tmpContent.txContent.destination, 20) == 0)) { - currentTokenSet = false; - return &tmpCtx.transactionContext.currentToken; + for(size_t i=0; iaddress, contractAddress, 20) == 0)) { + PRINTF("Token found at index %d\n", i); + return currentToken; + } } return NULL; } -customStatus_e customProcessor(txContext_t *context) { - if ((context->currentField == TX_RLP_DATA) && - (context->currentFieldLength != 0)) { - dataPresent = true; - // If handling a new contract rather than a function call, abort immediately - if (tmpContent.txContent.destinationLength == 0) { - return CUSTOM_NOT_HANDLED; - } - if (context->currentFieldPos == 0) { - // If handling the beginning of the data field, assume that the function selector is present - if (context->commandLength < 4) { - PRINTF("Missing function selector\n"); - return CUSTOM_FAULT; - } - // Initial check to see if the token content can be processed - tokenProvisioned = - (context->currentFieldLength == sizeof(dataContext.tokenContext.data)) && - (os_memcmp(context->workBuffer, TOKEN_TRANSFER_ID, 4) == 0) && - (getKnownToken() != NULL); - } - if (tokenProvisioned) { - if (context->currentFieldPos < context->currentFieldLength) { - uint32_t copySize = (context->commandLength < - ((context->currentFieldLength - - context->currentFieldPos)) - ? context->commandLength - : context->currentFieldLength - - context->currentFieldPos); - copyTxData(context, - dataContext.tokenContext.data + context->currentFieldPos, - copySize); - } - if (context->currentFieldPos == context->currentFieldLength) { - context->currentField++; - context->processingField = false; - } - return CUSTOM_HANDLED; - } - else { - uint32_t blockSize; - uint32_t copySize; - uint32_t fieldPos = context->currentFieldPos; - if (fieldPos == 0) { - if (!N_storage.dataAllowed) { - PRINTF("Data field forbidden\n"); - return CUSTOM_FAULT; - } - if (!N_storage.contractDetails) { - return CUSTOM_NOT_HANDLED; - } - dataContext.rawDataContext.fieldIndex = 0; - dataContext.rawDataContext.fieldOffset = 0; - blockSize = 4; - } - else { - if (!N_storage.contractDetails) { - return CUSTOM_NOT_HANDLED; - } - blockSize = 32 - (dataContext.rawDataContext.fieldOffset % 32); - } - - // Sanity check - if ((context->currentFieldLength - fieldPos) < blockSize) { - PRINTF("Unconsistent data\n"); - return CUSTOM_FAULT; - } - - copySize = (context->commandLength < blockSize ? context->commandLength : blockSize); - copyTxData(context, - dataContext.rawDataContext.data + dataContext.rawDataContext.fieldOffset, - copySize); - - if (context->currentFieldPos == context->currentFieldLength) { - context->currentField++; - context->processingField = false; - } - - dataContext.rawDataContext.fieldOffset += copySize; - - if (copySize == blockSize) { - // Can display - if (fieldPos != 0) { - dataContext.rawDataContext.fieldIndex++; - } - dataContext.rawDataContext.fieldOffset = 0; - if (fieldPos == 0) { - array_hexstr(strings.tmp.tmp, dataContext.rawDataContext.data, 4); -#if defined(TARGET_BLUE) - UX_DISPLAY(ui_data_selector_blue, ui_data_selector_blue_prepro); -#elif defined(HAVE_UX_FLOW) - ux_flow_init(0, ux_confirm_selector_flow, NULL); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = 2; - UX_DISPLAY(ui_data_selector_nanos, ui_data_selector_prepro); -#endif // #if TARGET_ID - } - else { - uint32_t offset = 0; - uint32_t i; - snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Field %d", dataContext.rawDataContext.fieldIndex); - for (i=0; i<4; i++) { - offset += splitBinaryParameterPart(strings.tmp.tmp + offset, dataContext.rawDataContext.data + 8 * i); - if (i != 3) { - strings.tmp.tmp[offset++] = ':'; - } - } -#if defined(TARGET_BLUE) - UX_DISPLAY(ui_data_parameter_blue, ui_data_parameter_blue_prepro); -#elif defined(HAVE_UX_FLOW) - ux_flow_init(0, ux_confirm_parameter_flow, NULL); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = 2; - UX_DISPLAY(ui_data_parameter_nanos, ui_data_parameter_prepro); -#endif // #if TARGET_ID - } - } - else { - return CUSTOM_HANDLED; - } - - return CUSTOM_SUSPENDED; - } - } - return CUSTOM_NOT_HANDLED; -} - -unsigned int const U_os_perso_seed_cookie[] = { - 0xda7aba5e, - 0xc1a551c5, -}; - -#ifndef HAVE_WALLET_ID_SDK - -void handleGetWalletId(volatile unsigned int *tx) { - unsigned char t[64]; - cx_ecfp_256_private_key_t priv; - cx_ecfp_256_public_key_t pub; - // seed => priv key - os_perso_derive_node_bip32(CX_CURVE_256K1, U_os_perso_seed_cookie, 2, t, NULL); - // priv key => pubkey - cx_ecdsa_init_private_key(CX_CURVE_256K1, t, 32, &priv); - cx_ecfp_generate_pair(CX_CURVE_256K1, &pub, &priv, 1); - // pubkey -> sha512 - cx_hash_sha512(pub.W, sizeof(pub.W), t, sizeof(t)); - // ! cookie ! - os_memmove(G_io_apdu_buffer, t, 64); - *tx = 64; - THROW(0x9000); -} - -#endif - -void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { - UNUSED(dataLength); - uint8_t privateKeyData[32]; - uint32_t bip32Path[MAX_BIP32_PATH]; - uint32_t i; - uint8_t bip32PathLength = *(dataBuffer++); - cx_ecfp_private_key_t privateKey; - reset_app_context(); - if ((bip32PathLength < 0x01) || - (bip32PathLength > MAX_BIP32_PATH)) { - PRINTF("Invalid path\n"); - THROW(0x6a80); - } - if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { - THROW(0x6B00); - } - if ((p2 != P2_CHAINCODE) && (p2 != P2_NO_CHAINCODE)) { - THROW(0x6B00); - } - for (i = 0; i < bip32PathLength; i++) { - bip32Path[i] = U4BE(dataBuffer, 0); - dataBuffer += 4; - } - tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE); - io_seproxyhal_io_heartbeat(); - os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength, privateKeyData, (tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL)); - cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); - io_seproxyhal_io_heartbeat(); - cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1); - os_memset(&privateKey, 0, sizeof(privateKey)); - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - io_seproxyhal_io_heartbeat(); - getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey, tmpCtx.publicKeyContext.address, &sha3); -#ifndef NO_CONSENT - if (p1 == P1_NON_CONFIRM) -#endif // NO_CONSENT - { - *tx = set_result_get_publicKey(); - THROW(0x9000); - } -#ifndef NO_CONSENT - else - { - /* - addressSummary[0] = '0'; - addressSummary[1] = 'x'; - os_memmove((unsigned char *)(addressSummary + 2), tmpCtx.publicKeyContext.address, 4); - os_memmove((unsigned char *)(addressSummary + 6), "...", 3); - os_memmove((unsigned char *)(addressSummary + 9), tmpCtx.publicKeyContext.address + 40 - 4, 4); - addressSummary[13] = '\0'; - */ - - // prepare for a UI based reply -#if defined(TARGET_BLUE) - snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address); - UX_DISPLAY(ui_address_blue, ui_address_blue_prepro); -#elif defined(HAVE_UX_FLOW) - snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address); - ux_flow_init(0, ux_display_public_flow, NULL); -#elif defined(TARGET_NANOS) - snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address); - ux_step = 0; - ux_step_count = 2; - UX_DISPLAY(ui_address_nanos, ui_address_prepro); -#endif // #if TARGET_ID - - *flags |= IO_ASYNCH_REPLY; - } -#endif // NO_CONSENT -} - -void finalizeParsing(bool direct) { - uint256_t gasPrice, startGas, uint256; - uint32_t i; - uint8_t address[41]; - uint8_t decimals = WEI_TO_ETHER; - uint8_t *ticker = (uint8_t *)PIC(chainConfig->coinName); - uint8_t *feeTicker = (uint8_t *)PIC(chainConfig->coinName); - uint8_t tickerOffset = 0; - - // Verify the chain - if (chainConfig->chainId != 0) { - uint32_t v = getV(&tmpContent.txContent); - if (chainConfig->chainId != v) { - reset_app_context(); - PRINTF("Invalid chainId %d expected %d\n", v, chainConfig->chainId); - if (direct) { - THROW(0x6A80); - } - else { - io_seproxyhal_send_status(0x6A80); - ui_idle(); - return; - } - } - } - // Store the hash - cx_hash((cx_hash_t *)&sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32); - // If there is a token to process, check if it is well known - if (tokenProvisioned) { - tokenDefinition_t *currentToken = getKnownToken(); - if (currentToken != NULL) { - dataPresent = false; - decimals = currentToken->decimals; - ticker = currentToken->ticker; - tmpContent.txContent.destinationLength = 20; - os_memmove(tmpContent.txContent.destination, dataContext.tokenContext.data + 4 + 12, 20); - os_memmove(tmpContent.txContent.value.value, dataContext.tokenContext.data + 4 + 32, 32); - tmpContent.txContent.value.length = 32; - } - } - else { - if (dataPresent && !N_storage.dataAllowed) { - reset_app_context(); - PRINTF("Data field forbidden\n"); - if (direct) { - THROW(0x6A80); - } - else { - io_seproxyhal_send_status(0x6A80); - ui_idle(); - return; - } - } - } - // Add address - if (tmpContent.txContent.destinationLength != 0) { - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); - /* - addressSummary[0] = '0'; - addressSummary[1] = 'x'; - os_memmove((unsigned char *)(addressSummary + 2), address, 4); - os_memmove((unsigned char *)(addressSummary + 6), "...", 3); - os_memmove((unsigned char *)(addressSummary + 9), address + 40 - 4, 4); - addressSummary[13] = '\0'; - */ - - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; - } - else - { - os_memmove((void*)addressSummary, CONTRACT_ADDRESS, sizeof(CONTRACT_ADDRESS)); - strcpy(strings.common.fullAddress, "Contract"); - } - // Add amount in ethers or tokens - convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &uint256); - tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100); - i = 0; - while (G_io_apdu_buffer[100 + i]) { - i++; - } - adjustDecimals((char *)(G_io_apdu_buffer + 100), i, (char *)G_io_apdu_buffer, 100, decimals); - i = 0; - tickerOffset = 0; - while (ticker[tickerOffset]) { - strings.common.fullAmount[tickerOffset] = ticker[tickerOffset]; - tickerOffset++; - } - while (G_io_apdu_buffer[i]) { - strings.common.fullAmount[tickerOffset + i] = G_io_apdu_buffer[i]; - i++; - } - strings.common.fullAmount[tickerOffset + i] = '\0'; - // Compute maximum fee - convertUint256BE(tmpContent.txContent.gasprice.value, tmpContent.txContent.gasprice.length, &gasPrice); - convertUint256BE(tmpContent.txContent.startgas.value, tmpContent.txContent.startgas.length, &startGas); - mul256(&gasPrice, &startGas, &uint256); - tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100); - i = 0; - while (G_io_apdu_buffer[100 + i]) { - i++; - } - adjustDecimals((char *)(G_io_apdu_buffer + 100), i, (char *)G_io_apdu_buffer, 100, WEI_TO_ETHER); - i = 0; - tickerOffset=0; - while (feeTicker[tickerOffset]) { - strings.common.maxFee[tickerOffset] = feeTicker[tickerOffset]; - tickerOffset++; - } - tickerOffset++; - while (G_io_apdu_buffer[i]) { - strings.common.maxFee[tickerOffset + i] = G_io_apdu_buffer[i]; - i++; - } - strings.common.maxFee[tickerOffset + i] = '\0'; - -#ifdef NO_CONSENT - io_seproxyhal_touch_tx_ok(NULL); -#else // NO_CONSENT -#if defined(TARGET_BLUE) - ui_approval_transaction_blue_init(); -#elif defined(HAVE_UX_FLOW) - ux_flow_init(0, - ((dataPresent && !N_storage.contractDetails) ? ux_approval_tx_data_warning_flow : ux_approval_tx_flow), - NULL); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = 5; - UX_DISPLAY(ui_approval_nanos, ui_approval_prepro); -#endif // #if TARGET_ID -#endif // NO_CONSENT -} - -void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { - UNUSED(p1); - UNUSED(p2); - UNUSED(flags); - uint32_t offset = 0; - uint8_t tickerLength; - uint32_t chainId; - uint8_t hash[32]; - cx_ecfp_public_key_t tokenKey; - if (dataLength < 1) { - THROW(0x6A80); - } - tickerLength = workBuffer[offset++]; - dataLength--; - if ((tickerLength + 1) >= sizeof(tmpCtx.transactionContext.currentToken.ticker)) { - THROW(0x6A80); - } - if (dataLength < tickerLength + 20 + 4 + 4) { - THROW(0x6A80); - } - cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash, 32); - os_memmove(tmpCtx.transactionContext.currentToken.ticker, workBuffer + offset, tickerLength); - tmpCtx.transactionContext.currentToken.ticker[tickerLength] = ' '; - tmpCtx.transactionContext.currentToken.ticker[tickerLength + 1] = '\0'; - offset += tickerLength; - dataLength -= tickerLength; - os_memmove(tmpCtx.transactionContext.currentToken.address, workBuffer + offset, 20); - offset += 20; - dataLength -= 20; - tmpCtx.transactionContext.currentToken.decimals = U4BE(workBuffer, offset); - offset += 4; - dataLength -= 4; - chainId = U4BE(workBuffer, offset); - if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) { - PRINTF("ChainId token mismatch\n"); - THROW(0x6A80); - } - offset += 4; - dataLength -= 4; - cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey); - if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) { - PRINTF("Invalid token signature\n"); - THROW(0x6A80); - } - currentTokenSet = true; - THROW(0x9000); -} - -void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { - UNUSED(tx); - parserStatus_e txResult; - uint32_t i; - if (p1 == P1_FIRST) { - if (dataLength < 1) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - if (appState != APP_STATE_IDLE) { - reset_app_context(); - } - appState = APP_STATE_SIGNING_TX; - tmpCtx.transactionContext.pathLength = workBuffer[0]; - if ((tmpCtx.transactionContext.pathLength < 0x01) || - (tmpCtx.transactionContext.pathLength > MAX_BIP32_PATH)) { - PRINTF("Invalid path\n"); - THROW(0x6a80); - } - workBuffer++; - dataLength--; - for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) { - if (dataLength < 4) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - tmpCtx.transactionContext.bip32Path[i] = U4BE(workBuffer, 0); - workBuffer += 4; - dataLength -= 4; - } - dataPresent = false; - tokenProvisioned = false; - initTx(&txContext, &sha3, &tmpContent.txContent, customProcessor, NULL); - } - else - if (p1 != P1_MORE) { - THROW(0x6B00); - } - if (p2 != 0) { - THROW(0x6B00); - } - if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_TX)) { - PRINTF("Signature not initialized\n"); - THROW(0x6985); - } - if (txContext.currentField == TX_RLP_NONE) { - PRINTF("Parser not initialized\n"); - THROW(0x6985); - } - txResult = processTx(&txContext, workBuffer, dataLength, (chainConfig->kind == CHAIN_KIND_WANCHAIN ? TX_FLAG_TYPE : 0)); - switch (txResult) { - case USTREAM_SUSPENDED: - break; - case USTREAM_FINISHED: - break; - case USTREAM_PROCESSING: - THROW(0x9000); - case USTREAM_FAULT: - THROW(0x6A80); - default: - PRINTF("Unexpected parser status\n"); - THROW(0x6A80); - } - - *flags |= IO_ASYNCH_REPLY; - - if (txResult == USTREAM_FINISHED) { - finalizeParsing(true); - } -} - -void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { - UNUSED(p1); - UNUSED(p2); - UNUSED(workBuffer); - UNUSED(dataLength); - UNUSED(flags); - G_io_apdu_buffer[0] = (N_storage.dataAllowed ? APP_FLAG_DATA_ALLOWED : 0x00); -#ifndef HAVE_TOKENS_LIST - G_io_apdu_buffer[0] |= APP_FLAG_EXTERNAL_TOKEN_NEEDED; -#endif - G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION; - G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION; - G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION; - *tx = 4; - THROW(0x9000); -} - -void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { - UNUSED(tx); - uint8_t hashMessage[32]; - if (p1 == P1_FIRST) { - char tmp[11]; - uint32_t index; - uint32_t base = 10; - uint8_t pos = 0; - uint32_t i; - if (dataLength < 1) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - if (appState != APP_STATE_IDLE) { - reset_app_context(); - } - appState = APP_STATE_SIGNING_MESSAGE; - tmpCtx.messageSigningContext.pathLength = workBuffer[0]; - if ((tmpCtx.messageSigningContext.pathLength < 0x01) || - (tmpCtx.messageSigningContext.pathLength > MAX_BIP32_PATH)) { - PRINTF("Invalid path\n"); - THROW(0x6a80); - } - workBuffer++; - dataLength--; - for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) { - if (dataLength < 4) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0); - workBuffer += 4; - dataLength -= 4; - } - if (dataLength < 4) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0); - workBuffer += 4; - dataLength -= 4; - // Initialize message header + length - cx_keccak_init(&sha3, 256); - cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0); - for (index = 1; (((index * base) <= tmpCtx.messageSigningContext.remainingLength) && - (((index * base) / base) == index)); - index *= base); - for (; index; index /= base) { - tmp[pos++] = '0' + ((tmpCtx.messageSigningContext.remainingLength / index) % base); - } - tmp[pos] = '\0'; - cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)tmp, pos, NULL, 0); - cx_sha256_init(&tmpContent.sha2); - } - else if (p1 != P1_MORE) { - THROW(0x6B00); - } - if (p2 != 0) { - THROW(0x6B00); - } - if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_MESSAGE)) { - PRINTF("Signature not initialized\n"); - THROW(0x6985); - } - if (dataLength > tmpCtx.messageSigningContext.remainingLength) { - THROW(0x6A80); - } - cx_hash((cx_hash_t *)&sha3, 0, workBuffer, dataLength, NULL, 0); - cx_hash((cx_hash_t *)&tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0); - tmpCtx.messageSigningContext.remainingLength -= dataLength; - if (tmpCtx.messageSigningContext.remainingLength == 0) { - cx_hash((cx_hash_t *)&sha3, CX_LAST, workBuffer, 0, tmpCtx.messageSigningContext.hash, 32); - cx_hash((cx_hash_t *)&tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32); - -#define HASH_LENGTH 4 - array_hexstr(strings.common.fullAddress, hashMessage, HASH_LENGTH / 2); - strings.common.fullAddress[HASH_LENGTH / 2 * 2] = '.'; - strings.common.fullAddress[HASH_LENGTH / 2 * 2 + 1] = '.'; - strings.common.fullAddress[HASH_LENGTH / 2 * 2 + 2] = '.'; - array_hexstr(strings.common.fullAddress + HASH_LENGTH / 2 * 2 + 3, hashMessage + 32 - HASH_LENGTH / 2, HASH_LENGTH / 2); - -#ifdef NO_CONSENT - io_seproxyhal_touch_signMessage_ok(NULL); -#else NO_CONSENT -#if defined(TARGET_BLUE) - ui_approval_message_sign_blue_init(); -#elif defined(HAVE_UX_FLOW) - ux_flow_init(0, ux_sign_flow, NULL); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = 2; - UX_DISPLAY(ui_approval_signMessage_nanos, - ui_approval_signMessage_prepro); -#endif // #if TARGET_ID -#endif // NO_CONSENT - - *flags |= IO_ASYNCH_REPLY; - - } else { - THROW(0x9000); - } -} - -void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) { +void handleApdu(unsigned int *flags, unsigned int *tx) { unsigned short sw = 0; BEGIN_TRY { TRY { -#ifndef HAVE_WALLET_ID_SDK +#ifdef HAVE_STARKWARE - if ((G_io_apdu_buffer[OFFSET_CLA] == COMMON_CLA) && (G_io_apdu_buffer[OFFSET_INS] == COMMON_INS_GET_WALLET_ID)) { - handleGetWalletId(tx); - return; + if (G_io_apdu_buffer[OFFSET_CLA] == STARKWARE_CLA) { + switch(G_io_apdu_buffer[OFFSET_INS]) { + case STARKWARE_INS_GET_PUBLIC_KEY: + handleStarkwareGetPublicKey(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); + break; + case STARKWARE_INS_SIGN_MESSAGE: + handleStarkwareSignMessage(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); + break; + case STARKWARE_INS_PROVIDE_QUANTUM: + handleStarkwareProvideQuantum(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); + break; + default: + THROW(0x6D00); + break; + } + CLOSE_TRY; + return; } -#endif +#endif if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { THROW(0x6E00); @@ -2664,12 +517,11 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) { switch (G_io_apdu_buffer[OFFSET_INS]) { case INS_GET_PUBLIC_KEY: - currentTokenSet = false; + os_memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN); handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); break; case INS_PROVIDE_ERC20_TOKEN_INFORMATION: - currentTokenSet = false; handleProvideErc20TokenInformation(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); break; @@ -2682,7 +534,7 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) { break; case INS_SIGN_PERSONAL_MESSAGE: - currentTokenSet = false; + os_memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN); handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx); break; @@ -2728,9 +580,9 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) { } void sample_main(void) { - volatile unsigned int rx = 0; - volatile unsigned int tx = 0; - volatile unsigned int flags = 0; + unsigned int rx = 0; + unsigned int tx = 0; + unsigned int flags = 0; // DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only // goal is to retrieve APDU. @@ -2739,7 +591,7 @@ void sample_main(void) { // switch event, before the apdu is replied to the bootloader. This avoid // APDU injection faults. for (;;) { - volatile unsigned short sw = 0; + unsigned short sw = 0; BEGIN_TRY { TRY { @@ -2754,7 +606,6 @@ void sample_main(void) { if (rx == 0) { THROW(0x6982); } - PRINTF("New APDU received:\n%.*H\n", rx, G_io_apdu_buffer); handleApdu(&flags, &tx); @@ -2829,21 +680,13 @@ unsigned char io_event(unsigned char channel) { UX_DISPLAYED_EVENT({}); break; +#if 0 case SEPROXYHAL_TAG_TICKER_EVENT: UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { - #ifndef HAVE_UX_FLOW - if (UX_ALLOWED) { - if (ux_step_count) { - // prepare next screen - ux_step = (ux_step+1)%ux_step_count; - // redisplay screen - UX_REDISPLAY(); - } - } - #endif // HAVE_UX_FLOW }); break; +#endif } // close the event if not done previously (by a display or whatever) @@ -2922,6 +765,7 @@ __attribute__((section(".boot"))) int main(int arg0) { } reset_app_context(); + tmpCtx.transactionContext.currentTokenIndex = 0; // ensure exception will work as planned os_boot(); @@ -2943,7 +787,7 @@ __attribute__((section(".boot"))) int main(int arg0) { storage.dataAllowed = 0x00; storage.contractDetails = 0x00; storage.initialized = 0x01; - nvm_write(&N_storage, (void*)&storage, sizeof(internalStorage_t)); + nvm_write((void*)&N_storage, (void*)&storage, sizeof(internalStorage_t)); } dataAllowed = N_storage.dataAllowed; contractDetails = N_storage.contractDetails; @@ -2957,7 +801,6 @@ __attribute__((section(".boot"))) int main(int arg0) { BLE_power(0, NULL); BLE_power(1, "Nano X"); #endif // HAVE_BLE - #if defined(TARGET_BLUE) // setup the status bar colors (remembered after wards, even more if another app does not resetup after app switch) UX_SET_STATUS_BAR_COLOR(0xFFFFFF, chainConfig->color_header); @@ -2967,9 +810,11 @@ __attribute__((section(".boot"))) int main(int arg0) { } CATCH(EXCEPTION_IO_RESET) { // reset IO and UX before continuing + CLOSE_TRY; continue; } CATCH_ALL { + CLOSE_TRY; break; } FINALLY { diff --git a/src/poorstream.c b/src/poorstream.c new file mode 100644 index 0000000..eeb46ca --- /dev/null +++ b/src/poorstream.c @@ -0,0 +1,43 @@ +#ifdef HAVE_STARKWARE + +#include "poorstream.h" + +void poorstream_init(poorstream_t *stream, uint8_t *buffer) { + os_memset((void*)stream, 0, sizeof(poorstream_t)); + stream->pointer = buffer; +} + +void poorstream_flush(poorstream_t *stream) { + //PRINTF("Flush\n"); + *(stream->pointer + 0) = (stream->accumulator >> 56); + *(stream->pointer + 1) = (stream->accumulator >> 48); + *(stream->pointer + 2) = (stream->accumulator >> 40); + *(stream->pointer + 3) = (stream->accumulator >> 32); + *(stream->pointer + 4) = (stream->accumulator >> 24); + *(stream->pointer + 5) = (stream->accumulator >> 16); + *(stream->pointer + 6) = (stream->accumulator >> 8); + *(stream->pointer + 7) = (stream->accumulator >> 0); +} + +void poorstream_write_bits(poorstream_t *stream, uint64_t bits, uint32_t num_bits) { + stream->offset += num_bits; + if (stream->offset < 64) { + stream->accumulator |= (bits << (64 - stream->offset)); + //PRINTF("ACC |= << %d\n", (64 - stream->offset)); + } else { + stream->offset -= 64; + stream->mask = ((1 << (num_bits - stream->offset)) - 1); + //PRINTF("Mask %lx\n", stream->mask); + //PRINTF("Offset %d\n", stream->offset); + stream->accumulator |= ((bits >> stream->offset) & stream->mask); + poorstream_flush(stream); + stream->accumulator = 0; + stream->pointer += 8; + if (stream->offset) { + stream->mask = ((1 << stream->offset) - 1); + stream->accumulator |= ((bits & stream->mask) << (64 - stream->offset)); + } + } +} + +#endif diff --git a/src/poorstream.h b/src/poorstream.h new file mode 100644 index 0000000..6539889 --- /dev/null +++ b/src/poorstream.h @@ -0,0 +1,21 @@ +#ifndef __POORSTREAM_H__ +#define __POORSTREAM_H__ + +#include +#include +#include + +#include "os.h" + +typedef struct poorstream_t { + uint8_t *pointer; + uint32_t offset; + uint64_t mask; + uint64_t accumulator; +} poorstream_t; + +void poorstream_init(poorstream_t *stream, uint8_t *buffer); +void poorstream_flush(poorstream_t *stream); +void poorstream_write_bits(poorstream_t *stream, uint64_t bits, uint32_t num_bits); + +#endif diff --git a/src/shared_context.h b/src/shared_context.h new file mode 100644 index 0000000..2cc24b6 --- /dev/null +++ b/src/shared_context.h @@ -0,0 +1,169 @@ +#ifndef __SHARED_CONTEXT_H__ + +#define __SHARED_CONTEXT_H__ + +#include +#include +#include + +#include "os.h" +#include "cx.h" +#include "os_io_seproxyhal.h" +#include "ethUstream.h" +#include "ethUtils.h" +#include "uint256.h" +#include "tokens.h" +#include "chainConfig.h" + +#define MAX_BIP32_PATH 10 + +#define MAX_TOKEN 2 + +#define WEI_TO_ETHER 18 + +#define N_storage (*(volatile internalStorage_t*) PIC(&N_storage_real)) + +typedef struct internalStorage_t { + unsigned char dataAllowed; + unsigned char contractDetails; + uint8_t initialized; +} internalStorage_t; + +typedef struct tokenContext_t { +#ifdef HAVE_STARKWARE + uint8_t data[4 + 32 + 32 + 32 + 32]; +#else + uint8_t data[4 + 32 + 32]; +#endif + uint32_t dataFieldPos; +#ifdef HAVE_STARKWARE + uint8_t quantum[32]; + uint8_t quantumIndex; +#endif +} tokenContext_t; + +typedef struct rawDataContext_t { + uint8_t data[32]; + uint8_t fieldIndex; + uint8_t fieldOffset; +} rawDataContext_t; + +typedef struct publicKeyContext_t { + cx_ecfp_public_key_t publicKey; + uint8_t address[41]; + uint8_t chainCode[32]; + bool getChaincode; +} publicKeyContext_t; + +typedef struct transactionContext_t { + uint8_t pathLength; + uint32_t bip32Path[MAX_BIP32_PATH]; + uint8_t hash[32]; + tokenDefinition_t tokens[MAX_TOKEN]; + uint8_t tokenSet[MAX_TOKEN]; + uint8_t currentTokenIndex; +} transactionContext_t; + +typedef struct messageSigningContext_t { + uint8_t pathLength; + uint32_t bip32Path[MAX_BIP32_PATH]; + uint8_t hash[32]; + uint32_t remainingLength; +} messageSigningContext_t; + +typedef union { + publicKeyContext_t publicKeyContext; + transactionContext_t transactionContext; + messageSigningContext_t messageSigningContext; +} tmpCtx_t; + +typedef union { + txContent_t txContent; + cx_sha256_t sha2; + char tmp[100]; +} tmpContent_t; + +#ifdef HAVE_STARKWARE + +typedef struct starkContext_t { + uint8_t w1[32]; + uint8_t w2[32]; + uint8_t w3[32]; +} starkContext_t; + +#endif + +typedef union { + tokenContext_t tokenContext; + rawDataContext_t rawDataContext; +#ifdef HAVE_STARKWARE + starkContext_t starkContext; +#endif +} dataContext_t; + +typedef enum { + APP_STATE_IDLE, + APP_STATE_SIGNING_TX, + APP_STATE_SIGNING_MESSAGE +} app_state_t; + +typedef enum { + CONTRACT_NONE, + CONTRACT_ERC20, + CONTRACT_ALLOWANCE, +#ifdef HAVE_STARKWARE + CONTRACT_STARKWARE_REGISTER, + CONTRACT_STARKWARE_DEPOSIT_TOKEN, + CONTRACT_STARKWARE_DEPOSIT_ETH, + CONTRACT_STARKWARE_WITHDRAW, + CONTRACT_STARKWARE_DEPOSIT_CANCEL, + CONTRACT_STARKWARE_DEPOSIT_RECLAIM, + CONTRACT_STARKWARE_FULL_WITHDRAWAL, + CONTRACT_STARKWARE_FREEZE, + CONTRACT_STARKWARE_ESCAPE, + CONTRACT_STARKWARE_VERIFY_ESCAPE +#endif +} contract_call_t; + +typedef struct strData_t { + char fullAddress[43]; + char fullAmount[50]; + char maxFee[50]; +} strData_t; + +typedef struct strDataTmp_t { + char tmp[100]; + char tmp2[40]; +} strDataTmp_t; + +typedef union { + strData_t common; + strDataTmp_t tmp; +} strings_t; + +extern chain_config_t *chainConfig; + +extern tmpCtx_t tmpCtx; +extern txContext_t txContext; +extern tmpContent_t tmpContent; +extern dataContext_t dataContext; +extern strings_t strings; +extern cx_sha3_t sha3; +extern const internalStorage_t N_storage_real; + +#ifdef TARGET_BLUE +extern bagl_element_t tmp_element; +extern char addressSummary[32]; +#endif + +extern bool dataPresent; +extern uint8_t appState; +extern contract_call_t contractProvisioned; +#ifdef HAVE_STARKWARE +extern bool quantumSet; +#endif + +void reset_app_context(void); + +#endif // __SHARED_CONTEXT_H__ + diff --git a/src/stark_crypto.c b/src/stark_crypto.c new file mode 100644 index 0000000..61409fa --- /dev/null +++ b/src/stark_crypto.c @@ -0,0 +1,85 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "stark_utils.h" +#include "ui_callbacks.h" +#include "utils.h" + +static unsigned char const C_cx_Stark256_n[] = { + //n: 0x0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb7, 0x81, 0x12, 0x6d, 0xca, 0xe7, 0xb2, 0x32, 0x1e, 0x66, 0xa2, 0x41, 0xad, 0xc6, 0x4d, 0x2f}; + + // C_cx_secp256k1_n - (C_cx_secp256k1_n % C_cx_Stark256_n) +static unsigned char const STARK_DERIVE_BIAS[] = { + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, + 0x38, 0xa1, 0x3b, 0x4b, 0x92, 0x0e, 0x94, 0x11, 0xae, 0x6d, 0xa5, 0xf4, 0x0b, 0x03, 0x58, 0xb1 +}; + +void starkDerivePrivateKey(uint32_t *bip32Path, uint32_t bip32PathLength, uint8_t *privateKeyData) { +#if 0 + // Sanity check + if (bip32Path[0] != STARK_BIP32_PATH_0) { + PRINTF("Invalid Stark derivation path %d\n", bip32Path[0]); + THROW(0x6a80); + } + os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength, privateKeyData, NULL); + PRINTF("Private key before processing %.*H\n", 32, privateKeyData); + // TODO - support additional schemes + cx_math_modm(privateKeyData, 32, C_cx_Stark256_n, 32); + PRINTF("Private key after processing %.*H\n", 32, privateKeyData); +#else + uint8_t tmp[33]; + uint8_t index = 0; + // Sanity check + if ((bip32PathLength < 2) || (bip32Path[0] != STARK_BIP32_PATH_0) || (bip32Path[1] != STARK_BIP32_PATH_1)) { + PRINTF("Invalid Stark derivation path %d %d\n", bip32Path[0], bip32Path[1]); + THROW(0x6a80); + } + os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength, tmp, NULL); + PRINTF("Private key before processing %.*H\n", 32, tmp); + for(;;) { + tmp[32] = index; + cx_hash_sha256(tmp, 33, privateKeyData, 32); + if (cx_math_cmp(privateKeyData, STARK_DERIVE_BIAS, 32) < 0) { + cx_math_modm(privateKeyData, 32, C_cx_Stark256_n, 32); + break; + } + index++; + } + +#endif +} + +void stark_get_amount_string(uint8_t *contractAddress, uint8_t *quantum256, uint8_t *amount64, char *tmp100, char *target100) { + uint256_t amountPre, quantum, amount; + uint8_t decimals; + char *ticker = (char*)PIC(chainConfig->coinName); + + PRINTF("stark_get_amount_string %.*H\n", 20, contractAddress); + + if (allzeroes(contractAddress, 20)) { + decimals = WEI_TO_ETHER; + PRINTF("stark_get_amount_string - ETH\n"); + } + else { + tokenDefinition_t *token = getKnownToken(contractAddress); + if (token == NULL) { // caught earlier + THROW(0x6A80); + } + decimals = token->decimals; + ticker = (char*)token->ticker; + PRINTF("stark_get_amount_string - decimals %d ticker %s\n", decimals, ticker); + } + convertUint256BE(amount64, 8, &amountPre); + readu256BE(quantum256, &quantum); + mul256(&amountPre, &quantum, &amount); + tostring256(&amount, 10, tmp100, 100); + PRINTF("stark_get_amount_string - mul256 %s\n", tmp100); + strcpy(target100, ticker); + adjustDecimals(tmp100, strlen(tmp100), target100 + strlen(ticker), 100, decimals); + PRINTF("get_amount_string %s\n", target100); +} + + +#endif // HAVE_STARK diff --git a/src/stark_crypto.h b/src/stark_crypto.h new file mode 100644 index 0000000..18b5dbb --- /dev/null +++ b/src/stark_crypto.h @@ -0,0 +1,25 @@ +#ifndef __STARK_CRYPTO_H__ +#define __STARK_CRYPTO_H__ + +#include +#include +#include + +#include "os.h" +#include "cx.h" + + +/* EC points */ +#define FIELD_ELEMENT_SIZE (32) +#define EC_POINT_SIZE (2 * FIELD_ELEMENT_SIZE + 1) +typedef unsigned char FieldElement[FIELD_ELEMENT_SIZE]; +typedef unsigned char ECPoint[EC_POINT_SIZE]; + +void pedersen(FieldElement res, /* out */ + FieldElement a, FieldElement b); + +int stark_sign(uint8_t *signautre, /* out */ + uint8_t *privateKeyData, FieldElement token1, + FieldElement token2, FieldElement msg); + +#endif diff --git a/src/stark_utils.c b/src/stark_utils.c new file mode 100644 index 0000000..9c20b73 --- /dev/null +++ b/src/stark_utils.c @@ -0,0 +1,141 @@ +#ifdef HAVE_STARKWARE + +#include "stark_crypto.h" +#include "ethUtils.h" + +#include "os_io_seproxyhal.h" + +#define SIGNATURE_MAX_LEN (72) + +static const ECPoint PEDERSEN_SHIFT[] = { { + 0x04, + + 0x04, 0x9e, 0xe3, 0xeb, 0xa8, 0xc1, 0x60, 0x07, 0x00, 0xee, 0x1b, + 0x87, 0xeb, 0x59, 0x9f, 0x16, 0x71, 0x6b, 0x0b, 0x10, 0x22, 0x94, + 0x77, 0x33, 0x55, 0x1f, 0xde, 0x40, 0x50, 0xca, 0x68, 0x04, + + 0x03, 0xca, 0x0c, 0xfe, 0x4b, 0x3b, 0xc6, 0xdd, 0xf3, 0x46, 0xd4, + 0x9d, 0x06, 0xea, 0x0e, 0xd3, 0x4e, 0x62, 0x10, 0x62, 0xc0, 0xe0, + 0x56, 0xc1, 0xd0, 0x40, 0x5d, 0x26, 0x6e, 0x10, 0x26, 0x8a, +}}; + +static const ECPoint PEDERSEN_POINTS[4] = { + { + 0x04, + + 0x02, 0x34, 0x28, 0x7d, 0xcb, 0xaf, 0xfe, 0x7f, 0x96, 0x9c, 0x74, + 0x86, 0x55, 0xfc, 0xa9, 0xe5, 0x8f, 0xa8, 0x12, 0x0b, 0x6d, 0x56, + 0xeb, 0x0c, 0x10, 0x80, 0xd1, 0x79, 0x57, 0xeb, 0xe4, 0x7b, + + 0x03, 0xb0, 0x56, 0xf1, 0x00, 0xf9, 0x6f, 0xb2, 0x1e, 0x88, 0x95, + 0x27, 0xd4, 0x1f, 0x4e, 0x39, 0x94, 0x01, 0x35, 0xdd, 0x7a, 0x6c, + 0x94, 0xcc, 0x6e, 0xd0, 0x26, 0x8e, 0xe8, 0x9e, 0x56, 0x15, + }, + { + 0x04, + + 0x04, 0xfa, 0x56, 0xf3, 0x76, 0xc8, 0x3d, 0xb3, 0x3f, 0x9d, 0xab, + 0x26, 0x56, 0x55, 0x8f, 0x33, 0x99, 0x09, 0x9e, 0xc1, 0xde, 0x5e, + 0x30, 0x18, 0xb7, 0xa6, 0x93, 0x2d, 0xba, 0x8a, 0xa3, 0x78, + + 0x03, 0xfa, 0x09, 0x84, 0xc9, 0x31, 0xc9, 0xe3, 0x81, 0x13, 0xe0, + 0xc0, 0xe4, 0x7e, 0x44, 0x01, 0x56, 0x27, 0x61, 0xf9, 0x2a, 0x7a, + 0x23, 0xb4, 0x51, 0x68, 0xf4, 0xe8, 0x0f, 0xf5, 0xb5, 0x4d, + }, + { + 0x04, + + 0x04, 0xba, 0x4c, 0xc1, 0x66, 0xbe, 0x8d, 0xec, 0x76, 0x49, 0x10, + 0xf7, 0x5b, 0x45, 0xf7, 0x4b, 0x40, 0xc6, 0x90, 0xc7, 0x47, 0x09, + 0xe9, 0x0f, 0x3a, 0xa3, 0x72, 0xf0, 0xbd, 0x2d, 0x69, 0x97, + + 0x00, 0x40, 0x30, 0x1c, 0xf5, 0xc1, 0x75, 0x1f, 0x4b, 0x97, 0x1e, + 0x46, 0xc4, 0xed, 0xe8, 0x5f, 0xca, 0xc5, 0xc5, 0x9a, 0x5c, 0xe5, + 0xae, 0x7c, 0x48, 0x15, 0x1f, 0x27, 0xb2, 0x4b, 0x21, 0x9c, + }, + { + 0x04, + + 0x05, 0x43, 0x02, 0xdc, 0xb0, 0xe6, 0xcc, 0x1c, 0x6e, 0x44, 0xcc, + 0xa8, 0xf6, 0x1a, 0x63, 0xbb, 0x2c, 0xa6, 0x50, 0x48, 0xd5, 0x3f, + 0xb3, 0x25, 0xd3, 0x6f, 0xf1, 0x2c, 0x49, 0xa5, 0x82, 0x02, + + 0x01, 0xb7, 0x7b, 0x3e, 0x37, 0xd1, 0x35, 0x04, 0xb3, 0x48, 0x04, + 0x62, 0x68, 0xd8, 0xae, 0x25, 0xce, 0x98, 0xad, 0x78, 0x3c, 0x25, + 0x56, 0x1a, 0x87, 0x9d, 0xcc, 0x77, 0xe9, 0x9c, 0x24, 0x26, + }}; + +void accum_ec_mul(ECPoint *hash, uint8_t *buf, int len, int pedersen_idx) { + ECPoint tmp; + if (!allzeroes(buf, len)) { + memcpy(tmp, PEDERSEN_POINTS[pedersen_idx], sizeof(ECPoint)); + io_seproxyhal_io_heartbeat(); + cx_ecfp_scalar_mult(CX_CURVE_Stark256, tmp, sizeof(ECPoint), buf, len); + io_seproxyhal_io_heartbeat(); + cx_ecfp_add_point(CX_CURVE_Stark256, *hash, *hash, tmp, sizeof(ECPoint)); + } +} + +void pedersen(FieldElement res, /* out */ + FieldElement a, FieldElement b) { + ECPoint hash; + + memcpy(hash, PEDERSEN_SHIFT, sizeof(hash)); + + accum_ec_mul(&hash, a, 1, 1); + accum_ec_mul(&hash, a+1, FIELD_ELEMENT_SIZE-1, 0); + accum_ec_mul(&hash, b, 1, 3); + accum_ec_mul(&hash, b+1, FIELD_ELEMENT_SIZE-1, 2); + + memcpy(res, hash + 1, FIELD_ELEMENT_SIZE); +} + +int stark_sign(uint8_t *signature, /* out */ + uint8_t *privateKeyData, + FieldElement token1, + FieldElement token2, + FieldElement msg) { + unsigned int info = 0; + FieldElement hash; + cx_ecfp_private_key_t privateKey; + PRINTF("Stark sign msg w1 %.*H\n", 32, token1); + PRINTF("Stark sign msg w2 %.*H\n", 32, token2); + PRINTF("Stark sign w3 %.*H\n", 32, msg); + pedersen(hash, token1, token2); + PRINTF("Pedersen hash 1 %.*H\n", 32, hash); + pedersen(hash, hash, msg); + PRINTF("Pedersen hash 2 %.*H\n", 32, hash); + cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + int signatureLength = cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256, + hash, sizeof(hash), signature, SIGNATURE_MAX_LEN, &info); + PRINTF("Stark signature %.*H\n", signatureLength, signature); + return signatureLength; +} + +// ERC20Token(address) +static const uint8_t ERC20_SELECTOR[] = { 0xf4, 0x72, 0x61, 0xb0 }; +// ETH() +static const uint8_t ETH_SELECTOR[] = { 0x83, 0x22, 0xff, 0xf2 }; + +void compute_token_id(cx_sha3_t *sha3, uint8_t *contractAddress, uint8_t *quantum, uint8_t *output) { + uint8_t tmp[36]; + cx_keccak_init(sha3, 256); + if ((contractAddress != NULL) && (!allzeroes(contractAddress, 20))) { + PRINTF("compute_token_id for %.*H\n", 20, contractAddress); + os_memset(tmp, 0, sizeof(tmp)); + os_memmove(tmp, ERC20_SELECTOR, 4); + os_memmove(tmp + 16, contractAddress, 20); + cx_hash((cx_hash_t*)sha3, 0, tmp, sizeof(tmp), NULL, 0); + } + else { + PRINTF("compute_token_id for ETH\n"); + cx_hash((cx_hash_t*)sha3, 0, ETH_SELECTOR, sizeof(ETH_SELECTOR), NULL, 0); + } + PRINTF("compute_token_id quantum %.*H\n", 32, quantum); + cx_hash((cx_hash_t*)sha3, CX_LAST, quantum, 32, output, 32); + output[0] &= 0x03; + PRINTF("compute_token_id computed token %.*H\n", 32, output); +} + +#endif // HAVE_STARK diff --git a/src/stark_utils.h b/src/stark_utils.h new file mode 100644 index 0000000..0edb5e4 --- /dev/null +++ b/src/stark_utils.h @@ -0,0 +1,25 @@ +#ifndef __STARK_UTILS_H__ +#define __STARK_UTILS_H__ + +#include +#include +#include + +#include "os.h" +#include "cx.h" +#include "stark_crypto.h" + +void compute_token_id(cx_sha3_t *sha3, uint8_t *contractAddress, uint8_t *quantum, uint8_t *output); + +void starkDerivePrivateKey(uint32_t *bip32Path, uint32_t bip32PathLength, uint8_t *privateKeyData); + +void stark_get_amount_string(uint8_t *contractAddress, uint8_t *quantum256, uint8_t *amount64, char *tmp100, char *target100); + +int stark_sign(uint8_t *signature, /* out */ + uint8_t *privateKeyData, + FieldElement token1, + FieldElement token2, + FieldElement msg); + +#endif + diff --git a/src/tokens.c b/src/tokens.c index 39387bd..cd57236 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -15,6 +15,21 @@ * limitations under the License. ********************************************************************************/ +#ifdef HAVE_TOKENS_EXTRA_LIST + +#include "tokens.h" + +const tokenDefinition_t const TOKENS_EXTRA[NUM_TOKENS_EXTRA] = { + + {{0x4c,0x5f,0x66,0x59,0x61,0x97,0xa8,0x6f,0xb3,0x0a,0x24,0x35,0xe2,0xef,0x4d,0xdc,0xb3,0x93,0x42,0xc9}, "tUSDT ", 6}, + {{0xcd,0x07,0x7a,0xbe,0xdd,0x83,0x1a,0x34,0x43,0xff,0xbe,0x24,0xfb,0x76,0x66,0x1b,0xbb,0x17,0xeb,0x69}, "tZRX ", 18}, + {{0x40,0xd8,0x97,0x85,0x00,0xbf,0x68,0x32,0x4a,0x51,0x53,0x3c,0xd6,0xa2,0x1e,0x3e,0x59,0xbe,0x32,0x4a}, "tBTC ", 18}, + + +}; + +#endif + #ifdef HAVE_TOKENS_LIST #include "tokens.h" diff --git a/src/tokens.h b/src/tokens.h index e54a82c..5db37e7 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -21,11 +21,22 @@ #include typedef struct tokenDefinition_t { +#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR + uint8_t contractName[20]; +#endif uint8_t address[20]; - uint8_t ticker[10]; + uint8_t ticker[12]; // 10 characters + ' \0' uint8_t decimals; } tokenDefinition_t; +#ifdef HAVE_TOKENS_EXTRA_LIST + +#define NUM_TOKENS_EXTRA 3 + +extern tokenDefinition_t const TOKENS_EXTRA[NUM_TOKENS_EXTRA]; + +#endif + #ifdef HAVE_TOKENS_LIST #define NUM_TOKENS_AKROMA 0 diff --git a/src/ui_blue.c b/src/ui_blue.c new file mode 100644 index 0000000..08afc4d --- /dev/null +++ b/src/ui_blue.c @@ -0,0 +1,564 @@ +#include "shared_context.h" + +#ifdef TARGET_BLUE + +#include "ui_blue.h" +#include "ui_callbacks.h" +#include "glyphs.h" + +#define BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH 10 +#define BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH 8 +#define MAX_CHAR_PER_LINE 25 + +void io_seproxyhal_io_heartbeat(void) { +} + +bagl_element_t tmp_element; + +const bagl_element_t* ui_menu_item_out_over(const bagl_element_t* e) { + // the selection rectangle is after the none|touchable + e = (const bagl_element_t*)(((unsigned int)e)+sizeof(bagl_element_t)); + return e; +} + +unsigned int map_color(unsigned int color) { + switch(color) { + case COLOR_APP: + return chainConfig->color_header; + + case COLOR_APP_LIGHT: + return chainConfig->color_dashboard; + } + return color; +} +void copy_element_and_map_coin_colors(const bagl_element_t* element) { + os_memmove(&tmp_element, element, sizeof(bagl_element_t)); + tmp_element.component.fgcolor = map_color(tmp_element.component.fgcolor); + tmp_element.component.bgcolor = map_color(tmp_element.component.bgcolor); + tmp_element.overfgcolor = map_color(tmp_element.overfgcolor); + tmp_element.overbgcolor = map_color(tmp_element.overbgcolor); +} + +const bagl_element_t *ui_idle_blue_prepro(const bagl_element_t *element) { + copy_element_and_map_coin_colors(element); + if (element->component.userid == 0x01) { + tmp_element.text = chainConfig->header_text; + } + return &tmp_element; +} + +const bagl_element_t ui_idle_blue[9] = { + // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, CHAINID_UPCASE, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_SETTINGS, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_settings, NULL, NULL}, + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 0, 270, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Open your wallet", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x00, 0, 308, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Connect your Ledger Blue and open your", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x00, 0, 331, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "preferred wallet to view your accounts.", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 0, 450, 320, 14, 0, 0, 0 , 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Validation requests will show automatically.", 10, 0, COLOR_BG_1, NULL, NULL, NULL }, +}; + +unsigned int ui_idle_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +const bagl_element_t * ui_settings_blue_toggle_data(const bagl_element_t * e) { + // swap setting and request redraw of settings elements + uint8_t setting = N_storage.dataAllowed?0:1; + nvm_write(&N_storage.dataAllowed, (void*)&setting, sizeof(uint8_t)); + + // only refresh settings mutable drawn elements + UX_REDISPLAY_IDX(7); + + // won't redisplay the bagl_none + return 0; +} + +const bagl_element_t * ui_settings_blue_toggle_details(const bagl_element_t * e) { + // swap setting and request redraw of settings elements + uint8_t setting = N_storage.contractDetails?0:1; + nvm_write(&N_storage.contractDetails, (void*)&setting, sizeof(uint8_t)); + + // only refresh settings mutable drawn elements + UX_REDISPLAY_IDX(7); + + // won't redisplay the bagl_none + return 0; +} + + +// don't perform any draw/color change upon finger event over settings +const bagl_element_t* ui_settings_out_over(const bagl_element_t* e) { + return NULL; +} + +unsigned int ui_settings_back_callback(const bagl_element_t* e) { + // go back to idle + ui_idle(); + return 0; +} + +const bagl_element_t ui_settings_blue[13] = { + // type userid x y w h str rad fill fg bg fid iid txt touchparams... ] + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "SETTINGS", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_LEFT, 0, COLOR_APP, 0xFFFFFF, ui_settings_back_callback, NULL, NULL}, + //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + + {{BAGL_LABELINE , 0x00, 30, 105, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, "Contract data", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x00, 30, 126, 260, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0 }, "Allow contract data in transactions", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 78, 320, 68, 0, 0, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_settings_blue_toggle_data, ui_settings_out_over, ui_settings_out_over }, + + {{BAGL_RECTANGLE, 0x00, 30, 146, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE, 0x00, 30, 174, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, "Display data", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE, 0x00, 30, 195, 260, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0}, "Display contract data details", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 147, 320, 68, 0, 0, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, NULL, 0, 0xEEEEEE, 0x000000, ui_settings_blue_toggle_details, ui_settings_out_over, ui_settings_out_over}, + + {{BAGL_ICON, 0x02, 258, 167, 32, 18, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_ICON , 0x01, 258, 98, 32, 18, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, 0, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, +}; + +const bagl_element_t * ui_settings_blue_prepro(const bagl_element_t * e) { + copy_element_and_map_coin_colors(e); + // none elements are skipped + if ((e->component.type&(~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { + return 0; + } + // swap icon buffer to be displayed depending on if corresponding setting is enabled or not. + if (e->component.userid) { + switch(e->component.userid) { + case 0x01: + // swap icon content + if (N_storage.dataAllowed) { + tmp_element.text = &C_icon_toggle_set; + } + else { + tmp_element.text = &C_icon_toggle_reset; + } + break; + case 0x02: + // swap icon content + if (N_storage.contractDetails) { + tmp_element.text = &C_icon_toggle_set; + } + else { + tmp_element.text = &C_icon_toggle_reset; + } + break; + } + } + return &tmp_element; +} + +unsigned int ui_settings_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +// reuse addressSummary for each line content +const char* ui_details_title; +const char* ui_details_content; +typedef void (*callback_t)(void); +callback_t ui_details_back_callback; + +const bagl_element_t* ui_details_blue_back_callback(const bagl_element_t* element) { + ui_details_back_callback(); + return 0; +} + + +const bagl_element_t ui_details_blue[16] = { + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_LEFT, 0, COLOR_APP, 0xFFFFFF, ui_details_blue_back_callback, NULL, NULL}, + //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "VALUE", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x12, 30, 182, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x13, 30, 205, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x14, 30, 228, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x15, 30, 251, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x16, 30, 274, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x17, 30, 297, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x18, 30, 320, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + //"..." at the end if too much + {{BAGL_LABELINE , 0x19, 30, 343, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 0, 450, 320, 14, 0, 0, 0 , 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Review the whole value before continuing.", 10, 0, COLOR_BG_1, NULL, NULL, NULL }, +}; + +const bagl_element_t* ui_details_blue_prepro(const bagl_element_t* element) { + copy_element_and_map_coin_colors(element); + if (element->component.userid == 1) { + tmp_element.text = ui_details_title; + return &tmp_element; + } + else if(element->component.userid > 0) { + unsigned int length = strlen(ui_details_content); + if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { + os_memset(addressSummary, 0, MAX_CHAR_PER_LINE+1); + os_memmove(addressSummary, ui_details_content+(element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); + return &tmp_element; + } + // nothing to draw for this line + return 0; + } + return &tmp_element; +} + +unsigned int ui_details_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +void ui_details_init(const char* title, const char* content, callback_t back_callback) { + ui_details_title = title; + ui_details_content = content; + ui_details_back_callback = back_callback; + UX_DISPLAY(ui_details_blue, ui_details_blue_prepro); +} + +void ui_approval_blue_init(void); + +bagl_element_callback_t ui_approval_blue_ok; +bagl_element_callback_t ui_approval_blue_cancel; + +const bagl_element_t* ui_approval_blue_ok_callback(const bagl_element_t* e) { + return ui_approval_blue_ok(e); +} + +const bagl_element_t* ui_approval_blue_cancel_callback(const bagl_element_t* e) { + return ui_approval_blue_cancel(e); +} + +ui_approval_blue_state_t G_ui_approval_blue_state; +// pointer to value to be displayed +const char* ui_approval_blue_values[3]; +// variable part of the structure +const char* const ui_approval_blue_details_name[][5] = { + /*APPROVAL_TRANSACTION*/ + {"AMOUNT", "ADDRESS", "MAX FEES","CONFIRM TRANSACTION","Transaction details",}, + + /*APPROVAL_MESSAGE*/ + {"HASH", NULL, NULL, "SIGN MESSAGE", "Message signature", }, +}; + +const bagl_element_t* ui_approval_blue_1_details(const bagl_element_t* e) { + if (strlen(ui_approval_blue_values[0])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >= 160) { + // display details screen + ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][0], ui_approval_blue_values[0], ui_approval_blue_init); + } + return 0; +}; + +const bagl_element_t* ui_approval_blue_2_details(const bagl_element_t* e) { + if (strlen(ui_approval_blue_values[1])*BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >= 160) { + ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][1], ui_approval_blue_values[1], ui_approval_blue_init); + } + return 0; +}; + +const bagl_element_t* ui_approval_blue_3_details(const bagl_element_t* e) { + if (strlen(ui_approval_blue_values[2])*BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >= 160) { + ui_details_init(ui_approval_blue_details_name[G_ui_approval_blue_state][2], ui_approval_blue_values[2], ui_approval_blue_init); + } + return 0; +}; + +const bagl_element_t ui_approval_blue[29] = { + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x60, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + // BADGE_TRANSACTION.GIF + {{BAGL_ICON , 0x40, 30, 98, 50, 50, 0, 0, BAGL_FILL, 0 , COLOR_BG_1, 0 , 0 } , &C_badge_transaction, 0, 0, 0, NULL, NULL, NULL }, + + {{BAGL_LABELINE , 0x50, 100, 117, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 100, 138, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0 }, "Check and confirm values", 0, 0, 0, NULL, NULL, NULL}, + + + {{BAGL_LABELINE , 0x70, 30, 196, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // AMOUNT + // x-18 when ... + {{BAGL_LABELINE , 0x10, 130, 200, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // fullAmount + {{BAGL_LABELINE , 0x20, 284, 196, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 168, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_1_details, ui_menu_item_out_over, ui_menu_item_out_over }, + {{BAGL_RECTANGLE , 0x20, 0, 168, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, + + {{BAGL_RECTANGLE , 0x31, 30, 216, 260, 1, 1, 0, 0 , 0xEEEEEE, COLOR_BG_1, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + + {{BAGL_LABELINE , 0x71, 30, 245, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // ADDRESS + // x-18 when ... + {{BAGL_LABELINE , 0x11, 130, 245, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // fullAddress + {{BAGL_LABELINE , 0x21, 284, 245, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 217, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_2_details, ui_menu_item_out_over, ui_menu_item_out_over }, + {{BAGL_RECTANGLE , 0x21, 0, 217, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, + + {{BAGL_RECTANGLE , 0x32, 30, 265, 260, 1, 1, 0, 0 , 0xEEEEEE, COLOR_BG_1, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + + {{BAGL_LABELINE , 0x72, 30, 294, 100, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, // MAX FEES + // x-18 when ... + {{BAGL_LABELINE , 0x12, 130, 294, 160, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, //maxFee + {{BAGL_LABELINE , 0x22, 284, 294, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_RIGHT, 0 }, BAGL_FONT_SYMBOLS_0_MINIRIGHT, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_NONE | BAGL_FLAG_TOUCHABLE , 0x00, 0, 266, 320, 48, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0 , 0 }, NULL, 0, 0xEEEEEE, 0x000000, ui_approval_blue_3_details, ui_menu_item_out_over, ui_menu_item_out_over }, + {{BAGL_RECTANGLE , 0x22, 0, 266, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0 , 0 }, NULL, 0, 0x41CCB4, 0, NULL, NULL, NULL }, + + {{BAGL_RECTANGLE, 0x90, 30, 314, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, NULL, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE, 0x90, 30, 343, 120, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, "CONTRACT DATA", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE, 0x90, 133, 343, 140, 30, 0, 0, BAGL_FILL, 0x666666, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, "Present", 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_ICON, 0x90, 278, 333, 12, 12, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0}, &C_icon_warning, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, ui_approval_blue_cancel_callback, NULL, NULL}, + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, ui_approval_blue_ok_callback, NULL, NULL}, + +}; + +const bagl_element_t* ui_approval_blue_prepro(const bagl_element_t* element) { + copy_element_and_map_coin_colors(element); + if (element->component.userid == 0) { + return &tmp_element; + } + // none elements are skipped + if ((element->component.type&(~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { + return 0; + } + else { + switch(element->component.userid&0xF0) { + + // icon + case 0x40: + return &tmp_element; + break; + + // TITLE + case 0x60: + tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][3]; + return &tmp_element; + break; + + // SUBLINE + case 0x50: + tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][4]; + return &tmp_element; + + // details label + case 0x70: + if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { + return NULL; + } + tmp_element.text = ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]; + return &tmp_element; + + // detail value + case 0x10: + // won't display + if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { + return NULL; + } + // always display the value + tmp_element.text = ui_approval_blue_values[(element->component.userid&0xF)]; + + // x -= 18 when overflow is detected + if (strlen(ui_approval_blue_values[(element->component.userid&0xF)])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >= 160) { + tmp_element.component.x -= 18; + } + return &tmp_element; + break; + + // right arrow and left selection rectangle + case 0x20: + if (!ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]) { + return NULL; + } + if (strlen(ui_approval_blue_values[(element->component.userid&0xF)])*BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH < 160) { + return NULL; + } + + // horizontal delimiter + case 0x30: + return ui_approval_blue_details_name[G_ui_approval_blue_state][element->component.userid&0xF]!=NULL?&tmp_element:NULL; + + case 0x90: + return (dataPresent && !N_storage.contractDetails); + } + } + return &tmp_element; +} +unsigned int ui_approval_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +// + +const bagl_element_t ui_address_blue[8] = { + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM ACCOUNT", 0, 0, 0, NULL, NULL, NULL}, + + //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "ACCOUNT", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_address_cancel, NULL, NULL}, + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_address_ok, NULL, NULL}, +}; + +unsigned int ui_address_blue_prepro(const bagl_element_t* element) { + copy_element_and_map_coin_colors(element); + if(element->component.userid > 0) { + unsigned int length = strlen(strings.common.fullAddress); + if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { + os_memset(addressSummary, 0, MAX_CHAR_PER_LINE+1); + os_memmove(addressSummary, strings.common.fullAddress+(element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); + return &tmp_element; + } + // nothing to draw for this line + return 0; + } + return &tmp_element; +} + +unsigned int ui_address_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +// + +const bagl_element_t ui_data_selector_blue[7] = { + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM SELECTOR", 0, 0, 0, NULL, NULL, NULL}, + + //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "SELECTOR", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, strings.tmp.tmp, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_data_cancel, NULL, NULL}, + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_data_ok, NULL, NULL}, +}; + +unsigned int ui_data_selector_blue_prepro(const bagl_element_t* element) { + copy_element_and_map_coin_colors(element); + if(element->component.userid > 0) { + unsigned int length = strlen(strings.tmp.tmp); + unsigned int offset = (element->component.userid & 0xF) * 24; + if (length >= offset) { + unsigned int copyLength = ((offset + 24) > length ? length - offset : 24); + os_memset(addressSummary, 0, 25); + os_memmove(addressSummary, strings.tmp.tmp + offset, copyLength); + return &tmp_element; + } + // nothing to draw for this line + return 0; + } + return &tmp_element; +} + +unsigned int ui_data_selector_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { + return 0; +} + +// + +const bagl_element_t ui_data_parameter_blue[11] = { + {{BAGL_RECTANGLE , 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL }, + + + // erase screen (only under the status bar) + {{BAGL_RECTANGLE , 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0 , 0 }, NULL, 0, 0, 0, NULL, NULL, NULL}, + + /// TOP STATUS BAR + {{BAGL_LABELINE , 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "CONFIRM PARAMETER", 0, 0, 0, NULL, NULL, NULL}, + + //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0 }, "PARAMETER", 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_LABELINE , 0x00, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, strings.tmp.tmp2, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x10, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x11, 30, 182, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x12, 30, 205, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + {{BAGL_LABELINE , 0x13, 30, 228, 260, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0 }, addressSummary, 0, 0, 0, NULL, NULL, NULL}, + + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0,18, BAGL_FILL, 0xCCCCCC, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "REJECT", 0, 0xB7B7B7, COLOR_BG_1, io_seproxyhal_touch_data_cancel, NULL, NULL}, + {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_data_ok, NULL, NULL}, +}; + +unsigned int ui_data_parameter_blue_prepro(const bagl_element_t* element) { + copy_element_and_map_coin_colors(element); + if(element->component.userid > 0) { + unsigned int pos = (element->component.userid & 0xF); + unsigned int i; + unsigned int offset = 0; + unsigned int copyLength; + for (i=0; ichainId); - offset = strlen(tmp); + snprintf((char*)tmp, sizeof(tmp), "%d0x", chainConfig->chainId); + offset = strlen((char*)tmp); } for (i = 0; i < 20; i++) { uint8_t digit = address[i]; diff --git a/src_common/ethUtils.h b/src_common/ethUtils.h index 9cdda12..d788acc 100644 --- a/src_common/ethUtils.h +++ b/src_common/ethUtils.h @@ -51,4 +51,22 @@ void getEthAddressStringFromBinary(uint8_t *address, uint8_t *out, bool adjustDecimals(char *src, uint32_t srcLength, char *target, uint32_t targetLength, uint8_t decimals); -#endif /* _ETHUTILS_H_ */ \ No newline at end of file +inline int allzeroes(uint8_t *buf, int n) { + for (int i = 0; i < n; ++i) { + if (buf[i]) { + return 0; + } + } + return 1; +} + +inline int ismaxint(uint8_t *buf, int n) { + for (int i = 0; i < n; ++i) { + if (buf[i] != 0xff) { + return 0; + } + } + return 1; +} + +#endif /* _ETHUTILS_H_ */ diff --git a/src_features/erc20_approval/ui_flow_erc20_approval.c b/src_features/erc20_approval/ui_flow_erc20_approval.c new file mode 100644 index 0000000..9851b21 --- /dev/null +++ b/src_features/erc20_approval/ui_flow_erc20_approval.c @@ -0,0 +1,73 @@ +#include "shared_context.h" +#include "ui_callbacks.h" + +UX_FLOW_DEF_NOCB(ux_approval_allowance_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_allowance_2_step, + bnnn_paging, + { + .title = "Allowance", + .text = " " + }); + +UX_FLOW_DEF_NOCB( + ux_approval_allowance_3_step, + bnnn_paging, + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_FLOW_DEF_NOCB( + ux_approval_allowance_4_step, + bnnn_paging, + { + .title = "Amount", + .text = strings.common.fullAmount + }); + +UX_FLOW_DEF_NOCB( + ux_approval_allowance_5_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_allowance_6_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_allowance_7_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_allowance_flow [] = { + &ux_approval_allowance_1_step, + &ux_approval_allowance_2_step, + &ux_approval_allowance_3_step, + &ux_approval_allowance_4_step, + &ux_approval_allowance_5_step, + &ux_approval_allowance_6_step, + &ux_approval_allowance_7_step, + FLOW_END_STEP, +}; + diff --git a/src_features/getAppConfiguration/cmd_getAppConfiguration.c b/src_features/getAppConfiguration/cmd_getAppConfiguration.c new file mode 100644 index 0000000..132693e --- /dev/null +++ b/src_features/getAppConfiguration/cmd_getAppConfiguration.c @@ -0,0 +1,29 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif + +void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(p1); + UNUSED(p2); + UNUSED(workBuffer); + UNUSED(dataLength); + UNUSED(flags); + G_io_apdu_buffer[0] = (N_storage.dataAllowed ? APP_FLAG_DATA_ALLOWED : 0x00); +#ifndef HAVE_TOKENS_LIST + G_io_apdu_buffer[0] |= APP_FLAG_EXTERNAL_TOKEN_NEEDED; +#endif +#ifdef HAVE_STARKWARE + G_io_apdu_buffer[0] |= APP_FLAG_STARKWARE; +#endif + G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION; + G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION; + G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION; + *tx = 4; + THROW(0x9000); +} + diff --git a/src_features/getPublicKey/cmd_getPublicKey.c b/src_features/getPublicKey/cmd_getPublicKey.c new file mode 100644 index 0000000..d6e7497 --- /dev/null +++ b/src_features/getPublicKey/cmd_getPublicKey.c @@ -0,0 +1,76 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif +#include "feature_getPublicKey.h" + +void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(dataLength); + uint8_t privateKeyData[32]; + uint32_t bip32Path[MAX_BIP32_PATH]; + uint32_t i; + uint8_t bip32PathLength = *(dataBuffer++); + cx_ecfp_private_key_t privateKey; + reset_app_context(); + if ((bip32PathLength < 0x01) || + (bip32PathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { + THROW(0x6B00); + } + if ((p2 != P2_CHAINCODE) && (p2 != P2_NO_CHAINCODE)) { + THROW(0x6B00); + } + for (i = 0; i < bip32PathLength; i++) { + bip32Path[i] = U4BE(dataBuffer, 0); + dataBuffer += 4; + } + tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE); + io_seproxyhal_io_heartbeat(); + os_perso_derive_node_bip32(CX_CURVE_256K1, bip32Path, bip32PathLength, privateKeyData, (tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL)); + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1); + os_memset(&privateKey, 0, sizeof(privateKey)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + io_seproxyhal_io_heartbeat(); + getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey, tmpCtx.publicKeyContext.address, &sha3); +#ifndef NO_CONSENT + if (p1 == P1_NON_CONFIRM) +#endif // NO_CONSENT + { + *tx = set_result_get_publicKey(); + THROW(0x9000); + } +#ifndef NO_CONSENT + else + { + /* + addressSummary[0] = '0'; + addressSummary[1] = 'x'; + os_memmove((unsigned char *)(addressSummary + 2), tmpCtx.publicKeyContext.address, 4); + os_memmove((unsigned char *)(addressSummary + 6), "...", 3); + os_memmove((unsigned char *)(addressSummary + 9), tmpCtx.publicKeyContext.address + 40 - 4, 4); + addressSummary[13] = '\0'; + */ + + // prepare for a UI based reply +#if defined(TARGET_BLUE) + snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address); + UX_DISPLAY(ui_address_blue, ui_address_blue_prepro); +#else + snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "0x%.*s", 40, tmpCtx.publicKeyContext.address); + ux_flow_init(0, ux_display_public_flow, NULL); +#endif // #if TARGET_ID + + *flags |= IO_ASYNCH_REPLY; + } +#endif // NO_CONSENT +} + diff --git a/src_features/getPublicKey/feature_getPublicKey.h b/src_features/getPublicKey/feature_getPublicKey.h new file mode 100644 index 0000000..392dda8 --- /dev/null +++ b/src_features/getPublicKey/feature_getPublicKey.h @@ -0,0 +1,4 @@ +#include "shared_context.h" + +uint32_t set_result_get_publicKey(void); + diff --git a/src_features/getPublicKey/logic_getPublicKey.c b/src_features/getPublicKey/logic_getPublicKey.c new file mode 100644 index 0000000..32a7ef0 --- /dev/null +++ b/src_features/getPublicKey/logic_getPublicKey.c @@ -0,0 +1,17 @@ +#include "shared_context.h" + +uint32_t set_result_get_publicKey() { + uint32_t tx = 0; + G_io_apdu_buffer[tx++] = 65; + os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.publicKey.W, 65); + tx += 65; + G_io_apdu_buffer[tx++] = 40; + os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.address, 40); + tx += 40; + if (tmpCtx.publicKeyContext.getChaincode) { + os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.chainCode, 32); + tx += 32; + } + return tx; +} + diff --git a/src_features/getPublicKey/ui_common_getPublicKey.c b/src_features/getPublicKey/ui_common_getPublicKey.c new file mode 100644 index 0000000..cbe6a27 --- /dev/null +++ b/src_features/getPublicKey/ui_common_getPublicKey.c @@ -0,0 +1,27 @@ +#include "shared_context.h" +#include "feature_getPublicKey.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e) { + uint32_t tx = set_result_get_publicKey(); + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e) { + G_io_apdu_buffer[0] = 0x69; + G_io_apdu_buffer[1] = 0x85; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + diff --git a/src_features/getPublicKey/ui_flow_getPublicKey.c b/src_features/getPublicKey/ui_flow_getPublicKey.c new file mode 100644 index 0000000..321253e --- /dev/null +++ b/src_features/getPublicKey/ui_flow_getPublicKey.c @@ -0,0 +1,47 @@ +#include "shared_context.h" +#include "ui_callbacks.h" + +#ifdef HAVE_UX_FLOW + +UX_FLOW_DEF_NOCB( + ux_display_public_flow_1_step, + pnn, + { + &C_icon_eye, + "Verify", + "address", + }); +UX_FLOW_DEF_NOCB( + ux_display_public_flow_2_step, + bnnn_paging, + { + .title = "Address", + .text = strings.common.fullAddress, + }); +UX_FLOW_DEF_VALID( + ux_display_public_flow_3_step, + pb, + io_seproxyhal_touch_address_ok(NULL), + { + &C_icon_validate_14, + "Approve", + }); +UX_FLOW_DEF_VALID( + ux_display_public_flow_4_step, + pb, + io_seproxyhal_touch_address_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_display_public_flow [] = { + &ux_display_public_flow_1_step, + &ux_display_public_flow_2_step, + &ux_display_public_flow_3_step, + &ux_display_public_flow_4_step, + FLOW_END_STEP, +}; + +#endif + diff --git a/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c new file mode 100644 index 0000000..d57783f --- /dev/null +++ b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c @@ -0,0 +1,179 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif + +static const uint8_t const TOKEN_SIGNATURE_PUBLIC_KEY[] = { +// production key 2019-01-11 03:07PM (erc20signer) + 0x04, + + 0x5e,0x6c,0x10,0x20,0xc1,0x4d,0xc4,0x64, + 0x42,0xfe,0x89,0xf9,0x7c,0x0b,0x68,0xcd, + 0xb1,0x59,0x76,0xdc,0x24,0xf2,0x4c,0x31, + 0x6e,0x7b,0x30,0xfe,0x4e,0x8c,0xc7,0x6b, + + 0x14,0x89,0x15,0x0c,0x21,0x51,0x4e,0xbf, + 0x44,0x0f,0xf5,0xde,0xa5,0x39,0x3d,0x83, + 0xde,0x53,0x58,0xcd,0x09,0x8f,0xce,0x8f, + 0xd0,0xf8,0x1d,0xaa,0x94,0x97,0x91,0x83 +}; + +#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR + +void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(p1); + UNUSED(p2); + UNUSED(flags); + uint32_t offset = 0; + uint8_t tickerLength, contractNameLength; + uint32_t chainId; + uint8_t hash[32]; + cx_sha256_t sha256; + cx_ecfp_public_key_t tokenKey; + + cx_sha256_init(&sha256); + + tmpCtx.transactionContext.currentTokenIndex = (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN; + tokenDefinition_t* token = &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex]; + + if (dataLength < 1) { + THROW(0x6A80); + } + tickerLength = workBuffer[offset++]; + dataLength--; + if ((tickerLength + 2) >= sizeof(token->ticker)) { // +2 because ' \0' is appended to ticker + THROW(0x6A80); + } + if (dataLength < tickerLength + 1) { + THROW(0x6A80); + } + cx_hash((cx_hash_t*)&sha256, 0, workBuffer + offset, tickerLength, NULL, 0); + os_memmove(token->ticker, workBuffer + offset, tickerLength); + token->ticker[tickerLength] = ' '; + token->ticker[tickerLength + 1] = '\0'; + offset += tickerLength; + dataLength -= tickerLength; + + contractNameLength = workBuffer[offset++]; + dataLength--; + if (dataLength < contractNameLength + 20 + 4 + 4) { + THROW(0x6A80); + } + cx_hash((cx_hash_t*)&sha256, CX_LAST, workBuffer + offset, contractNameLength + 20 + 4 + 4, hash, 32); + os_memmove(token->contractName, workBuffer + offset, MIN(contractNameLength, sizeof(token->contractName)-1)); + token->contractName[MIN(contractNameLength, sizeof(token->contractName)-1)] = '\0'; + offset += contractNameLength; + dataLength -= contractNameLength; + + os_memmove(token->address, workBuffer + offset, 20); + offset += 20; + dataLength -= 20; + token->decimals = U4BE(workBuffer, offset); + offset += 4; + dataLength -= 4; + chainId = U4BE(workBuffer, offset); + if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) { + PRINTF("ChainId token mismatch\n"); + THROW(0x6A80); + } + offset += 4; + dataLength -= 4; + cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey); + if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) { + PRINTF("Invalid token signature\n"); + THROW(0x6A80); + } + tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1; + THROW(0x9000); +} + +#else + +void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(p1); + UNUSED(p2); + UNUSED(flags); + uint32_t offset = 0; + uint8_t tickerLength; + uint32_t chainId; + uint8_t hash[32]; + cx_ecfp_public_key_t tokenKey; + + + tmpCtx.transactionContext.currentTokenIndex = (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN; + tokenDefinition_t* token = &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex]; + + PRINTF("Provisioning currentTokenIndex %d\n", tmpCtx.transactionContext.currentTokenIndex); + + if (dataLength < 1) { + THROW(0x6A80); + } + tickerLength = workBuffer[offset++]; + dataLength--; + if ((tickerLength + 1) >= sizeof(token->ticker)) { + THROW(0x6A80); + } + if (dataLength < tickerLength + 20 + 4 + 4) { + THROW(0x6A80); + } + cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash, 32); + os_memmove(token->ticker, workBuffer + offset, tickerLength); + token->ticker[tickerLength] = ' '; + token->ticker[tickerLength + 1] = '\0'; + offset += tickerLength; + dataLength -= tickerLength; + os_memmove(token->address, workBuffer + offset, 20); + offset += 20; + dataLength -= 20; + token->decimals = U4BE(workBuffer, offset); + offset += 4; + dataLength -= 4; + chainId = U4BE(workBuffer, offset); + if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) { + PRINTF("ChainId token mismatch\n"); + THROW(0x6A80); + } + offset += 4; + dataLength -= 4; + +#ifdef HAVE_TOKENS_EXTRA_LIST + tokenDefinition_t *currentToken = NULL; + uint32_t index; + for (index=0; index < NUM_TOKENS_EXTRA; index++) { + currentToken = (tokenDefinition_t *)PIC(&TOKENS_EXTRA[index]); + if (os_memcmp(currentToken->address, token->address, 20) == 0) { + strcpy((char*)token->ticker, (char*)currentToken->ticker); + token->decimals = currentToken->decimals; + break; + } + } + if (index < NUM_TOKENS_EXTRA) { + PRINTF("Descriptor whitelisted\n"); + } + else { + cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey); + if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) { + PRINTF("Invalid token signature\n"); + THROW(0x6A80); + } + } + +#else + + cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey); + if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) { + PRINTF("Invalid token signature\n"); + THROW(0x6A80); + } +#endif + + tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1; + THROW(0x9000); +} + +#endif + diff --git a/src_features/signMessage/cmd_signMessage.c b/src_features/signMessage/cmd_signMessage.c new file mode 100644 index 0000000..9440d9e --- /dev/null +++ b/src_features/signMessage/cmd_signMessage.c @@ -0,0 +1,105 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#include "utils.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif + +static const char const SIGN_MAGIC[] = "\x19" + "Ethereum Signed Message:\n"; + +void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(tx); + uint8_t hashMessage[32]; + if (p1 == P1_FIRST) { + char tmp[11]; + uint32_t index; + uint32_t base = 10; + uint8_t pos = 0; + uint32_t i; + if (dataLength < 1) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + appState = APP_STATE_SIGNING_MESSAGE; + tmpCtx.messageSigningContext.pathLength = workBuffer[0]; + if ((tmpCtx.messageSigningContext.pathLength < 0x01) || + (tmpCtx.messageSigningContext.pathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + workBuffer++; + dataLength--; + for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) { + if (dataLength < 4) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0); + workBuffer += 4; + dataLength -= 4; + } + if (dataLength < 4) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0); + workBuffer += 4; + dataLength -= 4; + // Initialize message header + length + cx_keccak_init(&sha3, 256); + cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0); + for (index = 1; (((index * base) <= tmpCtx.messageSigningContext.remainingLength) && + (((index * base) / base) == index)); + index *= base); + for (; index; index /= base) { + tmp[pos++] = '0' + ((tmpCtx.messageSigningContext.remainingLength / index) % base); + } + tmp[pos] = '\0'; + cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)tmp, pos, NULL, 0); + cx_sha256_init(&tmpContent.sha2); + } + else if (p1 != P1_MORE) { + THROW(0x6B00); + } + if (p2 != 0) { + THROW(0x6B00); + } + if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_MESSAGE)) { + PRINTF("Signature not initialized\n"); + THROW(0x6985); + } + if (dataLength > tmpCtx.messageSigningContext.remainingLength) { + THROW(0x6A80); + } + cx_hash((cx_hash_t *)&sha3, 0, workBuffer, dataLength, NULL, 0); + cx_hash((cx_hash_t *)&tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0); + tmpCtx.messageSigningContext.remainingLength -= dataLength; + if (tmpCtx.messageSigningContext.remainingLength == 0) { + cx_hash((cx_hash_t *)&sha3, CX_LAST, workBuffer, 0, tmpCtx.messageSigningContext.hash, 32); + cx_hash((cx_hash_t *)&tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32); + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%.*H", sizeof(hashMessage), hashMessage); + +#ifdef NO_CONSENT + io_seproxyhal_touch_signMessage_ok(NULL); +#else //NO_CONSENT +#if defined(TARGET_BLUE) + ui_approval_message_sign_blue_init(); +#else + ux_flow_init(0, ux_sign_flow, NULL); +#endif // #if TARGET_ID +#endif // NO_CONSENT + + *flags |= IO_ASYNCH_REPLY; + + } else { + THROW(0x9000); + } +} + diff --git a/src_features/signMessage/ui_common_signMessage.c b/src_features/signMessage/ui_common_signMessage.c new file mode 100644 index 0000000..79c1f4f --- /dev/null +++ b/src_features/signMessage/ui_common_signMessage.c @@ -0,0 +1,53 @@ +#include "shared_context.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e) { + uint8_t privateKeyData[32]; + uint8_t signature[100]; + uint8_t signatureLength; + cx_ecfp_private_key_t privateKey; + uint32_t tx = 0; + io_seproxyhal_io_heartbeat(); + os_perso_derive_node_bip32( + CX_CURVE_256K1, tmpCtx.messageSigningContext.bip32Path, + tmpCtx.messageSigningContext.pathLength, privateKeyData, NULL); + io_seproxyhal_io_heartbeat(); + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + unsigned int info = 0; + io_seproxyhal_io_heartbeat(); + signatureLength = + cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256, + tmpCtx.messageSigningContext.hash, + sizeof(tmpCtx.messageSigningContext.hash), signature, sizeof(signature), &info); + os_memset(&privateKey, 0, sizeof(privateKey)); + G_io_apdu_buffer[0] = 27; + if (info & CX_ECCINFO_PARITY_ODD) { + G_io_apdu_buffer[0]++; + } + if (info & CX_ECCINFO_xGTn) { + G_io_apdu_buffer[0] += 2; + } + format_signature_out(signature); + tx = 65; + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e) { + reset_app_context(); + G_io_apdu_buffer[0] = 0x69; + G_io_apdu_buffer[1] = 0x85; + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + diff --git a/src_features/signMessage/ui_flow_signMessage.c b/src_features/signMessage/ui_flow_signMessage.c new file mode 100644 index 0000000..26f8589 --- /dev/null +++ b/src_features/signMessage/ui_flow_signMessage.c @@ -0,0 +1,45 @@ +#include "shared_context.h" +#include "ui_callbacks.h" + +UX_FLOW_DEF_NOCB( + ux_sign_flow_1_step, + pnn, + { + &C_icon_certificate, + "Sign", + "message", + }); +UX_FLOW_DEF_NOCB( + ux_sign_flow_2_step, + bnnn_paging, + { + .title = "Message hash", + .text = strings.tmp.tmp, + }); +UX_FLOW_DEF_VALID( + ux_sign_flow_3_step, + pbb, + io_seproxyhal_touch_signMessage_ok(NULL), + { + &C_icon_validate_14, + "Sign", + "message", + }); +UX_FLOW_DEF_VALID( + ux_sign_flow_4_step, + pbb, + io_seproxyhal_touch_signMessage_cancel(NULL), + { + &C_icon_crossmark, + "Cancel", + "signature", + }); + +const ux_flow_step_t * const ux_sign_flow [] = { + &ux_sign_flow_1_step, + &ux_sign_flow_2_step, + &ux_sign_flow_3_step, + &ux_sign_flow_4_step, + FLOW_END_STEP, +}; + diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c new file mode 100644 index 0000000..ee685b5 --- /dev/null +++ b/src_features/signTx/cmd_signTx.c @@ -0,0 +1,81 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif +#include "feature_signTx.h" + +void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(tx); + parserStatus_e txResult; + uint32_t i; + if (p1 == P1_FIRST) { + if (dataLength < 1) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + appState = APP_STATE_SIGNING_TX; + tmpCtx.transactionContext.pathLength = workBuffer[0]; + if ((tmpCtx.transactionContext.pathLength < 0x01) || + (tmpCtx.transactionContext.pathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + workBuffer++; + dataLength--; + for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) { + if (dataLength < 4) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + tmpCtx.transactionContext.bip32Path[i] = U4BE(workBuffer, 0); + workBuffer += 4; + dataLength -= 4; + } + dataPresent = false; + contractProvisioned = CONTRACT_NONE; + initTx(&txContext, &sha3, &tmpContent.txContent, customProcessor, NULL); + } + else + if (p1 != P1_MORE) { + THROW(0x6B00); + } + if (p2 != 0) { + THROW(0x6B00); + } + if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_TX)) { + PRINTF("Signature not initialized\n"); + THROW(0x6985); + } + if (txContext.currentField == TX_RLP_NONE) { + PRINTF("Parser not initialized\n"); + THROW(0x6985); + } + txResult = processTx(&txContext, workBuffer, dataLength, (chainConfig->kind == CHAIN_KIND_WANCHAIN ? TX_FLAG_TYPE : 0)); + switch (txResult) { + case USTREAM_SUSPENDED: + break; + case USTREAM_FINISHED: + break; + case USTREAM_PROCESSING: + THROW(0x9000); + case USTREAM_FAULT: + THROW(0x6A80); + default: + PRINTF("Unexpected parser status\n"); + THROW(0x6A80); + } + + *flags |= IO_ASYNCH_REPLY; + + if (txResult == USTREAM_FINISHED) { + finalizeParsing(false); + } +} + diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h new file mode 100644 index 0000000..0301d14 --- /dev/null +++ b/src_features/signTx/feature_signTx.h @@ -0,0 +1,5 @@ +#include "shared_context.h" + +customStatus_e customProcessor(txContext_t *context); +void finalizeParsing(bool direct); + diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c new file mode 100644 index 0000000..e2899b9 --- /dev/null +++ b/src_features/signTx/logic_signTx.c @@ -0,0 +1,484 @@ +#include "shared_context.h" +#include "utils.h" +#include "ui_callbacks.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif +#ifdef HAVE_STARKWARE +#include "stark_utils.h" +#endif + +#define TOKEN_TRANSFER_DATA_SIZE 4 + 32 + 32 +static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb }; + +#define ALLOWANCE_DATA_SIZE 4 + 32 + 32 +static const uint8_t const ALLOWANCE_ID[] = { 0x09, 0x5e, 0xa7, 0xb3 }; + +#ifdef HAVE_STARKWARE + +#define STARKWARE_REGISTER_DATA_SIZE 4 + 32 +static const uint8_t const STARKWARE_REGISTER_ID[] = { 0x76, 0x57, 0x18, 0xd7 }; +#define STARKWARE_DEPOSIT_TOKEN_DATA_SIZE 4 + 32 + 32 + 32 +static const uint8_t const STARKWARE_DEPOSIT_TOKEN_ID[] = { 0x00, 0xae, 0xef, 0x8a }; +#define STARKWARE_DEPOSIT_ETH_DATA_SIZE 4 + 32 + 32 +static const uint8_t const STARKWARE_DEPOSIT_ETH_ID[] = { 0xe2, 0xbb, 0xb1, 0x58 }; +#define STARKWARE_DEPOSIT_CANCEL_DATA_SIZE 4 + 32 + 32 +static const uint8_t const STARKWARE_DEPOSIT_CANCEL_ID[] = { 0xc7, 0xfb, 0x11, 0x7c }; +#define STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE 4 + 32 + 32 +static const uint8_t const STARKWARE_DEPOSIT_RECLAIM_ID[] = { 0x4e, 0xab, 0x38, 0xf4 }; +#define STARKWARE_WITHDRAW_DATA_SIZE 4 + 32 +static const uint8_t const STARKWARE_WITHDRAW_ID[] = { 0x2e, 0x1a, 0x7d, 0x4d }; +#define STARKWARE_FULL_WITHDRAWAL_DATA_SIZE 4 + 32 +static const uint8_t const STARKWARE_FULL_WITHDRAWAL_ID[] = { 0x27, 0x6d, 0xd1, 0xde }; +#define STARKWARE_FREEZE_DATA_SIZE 4 + 32 +static const uint8_t const STARKWARE_FREEZE_ID[] = { 0xb9, 0x10, 0x72, 0x09 }; +#define STARKWARE_ESCAPE_DATA_SIZE 4 + 32 + 32 + 32 + 32 +static const uint8_t const STARKWARE_ESCAPE_ID[] = { 0x9e, 0x3a, 0xda, 0xc4 }; +static const uint8_t const STARKWARE_VERIFY_ESCAPE_ID[] = { 0x2d, 0xd5, 0x30, 0x06 }; + +#endif + +uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) { + uint32_t i; + for (i=0; i<8; i++) { + if (parameter[i] != 0x00) { + break; + } + } + if (i == 8) { + result[0] = '0'; + result[1] = '0'; + result[2] = '\0'; + return 2; + } + else { + array_hexstr(result, parameter + i, 8 - i); + return ((8 - i) * 2); + } +} + +customStatus_e customProcessor(txContext_t *context) { + if ((context->currentField == TX_RLP_DATA) && + (context->currentFieldLength != 0)) { + dataPresent = true; + // If handling a new contract rather than a function call, abort immediately + if (tmpContent.txContent.destinationLength == 0) { + return CUSTOM_NOT_HANDLED; + } + if (context->currentFieldPos == 0) { + // If handling the beginning of the data field, assume that the function selector is present + if (context->commandLength < 4) { + PRINTF("Missing function selector\n"); + return CUSTOM_FAULT; + } + // Initial check to see if the call can be processed + if ((context->currentFieldLength == TOKEN_TRANSFER_DATA_SIZE) && + (os_memcmp(context->workBuffer, TOKEN_TRANSFER_ID, 4) == 0) && + (getKnownToken(tmpContent.txContent.destination) != NULL)) { + contractProvisioned = CONTRACT_ERC20; + } + else + if ((context->currentFieldLength == ALLOWANCE_DATA_SIZE) && + (os_memcmp(context->workBuffer, ALLOWANCE_ID, 4) == 0)) { + contractProvisioned = CONTRACT_ALLOWANCE; + } +#ifdef HAVE_STARKWARE + else + if ((context->currentFieldLength >= STARKWARE_REGISTER_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_REGISTER_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_REGISTER; + } + else + if ((context->currentFieldLength == STARKWARE_DEPOSIT_ETH_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_ETH_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_ETH; + } + else + if ((context->currentFieldLength == STARKWARE_DEPOSIT_TOKEN_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_TOKEN_ID, 4) == 0) && + quantumSet) { + contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_TOKEN; + } + else + if ((context->currentFieldLength == STARKWARE_WITHDRAW_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_WITHDRAW_ID, 4) == 0) && + quantumSet) { + contractProvisioned = CONTRACT_STARKWARE_WITHDRAW; + } + else + if ((context->currentFieldLength == STARKWARE_DEPOSIT_CANCEL_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_CANCEL_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_CANCEL; + } + else + if ((context->currentFieldLength == STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_RECLAIM_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_RECLAIM; + } + else + if ((context->currentFieldLength == STARKWARE_FULL_WITHDRAWAL_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_FULL_WITHDRAWAL_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_FULL_WITHDRAWAL; + } + else + if ((context->currentFieldLength == STARKWARE_FREEZE_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_FREEZE_ID, 4) == 0)) { + contractProvisioned = CONTRACT_STARKWARE_FREEZE; + } + else + if ((context->currentFieldLength == STARKWARE_ESCAPE_DATA_SIZE) && + (os_memcmp(context->workBuffer, STARKWARE_ESCAPE_ID, 4) == 0) && + quantumSet) { + contractProvisioned = CONTRACT_STARKWARE_ESCAPE; + } + else + if (os_memcmp(context->workBuffer, STARKWARE_VERIFY_ESCAPE_ID, 4) == 0) { + contractProvisioned = CONTRACT_STARKWARE_VERIFY_ESCAPE; + } + +#endif + } + // Sanity check + // Also handle exception that only need to process the beginning of the data + if ((contractProvisioned != CONTRACT_NONE) && +#ifdef HAVE_STARKWARE + (contractProvisioned != CONTRACT_STARKWARE_VERIFY_ESCAPE) && + (contractProvisioned != CONTRACT_STARKWARE_REGISTER) && +#endif + (context->currentFieldLength > sizeof(dataContext.tokenContext.data))) { + PRINTF("Data field overflow - dropping customization\n"); + contractProvisioned = CONTRACT_NONE; + } + PRINTF("contractProvisioned %d\n", contractProvisioned); + if (contractProvisioned != CONTRACT_NONE) { + if (context->currentFieldPos < context->currentFieldLength) { + uint32_t copySize = MIN(context->commandLength, + context->currentFieldLength - context->currentFieldPos); + // Handle the case where we only need to handle the beginning of the data parameter + if ((context->currentFieldPos + copySize) < sizeof(dataContext.tokenContext.data)) { + copyTxData(context, + dataContext.tokenContext.data + context->currentFieldPos, + copySize); + } + else { + if (context->currentFieldPos < sizeof(dataContext.tokenContext.data)) { + uint32_t copySize2 = sizeof(dataContext.tokenContext.data) - context->currentFieldPos; + copyTxData(context, + dataContext.tokenContext.data + context->currentFieldPos, + copySize2); + copySize -= copySize2; + } + copyTxData(context, NULL, copySize); + } + } + if (context->currentFieldPos == context->currentFieldLength) { + context->currentField++; + context->processingField = false; + } + return CUSTOM_HANDLED; + } + else { + uint32_t blockSize; + uint32_t copySize; + uint32_t fieldPos = context->currentFieldPos; + if (fieldPos == 0) { + if (!N_storage.dataAllowed) { + PRINTF("Data field forbidden\n"); + return CUSTOM_FAULT; + } + if (!N_storage.contractDetails) { + return CUSTOM_NOT_HANDLED; + } + dataContext.rawDataContext.fieldIndex = 0; + dataContext.rawDataContext.fieldOffset = 0; + blockSize = 4; + } + else { + if (!N_storage.contractDetails) { + return CUSTOM_NOT_HANDLED; + } + blockSize = 32 - (dataContext.rawDataContext.fieldOffset % 32); + } + + // Sanity check + if ((context->currentFieldLength - fieldPos) < blockSize) { + PRINTF("Unconsistent data\n"); + return CUSTOM_FAULT; + } + + copySize = (context->commandLength < blockSize ? context->commandLength : blockSize); + copyTxData(context, + dataContext.rawDataContext.data + dataContext.rawDataContext.fieldOffset, + copySize); + + if (context->currentFieldPos == context->currentFieldLength) { + context->currentField++; + context->processingField = false; + } + + dataContext.rawDataContext.fieldOffset += copySize; + + if (copySize == blockSize) { + // Can display + if (fieldPos != 0) { + dataContext.rawDataContext.fieldIndex++; + } + dataContext.rawDataContext.fieldOffset = 0; + if (fieldPos == 0) { + array_hexstr(strings.tmp.tmp, dataContext.rawDataContext.data, 4); +#if defined(TARGET_BLUE) + UX_DISPLAY(ui_data_selector_blue, ui_data_selector_blue_prepro); +#else + ux_flow_init(0, ux_confirm_selector_flow, NULL); +#endif // #if TARGET_ID + } + else { + uint32_t offset = 0; + uint32_t i; + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Field %d", dataContext.rawDataContext.fieldIndex); + for (i=0; i<4; i++) { + offset += splitBinaryParameterPart(strings.tmp.tmp + offset, dataContext.rawDataContext.data + 8 * i); + if (i != 3) { + strings.tmp.tmp[offset++] = ':'; + } + } +#if defined(TARGET_BLUE) + UX_DISPLAY(ui_data_parameter_blue, ui_data_parameter_blue_prepro); +#else + ux_flow_init(0, ux_confirm_parameter_flow, NULL); +#endif // #if TARGET_ID + } + } + else { + return CUSTOM_HANDLED; + } + + return CUSTOM_SUSPENDED; + } + } + return CUSTOM_NOT_HANDLED; +} + +void finalizeParsing(bool direct) { + uint256_t gasPrice, startGas, uint256; + uint32_t i; + uint8_t address[41]; + uint8_t decimals = WEI_TO_ETHER; + uint8_t *ticker = (uint8_t *)PIC(chainConfig->coinName); + uint8_t *feeTicker = (uint8_t *)PIC(chainConfig->coinName); + uint8_t tickerOffset = 0; + + // Verify the chain + if (chainConfig->chainId != 0) { + uint32_t v = getV(&tmpContent.txContent); + if (chainConfig->chainId != v) { + reset_app_context(); + PRINTF("Invalid chainId %d expected %d\n", v, chainConfig->chainId); + if (direct) { + THROW(0x6A80); + } + else { + io_seproxyhal_send_status(0x6A80); + ui_idle(); + return; + } + } + } + // Store the hash + cx_hash((cx_hash_t *)&sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32); +#ifdef HAVE_STARKWARE + if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) || + (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) || + (contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) || + (contractProvisioned == CONTRACT_STARKWARE_ESCAPE)) { + // For a deposit / withdrawal / escape, check if the token ID is known or can't parse + uint8_t tokenIdOffset = (4 + ((contractProvisioned == CONTRACT_STARKWARE_ESCAPE) ? 32 + 32 : 0)); + if (quantumSet) { + tokenDefinition_t *currentToken = NULL; + if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + } + compute_token_id(&sha3, + (currentToken != NULL ? currentToken->address : NULL), + dataContext.tokenContext.quantum, G_io_apdu_buffer + 100); + if (os_memcmp(dataContext.tokenContext.data + tokenIdOffset, G_io_apdu_buffer + 100, 32) != 0) { + PRINTF("Token ID not matching - computed %.*H\n", 32, G_io_apdu_buffer + 100); + PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum); + PRINTF("Requested %.*H\n", 32, dataContext.tokenContext.data + tokenIdOffset); + contractProvisioned = CONTRACT_NONE; + } + } + else { + PRINTF("Quantum not set\n"); + contractProvisioned = CONTRACT_NONE; + } + } +#endif + // If there is a token to process, check if it is well known + if ((contractProvisioned == CONTRACT_ERC20) || (contractProvisioned == CONTRACT_ALLOWANCE)) { + tokenDefinition_t *currentToken = getKnownToken(tmpContent.txContent.destination); + if (currentToken != NULL) { + dataPresent = false; + decimals = currentToken->decimals; + ticker = currentToken->ticker; + tmpContent.txContent.destinationLength = 20; + os_memmove(tmpContent.txContent.destination, dataContext.tokenContext.data + 4 + 12, 20); + os_memmove(tmpContent.txContent.value.value, dataContext.tokenContext.data + 4 + 32, 32); + tmpContent.txContent.value.length = 32; + } + } + else { + if (dataPresent && contractProvisioned == CONTRACT_NONE && !N_storage.dataAllowed) { + reset_app_context(); + PRINTF("Data field forbidden\n"); + if (direct) { + THROW(0x6A80); + } + else { + io_seproxyhal_send_status(0x6A80); + ui_idle(); + return; + } + } + } + // Add address + if (tmpContent.txContent.destinationLength != 0) { + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + /* + addressSummary[0] = '0'; + addressSummary[1] = 'x'; + os_memmove((unsigned char *)(addressSummary + 2), address, 4); + os_memmove((unsigned char *)(addressSummary + 6), "...", 3); + os_memmove((unsigned char *)(addressSummary + 9), address + 40 - 4, 4); + addressSummary[13] = '\0'; + */ + + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; + } + else + { +#ifdef TARGET_BLUE + os_memmove((void*)addressSummary, CONTRACT_ADDRESS, sizeof(CONTRACT_ADDRESS)); +#endif + strcpy(strings.common.fullAddress, "Contract"); + } + if ((contractProvisioned == CONTRACT_NONE) || (contractProvisioned == CONTRACT_ERC20) || + (contractProvisioned == CONTRACT_ALLOWANCE)) { + // Add amount in ethers or tokens + if ((contractProvisioned == CONTRACT_ALLOWANCE) && ismaxint(tmpContent.txContent.value.value, 32)) { + strcpy((char*)G_io_apdu_buffer, "Unlimited"); + } + else { + convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &uint256); + tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100); + i = 0; + while (G_io_apdu_buffer[100 + i]) { + i++; + } + adjustDecimals((char *)(G_io_apdu_buffer + 100), i, (char *)G_io_apdu_buffer, 100, decimals); + } + i = 0; + tickerOffset = 0; + while (ticker[tickerOffset]) { + strings.common.fullAmount[tickerOffset] = ticker[tickerOffset]; + tickerOffset++; + } + while (G_io_apdu_buffer[i]) { + strings.common.fullAmount[tickerOffset + i] = G_io_apdu_buffer[i]; + i++; + } + strings.common.fullAmount[tickerOffset + i] = '\0'; + } + // Compute maximum fee + PRINTF("Max fee\n"); + PRINTF("Gasprice %.*H\n", tmpContent.txContent.gasprice.length, tmpContent.txContent.gasprice.value); + PRINTF("Startgas %.*H\n", tmpContent.txContent.startgas.length, tmpContent.txContent.startgas.value); + convertUint256BE(tmpContent.txContent.gasprice.value, tmpContent.txContent.gasprice.length, &gasPrice); + convertUint256BE(tmpContent.txContent.startgas.value, tmpContent.txContent.startgas.length, &startGas); + mul256(&gasPrice, &startGas, &uint256); + tostring256(&uint256, 10, (char *)(G_io_apdu_buffer + 100), 100); + i = 0; + while (G_io_apdu_buffer[100 + i]) { + i++; + } + adjustDecimals((char *)(G_io_apdu_buffer + 100), i, (char *)G_io_apdu_buffer, 100, WEI_TO_ETHER); + i = 0; + tickerOffset=0; + while (feeTicker[tickerOffset]) { + strings.common.maxFee[tickerOffset] = feeTicker[tickerOffset]; + tickerOffset++; + } + tickerOffset++; + while (G_io_apdu_buffer[i]) { + strings.common.maxFee[tickerOffset + i] = G_io_apdu_buffer[i]; + i++; + } + strings.common.maxFee[tickerOffset + i] = '\0'; + +#ifdef NO_CONSENT + io_seproxyhal_touch_tx_ok(NULL); +#else // NO_CONSENT +#if defined(TARGET_BLUE) + ui_approval_transaction_blue_init(); +#else + +#ifdef HAVE_STARKWARE + + if (contractProvisioned == CONTRACT_STARKWARE_REGISTER) { + ux_flow_init(0, ux_approval_starkware_register_flow, NULL); + return; + } + else + if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) { + ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL); + return; + } + else + if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) { + ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL); + return; + } + else + if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) || + (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) || + (contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) || + (contractProvisioned == CONTRACT_STARKWARE_FREEZE)) { + ux_flow_init(0, ux_approval_starkware_verify_vault_id_flow, NULL); + return; + } + else + if (contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) { + ux_flow_init(0, ux_approval_starkware_withdraw_flow, NULL); + return; + } + else + if (contractProvisioned == CONTRACT_STARKWARE_ESCAPE) { + ux_flow_init(0, ux_approval_starkware_escape_flow, NULL); + return; + } + else + if (contractProvisioned == CONTRACT_STARKWARE_VERIFY_ESCAPE) { + ux_flow_init(0, ux_approval_starkware_verify_escape_flow, NULL); + return; + } + +#endif + + if (contractProvisioned == CONTRACT_ALLOWANCE) { + ux_flow_init(0, ux_approval_allowance_flow, NULL); + return; + } + + ux_flow_init(0, + ((dataPresent && !N_storage.contractDetails) ? ux_approval_tx_data_warning_flow : ux_approval_tx_flow), + NULL); +#endif // #if TARGET_ID +#endif // NO_CONSENT +} + diff --git a/src_features/signTx/ui_common_signTx.c b/src_features/signTx/ui_common_signTx.c new file mode 100644 index 0000000..9218e4c --- /dev/null +++ b/src_features/signTx/ui_common_signTx.c @@ -0,0 +1,103 @@ +#include "shared_context.h" +#include "utils.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e) { + uint8_t privateKeyData[32]; + uint8_t signature[100]; + uint8_t signatureLength; + cx_ecfp_private_key_t privateKey; + uint32_t tx = 0; + uint32_t v = getV(&tmpContent.txContent); + io_seproxyhal_io_heartbeat(); + os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path, + tmpCtx.transactionContext.pathLength, + privateKeyData, NULL); + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, + &privateKey); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + unsigned int info = 0; + io_seproxyhal_io_heartbeat(); + signatureLength = + cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256, + tmpCtx.transactionContext.hash, + sizeof(tmpCtx.transactionContext.hash), signature, sizeof(signature), &info); + os_memset(&privateKey, 0, sizeof(privateKey)); + // Parity is present in the sequence tag in the legacy API + if (tmpContent.txContent.vLength == 0) { + // Legacy API + G_io_apdu_buffer[0] = 27; + } + else { + // New API + // Note that this is wrong for a large v, but the client can always recover + G_io_apdu_buffer[0] = (v * 2) + 35; + } + if (info & CX_ECCINFO_PARITY_ODD) { + G_io_apdu_buffer[0]++; + } + if (info & CX_ECCINFO_xGTn) { + G_io_apdu_buffer[0] += 2; + } + format_signature_out(signature); + tx = 65; + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e) { + reset_app_context(); + G_io_apdu_buffer[0] = 0x69; + G_io_apdu_buffer[1] = 0x85; + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e) { + parserStatus_e txResult = USTREAM_FINISHED; + txResult = continueTx(&txContext); + switch (txResult) { + case USTREAM_SUSPENDED: + break; + case USTREAM_FINISHED: + break; + case USTREAM_PROCESSING: + io_seproxyhal_send_status(0x9000); + ui_idle(); + break; + case USTREAM_FAULT: + reset_app_context(); + io_seproxyhal_send_status(0x6A80); + ui_idle(); + break; + default: + PRINTF("Unexpected parser status\n"); + reset_app_context(); + io_seproxyhal_send_status(0x6A80); + ui_idle(); + } + + if (txResult == USTREAM_FINISHED) { + finalizeParsing(false); + } + + return 0; +} + +unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e) { + reset_app_context(); + io_seproxyhal_send_status(0x6985); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + diff --git a/src_features/signTx/ui_flow_signTx.c b/src_features/signTx/ui_flow_signTx.c new file mode 100644 index 0000000..08033a7 --- /dev/null +++ b/src_features/signTx/ui_flow_signTx.c @@ -0,0 +1,165 @@ +#include "shared_context.h" +#include "ui_callbacks.h" + +#ifdef HAVE_UX_FLOW + +UX_FLOW_DEF_NOCB( + ux_confirm_selector_flow_1_step, + pnn, + { + &C_icon_eye, + "Verify", + "selector", + }); + +UX_FLOW_DEF_NOCB( + ux_confirm_selector_flow_2_step, + bn, + { + "Selector", + strings.tmp.tmp + }); +UX_FLOW_DEF_VALID( + ux_confirm_selector_flow_3_step, + pb, + io_seproxyhal_touch_data_ok(NULL), + { + &C_icon_validate_14, + "Approve", + }); +UX_FLOW_DEF_VALID( + ux_confirm_selector_flow_4_step, + pb, + io_seproxyhal_touch_data_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_confirm_selector_flow [] = { + &ux_confirm_selector_flow_1_step, + &ux_confirm_selector_flow_2_step, + &ux_confirm_selector_flow_3_step, + &ux_confirm_selector_flow_4_step, + FLOW_END_STEP, +}; + +////////////////////////////////////////////////////////////////////// +UX_FLOW_DEF_NOCB( + ux_confirm_parameter_flow_1_step, + pnn, + { + &C_icon_eye, + "Verify", + strings.tmp.tmp2 + }); +UX_FLOW_DEF_NOCB( + ux_confirm_parameter_flow_2_step, + bnnn_paging, + { + .title = "Parameter", + .text = strings.tmp.tmp, + }); +UX_FLOW_DEF_VALID( + ux_confirm_parameter_flow_3_step, + pb, + io_seproxyhal_touch_data_ok(NULL), + { + &C_icon_validate_14, + "Approve", + }); +UX_FLOW_DEF_VALID( + ux_confirm_parameter_flow_4_step, + pb, + io_seproxyhal_touch_data_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_confirm_parameter_flow [] = { + &ux_confirm_parameter_flow_1_step, + &ux_confirm_parameter_flow_2_step, + &ux_confirm_parameter_flow_3_step, + &ux_confirm_parameter_flow_4_step, + FLOW_END_STEP, +}; + +////////////////////////////////////////////////////////////////////// +UX_FLOW_DEF_NOCB(ux_approval_tx_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); +UX_FLOW_DEF_NOCB( + ux_approval_tx_2_step, + bnnn_paging, + { + .title = "Amount", + .text = strings.common.fullAmount + }); +UX_FLOW_DEF_NOCB( + ux_approval_tx_3_step, + bnnn_paging, + { + .title = "Address", + .text = strings.common.fullAddress, + }); +UX_FLOW_DEF_NOCB( + ux_approval_tx_4_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); +UX_FLOW_DEF_VALID( + ux_approval_tx_5_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); +UX_FLOW_DEF_VALID( + ux_approval_tx_6_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +UX_FLOW_DEF_NOCB(ux_approval_tx_data_warning_step, + pbb, + { + &C_icon_warning, + "Data", + "Present", + }); + + +const ux_flow_step_t * const ux_approval_tx_flow [] = { + &ux_approval_tx_1_step, + &ux_approval_tx_2_step, + &ux_approval_tx_3_step, + &ux_approval_tx_4_step, + &ux_approval_tx_5_step, + &ux_approval_tx_6_step, + FLOW_END_STEP, +}; + +const ux_flow_step_t * const ux_approval_tx_data_warning_flow [] = { + &ux_approval_tx_1_step, + &ux_approval_tx_data_warning_step, + &ux_approval_tx_2_step, + &ux_approval_tx_3_step, + &ux_approval_tx_4_step, + &ux_approval_tx_5_step, + &ux_approval_tx_6_step, + FLOW_END_STEP, +}; + +#endif diff --git a/src_features/stark_contract_deposit/ui_flow_stark_deposit.c b/src_features/stark_contract_deposit/ui_flow_stark_deposit.c new file mode 100644 index 0000000..7fb646b --- /dev/null +++ b/src_features/stark_contract_deposit/ui_flow_stark_deposit.c @@ -0,0 +1,126 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" +#include "utils.h" + +void prepare_deposit_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_deposit_4() { + snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 + 32 - 4)); +} + +void prepare_deposit_5() { + uint256_t amount, amountPre, quantum; + uint8_t decimals; + char *ticker = (char*)PIC(chainConfig->coinName); + + if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) { + decimals = WEI_TO_ETHER; + convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &amountPre); + } + else { + tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + decimals = token->decimals; + ticker = (char*)token->ticker; + readu256BE(dataContext.tokenContext.data + 4 + 32 + 32, &amountPre); + } + readu256BE(dataContext.tokenContext.quantum, &quantum); + mul256(&amountPre, &quantum, &amount); + tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100); + strcpy(strings.common.fullAmount, ticker); + adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals); +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_deposit_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_deposit_2_step, + bnnn_paging, + { + .title = "Deposit", + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_deposit_3_step, + bnnn_paging, + prepare_deposit_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_deposit_4_step, + bnnn_paging, + prepare_deposit_4(), + { + .title = "Token Account", + .text = strings.common.fullAddress + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_deposit_5_step, + bnnn_paging, + prepare_deposit_5(), + { + .title = "Amount", + .text = strings.common.fullAmount + }); + + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_deposit_6_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_deposit_7_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_deposit_8_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_deposit_flow [] = { + &ux_approval_starkware_deposit_1_step, + &ux_approval_starkware_deposit_2_step, + &ux_approval_starkware_deposit_3_step, + &ux_approval_starkware_deposit_4_step, + &ux_approval_starkware_deposit_5_step, + &ux_approval_starkware_deposit_6_step, + &ux_approval_starkware_deposit_7_step, + &ux_approval_starkware_deposit_8_step, + FLOW_END_STEP, +}; + +#endif + diff --git a/src_features/stark_contract_escape/ui_flow_stark_escape.c b/src_features/stark_contract_escape/ui_flow_stark_escape.c new file mode 100644 index 0000000..409a355 --- /dev/null +++ b/src_features/stark_contract_escape/ui_flow_stark_escape.c @@ -0,0 +1,136 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +void prepare_escape_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_escape_4() { + uint256_t amount, amountPre, quantum; + uint8_t decimals; + char *ticker = (char*)PIC(chainConfig->coinName); + + if (dataContext.tokenContext.quantumIndex == MAX_TOKEN) { + decimals = WEI_TO_ETHER; + } + else { + tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + decimals = token->decimals; + ticker = (char*)token->ticker; + } + readu256BE(dataContext.tokenContext.data + 4 + 32 + 32 + 32, &amountPre); + readu256BE(dataContext.tokenContext.quantum, &quantum); + mul256(&amountPre, &quantum, &amount); + tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100); + strcpy(strings.common.fullAmount, ticker); + adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals); +} + +void prepare_escape_5() { + snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4 + 32); +} + +void prepare_escape_6() { + snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 - 4)); +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_escape_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_escape_2_step, + bnnn_paging, + { + .title = "Escape", + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_escape_3_step, + bnnn_paging, + prepare_escape_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_escape_4_step, + bnnn_paging, + prepare_escape_4(), + { + .title = "Amount", + .text = strings.common.fullAmount + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_escape_5_step, + bnnn_paging, + prepare_escape_5(), + { + .title = "Master Account", + .text = strings.tmp.tmp + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_escape_6_step, + bnnn_paging, + prepare_escape_6(), + { + .title = "Token Account", + .text = strings.common.fullAddress + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_escape_7_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_escape_8_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_escape_9_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_escape_flow [] = { + &ux_approval_starkware_escape_1_step, + &ux_approval_starkware_escape_2_step, + &ux_approval_starkware_escape_3_step, + &ux_approval_starkware_escape_4_step, + &ux_approval_starkware_escape_5_step, + &ux_approval_starkware_escape_6_step, + &ux_approval_starkware_escape_7_step, + &ux_approval_starkware_escape_8_step, + &ux_approval_starkware_escape_9_step, + FLOW_END_STEP, +}; + +#endif diff --git a/src_features/stark_contract_register/ui_flow_stark_register.c b/src_features/stark_contract_register/ui_flow_stark_register.c new file mode 100644 index 0000000..e3ac50e --- /dev/null +++ b/src_features/stark_contract_register/ui_flow_stark_register.c @@ -0,0 +1,124 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +void prepare_register_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_register_4() { + uint8_t privateKeyData[32]; + uint8_t address[41]; + cx_ecfp_private_key_t privateKey; + cx_ecfp_public_key_t publicKey; + os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path, + tmpCtx.transactionContext.pathLength, + privateKeyData, NULL); + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1); + os_memset(&privateKey, 0, sizeof(privateKey)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + io_seproxyhal_io_heartbeat(); + getEthAddressStringFromKey(&publicKey, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_register_5() { + snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4); +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_register_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_register_2_step, + bnnn_paging, + { + .title = "Registration", + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_register_3_step, + bnnn_paging, + prepare_register_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_register_4_step, + bnnn_paging, + prepare_register_4(), + { + .title = "From ETH address", + .text = strings.common.fullAddress + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_register_5_step, + bnnn_paging, + prepare_register_5(), + { + .title = "Master account", + .text = strings.tmp.tmp + }); + + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_register_6_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_register_7_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_register_8_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_register_flow [] = { + &ux_approval_starkware_register_1_step, + &ux_approval_starkware_register_2_step, + &ux_approval_starkware_register_3_step, + &ux_approval_starkware_register_4_step, + &ux_approval_starkware_register_5_step, + &ux_approval_starkware_register_6_step, + &ux_approval_starkware_register_7_step, + &ux_approval_starkware_register_8_step, + FLOW_END_STEP, +}; + +#endif + diff --git a/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c b/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c new file mode 100644 index 0000000..0af588c --- /dev/null +++ b/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c @@ -0,0 +1,77 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +void prepare_verify_escape_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_verify_escape_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_verify_escape_2_step, + bnnn_paging, + { + .title = "Verify Escape", + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_verify_escape_3_step, + bnnn_paging, + prepare_verify_escape_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_verify_escape_4_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_verify_escape_5_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_verify_escape_6_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_verify_escape_flow [] = { + &ux_approval_starkware_verify_escape_1_step, + &ux_approval_starkware_verify_escape_2_step, + &ux_approval_starkware_verify_escape_3_step, + &ux_approval_starkware_verify_escape_4_step, + &ux_approval_starkware_verify_escape_5_step, + &ux_approval_starkware_verify_escape_6_step, + FLOW_END_STEP, +}; + +#endif diff --git a/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c b/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c new file mode 100644 index 0000000..47870bf --- /dev/null +++ b/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c @@ -0,0 +1,116 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +void prepare_verify_vault_id_2() { + if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) { + strcpy(strings.common.fullAddress, "Cancel Deposit"); + } + else + if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) { + strcpy(strings.common.fullAddress, "Reclaim Deposit"); + } + else + if (contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) { + strcpy(strings.common.fullAddress, "Full Withdrawal"); + } + else + if (contractProvisioned == CONTRACT_STARKWARE_FREEZE) { + strcpy(strings.common.fullAddress, "Freeze"); + } +} + +void prepare_verify_vault_id_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_verify_vault_id_4() { + uint8_t offset = 0; + if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) || (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM)) { + offset = 32; + } + snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + offset + 32 - 4)); +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_verify_vault_id_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_verify_vault_id_2_step, + bnnn_paging, + prepare_verify_vault_id_2(), + { + .title = strings.common.fullAddress, + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_verify_vault_id_3_step, + bnnn_paging, + prepare_verify_vault_id_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_verify_vault_id_4_step, + bnnn_paging, + prepare_verify_vault_id_4(), + { + .title = "Token Account", + .text = strings.common.fullAddress + }); + + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_verify_vault_id_5_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_verify_vault_id_6_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_verify_vault_id_7_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_verify_vault_id_flow [] = { + &ux_approval_starkware_verify_vault_id_1_step, + &ux_approval_starkware_verify_vault_id_2_step, + &ux_approval_starkware_verify_vault_id_3_step, + &ux_approval_starkware_verify_vault_id_4_step, + &ux_approval_starkware_verify_vault_id_5_step, + &ux_approval_starkware_verify_vault_id_6_step, + &ux_approval_starkware_verify_vault_id_7_step, + FLOW_END_STEP, +}; + +#endif + diff --git a/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c b/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c new file mode 100644 index 0000000..4043638 --- /dev/null +++ b/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c @@ -0,0 +1,110 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +void prepare_register_4(); + +void prepare_withdraw_3() { + uint8_t address[41]; + getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &sha3); + strings.common.fullAddress[0] = '0'; + strings.common.fullAddress[1] = 'x'; + os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); + strings.common.fullAddress[42] = '\0'; +} + +void prepare_withdraw_5() { + char *ticker = (char*)PIC(chainConfig->coinName); + + if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + ticker = (char*)token->ticker; + } + strcpy(strings.common.fullAmount, ticker); +} + +UX_FLOW_DEF_NOCB(ux_approval_starkware_withdraw_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_withdraw_2_step, + bnnn_paging, + { + .title = "Withdrawal", + .text = " " + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_withdraw_3_step, + bnnn_paging, + prepare_withdraw_3(), + { + .title = "Contract Name", + .text = strings.common.fullAddress, + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_withdraw_4_step, + bnnn_paging, + prepare_register_4(), + { + .title = "To Eth Address", + .text = strings.common.fullAddress + }); + +UX_STEP_NOCB_INIT( + ux_approval_starkware_withdraw_5_step, + bnnn_paging, + prepare_withdraw_5(), + { + .title = "Token Symbol", + .text = strings.common.fullAmount + }); + + +UX_FLOW_DEF_NOCB( + ux_approval_starkware_withdraw_6_step, + bnnn_paging, + { + .title = "Max Fees", + .text = strings.common.maxFee, + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_withdraw_7_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); + +UX_FLOW_DEF_VALID( + ux_approval_starkware_withdraw_8_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_approval_starkware_withdraw_flow [] = { + &ux_approval_starkware_withdraw_1_step, + &ux_approval_starkware_withdraw_2_step, + &ux_approval_starkware_withdraw_3_step, + &ux_approval_starkware_withdraw_4_step, + &ux_approval_starkware_withdraw_5_step, + &ux_approval_starkware_withdraw_6_step, + &ux_approval_starkware_withdraw_7_step, + &ux_approval_starkware_withdraw_8_step, + FLOW_END_STEP, +}; + +#endif diff --git a/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c b/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c new file mode 100644 index 0000000..2b72d2a --- /dev/null +++ b/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c @@ -0,0 +1,65 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "apdu_constants.h" +#include "stark_utils.h" +#include "feature_stark_getPublicKey.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif + +void handleStarkwareGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + UNUSED(dataLength); + uint8_t privateKeyData[32]; + uint32_t bip32Path[MAX_BIP32_PATH]; + uint32_t i; + uint8_t bip32PathLength = *(dataBuffer++); + cx_ecfp_private_key_t privateKey; + reset_app_context(); + if ((bip32PathLength < 0x01) || + (bip32PathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { + THROW(0x6B00); + } + if (p2 != 0) { + THROW(0x6B00); + } + for (i = 0; i < bip32PathLength; i++) { + bip32Path[i] = U4BE(dataBuffer, 0); + dataBuffer += 4; + } + io_seproxyhal_io_heartbeat(); + starkDerivePrivateKey(bip32Path, bip32PathLength, privateKeyData); + cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + cx_ecfp_generate_pair(CX_CURVE_Stark256, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1); + os_memset(&privateKey, 0, sizeof(privateKey)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + io_seproxyhal_io_heartbeat(); +#ifndef NO_CONSENT + if (p1 == P1_NON_CONFIRM) +#endif // NO_CONSENT + { + *tx = set_result_get_stark_publicKey(); + THROW(0x9000); + } +#ifndef NO_CONSENT + else + { + // prepare for a UI based reply + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, tmpCtx.publicKeyContext.publicKey.W + 1); + ux_flow_init(0, ux_display_stark_public_flow, NULL); + + *flags |= IO_ASYNCH_REPLY; + } +#endif // NO_CONSENT +} + + +#endif diff --git a/src_features/stark_getPublicKey/feature_stark_getPublicKey.h b/src_features/stark_getPublicKey/feature_stark_getPublicKey.h new file mode 100644 index 0000000..805ac3e --- /dev/null +++ b/src_features/stark_getPublicKey/feature_stark_getPublicKey.h @@ -0,0 +1,4 @@ +#include "shared_context.h" + +uint32_t set_result_get_stark_publicKey(void); + diff --git a/src_features/stark_getPublicKey/logic_stark_getPublicKey.c b/src_features/stark_getPublicKey/logic_stark_getPublicKey.c new file mode 100644 index 0000000..bd03ca4 --- /dev/null +++ b/src_features/stark_getPublicKey/logic_stark_getPublicKey.c @@ -0,0 +1,15 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "feature_stark_getPublicKey.h" + +uint32_t set_result_get_stark_publicKey() { + uint32_t tx = 0; + os_memmove(G_io_apdu_buffer + tx, tmpCtx.publicKeyContext.publicKey.W, 65); + tx += 65; + return tx; +} + +#endif + + diff --git a/src_features/stark_getPublicKey/ui_common_stark_getPublicKey.c b/src_features/stark_getPublicKey/ui_common_stark_getPublicKey.c new file mode 100644 index 0000000..b726d29 --- /dev/null +++ b/src_features/stark_getPublicKey/ui_common_stark_getPublicKey.c @@ -0,0 +1,20 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" +#include "feature_stark_getPublicKey.h" + +unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e) { + uint32_t tx = set_result_get_stark_publicKey(); + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +#endif + diff --git a/src_features/stark_getPublicKey/ui_flow_stark_getPublicKey.c b/src_features/stark_getPublicKey/ui_flow_stark_getPublicKey.c new file mode 100644 index 0000000..4c13d88 --- /dev/null +++ b/src_features/stark_getPublicKey/ui_flow_stark_getPublicKey.c @@ -0,0 +1,49 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e); + +UX_FLOW_DEF_NOCB( + ux_display_stark_public_flow_1_step, + pnn, + { + &C_icon_eye, + "Verify", + "Stark key", + }); +UX_FLOW_DEF_NOCB( + ux_display_stark_public_flow_2_step, + bnnn_paging, + { + .title = "Stark Key", + .text = strings.tmp.tmp, + }); +UX_FLOW_DEF_VALID( + ux_display_stark_public_flow_3_step, + pb, + io_seproxyhal_touch_stark_pubkey_ok(NULL), + { + &C_icon_validate_14, + "Approve", + }); +UX_FLOW_DEF_VALID( + ux_display_stark_public_flow_4_step, + pb, + io_seproxyhal_touch_address_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_display_stark_public_flow [] = { + &ux_display_stark_public_flow_1_step, + &ux_display_stark_public_flow_2_step, + &ux_display_stark_public_flow_3_step, + &ux_display_stark_public_flow_4_step, + FLOW_END_STEP, +}; + +#endif + diff --git a/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c new file mode 100644 index 0000000..a712b7b --- /dev/null +++ b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c @@ -0,0 +1,42 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "apdu_constants.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif + +void handleStarkwareProvideQuantum(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + size_t i = 0; + tokenDefinition_t *currentToken = NULL; + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + if (dataLength != 20 + 32) { + THROW(0x6700); + } + if (!allzeroes(dataBuffer, 20)) { + for(i=0; iaddress, dataBuffer, 20) == 0)) { + break; + } + } + if (i == MAX_TOKEN) { + PRINTF("Associated token not found\n"); + THROW(0x6A80); + } + } + else { + i = MAX_TOKEN; + } + os_memmove(dataContext.tokenContext.quantum, dataBuffer + 20, 32); + dataContext.tokenContext.quantumIndex = i; + quantumSet = true; + THROW(0x9000); +} + +#endif diff --git a/src_features/stark_sign/cmd_stark_sign.c b/src_features/stark_sign/cmd_stark_sign.c new file mode 100644 index 0000000..32734ef --- /dev/null +++ b/src_features/stark_sign/cmd_stark_sign.c @@ -0,0 +1,140 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "apdu_constants.h" +#include "stark_utils.h" +#ifdef TARGET_BLUE +#include "ui_blue.h" +#endif +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif +#include "poorstream.h" +#include "ui_callbacks.h" + +#define U8BE(buf, off) (uint64_t)((((uint64_t)U4BE(buf, off)) << 32) | (((uint64_t)U4BE(buf, off + 4)) & 0xFFFFFFFF)) +#define TMP_OFFSET 140 + +void handleStarkwareSignMessage(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, unsigned int *flags, unsigned int *tx) { + uint8_t privateKeyData[32]; + uint32_t i; + uint8_t bip32PathLength = *(dataBuffer); + uint8_t offset = 1; + cx_ecfp_private_key_t privateKey; + poorstream_t bitstream; + bool selfTransfer = false; + // Initial checks + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + if ((bip32PathLength < 0x01) || + (bip32PathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + switch(p1) { + case P1_STARK_ORDER: + if (dataLength != (20 + 32 + 20 + 32 + 4 + 4 + 8 + 8 + 4 + 4 + 1 + 4 * bip32PathLength)) { + THROW(0x6700); + } + break; + case P1_STARK_TRANSFER: + if (dataLength != (20 + 32 + 32 + 4 + 4 + 8 + 4 + 4 + 1 + 4 * bip32PathLength)) { + THROW(0x6700); + } + break; + default: + THROW(0x6B00); + } + if (p2 != 0) { + THROW(0x6B00); + } + tmpCtx.transactionContext.pathLength = bip32PathLength; + for (i = 0; i < bip32PathLength; i++) { + tmpCtx.transactionContext.bip32Path[i] = U4BE(dataBuffer, offset); + PRINTF("Storing path %d %d\n", i, tmpCtx.transactionContext.bip32Path[i]); + offset += 4; + } + // Discard the path to use part of dataBuffer as a temporary buffer + os_memmove(dataBuffer, dataBuffer + offset, dataLength - offset); + // Fail immediately if the contract is unknown + if (!allzeroes(dataBuffer, 20) && getKnownToken(dataBuffer) == NULL) { + PRINTF("stark - cannot process unknown token %.*H", 20, dataBuffer); + THROW(0x6A80); + } + if ((p1 == P1_STARK_ORDER) && (!allzeroes(dataBuffer + 20 + 32, 20) && getKnownToken(dataBuffer + 20 + 32) == NULL)) { + PRINTF("stark - cannot process unknown token %.*H", 20, dataBuffer + 20 + 32); + THROW(0x6A80); + } + // Prepare the Stark parameters + io_seproxyhal_io_heartbeat(); + compute_token_id(&sha3, dataBuffer, dataBuffer + 20, dataContext.starkContext.w1); + if (p1 == P1_STARK_ORDER) { + io_seproxyhal_io_heartbeat(); + compute_token_id(&sha3, dataBuffer + 20 + 32, dataBuffer + 20 + 32 + 20, dataContext.starkContext.w2); + offset = 20 + 32 + 20 + 32; + } + else { + os_memmove(dataContext.starkContext.w2, dataBuffer + 20 + 32, 32); + offset = 20 + 32 + 32; + } + poorstream_init(&bitstream, dataContext.starkContext.w3); + poorstream_write_bits(&bitstream, 0, 11); // padding + poorstream_write_bits(&bitstream, (p1 == P1_STARK_ORDER ? STARK_ORDER_TYPE : STARK_TRANSFER_TYPE), 4); + poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset), 31); + poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset + 4), 31); + poorstream_write_bits(&bitstream, U8BE(dataBuffer, offset + 4 + 4), 63); + if (p1 == P1_STARK_ORDER) { + poorstream_write_bits(&bitstream, U8BE(dataBuffer, offset + 4 + 4 + 8), 63); + offset += 4 + 4 + 8 + 8; + } + else { + poorstream_write_bits(&bitstream, 0, 63); + offset += 4 + 4 + 8; + } + poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset), 31); + poorstream_write_bits(&bitstream, U4BE(dataBuffer, offset + 4), 22); + + PRINTF("stark w1 %.*H\n", 32, dataContext.starkContext.w1); + PRINTF("stark w2 %.*H\n", 32, dataContext.starkContext.w2); + PRINTF("stark w3 %.*H\n", 32, dataContext.starkContext.w3); + // Prepare the UI + if (p1 == P1_STARK_ORDER) { + io_seproxyhal_io_heartbeat(); + // amount to sell + stark_get_amount_string(dataBuffer, dataBuffer + 20, dataBuffer + 20 + 32 + 20 + 32 + 4 + 4, (char*)(dataBuffer + TMP_OFFSET), strings.common.fullAmount); + io_seproxyhal_io_heartbeat(); + // amount to buy + stark_get_amount_string(dataBuffer + 20 + 32, dataBuffer + 20 + 32 + 20, dataBuffer + 20 + 32 + 20 + 32 + 4 + 4 + 8, (char*)(dataBuffer + TMP_OFFSET), strings.common.maxFee); + // src vault ID + snprintf(strings.common.fullAddress, sizeof(strings.common.fullAddress), "%d", U4BE(dataBuffer, 20 + 32 + 20 + 32)); + } + else { + cx_ecfp_public_key_t publicKey; + // Check if the transfer is a self transfer + io_seproxyhal_io_heartbeat(); + starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, bip32PathLength, privateKeyData); + cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1); + os_memset(&privateKey, 0, sizeof(privateKey)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + io_seproxyhal_io_heartbeat(); + selfTransfer = (os_memcmp(publicKey.W + 1, dataBuffer + 20 + 32, 32) == 0); + PRINTF("self transfer %d\n", selfTransfer); + io_seproxyhal_io_heartbeat(); + // amount to transfer + stark_get_amount_string(dataBuffer, dataBuffer + 20, dataBuffer + 20 + 32 + 32 + 4 + 4, (char*)(dataBuffer + TMP_OFFSET), tmpContent.tmp); + // dest vault ID + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "%d", U4BE(dataBuffer, 20 + 32 + 32 + 4)); + if (!selfTransfer) { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataBuffer + 20 + 32); + } + } + ux_flow_init(0, p1 == P1_STARK_ORDER ? ux_stark_limit_order_flow : selfTransfer ? + ux_stark_self_transfer_flow : ux_stark_transfer_flow, NULL); + + *flags |= IO_ASYNCH_REPLY; +} + +#endif diff --git a/src_features/stark_sign/ui_common_stark_sign.c b/src_features/stark_sign/ui_common_stark_sign.c new file mode 100644 index 0000000..d05e4e3 --- /dev/null +++ b/src_features/stark_sign/ui_common_stark_sign.c @@ -0,0 +1,28 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "stark_utils.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e) { + uint8_t privateKeyData[32]; + uint8_t signature[72]; + uint32_t tx = 0; + io_seproxyhal_io_heartbeat(); + starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, tmpCtx.transactionContext.pathLength, privateKeyData); + io_seproxyhal_io_heartbeat(); + stark_sign(signature, privateKeyData, dataContext.starkContext.w1, dataContext.starkContext.w2, dataContext.starkContext.w3); + G_io_apdu_buffer[0] = 0; + format_signature_out(signature); + tx = 65; + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + reset_app_context(); + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); + // Display back the original UX + ui_idle(); + return 0; // do not redraw the widget +} + +#endif diff --git a/src_features/stark_sign/ui_flow_stark_sign.c b/src_features/stark_sign/ui_flow_stark_sign.c new file mode 100644 index 0000000..f4ac15c --- /dev/null +++ b/src_features/stark_sign/ui_flow_stark_sign.c @@ -0,0 +1,165 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" +#include "ui_callbacks.h" + +unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_2_step, + bnnn_paging, + { + .title = "Limit", + .text = "Order" + }); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_3_step, + bnnn_paging, + { + .title = "Trading", + .text = "Pair" + }); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_4_step, + bnnn_paging, + { + .title = "Sell", + .text = strings.common.fullAmount + }); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_5_step, + bnnn_paging, + { + .title = "Buy", + .text = strings.common.maxFee + }); + +UX_FLOW_DEF_NOCB(ux_stark_limit_order_6_step, + bnnn_paging, + { + .title = "Token Accont", + .text = strings.common.fullAddress + }); + +UX_FLOW_DEF_VALID( + ux_stark_limit_order_7_step, + pbb, + io_seproxyhal_touch_stark_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); +UX_FLOW_DEF_VALID( + ux_stark_limit_order_8_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_stark_limit_order_flow [] = { + &ux_stark_limit_order_1_step, + &ux_stark_limit_order_2_step, + &ux_stark_limit_order_3_step, + &ux_stark_limit_order_4_step, + &ux_stark_limit_order_5_step, + &ux_stark_limit_order_6_step, + &ux_stark_limit_order_7_step, + &ux_stark_limit_order_8_step, + FLOW_END_STEP, +}; + +////////////////////////////////////////////////////////////////////// +UX_FLOW_DEF_NOCB(ux_stark_transfer_1_step, + pnn, + { + &C_icon_eye, + "Review", + "transaction", + }); + +UX_FLOW_DEF_NOCB(ux_stark_transfer_2_step, + bnnn_paging, + { + .title = "Transfer", + .text = " " + }); + +UX_FLOW_DEF_NOCB(ux_stark_self_transfer_2_step, + bnnn_paging, + { + .title = "Self", + .text = "Transfer" + }); + + +UX_FLOW_DEF_NOCB(ux_stark_transfer_3_step, + bnnn_paging, + { + .title = "Amount", + .text = tmpContent.tmp + }); + +UX_FLOW_DEF_NOCB(ux_stark_transfer_4_step, + bnnn_paging, + { + .title = "Master Account", + .text = strings.tmp.tmp + }); + +UX_FLOW_DEF_NOCB(ux_stark_transfer_5_step, + bnnn_paging, + { + .title = "Token Accont", + .text = strings.tmp.tmp2 + }); + +UX_FLOW_DEF_VALID( + ux_stark_transfer_6_step, + pbb, + io_seproxyhal_touch_stark_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); +UX_FLOW_DEF_VALID( + ux_stark_transfer_7_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_stark_transfer_flow [] = { + &ux_stark_transfer_1_step, + &ux_stark_transfer_2_step, + &ux_stark_transfer_3_step, + &ux_stark_transfer_4_step, + &ux_stark_transfer_5_step, + &ux_stark_transfer_6_step, + &ux_stark_transfer_7_step, + FLOW_END_STEP, +}; + +const ux_flow_step_t * const ux_stark_self_transfer_flow [] = { + &ux_stark_transfer_1_step, + &ux_stark_self_transfer_2_step, + &ux_stark_transfer_3_step, + &ux_stark_transfer_5_step, + &ux_stark_transfer_6_step, + &ux_stark_transfer_7_step, + FLOW_END_STEP, +}; + +#endif