From c7d199cdac6e55efc48efd3a804112197badb118 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Mon, 3 Sep 2018 21:18:24 +0200 Subject: [PATCH 01/92] enable auto deploy features in Makefile and add manager icons --- Makefile | 22 ++++++++++++++++------ tezos_baking.png | Bin 0 -> 3469 bytes tezos_wallet.png | Bin 0 -> 3481 bytes 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 tezos_baking.png create mode 100644 tezos_wallet.png diff --git a/Makefile b/Makefile index 8a0d2556..c33a02a3 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,13 @@ $(error Environment variable BOLOS_SDK is not set) endif include $(BOLOS_SDK)/Makefile.defines -ifeq ($(BAKING_APP),Y) +ifeq ($(APP),) +APP=tezos_wallet +endif + +ifeq ($(APP),tezos_baking) APPNAME = "Tezos Baking" -else +else ifeq ($(APP),tezos_wallet) APPNAME = "Tezos Wallet" endif APP_LOAD_PARAMS=--appFlags 0 --curve ed25519 --curve secp256k1 --curve prime256r1 --path "44'/1729'" $(COMMON_LOAD_PARAMS) @@ -53,11 +57,14 @@ GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ CC := $(CLANGPATH)clang -ifeq ($(BAKING_APP),) +ifeq ($(APP),tezos_wallet) CFLAGS += -O3 -Os -Wall -Wextra -endif -ifeq ($(BAKING_APP),Y) +else ifeq ($(APP),tezos_baking) CFLAGS += -DBAKING_APP -O3 -Os -Wall -Wextra +else +ifeq ($(filter clean,$(MAKECMDGOALS)),) +$(error Unsupported APP - use tezos_wallet, tezos_baking) +endif endif AS := $(GCCPATH)arm-none-eabi-gcc @@ -74,7 +81,7 @@ APP_SOURCE_PATH += src SDK_SOURCE_PATH += lib_stusb lib_stusb_impl ### U2F support (wallet app only) -ifeq ($(BAKING_APP),) +ifeq ($(APP), tezos_wallet) SDK_SOURCE_PATH += lib_u2f lib_stusb_impl DEFINES += USB_SEGMENT_SIZE=64 @@ -97,3 +104,6 @@ include $(BOLOS_SDK)/Makefile.rules #add dependency on custom makefile filename dep/%.d: %.c Makefile + +listvariants: + @echo VARIANTS APP tezos_wallet tezos_baking \ No newline at end of file diff --git a/tezos_baking.png b/tezos_baking.png new file mode 100644 index 0000000000000000000000000000000000000000..9f41169b754d3916f3b097637876396f1e40f761 GIT binary patch literal 3469 zcmX|E2Q(X88%9E-siHmNg^Tt2A|Sph=MQHNzO!F^ItM@rpV{w=ZBYrLhs+dFMeNA+}qa~3P&Q5 zP?!W%LPCt(Aw~%D@^c6j^CIy7h4>$aI*x$xb;bL+dV4|sVmdf_`}-;K@j?HRK>uYS z%Y*)3A36E|4dJ}V*WydopU-K(OF;oX*HK5A2Ii9Tg7Bsq+=I#loS+O60=Mr1)-4Ta zC+Ml9>oJnU*dk#@!&KB}p=!nk8Bodwf^QS-Q!Iaruh@57s zuLOH0{31_wg?!+-y1fjZ)dD@s)XSyDu+zp?#3>knK-p2V(`^Hix|mgFjMXAJSyYj1 zj3c;)8rq=S+RU%7exy(ODK#xVE*rIP8Ea%~yOa!3i%{#DwzZw*GUk6g%^T(W^Si2H zAls0J%X`&813^4P(V0RI6Y2?N0p*es$IL>!iZBtEpSxZC`4sU3r6qRx0ZkESRafv` zq1IqkZkolPFN}Gk_H#+!fg-57h*ZYMbW6$z5H$S}T}KvVvWp06WY5z8rakO~J<5WF z15WOjYVZyL7a0=q%mQGGPn+ve6h*y$ZJ5Frklkx;GE~ttMeU^5q~8WqJ69e{rH*sz z{eJB?*<9+xB%h=;7v2o2oZ%48ShZKcU+-aKvxg4x09KcKWaszHvtuk8 zog~c~4=5W%zqUsvLr7-k9C$-Wm26fxYyI5vey-B+UZ3XuKmR0u*BV~dRMui>kkbBM$;RovSu%0IkYtgvO5z?SbT9+0yMn( zj4w1dY@*Yyv%n~UD9s^vy*TNZNQkz}lny&K0y0FAO-IX{b~O?*P zSNhFAX%N0FtFeZLEbMUG^vhSoP9k02K*yrU@Lqc}HU@k4`OjIx?KQKo`pR(PJ&RWR zI_%lOiKcn=a8Yr?ly^|DS{bcx%l0$7sTLF4{^QXL_K2he#E;XGdV!TSRzRz9W{F8R zA+dD&Hmdza5fEO;RKQRIch{BXk1dTKn6V~ye{ameT?e$rLjb3TPTitDf0AE53%)q} z{q6DH_rQ<0d|E>@S#EH3Cla$CSdkResu1wPMDTNyiomeFj=n)TDAwdT+eANg5O%;N zhJJgCx!M$72nI_aBr{tMku@Erb!-zFHx79hNxs zL!uCFQc%&B)GXnUOKtlJ9JV1svFWEjS-=PGEz z1pzy!I;#*@a|WFfqZ3OqUs)){uG%V2l+RdKR6`3?V zVn0wU+6GIu8v7_OvE6fR`MFM9qSG%Dz{N{?k95K0@g=XUxlstU5F>E7-7RUTe{dq# zVLOKvPB=qpn}7?o<^GV3%+tQ?G1ku0+6bJ#!uI8|qeNUt5#l zo!iRX;>%_H8v0zyN&SRf%`*SrboywW)e@my=e4$+T8q`K^kH#rOuhSV3h3dY$XmC@ z{h54zkI(PhQLFKc9)W$%^mTbt?K+2PbxZzhA^*-n=0 zT8bnv=Rx_^dI|!Lx;kG4c`|+>LBE>JNi|qWWxI;#b4taPYd>6QvAdBPyjnc#=ysF% zg9=k8p}tZdGe3}DU=SQG$)(O1X;Jk$V8vkZs5i8YQuW@WYnUL9CtJ|ze5Bn#Dd|*U z{4o66r~vh*x1=Ct@vDhdyQ_6ir`zf`| zp9ellhcQzoAvyy#Rj*2h$KItkc9@llg*u-S@=4mZ1q$r7XdW90@0MG6zvbZ-O=mQ-FP zKdcjUTL*^`(BfzUcx}0_t^I%wy$xFr$;>Y(2cQ@T=jzm;Nao@6wz4cSR^zg$z9W|yY_V<=AVpn+BQRhI z!K#En?dNv1lpWc|=u}82P$B?Y25nr?yE=hnKP>LEDD&>8AJfMzmp|wmP%oTzs~}FQ zC!!BZCd+jU(aRShb%6nsh_Fn-h3IkluoNjCQrfhT`&LE!f~FWmFM3mKOZmK%RPp*y zbu*J~GVsC1}Hy(Xd{iZ89YSi9o=wav3B*) zUBpl46hg~F^zF+D;Ef?|LjZ(ZvfZPTEzEMifJY2RhwAvk8=Oy zNPf1V4ItZ0l@mA_EtX&-f_unPboRgwh`baU`?^JY!cqEOxsRk_wMr+YcmvPJfMq-S zW)a@5qIpkhIG@KjHy{3tV!M$JA3A1}(sSuX8-r8Sep9b;+KaB%h%6>YEmxu4PAB?tADkQ?zZg-q? zhWrG;Xo_;%tJnYWe=HyI+H6DJ{<t*7%aRl^jC~gSSU;o^pD1fFS<6zEXzYwK zmSjnbEo3Vug%GBE`KSN?p8r12UCuq{zVAKvo_n6>-mQo|yqXz&m22_fNX8<7-j3@Z`k@O(z zZ43z5&r1(-@r)J1ieg0Y^)rhKCfG+=J9tI~c%r=^2Kqu6styN$NC@=+Q;C735FM%> zA^$1OMa(AJ0*P}h!5se|$hxls0EY!}##jex!8cko#o@hNpTVzwX#Osg3zPr^ z8NN7qXT#(*xTJ!jfy#`-yTmGTDJYCxQt#XE%r_d$q zE`*Rg#>M0k%{53c`S8g`%vMmAD024&CB zK@lUcuZTL4388`=X7@YNY@u)re-;m;^x)R0)S~FYx_vS`W0jOxBZQQj5dO+V;&)h+ z2F+Cfw>*yGJ1ry5DERQ*;ze;?<K;;(rGk4#LYRCK=z)iEdP6?ur<{OJ+MH z<&mvtRs@9O4Vflt$%|gxhk#on3bLY08gsGd)=TVoXcXpt^x&QT*s$mme4EpBWdUhs zjClOe4rEG2K{#GSUJI6-R~Rvw3Rdbjo|7{sT%#TlSpZ*XEY1Zf3hfQZMES)4I_#rP5-6>hb4W(0pK`Qd;$A8GTUglQ-F^A-D>GiPBEf6L1+ zo=n~Ez2aW>DfN8D!@Y^?vqY_}jmN)~YyIdlBY|t==!@9>RH0@rV=Q!UJO`ZnlG`q0 zt}GLIOr!+NibE+Tn@J}p9^PzFz#8)>D4RVg6cL1g_)5Sj;m{-T3B`PJ+j`||$Oa`v zqz@jH7T$YIvK)WKp(3yVEtYu$`Kxiu_?J{VSQVnxln(PW_4 z^AvW#9&U&B)BX6NKFl`SaGt2f-^b^5{~CLoF74b>oRQKX6}w{@(B3#VB+|t)|3TFZ^V(ap_WIt z^_O*3LqCXCH$J63N_Ew8{koa#YC*;ssco%l@8m@W+duut1i*zBPGc_pP%EIln8xAi z=WR%^lJ;JMw3h2!4Jom~CYYnM(2my1KE*nnkpuiWKo-yjSUkS@4$pSwdaNxOJYX|3 zJ$qOHJEkgYTBg5LwK`*>8PFuIVsiKxAW@YOCa&3w`(<-+S>h+1E4SpTFfy(zA(~{_ z@+B($<=~XsAqge^ZBH!zWEh6=)#YW~naG~ln(*&Z|1{>({pXz_+l`DS`OK~Xk?ScE zJU{&eTYSQW>@~##YkuVMIuW-E-@dl{=_>dVm+slZqubq z%criWV8S2Bgxqz`&`m_pZdA3R1_E48>x`M<&Wj})mVe=K%g7Uu8_~-kh71KtHivj4 zU4|rVf&$BKf7RmIA|JyBZcM=+ z)%hD0kADpl?-7RdKkJm#uy$(N%uTnU?1eT2N=I9)PF{8y;oSX`7Skw|!4;2DO!QwU zI8iO=qp!AYj7-UUv;2wI(=T7GXV(WC6}xIYVeh-ErPmsOW%N}75D~-X;V-RiiUqLh zjWUF8JRzL*J2Lt>`{QJ%`1TH~dvEu|XQXC8-I5-leAsQt=7emv$kmv)X4^Uk)isVu zV5O>0Gt!x+x{W`Bw(ESIrcaT=TF9iTndY|Ck@|y`_e7C%p;Up?CsWX?r2<%U74`lb z58cFin#F$!Z;LH%V;K7bCUwJWoqdwJLRDVfYt5wQrL0=GZ4&rUy7n8+DxzLYpYLRy zVCll%hqszUw{shxibG=c8=ft2XNM8*dPLD~usp>SyW7(sysR~iEkC=oKq@C8qH6B< zz8mBP;SF)gf~Po<@z$cA$|@HlF61~iZq&y5$VSz4+`UKpTuARoVeNF%9~T6jep>(D z-v5I5s{AvC!Fw-BD)EEp&B9AglY?@aH*hx|9!-Tp4NWaBH+8cjxzA?-g;!_ybH3aN z&t3bbaWB%}Lc?c7H|}84g#EDQR*(m&RweW81 z)M#29F0Et8xL83NB;sNHa9L2?o^WaFKVR1qt`1&{XR8x&2~zVi&tQ;?&FSs$80Vhz5!hk<*gvE zdA%B56wjeWpocaPYCmn+svBXsQru-ps`oYTO34p+OcYTZU=sfH6y{mI)f^l5$#$*W zw`u7@=Sc<`;5i4I5qo8~jAFrgjJwj?b(-egC50L}Vwja-bKpeFqa#%^Wv(i-#@`dC zDc>^m7rn}*ubAt`IzJ0^jmvJZN1il5LWM_@7zTxm1MO;1A!Oid-zxae(D_GacqwkZk6_|Q$4GZ-ng8=y+*x{A&sMF(i`%%;+6Xa|H8~14*NjGsv`E59lbgU-0{M#0=9H7P0JkgJx_W_~k;iCktJDH@mx%oyvBTn8c zE&X5|kZ;$>lc9mR-?yATuVi4J>+0kXQGD%v;uzJR~c=l!ZE9G;ez?@TG&go z+b#aAm5D58P6olFgtHv!wr^isU-mibjRhkuEu5;JuK|E_mPRs+sDFwWQf?>sM{|Fi zT3~q$#1!6@fOlSgG0@hBAb)xN_{BT_?3vT<=-FTGiPdc?7($6|3{MGoIlh#)|M`p6 zrd&Ih(Lu3@bgb0E6v7?wBX+~1LH zsOQ)8oCYH^J&)l9GGjwBzyj}3;gpir?1jZN*|SsUF8idXS2w=;m1Cfq7|1s!3V5Hx zhXpDtSC$r)-R1<;$s5Kwzi`&npk>kdd{C?$r)h|fFdlEA{^ z4oxY=<*}p1UEKGtzVwmJ$Y^KHaGa=Ps4T6fSB_1lTg7byxo+PT!?&nU3 z&H^o(=jpI;&kXKKYsAT!yTD%i1+h6xU>83 zJRAFF%=abbVQb}QJSxEQ7~z8Uh$36S;JHDi^2d=Er3Sv4VqjWQdq?p{@nBDr!JAi@ zF+prK)EddOpRteQ&*2gVrUOOtA|=3U}VxxW>>u@jvtx BUZwy5 literal 0 HcmV?d00001 From af01012e108a18b83ac0744578de2c762ebb0782 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Mon, 3 Sep 2018 17:33:20 -0400 Subject: [PATCH 02/92] fix for zarith amounts that do not fit in a signed int32_t --- src/operations.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operations.c b/src/operations.c index d2e59007..ff458725 100644 --- a/src/operations.c +++ b/src/operations.c @@ -71,7 +71,7 @@ static inline uint64_t parse_z(const void *data, size_t *ix, size_t length, uint uint64_t acc = 0; uint64_t shift = 0; while (true) { - uint8_t byte = next_byte(data, ix, length, lineno); + uint64_t byte = next_byte(data, ix, length, lineno); acc |= (byte & 0x7F) << shift; shift += 7; if (!(byte & 0x80)) { From 0954efc8e26b8768327e350820a917588d6066eb Mon Sep 17 00:00:00 2001 From: Jean P <10632523+TamtamHero@users.noreply.github.com> Date: Wed, 7 Nov 2018 23:40:11 +0100 Subject: [PATCH 03/92] Makefile more flexible with dev env --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e666a985..c58c1702 100644 --- a/Makefile +++ b/Makefile @@ -63,8 +63,11 @@ DEFINES += COMMIT=\"$(COMMIT)\" APPVERSION_N=$(APPVERSION_N) APPVERSION_P=$(AP ############## # Compiler # ############## +ifneq ($(BOLOS_ENV),) GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ +endif + CC := $(CLANGPATH)clang ifeq ($(APP),tezos_wallet) @@ -116,4 +119,4 @@ include $(BOLOS_SDK)/Makefile.rules dep/%.d: %.c Makefile listvariants: - @echo VARIANTS APP tezos_wallet tezos_baking \ No newline at end of file + @echo VARIANTS APP tezos_wallet tezos_baking From d7e07a0857adbbe9e84d9ece9005db9053f82df9 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 28 Dec 2018 11:59:49 -0500 Subject: [PATCH 04/92] just in time buffer copy screen display working with display glitches -- step one to closure based display --- src/ui.c | 5 ++++- src/ui_prompt.c | 57 +++++++++++++++++++++++-------------------------- src/ui_prompt.h | 1 + 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/ui.c b/src/ui.c index 3c8b8f77..53baf90b 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,6 +1,7 @@ #include "ui.h" #include "ui_menu.h" +#include "ui_prompt.h" #include "baking_auth.h" #include "keys.h" @@ -172,7 +173,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { min = 4000; } - if (ux_step == element->component.userid - 1) { + if (ux_step == element->component.userid - 1 || element->component.userid == 100) { // timeouts are in millis if (ux_step_count > 1) { UX_CALLBACK_SET_INTERVAL(MAX(min, @@ -191,6 +192,7 @@ void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callbac // Adapted from definition of UX_DISPLAY in header file timeout_cycle_count = 0; ux_step = 0; + switch_screen(0); ux_step_count = step_count; ok_callback = ok_c; cxl_callback = cxl_c; @@ -225,6 +227,7 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { if (ux.callback_interval_ms == 0) { // prepare next screen ux_step = (ux_step + 1) % ux_step_count; + switch_screen(ux_step); // check if we've timed out if (ux_step == 0) { diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 7df7a57c..03308fb2 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -8,28 +8,8 @@ static char prompts[MAX_SCREEN_COUNT][PROMPT_WIDTH + 1]; // Additional bytes ini static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; // and null they shall remain. // TODO: Get rid of +1 -#define SCREEN_FOR(SCREEN_ID) \ - {{BAGL_LABELINE, SCREEN_ID + 1, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, \ - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, \ - prompts[SCREEN_ID], \ - 0, \ - 0, \ - 0, \ - NULL, \ - NULL, \ - NULL}, \ - {{BAGL_LABELINE, SCREEN_ID + 1, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, \ - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, \ - values[SCREEN_ID], \ - 0, \ - 0, \ - 0, \ - NULL, \ - NULL, \ - NULL} - -#define INITIAL_ELEMENTS_COUNT 3 -#define ELEMENTS_PER_SCREEN 2 +static char active_prompt[PROMPT_WIDTH + 1]; +static char active_value[PROMPT_WIDTH + 1]; static const bagl_element_t ui_multi_screen[] = { {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, @@ -62,13 +42,25 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - SCREEN_FOR(0), - SCREEN_FOR(1), - SCREEN_FOR(2), - SCREEN_FOR(3), - SCREEN_FOR(4), - SCREEN_FOR(5), - SCREEN_FOR(6), + {{BAGL_LABELINE, 100, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, + BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, + active_prompt, + 0, + 0, + 0, + NULL, + NULL, + NULL}, + + {{BAGL_LABELINE, 100, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, + BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, + active_value, + 0, + 0, + 0, + NULL, + NULL, + NULL} }; char *get_value_buffer(uint32_t which) { @@ -76,6 +68,11 @@ char *get_value_buffer(uint32_t which) { return values[which]; } +void switch_screen(uint32_t which) { + memcpy(active_prompt, prompts[which], sizeof(active_prompt)); + memcpy(active_value, values[which], sizeof(active_value)); +} + __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c) { check_null(labels); @@ -95,7 +92,7 @@ void ui_prompt(const char *const *labels, const char *const *data, callback_t ok } size_t screen_count = i; - ui_display(ui_multi_screen, INITIAL_ELEMENTS_COUNT + screen_count * ELEMENTS_PER_SCREEN, + ui_display(ui_multi_screen, sizeof(ui_multi_screen) / sizeof(*ui_multi_screen), ok_c, cxl_c, screen_count); THROW(ASYNC_EXCEPTION); } diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 25c82eb2..82338dea 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -25,3 +25,4 @@ __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c); char *get_value_buffer(uint32_t which); +void switch_screen(uint32_t which); From c4527fa8c906ae44a07b1c5f77991c9f219e0f6b Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 28 Dec 2018 12:21:04 -0500 Subject: [PATCH 05/92] fix glitch in buffer copying version --- src/ui.c | 8 ++------ src/ui_prompt.c | 7 ++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/ui.c b/src/ui.c index 53baf90b..7b0a7e95 100644 --- a/src/ui.c +++ b/src/ui.c @@ -175,12 +175,8 @@ const bagl_element_t *prepro(const bagl_element_t *element) { if (ux_step == element->component.userid - 1 || element->component.userid == 100) { // timeouts are in millis - if (ux_step_count > 1) { - UX_CALLBACK_SET_INTERVAL(MAX(min, - (1500 + bagl_label_roundtrip_duration_ms(element, 7)) / div)); - } else { - UX_CALLBACK_SET_INTERVAL(30000 / PROMPT_CYCLES); - } + UX_CALLBACK_SET_INTERVAL(MAX(min, + (1500 + bagl_label_roundtrip_duration_ms(element, 7)) / div)); return element; } else { return NULL; diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 03308fb2..239a9808 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -9,7 +9,7 @@ static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; // and null they shall // TODO: Get rid of +1 static char active_prompt[PROMPT_WIDTH + 1]; -static char active_value[PROMPT_WIDTH + 1]; +static char active_value[VALUE_WIDTH + 1]; static const bagl_element_t ui_multi_screen[] = { {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, @@ -42,7 +42,7 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - {{BAGL_LABELINE, 100, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, + {{BAGL_LABELINE, 0, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, active_prompt, 0, @@ -60,7 +60,7 @@ static const bagl_element_t ui_multi_screen[] = { 0, NULL, NULL, - NULL} + NULL}, }; char *get_value_buffer(uint32_t which) { @@ -69,6 +69,7 @@ char *get_value_buffer(uint32_t which) { } void switch_screen(uint32_t which) { + if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); memcpy(active_prompt, prompts[which], sizeof(active_prompt)); memcpy(active_value, values[which], sizeof(active_value)); } From 45df4185efc759a8a4c1d27a59119ef93033ef8a Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 28 Dec 2018 12:30:56 -0500 Subject: [PATCH 06/92] only have one dynamic buffer for prompts rather than copying all current prompts into big memory blob --- src/ui_prompt.c | 16 +++++++++++----- src/ui_prompt.h | 5 ++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 239a9808..39383070 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -4,8 +4,11 @@ #include -static char prompts[MAX_SCREEN_COUNT][PROMPT_WIDTH + 1]; // Additional bytes init'ed to null, -static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; // and null they shall remain. +// This will and must always be static memory full of constants +static const char *const *prompts; + +// Additional bytes (by OS) init'ed to null, and null they shall remain. +static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; // TODO: Get rid of +1 static char active_prompt[PROMPT_WIDTH + 1]; @@ -70,21 +73,24 @@ char *get_value_buffer(uint32_t which) { void switch_screen(uint32_t which) { if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); - memcpy(active_prompt, prompts[which], sizeof(active_prompt)); + const char *label = (const char*)PIC(prompts[which]); + + // This will not overwrite terminating bytes + strncpy(active_prompt, label, PROMPT_WIDTH); memcpy(active_value, values[which], sizeof(active_value)); } __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c) { check_null(labels); + prompts = labels; size_t i; for (i = 0; labels[i] != NULL; i++) { const char *label = (const char *)PIC(labels[i]); if (i >= MAX_SCREEN_COUNT || strlen(label) > PROMPT_WIDTH) THROW(EXC_MEMORY_ERROR); - // These will not overwrite terminating bytes - strncpy(prompts[i], label, PROMPT_WIDTH); + // This will not overwrite terminating bytes if (data != NULL) { const char *value = (const char *)PIC(data[i]); if (strlen(value) > VALUE_WIDTH) THROW(EXC_MEMORY_ERROR); diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 82338dea..d4f69b53 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -20,7 +20,10 @@ }) // Displays labels (terminated with a NULL pointer) associated with data -// If data is NULL, assume we've filled it in directly with get_value_buffer +// labels must be completely static string constants +// data may be dynamic +// Alternatively, if data is NULL, assume we've filled it in directly with get_value_buffer +// All pointers may be unrelocated __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c); From f6231b9e5663d1b56bf3918e1f90f3ac544a4233 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Mon, 31 Dec 2018 13:32:02 -0500 Subject: [PATCH 07/92] use symbolic constants to make clear intention in magic number bagl values --- src/ui.c | 5 ++--- src/ui.h | 3 +++ src/ui_prompt.c | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ui.c b/src/ui.c index 7b0a7e95..e6afcbd3 100644 --- a/src/ui.c +++ b/src/ui.c @@ -163,8 +163,7 @@ unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned b } const bagl_element_t *prepro(const bagl_element_t *element) { - // Always display elements with userid 0 - if (element->component.userid == 0) return element; + if (element->component.userid == BAGL_STATIC_ELEMENT) return element; uint32_t min = 2000; uint32_t div = 2; @@ -173,7 +172,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { min = 4000; } - if (ux_step == element->component.userid - 1 || element->component.userid == 100) { + if (ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { // timeouts are in millis UX_CALLBACK_SET_INTERVAL(MAX(min, (1500 + bagl_label_roundtrip_duration_ms(element, 7)) / div)); diff --git a/src/ui.h b/src/ui.h index a7c4d878..9bbdeeda 100644 --- a/src/ui.h +++ b/src/ui.h @@ -8,6 +8,9 @@ #define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte +#define BAGL_STATIC_ELEMENT 0 +#define BAGL_SCROLLING_ELEMENT 100 // Arbitrary value + typedef bool (*callback_t)(void); // return true to go back to idle screen void ui_initial_screen(void); diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 39383070..af6b3c13 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -15,7 +15,7 @@ static char active_prompt[PROMPT_WIDTH + 1]; static char active_value[VALUE_WIDTH + 1]; static const bagl_element_t ui_multi_screen[] = { - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, + {{BAGL_RECTANGLE, BAGL_STATIC_ELEMENT, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, NULL, 0, @@ -25,7 +25,7 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, + {{BAGL_ICON, BAGL_STATIC_ELEMENT, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, NULL, 0, @@ -35,7 +35,7 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, + {{BAGL_ICON, BAGL_STATIC_ELEMENT, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, NULL, 0, @@ -45,7 +45,7 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - {{BAGL_LABELINE, 0, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, + {{BAGL_LABELINE, BAGL_STATIC_ELEMENT, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, active_prompt, 0, @@ -55,7 +55,7 @@ static const bagl_element_t ui_multi_screen[] = { NULL, NULL}, - {{BAGL_LABELINE, 100, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, + {{BAGL_LABELINE, BAGL_SCROLLING_ELEMENT, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, active_value, 0, From 8a5c02e1eeb4796678579c49221c43856b7d36fa Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 16:26:25 -0500 Subject: [PATCH 08/92] first attempt at callbacks --- src/to_string.c | 19 +++++++++++++++++++ src/to_string.h | 7 ++++++- src/ui_prompt.c | 21 ++++++++++++++++++++- src/ui_prompt.h | 4 ++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/to_string.c b/src/to_string.c index a72e2304..1b225619 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -8,6 +8,8 @@ #define NO_CONTRACT_STRING "None" +static int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); + int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { if (contract->originated == 0 && contract->curve_code == TEZOS_NO_CURVE) { if (buff_size < sizeof(NO_CONTRACT_STRING)) return 0; @@ -128,6 +130,16 @@ static inline size_t convert_number(char dest[MAX_INT_DIGITS], uint64_t number, return 0; } +size_t number_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) return 0; // terminating null + return number_to_string(dest, *number); +} + +size_t microtez_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) return 0; // + terminating null + decimal point + return microtez_to_string(dest, *number); +} + size_t number_to_string(char *dest, uint64_t number) { char tmp[MAX_INT_DIGITS]; size_t off = convert_number(tmp, number, false); @@ -171,3 +183,10 @@ size_t microtez_to_string(char *dest, uint64_t number) { dest[off] = '\0'; return off; } + +bool copy_string(char *dest, uint32_t buff_size, const char *src) { + // I don't care that we will loop through the string twice, latency is not an issue + if (strlen(src) >= buff_size) return false; + strcpy(dest, src); + return true; +} diff --git a/src/to_string.h b/src/to_string.h index 1e6f09f7..8b2c1173 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -12,7 +12,6 @@ int pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, const cx_ecfp_public_key_t *public_key); -int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); size_t protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); @@ -21,3 +20,9 @@ int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parse #define MAX_INT_DIGITS 20 size_t number_to_string(char *dest, uint64_t number); size_t microtez_to_string(char *dest, uint64_t number); + +// These take their number parameter through a pointer and take a length +size_t number_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); +size_t microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); + +bool copy_string(char *dest, uint32_t buff_size, const char *src); diff --git a/src/ui_prompt.c b/src/ui_prompt.c index af6b3c13..a17e0c2b 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -11,6 +11,9 @@ static const char *const *prompts; static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; // TODO: Get rid of +1 +static string_generation_callback callbacks[MAX_SCREEN_COUNT]; +static const void *callback_data[MAX_SCREEN_COUNT]; + static char active_prompt[PROMPT_WIDTH + 1]; static char active_value[VALUE_WIDTH + 1]; @@ -77,7 +80,23 @@ void switch_screen(uint32_t which) { // This will not overwrite terminating bytes strncpy(active_prompt, label, PROMPT_WIDTH); - memcpy(active_value, values[which], sizeof(active_value)); + if (callbacks[which] != NULL) { + callbacks[which](active_value, sizeof(active_value), callback_data[which]); + } else { + memcpy(active_value, values[which], sizeof(active_value)); + } +} + +void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data) { + if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); + callbacks[which] = cb; + callback_data[which] = data; +} + +void clear_ui_callbacks(void) { + for (int i = 0; i < MAX_SCREEN_COUNT; ++i) { + callbacks[i] = NULL; + } } __attribute__((noreturn)) diff --git a/src/ui_prompt.h b/src/ui_prompt.h index d4f69b53..4a529229 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -29,3 +29,7 @@ void ui_prompt(const char *const *labels, const char *const *data, callback_t ok char *get_value_buffer(uint32_t which); void switch_screen(uint32_t which); + +typedef bool (*string_generation_callback)(char *dest, size_t size, const void *data); +void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); +void clear_ui_callbacks(void); From f93f2b41a1368ea2c42942f1fd873b2f8e298259 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 16:38:31 -0500 Subject: [PATCH 09/92] believe this fixes the crashes in previous patch --- src/boot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/boot.c b/src/boot.c index 3eced2cb..0693f9b1 100644 --- a/src/boot.c +++ b/src/boot.c @@ -40,6 +40,7 @@ __attribute__((section(".boot"))) int main(void) { USB_power(0); USB_power(1); + clear_ui_callbacks(); ui_initial_screen(); app_main(); From 4e513b4a5481f6889ab6aeffc3d6649d854e07a2 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 17:08:40 -0500 Subject: [PATCH 10/92] use callback system to display some (but not all) prompts --- src/apdu_sign.c | 24 ++++++++++-------------- src/boot.c | 1 - src/to_string.c | 3 ++- src/ui.c | 2 ++ src/ui_prompt.c | 4 +++- src/ui_prompt.h | 1 + 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index f7110907..a703b462 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -150,7 +150,7 @@ const char *const insecure_values[] = { #define MAX_NUMBER_CHARS (MAX_INT_DIGITS + 2) // include decimal point and terminating null -#define SET_STATIC_UI_VALUE(index, str) strcpy(get_value_buffer(index), STATIC_UI_VALUE(str)) +#define SET_STATIC_UI_VALUE(index, str) REGISTER_UI_CALLBACK(index, copy_string, STATIC_UI_VALUE(str)) // Return false if the transaction isn't easily parseable, otherwise prompt with given callbacks // and do not return, but rather throw ASYNC. @@ -208,11 +208,9 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - if (!number_to_string(get_value_buffer(PERIOD_INDEX), - ops->operation.proposal.voting_period)) return false; - + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + REGISTER_UI_CALLBACK(PERIOD_INDEX, number_to_string_indirect, &ops->operation.proposal.voting_period); + REGISTER_UI_CALLBACK(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); if (!protocol_hash_to_string(get_value_buffer(PROTOCOL_HASH_INDEX), VALUE_WIDTH, ops->operation.proposal.protocol_hash)) return false; @@ -390,17 +388,15 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - if (!parsed_contract_to_string(get_value_buffer(DESTINATION_INDEX), VALUE_WIDTH, - &ops->operation.destination)) return false; - microtez_to_string(get_value_buffer(FEE_INDEX), ops->total_fee); - number_to_string(get_value_buffer(STORAGE_INDEX), ops->total_storage_limit); + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + &ops->operation.destination); + REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + REGISTER_UI_CALLBACK(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); SET_STATIC_UI_VALUE(TYPE_INDEX, "Transaction"); - microtez_to_string(get_value_buffer(AMOUNT_INDEX), ops->operation.amount); - ui_prompt(transaction_prompts, NULL, ok, cxl); } case OPERATION_TAG_NONE: diff --git a/src/boot.c b/src/boot.c index 0693f9b1..3eced2cb 100644 --- a/src/boot.c +++ b/src/boot.c @@ -40,7 +40,6 @@ __attribute__((section(".boot"))) int main(void) { USB_power(0); USB_power(1); - clear_ui_callbacks(); ui_initial_screen(); app_main(); diff --git a/src/to_string.c b/src/to_string.c index 1b225619..ab20b732 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -184,7 +184,8 @@ size_t microtez_to_string(char *dest, uint64_t number) { return off; } -bool copy_string(char *dest, uint32_t buff_size, const char *src) { +bool copy_string(char *dest, uint32_t buff_size, const char *src_in) { + const char *src = (const char *)PIC(src_in); // I don't care that we will loop through the string twice, latency is not an issue if (strlen(src) >= buff_size) return false; strcpy(dest, src); diff --git a/src/ui.c b/src/ui.c index e6afcbd3..fd625f2b 100644 --- a/src/ui.c +++ b/src/ui.c @@ -125,6 +125,7 @@ void ui_initial_screen(void) { #ifdef BAKING_APP change_idle_display(N_data.highest_level); #endif + clear_ui_callbacks(); ui_idle(); } @@ -157,6 +158,7 @@ unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned b return 0; } if (callback()) { + clear_ui_callbacks(); ui_idle(); } return 0; diff --git a/src/ui_prompt.c b/src/ui_prompt.c index a17e0c2b..1ad5f909 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -81,7 +81,9 @@ void switch_screen(uint32_t which) { // This will not overwrite terminating bytes strncpy(active_prompt, label, PROMPT_WIDTH); if (callbacks[which] != NULL) { - callbacks[which](active_value, sizeof(active_value), callback_data[which]); + if (!callbacks[which](active_value, sizeof(active_value), callback_data[which])) { + THROW(EXC_MEMORY_ERROR); + } } else { memcpy(active_value, values[which], sizeof(active_value)); } diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 4a529229..b4ce95a8 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -32,4 +32,5 @@ void switch_screen(uint32_t which); typedef bool (*string_generation_callback)(char *dest, size_t size, const void *data); void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); +#define REGISTER_UI_CALLBACK(which, cb, data) register_ui_callback(which, (string_generation_callback)cb, data) void clear_ui_callbacks(void); From 9d9f62d81fad93bb7cf8479ab8dc9cf72e3d23a2 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 17:24:59 -0500 Subject: [PATCH 11/92] first compiling full conversion --- src/apdu_baking.c | 7 +++--- src/apdu_sign.c | 57 +++++++++++++++++++++-------------------------- src/operations.h | 3 ++- src/to_string.c | 4 ++++ src/to_string.h | 4 ---- src/ui.c | 4 ++-- src/ui.h | 2 +- src/ui_prompt.c | 23 +++++-------------- src/ui_prompt.h | 8 ++++--- 9 files changed, 48 insertions(+), 64 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 1a62293a..a305a9c4 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -9,7 +9,8 @@ #include -static level_t reset_level; +// Must be wider to be passed to number_to_string_indirect +static uint64_t reset_level; static bool reset_ok(void); @@ -27,7 +28,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { reset_level = lvl; - number_to_string(get_value_buffer(0), reset_level); + REGISTER_UI_CALLBACK(0, number_to_string_indirect, &reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), @@ -37,7 +38,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { } bool reset_ok(void) { - write_highest_level(reset_level, false); // We have not yet had an endorsement at this level + write_highest_level((level_t)reset_level, false); // We have not yet had an endorsement at this level uint32_t tx = 0; G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_sign.c b/src/apdu_sign.c index a703b462..8df28060 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -211,8 +211,6 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); REGISTER_UI_CALLBACK(PERIOD_INDEX, number_to_string_indirect, &ops->operation.proposal.voting_period); REGISTER_UI_CALLBACK(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); - if (!protocol_hash_to_string(get_value_buffer(PROTOCOL_HASH_INDEX), VALUE_WIDTH, - ops->operation.proposal.protocol_hash)) return false; SET_STATIC_UI_VALUE(TYPE_INDEX, "Proposal"); ui_prompt(proposal_prompts, NULL, ok, cxl); @@ -233,12 +231,10 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - if (!protocol_hash_to_string(get_value_buffer(PROTOCOL_HASH_INDEX), VALUE_WIDTH, - ops->operation.ballot.protocol_hash)) return false; - if (!number_to_string(get_value_buffer(PERIOD_INDEX), - ops->operation.ballot.voting_period)) return false; + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + REGISTER_UI_CALLBACK(PROTOCOL_HASH_INDEX, protocol_hash_to_string, + ops->operation.ballot.protocol_hash); + REGISTER_UI_CALLBACK(PERIOD_INDEX, number_to_string_indirect, &ops->operation.ballot.voting_period); switch (ops->operation.ballot.vote) { case BALLOT_VOTE_YEA: @@ -265,12 +261,12 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t DELEGATE_INDEX = 5; static const uint32_t STORAGE_INDEX = 6; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - if (!parsed_contract_to_string(get_value_buffer(DESTINATION_INDEX), VALUE_WIDTH, - &ops->operation.destination)) return false; - microtez_to_string(get_value_buffer(FEE_INDEX), ops->total_fee); - number_to_string(get_value_buffer(STORAGE_INDEX), ops->total_storage_limit); + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + &ops->operation.destination); + REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, + &ops->total_storage_limit); static const char *const origination_prompts_fixed[] = { PROMPT("Confirm"), @@ -306,22 +302,22 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve if (!(ops->operation.flags & ORIGINATION_FLAG_SPENDABLE)) return false; SET_STATIC_UI_VALUE(TYPE_INDEX, "Origination"); - microtez_to_string(get_value_buffer(AMOUNT_INDEX), ops->operation.amount); + REGISTER_UI_CALLBACK(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); const char *const *prompts; bool delegatable = ops->operation.flags & ORIGINATION_FLAG_DELEGATABLE; bool has_delegate = ops->operation.delegate.curve_code != TEZOS_NO_CURVE; if (delegatable && has_delegate) { prompts = origination_prompts_delegatable; - if (!parsed_contract_to_string(get_value_buffer(DELEGATE_INDEX), VALUE_WIDTH, - &ops->operation.delegate)) return false; + REGISTER_UI_CALLBACK(DELEGATE_INDEX, parsed_contract_to_string, + &ops->operation.delegate); } else if (delegatable && !has_delegate) { prompts = origination_prompts_delegatable; SET_STATIC_UI_VALUE(DELEGATE_INDEX, "Any"); } else if (!delegatable && has_delegate) { prompts = origination_prompts_fixed; - if (!parsed_contract_to_string(get_value_buffer(DELEGATE_INDEX), VALUE_WIDTH, - &ops->operation.delegate)) return false; + REGISTER_UI_CALLBACK(DELEGATE_INDEX, parsed_contract_to_string, + &ops->operation.delegate); } else if (!delegatable && !has_delegate) { prompts = origination_prompts_undelegatable; SET_STATIC_UI_VALUE(DELEGATE_INDEX, "Disabled"); @@ -337,12 +333,12 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t DESTINATION_INDEX = 3; static const uint32_t STORAGE_INDEX = 4; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - if (!parsed_contract_to_string(get_value_buffer(DESTINATION_INDEX), VALUE_WIDTH, - &ops->operation.destination)) return false; - microtez_to_string(get_value_buffer(FEE_INDEX), ops->total_fee); - number_to_string(get_value_buffer(STORAGE_INDEX), ops->total_storage_limit); + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, + &ops->operation.source); + REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + &ops->operation.destination); + REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); static const char *const withdrawal_prompts[] = { PROMPT("Withdraw"), @@ -406,10 +402,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t FEE_INDEX = 2; static const uint32_t STORAGE_INDEX = 3; - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; - - microtez_to_string(get_value_buffer(FEE_INDEX), ops->total_fee); + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); // Parser function guarantees this has a reveal static const char *const reveal_prompts[] = { @@ -421,9 +415,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve }; SET_STATIC_UI_VALUE(TYPE_INDEX, "To Blockchain"); - number_to_string(get_value_buffer(STORAGE_INDEX), ops->total_storage_limit); - if (!parsed_contract_to_string(get_value_buffer(SOURCE_INDEX), VALUE_WIDTH, - &ops->operation.source)) return false; + REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); ui_prompt(reveal_prompts, NULL, ok, cxl); } diff --git a/src/operations.h b/src/operations.h index d1a4a9fa..f0a0e61d 100644 --- a/src/operations.h +++ b/src/operations.h @@ -19,7 +19,8 @@ struct parsed_contract { #define PROTOCOL_HASH_SIZE 32 struct parsed_proposal { - int32_t voting_period; + uint64_t voting_period; // must be 64 bit for function call reasons + // TODO: Make 32 bit version of number_to_string_indirect uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; }; diff --git a/src/to_string.c b/src/to_string.c index ab20b732..c244cc5c 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -10,6 +10,10 @@ static int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); +// These functions output terminating null bytes, and return the ending offset. +static size_t number_to_string(char *dest, uint64_t number); +static size_t microtez_to_string(char *dest, uint64_t number); + int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { if (contract->originated == 0 && contract->curve_code == TEZOS_NO_CURVE) { if (buff_size < sizeof(NO_CONTRACT_STRING)) return 0; diff --git a/src/to_string.h b/src/to_string.h index 8b2c1173..842029ba 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -15,11 +15,7 @@ int pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, size_t protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); - -// These functions output terminating null bytes, and return the ending offset. #define MAX_INT_DIGITS 20 -size_t number_to_string(char *dest, uint64_t number); -size_t microtez_to_string(char *dest, uint64_t number); // These take their number parameter through a pointer and take a length size_t number_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); diff --git a/src/ui.c b/src/ui.c index fd625f2b..04c95b5c 100644 --- a/src/ui.c +++ b/src/ui.c @@ -116,8 +116,8 @@ static void ui_idle(void) { #endif } -void change_idle_display(uint32_t new) { - number_to_string(idle_text, new); +void change_idle_display(uint64_t new) { + number_to_string_indirect(idle_text, sizeof(idle_text), &new); update_auth_text(); } diff --git a/src/ui.h b/src/ui.h index 9bbdeeda..d89fbb81 100644 --- a/src/ui.h +++ b/src/ui.h @@ -22,6 +22,6 @@ void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callbac uint32_t step_count); unsigned char io_event(unsigned char channel); void io_seproxyhal_display(const bagl_element_t *element); -void change_idle_display(uint32_t new); +void change_idle_display(uint64_t new); extern char baking_auth_text[PKH_STRING_SIZE]; // TODO: Is this the right name? diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 1ad5f909..4f3074af 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -1,16 +1,13 @@ #include "ui_prompt.h" #include "exception.h" +#include "to_string.h" #include // This will and must always be static memory full of constants static const char *const *prompts; -// Additional bytes (by OS) init'ed to null, and null they shall remain. -static char values[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; - // TODO: Get rid of +1 - static string_generation_callback callbacks[MAX_SCREEN_COUNT]; static const void *callback_data[MAX_SCREEN_COUNT]; @@ -69,23 +66,15 @@ static const bagl_element_t ui_multi_screen[] = { NULL}, }; -char *get_value_buffer(uint32_t which) { - if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); - return values[which]; -} - void switch_screen(uint32_t which) { if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); const char *label = (const char*)PIC(prompts[which]); // This will not overwrite terminating bytes strncpy(active_prompt, label, PROMPT_WIDTH); - if (callbacks[which] != NULL) { - if (!callbacks[which](active_value, sizeof(active_value), callback_data[which])) { - THROW(EXC_MEMORY_ERROR); - } - } else { - memcpy(active_value, values[which], sizeof(active_value)); + if (callbacks[which] == NULL) THROW(EXC_MEMORY_ERROR); + if (!callbacks[which](active_value, sizeof(active_value), callback_data[which])) { + THROW(EXC_MEMORY_ERROR); } } @@ -113,9 +102,7 @@ void ui_prompt(const char *const *labels, const char *const *data, callback_t ok // This will not overwrite terminating bytes if (data != NULL) { - const char *value = (const char *)PIC(data[i]); - if (strlen(value) > VALUE_WIDTH) THROW(EXC_MEMORY_ERROR); - strncpy(values[i], value, VALUE_WIDTH); + REGISTER_UI_CALLBACK(i, copy_string, data[i]); } } size_t screen_count = i; diff --git a/src/ui_prompt.h b/src/ui_prompt.h index b4ce95a8..859ae57e 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -22,15 +22,17 @@ // Displays labels (terminated with a NULL pointer) associated with data // labels must be completely static string constants // data may be dynamic -// Alternatively, if data is NULL, assume we've filled it in directly with get_value_buffer +// Alternatively, if data is NULL, assume we've registered appropriate callbacks to generate the data // All pointers may be unrelocated __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c); -char *get_value_buffer(uint32_t which); +// This is called by internal UI code to implement buffering void switch_screen(uint32_t which); +// This is called by internal UI code to prevent callbacks from sticking around +void clear_ui_callbacks(void); +// This function (and also macro) registers how a value is to be produced typedef bool (*string_generation_callback)(char *dest, size_t size, const void *data); void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); #define REGISTER_UI_CALLBACK(which, cb, data) register_ui_callback(which, (string_generation_callback)cb, data) -void clear_ui_callbacks(void); From f6c8203bbd5e6b013dd17875b2df9f1fc3403d05 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 17:44:04 -0500 Subject: [PATCH 12/92] clean up return types --- src/to_string.c | 28 +++++++++++++++------------- src/to_string.h | 10 +++++----- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/to_string.c b/src/to_string.c index c244cc5c..40987249 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -8,13 +8,13 @@ #define NO_CONTRACT_STRING "None" -static int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); +static size_t pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); // These functions output terminating null bytes, and return the ending offset. static size_t number_to_string(char *dest, uint64_t number); static size_t microtez_to_string(char *dest, uint64_t number); -int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { +bool parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { if (contract->originated == 0 && contract->curve_code == TEZOS_NO_CURVE) { if (buff_size < sizeof(NO_CONTRACT_STRING)) return 0; strcpy(buff, NO_CONTRACT_STRING); @@ -27,18 +27,18 @@ int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parse } else { curve = curve_code_to_curve(contract->curve_code); } - return pkh_to_string(buff, buff_size, curve, contract->hash); + return pkh_to_string(buff, buff_size, curve, contract->hash) <= buff_size; } -int pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, +bool pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, const cx_ecfp_public_key_t *public_key) { uint8_t hash[HASH_SIZE]; public_key_hash(hash, curve, public_key); - return pkh_to_string(buff, buff_size, curve, hash); + return pkh_to_string(buff, buff_size, curve, hash) <= buff_size; } // TODO: this should return size_t -int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]) { +size_t pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]) { if (buff_size < PKH_STRING_SIZE) THROW(EXC_WRONG_LENGTH); // Data to encode @@ -88,7 +88,7 @@ int pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, co return out_size; } -size_t protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]) { +bool protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]) { if (buff_size < PROTOCOL_HASH_BASE58_STRING_SIZE) THROW(EXC_WRONG_LENGTH); // Data to encode @@ -134,14 +134,16 @@ static inline size_t convert_number(char dest[MAX_INT_DIGITS], uint64_t number, return 0; } -size_t number_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { - if (buff_size < MAX_INT_DIGITS + 1) return 0; // terminating null - return number_to_string(dest, *number); +bool number_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) return false; // terminating null + number_to_string(dest, *number); + return true; } -size_t microtez_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { - if (buff_size < MAX_INT_DIGITS + 1) return 0; // + terminating null + decimal point - return microtez_to_string(dest, *number); +bool microtez_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) return false; // + terminating null + decimal point + microtez_to_string(dest, *number); + return true; } size_t number_to_string(char *dest, uint64_t number) { diff --git a/src/to_string.h b/src/to_string.h index 842029ba..dfd013e7 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -10,15 +10,15 @@ #include "cx.h" #include "ui.h" -int pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, +bool pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, const cx_ecfp_public_key_t *public_key); -size_t protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); -int parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); +bool protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); +bool parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); #define MAX_INT_DIGITS 20 // These take their number parameter through a pointer and take a length -size_t number_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); -size_t microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); +bool number_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); +bool microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); bool copy_string(char *dest, uint32_t buff_size, const char *src); From f1dd50c91904af48a09996360aa7a02885327f2b Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 3 Jan 2019 18:14:00 -0500 Subject: [PATCH 13/92] code cleanup --- src/apdu_baking.c | 2 +- src/apdu_sign.c | 59 ++++++++++++++++++++++++----------------------- src/memory.h | 9 ++++++++ src/operations.c | 3 ++- src/ui.c | 3 ++- src/ui_prompt.c | 5 ++-- src/ui_prompt.h | 3 +-- 7 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 src/memory.h diff --git a/src/apdu_baking.c b/src/apdu_baking.c index a305a9c4..91673471 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -28,7 +28,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { reset_level = lvl; - REGISTER_UI_CALLBACK(0, number_to_string_indirect, &reset_level); + register_ui_callback(0, number_to_string_indirect, &reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 8df28060..b56866f5 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -5,6 +5,7 @@ #include "base58.h" #include "blake2.h" #include "keys.h" +#include "memory.h" #include "protocol.h" #include "to_string.h" #include "ui_prompt.h" @@ -123,10 +124,10 @@ uint32_t baking_sign_complete(void) { if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); // Must be self-delegation signed by the same key - if (memcmp(&ops->operation.source, &ops->signing, sizeof(ops->signing))) { + if (COMPARE(&ops->operation.source, &ops->signing)) { THROW(EXC_PARSE_ERROR); } - if (memcmp(&ops->operation.destination, &ops->signing, sizeof(ops->signing))) { + if (COMPARE(&ops->operation.destination, &ops->signing)) { THROW(EXC_PARSE_ERROR); } @@ -150,7 +151,7 @@ const char *const insecure_values[] = { #define MAX_NUMBER_CHARS (MAX_INT_DIGITS + 2) // include decimal point and terminating null -#define SET_STATIC_UI_VALUE(index, str) REGISTER_UI_CALLBACK(index, copy_string, STATIC_UI_VALUE(str)) +#define SET_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) // Return false if the transaction isn't easily parseable, otherwise prompt with given callbacks // and do not return, but rather throw ASYNC. @@ -208,9 +209,9 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(PERIOD_INDEX, number_to_string_indirect, &ops->operation.proposal.voting_period); - REGISTER_UI_CALLBACK(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(PERIOD_INDEX, number_to_string_indirect, &ops->operation.proposal.voting_period); + register_ui_callback(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); SET_STATIC_UI_VALUE(TYPE_INDEX, "Proposal"); ui_prompt(proposal_prompts, NULL, ok, cxl); @@ -231,10 +232,10 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(PROTOCOL_HASH_INDEX, protocol_hash_to_string, + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.ballot.protocol_hash); - REGISTER_UI_CALLBACK(PERIOD_INDEX, number_to_string_indirect, &ops->operation.ballot.voting_period); + register_ui_callback(PERIOD_INDEX, number_to_string_indirect, &ops->operation.ballot.voting_period); switch (ops->operation.ballot.vote) { case BALLOT_VOTE_YEA: @@ -261,11 +262,11 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t DELEGATE_INDEX = 5; static const uint32_t STORAGE_INDEX = 6; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); - REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); static const char *const origination_prompts_fixed[] = { @@ -302,21 +303,21 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve if (!(ops->operation.flags & ORIGINATION_FLAG_SPENDABLE)) return false; SET_STATIC_UI_VALUE(TYPE_INDEX, "Origination"); - REGISTER_UI_CALLBACK(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); + register_ui_callback(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); const char *const *prompts; bool delegatable = ops->operation.flags & ORIGINATION_FLAG_DELEGATABLE; bool has_delegate = ops->operation.delegate.curve_code != TEZOS_NO_CURVE; if (delegatable && has_delegate) { prompts = origination_prompts_delegatable; - REGISTER_UI_CALLBACK(DELEGATE_INDEX, parsed_contract_to_string, + register_ui_callback(DELEGATE_INDEX, parsed_contract_to_string, &ops->operation.delegate); } else if (delegatable && !has_delegate) { prompts = origination_prompts_delegatable; SET_STATIC_UI_VALUE(DELEGATE_INDEX, "Any"); } else if (!delegatable && has_delegate) { prompts = origination_prompts_fixed; - REGISTER_UI_CALLBACK(DELEGATE_INDEX, parsed_contract_to_string, + register_ui_callback(DELEGATE_INDEX, parsed_contract_to_string, &ops->operation.delegate); } else if (!delegatable && !has_delegate) { prompts = origination_prompts_undelegatable; @@ -333,12 +334,12 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t DESTINATION_INDEX = 3; static const uint32_t STORAGE_INDEX = 4; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); - REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); static const char *const withdrawal_prompts[] = { PROMPT("Withdraw"), @@ -384,12 +385,12 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(DESTINATION_INDEX, parsed_contract_to_string, + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); - REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); - REGISTER_UI_CALLBACK(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); SET_STATIC_UI_VALUE(TYPE_INDEX, "Transaction"); @@ -402,8 +403,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve static const uint32_t FEE_INDEX = 2; static const uint32_t STORAGE_INDEX = 3; - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - REGISTER_UI_CALLBACK(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); // Parser function guarantees this has a reveal static const char *const reveal_prompts[] = { @@ -415,8 +416,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve }; SET_STATIC_UI_VALUE(TYPE_INDEX, "To Blockchain"); - REGISTER_UI_CALLBACK(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); - REGISTER_UI_CALLBACK(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); ui_prompt(reveal_prompts, NULL, ok, cxl); } diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 00000000..42d3413e --- /dev/null +++ b/src/memory.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#define COMPARE(a, b) ({ \ + _Static_assert(sizeof(*a) == sizeof(*b), "Size mismatch in COMPARE"); \ + memcmp(a, b, sizeof(*a)); \ +}) +#define NUM_ELEMENTS(a) (sizeof(a)/sizeof(*a)) diff --git a/src/operations.c b/src/operations.c index 5484c194..6ab0b658 100644 --- a/src/operations.c +++ b/src/operations.c @@ -1,6 +1,7 @@ #include "operations.h" #include "apdu.h" +#include "memory.h" #include "ui.h" #include "to_string.h" @@ -204,7 +205,7 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, // If the source is an implicit contract,... if (out.operation.source.originated == 0) { // ... it had better match our key, otherwise why are we signing it? - if (memcmp(&out.operation.source, &out.signing, sizeof(out.signing))) return false; + if (COMPARE(&out.operation.source, &out.signing)) return false; } // OK, it passes muster. diff --git a/src/ui.c b/src/ui.c index 04c95b5c..2d0f03f5 100644 --- a/src/ui.c +++ b/src/ui.c @@ -5,6 +5,7 @@ #include "baking_auth.h" #include "keys.h" +#include "memory.h" #include "to_string.h" #include @@ -109,7 +110,7 @@ static bool do_nothing(void) { static void ui_idle(void) { #ifdef BAKING_APP update_auth_text(); - ui_display(ui_idle_screen, sizeof(ui_idle_screen)/sizeof(*ui_idle_screen), + ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), do_nothing, exit_app, 2); #else main_menu(); diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 4f3074af..e675a85e 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -1,6 +1,7 @@ #include "ui_prompt.h" #include "exception.h" +#include "memory.h" #include "to_string.h" #include @@ -102,12 +103,12 @@ void ui_prompt(const char *const *labels, const char *const *data, callback_t ok // This will not overwrite terminating bytes if (data != NULL) { - REGISTER_UI_CALLBACK(i, copy_string, data[i]); + register_ui_callback(i, copy_string, data[i]); } } size_t screen_count = i; - ui_display(ui_multi_screen, sizeof(ui_multi_screen) / sizeof(*ui_multi_screen), + ui_display(ui_multi_screen, NUM_ELEMENTS(ui_multi_screen), ok_c, cxl_c, screen_count); THROW(ASYNC_EXCEPTION); } diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 859ae57e..0f280d11 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -33,6 +33,5 @@ void switch_screen(uint32_t which); void clear_ui_callbacks(void); // This function (and also macro) registers how a value is to be produced -typedef bool (*string_generation_callback)(char *dest, size_t size, const void *data); +typedef bool (*string_generation_callback)(); void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); -#define REGISTER_UI_CALLBACK(which, cb, data) register_ui_callback(which, (string_generation_callback)cb, data) From 7e6114f6c7e02936f7feb62113f1a433ff95ec90 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 4 Jan 2019 12:07:00 -0500 Subject: [PATCH 14/92] fix random app quitting after prompt --- src/ui.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui.c b/src/ui.c index 2d0f03f5..de2b5be8 100644 --- a/src/ui.c +++ b/src/ui.c @@ -225,7 +225,9 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { if (ux.callback_interval_ms == 0) { // prepare next screen ux_step = (ux_step + 1) % ux_step_count; - switch_screen(ux_step); + if (!is_idling) { + switch_screen(ux_step); + } // check if we've timed out if (ux_step == 0) { From c2a304098353439a0d41d766bf952d4b91fb2931 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 4 Jan 2019 12:26:58 -0500 Subject: [PATCH 15/92] actually fix bug --- src/ui.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ui.c b/src/ui.c index de2b5be8..12a72f1d 100644 --- a/src/ui.c +++ b/src/ui.c @@ -113,6 +113,7 @@ static void ui_idle(void) { ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), do_nothing, exit_app, 2); #else + cxl_callback = exit_app; main_menu(); #endif } @@ -190,10 +191,12 @@ void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callbac // Adapted from definition of UX_DISPLAY in header file timeout_cycle_count = 0; ux_step = 0; - switch_screen(0); ux_step_count = step_count; ok_callback = ok_c; cxl_callback = cxl_c; + if (!is_idling()) { + switch_screen(0); + } ux.elements = elems; ux.elements_count = sz; ux.button_push_handler = button_handler; @@ -225,7 +228,7 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { if (ux.callback_interval_ms == 0) { // prepare next screen ux_step = (ux_step + 1) % ux_step_count; - if (!is_idling) { + if (!is_idling()) { switch_screen(ux_step); } From e054baee5687ad3147dc8aae3a2a2647a363db35 Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Fri, 4 Jan 2019 17:03:57 -0500 Subject: [PATCH 16/92] clean up elliot comments --- src/apdu_baking.c | 7 +++---- src/apdu_sign.c | 15 +++++++++------ src/baking_auth.c | 9 ++------- src/operations.h | 4 ++-- src/to_string.c | 43 +++++++++++++++++++++---------------------- src/to_string.h | 19 ++++++++++++------- src/ui.c | 9 +++++---- src/ui.h | 4 ++-- src/ui_prompt.c | 4 +--- src/ui_prompt.h | 6 ++++-- 10 files changed, 61 insertions(+), 59 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 91673471..b196d365 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -9,8 +9,7 @@ #include -// Must be wider to be passed to number_to_string_indirect -static uint64_t reset_level; +static level_t reset_level; static bool reset_ok(void); @@ -28,7 +27,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { reset_level = lvl; - register_ui_callback(0, number_to_string_indirect, &reset_level); + register_ui_callback(0, number_to_string_indirect32, &reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), @@ -38,7 +37,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { } bool reset_ok(void) { - write_highest_level((level_t)reset_level, false); // We have not yet had an endorsement at this level + write_highest_level(reset_level, false); // We have not yet had an endorsement at this level uint32_t tx = 0; G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_sign.c b/src/apdu_sign.c index b56866f5..b4e4e17e 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -210,7 +210,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve }; register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); - register_ui_callback(PERIOD_INDEX, number_to_string_indirect, &ops->operation.proposal.voting_period); + register_ui_callback(PERIOD_INDEX, number_to_string_indirect32, &ops->operation.proposal.voting_period); register_ui_callback(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); SET_STATIC_UI_VALUE(TYPE_INDEX, "Proposal"); @@ -235,7 +235,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); register_ui_callback(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.ballot.protocol_hash); - register_ui_callback(PERIOD_INDEX, number_to_string_indirect, &ops->operation.ballot.voting_period); + register_ui_callback(PERIOD_INDEX, number_to_string_indirect32, &ops->operation.ballot.voting_period); switch (ops->operation.ballot.vote) { case BALLOT_VOTE_YEA: @@ -266,7 +266,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - register_ui_callback(STORAGE_INDEX, number_to_string_indirect, + register_ui_callback(STORAGE_INDEX, number_to_string_indirect64, &ops->total_storage_limit); static const char *const origination_prompts_fixed[] = { @@ -339,7 +339,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect64, + &ops->total_storage_limit); static const char *const withdrawal_prompts[] = { PROMPT("Withdraw"), @@ -389,7 +390,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve register_ui_callback(DESTINATION_INDEX, parsed_contract_to_string, &ops->operation.destination); register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &ops->total_fee); - register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect64, + &ops->total_storage_limit); register_ui_callback(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); SET_STATIC_UI_VALUE(TYPE_INDEX, "Transaction"); @@ -416,7 +418,8 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve }; SET_STATIC_UI_VALUE(TYPE_INDEX, "To Blockchain"); - register_ui_callback(STORAGE_INDEX, number_to_string_indirect, &ops->total_storage_limit); + register_ui_callback(STORAGE_INDEX, number_to_string_indirect64, + &ops->total_storage_limit); register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); ui_prompt(reveal_prompts, NULL, ok, cxl); diff --git a/src/baking_auth.c b/src/baking_auth.c index d2d7a401..af983a91 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -114,10 +114,7 @@ static const char *const baking_values[] = { // TODO: Unshare code with next function void prompt_contract_for_baking(struct parsed_contract *contract, callback_t ok_cb, callback_t cxl_cb) { - if (!parsed_contract_to_string(address_display_data, sizeof(address_display_data), contract)) { - THROW(EXC_WRONG_VALUES); - } - + parsed_contract_to_string(address_display_data, sizeof(address_display_data), contract); ui_prompt(get_baking_prompts(), baking_values, ok_cb, cxl_cb); } #endif @@ -129,9 +126,7 @@ void prompt_address( bool baking, cx_curve_t curve, const cx_ecfp_public_key_t *key, callback_t ok_cb, callback_t cxl_cb) { - if (!pubkey_to_pkh_string(address_display_data, sizeof(address_display_data), curve, key)) { - THROW(EXC_WRONG_VALUES); - } + pubkey_to_pkh_string(address_display_data, sizeof(address_display_data), curve, key); #ifdef BAKING_APP if (baking) { diff --git a/src/operations.h b/src/operations.h index f0a0e61d..bad4a626 100644 --- a/src/operations.h +++ b/src/operations.h @@ -19,7 +19,7 @@ struct parsed_contract { #define PROTOCOL_HASH_SIZE 32 struct parsed_proposal { - uint64_t voting_period; // must be 64 bit for function call reasons + uint32_t voting_period; // TODO: Make 32 bit version of number_to_string_indirect uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; }; @@ -31,7 +31,7 @@ enum ballot_vote { }; struct parsed_ballot { - int32_t voting_period; + uint32_t voting_period; uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; enum ballot_vote vote; }; diff --git a/src/to_string.c b/src/to_string.c index 40987249..f7122c21 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -8,17 +8,16 @@ #define NO_CONTRACT_STRING "None" -static size_t pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); +static void pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); // These functions output terminating null bytes, and return the ending offset. -static size_t number_to_string(char *dest, uint64_t number); static size_t microtez_to_string(char *dest, uint64_t number); -bool parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { +void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract) { if (contract->originated == 0 && contract->curve_code == TEZOS_NO_CURVE) { - if (buff_size < sizeof(NO_CONTRACT_STRING)) return 0; + if (buff_size < sizeof(NO_CONTRACT_STRING)) THROW(EXC_WRONG_LENGTH); strcpy(buff, NO_CONTRACT_STRING); - return buff_size; + return; } cx_curve_t curve; @@ -27,18 +26,18 @@ bool parsed_contract_to_string(char *buff, uint32_t buff_size, const struct pars } else { curve = curve_code_to_curve(contract->curve_code); } - return pkh_to_string(buff, buff_size, curve, contract->hash) <= buff_size; + pkh_to_string(buff, buff_size, curve, contract->hash); } -bool pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, +void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, const cx_ecfp_public_key_t *public_key) { uint8_t hash[HASH_SIZE]; public_key_hash(hash, curve, public_key); - return pkh_to_string(buff, buff_size, curve, hash) <= buff_size; + pkh_to_string(buff, buff_size, curve, hash); } -// TODO: this should return size_t -size_t pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]) { +void pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, + const uint8_t hash[HASH_SIZE]) { if (buff_size < PKH_STRING_SIZE) THROW(EXC_WRONG_LENGTH); // Data to encode @@ -85,10 +84,9 @@ size_t pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, size_t out_size = buff_size; if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); - return out_size; } -bool protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]) { +void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]) { if (buff_size < PROTOCOL_HASH_BASE58_STRING_SIZE) THROW(EXC_WRONG_LENGTH); // Data to encode @@ -114,7 +112,6 @@ bool protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t h size_t out_size = buff_size; if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); - return out_size; } // These functions do not output terminating null bytes. @@ -134,16 +131,19 @@ static inline size_t convert_number(char dest[MAX_INT_DIGITS], uint64_t number, return 0; } -bool number_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { - if (buff_size < MAX_INT_DIGITS + 1) return false; // terminating null +void number_to_string_indirect64(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) THROW(EXC_WRONG_LENGTH); // terminating null number_to_string(dest, *number); - return true; } -bool microtez_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { - if (buff_size < MAX_INT_DIGITS + 1) return false; // + terminating null + decimal point +void number_to_string_indirect32(char *dest, size_t buff_size, const uint32_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) THROW(EXC_WRONG_LENGTH); // terminating null + number_to_string(dest, *number); +} + +void microtez_to_string_indirect(char *dest, size_t buff_size, const uint64_t *number) { + if (buff_size < MAX_INT_DIGITS + 1) THROW(EXC_WRONG_LENGTH); // + terminating null + decimal point microtez_to_string(dest, *number); - return true; } size_t number_to_string(char *dest, uint64_t number) { @@ -190,10 +190,9 @@ size_t microtez_to_string(char *dest, uint64_t number) { return off; } -bool copy_string(char *dest, uint32_t buff_size, const char *src_in) { +void copy_string(char *dest, uint32_t buff_size, const char *src_in) { const char *src = (const char *)PIC(src_in); // I don't care that we will loop through the string twice, latency is not an issue - if (strlen(src) >= buff_size) return false; + if (strlen(src) >= buff_size) THROW(EXC_WRONG_LENGTH); strcpy(dest, src); - return true; } diff --git a/src/to_string.h b/src/to_string.h index dfd013e7..13bbe9b0 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -10,15 +10,20 @@ #include "cx.h" #include "ui.h" -bool pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, - const cx_ecfp_public_key_t *public_key); -bool protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); -bool parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); +void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, + const cx_ecfp_public_key_t *public_key); +void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); +void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); #define MAX_INT_DIGITS 20 +// dest must be at least MAX_INT_DIGITS +size_t number_to_string(char *dest, uint64_t number); // These take their number parameter through a pointer and take a length -bool number_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); -bool microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); +void number_to_string_indirect64(char *dest, uint32_t buff_size, const uint64_t *number); +void number_to_string_indirect32(char *dest, uint32_t buff_size, const uint32_t *number); +void microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t *number); -bool copy_string(char *dest, uint32_t buff_size, const char *src); +// This is designed to be called with potentially unrelocated pointers from rodata tables +// for the src argument, so performs PIC on src. +void copy_string(char *dest, uint32_t buff_size, const char *src); diff --git a/src/ui.c b/src/ui.c index 12a72f1d..8b09340d 100644 --- a/src/ui.c +++ b/src/ui.c @@ -118,8 +118,8 @@ static void ui_idle(void) { #endif } -void change_idle_display(uint64_t new) { - number_to_string_indirect(idle_text, sizeof(idle_text), &new); +void change_idle_display(uint32_t new) { + number_to_string(idle_text, new); update_auth_text(); } @@ -169,8 +169,9 @@ unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned b const bagl_element_t *prepro(const bagl_element_t *element) { if (element->component.userid == BAGL_STATIC_ELEMENT) return element; + static const uint32_t pause_millis = 1500; uint32_t min = 2000; - uint32_t div = 2; + static const uint32_t div = 2; if (is_idling()) { min = 4000; @@ -179,7 +180,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { if (ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { // timeouts are in millis UX_CALLBACK_SET_INTERVAL(MAX(min, - (1500 + bagl_label_roundtrip_duration_ms(element, 7)) / div)); + (pause_millis + bagl_label_roundtrip_duration_ms(element, 7)) / div)); return element; } else { return NULL; diff --git a/src/ui.h b/src/ui.h index d89fbb81..76c8e234 100644 --- a/src/ui.h +++ b/src/ui.h @@ -9,7 +9,7 @@ #define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte #define BAGL_STATIC_ELEMENT 0 -#define BAGL_SCROLLING_ELEMENT 100 // Arbitrary value +#define BAGL_SCROLLING_ELEMENT 100 // Arbitrary value chosen to connect data structures with prepro func typedef bool (*callback_t)(void); // return true to go back to idle screen @@ -22,6 +22,6 @@ void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callbac uint32_t step_count); unsigned char io_event(unsigned char channel); void io_seproxyhal_display(const bagl_element_t *element); -void change_idle_display(uint64_t new); +void change_idle_display(uint32_t new); extern char baking_auth_text[PKH_STRING_SIZE]; // TODO: Is this the right name? diff --git a/src/ui_prompt.c b/src/ui_prompt.c index e675a85e..1499c25a 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -74,9 +74,7 @@ void switch_screen(uint32_t which) { // This will not overwrite terminating bytes strncpy(active_prompt, label, PROMPT_WIDTH); if (callbacks[which] == NULL) THROW(EXC_MEMORY_ERROR); - if (!callbacks[which](active_value, sizeof(active_value), callback_data[which])) { - THROW(EXC_MEMORY_ERROR); - } + callbacks[which](active_value, sizeof(active_value), callback_data[which]); } void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data) { diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 0f280d11..8c9cd61d 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -32,6 +32,8 @@ void switch_screen(uint32_t which); // This is called by internal UI code to prevent callbacks from sticking around void clear_ui_callbacks(void); -// This function (and also macro) registers how a value is to be produced -typedef bool (*string_generation_callback)(); +// Uses K&R style declaration to avoid being stuck on const void *, to avoid having to cast the +// function pointers. +typedef void (*string_generation_callback)(/* char *buffer, size_t buffer_size, const void *data */); +// This function registers how a value is to be produced void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); From 5ec9189393cfa9f74073621daf3b647f36edb29c Mon Sep 17 00:00:00 2001 From: Jimmy Hartzell Date: Thu, 13 Dec 2018 15:38:44 -0500 Subject: [PATCH 17/92] comments and use of macros --- src/apdu.c | 4 +++- src/apdu_pubkey.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 226351fb..1cd47abc 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -36,13 +36,15 @@ void main_loop(apdu_handler handlers[INS_MAX]) { while (true) { BEGIN_TRY { TRY { + // Process APDU of size rx + if (rx == 0) { // no apdu received, well, reset the session, and reset the // bootloader configuration THROW(EXC_SECURITY); } - if (G_io_apdu_buffer[0] != CLA) { + if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { THROW(EXC_CLASS); } diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index ffeecda9..d147cd15 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -42,10 +42,11 @@ static bool baking_ok(void) { unsigned int handle_apdu_get_public_key(uint8_t instruction) { uint8_t *dataBuffer = G_io_apdu_buffer + OFFSET_CDATA; - if (G_io_apdu_buffer[OFFSET_P1] != 0) + if (G_io_apdu_buffer[OFFSET_P1] != 0) { THROW(EXC_WRONG_PARAM); + } - // do not expose pks without prompt over U2F + // do not expose pks without prompt over U2F (browser support) if (instruction == INS_GET_PUBLIC_KEY) { require_hid(); } From 7d4ca753bd06c66634c41e4e6dd3eafc5050872a Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 20:14:10 -0500 Subject: [PATCH 18/92] Update README for build instructions --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 32fba886..fa865b0f 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ $ export BOLOS_ENV=$PWD/bolos_env To build the Tezos Wallet app: ``` -$ env BAKING_APP= make +$ APP=tezos_wallet make $ mv bin/app.hex wallet.hex ``` If this results in an error message that includes this line (possibly repeatedly): @@ -267,17 +267,17 @@ Note that if you build *both* apps, you need to run `make clean` before building the second one. So, to build both apps run: ``` -$ env BAKING_APP= make +$ APP=tezos_wallet make $ mv bin/app.hex wallet.hex $ make clean -$ env BAKING_APP=Y make +$ APP=tezos_baking make $ mv bin/app.hex baking.hex ``` To build just the Tezos Baking App: ``` -$ env BAKING_APP=Y make +$ APP=tezos_baking make $ mv bin/app.hex baking.hex ``` From 9cc1f030ec0ad4feeb1d2f35f734912ad1f9d7ce Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 20:15:15 -0500 Subject: [PATCH 19/92] Add nix build support --- .gitignore | 2 +- default.nix | 43 ++++++++++++++++++++++++++++ nix/bolos-env.nix | 25 ++++++++++++++++ nix/dep/nanos-secure-sdk/default.nix | 7 +++++ nix/dep/nanos-secure-sdk/github.json | 7 +++++ nix/fhs.nix | 21 ++++++++++++++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 default.nix create mode 100644 nix/bolos-env.nix create mode 100644 nix/dep/nanos-secure-sdk/default.nix create mode 100644 nix/dep/nanos-secure-sdk/github.json create mode 100644 nix/fhs.nix diff --git a/.gitignore b/.gitignore index 4d441ddc..1030c38d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ bin debug -dep +/dep obj src/u2f_crypto_data.h src/glyphs.h diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..77b1c4cd --- /dev/null +++ b/default.nix @@ -0,0 +1,43 @@ +{ pkgs, ... }: +let + + fetchThunk = p: + if builtins.pathExists (p + /git.json) + then pkgs.fetchgit { inherit (builtins.fromJSON (builtins.readFile (p + /git.json))) url rev sha256; } + else if builtins.pathExists (p + /github.json) + then pkgs.fetchFromGitHub { inherit (builtins.fromJSON (builtins.readFile (p + /github.json))) owner repo rev sha256; } + else p; + + fhs = pkgs.callPackage nix/fhs.nix {}; + bolosEnv = pkgs.callPackage nix/bolos-env.nix {}; + bolosSdk = fetchThunk nix/dep/nanos-secure-sdk; + src = ./.; + + app = bakingApp: pkgs.runCommand "ledger-app-tezos-${if bakingApp then "baking" else "wallet"}" {} '' + set -Eeuo pipefail + + cp -a '${src}'/* . + chmod -R u+w . + + '${fhs}/bin/enter-fhs' < {}).fetchFromGitHub ( + let json = builtins.fromJSON (builtins.readFile ./github.json); + in { inherit (json) owner repo rev sha256; + private = json.private or false; + } +)) diff --git a/nix/dep/nanos-secure-sdk/github.json b/nix/dep/nanos-secure-sdk/github.json new file mode 100644 index 00000000..1878dd54 --- /dev/null +++ b/nix/dep/nanos-secure-sdk/github.json @@ -0,0 +1,7 @@ +{ + "owner": "LedgerHQ", + "repo": "nanos-secure-sdk", + "branch": "master", + "rev": "f9e1c7b8904df2eee0ae7e603f552b876c169334", + "sha256": "1wzra32zkqw521a5cmvqsblpkbkfzgx2pbngqpymcgf8db20p50i" +} diff --git a/nix/fhs.nix b/nix/fhs.nix new file mode 100644 index 00000000..a084e83a --- /dev/null +++ b/nix/fhs.nix @@ -0,0 +1,21 @@ +{ pkgs, ... }: +let + libtinfo5 = pkgs.runCommand "libtinfo5" {} '' + mkdir -p "$out/lib" + ln -s '${pkgs.ncurses5}/lib/libncursesw.so.5' "$out/lib/libtinfo.so.5" + ''; +in pkgs.buildFHSUserEnv { + name = "enter-fhs"; + + # TODO: Reduce this set to the minimal set + targetPkgs = pkgs: with pkgs; [ + alsaLib atk cairo cups dbus expat file fontconfig freetype gdb git glib + libnotify libxml2 libxslt + netcat nspr nss strace udev watch wget which xorg.libX11 + xorg.libXScrnSaver xorg.libXcomposite xorg.libXcursor xorg.libXdamage + xorg.libXext xorg.libXfixes xorg.libXi xorg.libXrandr xorg.libXrender + xorg.libXtst xorg.libxcb xorg.xcbutilkeysyms zlib zsh + gnumake libtinfo5 glibc_multi.dev (python2.withPackages (ps: [ps.pillow])) + ]; + runScript = "bash"; # "$SHELL"; +} From 50a04d6dba69e83e7d246d6b948f6ce4553e3cbc Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 23:32:31 -0500 Subject: [PATCH 20/92] Make VERSION_TAG and COMMIT nix-friendly --- Makefile | 14 +++++++++++--- default.nix | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f33fdf77..814b4237 100644 --- a/Makefile +++ b/Makefile @@ -30,17 +30,25 @@ else ifeq ($(APP),tezos_wallet) APPNAME = "Tezos Wallet" endif APP_LOAD_PARAMS=--appFlags 0 --curve ed25519 --curve secp256k1 --curve prime256r1 --path "44'/1729'" $(COMMON_LOAD_PARAMS) -VERSION_TAG=$(shell git describe --tags | cut -f1 -d-) +VERSION_TAG ?= $(shell git describe --tags 2>/dev/null | cut -f1 -d-) APPVERSION_M=1 APPVERSION_N=5 APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) -ifneq (v$(APPVERSION), $(VERSION_TAG)) +# Only warn about version tags if specified/inferred +ifeq ($(VERSION_TAG),) + $(warning VERSION_TAG not checked) +else + ifneq (v$(APPVERSION), $(VERSION_TAG)) $(warning "Version-Tag Mismatch: v$(APPVERSION) version and $(VERSION_TAG) tag") + endif endif -COMMIT := $(shell git describe --abbrev=8 --always) +COMMIT ?= $(shell git describe --abbrev=8 --always 2>/dev/null) +ifeq ($(COMMIT),) + $(error COMMIT not specified and could not be determined with git) +endif ICONNAME=icon.gif ################ diff --git a/default.nix b/default.nix index 77b1c4cd..7156d5d9 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -{ pkgs, ... }: +{ pkgs, commitHash, ... }: let fetchThunk = p: @@ -24,6 +24,7 @@ let export BOLOS_SDK='${bolosSdk}' export BOLOS_ENV='${bolosEnv}' export APP='${if bakingApp then "tezos_baking" else "tezos_wallet"}' + export COMMIT='${commitHash}' make clean make EOF From 0b9bdc5a334815bad449ffbc6487662771bae2a8 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 23:42:12 -0500 Subject: [PATCH 21/92] Add helper script for nix builds --- default.nix | 4 ++-- nix/build.sh | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100755 nix/build.sh diff --git a/default.nix b/default.nix index 7156d5d9..c3c8aed9 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -{ pkgs, commitHash, ... }: +{ pkgs ? import {}, commit, ... }: let fetchThunk = p: @@ -24,7 +24,7 @@ let export BOLOS_SDK='${bolosSdk}' export BOLOS_ENV='${bolosEnv}' export APP='${if bakingApp then "tezos_baking" else "tezos_wallet"}' - export COMMIT='${commitHash}' + export COMMIT='${commit}' make clean make EOF diff --git a/nix/build.sh b/nix/build.sh new file mode 100755 index 00000000..9e90926e --- /dev/null +++ b/nix/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# Override package set by passing --arg pkgs + +commit=$(git describe --abbrev=8 --always 2>/dev/null) +echo "Git commit: $commit" +nix-build --no-out-link --argstr commit "$commit" "$@" From 0bf7839057d79bfcb61c181f56dabd8a229b0d34 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 23:51:52 -0500 Subject: [PATCH 22/92] Report binary size in nix build --- default.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/default.nix b/default.nix index c3c8aed9..e9e5156b 100644 --- a/default.nix +++ b/default.nix @@ -32,6 +32,10 @@ let mkdir -p "$out" cp -R bin "$out" cp -R debug "$out" + + echo + echo ">>>> Application size: <<<<" + '${pkgs.binutils-unwrapped}/bin/size' "$out/bin/app.elf" ''; in rec { wallet = app false; From 9fc300bf67b84393be2a461df779feae453f334d Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 18 Jan 2019 00:05:52 -0500 Subject: [PATCH 23/92] Add ledgerblue shell for nix --- nix/ledgerblue.nix | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 nix/ledgerblue.nix diff --git a/nix/ledgerblue.nix b/nix/ledgerblue.nix new file mode 100644 index 00000000..430e58dc --- /dev/null +++ b/nix/ledgerblue.nix @@ -0,0 +1,7 @@ +{ pkgs ? import {}, ... }: +rec { + withLedgerblue = (pkgs.python36.withPackages (ps: with ps; [ + ecpy hidapi pycrypto python-u2flib-host requests ledgerblue pillow pkgs.hidapi protobuf + ])); + shell = withLedgerblue.env; +} From b28d62f933ddab7ca1fcc7bb06c92d6d222185ef Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 18 Jan 2019 00:36:21 -0500 Subject: [PATCH 24/92] Add install script for nix --- default.nix | 2 +- nix/build.sh | 4 ++-- nix/install.sh | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100755 nix/install.sh diff --git a/default.nix b/default.nix index e9e5156b..f8edda55 100644 --- a/default.nix +++ b/default.nix @@ -11,7 +11,7 @@ let fhs = pkgs.callPackage nix/fhs.nix {}; bolosEnv = pkgs.callPackage nix/bolos-env.nix {}; bolosSdk = fetchThunk nix/dep/nanos-secure-sdk; - src = ./.; + src = pkgs.lib.sources.cleanSource ./.; app = bakingApp: pkgs.runCommand "ledger-app-tezos-${if bakingApp then "baking" else "wallet"}" {} '' set -Eeuo pipefail diff --git a/nix/build.sh b/nix/build.sh index 9e90926e..5676e2c7 100755 --- a/nix/build.sh +++ b/nix/build.sh @@ -4,5 +4,5 @@ set -Eeuo pipefail # Override package set by passing --arg pkgs commit=$(git describe --abbrev=8 --always 2>/dev/null) -echo "Git commit: $commit" -nix-build --no-out-link --argstr commit "$commit" "$@" +echo >&2 "Git commit: $commit" +exec nix-build --no-out-link --argstr commit "$commit" "$@" diff --git a/nix/install.sh b/nix/install.sh new file mode 100755 index 00000000..a80ee37f --- /dev/null +++ b/nix/install.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail + +rootdir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) + +: "${VERSION:=${2:-"$(git -C "$rootdir" describe --tags | cut -f1 -d- | cut -f2 -dv)"}}" + +install-wallet() { + "$rootdir/install.sh" 'Tezos Wallet' "$("$rootdir/nix/build.sh" -A wallet)/bin/app.hex" "$VERSION" +} +install-baking() { + "$rootdir/install.sh" 'Tezos Baking' "$("$rootdir/nix/build.sh" -A baking)/bin/app.hex" "$VERSION" +} + +export rootdir +export VERSION +export -f install-wallet +export -f install-baking + +nix-shell "$rootdir/nix/ledgerblue.nix" -A shell --run "$(cat < Date: Fri, 18 Jan 2019 16:27:08 -0500 Subject: [PATCH 25/92] Remove old ledger-blue-shell.nix --- test/ledger-blue-shell.nix | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 test/ledger-blue-shell.nix diff --git a/test/ledger-blue-shell.nix b/test/ledger-blue-shell.nix deleted file mode 100644 index 7fb7c441..00000000 --- a/test/ledger-blue-shell.nix +++ /dev/null @@ -1,11 +0,0 @@ -let - # TODO: Upstream this change and use ../nixpkgs.nix as the pin. - pkgs = import ((import {}).fetchFromGitHub { - owner = "NixOS"; - repo = "nixpkgs"; - rev = "e3be0e49f082df2e653d364adf91b835224923d9"; - sha256 = "15xcs10fdc3lxvasd7cszd6a6vip1hs9r9r1qz2z0n9zkkfv3rrq"; - }) {}; -in pkgs.runCommand "ledger-blue-shell" { - buildInputs = with pkgs.python36Packages; [ ecpy hidapi pycrypto python-u2flib-host requests ledgerblue pillow pkgs.hidapi ]; -} "" From dcbe91dc07ee1a441b9c7f0fee8e0196070c3573 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Mon, 14 Jan 2019 20:23:01 -0500 Subject: [PATCH 26/92] Move most globals to globals.c --- src/apdu_baking.c | 4 +-- src/apdu_pubkey.c | 8 ++---- src/apdu_sign.c | 19 ++----------- src/baking_auth.c | 10 +++---- src/baking_auth.h | 14 ++------- src/globals.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++ src/globals.h | 51 +++++++++++++++++++++++++++++++++ src/keys.h | 1 - src/protocol.h | 3 +- src/types.h | 45 +++++++++++++++++++++++++++++ src/ui.c | 17 ++--------- src/ui.h | 10 ++----- src/ui_prompt.c | 8 ++---- src/ui_prompt.h | 23 ++------------- 14 files changed, 193 insertions(+), 92 deletions(-) create mode 100644 src/globals.c create mode 100644 src/globals.h create mode 100644 src/types.h diff --git a/src/apdu_baking.c b/src/apdu_baking.c index b196d365..7f613076 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -1,3 +1,5 @@ +#include "globals.h" + #include "apdu.h" #include "baking_auth.h" #include "apdu_baking.h" @@ -9,8 +11,6 @@ #include -static level_t reset_level; - static bool reset_ok(void); unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index d147cd15..39e27b0b 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -1,3 +1,5 @@ +#include "globals.h" + #include "apdu_pubkey.h" #include "apdu.h" @@ -9,10 +11,6 @@ #include -static cx_ecfp_public_key_t public_key; -static cx_curve_t curve; - - static int provide_pubkey(void) { int tx = 0; G_io_apdu_buffer[tx++] = public_key.W_len; @@ -75,7 +73,7 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { return provide_pubkey(); } else { // instruction == INS_PROMPT_PUBLIC_KEY || instruction == INS_AUTHORIZE_BAKING - callback_t cb; + ui_callback_t cb; bool bake; #ifdef BAKING_APP if (instruction == INS_AUTHORIZE_BAKING) { diff --git a/src/apdu_sign.c b/src/apdu_sign.c index b4e4e17e..48160281 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -1,3 +1,5 @@ +#include "globals.h" + #include "apdu_sign.h" #include "apdu.h" @@ -15,24 +17,9 @@ #include -// Where does this number come from? -#ifdef BAKING_APP -# define TEZOS_BUFSIZE 512 -#else -# define TEZOS_BUFSIZE 256 -#endif - #define SIGN_HASH_SIZE 32 #define B2B_BLOCKBYTES 128 -static uint8_t message_data[TEZOS_BUFSIZE]; -static uint32_t message_data_length; -static cx_curve_t curve; - -static bool is_hash_state_inited; -static uint8_t magic_number; -static bool hash_only; - static void conditional_init_hash_state(void) { if (!is_hash_state_inited) { b2b_init(&hash_state, SIGN_HASH_SIZE); @@ -157,7 +144,7 @@ const char *const insecure_values[] = { // and do not return, but rather throw ASYNC. static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve, size_t path_length, uint32_t *bip32_path, - callback_t ok, callback_t cxl) { + ui_callback_t ok, ui_callback_t cxl) { struct parsed_operation_group *ops; #ifndef TEZOS_DEBUG diff --git a/src/baking_auth.c b/src/baking_auth.c index af983a91..782fdd27 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -1,3 +1,4 @@ +#include "globals.h" #include "apdu.h" #include "baking_auth.h" #include "keys.h" @@ -10,9 +11,6 @@ #include -WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? -static nvram_data new_data; // Staging area for setting N_data - bool is_valid_level(level_t lvl) { return !(lvl & 0xC0000000); } @@ -113,7 +111,7 @@ static const char *const baking_values[] = { }; // TODO: Unshare code with next function -void prompt_contract_for_baking(struct parsed_contract *contract, callback_t ok_cb, callback_t cxl_cb) { +void prompt_contract_for_baking(struct parsed_contract *contract, ui_callback_t ok_cb, ui_callback_t cxl_cb) { parsed_contract_to_string(address_display_data, sizeof(address_display_data), contract); ui_prompt(get_baking_prompts(), baking_values, ok_cb, cxl_cb); } @@ -124,8 +122,8 @@ void prompt_address( __attribute__((unused)) #endif bool baking, - cx_curve_t curve, const cx_ecfp_public_key_t *key, callback_t ok_cb, - callback_t cxl_cb) { + cx_curve_t curve, const cx_ecfp_public_key_t *key, ui_callback_t ok_cb, + ui_callback_t cxl_cb) { pubkey_to_pkh_string(address_display_data, sizeof(address_display_data), curve, key); #ifdef BAKING_APP diff --git a/src/baking_auth.h b/src/baking_auth.h index 9f72856e..ae428045 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -7,16 +7,6 @@ #include "protocol.h" #include "operations.h" -#define MAX_BIP32_PATH 10 - -typedef struct { - cx_curve_t curve; - level_t highest_level; - bool had_endorsement; - uint8_t path_length; - uint32_t bip32_path[MAX_BIP32_PATH]; -} nvram_data; -extern WIDE nvram_data N_data_real; #define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t pathLength); @@ -29,10 +19,10 @@ bool is_level_authorized(level_t level, bool is_endorsement); bool is_valid_level(level_t level); void update_auth_text(void); -void prompt_contract_for_baking(struct parsed_contract *contract, callback_t ok_cb, callback_t cxl_cb); +void prompt_contract_for_baking(struct parsed_contract *contract, ui_callback_t ok_cb, ui_callback_t cxl_cb); void prompt_address(bool bake, cx_curve_t curve, const cx_ecfp_public_key_t *key, - callback_t ok_cb, callback_t cxl_cb) __attribute__((noreturn)); + ui_callback_t ok_cb, ui_callback_t cxl_cb) __attribute__((noreturn)); struct parsed_baking_data { bool is_endorsement; diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 00000000..d635014e --- /dev/null +++ b/src/globals.c @@ -0,0 +1,72 @@ +#include "globals.h" + +#include + +// The GLOBAL macro helps avoid any temptation to define an initialization value +// since they are not supported on the ledger. +#define GLOBAL(type, name) type name +#define CLEAR_VAR(name) memset(&name, 0, sizeof(name)) +#define CLEAR_ARRAY(name) memset(name, 0, sizeof(name)) + +GLOBAL(level_t, reset_level); + +GLOBAL(cx_ecfp_public_key_t, public_key); +GLOBAL(cx_curve_t, curve); + +GLOBAL(uint8_t, message_data[TEZOS_BUFSIZE]); +GLOBAL(uint32_t, message_data_length); +GLOBAL(cx_curve_t, curve); + +GLOBAL(bool, is_hash_state_inited); +GLOBAL(uint8_t, magic_number); +GLOBAL(bool, hash_only); + +// UI +GLOBAL(ux_state_t, ux); +GLOBAL(unsigned, char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]); + +GLOBAL(ui_callback_t, ok_callback); +GLOBAL(ui_callback_t, cxl_callback); + +GLOBAL(uint32_t, ux_step); +GLOBAL(uint32_t, ux_step_count); + +GLOBAL(uint32_t, timeout_cycle_count); + +GLOBAL(char, idle_text[16]); +GLOBAL(char, baking_auth_text[PKH_STRING_SIZE]); + +// UI Prompt +GLOBAL(string_generation_callback, callbacks[MAX_SCREEN_COUNT]); +GLOBAL(char, active_prompt[PROMPT_WIDTH + 1]); +GLOBAL(char, active_value[VALUE_WIDTH + 1]); + +// Baking Auth +GLOBAL(WIDE nvram_data, N_data_real); // TODO: What does WIDE actually mean? +GLOBAL(nvram_data, new_data); // Staging area for setting N_data + +void init_globals(void) { + CLEAR_VAR(reset_level); + CLEAR_VAR(public_key); + CLEAR_VAR(curve); + CLEAR_ARRAY(message_data); + CLEAR_VAR(message_data_length); + CLEAR_VAR(curve); + CLEAR_VAR(is_hash_state_inited); + CLEAR_VAR(magic_number); + CLEAR_VAR(hash_only); + CLEAR_VAR(ux); + CLEAR_ARRAY(G_io_seproxyhal_spi_buffer); + CLEAR_VAR(ok_callback); + CLEAR_VAR(cxl_callback); + CLEAR_VAR(ux_step); + CLEAR_VAR(ux_step_count); + CLEAR_VAR(timeout_cycle_count); + CLEAR_ARRAY(idle_text); + CLEAR_ARRAY(baking_auth_text); + CLEAR_ARRAY(callbacks); + CLEAR_ARRAY(active_prompt); + CLEAR_ARRAY(active_value); + CLEAR_VAR(N_data_real); + CLEAR_VAR(new_data); +} diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 00000000..0a07224d --- /dev/null +++ b/src/globals.h @@ -0,0 +1,51 @@ +#pragma once + +#include "types.h" + +#include + + +void init_globals(void); + +// Where does this number come from? +#ifdef BAKING_APP +# define TEZOS_BUFSIZE 512 +#else +# define TEZOS_BUFSIZE 256 +#endif + +extern level_t reset_level; +extern cx_ecfp_public_key_t public_key; +extern cx_curve_t curve; + +extern uint8_t message_data[TEZOS_BUFSIZE]; +extern uint32_t message_data_length; +extern cx_curve_t curve; + +extern bool is_hash_state_inited; +extern uint8_t magic_number; +extern bool hash_only; + +// UI +extern ux_state_t ux; +extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + +extern ui_callback_t ok_callback; +extern ui_callback_t cxl_callback; + +extern uint32_t ux_step; +extern uint32_t ux_step_count; + +extern uint32_t timeout_cycle_count; + +extern char idle_text[16]; +extern char baking_auth_text[PKH_STRING_SIZE]; + +// UI Prompt +extern string_generation_callback callbacks[MAX_SCREEN_COUNT]; +extern char active_prompt[PROMPT_WIDTH + 1]; +extern char active_value[VALUE_WIDTH + 1]; + +// Baking Auth +extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? +extern nvram_data new_data; // Staging area for setting N_data diff --git a/src/keys.h b/src/keys.h index 76e1ac2d..01ad61f3 100644 --- a/src/keys.h +++ b/src/keys.h @@ -26,7 +26,6 @@ struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_size, uint32_ // TODO: Rename to KEY_HASH_SIZE #define HASH_SIZE 20 -#define PKH_STRING_SIZE 40 cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, const cx_ecfp_public_key_t *public_key); diff --git a/src/protocol.h b/src/protocol.h index 82b9a5f5..2df05ab4 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -6,6 +6,7 @@ #include "os.h" #include "cx.h" +#include "types.h" #define MAGIC_BYTE_INVALID 0x00 #define MAGIC_BYTE_BLOCK 0x01 @@ -14,8 +15,6 @@ #define MAGIC_BYTE_UNSAFE_OP2 0x04 #define MAGIC_BYTE_UNSAFE_OP3 0x05 -typedef uint32_t level_t; - static inline uint8_t get_magic_byte(const uint8_t *data, size_t length) { if (data == NULL || length == 0) return MAGIC_BYTE_INVALID; else return *data; diff --git a/src/types.h b/src/types.h new file mode 100644 index 00000000..1f4aaea9 --- /dev/null +++ b/src/types.h @@ -0,0 +1,45 @@ +#pragma once + +#include "os.h" +#include "os_io_seproxyhal.h" + +#include + +typedef uint32_t level_t; + +// UI +typedef bool (*ui_callback_t)(void); // return true to go back to idle screen + +// Uses K&R style declaration to avoid being stuck on const void *, to avoid having to cast the +// function pointers. +typedef void (*string_generation_callback)(/* char *buffer, size_t buffer_size, const void *data */); + +// Baking Auth +#define MAX_BIP32_PATH 10 +typedef struct { + cx_curve_t curve; + level_t highest_level; + bool had_endorsement; + uint8_t path_length; + uint32_t bip32_path[MAX_BIP32_PATH]; +} nvram_data; + +#define PKH_STRING_SIZE 40 +#define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte + +#define MAX_SCREEN_COUNT 7 // Current maximum usage +#define PROMPT_WIDTH 16 +#define VALUE_WIDTH PROTOCOL_HASH_BASE58_STRING_SIZE + +// Macros to wrap a static prompt and value strings and ensure they aren't too long. +#define PROMPT(str) \ + ({ \ + _Static_assert(sizeof(str) <= PROMPT_WIDTH + 1/*null byte*/ , str " won't fit in the UI prompt."); \ + str; \ + }) + +#define STATIC_UI_VALUE(str) \ + ({ \ + _Static_assert(sizeof(str) <= VALUE_WIDTH + 1/*null byte*/, str " won't fit in the UI.".); \ + str; \ + }) diff --git a/src/ui.c b/src/ui.c index 8b09340d..9c5c01b4 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,3 +1,4 @@ +#include "globals.h" #include "ui.h" #include "ui_menu.h" @@ -11,21 +12,9 @@ #include #include -ux_state_t ux; -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -static callback_t ok_callback; -static callback_t cxl_callback; - static unsigned button_handler(unsigned button_mask, unsigned button_mask_counter); -static uint32_t ux_step, ux_step_count; - #define PROMPT_CYCLES 3 -static uint32_t timeout_cycle_count; - -static char idle_text[16]; -char baking_auth_text[PKH_STRING_SIZE]; void require_pin(void) { bolos_ux_params_t params; @@ -148,7 +137,7 @@ static void timeout(void) { } unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned button_mask_counter) { - callback_t callback; + ui_callback_t callback; switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: callback = cxl_callback; @@ -187,7 +176,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { } } -void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callback_t cxl_c, +void ui_display(const bagl_element_t *elems, size_t sz, ui_callback_t ok_c, ui_callback_t cxl_c, uint32_t step_count) { // Adapted from definition of UX_DISPLAY in header file timeout_cycle_count = 0; diff --git a/src/ui.h b/src/ui.h index 76c8e234..6cbb9a8d 100644 --- a/src/ui.h +++ b/src/ui.h @@ -2,26 +2,20 @@ #include "os_io_seproxyhal.h" -#include +#include "types.h" #include "keys.h" -#define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte - #define BAGL_STATIC_ELEMENT 0 #define BAGL_SCROLLING_ELEMENT 100 // Arbitrary value chosen to connect data structures with prepro func -typedef bool (*callback_t)(void); // return true to go back to idle screen - void ui_initial_screen(void); void ui_init(void); __attribute__((noreturn)) bool exit_app(void); // Might want to send it arguments to use as callback -void ui_display(const bagl_element_t *elems, size_t sz, callback_t ok_c, callback_t cxl_c, +void ui_display(const bagl_element_t *elems, size_t sz, ui_callback_t ok_c, ui_callback_t cxl_c, uint32_t step_count); unsigned char io_event(unsigned char channel); void io_seproxyhal_display(const bagl_element_t *element); void change_idle_display(uint32_t new); - -extern char baking_auth_text[PKH_STRING_SIZE]; // TODO: Is this the right name? diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 49751cb1..38c664f2 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -1,3 +1,5 @@ +#include "globals.h" + #include "ui_prompt.h" #include "exception.h" @@ -9,12 +11,8 @@ // This will and must always be static memory full of constants static const char *const *prompts; -static string_generation_callback callbacks[MAX_SCREEN_COUNT]; static const void *callback_data[MAX_SCREEN_COUNT]; -static char active_prompt[PROMPT_WIDTH + 1]; -static char active_value[VALUE_WIDTH + 1]; - static const bagl_element_t ui_multi_screen[] = { {{BAGL_RECTANGLE, BAGL_STATIC_ELEMENT, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, @@ -89,7 +87,7 @@ void clear_ui_callbacks(void) { } __attribute__((noreturn)) -void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c) { +void ui_prompt(const char *const *labels, const char *const *data, ui_callback_t ok_c, ui_callback_t cxl_c) { check_null(labels); prompts = labels; diff --git a/src/ui_prompt.h b/src/ui_prompt.h index 8c9cd61d..bc05e8a8 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -1,23 +1,7 @@ #pragma once #include "ui.h" - -#define MAX_SCREEN_COUNT 7 // Current maximum usage -#define PROMPT_WIDTH 16 -#define VALUE_WIDTH PROTOCOL_HASH_BASE58_STRING_SIZE - -// Macros to wrap a static prompt and value strings and ensure they aren't too long. -#define PROMPT(str) \ - ({ \ - _Static_assert(sizeof(str) <= PROMPT_WIDTH + 1/*null byte*/ , str " won't fit in the UI prompt."); \ - str; \ - }) - -#define STATIC_UI_VALUE(str) \ - ({ \ - _Static_assert(sizeof(str) <= VALUE_WIDTH + 1/*null byte*/, str " won't fit in the UI.".); \ - str; \ - }) +#include "types.h" // Displays labels (terminated with a NULL pointer) associated with data // labels must be completely static string constants @@ -25,15 +9,12 @@ // Alternatively, if data is NULL, assume we've registered appropriate callbacks to generate the data // All pointers may be unrelocated __attribute__((noreturn)) -void ui_prompt(const char *const *labels, const char *const *data, callback_t ok_c, callback_t cxl_c); +void ui_prompt(const char *const *labels, const char *const *data, ui_callback_t ok_c, ui_callback_t cxl_c); // This is called by internal UI code to implement buffering void switch_screen(uint32_t which); // This is called by internal UI code to prevent callbacks from sticking around void clear_ui_callbacks(void); -// Uses K&R style declaration to avoid being stuck on const void *, to avoid having to cast the -// function pointers. -typedef void (*string_generation_callback)(/* char *buffer, size_t buffer_size, const void *data */); // This function registers how a value is to be produced void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); From 70ab7928d440c0b539c402f390ea0a96efedc24e Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Tue, 15 Jan 2019 16:30:12 -0500 Subject: [PATCH 27/92] WIP: Use single struct for globals --- src/apdu.c | 12 +++----- src/apdu.h | 10 ++---- src/apdu_baking.c | 6 ++-- src/apdu_pubkey.c | 20 ++++++------ src/apdu_sign.c | 73 ++++++++++++++++++++++---------------------- src/baking_auth.c | 27 ++++++++-------- src/baking_auth.h | 3 +- src/globals.c | 67 ++-------------------------------------- src/globals.h | 78 +++++++++++++++++++++++++++++++---------------- src/keys.c | 5 +-- src/keys.h | 7 +---- src/main.c | 35 ++++++++++----------- src/types.h | 3 ++ src/ui.c | 36 +++++++++++----------- src/ui_prompt.c | 23 +++++++------- 15 files changed, 179 insertions(+), 226 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 1cd47abc..a08116f9 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -56,13 +56,11 @@ void main_loop(apdu_handler handlers[INS_MAX]) { } uint8_t instruction = G_io_apdu_buffer[OFFSET_INS]; - apdu_handler cb; - if (instruction >= INS_MAX) { - cb = handle_apdu_error; - } else { - cb = handlers[instruction]; - } - uint32_t tx = cb(instruction); + const apdu_handler cb = (instruction >= INS_MAX) + ? handle_apdu_error + : handlers[instruction]; + + const uint32_t tx = cb(instruction); rx = io_exchange(CHANNEL_APDU, tx); } CATCH(ASYNC_EXCEPTION) { diff --git a/src/apdu.h b/src/apdu.h index f38c06bf..1767a11c 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -1,6 +1,7 @@ #pragma once #include "exception.h" +#include "globals.h" #include "keys.h" #include "ui.h" @@ -22,10 +23,6 @@ #define INS_VERSION 0x00 #define INS_GIT 0x09 -#define INS_MAX 0x0B - -// Return number of bytes to transmit (tx) -typedef uint32_t (*apdu_handler)(uint8_t instruction); __attribute__((noreturn)) void main_loop(apdu_handler handlers[INS_MAX]); @@ -56,12 +53,9 @@ uint32_t handle_apdu_error(uint8_t instruction); uint32_t handle_apdu_version(uint8_t instruction); uint32_t handle_apdu_git(uint8_t instruction); -extern uint32_t app_stack_canary; - -extern void *stack_root; static inline void throw_stack_size() { uint8_t st; // uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; - uint32_t tmp2 = (uint32_t)stack_root - (uint32_t)&st; + uint32_t tmp2 = (uint32_t)global.stack_root - (uint32_t)&st; THROW(0x9000 + tmp2); } diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 7f613076..1907fa31 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -25,9 +25,9 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { THROW(EXC_PARSE_ERROR); } - reset_level = lvl; + global.baking.reset_level = lvl; - register_ui_callback(0, number_to_string_indirect32, &reset_level); + register_ui_callback(0, number_to_string_indirect32, &global.baking.reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), @@ -37,7 +37,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { } bool reset_ok(void) { - write_highest_level(reset_level, false); // We have not yet had an endorsement at this level + write_highest_level(global.baking.reset_level, false); // We have not yet had an endorsement at this level uint32_t tx = 0; G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 39e27b0b..5df1736a 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -13,11 +13,11 @@ static int provide_pubkey(void) { int tx = 0; - G_io_apdu_buffer[tx++] = public_key.W_len; + G_io_apdu_buffer[tx++] = global.pubkey.public_key.W_len; os_memmove(G_io_apdu_buffer + tx, - public_key.W, - public_key.W_len); - tx += public_key.W_len; + global.pubkey.public_key.W, + global.pubkey.public_key.W_len); + tx += global.pubkey.public_key.W_len; G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; return tx; @@ -31,7 +31,7 @@ static bool pubkey_ok(void) { #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(curve, bip32_path, bip32_path_length); + authorize_baking(global.baking.curve, bip32_path, bip32_path_length); pubkey_ok(); return true; } @@ -49,7 +49,7 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { require_hid(); } - curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + global.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); #ifdef BAKING_APP if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { @@ -58,16 +58,16 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { memcpy(bip32_path, N_data.bip32_path, sizeof(*bip32_path) * bip32_path_length); } else { #endif - bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], bip32_path, dataBuffer); + global.pubkey.bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], global.pubkey.bip32_path, dataBuffer); #ifdef BAKING_APP if (bip32_path_length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } } #endif - struct key_pair *pair = generate_key_pair(curve, bip32_path_length, bip32_path); + struct key_pair *pair = generate_key_pair(global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path); os_memset(&pair->private_key, 0, sizeof(pair->private_key)); - memcpy(&public_key, &pair->public_key, sizeof(public_key)); + memcpy(&global.pubkey.public_key, &pair->public_key, sizeof(global.pubkey.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { return provide_pubkey(); @@ -87,6 +87,6 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP } #endif - prompt_address(bake, curve, &public_key, cb, delay_reject); + prompt_address(bake, global.pubkey.curve, &global.pubkey.public_key, cb, delay_reject); } } diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 48160281..91a4e986 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -1,11 +1,10 @@ -#include "globals.h" - #include "apdu_sign.h" #include "apdu.h" #include "baking_auth.h" #include "base58.h" #include "blake2.h" +#include "globals.h" #include "keys.h" #include "memory.h" #include "protocol.h" @@ -21,31 +20,31 @@ #define B2B_BLOCKBYTES 128 static void conditional_init_hash_state(void) { - if (!is_hash_state_inited) { + if (!global.sign.is_hash_state_inited) { b2b_init(&hash_state, SIGN_HASH_SIZE); - is_hash_state_inited = true; + global.sign.is_hash_state_inited = true; } } static void hash_buffer(void) { - const uint8_t *current = message_data; - while (message_data_length > B2B_BLOCKBYTES) { + const uint8_t *current = global.sign.message_data; + while (global.sign.message_data_length > B2B_BLOCKBYTES) { conditional_init_hash_state(); b2b_update(&hash_state, current, B2B_BLOCKBYTES); - message_data_length -= B2B_BLOCKBYTES; + global.sign.message_data_length -= B2B_BLOCKBYTES; current += B2B_BLOCKBYTES; } // TODO use circular buffer at some point - memmove(message_data, current, message_data_length); + memmove(global.sign.message_data, current, global.sign.message_data_length); } static void finish_hashing(uint8_t *hash, size_t hash_size) { hash_buffer(); conditional_init_hash_state(); - b2b_update(&hash_state, message_data, message_data_length); + b2b_update(&hash_state, global.sign.message_data, global.sign.message_data_length); b2b_final(&hash_state, hash, hash_size); - message_data_length = 0; - is_hash_state_inited = false; + global.sign.message_data_length = 0; + global.sign.is_hash_state_inited = false; } static int perform_signature(bool hash_first); @@ -72,11 +71,11 @@ static bool sign_unsafe_ok(void) { #endif static void clear_data(void) { - bip32_path_length = 0; - message_data_length = 0; - is_hash_state_inited = false; - magic_number = 0; - hash_only = false; + global.pubkey.bip32_path_length = 0; + global.sign.message_data_length = 0; + global.sign.is_hash_state_inited = false; + global.sign.magic_number = 0; + global.sign.hash_only = false; } static bool sign_reject(void) { @@ -429,17 +428,18 @@ uint32_t wallet_sign_complete(uint8_t instruction) { }; ui_prompt(prehashed_prompts, insecure_values, sign_unsafe_ok, sign_reject); } else { - switch (magic_number) { + switch (global.sign.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: default: THROW(EXC_PARSE_ERROR); case MAGIC_BYTE_UNSAFE_OP: - if (is_hash_state_inited) { + if (global.sign.is_hash_state_inited) { goto unsafe; } - if (!prompt_transaction(message_data, message_data_length, curve, - bip32_path_length, bip32_path, sign_ok, sign_reject)) { + if (!prompt_transaction( + global.sign.message_data, global.sign.message_data_length, global.pubkey.curve, + global.pubkey.bip32_path_length, global.pubkey.bip32_path, sign_ok, sign_reject)) { goto unsafe; } case MAGIC_BYTE_UNSAFE_OP2: @@ -466,19 +466,19 @@ unsigned int handle_apdu_sign(uint8_t instruction) { switch (p1 & ~P1_LAST_MARKER) { case P1_FIRST: clear_data(); - os_memset(message_data, 0, sizeof(message_data)); - message_data_length = 0; - bip32_path_length = read_bip32_path(dataLength, bip32_path, dataBuffer); - curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + os_memset(global.sign.message_data, 0, sizeof(global.sign.message_data)); + global.sign.message_data_length = 0; + global.pubkey.bip32_path_length = read_bip32_path(dataLength, global.pubkey.bip32_path, dataBuffer); + global.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); return_ok(); #ifndef BAKING_APP case P1_HASH_ONLY_NEXT: // This is a debugging Easter egg - hash_only = true; + global.sign.hash_only = true; // FALL THROUGH #endif case P1_NEXT: - if (bip32_path_length == 0) { + if (global.pubkey.bip32_path_length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } break; @@ -486,8 +486,8 @@ unsigned int handle_apdu_sign(uint8_t instruction) { THROW(EXC_WRONG_PARAM); } - if (instruction != INS_SIGN_UNSAFE && magic_number == 0) { - magic_number = get_magic_byte(dataBuffer, dataLength); + if (instruction != INS_SIGN_UNSAFE && global.sign.magic_number == 0) { + global.sign.magic_number = get_magic_byte(dataBuffer, dataLength); } #ifndef BAKING_APP @@ -496,12 +496,12 @@ unsigned int handle_apdu_sign(uint8_t instruction) { } #endif - if (message_data_length + dataLength > sizeof(message_data)) { + if (global.sign.message_data_length + dataLength > sizeof(global.sign.message_data)) { THROW(EXC_PARSE_ERROR); } - os_memmove(message_data + message_data_length, dataBuffer, dataLength); - message_data_length += dataLength; + os_memmove(global.sign.message_data + global.sign.message_data_length, dataBuffer, dataLength); + global.sign.message_data_length += dataLength; if (!last) { return_ok(); @@ -516,8 +516,8 @@ unsigned int handle_apdu_sign(uint8_t instruction) { static int perform_signature(bool hash_first) { uint8_t hash[SIGN_HASH_SIZE]; - uint8_t *data = message_data; - uint32_t datalen = message_data_length; + uint8_t *data = global.sign.message_data; + uint32_t datalen = global.sign.message_data_length; #ifdef BAKING_APP update_high_water_mark(message_data, message_data_length); @@ -530,7 +530,7 @@ static int perform_signature(bool hash_first) { datalen = SIGN_HASH_SIZE; #ifndef BAKING_APP - if (hash_only) { + if (global.sign.hash_only) { memcpy(G_io_apdu_buffer, data, datalen); uint32_t tx = datalen; @@ -541,10 +541,11 @@ static int perform_signature(bool hash_first) { #endif } - struct key_pair *pair = generate_key_pair(curve, bip32_path_length, bip32_path); + struct key_pair *pair = generate_key_pair( + global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path); uint32_t tx; - switch (curve) { + switch (global.pubkey.curve) { case CX_CURVE_Ed25519: { tx = cx_eddsa_sign(&pair->private_key, 0, diff --git a/src/baking_auth.c b/src/baking_auth.c index 782fdd27..a6c116f7 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -17,10 +17,10 @@ bool is_valid_level(level_t lvl) { void write_highest_level(level_t lvl, bool is_endorsement) { if (!is_valid_level(lvl)) return; - memcpy(&new_data, &N_data, sizeof(new_data)); - new_data.highest_level = lvl; - new_data.had_endorsement = is_endorsement; - nvm_write((void*)&N_data, &new_data, sizeof(N_data)); + memcpy(&global.baking_auth.new_data, &N_data, sizeof(global.baking_auth.new_data)); + global.baking_auth.new_data.highest_level = lvl; + global.baking_auth.new_data.had_endorsement = is_endorsement; + nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); change_idle_display(N_data.highest_level); } @@ -29,12 +29,12 @@ void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t path_lengt return; } - new_data.highest_level = N_data.highest_level; - new_data.had_endorsement = N_data.had_endorsement; - new_data.curve = curve; - memcpy(new_data.bip32_path, bip32_path, path_length * sizeof(*bip32_path)); - new_data.path_length = path_length; - nvm_write((void*)&N_data, &new_data, sizeof(N_data)); + global.baking_auth.new_data.highest_level = N_data.highest_level; + global.baking_auth.new_data.had_endorsement = N_data.had_endorsement; + global.baking_auth.new_data.curve = curve; + memcpy(global.baking_auth.new_data.bip32_path, bip32_path, path_length * sizeof(*bip32_path)); + global.baking_auth.new_data.path_length = path_length; + nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); change_idle_display(N_data.highest_level); } @@ -76,12 +76,13 @@ void update_high_water_mark(void *data, int datalen) { void update_auth_text(void) { if (N_data.path_length == 0) { - strcpy(baking_auth_text, "No Key Authorized"); + strcpy(global.ui.baking_auth_text, "No Key Authorized"); } else { struct key_pair *pair = generate_key_pair(N_data.curve, N_data.path_length, N_data.bip32_path); os_memset(&pair->private_key, 0, sizeof(pair->private_key)); - pubkey_to_pkh_string(baking_auth_text, sizeof(baking_auth_text), N_data.curve, - &pair->public_key); + pubkey_to_pkh_string( + global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), + N_data.curve, &pair->public_key); } } diff --git a/src/baking_auth.h b/src/baking_auth.h index ae428045..8900511f 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -4,10 +4,11 @@ #include #include "apdu.h" +#include "globals.h" #include "protocol.h" #include "operations.h" -#define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) +#define N_data (*(WIDE nvram_data*)PIC(&global.baking_auth.N_data_real)) void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t pathLength); void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, uint32_t *bip32_path, diff --git a/src/globals.c b/src/globals.c index d635014e..af1283e4 100644 --- a/src/globals.c +++ b/src/globals.c @@ -2,71 +2,8 @@ #include -// The GLOBAL macro helps avoid any temptation to define an initialization value -// since they are not supported on the ledger. -#define GLOBAL(type, name) type name -#define CLEAR_VAR(name) memset(&name, 0, sizeof(name)) -#define CLEAR_ARRAY(name) memset(name, 0, sizeof(name)) - -GLOBAL(level_t, reset_level); - -GLOBAL(cx_ecfp_public_key_t, public_key); -GLOBAL(cx_curve_t, curve); - -GLOBAL(uint8_t, message_data[TEZOS_BUFSIZE]); -GLOBAL(uint32_t, message_data_length); -GLOBAL(cx_curve_t, curve); - -GLOBAL(bool, is_hash_state_inited); -GLOBAL(uint8_t, magic_number); -GLOBAL(bool, hash_only); - -// UI -GLOBAL(ux_state_t, ux); -GLOBAL(unsigned, char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]); - -GLOBAL(ui_callback_t, ok_callback); -GLOBAL(ui_callback_t, cxl_callback); - -GLOBAL(uint32_t, ux_step); -GLOBAL(uint32_t, ux_step_count); - -GLOBAL(uint32_t, timeout_cycle_count); - -GLOBAL(char, idle_text[16]); -GLOBAL(char, baking_auth_text[PKH_STRING_SIZE]); - -// UI Prompt -GLOBAL(string_generation_callback, callbacks[MAX_SCREEN_COUNT]); -GLOBAL(char, active_prompt[PROMPT_WIDTH + 1]); -GLOBAL(char, active_value[VALUE_WIDTH + 1]); - -// Baking Auth -GLOBAL(WIDE nvram_data, N_data_real); // TODO: What does WIDE actually mean? -GLOBAL(nvram_data, new_data); // Staging area for setting N_data +globals_t global; void init_globals(void) { - CLEAR_VAR(reset_level); - CLEAR_VAR(public_key); - CLEAR_VAR(curve); - CLEAR_ARRAY(message_data); - CLEAR_VAR(message_data_length); - CLEAR_VAR(curve); - CLEAR_VAR(is_hash_state_inited); - CLEAR_VAR(magic_number); - CLEAR_VAR(hash_only); - CLEAR_VAR(ux); - CLEAR_ARRAY(G_io_seproxyhal_spi_buffer); - CLEAR_VAR(ok_callback); - CLEAR_VAR(cxl_callback); - CLEAR_VAR(ux_step); - CLEAR_VAR(ux_step_count); - CLEAR_VAR(timeout_cycle_count); - CLEAR_ARRAY(idle_text); - CLEAR_ARRAY(baking_auth_text); - CLEAR_ARRAY(callbacks); - CLEAR_ARRAY(active_prompt); - CLEAR_ARRAY(active_value); - CLEAR_VAR(N_data_real); - CLEAR_VAR(new_data); + memset(&global, 0, sizeof(global)); } diff --git a/src/globals.h b/src/globals.h index 0a07224d..a0bb88d5 100644 --- a/src/globals.h +++ b/src/globals.h @@ -14,38 +14,64 @@ void init_globals(void); # define TEZOS_BUFSIZE 256 #endif -extern level_t reset_level; -extern cx_ecfp_public_key_t public_key; -extern cx_curve_t curve; +#define INS_MAX 0x0B -extern uint8_t message_data[TEZOS_BUFSIZE]; -extern uint32_t message_data_length; -extern cx_curve_t curve; +typedef struct { + void *stack_root; + apdu_handler handlers[INS_MAX]; -extern bool is_hash_state_inited; -extern uint8_t magic_number; -extern bool hash_only; + struct { + level_t reset_level; + } baking; -// UI -extern ux_state_t ux; -extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + struct { + cx_ecfp_public_key_t public_key; + cx_curve_t curve; -extern ui_callback_t ok_callback; -extern ui_callback_t cxl_callback; + // The following need to be persisted for baking app + uint8_t bip32_path_length; + uint32_t bip32_path[MAX_BIP32_PATH]; + } pubkey; -extern uint32_t ux_step; -extern uint32_t ux_step_count; + struct { + uint8_t message_data[TEZOS_BUFSIZE]; + uint32_t message_data_length; + cx_curve_t curve; -extern uint32_t timeout_cycle_count; + bool is_hash_state_inited; + uint8_t magic_number; + bool hash_only; + } sign; -extern char idle_text[16]; -extern char baking_auth_text[PKH_STRING_SIZE]; + struct { + ux_state_t ux; + unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; -// UI Prompt -extern string_generation_callback callbacks[MAX_SCREEN_COUNT]; -extern char active_prompt[PROMPT_WIDTH + 1]; -extern char active_value[VALUE_WIDTH + 1]; + ui_callback_t ok_callback; + ui_callback_t cxl_callback; -// Baking Auth -extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? -extern nvram_data new_data; // Staging area for setting N_data + uint32_t ux_step; + uint32_t ux_step_count; + + uint32_t timeout_cycle_count; + + char idle_text[16]; + char baking_auth_text[PKH_STRING_SIZE]; + + struct { + string_generation_callback callbacks[MAX_SCREEN_COUNT]; + const void *callback_data[MAX_SCREEN_COUNT]; + char active_prompt[PROMPT_WIDTH + 1]; + char active_value[VALUE_WIDTH + 1]; + } prompt; + } ui; + + struct { + WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? + nvram_data new_data; // Staging area for setting N_data + } baking_auth; +} globals_t; + +extern globals_t global; + +extern unsigned int app_stack_canary; // From SDK diff --git a/src/keys.c b/src/keys.c index 2b8342d3..a1b09252 100644 --- a/src/keys.c +++ b/src/keys.c @@ -25,10 +25,6 @@ #include #include -// The following need to be persisted for baking app -uint8_t bip32_path_length; -uint32_t bip32_path[MAX_BIP32_PATH]; - uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *buf) { uint32_t path_length = *buf; if (bytes < path_length * sizeof(uint32_t) + 1) THROW(EXC_WRONG_LENGTH_FOR_INS); @@ -72,6 +68,7 @@ struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_length, uint3 return &res; } +// TODO: Should we get rid of "restrict" here? It's not in header. cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, const cx_ecfp_public_key_t *restrict public_key) { static cx_ecfp_public_key_t compressed; diff --git a/src/keys.h b/src/keys.h index 01ad61f3..fd6382ec 100644 --- a/src/keys.h +++ b/src/keys.h @@ -7,12 +7,7 @@ #include "cx.h" #include "exception.h" - -#define MAX_BIP32_PATH 10 - -// The following need to be persisted for baking app -extern uint8_t bip32_path_length; -extern uint32_t bip32_path[MAX_BIP32_PATH]; +#include "globals.h" // Throws upon error uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *buf); diff --git a/src/main.c b/src/main.c index ef02a945..1f1f15ae 100644 --- a/src/main.c +++ b/src/main.c @@ -19,32 +19,33 @@ #include "apdu_pubkey.h" #include "apdu_sign.h" #include "apdu_baking.h" - -void *stack_root; +#include "globals.h" +#include "memory.h" __attribute__((noreturn)) void app_main(void) { - static apdu_handler handlers[INS_MAX]; uint8_t tag; - stack_root = &tag; + init_globals(); + global.stack_root = &tag; + // TODO: Consider using static initialization of a const, instead of this - for (size_t i = 0; i < INS_MAX; i++) { - handlers[i] = handle_apdu_error; + for (size_t i = 0; i < NUM_ELEMENTS(global.handlers); i++) { + global.handlers[i] = handle_apdu_error; } - handlers[INS_VERSION] = handle_apdu_version; - handlers[INS_GET_PUBLIC_KEY] = handle_apdu_get_public_key; - handlers[INS_PROMPT_PUBLIC_KEY] = handle_apdu_get_public_key; + global.handlers[INS_VERSION] = handle_apdu_version; + global.handlers[INS_GET_PUBLIC_KEY] = handle_apdu_get_public_key; + global.handlers[INS_PROMPT_PUBLIC_KEY] = handle_apdu_get_public_key; #ifdef BAKING_APP - handlers[INS_AUTHORIZE_BAKING] = handle_apdu_get_public_key; - handlers[INS_RESET] = handle_apdu_reset; - handlers[INS_QUERY_AUTH_KEY] = handle_apdu_query_auth_key; - handlers[INS_QUERY_HWM] = handle_apdu_hwm; + global.handlers[INS_AUTHORIZE_BAKING] = handle_apdu_get_public_key; + global.handlers[INS_RESET] = handle_apdu_reset; + global.handlers[INS_QUERY_AUTH_KEY] = handle_apdu_query_auth_key; + global.handlers[INS_QUERY_HWM] = handle_apdu_hwm; #endif - handlers[INS_SIGN] = handle_apdu_sign; + global.handlers[INS_SIGN] = handle_apdu_sign; #ifndef BAKING_APP - handlers[INS_SIGN_UNSAFE] = handle_apdu_sign; + global.handlers[INS_SIGN_UNSAFE] = handle_apdu_sign; #endif - handlers[INS_GIT] = handle_apdu_git; - main_loop(handlers); + global.handlers[INS_GIT] = handle_apdu_git; + main_loop(global.handlers); } diff --git a/src/types.h b/src/types.h index 1f4aaea9..751f5997 100644 --- a/src/types.h +++ b/src/types.h @@ -5,6 +5,9 @@ #include +// Return number of bytes to transmit (tx) +typedef uint32_t (*apdu_handler)(uint8_t instruction); + typedef uint32_t level_t; // UI diff --git a/src/ui.c b/src/ui.c index 9c5c01b4..a79997f1 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,10 +1,10 @@ -#include "globals.h" #include "ui.h" #include "ui_menu.h" #include "ui_prompt.h" #include "baking_auth.h" +#include "globals.h" #include "keys.h" #include "memory.h" #include "to_string.h" @@ -102,13 +102,13 @@ static void ui_idle(void) { ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), do_nothing, exit_app, 2); #else - cxl_callback = exit_app; + global.ui.cxl_callback = exit_app; main_menu(); #endif } void change_idle_display(uint32_t new) { - number_to_string(idle_text, new); + number_to_string(global.ui.idle_text, new); update_auth_text(); } @@ -121,14 +121,14 @@ void ui_initial_screen(void) { } static bool is_idling(void) { - return cxl_callback == exit_app; + return global.ui.cxl_callback == exit_app; } static void timeout(void) { if (is_idling()) { // Idle app timeout update_auth_text(); - timeout_cycle_count = 0; + global.ui.timeout_cycle_count = 0; UX_REDISPLAY(); } else { // Prompt timeout -- simulate cancel button @@ -140,10 +140,10 @@ unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned b ui_callback_t callback; switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: - callback = cxl_callback; + callback = global.ui.cxl_callback; break; case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - callback = ok_callback; + callback = global.ui.ok_callback; break; default: return 0; @@ -166,7 +166,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { min = 4000; } - if (ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { + if (global.ui.ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { // timeouts are in millis UX_CALLBACK_SET_INTERVAL(MAX(min, (pause_millis + bagl_label_roundtrip_duration_ms(element, 7)) / div)); @@ -179,11 +179,11 @@ const bagl_element_t *prepro(const bagl_element_t *element) { void ui_display(const bagl_element_t *elems, size_t sz, ui_callback_t ok_c, ui_callback_t cxl_c, uint32_t step_count) { // Adapted from definition of UX_DISPLAY in header file - timeout_cycle_count = 0; - ux_step = 0; - ux_step_count = step_count; - ok_callback = ok_c; - cxl_callback = cxl_c; + global.ui.timeout_cycle_count = 0; + global.ui.ux_step = 0; + global.ui.ux_step_count = step_count; + global.ui.ok_callback = ok_c; + global.ui.cxl_callback = cxl_c; if (!is_idling()) { switch_screen(0); } @@ -217,15 +217,15 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { ux.callback_interval_ms -= MIN(ux.callback_interval_ms, 100); if (ux.callback_interval_ms == 0) { // prepare next screen - ux_step = (ux_step + 1) % ux_step_count; + global.ui.ux_step = (global.ui.ux_step + 1) % global.ui.ux_step_count; if (!is_idling()) { - switch_screen(ux_step); + switch_screen(global.ui.ux_step); } // check if we've timed out - if (ux_step == 0) { - timeout_cycle_count++; - if (timeout_cycle_count == PROMPT_CYCLES) { + if (global.ui.ux_step == 0) { + global.ui.timeout_cycle_count++; + if (global.ui.timeout_cycle_count == PROMPT_CYCLES) { timeout(); break; // timeout() will often display a new screen } diff --git a/src/ui_prompt.c b/src/ui_prompt.c index 38c664f2..f2b85923 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -1,8 +1,7 @@ -#include "globals.h" - #include "ui_prompt.h" #include "exception.h" +#include "globals.h" #include "memory.h" #include "to_string.h" @@ -11,8 +10,6 @@ // This will and must always be static memory full of constants static const char *const *prompts; -static const void *callback_data[MAX_SCREEN_COUNT]; - static const bagl_element_t ui_multi_screen[] = { {{BAGL_RECTANGLE, BAGL_STATIC_ELEMENT, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, @@ -46,7 +43,7 @@ static const bagl_element_t ui_multi_screen[] = { {{BAGL_LABELINE, BAGL_STATIC_ELEMENT, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - active_prompt, + global.ui.prompt.active_prompt, 0, 0, 0, @@ -56,7 +53,7 @@ static const bagl_element_t ui_multi_screen[] = { {{BAGL_LABELINE, BAGL_SCROLLING_ELEMENT, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - active_value, + global.ui.prompt.active_value, 0, 0, 0, @@ -69,20 +66,22 @@ void switch_screen(uint32_t which) { if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); const char *label = (const char*)PIC(prompts[which]); - strncpy(active_prompt, label, sizeof(active_prompt)); - if (callbacks[which] == NULL) THROW(EXC_MEMORY_ERROR); - callbacks[which](active_value, sizeof(active_value), callback_data[which]); + strncpy(global.ui.prompt.active_prompt, label, sizeof(global.ui.prompt.active_prompt)); + if (global.ui.prompt.callbacks[which] == NULL) THROW(EXC_MEMORY_ERROR); + global.ui.prompt.callbacks[which]( + global.ui.prompt.active_value, sizeof(global.ui.prompt.active_value), + global.ui.prompt.callback_data[which]); } void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data) { if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); - callbacks[which] = cb; - callback_data[which] = data; + global.ui.prompt.callbacks[which] = cb; + global.ui.prompt.callback_data[which] = data; } void clear_ui_callbacks(void) { for (int i = 0; i < MAX_SCREEN_COUNT; ++i) { - callbacks[i] = NULL; + global.ui.prompt.callbacks[i] = NULL; } } From 12135ead770dcc9fb9cf1d1dcd1f676a6891a9ee Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Tue, 15 Jan 2019 17:02:17 -0500 Subject: [PATCH 28/92] Move some globals back --- src/globals.c | 5 +++++ src/globals.h | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/globals.c b/src/globals.c index af1283e4..119727af 100644 --- a/src/globals.c +++ b/src/globals.c @@ -4,6 +4,11 @@ globals_t global; +ux_state_t ux; +unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + void init_globals(void) { memset(&global, 0, sizeof(global)); + memset(&ux, 0, sizeof(ux)); + memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); } diff --git a/src/globals.h b/src/globals.h index a0bb88d5..cf50fa08 100644 --- a/src/globals.h +++ b/src/globals.h @@ -44,9 +44,6 @@ typedef struct { } sign; struct { - ux_state_t ux; - unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - ui_callback_t ok_callback; ui_callback_t cxl_callback; @@ -75,3 +72,7 @@ typedef struct { extern globals_t global; extern unsigned int app_stack_canary; // From SDK + +// Used by macros that we don't control. +extern ux_state_t ux; +extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; From 7609e3b6e84008a7f37b52f38aeb02ce2981ade3 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Tue, 15 Jan 2019 17:18:31 -0500 Subject: [PATCH 29/92] Fix baking app --- src/apdu_pubkey.c | 10 +++++----- src/apdu_sign.c | 17 ++++++++++------- src/ui.c | 4 ++-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 5df1736a..24b6dc3d 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -31,7 +31,7 @@ static bool pubkey_ok(void) { #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(global.baking.curve, bip32_path, bip32_path_length); + authorize_baking(global.pubkey.curve, global.pubkey.bip32_path, global.pubkey.bip32_path_length); pubkey_ok(); return true; } @@ -53,14 +53,14 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { - curve = N_data.curve; - bip32_path_length = N_data.path_length; - memcpy(bip32_path, N_data.bip32_path, sizeof(*bip32_path) * bip32_path_length); + global.pubkey.curve = N_data.curve; + global.pubkey.bip32_path_length = N_data.path_length; + memcpy(global.pubkey.bip32_path, N_data.bip32_path, sizeof(*global.pubkey.bip32_path) * global.pubkey.bip32_path_length); } else { #endif global.pubkey.bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], global.pubkey.bip32_path, dataBuffer); #ifdef BAKING_APP - if (bip32_path_length == 0) { + if (global.pubkey.bip32_path_length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } } diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 91a4e986..c5881e0f 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -51,7 +51,7 @@ static int perform_signature(bool hash_first); #ifdef BAKING_APP static bool bake_auth_ok(void) { - authorize_baking(curve, bip32_path, bip32_path_length); + authorize_baking(global.pubkey.curve, global.pubkey.bip32_path, global.pubkey.bip32_path_length); int tx = perform_signature(true); delayed_send(tx); return true; @@ -87,11 +87,12 @@ static bool sign_reject(void) { #ifdef BAKING_APP uint32_t baking_sign_complete(void) { // Have raw data, can get insight into it - switch (magic_number) { + switch (global.sign.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: - guard_baking_authorized(curve, message_data, message_data_length, - bip32_path, bip32_path_length); + guard_baking_authorized( + global.pubkey.curve, global.sign.message_data, global.sign.message_data_length, + global.pubkey.bip32_path, global.pubkey.bip32_path_length); return perform_signature(true); case MAGIC_BYTE_UNSAFE_OP: @@ -103,8 +104,10 @@ uint32_t baking_sign_complete(void) { allow_operation(&allowed, OPERATION_TAG_REVEAL); struct parsed_operation_group *ops = - parse_operations(message_data, message_data_length, curve, - bip32_path_length, bip32_path, allowed); + parse_operations( + global.sign.message_data, global.sign.message_data_length, + global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path, + allowed); // With < nickel fee if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); @@ -520,7 +523,7 @@ static int perform_signature(bool hash_first) { uint32_t datalen = global.sign.message_data_length; #ifdef BAKING_APP - update_high_water_mark(message_data, message_data_length); + update_high_water_mark(global.sign.message_data, global.sign.message_data_length); #endif if (hash_first) { diff --git a/src/ui.c b/src/ui.c index a79997f1..807882b6 100644 --- a/src/ui.c +++ b/src/ui.c @@ -62,7 +62,7 @@ const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - idle_text, + global.ui.idle_text, 0, 0, 0, @@ -82,7 +82,7 @@ const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - baking_auth_text, + global.ui.baking_auth_text, 0, 0, 0, From f7ce71ea79f4953fec577df31caafe72ea5e8960 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Tue, 15 Jan 2019 17:59:55 -0500 Subject: [PATCH 30/92] Pull in some lingering globals --- src/baking_auth.c | 12 ++++++------ src/blake2.h | 2 +- src/blake2b-ref.c | 2 +- src/globals.c | 7 +++++-- src/globals.h | 5 +++++ src/ui.c | 4 ++-- src/ui_prompt.c | 7 ++----- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/baking_auth.c b/src/baking_auth.c index a6c116f7..dfa299e6 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -86,11 +86,9 @@ void update_auth_text(void) { } } -static char address_display_data[VALUE_WIDTH]; - static const char *const pubkey_values[] = { "Public Key", - address_display_data, + global.baking_auth.address_display_data, NULL, }; @@ -107,13 +105,14 @@ static char const * const * get_baking_prompts() { static const char *const baking_values[] = { "With Public Key?", - address_display_data, + global.baking_auth.address_display_data, NULL, }; // TODO: Unshare code with next function void prompt_contract_for_baking(struct parsed_contract *contract, ui_callback_t ok_cb, ui_callback_t cxl_cb) { - parsed_contract_to_string(address_display_data, sizeof(address_display_data), contract); + parsed_contract_to_string( + global.baking_auth.address_display_data, sizeof(global.baking_auth.address_display_data), contract); ui_prompt(get_baking_prompts(), baking_values, ok_cb, cxl_cb); } #endif @@ -125,7 +124,8 @@ void prompt_address( bool baking, cx_curve_t curve, const cx_ecfp_public_key_t *key, ui_callback_t ok_cb, ui_callback_t cxl_cb) { - pubkey_to_pkh_string(address_display_data, sizeof(address_display_data), curve, key); + pubkey_to_pkh_string( + global.baking_auth.address_display_data, sizeof(global.baking_auth.address_display_data), curve, key); #ifdef BAKING_APP if (baking) { diff --git a/src/blake2.h b/src/blake2.h index 11cab5ec..a5793945 100644 --- a/src/blake2.h +++ b/src/blake2.h @@ -151,5 +151,5 @@ extern "C" { } #endif -extern b2b_state hash_state; +extern b2b_state hash_state; // IMPORTANT: NOT initted by init_globals. TODO: Use blake2b from SDK. #endif diff --git a/src/blake2b-ref.c b/src/blake2b-ref.c index ccf94d48..063e93e4 100644 --- a/src/blake2b-ref.c +++ b/src/blake2b-ref.c @@ -20,7 +20,7 @@ #include "blake2.h" #include "blake2-impl.h" -b2b_state hash_state; +b2b_state hash_state; // IMPORTANT: NOT initted by init_globals. static const uint64_t b2b_IV[8] = { diff --git a/src/globals.c b/src/globals.c index 119727af..edd71d56 100644 --- a/src/globals.c +++ b/src/globals.c @@ -4,11 +4,14 @@ globals_t global; +// These are strange variables that the SDK uses directly. ux_state_t ux; unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; void init_globals(void) { memset(&global, 0, sizeof(global)); - memset(&ux, 0, sizeof(ux)); - memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); + + // DO NOT INITIALIZE THESE. They are, apparently, initialized by the SDK. + //memset(&ux, 0, sizeof(ux)); + //memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); } diff --git a/src/globals.h b/src/globals.h index cf50fa08..06a300f4 100644 --- a/src/globals.h +++ b/src/globals.h @@ -60,12 +60,17 @@ typedef struct { const void *callback_data[MAX_SCREEN_COUNT]; char active_prompt[PROMPT_WIDTH + 1]; char active_value[VALUE_WIDTH + 1]; + + // This will and must always be static memory full of constants + const char *const *prompts; } prompt; } ui; struct { WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? nvram_data new_data; // Staging area for setting N_data + + char address_display_data[VALUE_WIDTH]; } baking_auth; } globals_t; diff --git a/src/ui.c b/src/ui.c index 807882b6..0326ed4e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -24,7 +24,7 @@ void require_pin(void) { } #ifdef BAKING_APP -const bagl_element_t ui_idle_screen[] = { +static const bagl_element_t ui_idle_screen[] = { // 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, @@ -136,7 +136,7 @@ static void timeout(void) { } } -unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned button_mask_counter) { +static unsigned button_handler(unsigned button_mask, __attribute__((unused)) unsigned button_mask_counter) { ui_callback_t callback; switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: diff --git a/src/ui_prompt.c b/src/ui_prompt.c index f2b85923..f148df64 100644 --- a/src/ui_prompt.c +++ b/src/ui_prompt.c @@ -7,9 +7,6 @@ #include -// This will and must always be static memory full of constants -static const char *const *prompts; - static const bagl_element_t ui_multi_screen[] = { {{BAGL_RECTANGLE, BAGL_STATIC_ELEMENT, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, @@ -64,7 +61,7 @@ static const bagl_element_t ui_multi_screen[] = { void switch_screen(uint32_t which) { if (which >= MAX_SCREEN_COUNT) THROW(EXC_MEMORY_ERROR); - const char *label = (const char*)PIC(prompts[which]); + const char *label = (const char*)PIC(global.ui.prompt.prompts[which]); strncpy(global.ui.prompt.active_prompt, label, sizeof(global.ui.prompt.active_prompt)); if (global.ui.prompt.callbacks[which] == NULL) THROW(EXC_MEMORY_ERROR); @@ -88,7 +85,7 @@ void clear_ui_callbacks(void) { __attribute__((noreturn)) void ui_prompt(const char *const *labels, const char *const *data, ui_callback_t ok_c, ui_callback_t cxl_c) { check_null(labels); - prompts = labels; + global.ui.prompt.prompts = labels; size_t i; for (i = 0; labels[i] != NULL; i++) { From fc106145eb7c36cd9ddf6af70256fde6fc2abc96 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 16 Jan 2019 17:18:38 -0500 Subject: [PATCH 31/92] Function-local statics moved to globals --- src/apdu.c | 2 +- src/apdu.h | 3 +- src/apdu_baking.c | 10 +++--- src/apdu_pubkey.c | 6 ++-- src/apdu_sign.c | 3 +- src/baking_auth.c | 8 +++-- src/baking_auth.h | 9 ++--- src/boot.c | 7 ++++ src/globals.c | 18 ++++++++-- src/globals.h | 22 +++++++++++- src/keys.c | 48 ++++++++++++++------------ src/keys.h | 12 ++----- src/main.c | 5 --- src/operations.c | 86 ++++++++++++++++++++++++----------------------- src/operations.h | 57 +------------------------------ src/types.h | 69 +++++++++++++++++++++++++++++++++++++ 16 files changed, 208 insertions(+), 157 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index a08116f9..17d59501 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -19,7 +19,7 @@ unsigned int handle_apdu_version(uint8_t __attribute__((unused)) instruction) { } unsigned int handle_apdu_git(uint8_t __attribute__((unused)) instruction) { - static char commit[] = COMMIT; + static const char commit[] = COMMIT; memcpy(G_io_apdu_buffer, commit, sizeof(commit)); uint32_t tx = sizeof(commit); diff --git a/src/apdu.h b/src/apdu.h index 1767a11c..385843af 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -3,8 +3,9 @@ #include "exception.h" #include "globals.h" #include "keys.h" - #include "ui.h" + +// Order matters #include "os.h" #include diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 1907fa31..9e9fde28 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -1,14 +1,16 @@ -#include "globals.h" +#include "apdu_baking.h" #include "apdu.h" #include "baking_auth.h" -#include "apdu_baking.h" -#include "cx.h" -#include "os.h" +#include "globals.h" #include "protocol.h" #include "to_string.h" #include "ui_prompt.h" +// Order matters +#include "cx.h" +#include "os.h" + #include static bool reset_ok(void); diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 24b6dc3d..318ba137 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -1,13 +1,13 @@ -#include "globals.h" - #include "apdu_pubkey.h" #include "apdu.h" #include "baking_auth.h" +#include "globals.h" #include "keys.h" +#include "ui.h" +// Order matters #include "cx.h" -#include "ui.h" #include diff --git a/src/apdu_sign.c b/src/apdu_sign.c index c5881e0f..dad71f34 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -9,10 +9,11 @@ #include "memory.h" #include "protocol.h" #include "to_string.h" +#include "ui.h" #include "ui_prompt.h" +// Order matters #include "cx.h" -#include "ui.h" #include diff --git a/src/baking_auth.c b/src/baking_auth.c index dfa299e6..0318c166 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -1,11 +1,13 @@ -#include "globals.h" -#include "apdu.h" #include "baking_auth.h" + +#include "apdu.h" +#include "globals.h" #include "keys.h" #include "protocol.h" -#include "ui_prompt.h" #include "to_string.h" +#include "ui_prompt.h" +// Order matters #include "os.h" #include "cx.h" diff --git a/src/baking_auth.h b/src/baking_auth.h index 8900511f..a3309804 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -1,14 +1,11 @@ #pragma once -#include -#include - #include "apdu.h" -#include "globals.h" -#include "protocol.h" #include "operations.h" +#include "protocol.h" -#define N_data (*(WIDE nvram_data*)PIC(&global.baking_auth.N_data_real)) +#include +#include void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t pathLength); void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, uint32_t *bip32_path, diff --git a/src/boot.c b/src/boot.c index 3eced2cb..53b5cafb 100644 --- a/src/boot.c +++ b/src/boot.c @@ -17,9 +17,12 @@ #include "ui.h" +// Order matters #include "os.h" #include "cx.h" +#include "globals.h" + __attribute__((noreturn)) void app_main(void); @@ -30,6 +33,10 @@ __attribute__((section(".boot"))) int main(void) { // ensure exception will work as planned os_boot(); + uint8_t tag; + init_globals(); + global.stack_root = &tag; + for (;;) { BEGIN_TRY { TRY { diff --git a/src/globals.c b/src/globals.c index edd71d56..51726455 100644 --- a/src/globals.c +++ b/src/globals.c @@ -2,16 +2,28 @@ #include +// WARNING: *************************************************** +// Non-const globals MUST NOT HAVE AN INITIALIZER. +// +// Providing an initializer will cause the application to crash +// if you write to it. +// ************************************************************ + + globals_t global; -// These are strange variables that the SDK uses directly. +// These are strange variables that the SDK relies on us to define but uses directly itself. ux_state_t ux; unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; +// DO NOT TRY TO ZERO THIS. This can only be written via an system call. +// The "N_" is *significant*. It tells the linker to put this in NVRAM. +WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? + void init_globals(void) { memset(&global, 0, sizeof(global)); // DO NOT INITIALIZE THESE. They are, apparently, initialized by the SDK. - //memset(&ux, 0, sizeof(ux)); - //memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); + memset(&ux, 0, sizeof(ux)); + memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); } diff --git a/src/globals.h b/src/globals.h index 06a300f4..ba5e8453 100644 --- a/src/globals.h +++ b/src/globals.h @@ -16,6 +16,11 @@ void init_globals(void); #define INS_MAX 0x0B +struct priv_generate_key_pair { + uint8_t privateKeyData[32]; + struct key_pair res; +}; + typedef struct { void *stack_root; apdu_handler handlers[INS_MAX]; @@ -67,11 +72,22 @@ typedef struct { } ui; struct { - WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? nvram_data new_data; // Staging area for setting N_data char address_display_data[VALUE_WIDTH]; } baking_auth; + + struct { + struct priv_generate_key_pair generate_key_pair; + + struct { + cx_ecfp_public_key_t compressed; + } public_key_hash; + + struct { + struct parsed_operation_group out; + } parse_operations; + } priv; } globals_t; extern globals_t global; @@ -81,3 +97,7 @@ extern unsigned int app_stack_canary; // From SDK // Used by macros that we don't control. extern ux_state_t ux; extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + +extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? + +#define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) diff --git a/src/keys.c b/src/keys.c index a1b09252..b6f8c8dc 100644 --- a/src/keys.c +++ b/src/keys.c @@ -18,10 +18,13 @@ #include "keys.h" #include "apdu.h" -#include "os.h" #include "blake2.h" +#include "globals.h" #include "protocol.h" +// Order matters +#include "os.h" + #include #include @@ -45,53 +48,56 @@ uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *bu } struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_length, uint32_t *bip32_path) { - static uint8_t privateKeyData[32]; - static struct key_pair res; + struct priv_generate_key_pair *const priv = &global.priv.generate_key_pair; + #if CX_APILEVEL > 8 if (curve == CX_CURVE_Ed25519) { - os_perso_derive_node_bip32_seed_key(HDW_ED25519_SLIP10, curve, bip32_path, path_length, - privateKeyData, NULL, NULL, 0); + os_perso_derive_node_bip32_seed_key( + HDW_ED25519_SLIP10, curve, bip32_path, path_length, + priv->privateKeyData, NULL, NULL, 0); } else { #endif - os_perso_derive_node_bip32(curve, bip32_path, path_length, privateKeyData, NULL); + os_perso_derive_node_bip32(curve, bip32_path, path_length, priv->privateKeyData, NULL); #if CX_APILEVEL > 8 } #endif - cx_ecfp_init_private_key(curve, privateKeyData, 32, &res.private_key); - cx_ecfp_generate_pair(curve, &res.public_key, &res.private_key, 1); + cx_ecfp_init_private_key(curve, priv->privateKeyData, 32, &priv->res.private_key); + cx_ecfp_generate_pair(curve, &priv->res.public_key, &priv->res.private_key, 1); if (curve == CX_CURVE_Ed25519) { - cx_edward_compress_point(curve, res.public_key.W, res.public_key.W_len); - res.public_key.W_len = 33; + cx_edward_compress_point( + curve, + priv->res.public_key.W, + priv->res.public_key.W_len); + priv->res.public_key.W_len = 33; } - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - return &res; + os_memset(priv->privateKeyData, 0, sizeof(priv->privateKeyData)); + return &priv->res; } -// TODO: Should we get rid of "restrict" here? It's not in header. cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, const cx_ecfp_public_key_t *restrict public_key) { - static cx_ecfp_public_key_t compressed; + cx_ecfp_public_key_t *const compressed = &global.priv.public_key_hash.compressed; switch (curve) { case CX_CURVE_Ed25519: { - compressed.W_len = public_key->W_len - 1; - memcpy(compressed.W, public_key->W + 1, compressed.W_len); + compressed->W_len = public_key->W_len - 1; + memcpy(compressed->W, public_key->W + 1, compressed->W_len); break; } case CX_CURVE_SECP256K1: case CX_CURVE_SECP256R1: { - memcpy(compressed.W, public_key->W, public_key->W_len); - compressed.W[0] = 0x02 + (public_key->W[64] & 0x01); - compressed.W_len = 33; + memcpy(compressed->W, public_key->W, public_key->W_len); + compressed->W[0] = 0x02 + (public_key->W[64] & 0x01); + compressed->W_len = 33; break; } default: THROW(EXC_WRONG_PARAM); } b2b_init(&hash_state, HASH_SIZE); - b2b_update(&hash_state, compressed.W, compressed.W_len); + b2b_update(&hash_state, compressed->W, compressed->W_len); b2b_final(&hash_state, output, HASH_SIZE); - return &compressed; + return compressed; } diff --git a/src/keys.h b/src/keys.h index fd6382ec..08a23f34 100644 --- a/src/keys.h +++ b/src/keys.h @@ -7,23 +7,15 @@ #include "cx.h" #include "exception.h" -#include "globals.h" +#include "types.h" // Throws upon error uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *buf); -struct key_pair { - cx_ecfp_public_key_t public_key; - cx_ecfp_private_key_t private_key; -}; - struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_size, uint32_t *bip32_path); -// TODO: Rename to KEY_HASH_SIZE -#define HASH_SIZE 20 - cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, - const cx_ecfp_public_key_t *public_key); + const cx_ecfp_public_key_t *restrict public_key); enum curve_code { TEZOS_ED, diff --git a/src/main.c b/src/main.c index 1f1f15ae..90707a7c 100644 --- a/src/main.c +++ b/src/main.c @@ -24,11 +24,6 @@ __attribute__((noreturn)) void app_main(void) { - uint8_t tag; - init_globals(); - global.stack_root = &tag; - - // TODO: Consider using static initialization of a const, instead of this for (size_t i = 0; i < NUM_ELEMENTS(global.handlers); i++) { global.handlers[i] = handle_apdu_error; diff --git a/src/operations.c b/src/operations.c index 6ab0b658..5544bbb7 100644 --- a/src/operations.c +++ b/src/operations.c @@ -1,9 +1,10 @@ #include "operations.h" #include "apdu.h" +#include "globals.h" #include "memory.h" -#include "ui.h" #include "to_string.h" +#include "ui.h" #include #include @@ -136,14 +137,15 @@ static inline void parse_contract(struct parsed_contract *out, const struct cont struct parsed_operation_group *parse_operations(const void *data, size_t length, cx_curve_t curve, size_t path_length, uint32_t *bip32_path, allowed_operation_set ops) { - static struct parsed_operation_group out; check_null(data); check_null(bip32_path); - memset(&out, 0, sizeof(out)); - out.operation.tag = OPERATION_TAG_NONE; + struct parsed_operation_group *const out = &global.priv.parse_operations.out; + memset(out, 0, sizeof(*out)); + + out->operation.tag = OPERATION_TAG_NONE; - compute_pkh(curve, path_length, bip32_path, &out); // sets up "signing" and "public_key" members + compute_pkh(curve, path_length, bip32_path, out); // sets up "signing" and "public_key" members size_t ix = 0; @@ -153,7 +155,7 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, // Start out with source = signing, for reveals // TODO: This is slightly hackish - memcpy(&out.operation.source, &out.signing, sizeof(out.signing)); + memcpy(&out->operation.source, &out->signing, sizeof(out->signing)); while (ix < length) { const enum operation_tag tag = NEXT_BYTE(data, &ix, length); // 1 byte is always aligned @@ -163,55 +165,55 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, if (tag == OPERATION_TAG_PROPOSAL || tag == OPERATION_TAG_BALLOT) { // These tags don't have the "originated" byte so we have to parse PKH differently. const struct implicit_contract *implicit_source = NEXT_TYPE(struct implicit_contract); - out.operation.source.originated = 0; - out.operation.source.curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &implicit_source->curve_code); - memcpy(out.operation.source.hash, implicit_source->pkh, sizeof(out.operation.source.hash)); + out->operation.source.originated = 0; + out->operation.source.curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &implicit_source->curve_code); + memcpy(out->operation.source.hash, implicit_source->pkh, sizeof(out->operation.source.hash)); } else { const struct contract *source = NEXT_TYPE(struct contract); - parse_contract(&out.operation.source, source); + parse_contract(&out->operation.source, source); - out.total_fee += PARSE_Z(data, &ix, length); // fee + out->total_fee += PARSE_Z(data, &ix, length); // fee PARSE_Z(data, &ix, length); // counter PARSE_Z(data, &ix, length); // gas limit - out.total_storage_limit += PARSE_Z(data, &ix, length); // storage limit + out->total_storage_limit += PARSE_Z(data, &ix, length); // storage limit } - // out.operation.source IS NORMALIZED AT THIS POINT + // out->operation.source IS NORMALIZED AT THIS POINT if (tag == OPERATION_TAG_REVEAL) { // Public key up next! Ensure it matches signing key. // Ignore source :-) and do not parse it from hdr. // We don't much care about reveals, they have very little in the way of bad security // implications and any fees have already been accounted for - if (NEXT_BYTE(data, &ix, length) != out.signing.curve_code) PARSE_ERROR(); + if (NEXT_BYTE(data, &ix, length) != out->signing.curve_code) PARSE_ERROR(); - size_t klen = out.public_key.W_len; + size_t klen = out->public_key.W_len; advance_ix(&ix, length, klen); - if (memcmp(out.public_key.W, data + ix - klen, klen) != 0) PARSE_ERROR(); + if (memcmp(out->public_key.W, data + ix - klen, klen) != 0) PARSE_ERROR(); - out.has_reveal = true; + out->has_reveal = true; continue; } - if (out.operation.tag != OPERATION_TAG_NONE) { + if (out->operation.tag != OPERATION_TAG_NONE) { // We are only currently allowing one non-reveal operation PARSE_ERROR(); } // This is the one allowable non-reveal operation per set - out.operation.tag = (uint8_t)tag; + out->operation.tag = (uint8_t)tag; // If the source is an implicit contract,... - if (out.operation.source.originated == 0) { + if (out->operation.source.originated == 0) { // ... it had better match our key, otherwise why are we signing it? - if (COMPARE(&out.operation.source, &out.signing)) return false; + if (COMPARE(&out->operation.source, &out->signing)) return false; } // OK, it passes muster. // This should by default be blanked out - out.operation.delegate.curve_code = TEZOS_NO_CURVE; - out.operation.delegate.originated = 0; + out->operation.delegate.curve_code = TEZOS_NO_CURVE; + out->operation.delegate.originated = 0; switch (tag) { case OPERATION_TAG_PROPOSAL: @@ -222,8 +224,8 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, const size_t payload_size = READ_UNALIGNED_BIG_ENDIAN(int32_t, &proposal_data->num_bytes); if (payload_size != PROTOCOL_HASH_SIZE) PARSE_ERROR(); // We only accept exactly 1 proposal hash. - out.operation.proposal.voting_period = READ_UNALIGNED_BIG_ENDIAN(int32_t, &proposal_data->period); - memcpy(out.operation.proposal.protocol_hash, proposal_data->hash, sizeof(out.operation.proposal.protocol_hash)); + out->operation.proposal.voting_period = READ_UNALIGNED_BIG_ENDIAN(int32_t, &proposal_data->period); + memcpy(out->operation.proposal.protocol_hash, proposal_data->hash, sizeof(out->operation.proposal.protocol_hash)); } break; case OPERATION_TAG_BALLOT: @@ -231,19 +233,19 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, const struct ballot_contents *ballot_data = NEXT_TYPE(struct ballot_contents); if (ix != length) PARSE_ERROR(); - out.operation.ballot.voting_period = READ_UNALIGNED_BIG_ENDIAN(int32_t, &ballot_data->period); - memcpy(out.operation.ballot.protocol_hash, ballot_data->proposal, sizeof(out.operation.ballot.protocol_hash)); + out->operation.ballot.voting_period = READ_UNALIGNED_BIG_ENDIAN(int32_t, &ballot_data->period); + memcpy(out->operation.ballot.protocol_hash, ballot_data->proposal, sizeof(out->operation.ballot.protocol_hash)); const int8_t ballot_vote = READ_UNALIGNED_BIG_ENDIAN(int8_t, &ballot_data->ballot); switch (ballot_vote) { case 0: - out.operation.ballot.vote = BALLOT_VOTE_YEA; + out->operation.ballot.vote = BALLOT_VOTE_YEA; break; case 1: - out.operation.ballot.vote = BALLOT_VOTE_NAY; + out->operation.ballot.vote = BALLOT_VOTE_NAY; break; case 2: - out.operation.ballot.vote = BALLOT_VOTE_PASS; + out->operation.ballot.vote = BALLOT_VOTE_PASS; break; default: PARSE_ERROR(); @@ -255,11 +257,11 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, uint8_t delegate_present = NEXT_BYTE(data, &ix, length); if (delegate_present) { const struct delegation_contents *dlg = NEXT_TYPE(struct delegation_contents); - parse_implicit(&out.operation.destination, dlg->curve_code, dlg->hash); + parse_implicit(&out->operation.destination, dlg->curve_code, dlg->hash); } else { // Encode "not present" - out.operation.destination.originated = 0; - out.operation.destination.curve_code = TEZOS_NO_CURVE; + out->operation.destination.originated = 0; + out->operation.destination.curve_code = TEZOS_NO_CURVE; } } break; @@ -271,28 +273,28 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, } __attribute__((packed)); const struct origination_header *hdr = NEXT_TYPE(struct origination_header); - parse_implicit(&out.operation.destination, hdr->curve_code, hdr->hash); - out.operation.amount = PARSE_Z(data, &ix, length); + parse_implicit(&out->operation.destination, hdr->curve_code, hdr->hash); + out->operation.amount = PARSE_Z(data, &ix, length); if (NEXT_BYTE(data, &ix, length) != 0) { - out.operation.flags |= ORIGINATION_FLAG_SPENDABLE; + out->operation.flags |= ORIGINATION_FLAG_SPENDABLE; } if (NEXT_BYTE(data, &ix, length) != 0) { - out.operation.flags |= ORIGINATION_FLAG_DELEGATABLE; + out->operation.flags |= ORIGINATION_FLAG_DELEGATABLE; } if (NEXT_BYTE(data, &ix, length) != 0) { // Has delegate const struct delegation_contents *dlg = NEXT_TYPE(struct delegation_contents); - parse_implicit(&out.operation.delegate, dlg->curve_code, dlg->hash); + parse_implicit(&out->operation.delegate, dlg->curve_code, dlg->hash); } if (NEXT_BYTE(data, &ix, length) != 0) PARSE_ERROR(); // Has script } break; case OPERATION_TAG_TRANSACTION: { - out.operation.amount = PARSE_Z(data, &ix, length); + out->operation.amount = PARSE_Z(data, &ix, length); const struct contract *destination = NEXT_TYPE(struct contract); - parse_contract(&out.operation.destination, destination); + parse_contract(&out->operation.destination, destination); uint8_t params = NEXT_BYTE(data, &ix, length); if (params) PARSE_ERROR(); // TODO: Support params @@ -303,9 +305,9 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, } } - if (out.operation.tag == OPERATION_TAG_NONE && !out.has_reveal) { + if (out->operation.tag == OPERATION_TAG_NONE && !out->has_reveal) { PARSE_ERROR(); // Must have at least one op } - return &out; // Success! + return out; // Success! } diff --git a/src/operations.h b/src/operations.h index bad4a626..e61e1f09 100644 --- a/src/operations.h +++ b/src/operations.h @@ -8,43 +8,7 @@ #include "protocol.h" #include "cx.h" - -struct parsed_contract { - uint8_t originated; - uint8_t curve_code; // TEZOS_NO_CURVE in originated case - // An implicit contract with TEZOS_NO_CURVE means not present - uint8_t hash[HASH_SIZE]; -}; - -#define PROTOCOL_HASH_SIZE 32 - -struct parsed_proposal { - uint32_t voting_period; - // TODO: Make 32 bit version of number_to_string_indirect - uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; -}; - -enum ballot_vote { - BALLOT_VOTE_YEA, - BALLOT_VOTE_NAY, - BALLOT_VOTE_PASS, -}; - -struct parsed_ballot { - uint32_t voting_period; - uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; - enum ballot_vote vote; -}; - -enum operation_tag { - OPERATION_TAG_NONE = -1, // Sentinal value, as 0 is possibly used for something - OPERATION_TAG_PROPOSAL = 5, - OPERATION_TAG_BALLOT = 6, - OPERATION_TAG_REVEAL = 7, - OPERATION_TAG_TRANSACTION = 8, - OPERATION_TAG_ORIGINATION = 9, - OPERATION_TAG_DELEGATION = 10, -}; +#include "types.h" typedef uint32_t allowed_operation_set; @@ -63,25 +27,6 @@ static inline void clear_operation_set(allowed_operation_set *ops) { #define ORIGINATION_FLAG_SPENDABLE 1 #define ORIGINATION_FLAG_DELEGATABLE 2 -struct parsed_operation { - enum operation_tag tag; - struct parsed_contract source; - struct parsed_contract destination; - struct parsed_contract delegate; // For originations only - struct parsed_proposal proposal; // For proposals only - struct parsed_ballot ballot; // For ballots only - uint64_t amount; // 0 where inappropriate - uint32_t flags; // Interpretation depends on operation type -}; - -struct parsed_operation_group { - cx_ecfp_public_key_t public_key; // compressed - uint64_t total_fee; - uint64_t total_storage_limit; - bool has_reveal; - struct parsed_contract signing; - struct parsed_operation operation; -}; // Throws upon invalid data. // Allows arbitrarily many "REVEAL" operations but only one operation of any other type, diff --git a/src/types.h b/src/types.h index 751f5997..fba82816 100644 --- a/src/types.h +++ b/src/types.h @@ -17,6 +17,12 @@ typedef bool (*ui_callback_t)(void); // return true to go back to idle screen // function pointers. typedef void (*string_generation_callback)(/* char *buffer, size_t buffer_size, const void *data */); +// Keys +struct key_pair { + cx_ecfp_public_key_t public_key; + cx_ecfp_private_key_t private_key; +}; + // Baking Auth #define MAX_BIP32_PATH 10 typedef struct { @@ -46,3 +52,66 @@ typedef struct { _Static_assert(sizeof(str) <= VALUE_WIDTH + 1/*null byte*/, str " won't fit in the UI.".); \ str; \ }) + + +// Operations +#define PROTOCOL_HASH_SIZE 32 + +// TODO: Rename to KEY_HASH_SIZE +#define HASH_SIZE 20 + +struct parsed_contract { + uint8_t originated; + uint8_t curve_code; // TEZOS_NO_CURVE in originated case + // An implicit contract with TEZOS_NO_CURVE means not present + uint8_t hash[HASH_SIZE]; +}; + +struct parsed_proposal { + uint32_t voting_period; + // TODO: Make 32 bit version of number_to_string_indirect + uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; +}; + +enum ballot_vote { + BALLOT_VOTE_YEA, + BALLOT_VOTE_NAY, + BALLOT_VOTE_PASS, +}; + +struct parsed_ballot { + uint32_t voting_period; + uint8_t protocol_hash[PROTOCOL_HASH_SIZE]; + enum ballot_vote vote; +}; + +enum operation_tag { + OPERATION_TAG_NONE = -1, // Sentinal value, as 0 is possibly used for something + OPERATION_TAG_PROPOSAL = 5, + OPERATION_TAG_BALLOT = 6, + OPERATION_TAG_REVEAL = 7, + OPERATION_TAG_TRANSACTION = 8, + OPERATION_TAG_ORIGINATION = 9, + OPERATION_TAG_DELEGATION = 10, +}; + + +struct parsed_operation { + enum operation_tag tag; + struct parsed_contract source; + struct parsed_contract destination; + struct parsed_contract delegate; // For originations only + struct parsed_proposal proposal; // For proposals only + struct parsed_ballot ballot; // For ballots only + uint64_t amount; // 0 where inappropriate + uint32_t flags; // Interpretation depends on operation type +}; + +struct parsed_operation_group { + cx_ecfp_public_key_t public_key; // compressed + uint64_t total_fee; + uint64_t total_storage_limit; + bool has_reveal; + struct parsed_contract signing; + struct parsed_operation operation; +}; From 2e025a5f7dda3d4d8c42a370028e7745431e5c77 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 16 Jan 2019 17:27:41 -0500 Subject: [PATCH 32/92] Fix comment --- src/globals.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/globals.c b/src/globals.c index 51726455..6cd6da16 100644 --- a/src/globals.c +++ b/src/globals.c @@ -16,14 +16,12 @@ globals_t global; ux_state_t ux; unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; -// DO NOT TRY TO ZERO THIS. This can only be written via an system call. +// DO NOT TRY TO INIT THIS. This can only be written via an system call. // The "N_" is *significant*. It tells the linker to put this in NVRAM. WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? void init_globals(void) { memset(&global, 0, sizeof(global)); - - // DO NOT INITIALIZE THESE. They are, apparently, initialized by the SDK. memset(&ux, 0, sizeof(ux)); memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); } From 7c18c487df3de6f6da2a547755322bd4d8f4642f Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 16 Jan 2019 17:55:12 -0500 Subject: [PATCH 33/92] Make size of privateKeyData clearer --- src/globals.h | 4 +++- src/keys.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/globals.h b/src/globals.h index ba5e8453..18840c5e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -16,8 +16,10 @@ void init_globals(void); #define INS_MAX 0x0B +#define PRIVATE_KEY_DATA_SIZE 32 + struct priv_generate_key_pair { - uint8_t privateKeyData[32]; + uint8_t privateKeyData[PRIVATE_KEY_DATA_SIZE]; struct key_pair res; }; diff --git a/src/keys.c b/src/keys.c index b6f8c8dc..d98f3a26 100644 --- a/src/keys.c +++ b/src/keys.c @@ -61,7 +61,7 @@ struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_length, uint3 #if CX_APILEVEL > 8 } #endif - cx_ecfp_init_private_key(curve, priv->privateKeyData, 32, &priv->res.private_key); + cx_ecfp_init_private_key(curve, priv->privateKeyData, sizeof(priv->privateKeyData), &priv->res.private_key); cx_ecfp_generate_pair(curve, &priv->res.public_key, &priv->res.private_key, 1); if (curve == CX_CURVE_Ed25519) { From 117d81b7a73a86666813bd76af738e381299de05 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 16 Jan 2019 18:03:10 -0500 Subject: [PATCH 34/92] Use memset instead of os_memset for consistency --- src/apdu_pubkey.c | 2 +- src/apdu_sign.c | 4 ++-- src/baking_auth.c | 2 +- src/keys.c | 2 +- src/operations.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 318ba137..b2fb161e 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -66,7 +66,7 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { } #endif struct key_pair *pair = generate_key_pair(global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path); - os_memset(&pair->private_key, 0, sizeof(pair->private_key)); + memset(&pair->private_key, 0, sizeof(pair->private_key)); memcpy(&global.pubkey.public_key, &pair->public_key, sizeof(global.pubkey.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { diff --git a/src/apdu_sign.c b/src/apdu_sign.c index dad71f34..22e244ba 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -470,7 +470,7 @@ unsigned int handle_apdu_sign(uint8_t instruction) { switch (p1 & ~P1_LAST_MARKER) { case P1_FIRST: clear_data(); - os_memset(global.sign.message_data, 0, sizeof(global.sign.message_data)); + memset(global.sign.message_data, 0, sizeof(global.sign.message_data)); global.sign.message_data_length = 0; global.pubkey.bip32_path_length = read_bip32_path(dataLength, global.pubkey.bip32_path, dataBuffer); global.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); @@ -584,7 +584,7 @@ static int perform_signature(bool hash_first) { THROW(EXC_WRONG_PARAM); // This should not be able to happen. } - os_memset(&pair->private_key, 0, sizeof(pair->private_key)); + memset(&pair->private_key, 0, sizeof(pair->private_key)); G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; diff --git a/src/baking_auth.c b/src/baking_auth.c index 0318c166..8709bc0e 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -81,7 +81,7 @@ void update_auth_text(void) { strcpy(global.ui.baking_auth_text, "No Key Authorized"); } else { struct key_pair *pair = generate_key_pair(N_data.curve, N_data.path_length, N_data.bip32_path); - os_memset(&pair->private_key, 0, sizeof(pair->private_key)); + memset(&pair->private_key, 0, sizeof(pair->private_key)); pubkey_to_pkh_string( global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), N_data.curve, &pair->public_key); diff --git a/src/keys.c b/src/keys.c index d98f3a26..1381e6a6 100644 --- a/src/keys.c +++ b/src/keys.c @@ -71,7 +71,7 @@ struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_length, uint3 priv->res.public_key.W_len); priv->res.public_key.W_len = 33; } - os_memset(priv->privateKeyData, 0, sizeof(priv->privateKeyData)); + memset(priv->privateKeyData, 0, sizeof(priv->privateKeyData)); return &priv->res; } diff --git a/src/operations.c b/src/operations.c index 5544bbb7..88b52223 100644 --- a/src/operations.c +++ b/src/operations.c @@ -108,7 +108,7 @@ static inline uint64_t parse_z(const void *data, size_t *ix, size_t length, uint static inline void compute_pkh(cx_curve_t curve, size_t path_length, uint32_t *bip32_path, struct parsed_operation_group *out) { struct key_pair *pair = generate_key_pair(curve, path_length, bip32_path); - os_memset(&pair->private_key, 0, sizeof(pair->private_key)); + memset(&pair->private_key, 0, sizeof(pair->private_key)); cx_ecfp_public_key_t *key = public_key_hash(out->signing.hash, curve, &pair->public_key); memcpy(&out->public_key, key, sizeof(out->public_key)); From d03887b672c50cb1740170ca5304d485b19c9151 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 17:56:46 -0500 Subject: [PATCH 35/92] Use union for APDU globals --- src/apdu_baking.c | 6 ++-- src/apdu_pubkey.c | 28 ++++++++-------- src/apdu_sign.c | 82 +++++++++++++++++++++++------------------------ src/globals.h | 43 +++++++++++++------------ src/operations.h | 3 -- src/types.h | 4 +++ 6 files changed, 85 insertions(+), 81 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 9e9fde28..b17e51bf 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -27,9 +27,9 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { THROW(EXC_PARSE_ERROR); } - global.baking.reset_level = lvl; + global.u.baking.reset_level = lvl; - register_ui_callback(0, number_to_string_indirect32, &global.baking.reset_level); + register_ui_callback(0, number_to_string_indirect32, &global.u.baking.reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), @@ -39,7 +39,7 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { } bool reset_ok(void) { - write_highest_level(global.baking.reset_level, false); // We have not yet had an endorsement at this level + write_highest_level(global.u.baking.reset_level, false); // We have not yet had an endorsement at this level uint32_t tx = 0; G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index b2fb161e..244f4440 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -13,11 +13,11 @@ static int provide_pubkey(void) { int tx = 0; - G_io_apdu_buffer[tx++] = global.pubkey.public_key.W_len; + G_io_apdu_buffer[tx++] = global.u.pubkey.public_key.W_len; os_memmove(G_io_apdu_buffer + tx, - global.pubkey.public_key.W, - global.pubkey.public_key.W_len); - tx += global.pubkey.public_key.W_len; + global.u.pubkey.public_key.W, + global.u.pubkey.public_key.W_len); + tx += global.u.pubkey.public_key.W_len; G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; return tx; @@ -31,7 +31,7 @@ static bool pubkey_ok(void) { #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(global.pubkey.curve, global.pubkey.bip32_path, global.pubkey.bip32_path_length); + authorize_baking(global.u.pubkey.curve, global.u.pubkey.bip32_path, global.u.pubkey.bip32_path_length); pubkey_ok(); return true; } @@ -49,25 +49,25 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { require_hid(); } - global.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + global.u.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); #ifdef BAKING_APP if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { - global.pubkey.curve = N_data.curve; - global.pubkey.bip32_path_length = N_data.path_length; - memcpy(global.pubkey.bip32_path, N_data.bip32_path, sizeof(*global.pubkey.bip32_path) * global.pubkey.bip32_path_length); + global.u.pubkey.curve = N_data.curve; + global.u.pubkey.bip32_path_length = N_data.path_length; + memcpy(global.u.pubkey.bip32_path, N_data.bip32_path, sizeof(*global.u.pubkey.bip32_path) * global.u.pubkey.bip32_path_length); } else { #endif - global.pubkey.bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], global.pubkey.bip32_path, dataBuffer); + global.u.pubkey.bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], global.u.pubkey.bip32_path, dataBuffer); #ifdef BAKING_APP - if (global.pubkey.bip32_path_length == 0) { + if (global.u.pubkey.bip32_path_length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } } #endif - struct key_pair *pair = generate_key_pair(global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path); + struct key_pair *pair = generate_key_pair(global.u.pubkey.curve, global.u.pubkey.bip32_path_length, global.u.pubkey.bip32_path); memset(&pair->private_key, 0, sizeof(pair->private_key)); - memcpy(&global.pubkey.public_key, &pair->public_key, sizeof(global.pubkey.public_key)); + memcpy(&global.u.pubkey.public_key, &pair->public_key, sizeof(global.u.pubkey.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { return provide_pubkey(); @@ -87,6 +87,6 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP } #endif - prompt_address(bake, global.pubkey.curve, &global.pubkey.public_key, cb, delay_reject); + prompt_address(bake, global.u.pubkey.curve, &global.u.pubkey.public_key, cb, delay_reject); } } diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 22e244ba..e5db8e34 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -21,38 +21,38 @@ #define B2B_BLOCKBYTES 128 static void conditional_init_hash_state(void) { - if (!global.sign.is_hash_state_inited) { + if (!global.u.sign.is_hash_state_inited) { b2b_init(&hash_state, SIGN_HASH_SIZE); - global.sign.is_hash_state_inited = true; + global.u.sign.is_hash_state_inited = true; } } static void hash_buffer(void) { - const uint8_t *current = global.sign.message_data; - while (global.sign.message_data_length > B2B_BLOCKBYTES) { + const uint8_t *current = global.u.sign.message_data; + while (global.u.sign.message_data_length > B2B_BLOCKBYTES) { conditional_init_hash_state(); b2b_update(&hash_state, current, B2B_BLOCKBYTES); - global.sign.message_data_length -= B2B_BLOCKBYTES; + global.u.sign.message_data_length -= B2B_BLOCKBYTES; current += B2B_BLOCKBYTES; } // TODO use circular buffer at some point - memmove(global.sign.message_data, current, global.sign.message_data_length); + memmove(global.u.sign.message_data, current, global.u.sign.message_data_length); } static void finish_hashing(uint8_t *hash, size_t hash_size) { hash_buffer(); conditional_init_hash_state(); - b2b_update(&hash_state, global.sign.message_data, global.sign.message_data_length); + b2b_update(&hash_state, global.u.sign.message_data, global.u.sign.message_data_length); b2b_final(&hash_state, hash, hash_size); - global.sign.message_data_length = 0; - global.sign.is_hash_state_inited = false; + global.u.sign.message_data_length = 0; + global.u.sign.is_hash_state_inited = false; } static int perform_signature(bool hash_first); #ifdef BAKING_APP static bool bake_auth_ok(void) { - authorize_baking(global.pubkey.curve, global.pubkey.bip32_path, global.pubkey.bip32_path_length); + authorize_baking(global.u.sign.curve, global.u.sign.bip32_path, global.u.sign.bip32_path_length); int tx = perform_signature(true); delayed_send(tx); return true; @@ -72,11 +72,11 @@ static bool sign_unsafe_ok(void) { #endif static void clear_data(void) { - global.pubkey.bip32_path_length = 0; - global.sign.message_data_length = 0; - global.sign.is_hash_state_inited = false; - global.sign.magic_number = 0; - global.sign.hash_only = false; + global.u.sign.bip32_path_length = 0; + global.u.sign.message_data_length = 0; + global.u.sign.is_hash_state_inited = false; + global.u.sign.magic_number = 0; + global.u.sign.hash_only = false; } static bool sign_reject(void) { @@ -88,12 +88,12 @@ static bool sign_reject(void) { #ifdef BAKING_APP uint32_t baking_sign_complete(void) { // Have raw data, can get insight into it - switch (global.sign.magic_number) { + switch (global.u.sign.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: guard_baking_authorized( - global.pubkey.curve, global.sign.message_data, global.sign.message_data_length, - global.pubkey.bip32_path, global.pubkey.bip32_path_length); + global.u.sign.curve, global.u.sign.message_data, global.u.sign.message_data_length, + global.u.sign.bip32_path, global.u.sign.bip32_path_length); return perform_signature(true); case MAGIC_BYTE_UNSAFE_OP: @@ -106,8 +106,8 @@ uint32_t baking_sign_complete(void) { struct parsed_operation_group *ops = parse_operations( - global.sign.message_data, global.sign.message_data_length, - global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path, + global.u.sign.message_data, global.u.sign.message_data_length, + global.u.sign.curve, global.u.sign.bip32_path_length, global.u.sign.bip32_path, allowed); // With < nickel fee @@ -432,18 +432,18 @@ uint32_t wallet_sign_complete(uint8_t instruction) { }; ui_prompt(prehashed_prompts, insecure_values, sign_unsafe_ok, sign_reject); } else { - switch (global.sign.magic_number) { + switch (global.u.sign.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: default: THROW(EXC_PARSE_ERROR); case MAGIC_BYTE_UNSAFE_OP: - if (global.sign.is_hash_state_inited) { + if (global.u.sign.is_hash_state_inited) { goto unsafe; } if (!prompt_transaction( - global.sign.message_data, global.sign.message_data_length, global.pubkey.curve, - global.pubkey.bip32_path_length, global.pubkey.bip32_path, sign_ok, sign_reject)) { + global.u.sign.message_data, global.u.sign.message_data_length, global.u.sign.curve, + global.u.sign.bip32_path_length, global.u.sign.bip32_path, sign_ok, sign_reject)) { goto unsafe; } case MAGIC_BYTE_UNSAFE_OP2: @@ -470,19 +470,19 @@ unsigned int handle_apdu_sign(uint8_t instruction) { switch (p1 & ~P1_LAST_MARKER) { case P1_FIRST: clear_data(); - memset(global.sign.message_data, 0, sizeof(global.sign.message_data)); - global.sign.message_data_length = 0; - global.pubkey.bip32_path_length = read_bip32_path(dataLength, global.pubkey.bip32_path, dataBuffer); - global.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + memset(global.u.sign.message_data, 0, sizeof(global.u.sign.message_data)); + global.u.sign.message_data_length = 0; + global.u.sign.bip32_path_length = read_bip32_path(dataLength, global.u.sign.bip32_path, dataBuffer); + global.u.sign.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); return_ok(); #ifndef BAKING_APP case P1_HASH_ONLY_NEXT: // This is a debugging Easter egg - global.sign.hash_only = true; + global.u.sign.hash_only = true; // FALL THROUGH #endif case P1_NEXT: - if (global.pubkey.bip32_path_length == 0) { + if (global.u.sign.bip32_path_length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } break; @@ -490,8 +490,8 @@ unsigned int handle_apdu_sign(uint8_t instruction) { THROW(EXC_WRONG_PARAM); } - if (instruction != INS_SIGN_UNSAFE && global.sign.magic_number == 0) { - global.sign.magic_number = get_magic_byte(dataBuffer, dataLength); + if (instruction != INS_SIGN_UNSAFE && global.u.sign.magic_number == 0) { + global.u.sign.magic_number = get_magic_byte(dataBuffer, dataLength); } #ifndef BAKING_APP @@ -500,12 +500,12 @@ unsigned int handle_apdu_sign(uint8_t instruction) { } #endif - if (global.sign.message_data_length + dataLength > sizeof(global.sign.message_data)) { + if (global.u.sign.message_data_length + dataLength > sizeof(global.u.sign.message_data)) { THROW(EXC_PARSE_ERROR); } - os_memmove(global.sign.message_data + global.sign.message_data_length, dataBuffer, dataLength); - global.sign.message_data_length += dataLength; + os_memmove(global.u.sign.message_data + global.u.sign.message_data_length, dataBuffer, dataLength); + global.u.sign.message_data_length += dataLength; if (!last) { return_ok(); @@ -520,11 +520,11 @@ unsigned int handle_apdu_sign(uint8_t instruction) { static int perform_signature(bool hash_first) { uint8_t hash[SIGN_HASH_SIZE]; - uint8_t *data = global.sign.message_data; - uint32_t datalen = global.sign.message_data_length; + uint8_t *data = global.u.sign.message_data; + uint32_t datalen = global.u.sign.message_data_length; #ifdef BAKING_APP - update_high_water_mark(global.sign.message_data, global.sign.message_data_length); + update_high_water_mark(global.u.sign.message_data, global.u.sign.message_data_length); #endif if (hash_first) { @@ -534,7 +534,7 @@ static int perform_signature(bool hash_first) { datalen = SIGN_HASH_SIZE; #ifndef BAKING_APP - if (global.sign.hash_only) { + if (global.u.sign.hash_only) { memcpy(G_io_apdu_buffer, data, datalen); uint32_t tx = datalen; @@ -546,10 +546,10 @@ static int perform_signature(bool hash_first) { } struct key_pair *pair = generate_key_pair( - global.pubkey.curve, global.pubkey.bip32_path_length, global.pubkey.bip32_path); + global.u.sign.curve, global.u.sign.bip32_path_length, global.u.sign.bip32_path); uint32_t tx; - switch (global.pubkey.curve) { + switch (global.u.sign.curve) { case CX_CURVE_Ed25519: { tx = cx_eddsa_sign(&pair->private_key, 0, diff --git a/src/globals.h b/src/globals.h index 18840c5e..fef1019e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -27,28 +27,31 @@ typedef struct { void *stack_root; apdu_handler handlers[INS_MAX]; - struct { - level_t reset_level; - } baking; - - struct { - cx_ecfp_public_key_t public_key; - cx_curve_t curve; + union { + struct { + level_t reset_level; + } baking; - // The following need to be persisted for baking app - uint8_t bip32_path_length; - uint32_t bip32_path[MAX_BIP32_PATH]; - } pubkey; + struct { + cx_ecfp_public_key_t public_key; + uint8_t bip32_path_length; + uint32_t bip32_path[MAX_BIP32_PATH]; + cx_curve_t curve; + } pubkey; - struct { - uint8_t message_data[TEZOS_BUFSIZE]; - uint32_t message_data_length; - cx_curve_t curve; - - bool is_hash_state_inited; - uint8_t magic_number; - bool hash_only; - } sign; + struct { + uint8_t bip32_path_length; + uint32_t bip32_path[MAX_BIP32_PATH]; + cx_curve_t curve; + + uint8_t message_data[TEZOS_BUFSIZE]; + uint32_t message_data_length; + + bool is_hash_state_inited; + uint8_t magic_number; + bool hash_only; + } sign; + } u; struct { ui_callback_t ok_callback; diff --git a/src/operations.h b/src/operations.h index e61e1f09..4092e60b 100644 --- a/src/operations.h +++ b/src/operations.h @@ -24,9 +24,6 @@ static inline void clear_operation_set(allowed_operation_set *ops) { *ops = 0; } -#define ORIGINATION_FLAG_SPENDABLE 1 -#define ORIGINATION_FLAG_DELEGATABLE 2 - // Throws upon invalid data. // Allows arbitrarily many "REVEAL" operations but only one operation of any other type, diff --git a/src/types.h b/src/types.h index fba82816..ba3b8a43 100644 --- a/src/types.h +++ b/src/types.h @@ -95,6 +95,10 @@ enum operation_tag { OPERATION_TAG_DELEGATION = 10, }; +// TODO: Make this an enum. +// Flags for parsed_operation.flag +#define ORIGINATION_FLAG_SPENDABLE 1 +#define ORIGINATION_FLAG_DELEGATABLE 2 struct parsed_operation { enum operation_tag tag; From 803ad208fb3866072cd059ee8897b5f41ded04cd Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 17 Jan 2019 18:01:55 -0500 Subject: [PATCH 36/92] Move blake2b hash_state to globals --- src/apdu_sign.c | 8 ++++---- src/blake2.h | 1 - src/blake2b-ref.c | 2 -- src/globals.h | 5 +++++ src/keys.c | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index e5db8e34..d5347b37 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -22,7 +22,7 @@ static void conditional_init_hash_state(void) { if (!global.u.sign.is_hash_state_inited) { - b2b_init(&hash_state, SIGN_HASH_SIZE); + b2b_init(&global.blake2b.hash_state, SIGN_HASH_SIZE); global.u.sign.is_hash_state_inited = true; } } @@ -31,7 +31,7 @@ static void hash_buffer(void) { const uint8_t *current = global.u.sign.message_data; while (global.u.sign.message_data_length > B2B_BLOCKBYTES) { conditional_init_hash_state(); - b2b_update(&hash_state, current, B2B_BLOCKBYTES); + b2b_update(&global.blake2b.hash_state, current, B2B_BLOCKBYTES); global.u.sign.message_data_length -= B2B_BLOCKBYTES; current += B2B_BLOCKBYTES; } @@ -42,8 +42,8 @@ static void hash_buffer(void) { static void finish_hashing(uint8_t *hash, size_t hash_size) { hash_buffer(); conditional_init_hash_state(); - b2b_update(&hash_state, global.u.sign.message_data, global.u.sign.message_data_length); - b2b_final(&hash_state, hash, hash_size); + b2b_update(&global.blake2b.hash_state, global.u.sign.message_data, global.u.sign.message_data_length); + b2b_final(&global.blake2b.hash_state, hash, hash_size); global.u.sign.message_data_length = 0; global.u.sign.is_hash_state_inited = false; } diff --git a/src/blake2.h b/src/blake2.h index a5793945..0dae3fdb 100644 --- a/src/blake2.h +++ b/src/blake2.h @@ -151,5 +151,4 @@ extern "C" { } #endif -extern b2b_state hash_state; // IMPORTANT: NOT initted by init_globals. TODO: Use blake2b from SDK. #endif diff --git a/src/blake2b-ref.c b/src/blake2b-ref.c index 063e93e4..5adf08fb 100644 --- a/src/blake2b-ref.c +++ b/src/blake2b-ref.c @@ -20,8 +20,6 @@ #include "blake2.h" #include "blake2-impl.h" -b2b_state hash_state; // IMPORTANT: NOT initted by init_globals. - static const uint64_t b2b_IV[8] = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, diff --git a/src/globals.h b/src/globals.h index fef1019e..afb7c57a 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,5 +1,6 @@ #pragma once +#include "blake2.h" #include "types.h" #include @@ -82,6 +83,10 @@ typedef struct { char address_display_data[VALUE_WIDTH]; } baking_auth; + struct { + b2b_state hash_state; + } blake2b; // TODO: Use blake2b from SDK + struct { struct priv_generate_key_pair generate_key_pair; diff --git a/src/keys.c b/src/keys.c index 1381e6a6..a6054627 100644 --- a/src/keys.c +++ b/src/keys.c @@ -96,8 +96,8 @@ cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curv default: THROW(EXC_WRONG_PARAM); } - b2b_init(&hash_state, HASH_SIZE); - b2b_update(&hash_state, compressed->W, compressed->W_len); - b2b_final(&hash_state, output, HASH_SIZE); + b2b_init(&global.blake2b.hash_state, HASH_SIZE); + b2b_update(&global.blake2b.hash_state, compressed->W, compressed->W_len); + b2b_final(&global.blake2b.hash_state, output, HASH_SIZE); return compressed; } From e9b944244d8e3ccd84cd3a3ed8c7d13ca49034b1 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 37/92] Add nix/env.sh and show what app is being built in Makefile --- Makefile | 9 ++++++++- default.nix | 11 ++++++++++- nix/env.sh | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100755 nix/env.sh diff --git a/Makefile b/Makefile index 814b4237..ba1c1017 100644 --- a/Makefile +++ b/Makefile @@ -51,10 +51,17 @@ ifeq ($(COMMIT),) endif ICONNAME=icon.gif + ################ # Default rule # ################ -all: default +all: show-app default + + +.PHONY: show-app +show-app: + @echo ">>>>> Building $(APP) at commit $(COMMIT)" + ############ # Platform # diff --git a/default.nix b/default.nix index f8edda55..4d3ee579 100644 --- a/default.nix +++ b/default.nix @@ -26,7 +26,7 @@ let export APP='${if bakingApp then "tezos_baking" else "tezos_wallet"}' export COMMIT='${commit}' make clean - make + make all EOF mkdir -p "$out" @@ -45,4 +45,13 @@ in rec { cp '${baking}/bin/app.hex' baking.hex tar czf "$out" wallet.hex baking.hex ''; + + # Script that places you in the environment to run `make`, etc. + env-shell = pkgs.writeScriptBin "env-shell" '' + #!${pkgs.stdenv.shell} + export BOLOS_SDK='${bolosSdk}' + export BOLOS_ENV='${bolosEnv}' + export COMMIT='${commit}' + exec '${fhs}/bin/enter-fhs' + ''; } diff --git a/nix/env.sh b/nix/env.sh new file mode 100755 index 00000000..91a52d9c --- /dev/null +++ b/nix/env.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +commit=$(git describe --abbrev=8 --always 2>/dev/null) +echo >&2 "Git commit: $commit" +shell_dir="$(nix-build -A env-shell --no-out-link --argstr commit "$commit" "${NIX_BUILD_ARGS:-}")" +shell="$shell_dir/bin/env-shell" + +if [ $# -eq 0 ]; then + echo >&2 "Entering via $shell" + exec "$shell" +else + exec "$shell" < Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 38/92] Add watch script --- nix/watch.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 nix/watch.sh diff --git a/nix/watch.sh b/nix/watch.sh new file mode 100755 index 00000000..35cd7bd8 --- /dev/null +++ b/nix/watch.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -uo pipefail + +root="$(git rev-parse --show-toplevel)" + +fail() { unset ___empty; : "${___empty:?$1}"; } + +[ -z "${1:-}" ] && fail "No command given; try running $0 make" + +watchdirs=("$root/default.nix" "$root/nix" "$root/Makefile" "$root/src") + +inotifywait="$(nix-build '' -A inotify-tools --no-out-link)/bin/inotifywait" +while true; do + "$root/nix/env.sh" < Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 39/92] Improve install scripts --- install.sh | 13 +++++++------ nix/install.sh | 15 ++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/install.sh b/install.sh index 01a4c510..36dd0114 100755 --- a/install.sh +++ b/install.sh @@ -1,24 +1,25 @@ -#!/bin/sh -set -eux +#!/usr/bin/env bash +set -Eeuo pipefail -rootdir="$(cd "$(dirname "$0")"/; pwd)" +root="$(git rev-parse --show-toplevel)" app_name=Tezos if [ "${1:-}X" != X ]; then app_name="$1" fi -app_file=$rootdir/bin/app.hex +app_file="$root/bin/app.hex" if [ "${2:-}X" != X ]; then app_file="$2" fi if [ "${3:-}X" = X ]; then - version="$(git -C "$rootdir" describe --tags | cut -f1 -d- | cut -f2 -dv)" + version="$(git -C "$root" describe --tags | cut -f1 -d- | cut -f2 -dv)" else version="$3" fi + set -x python -m ledgerblue.loadApp \ --appFlags 0x00 \ @@ -33,5 +34,5 @@ python -m ledgerblue.loadApp \ --fileName "$app_file" \ --appName "$app_name" \ --appVersion "$version" \ - --icon "$(cat "$rootdir/dist/icon.hex")" \ + --icon "$(cat "$root/dist/icon.hex")" \ --targetVersion "" diff --git a/nix/install.sh b/nix/install.sh index a80ee37f..ff85c031 100755 --- a/nix/install.sh +++ b/nix/install.sh @@ -1,23 +1,24 @@ #!/usr/bin/env bash -set -Eeuxo pipefail +set -Eeuo pipefail -rootdir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) +root="$(git rev-parse --show-toplevel)" -: "${VERSION:=${2:-"$(git -C "$rootdir" describe --tags | cut -f1 -d- | cut -f2 -dv)"}}" +: "${VERSION:=${2:-"$(git -C "$root" describe --tags | cut -f1 -d- | cut -f2 -dv)"}}" install-wallet() { - "$rootdir/install.sh" 'Tezos Wallet' "$("$rootdir/nix/build.sh" -A wallet)/bin/app.hex" "$VERSION" + "$root/install.sh" 'Tezos Wallet' "$("$root/nix/build.sh" -A wallet)/bin/app.hex" "$VERSION" } install-baking() { - "$rootdir/install.sh" 'Tezos Baking' "$("$rootdir/nix/build.sh" -A baking)/bin/app.hex" "$VERSION" + "$root/install.sh" 'Tezos Baking' "$("$root/nix/build.sh" -A baking)/bin/app.hex" "$VERSION" } -export rootdir +export root export VERSION export -f install-wallet export -f install-baking -nix-shell "$rootdir/nix/ledgerblue.nix" -A shell --run "$(cat < Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 40/92] Automatically detect NVRAM data size during install --- install.sh | 8 ++++---- nix/install.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/install.sh b/install.sh index 36dd0114..ef6a63ef 100755 --- a/install.sh +++ b/install.sh @@ -8,9 +8,9 @@ if [ "${1:-}X" != X ]; then app_name="$1" fi -app_file="$root/bin/app.hex" +app_dir="$root" if [ "${2:-}X" != X ]; then - app_file="$2" + app_dir="$2" fi if [ "${3:-}X" = X ]; then @@ -23,7 +23,7 @@ fi set -x python -m ledgerblue.loadApp \ --appFlags 0x00 \ - --dataSize 0x80 \ + --dataSize "$(grep _nvram_data_size "$app_dir/debug/app.map" | tr -s ' ' | cut -f2 -d' ')" \ --tlv \ --curve ed25519 \ --curve secp256k1 \ @@ -31,7 +31,7 @@ python -m ledgerblue.loadApp \ --targetId "${TARGET_ID:-0x31100004}" \ --delete \ --path 44"'"/1729"'" \ - --fileName "$app_file" \ + --fileName "$app_dir/bin/app.hex" \ --appName "$app_name" \ --appVersion "$version" \ --icon "$(cat "$root/dist/icon.hex")" \ diff --git a/nix/install.sh b/nix/install.sh index ff85c031..da9f7a2f 100755 --- a/nix/install.sh +++ b/nix/install.sh @@ -6,10 +6,10 @@ root="$(git rev-parse --show-toplevel)" : "${VERSION:=${2:-"$(git -C "$root" describe --tags | cut -f1 -d- | cut -f2 -dv)"}}" install-wallet() { - "$root/install.sh" 'Tezos Wallet' "$("$root/nix/build.sh" -A wallet)/bin/app.hex" "$VERSION" + "$root/install.sh" 'Tezos Wallet' "$("$root/nix/build.sh" -A wallet)" "$VERSION" } install-baking() { - "$root/install.sh" 'Tezos Baking' "$("$root/nix/build.sh" -A baking)/bin/app.hex" "$VERSION" + "$root/install.sh" 'Tezos Baking' "$("$root/nix/build.sh" -A baking)" "$VERSION" } export root From bd7f8b24aeaf49e18d3ce69803c79b8dd6d57486 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 41/92] Clean up test/apdu.sh --- test/apdu.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/apdu.sh b/test/apdu.sh index 1c3c0d01..df7647b6 100755 --- a/test/apdu.sh +++ b/test/apdu.sh @@ -1,9 +1,5 @@ #!/usr/bin/env bash -set -eu +set -Eeuo pipefail -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -cd "$DIR" - -if ! nix-shell ledger-blue-shell.nix --pure --run 'python -m ledgerblue.runScript --apdu'; then - python -m ledgerblue.runScript --apdu -fi +root="$(git rev-parse --show-toplevel)" +nix-shell "$root/nix/ledgerblue.nix" -A shell --pure --run 'python -m ledgerblue.runScript --apdu' From 2a650cb87ee1c572c91ecec7959f55fd90d486fc Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 42/92] Change copy_string for reasons --- src/to_string.c | 8 ++++---- src/to_string.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/to_string.c b/src/to_string.c index f7122c21..7673657a 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -190,9 +190,9 @@ size_t microtez_to_string(char *dest, uint64_t number) { return off; } -void copy_string(char *dest, uint32_t buff_size, const char *src_in) { - const char *src = (const char *)PIC(src_in); +void copy_string(char *const dest, size_t const buff_size, char const *const src) { + char const *const src_in = (char const *)PIC(src); // I don't care that we will loop through the string twice, latency is not an issue - if (strlen(src) >= buff_size) THROW(EXC_WRONG_LENGTH); - strcpy(dest, src); + if (strlen(src_in) >= buff_size) THROW(EXC_WRONG_LENGTH); + strcpy(dest, src_in); } diff --git a/src/to_string.h b/src/to_string.h index 13bbe9b0..2943501d 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -26,4 +26,4 @@ void microtez_to_string_indirect(char *dest, uint32_t buff_size, const uint64_t // This is designed to be called with potentially unrelocated pointers from rodata tables // for the src argument, so performs PIC on src. -void copy_string(char *dest, uint32_t buff_size, const char *src); +void copy_string(char *const dest, size_t const buff_size, char const *const src); From ebe6592509bc1ed03ff51becf4735d7e90d2bac5 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 43/92] Clean up headers --- src/apdu.h | 1 - src/keys.c | 3 --- src/keys.h | 3 +-- src/main.c | 4 ++-- src/os_cx.h | 5 +++++ 5 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 src/os_cx.h diff --git a/src/apdu.h b/src/apdu.h index 385843af..f3bf9fbf 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -5,7 +5,6 @@ #include "keys.h" #include "ui.h" -// Order matters #include "os.h" #include diff --git a/src/keys.c b/src/keys.c index a6054627..787acaee 100644 --- a/src/keys.c +++ b/src/keys.c @@ -22,9 +22,6 @@ #include "globals.h" #include "protocol.h" -// Order matters -#include "os.h" - #include #include diff --git a/src/keys.h b/src/keys.h index 08a23f34..6d592e45 100644 --- a/src/keys.h +++ b/src/keys.h @@ -3,10 +3,9 @@ #include #include #include -#include "os.h" -#include "cx.h" #include "exception.h" +#include "os_cx.h" #include "types.h" // Throws upon error diff --git a/src/main.c b/src/main.c index 90707a7c..9daf7499 100644 --- a/src/main.c +++ b/src/main.c @@ -15,10 +15,10 @@ * limitations under the License. ********************************************************************************/ -#include "apdu.h" +#include "apdu_baking.h" #include "apdu_pubkey.h" #include "apdu_sign.h" -#include "apdu_baking.h" +#include "apdu.h" #include "globals.h" #include "memory.h" diff --git a/src/os_cx.h b/src/os_cx.h new file mode 100644 index 00000000..c1e92cb1 --- /dev/null +++ b/src/os_cx.h @@ -0,0 +1,5 @@ +#pragma once + +// Order matters +#include "os.h" +#include "cx.h" From c7a3bb59c8ac3ebe467350873b12150e4a7adfad Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 44/92] Centralize APDU instruction table --- src/apdu.h | 17 +++++++++++++---- src/apdu_baking.h | 4 ---- src/apdu_pubkey.h | 4 ---- src/apdu_sign.h | 3 --- src/globals.h | 2 -- src/main.c | 20 ++++++++++---------- src/types.h | 8 ++++++++ 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index f3bf9fbf..0003892c 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -15,13 +15,22 @@ #endif #define OFFSET_CLA 0 -#define OFFSET_INS 1 -#define OFFSET_P1 2 +#define OFFSET_INS 1 // instruction code +#define OFFSET_P1 2 // user-defined 1-byte parameter #define OFFSET_CURVE 3 -#define OFFSET_LC 4 -#define OFFSET_CDATA 5 +#define OFFSET_LC 4 // length of CDATA +#define OFFSET_CDATA 5 // payload +// Instruction codes #define INS_VERSION 0x00 +#define INS_AUTHORIZE_BAKING 0x01 +#define INS_GET_PUBLIC_KEY 0x02 +#define INS_PROMPT_PUBLIC_KEY 0x03 +#define INS_SIGN 0x04 +#define INS_SIGN_UNSAFE 0x05 // Data that is already hashed. +#define INS_RESET 0x06 +#define INS_QUERY_AUTH_KEY 0x07 +#define INS_QUERY_HWM 0x08 #define INS_GIT 0x09 __attribute__((noreturn)) diff --git a/src/apdu_baking.h b/src/apdu_baking.h index 1d60df1e..b32e8fcd 100644 --- a/src/apdu_baking.h +++ b/src/apdu_baking.h @@ -2,10 +2,6 @@ #include -#define INS_RESET 0x06 -#define INS_QUERY_AUTH_KEY 0x07 -#define INS_QUERY_HWM 0x08 - unsigned int handle_apdu_reset(uint8_t instruction); unsigned int handle_apdu_query_auth_key(uint8_t instruction); unsigned int handle_apdu_hwm(uint8_t instruction); diff --git a/src/apdu_pubkey.h b/src/apdu_pubkey.h index ebda2972..460ca218 100644 --- a/src/apdu_pubkey.h +++ b/src/apdu_pubkey.h @@ -2,8 +2,4 @@ #include "apdu.h" -#define INS_AUTHORIZE_BAKING 0x01 -#define INS_GET_PUBLIC_KEY 0x02 -#define INS_PROMPT_PUBLIC_KEY 0x03 - unsigned int handle_apdu_get_public_key(uint8_t instruction); diff --git a/src/apdu_sign.h b/src/apdu_sign.h index dac7a31f..0e5ad17c 100644 --- a/src/apdu_sign.h +++ b/src/apdu_sign.h @@ -2,7 +2,4 @@ #include "apdu.h" -#define INS_SIGN 0x04 -#define INS_SIGN_UNSAFE 0x05 // Data that is already hashed. - unsigned int handle_apdu_sign(uint8_t instruction); diff --git a/src/globals.h b/src/globals.h index afb7c57a..62adcc96 100644 --- a/src/globals.h +++ b/src/globals.h @@ -15,8 +15,6 @@ void init_globals(void); # define TEZOS_BUFSIZE 256 #endif -#define INS_MAX 0x0B - #define PRIVATE_KEY_DATA_SIZE 32 struct priv_generate_key_pair { diff --git a/src/main.c b/src/main.c index 9daf7499..73a95d08 100644 --- a/src/main.c +++ b/src/main.c @@ -28,19 +28,19 @@ void app_main(void) { for (size_t i = 0; i < NUM_ELEMENTS(global.handlers); i++) { global.handlers[i] = handle_apdu_error; } - global.handlers[INS_VERSION] = handle_apdu_version; - global.handlers[INS_GET_PUBLIC_KEY] = handle_apdu_get_public_key; - global.handlers[INS_PROMPT_PUBLIC_KEY] = handle_apdu_get_public_key; + global.handlers[APDU_INS(INS_VERSION)] = handle_apdu_version; + global.handlers[APDU_INS(INS_GET_PUBLIC_KEY)] = handle_apdu_get_public_key; + global.handlers[APDU_INS(INS_PROMPT_PUBLIC_KEY)] = handle_apdu_get_public_key; #ifdef BAKING_APP - global.handlers[INS_AUTHORIZE_BAKING] = handle_apdu_get_public_key; - global.handlers[INS_RESET] = handle_apdu_reset; - global.handlers[INS_QUERY_AUTH_KEY] = handle_apdu_query_auth_key; - global.handlers[INS_QUERY_HWM] = handle_apdu_hwm; + global.handlers[APDU_INS(INS_AUTHORIZE_BAKING)] = handle_apdu_get_public_key; + global.handlers[APDU_INS(INS_RESET)] = handle_apdu_reset; + global.handlers[APDU_INS(INS_QUERY_AUTH_KEY)] = handle_apdu_query_auth_key; + global.handlers[APDU_INS(INS_QUERY_HWM)] = handle_apdu_hwm; #endif - global.handlers[INS_SIGN] = handle_apdu_sign; + global.handlers[APDU_INS(INS_SIGN)] = handle_apdu_sign; #ifndef BAKING_APP - global.handlers[INS_SIGN_UNSAFE] = handle_apdu_sign; + global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif - global.handlers[INS_GIT] = handle_apdu_git; + global.handlers[APDU_INS(INS_GIT)] = handle_apdu_git; main_loop(global.handlers); } diff --git a/src/types.h b/src/types.h index ba3b8a43..ab93d70a 100644 --- a/src/types.h +++ b/src/types.h @@ -119,3 +119,11 @@ struct parsed_operation_group { struct parsed_contract signing; struct parsed_operation operation; }; + +// Maximum number of APDU instructions +#define INS_MAX 0x0B + +#define APDU_INS(x) ({ \ + _Static_assert(x <= INS_MAX, "APDU instruction is out of bounds"); \ + x; \ +}) From 1091eb2fc45dc4c1ea862d23b674eccdcbb08f3b Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 45/92] Move throw_stack_size to globals.h --- src/apdu.h | 9 +-------- src/globals.h | 8 ++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index 0003892c..ff6dc47c 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -1,8 +1,8 @@ #pragma once #include "exception.h" -#include "globals.h" #include "keys.h" +#include "types.h" #include "ui.h" #include "os.h" @@ -61,10 +61,3 @@ static inline void require_hid(void) { uint32_t handle_apdu_error(uint8_t instruction); uint32_t handle_apdu_version(uint8_t instruction); uint32_t handle_apdu_git(uint8_t instruction); - -static inline void throw_stack_size() { - uint8_t st; - // uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; - uint32_t tmp2 = (uint32_t)global.stack_root - (uint32_t)&st; - THROW(0x9000 + tmp2); -} diff --git a/src/globals.h b/src/globals.h index 62adcc96..03e747bc 100644 --- a/src/globals.h +++ b/src/globals.h @@ -109,3 +109,11 @@ extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? #define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) + + +static inline void throw_stack_size() { + uint8_t st; + // uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; + uint32_t tmp2 = (uint32_t)global.stack_root - (uint32_t)&st; + THROW(0x9000 + tmp2); +} From 160d6f31658c93345e30748a41c317b33a476de6 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 46/92] Add some helpful debug macros --- src/memory.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/memory.h b/src/memory.h index 42d3413e..dea9ea22 100644 --- a/src/memory.h +++ b/src/memory.h @@ -7,3 +7,8 @@ memcmp(a, b, sizeof(*a)); \ }) #define NUM_ELEMENTS(a) (sizeof(a)/sizeof(*a)) + + +// Macro that produces a compile-error showing the sizeof the argument. +#define ERROR_WITH_NUMBER_EXPANSION(x) char (*__kaboom)[x] = 1; +#define SIZEOF_ERROR(x) ERROR_WITH_NUMBER_EXPANSION(sizeof(x)); From 5f3d29d9b7bbe2321a47f6444b3c78dd59653998 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 47/92] Refactor to use bip32_path struct --- src/apdu.c | 2 +- src/apdu_baking.c | 6 +++--- src/apdu_pubkey.c | 17 ++++++++--------- src/apdu_sign.c | 23 +++++++++++------------ src/baking_auth.c | 31 ++++++++++++++----------------- src/baking_auth.h | 7 +++---- src/globals.h | 6 ++---- src/keys.c | 32 +++++++++++++++++--------------- src/keys.h | 11 ++++++++--- src/operations.c | 10 +++++----- src/operations.h | 5 +++-- src/protocol.h | 6 +++++- src/types.h | 25 +++++++++++++++++++++++-- 13 files changed, 103 insertions(+), 78 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 17d59501..31f3e4d3 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -55,7 +55,7 @@ void main_loop(apdu_handler handlers[INS_MAX]) { THROW(EXC_WRONG_LENGTH); } - uint8_t instruction = G_io_apdu_buffer[OFFSET_INS]; + const uint8_t instruction = G_io_apdu_buffer[OFFSET_INS]; const apdu_handler cb = (instruction >= INS_MAX) ? handle_apdu_error : handlers[instruction]; diff --git a/src/apdu_baking.c b/src/apdu_baking.c index b17e51bf..ac43444a 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -76,13 +76,13 @@ unsigned int handle_apdu_hwm(__attribute__((unused)) uint8_t instruction) { } unsigned int handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { - uint32_t tx = 0; + uint8_t const length = N_data.bip32_path.length; - uint8_t length = N_data.path_length; + uint32_t tx = 0; G_io_apdu_buffer[tx++] = length; for (uint8_t i = 0; i < length; ++i) { - tx = send_word_big_endian(tx, N_data.bip32_path[i]); + tx = send_word_big_endian(tx, N_data.bip32_path.components[i]); } G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 244f4440..e90d2ea7 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -11,8 +11,8 @@ #include -static int provide_pubkey(void) { - int tx = 0; +static uint32_t provide_pubkey(void) { + uint32_t tx = 0; G_io_apdu_buffer[tx++] = global.u.pubkey.public_key.W_len; os_memmove(G_io_apdu_buffer + tx, global.u.pubkey.public_key.W, @@ -24,14 +24,14 @@ static int provide_pubkey(void) { } static bool pubkey_ok(void) { - int tx = provide_pubkey(); + uint32_t const tx = provide_pubkey(); delayed_send(tx); return true; } #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(global.u.pubkey.curve, global.u.pubkey.bip32_path, global.u.pubkey.bip32_path_length); + authorize_baking(global.u.pubkey.curve, &global.u.pubkey.bip32_path); pubkey_ok(); return true; } @@ -54,18 +54,17 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { global.u.pubkey.curve = N_data.curve; - global.u.pubkey.bip32_path_length = N_data.path_length; - memcpy(global.u.pubkey.bip32_path, N_data.bip32_path, sizeof(*global.u.pubkey.bip32_path) * global.u.pubkey.bip32_path_length); + copy_bip32_path(&global.u.pubkey.bip32_path, &N_data.bip32_path); } else { #endif - global.u.pubkey.bip32_path_length = read_bip32_path(G_io_apdu_buffer[OFFSET_LC], global.u.pubkey.bip32_path, dataBuffer); + read_bip32_path(&global.u.pubkey.bip32_path, dataBuffer, G_io_apdu_buffer[OFFSET_LC]); #ifdef BAKING_APP - if (global.u.pubkey.bip32_path_length == 0) { + if (global.u.pubkey.bip32_path.length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } } #endif - struct key_pair *pair = generate_key_pair(global.u.pubkey.curve, global.u.pubkey.bip32_path_length, global.u.pubkey.bip32_path); + struct key_pair *pair = generate_key_pair(global.u.pubkey.curve, &global.u.pubkey.bip32_path); memset(&pair->private_key, 0, sizeof(pair->private_key)); memcpy(&global.u.pubkey.public_key, &pair->public_key, sizeof(global.u.pubkey.public_key)); diff --git a/src/apdu_sign.c b/src/apdu_sign.c index d5347b37..f683b679 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -52,7 +52,7 @@ static int perform_signature(bool hash_first); #ifdef BAKING_APP static bool bake_auth_ok(void) { - authorize_baking(global.u.sign.curve, global.u.sign.bip32_path, global.u.sign.bip32_path_length); + authorize_baking(global.u.sign.curve, &global.u.sign.bip32_path); int tx = perform_signature(true); delayed_send(tx); return true; @@ -72,7 +72,7 @@ static bool sign_unsafe_ok(void) { #endif static void clear_data(void) { - global.u.sign.bip32_path_length = 0; + global.u.sign.bip32_path.length = 0; global.u.sign.message_data_length = 0; global.u.sign.is_hash_state_inited = false; global.u.sign.magic_number = 0; @@ -93,7 +93,7 @@ uint32_t baking_sign_complete(void) { case MAGIC_BYTE_BAKING_OP: guard_baking_authorized( global.u.sign.curve, global.u.sign.message_data, global.u.sign.message_data_length, - global.u.sign.bip32_path, global.u.sign.bip32_path_length); + &global.u.sign.bip32_path); return perform_signature(true); case MAGIC_BYTE_UNSAFE_OP: @@ -107,8 +107,7 @@ uint32_t baking_sign_complete(void) { struct parsed_operation_group *ops = parse_operations( global.u.sign.message_data, global.u.sign.message_data_length, - global.u.sign.curve, global.u.sign.bip32_path_length, global.u.sign.bip32_path, - allowed); + global.u.sign.curve, &global.u.sign.bip32_path, allowed); // With < nickel fee if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); @@ -146,8 +145,9 @@ const char *const insecure_values[] = { // Return false if the transaction isn't easily parseable, otherwise prompt with given callbacks // and do not return, but rather throw ASYNC. static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve, - size_t path_length, uint32_t *bip32_path, + bip32_path_t const *const bip32_path, ui_callback_t ok, ui_callback_t cxl) { + check_null(bip32_path); struct parsed_operation_group *ops; #ifndef TEZOS_DEBUG @@ -167,7 +167,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve allow_operation(&allowed, OPERATION_TAG_TRANSACTION); // TODO: Add still other operations - ops = parse_operations(data, length, curve, path_length, bip32_path, allowed); + ops = parse_operations(data, length, curve, bip32_path, allowed); #ifndef TEZOS_DEBUG } CATCH_OTHER(e) { @@ -443,7 +443,7 @@ uint32_t wallet_sign_complete(uint8_t instruction) { } if (!prompt_transaction( global.u.sign.message_data, global.u.sign.message_data_length, global.u.sign.curve, - global.u.sign.bip32_path_length, global.u.sign.bip32_path, sign_ok, sign_reject)) { + &global.u.sign.bip32_path, sign_ok, sign_reject)) { goto unsafe; } case MAGIC_BYTE_UNSAFE_OP2: @@ -472,7 +472,7 @@ unsigned int handle_apdu_sign(uint8_t instruction) { clear_data(); memset(global.u.sign.message_data, 0, sizeof(global.u.sign.message_data)); global.u.sign.message_data_length = 0; - global.u.sign.bip32_path_length = read_bip32_path(dataLength, global.u.sign.bip32_path, dataBuffer); + read_bip32_path(&global.u.sign.bip32_path, dataBuffer, dataLength); global.u.sign.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); return_ok(); #ifndef BAKING_APP @@ -482,7 +482,7 @@ unsigned int handle_apdu_sign(uint8_t instruction) { // FALL THROUGH #endif case P1_NEXT: - if (global.u.sign.bip32_path_length == 0) { + if (global.u.sign.bip32_path.length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } break; @@ -545,8 +545,7 @@ static int perform_signature(bool hash_first) { #endif } - struct key_pair *pair = generate_key_pair( - global.u.sign.curve, global.u.sign.bip32_path_length, global.u.sign.bip32_path); + struct key_pair *const pair = generate_key_pair(global.u.sign.curve, &global.u.sign.bip32_path); uint32_t tx; switch (global.u.sign.curve) { diff --git a/src/baking_auth.c b/src/baking_auth.c index 8709bc0e..c8ce2bd6 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -3,6 +3,7 @@ #include "apdu.h" #include "globals.h" #include "keys.h" +#include "memory.h" #include "protocol.h" #include "to_string.h" #include "ui_prompt.h" @@ -26,16 +27,14 @@ void write_highest_level(level_t lvl, bool is_endorsement) { change_idle_display(N_data.highest_level); } -void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t path_length) { - if (bip32_path == NULL || path_length > MAX_BIP32_PATH || path_length == 0) { - return; - } +void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path) { + check_null(bip32_path); + if (bip32_path->length > NUM_ELEMENTS(N_data.bip32_path.components) || bip32_path->length == 0) return; global.baking_auth.new_data.highest_level = N_data.highest_level; global.baking_auth.new_data.had_endorsement = N_data.had_endorsement; global.baking_auth.new_data.curve = curve; - memcpy(global.baking_auth.new_data.bip32_path, bip32_path, path_length * sizeof(*bip32_path)); - global.baking_auth.new_data.path_length = path_length; + copy_bip32_path(&global.baking_auth.new_data.bip32_path, bip32_path); nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); change_idle_display(N_data.highest_level); } @@ -49,21 +48,19 @@ bool is_level_authorized(level_t level, bool is_endorsement) { return is_endorsement && !N_data.had_endorsement; } -bool is_path_authorized(cx_curve_t curve, uint32_t *bip32_path, uint8_t path_length) { - return bip32_path != NULL && - path_length != 0 && - path_length == N_data.path_length && +bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path) { + check_null(bip32_path); + return curve == N_data.curve && - memcmp(bip32_path, N_data.bip32_path, path_length * sizeof(*bip32_path)) == 0; + bip32_path->length > 0 && + bip32_paths_eq(bip32_path, &N_data.bip32_path); } -void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, uint32_t *bip32_path, - uint8_t path_length) { - if (!is_path_authorized(curve, bip32_path, path_length)) THROW(EXC_SECURITY); +void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path) { + if (!is_path_authorized(curve, bip32_path)) THROW(EXC_SECURITY); struct parsed_baking_data baking_info; if (!parse_baking_data(data, datalen, &baking_info)) THROW(EXC_PARSE_ERROR); - if (!is_level_authorized(baking_info.level, baking_info.is_endorsement)) THROW(EXC_WRONG_VALUES); } @@ -77,10 +74,10 @@ void update_high_water_mark(void *data, int datalen) { } void update_auth_text(void) { - if (N_data.path_length == 0) { + if (N_data.bip32_path.length == 0) { strcpy(global.ui.baking_auth_text, "No Key Authorized"); } else { - struct key_pair *pair = generate_key_pair(N_data.curve, N_data.path_length, N_data.bip32_path); + struct key_pair *pair = generate_key_pair(N_data.curve, &N_data.bip32_path); memset(&pair->private_key, 0, sizeof(pair->private_key)); pubkey_to_pkh_string( global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), diff --git a/src/baking_auth.h b/src/baking_auth.h index a3309804..bbbfea59 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -7,10 +7,9 @@ #include #include -void authorize_baking(cx_curve_t curve, uint32_t *bip32_path, uint8_t pathLength); -void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, uint32_t *bip32_path, - uint8_t path_length); -bool is_path_authorized(cx_curve_t curve, uint32_t *bip32_path, uint8_t path_length); +void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path); +void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path); +bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path); void update_high_water_mark(void *data, int datalen); void write_highest_level(level_t level, bool is_endorsement); bool is_level_authorized(level_t level, bool is_endorsement); diff --git a/src/globals.h b/src/globals.h index 03e747bc..77f7e7a7 100644 --- a/src/globals.h +++ b/src/globals.h @@ -33,14 +33,12 @@ typedef struct { struct { cx_ecfp_public_key_t public_key; - uint8_t bip32_path_length; - uint32_t bip32_path[MAX_BIP32_PATH]; + bip32_path_t bip32_path; cx_curve_t curve; } pubkey; struct { - uint8_t bip32_path_length; - uint32_t bip32_path[MAX_BIP32_PATH]; + bip32_path_t bip32_path; cx_curve_t curve; uint8_t message_data[TEZOS_BUFSIZE]; diff --git a/src/keys.c b/src/keys.c index 787acaee..a0b2e387 100644 --- a/src/keys.c +++ b/src/keys.c @@ -20,41 +20,43 @@ #include "apdu.h" #include "blake2.h" #include "globals.h" +#include "memory.h" #include "protocol.h" +#include "types.h" #include #include -uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *buf) { - uint32_t path_length = *buf; - if (bytes < path_length * sizeof(uint32_t) + 1) THROW(EXC_WRONG_LENGTH_FOR_INS); +size_t read_bip32_path(bip32_path_t *const out, uint8_t const *const in, size_t const in_size) { + struct bip32_path_wire const *const buf_as_bip32 = (struct bip32_path_wire const *)in; - buf++; + if (in_size < sizeof(buf_as_bip32->length)) THROW(EXC_WRONG_LENGTH_FOR_INS); - if (path_length == 0 || path_length > MAX_BIP32_PATH) { - screen_printf("Invalid path\n"); - THROW(EXC_WRONG_VALUES); - } + size_t ix = 0; + out->length = CONSUME_UNALIGNED_BIG_ENDIAN(ix, uint8_t, &buf_as_bip32->length); + + if (in_size - ix < out->length * sizeof(*buf_as_bip32->components)) THROW(EXC_WRONG_LENGTH_FOR_INS); + if (out->length == 0 || out->length > NUM_ELEMENTS(out->components)) THROW(EXC_WRONG_VALUES); - for (size_t i = 0; i < path_length; i++) { - bip32_path[i] = READ_UNALIGNED_BIG_ENDIAN(uint32_t, (uint32_t*)buf); - buf += 4; + for (size_t i = 0; i < out->length; i++) { + out->components[i] = CONSUME_UNALIGNED_BIG_ENDIAN(ix, uint32_t, &buf_as_bip32->components[i]); } - return path_length; + return ix; } -struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_length, uint32_t *bip32_path) { +struct key_pair *generate_key_pair(cx_curve_t const curve, bip32_path_t const *const bip32_path) { + check_null(bip32_path); struct priv_generate_key_pair *const priv = &global.priv.generate_key_pair; #if CX_APILEVEL > 8 if (curve == CX_CURVE_Ed25519) { os_perso_derive_node_bip32_seed_key( - HDW_ED25519_SLIP10, curve, bip32_path, path_length, + HDW_ED25519_SLIP10, curve, bip32_path->components, bip32_path->length, priv->privateKeyData, NULL, NULL, 0); } else { #endif - os_perso_derive_node_bip32(curve, bip32_path, path_length, priv->privateKeyData, NULL); + os_perso_derive_node_bip32(curve, bip32_path->components, bip32_path->length, priv->privateKeyData, NULL); #if CX_APILEVEL > 8 } #endif diff --git a/src/keys.h b/src/keys.h index 6d592e45..5e3e839a 100644 --- a/src/keys.h +++ b/src/keys.h @@ -8,10 +8,15 @@ #include "os_cx.h" #include "types.h" -// Throws upon error -uint32_t read_bip32_path(uint32_t bytes, uint32_t *bip32_path, const uint8_t *buf); +struct bip32_path_wire { + uint8_t length; + uint32_t components[0]; +} __attribute__((packed)); -struct key_pair *generate_key_pair(cx_curve_t curve, uint32_t path_size, uint32_t *bip32_path); +// throws +size_t read_bip32_path(bip32_path_t *const out, uint8_t const *const in, size_t const in_size); + +struct key_pair *generate_key_pair(cx_curve_t const curve, bip32_path_t const *const bip32_path); cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, const cx_ecfp_public_key_t *restrict public_key); diff --git a/src/operations.c b/src/operations.c index 88b52223..072a672b 100644 --- a/src/operations.c +++ b/src/operations.c @@ -105,9 +105,9 @@ static inline uint64_t parse_z(const void *data, size_t *ix, size_t length, uint val; \ }) -static inline void compute_pkh(cx_curve_t curve, size_t path_length, uint32_t *bip32_path, - struct parsed_operation_group *out) { - struct key_pair *pair = generate_key_pair(curve, path_length, bip32_path); +static inline void compute_pkh(cx_curve_t curve, bip32_path_t const *const bip32_path, + struct parsed_operation_group *const out) { + check_null(bip32_path); memset(&pair->private_key, 0, sizeof(pair->private_key)); cx_ecfp_public_key_t *key = public_key_hash(out->signing.hash, curve, &pair->public_key); @@ -135,7 +135,7 @@ static inline void parse_contract(struct parsed_contract *out, const struct cont } struct parsed_operation_group *parse_operations(const void *data, size_t length, cx_curve_t curve, - size_t path_length, uint32_t *bip32_path, + bip32_path_t const *const bip32_path, allowed_operation_set ops) { check_null(data); check_null(bip32_path); @@ -145,7 +145,7 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, out->operation.tag = OPERATION_TAG_NONE; - compute_pkh(curve, path_length, bip32_path, out); // sets up "signing" and "public_key" members + compute_pkh(curve, bip32_path, out); // sets up "signing" and "public_key" members size_t ix = 0; diff --git a/src/operations.h b/src/operations.h index 4092e60b..c3d909dc 100644 --- a/src/operations.h +++ b/src/operations.h @@ -31,5 +31,6 @@ static inline void clear_operation_set(allowed_operation_set *ops) { // Returns pointer to static data -- non-reentrant as hell. struct parsed_operation_group * -parse_operations(const void *data, size_t length, cx_curve_t curve, size_t path_length, - uint32_t *bip32_path, allowed_operation_set ops); +parse_operations(const void *data, size_t length, cx_curve_t curve, + bip32_path_t const *const bip32_path, + allowed_operation_set ops); diff --git a/src/protocol.h b/src/protocol.h index 2df05ab4..892ef81e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -22,7 +22,7 @@ static inline uint8_t get_magic_byte(const uint8_t *data, size_t length) { #define READ_UNALIGNED_BIG_ENDIAN(type, in) \ ({ \ - const uint8_t *bytes = (uint8_t*)in; \ + uint8_t const *bytes = (uint8_t const *)in; \ uint8_t out_bytes[sizeof(type)]; \ type res; \ \ @@ -33,3 +33,7 @@ static inline uint8_t get_magic_byte(const uint8_t *data, size_t length) { \ res; \ }) + +// Same as READ_UNALIGNED_BIG_ENDIAN but helps keep track of how many bytes +// have been read by adding sizeof(type) to the given counter. +#define CONSUME_UNALIGNED_BIG_ENDIAN(counter, type, addr) ({ counter += sizeof(type); READ_UNALIGNED_BIG_ENDIAN(type, addr); }) diff --git a/src/types.h b/src/types.h index ab93d70a..dedb1904 100644 --- a/src/types.h +++ b/src/types.h @@ -4,6 +4,7 @@ #include "os_io_seproxyhal.h" #include +#include // Return number of bytes to transmit (tx) typedef uint32_t (*apdu_handler)(uint8_t instruction); @@ -25,14 +26,34 @@ struct key_pair { // Baking Auth #define MAX_BIP32_PATH 10 + +typedef struct { + uint8_t length; + uint32_t components[MAX_BIP32_PATH]; +} bip32_path_t; + +static inline void copy_bip32_path(bip32_path_t *const out, bip32_path_t const *const in) { + memcpy(out->components, in->components, in->length * sizeof(*in->components)); + out->length = in->length; +} + +static inline bool bip32_paths_eq(bip32_path_t const *const a, bip32_path_t const *const b) { + return a == b || ( + a != NULL && + b != NULL && + a->length == b->length && + memcmp(a->components, b->components, a->length * sizeof(*a->components)) == 0 + ); +} + typedef struct { cx_curve_t curve; level_t highest_level; bool had_endorsement; - uint8_t path_length; - uint32_t bip32_path[MAX_BIP32_PATH]; + bip32_path_t bip32_path; } nvram_data; + #define PKH_STRING_SIZE 40 #define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte From 7da20afc458f20a57ea1258a60e83a425bbbe831 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 48/92] Clean up headers --- src/apdu_baking.c | 5 +---- src/apdu_pubkey.c | 4 +--- src/baking_auth.c | 4 +--- src/to_string.h | 4 +--- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index ac43444a..6c580514 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -3,14 +3,11 @@ #include "apdu.h" #include "baking_auth.h" #include "globals.h" +#include "os_cx.h" #include "protocol.h" #include "to_string.h" #include "ui_prompt.h" -// Order matters -#include "cx.h" -#include "os.h" - #include static bool reset_ok(void); diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index e90d2ea7..b18eea46 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -2,13 +2,11 @@ #include "apdu.h" #include "baking_auth.h" +#include "cx.h" #include "globals.h" #include "keys.h" #include "ui.h" -// Order matters -#include "cx.h" - #include static uint32_t provide_pubkey(void) { diff --git a/src/baking_auth.c b/src/baking_auth.c index c8ce2bd6..1b829fe5 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -8,9 +8,7 @@ #include "to_string.h" #include "ui_prompt.h" -// Order matters -#include "os.h" -#include "cx.h" +#include "os_cx.h" #include diff --git a/src/to_string.h b/src/to_string.h index 2943501d..3eb5516d 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -5,9 +5,7 @@ #include "keys.h" #include "operations.h" - -#include "os.h" -#include "cx.h" +#include "os_cx.h" #include "ui.h" void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, From ac036ba6889ca2fac126c3c7a0c673ea411d1cc7 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 49/92] Add setup APDU --- src/apdu.h | 1 + src/apdu_baking.c | 16 ++++--- src/apdu_setup.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ src/apdu_setup.h | 5 +++ src/baking_auth.c | 86 ++++++++++++++++++----------------- src/baking_auth.h | 10 ++--- src/globals.h | 29 ++++++++++++ src/main.c | 9 ++-- src/to_string.c | 65 ++++++++++++++++----------- src/to_string.h | 2 + src/types.h | 19 +++++++- src/ui.c | 2 +- 12 files changed, 271 insertions(+), 84 deletions(-) create mode 100644 src/apdu_setup.c create mode 100644 src/apdu_setup.h diff --git a/src/apdu.h b/src/apdu.h index ff6dc47c..bd3f65a5 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -32,6 +32,7 @@ #define INS_QUERY_AUTH_KEY 0x07 #define INS_QUERY_HWM 0x08 #define INS_GIT 0x09 +#define INS_SETUP 0x0A __attribute__((noreturn)) void main_loop(apdu_handler handlers[INS_MAX]); diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 6c580514..c1cb7497 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -18,11 +18,8 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { if (dataLength != sizeof(int)) { THROW(EXC_WRONG_LENGTH_FOR_INS); } - level_t lvl = READ_UNALIGNED_BIG_ENDIAN(level_t, dataBuffer); - - if (!is_valid_level(lvl)) { - THROW(EXC_PARSE_ERROR); - } + level_t const lvl = READ_UNALIGNED_BIG_ENDIAN(level_t, dataBuffer); + if (!is_valid_level(lvl)) THROW(EXC_PARSE_ERROR); global.u.baking.reset_level = lvl; @@ -36,7 +33,12 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { } bool reset_ok(void) { - write_highest_level(global.u.baking.reset_level, false); // We have not yet had an endorsement at this level + UPDATE_NVRAM(ram, { + ram->hwm.main.highest_level = global.u.baking.reset_level; + ram->hwm.main.had_endorsement = false; + ram->hwm.test.highest_level = global.u.baking.reset_level; + ram->hwm.test.had_endorsement = false; + }); uint32_t tx = 0; G_io_apdu_buffer[tx++] = 0x90; @@ -64,7 +66,7 @@ uint32_t send_word_big_endian(uint32_t tx, uint32_t word) { unsigned int handle_apdu_hwm(__attribute__((unused)) uint8_t instruction) { uint32_t tx = 0; - level_t level = N_data.highest_level; + level_t level = N_data.hwm.main.highest_level; tx = send_word_big_endian(tx, level); G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_setup.c b/src/apdu_setup.c new file mode 100644 index 00000000..d944dad8 --- /dev/null +++ b/src/apdu_setup.c @@ -0,0 +1,111 @@ +#include "apdu_setup.h" + +#include "apdu.h" +#include "cx.h" +#include "globals.h" +#include "keys.h" +#include "to_string.h" +#include "ui_prompt.h" +#include "ui.h" + +#include + +#define G global.u.setup + +struct setup_wire { + uint32_t main_chain_id; + struct { + uint32_t main; + uint32_t test; + } hwm; + struct bip32_path_wire bip32_path; +} __attribute__((packed)); + + +static size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *const pubkey) { + size_t tx = 0; + io_buffer[tx++] = pubkey->W_len; + memmove(io_buffer + tx, pubkey->W, pubkey->W_len); + tx += pubkey->W_len; + io_buffer[tx++] = 0x90; + io_buffer[tx++] = 0x00; + return tx; +} + +static bool ok(void) { + UPDATE_NVRAM(ram, { + ram->curve = G.curve; + copy_bip32_path(&ram->bip32_path, &G.bip32_path); + ram->main_chain_id = G.main_chain_id; + ram->hwm.main.highest_level = G.hwm.main; + ram->hwm.main.had_endorsement = false; + ram->hwm.test.highest_level = G.hwm.test; + ram->hwm.test.had_endorsement = false; + }); + delayed_send(provide_pubkey(G_io_apdu_buffer, &G.public_key)); + return true; +} + +static void pubkey_to_string(char *const out, size_t const out_size, cx_ecfp_public_key_t const *const pubkey) { + check_null(out); + check_null(pubkey); + pubkey_to_pkh_string(out, out_size, G.curve, pubkey); +} + +#define SET_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) + +__attribute__((noreturn)) static void prompt_setup( + ui_callback_t const ok_cb, + ui_callback_t const cxl_cb) +{ + static const size_t TYPE_INDEX = 0; + static const size_t ADDRESS_INDEX = 1; + static const size_t CHAIN_INDEX = 2; + static const size_t MAIN_HWM_INDEX = 3; + static const size_t TEST_HWM_INDEX = 4; + + static const char *const prompts[] = { + PROMPT("Setup"), + PROMPT("Address"), + PROMPT("Chain"), + PROMPT("Main Chain HWM"), + PROMPT("Test Chain HWM"), + NULL, + }; + + SET_STATIC_UI_VALUE(TYPE_INDEX, "Baking?"); + register_ui_callback(ADDRESS_INDEX, pubkey_to_string, &G.public_key); + register_ui_callback(CHAIN_INDEX, chain_id_to_string, &G.main_chain_id); + register_ui_callback(MAIN_HWM_INDEX, number_to_string_indirect32, &G.hwm.main); + register_ui_callback(TEST_HWM_INDEX, number_to_string_indirect32, &G.hwm.test); + + ui_prompt(prompts, NULL, ok_cb, cxl_cb); +} + +__attribute__((noreturn)) unsigned int handle_apdu_setup(__attribute__((unused)) uint8_t instruction) { + if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); + + uint32_t const buff_size = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]); + if (buff_size < sizeof(struct setup_wire)) THROW(EXC_WRONG_LENGTH_FOR_INS); + + uint8_t const curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_CURVE]); + G.curve = curve_code_to_curve(curve_code); + + { + struct setup_wire const *const buff_as_setup = (struct setup_wire const *)&G_io_apdu_buffer[OFFSET_CDATA]; + + size_t consumed = 0; + G.main_chain_id.v = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->main_chain_id); + G.hwm.main = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->hwm.main); + G.hwm.test = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->hwm.test); + consumed += read_bip32_path(&G.bip32_path, (uint8_t const *)&buff_as_setup->bip32_path, buff_size - consumed); + + if (consumed != buff_size) THROW(EXC_WRONG_LENGTH); + } + + struct key_pair *const pair = generate_key_pair(G.curve, &G.bip32_path); + memset(&pair->private_key, 0, sizeof(pair->private_key)); + memcpy(&G.public_key, &pair->public_key, sizeof(G.public_key)); + + prompt_setup(ok, delay_reject); +} diff --git a/src/apdu_setup.h b/src/apdu_setup.h new file mode 100644 index 00000000..a61beb1b --- /dev/null +++ b/src/apdu_setup.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +unsigned int handle_apdu_setup(uint8_t instruction); diff --git a/src/baking_auth.c b/src/baking_auth.c index 1b829fe5..bac39f22 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -16,34 +16,38 @@ bool is_valid_level(level_t lvl) { return !(lvl & 0xC0000000); } -void write_highest_level(level_t lvl, bool is_endorsement) { - if (!is_valid_level(lvl)) return; - memcpy(&global.baking_auth.new_data, &N_data, sizeof(global.baking_auth.new_data)); - global.baking_auth.new_data.highest_level = lvl; - global.baking_auth.new_data.had_endorsement = is_endorsement; - nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); - change_idle_display(N_data.highest_level); +static void write_high_watermark(parsed_baking_data_t const *const in) { + check_null(in); + if (!is_valid_level(in->level)) THROW(EXC_WRONG_VALUES); + UPDATE_NVRAM(ram, { + // If the chain matches the main chain *or* the main chain is not set, then use 'main' HWM. + high_watermark_t *const dest = select_hwm_by_chain(in->chain_id, ram); + dest->highest_level = in->level; + dest->had_endorsement = in->is_endorsement; + }); + change_idle_display(N_data.hwm.main.highest_level); } void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path) { check_null(bip32_path); if (bip32_path->length > NUM_ELEMENTS(N_data.bip32_path.components) || bip32_path->length == 0) return; - global.baking_auth.new_data.highest_level = N_data.highest_level; - global.baking_auth.new_data.had_endorsement = N_data.had_endorsement; - global.baking_auth.new_data.curve = curve; - copy_bip32_path(&global.baking_auth.new_data.bip32_path, bip32_path); - nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); - change_idle_display(N_data.highest_level); + UPDATE_NVRAM(ram, { + ram->curve = curve; + copy_bip32_path(&ram->bip32_path, bip32_path); + }); + change_idle_display(N_data.hwm.main.highest_level); } -bool is_level_authorized(level_t level, bool is_endorsement) { - if (!is_valid_level(level)) return false; - if (level > N_data.highest_level) return true; +static bool is_level_authorized(parsed_baking_data_t const *const baking_info) { + check_null(baking_info); + if (!is_valid_level(baking_info->level)) return false; + high_watermark_t const *const hwm = select_hwm_by_chain(baking_info->chain_id, &N_data); + return baking_info->level > hwm->highest_level - // Levels are tied. In order for this to be OK, this must be an endorsement, and we must not - // have previously seen an endorsement. - return is_endorsement && !N_data.had_endorsement; + // Levels are tied. In order for this to be OK, this must be an endorsement, and we must not + // have previously seen an endorsement. + || (baking_info->is_endorsement && !hwm->had_endorsement); } bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path) { @@ -55,20 +59,22 @@ bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path) } void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path) { + check_null(data); + check_null(bip32_path); if (!is_path_authorized(curve, bip32_path)) THROW(EXC_SECURITY); - struct parsed_baking_data baking_info; - if (!parse_baking_data(data, datalen, &baking_info)) THROW(EXC_PARSE_ERROR); - if (!is_level_authorized(baking_info.level, baking_info.is_endorsement)) THROW(EXC_WRONG_VALUES); + parsed_baking_data_t baking_info; + if (!parse_baking_data(&baking_info, data, datalen)) THROW(EXC_PARSE_ERROR); + if (!is_level_authorized(&baking_info)) THROW(EXC_WRONG_VALUES); } void update_high_water_mark(void *data, int datalen) { - struct parsed_baking_data baking_info; - if (!parse_baking_data(data, datalen, &baking_info)) { + check_null(data); + parsed_baking_data_t baking_info; + if (!parse_baking_data(&baking_info, data, datalen)) { return; // Must be signing a delegation } - - write_highest_level(baking_info.level, baking_info.is_endorsement); + write_high_watermark(&baking_info); } void update_auth_text(void) { @@ -140,36 +146,36 @@ void prompt_address( #endif } -struct __attribute__((packed)) block { - char magic_byte; +struct block_wire { + uint8_t magic_byte; uint32_t chain_id; - level_t level; + uint32_t level; uint8_t proto; // ... beyond this we don't care -}; +} __attribute__((packed)); -struct __attribute__((packed)) endorsement { +struct endorsement_wire { uint8_t magic_byte; uint32_t chain_id; uint8_t branch[32]; uint8_t tag; uint32_t level; -}; +} __attribute__((packed)); -bool parse_baking_data(const void *data, size_t length, struct parsed_baking_data *out) { +bool parse_baking_data(parsed_baking_data_t *const out, void const *const data, size_t const length) { switch (get_magic_byte(data, length)) { case MAGIC_BYTE_BAKING_OP: - if (length != sizeof(struct endorsement)) return false; - const struct endorsement *endorsement = data; - // TODO: Check chain ID + if (length != sizeof(struct endorsement_wire)) return false; + struct endorsement_wire const *const endorsement = data; out->is_endorsement = true; - out->level = READ_UNALIGNED_BIG_ENDIAN(level_t, &endorsement->level); + out->chain_id.v = READ_UNALIGNED_BIG_ENDIAN(uint32_t, &endorsement->chain_id); + out->level = READ_UNALIGNED_BIG_ENDIAN(uint32_t, &endorsement->level); return true; case MAGIC_BYTE_BLOCK: - if (length < sizeof(struct block)) return false; - // TODO: Check chain ID + if (length < sizeof(struct block_wire)) return false; + struct block_wire const *const block = data; out->is_endorsement = false; - const struct block *block = data; + out->chain_id.v = READ_UNALIGNED_BIG_ENDIAN(uint32_t, &block->chain_id); out->level = READ_UNALIGNED_BIG_ENDIAN(level_t, &block->level); return true; case MAGIC_BYTE_INVALID: diff --git a/src/baking_auth.h b/src/baking_auth.h index bbbfea59..438567ac 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -3,6 +3,7 @@ #include "apdu.h" #include "operations.h" #include "protocol.h" +#include "types.h" #include #include @@ -11,8 +12,6 @@ void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path); void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path); bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path); void update_high_water_mark(void *data, int datalen); -void write_highest_level(level_t level, bool is_endorsement); -bool is_level_authorized(level_t level, bool is_endorsement); bool is_valid_level(level_t level); void update_auth_text(void); @@ -21,9 +20,10 @@ void prompt_address(bool bake, cx_curve_t curve, const cx_ecfp_public_key_t *key, ui_callback_t ok_cb, ui_callback_t cxl_cb) __attribute__((noreturn)); -struct parsed_baking_data { +typedef struct { + chain_id_t chain_id; bool is_endorsement; level_t level; -}; +} parsed_baking_data_t; // Return false if it is invalid -bool parse_baking_data(const void *data, size_t length, struct parsed_baking_data *out); +bool parse_baking_data(parsed_baking_data_t *const out, void const *const data, size_t const length); diff --git a/src/globals.h b/src/globals.h index 77f7e7a7..e2e227d6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -22,6 +22,17 @@ struct priv_generate_key_pair { struct key_pair res; }; +struct apdu_setup_globals { + bip32_path_t bip32_path; + cx_curve_t curve; + cx_ecfp_public_key_t public_key; + chain_id_t main_chain_id; + struct { + level_t main; + level_t test; + } hwm; +}; + typedef struct { void *stack_root; apdu_handler handlers[INS_MAX]; @@ -48,6 +59,8 @@ typedef struct { uint8_t magic_number; bool hash_only; } sign; + + struct apdu_setup_globals setup; } u; struct { @@ -108,6 +121,22 @@ extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? #define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) +// Properly updates NVRAM data to prevent any clobbering of data. +// 'out_param' defines the name of a pointer to the nvram_data struct +// that 'body' can change to apply updates. +#define UPDATE_NVRAM(out_name, body) ({ \ + nvram_data *const out_name = &global.baking_auth.new_data; \ + memcpy(&global.baking_auth.new_data, &N_data, sizeof(global.baking_auth.new_data)); \ + body; \ + nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); \ +}) + +static inline high_watermark_t *select_hwm_by_chain(chain_id_t const chain_id, nvram_data *const ram) { + check_null(ram); + return chain_id.v == ram->main_chain_id.v || ram->main_chain_id.v == 0 + ? &ram->hwm.main + : &ram->hwm.test; +} static inline void throw_stack_size() { uint8_t st; diff --git a/src/main.c b/src/main.c index 73a95d08..1b20ba51 100644 --- a/src/main.c +++ b/src/main.c @@ -17,6 +17,7 @@ #include "apdu_baking.h" #include "apdu_pubkey.h" +#include "apdu_setup.h" #include "apdu_sign.h" #include "apdu.h" #include "globals.h" @@ -31,16 +32,16 @@ void app_main(void) { global.handlers[APDU_INS(INS_VERSION)] = handle_apdu_version; global.handlers[APDU_INS(INS_GET_PUBLIC_KEY)] = handle_apdu_get_public_key; global.handlers[APDU_INS(INS_PROMPT_PUBLIC_KEY)] = handle_apdu_get_public_key; + global.handlers[APDU_INS(INS_SIGN)] = handle_apdu_sign; + global.handlers[APDU_INS(INS_GIT)] = handle_apdu_git; #ifdef BAKING_APP global.handlers[APDU_INS(INS_AUTHORIZE_BAKING)] = handle_apdu_get_public_key; global.handlers[APDU_INS(INS_RESET)] = handle_apdu_reset; global.handlers[APDU_INS(INS_QUERY_AUTH_KEY)] = handle_apdu_query_auth_key; global.handlers[APDU_INS(INS_QUERY_HWM)] = handle_apdu_hwm; -#endif - global.handlers[APDU_INS(INS_SIGN)] = handle_apdu_sign; -#ifndef BAKING_APP + global.handlers[APDU_INS(INS_SETUP)] = handle_apdu_setup; +#else global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif - global.handlers[APDU_INS(INS_GIT)] = handle_apdu_git; main_loop(global.handlers); } diff --git a/src/to_string.c b/src/to_string.c index 7673657a..f40fb856 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -8,6 +8,8 @@ #define NO_CONTRACT_STRING "None" +#define TEZOS_HASH_CHECKSUM_SIZE 4 + static void pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]); // These functions output terminating null bytes, and return the ending offset. @@ -20,31 +22,35 @@ void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct pars return; } - cx_curve_t curve; - if (contract->originated != 0) { - curve = CX_CURVE_NONE; - } else { - curve = curve_code_to_curve(contract->curve_code); - } + cx_curve_t const curve = contract->originated != 0 + ? CX_CURVE_NONE + : curve_code_to_curve(contract->curve_code); pkh_to_string(buff, buff_size, curve, contract->hash); } void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, - const cx_ecfp_public_key_t *public_key) { + const cx_ecfp_public_key_t *public_key) { uint8_t hash[HASH_SIZE]; public_key_hash(hash, curve, public_key); pkh_to_string(buff, buff_size, curve, hash); } +void compute_hash_checksum(uint8_t out[TEZOS_HASH_CHECKSUM_SIZE], void const *const data, size_t size) { + uint8_t checksum[32]; + cx_hash_sha256(data, size, checksum, sizeof(checksum)); + cx_hash_sha256(checksum, sizeof(checksum), checksum, sizeof(checksum)); + memcpy(out, checksum, TEZOS_HASH_CHECKSUM_SIZE); +} + void pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, const uint8_t hash[HASH_SIZE]) { if (buff_size < PKH_STRING_SIZE) THROW(EXC_WRONG_LENGTH); // Data to encode struct __attribute__((packed)) { - char prefix[3]; + uint8_t prefix[3]; uint8_t hash[HASH_SIZE]; - char checksum[4]; + uint8_t checksum[TEZOS_HASH_CHECKSUM_SIZE]; } data; // prefix @@ -75,12 +81,7 @@ void pkh_to_string(char *buff, const size_t buff_size, const cx_curve_t curve, // hash memcpy(data.hash, hash, sizeof(data.hash)); - - // checksum -- twice because them's the rules - uint8_t checksum[32]; - cx_hash_sha256((void*)&data, sizeof(data) - sizeof(data.checksum), checksum, sizeof(checksum)); - cx_hash_sha256(checksum, sizeof(checksum), checksum, sizeof(checksum)); - memcpy(data.checksum, checksum, sizeof(data.checksum)); + compute_hash_checksum(data.checksum, &data, sizeof(data) - sizeof(data.checksum)); size_t out_size = buff_size; if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); @@ -91,24 +92,36 @@ void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t h // Data to encode struct __attribute__((packed)) { - char prefix[2]; + uint8_t prefix[2]; uint8_t hash[PROTOCOL_HASH_SIZE]; - char checksum[4]; + uint8_t checksum[TEZOS_HASH_CHECKSUM_SIZE]; } data = { .prefix = {2, 170} }; memcpy(data.hash, hash, sizeof(data.hash)); + compute_hash_checksum(data.checksum, &data, sizeof(data) - sizeof(data.checksum)); - // checksum -- twice because them's the rules - // Hash the input (prefix + hash) - // Hash that hash. - // Take the first 4 bytes of that - // Voila: a checksum. - uint8_t checksum[32]; - cx_hash_sha256((void*)&data, sizeof(data) - sizeof(data.checksum), checksum, sizeof(checksum)); - cx_hash_sha256(checksum, sizeof(checksum), checksum, sizeof(checksum)); - memcpy(data.checksum, checksum, sizeof(data.checksum)); + size_t out_size = buff_size; + if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); +} + +void chain_id_to_string(char *const buff, size_t const buff_size, chain_id_t const chain_id) { + if (buff_size < CHAIN_ID_BASE58_STRING_SIZE) THROW(EXC_WRONG_LENGTH); + + // Data to encode + struct __attribute__((packed)) { + uint8_t prefix[3]; + int32_t chain_id; + uint8_t checksum[TEZOS_HASH_CHECKSUM_SIZE]; + } data = { + .prefix = {87, 82, 0}, + + // Must hash big-endian data so treating little endian as big endian just flips + .chain_id = READ_UNALIGNED_BIG_ENDIAN(uint32_t, &chain_id.v) + }; + + compute_hash_checksum(data.checksum, &data, sizeof(data) - sizeof(data.checksum)); size_t out_size = buff_size; if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); diff --git a/src/to_string.h b/src/to_string.h index 3eb5516d..58541a09 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -6,12 +6,14 @@ #include "keys.h" #include "operations.h" #include "os_cx.h" +#include "types.h" #include "ui.h" void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, const cx_ecfp_public_key_t *public_key); void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); +void chain_id_to_string(char *buff, size_t const buff_size, chain_id_t const chain_id); #define MAX_INT_DIGITS 20 // dest must be at least MAX_INT_DIGITS diff --git a/src/types.h b/src/types.h index dedb1904..13b22cc2 100644 --- a/src/types.h +++ b/src/types.h @@ -11,6 +11,15 @@ typedef uint32_t (*apdu_handler)(uint8_t instruction); typedef uint32_t level_t; +#define CHAIN_ID_BASE58_STRING_SIZE (15 + 1) // with null termination + +typedef struct { + uint32_t v; +} chain_id_t; + +// Mainnet Chain ID: NetXdQprcVkpaWU +static chain_id_t const mainnet_chain_id = { .v = 0x7A06A770 }; + // UI typedef bool (*ui_callback_t)(void); // return true to go back to idle screen @@ -47,9 +56,17 @@ static inline bool bip32_paths_eq(bip32_path_t const *const a, bip32_path_t cons } typedef struct { - cx_curve_t curve; level_t highest_level; bool had_endorsement; +} high_watermark_t; + +typedef struct { + chain_id_t main_chain_id; + struct { + high_watermark_t main; + high_watermark_t test; + } hwm; + cx_curve_t curve; bip32_path_t bip32_path; } nvram_data; diff --git a/src/ui.c b/src/ui.c index 0326ed4e..bea319f6 100644 --- a/src/ui.c +++ b/src/ui.c @@ -114,7 +114,7 @@ void change_idle_display(uint32_t new) { void ui_initial_screen(void) { #ifdef BAKING_APP - change_idle_display(N_data.highest_level); + change_idle_display(N_data.hwm.main.highest_level); #endif clear_ui_callbacks(); ui_idle(); From 544523e8d8b3f6fd7e02838964ddfeb5e3fe75a8 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 50/92] Use G for globals in apdu_pubkey.c --- src/apdu_pubkey.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index b18eea46..76e01e8a 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -9,13 +9,15 @@ #include +#define G global.u.pubkey + static uint32_t provide_pubkey(void) { uint32_t tx = 0; - G_io_apdu_buffer[tx++] = global.u.pubkey.public_key.W_len; + G_io_apdu_buffer[tx++] = G.public_key.W_len; os_memmove(G_io_apdu_buffer + tx, - global.u.pubkey.public_key.W, - global.u.pubkey.public_key.W_len); - tx += global.u.pubkey.public_key.W_len; + G.public_key.W, + G.public_key.W_len); + tx += G.public_key.W_len; G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; return tx; @@ -29,7 +31,7 @@ static bool pubkey_ok(void) { #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(global.u.pubkey.curve, &global.u.pubkey.bip32_path); + authorize_baking(G.curve, &G.bip32_path); pubkey_ok(); return true; } @@ -47,24 +49,24 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { require_hid(); } - global.u.pubkey.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + G.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); #ifdef BAKING_APP if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { - global.u.pubkey.curve = N_data.curve; - copy_bip32_path(&global.u.pubkey.bip32_path, &N_data.bip32_path); + G.curve = N_data.curve; + copy_bip32_path(&G.bip32_path, &N_data.bip32_path); } else { #endif - read_bip32_path(&global.u.pubkey.bip32_path, dataBuffer, G_io_apdu_buffer[OFFSET_LC]); + read_bip32_path(&G.bip32_path, dataBuffer, G_io_apdu_buffer[OFFSET_LC]); #ifdef BAKING_APP - if (global.u.pubkey.bip32_path.length == 0) { + if (G.bip32_path.length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } } #endif - struct key_pair *pair = generate_key_pair(global.u.pubkey.curve, &global.u.pubkey.bip32_path); + struct key_pair *pair = generate_key_pair(G.curve, &G.bip32_path); memset(&pair->private_key, 0, sizeof(pair->private_key)); - memcpy(&global.u.pubkey.public_key, &pair->public_key, sizeof(global.u.pubkey.public_key)); + memcpy(&G.public_key, &pair->public_key, sizeof(G.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { return provide_pubkey(); @@ -84,6 +86,6 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP } #endif - prompt_address(bake, global.u.pubkey.curve, &global.u.pubkey.public_key, cb, delay_reject); + prompt_address(bake, G.curve, &G.public_key, cb, delay_reject); } } From 516c2a2c785ae79751822cce93f78ea89903c3fd Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 51/92] Use G for globals in apdu_baking.c --- src/apdu_baking.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index c1cb7497..7b55e315 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -10,6 +10,8 @@ #include +#define G global.u.baking + static bool reset_ok(void); unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { @@ -21,9 +23,9 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { level_t const lvl = READ_UNALIGNED_BIG_ENDIAN(level_t, dataBuffer); if (!is_valid_level(lvl)) THROW(EXC_PARSE_ERROR); - global.u.baking.reset_level = lvl; + G.reset_level = lvl; - register_ui_callback(0, number_to_string_indirect32, &global.u.baking.reset_level); + register_ui_callback(0, number_to_string_indirect32, &G.reset_level); static const char *const reset_prompts[] = { PROMPT("Reset HWM"), @@ -34,9 +36,9 @@ unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { bool reset_ok(void) { UPDATE_NVRAM(ram, { - ram->hwm.main.highest_level = global.u.baking.reset_level; + ram->hwm.main.highest_level = G.reset_level; ram->hwm.main.had_endorsement = false; - ram->hwm.test.highest_level = global.u.baking.reset_level; + ram->hwm.test.highest_level = G.reset_level; ram->hwm.test.had_endorsement = false; }); From 665a3f22bac5945aced40c7fbec8a5d5a5192703 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 52/92] Factor out provide_pubkey --- src/apdu.c | 10 ++++++++++ src/apdu.h | 2 ++ src/apdu_pubkey.c | 17 ++--------------- src/apdu_setup.c | 11 ----------- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 31f3e4d3..1af9a409 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -5,6 +5,16 @@ #include #include +size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *const pubkey) { + size_t tx = 0; + io_buffer[tx++] = pubkey->W_len; + memmove(io_buffer + tx, pubkey->W, pubkey->W_len); + tx += pubkey->W_len; + io_buffer[tx++] = 0x90; + io_buffer[tx++] = 0x00; + return tx; +} + unsigned int handle_apdu_error(uint8_t __attribute__((unused)) instruction) { THROW(EXC_INVALID_INS); } diff --git a/src/apdu.h b/src/apdu.h index bd3f65a5..dc6c859a 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -59,6 +59,8 @@ static inline void require_hid(void) { } } +size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *const pubkey); + uint32_t handle_apdu_error(uint8_t instruction); uint32_t handle_apdu_version(uint8_t instruction); uint32_t handle_apdu_git(uint8_t instruction); diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 76e01e8a..f2bb19fe 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -11,21 +11,8 @@ #define G global.u.pubkey -static uint32_t provide_pubkey(void) { - uint32_t tx = 0; - G_io_apdu_buffer[tx++] = G.public_key.W_len; - os_memmove(G_io_apdu_buffer + tx, - G.public_key.W, - G.public_key.W_len); - tx += G.public_key.W_len; - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; -} - static bool pubkey_ok(void) { - uint32_t const tx = provide_pubkey(); - delayed_send(tx); + delayed_send(provide_pubkey(G_io_apdu_buffer, &G.public_key)); return true; } @@ -69,7 +56,7 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { memcpy(&G.public_key, &pair->public_key, sizeof(G.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { - return provide_pubkey(); + return provide_pubkey(G_io_apdu_buffer, &G.public_key); } else { // instruction == INS_PROMPT_PUBLIC_KEY || instruction == INS_AUTHORIZE_BAKING ui_callback_t cb; diff --git a/src/apdu_setup.c b/src/apdu_setup.c index d944dad8..5940edfd 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -21,17 +21,6 @@ struct setup_wire { struct bip32_path_wire bip32_path; } __attribute__((packed)); - -static size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *const pubkey) { - size_t tx = 0; - io_buffer[tx++] = pubkey->W_len; - memmove(io_buffer + tx, pubkey->W, pubkey->W_len); - tx += pubkey->W_len; - io_buffer[tx++] = 0x90; - io_buffer[tx++] = 0x00; - return tx; -} - static bool ok(void) { UPDATE_NVRAM(ram, { ram->curve = G.curve; From e1f875b40e4542af1fd0d59c7a16e56ee7cec597 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 53/92] Use safer abstraction for pubkey generation --- src/apdu_pubkey.c | 26 +++++++++++--------------- src/apdu_setup.c | 5 ++--- src/apdu_sign.c | 1 + src/baking_auth.c | 5 ++--- src/keys.c | 13 +++++++++++-- src/keys.h | 6 ++++-- src/operations.c | 6 +++--- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index f2bb19fe..4109cc21 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -27,33 +27,29 @@ static bool baking_ok(void) { unsigned int handle_apdu_get_public_key(uint8_t instruction) { uint8_t *dataBuffer = G_io_apdu_buffer + OFFSET_CDATA; - if (G_io_apdu_buffer[OFFSET_P1] != 0) { - THROW(EXC_WRONG_PARAM); - } + if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); // do not expose pks without prompt over U2F (browser support) - if (instruction == INS_GET_PUBLIC_KEY) { - require_hid(); - } + if (instruction == INS_GET_PUBLIC_KEY) require_hid(); - G.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + uint8_t const curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_CURVE]); + G.curve = curve_code_to_curve(curve_code); + + size_t const cdata_size = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]); #ifdef BAKING_APP - if (G_io_apdu_buffer[OFFSET_LC] == 0 && instruction == INS_AUTHORIZE_BAKING) { + if (cdata_size == 0 && instruction == INS_AUTHORIZE_BAKING) { G.curve = N_data.curve; copy_bip32_path(&G.bip32_path, &N_data.bip32_path); } else { #endif - read_bip32_path(&G.bip32_path, dataBuffer, G_io_apdu_buffer[OFFSET_LC]); + read_bip32_path(&G.bip32_path, dataBuffer, cdata_size); #ifdef BAKING_APP - if (G.bip32_path.length == 0) { - THROW(EXC_WRONG_LENGTH_FOR_INS); - } + if (G.bip32_path.length == 0) THROW(EXC_WRONG_LENGTH_FOR_INS); } #endif - struct key_pair *pair = generate_key_pair(G.curve, &G.bip32_path); - memset(&pair->private_key, 0, sizeof(pair->private_key)); - memcpy(&G.public_key, &pair->public_key, sizeof(G.public_key)); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.curve, &G.bip32_path); + memcpy(&G.public_key, pubkey, sizeof(G.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { return provide_pubkey(G_io_apdu_buffer, &G.public_key); diff --git a/src/apdu_setup.c b/src/apdu_setup.c index 5940edfd..128b395f 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -92,9 +92,8 @@ __attribute__((noreturn)) unsigned int handle_apdu_setup(__attribute__((unused)) if (consumed != buff_size) THROW(EXC_WRONG_LENGTH); } - struct key_pair *const pair = generate_key_pair(G.curve, &G.bip32_path); - memset(&pair->private_key, 0, sizeof(pair->private_key)); - memcpy(&G.public_key, &pair->public_key, sizeof(G.public_key)); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.curve, &G.bip32_path); + memcpy(&G.public_key, pubkey, sizeof(G.public_key)); prompt_setup(ok, delay_reject); } diff --git a/src/apdu_sign.c b/src/apdu_sign.c index f683b679..4805da39 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -147,6 +147,7 @@ const char *const insecure_values[] = { static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve, bip32_path_t const *const bip32_path, ui_callback_t ok, ui_callback_t cxl) { + check_null(data); check_null(bip32_path); struct parsed_operation_group *ops; diff --git a/src/baking_auth.c b/src/baking_auth.c index bac39f22..c7e9061d 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -81,11 +81,10 @@ void update_auth_text(void) { if (N_data.bip32_path.length == 0) { strcpy(global.ui.baking_auth_text, "No Key Authorized"); } else { - struct key_pair *pair = generate_key_pair(N_data.curve, &N_data.bip32_path); - memset(&pair->private_key, 0, sizeof(pair->private_key)); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); pubkey_to_pkh_string( global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), - N_data.curve, &pair->public_key); + N_data.curve, pubkey); } } diff --git a/src/keys.c b/src/keys.c index a0b2e387..c7f97da2 100644 --- a/src/keys.c +++ b/src/keys.c @@ -74,8 +74,17 @@ struct key_pair *generate_key_pair(cx_curve_t const curve, bip32_path_t const *c return &priv->res; } -cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, - const cx_ecfp_public_key_t *restrict public_key) { +cx_ecfp_public_key_t const *generate_public_key(cx_curve_t const curve, bip32_path_t const *const bip32_path) { + check_null(bip32_path); + struct key_pair *const pair = generate_key_pair(curve, bip32_path); + memset(&pair->private_key, 0, sizeof(pair->private_key)); + return &pair->public_key; +} + +cx_ecfp_public_key_t const *public_key_hash( + uint8_t output[HASH_SIZE], cx_curve_t curve, + cx_ecfp_public_key_t const *const restrict public_key) +{ cx_ecfp_public_key_t *const compressed = &global.priv.public_key_hash.compressed; switch (curve) { case CX_CURVE_Ed25519: diff --git a/src/keys.h b/src/keys.h index 5e3e839a..609c8c60 100644 --- a/src/keys.h +++ b/src/keys.h @@ -17,9 +17,11 @@ struct bip32_path_wire { size_t read_bip32_path(bip32_path_t *const out, uint8_t const *const in, size_t const in_size); struct key_pair *generate_key_pair(cx_curve_t const curve, bip32_path_t const *const bip32_path); +cx_ecfp_public_key_t const *generate_public_key(cx_curve_t const curve, bip32_path_t const *const bip32_path); -cx_ecfp_public_key_t *public_key_hash(uint8_t output[HASH_SIZE], cx_curve_t curve, - const cx_ecfp_public_key_t *restrict public_key); +cx_ecfp_public_key_t const *public_key_hash( + uint8_t output[HASH_SIZE], cx_curve_t curve, + cx_ecfp_public_key_t const *const restrict public_key); enum curve_code { TEZOS_ED, diff --git a/src/operations.c b/src/operations.c index 072a672b..e7feeb7e 100644 --- a/src/operations.c +++ b/src/operations.c @@ -108,9 +108,9 @@ static inline uint64_t parse_z(const void *data, size_t *ix, size_t length, uint static inline void compute_pkh(cx_curve_t curve, bip32_path_t const *const bip32_path, struct parsed_operation_group *const out) { check_null(bip32_path); - memset(&pair->private_key, 0, sizeof(pair->private_key)); - - cx_ecfp_public_key_t *key = public_key_hash(out->signing.hash, curve, &pair->public_key); + check_null(out); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(curve, bip32_path); + cx_ecfp_public_key_t const *const key = public_key_hash(out->signing.hash, curve, pubkey); memcpy(&out->public_key, key, sizeof(out->public_key)); out->signing.curve_code = curve_to_curve_code(curve); out->signing.originated = 0; From f11b103a1fd1712a566fa9eda810fa6a30a7135c Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 54/92] Add new APDU for querying both HWMs and chain ID --- src/apdu.h | 3 ++- src/apdu_baking.c | 16 ++++++++++++---- src/apdu_baking.h | 3 ++- src/main.c | 3 ++- src/types.h | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index dc6c859a..637b45b8 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -30,9 +30,10 @@ #define INS_SIGN_UNSAFE 0x05 // Data that is already hashed. #define INS_RESET 0x06 #define INS_QUERY_AUTH_KEY 0x07 -#define INS_QUERY_HWM 0x08 +#define INS_QUERY_MAIN_HWM 0x08 #define INS_GIT 0x09 #define INS_SETUP 0x0A +#define INS_QUERY_ALL_HWM 0x0B __attribute__((noreturn)) void main_loop(apdu_handler handlers[INS_MAX]); diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 7b55e315..480d0864 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -65,17 +65,25 @@ uint32_t send_word_big_endian(uint32_t tx, uint32_t word) { return tx + i; } -unsigned int handle_apdu_hwm(__attribute__((unused)) uint8_t instruction) { +unsigned int handle_apdu_all_hwm(__attribute__((unused)) uint8_t instruction) { uint32_t tx = 0; + tx = send_word_big_endian(tx, N_data.hwm.main.highest_level); + tx = send_word_big_endian(tx, N_data.hwm.test.highest_level); + tx = send_word_big_endian(tx, N_data.main_chain_id.v); + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + return tx; +} - level_t level = N_data.hwm.main.highest_level; - tx = send_word_big_endian(tx, level); - +unsigned int handle_apdu_main_hwm(__attribute__((unused)) uint8_t instruction) { + uint32_t tx = 0; + tx = send_word_big_endian(tx, N_data.hwm.main.highest_level); G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; return tx; } + unsigned int handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { uint8_t const length = N_data.bip32_path.length; diff --git a/src/apdu_baking.h b/src/apdu_baking.h index b32e8fcd..632d8690 100644 --- a/src/apdu_baking.h +++ b/src/apdu_baking.h @@ -4,4 +4,5 @@ unsigned int handle_apdu_reset(uint8_t instruction); unsigned int handle_apdu_query_auth_key(uint8_t instruction); -unsigned int handle_apdu_hwm(uint8_t instruction); +unsigned int handle_apdu_main_hwm(uint8_t instruction); +unsigned int handle_apdu_all_hwm(uint8_t instruction); diff --git a/src/main.c b/src/main.c index 1b20ba51..38d5780f 100644 --- a/src/main.c +++ b/src/main.c @@ -38,8 +38,9 @@ void app_main(void) { global.handlers[APDU_INS(INS_AUTHORIZE_BAKING)] = handle_apdu_get_public_key; global.handlers[APDU_INS(INS_RESET)] = handle_apdu_reset; global.handlers[APDU_INS(INS_QUERY_AUTH_KEY)] = handle_apdu_query_auth_key; - global.handlers[APDU_INS(INS_QUERY_HWM)] = handle_apdu_hwm; + global.handlers[APDU_INS(INS_QUERY_MAIN_HWM)] = handle_apdu_main_hwm; global.handlers[APDU_INS(INS_SETUP)] = handle_apdu_setup; + global.handlers[APDU_INS(INS_QUERY_ALL_HWM)] = handle_apdu_all_hwm; #else global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif diff --git a/src/types.h b/src/types.h index 13b22cc2..3db418c9 100644 --- a/src/types.h +++ b/src/types.h @@ -159,7 +159,7 @@ struct parsed_operation_group { }; // Maximum number of APDU instructions -#define INS_MAX 0x0B +#define INS_MAX 0x0C #define APDU_INS(x) ({ \ _Static_assert(x <= INS_MAX, "APDU instruction is out of bounds"); \ From ee3639ea68bb418c01780741fe0e5fd45b17fde2 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 55/92] Fix HWM updates on baking idle screens Refactor the idle screen updates to all occur in the same place by the same code. --- src/baking_auth.c | 13 ------------- src/baking_auth.h | 1 - src/ui.c | 24 ++++++++++++++++-------- src/ui.h | 1 - 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/baking_auth.c b/src/baking_auth.c index c7e9061d..02ac20e3 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -25,7 +25,6 @@ static void write_high_watermark(parsed_baking_data_t const *const in) { dest->highest_level = in->level; dest->had_endorsement = in->is_endorsement; }); - change_idle_display(N_data.hwm.main.highest_level); } void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path) { @@ -36,7 +35,6 @@ void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path) { ram->curve = curve; copy_bip32_path(&ram->bip32_path, bip32_path); }); - change_idle_display(N_data.hwm.main.highest_level); } static bool is_level_authorized(parsed_baking_data_t const *const baking_info) { @@ -77,17 +75,6 @@ void update_high_water_mark(void *data, int datalen) { write_high_watermark(&baking_info); } -void update_auth_text(void) { - if (N_data.bip32_path.length == 0) { - strcpy(global.ui.baking_auth_text, "No Key Authorized"); - } else { - cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); - pubkey_to_pkh_string( - global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), - N_data.curve, pubkey); - } -} - static const char *const pubkey_values[] = { "Public Key", global.baking_auth.address_display_data, diff --git a/src/baking_auth.h b/src/baking_auth.h index 438567ac..1a57bb2f 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -13,7 +13,6 @@ void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_pa bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path); void update_high_water_mark(void *data, int datalen); bool is_valid_level(level_t level); -void update_auth_text(void); void prompt_contract_for_baking(struct parsed_contract *contract, ui_callback_t ok_cb, ui_callback_t cxl_cb); void prompt_address(bool bake, cx_curve_t curve, diff --git a/src/ui.c b/src/ui.c index bea319f6..dac1a491 100644 --- a/src/ui.c +++ b/src/ui.c @@ -96,9 +96,22 @@ static bool do_nothing(void) { } #endif +static void update_baking_idle_screens(void) { + number_to_string(global.ui.idle_text, N_data.hwm.main.highest_level); + + if (N_data.bip32_path.length == 0) { + strcpy(global.ui.baking_auth_text, "No Key Authorized"); + } else { + cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); + pubkey_to_pkh_string( + global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), + N_data.curve, pubkey); + } +} + static void ui_idle(void) { #ifdef BAKING_APP - update_auth_text(); + update_baking_idle_screens(); ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), do_nothing, exit_app, 2); #else @@ -107,14 +120,9 @@ static void ui_idle(void) { #endif } -void change_idle_display(uint32_t new) { - number_to_string(global.ui.idle_text, new); - update_auth_text(); -} - void ui_initial_screen(void) { #ifdef BAKING_APP - change_idle_display(N_data.hwm.main.highest_level); + update_baking_idle_screens(); #endif clear_ui_callbacks(); ui_idle(); @@ -127,7 +135,7 @@ static bool is_idling(void) { static void timeout(void) { if (is_idling()) { // Idle app timeout - update_auth_text(); + update_baking_idle_screens(); global.ui.timeout_cycle_count = 0; UX_REDISPLAY(); } else { diff --git a/src/ui.h b/src/ui.h index 6cbb9a8d..1bdeb5c5 100644 --- a/src/ui.h +++ b/src/ui.h @@ -18,4 +18,3 @@ void ui_display(const bagl_element_t *elems, size_t sz, ui_callback_t ok_c, ui_c uint32_t step_count); unsigned char io_event(unsigned char channel); void io_seproxyhal_display(const bagl_element_t *element); -void change_idle_display(uint32_t new); From 971970e0d2f3a826b362560ce33544859ab8f1d1 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 56/92] Use G for globals in ui.c --- src/ui.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/ui.c b/src/ui.c index dac1a491..85c118ce 100644 --- a/src/ui.c +++ b/src/ui.c @@ -12,6 +12,8 @@ #include #include +#define G global.ui + static unsigned button_handler(unsigned button_mask, unsigned button_mask_counter); #define PROMPT_CYCLES 3 @@ -62,7 +64,7 @@ static const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - global.ui.idle_text, + G.idle_text, 0, 0, 0, @@ -82,7 +84,7 @@ static const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - global.ui.baking_auth_text, + G.baking_auth_text, 0, 0, 0, @@ -97,14 +99,14 @@ static bool do_nothing(void) { #endif static void update_baking_idle_screens(void) { - number_to_string(global.ui.idle_text, N_data.hwm.main.highest_level); + number_to_string(G.idle_text, N_data.hwm.main.highest_level); if (N_data.bip32_path.length == 0) { - strcpy(global.ui.baking_auth_text, "No Key Authorized"); + strcpy(G.baking_auth_text, "No Key Authorized"); } else { cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); pubkey_to_pkh_string( - global.ui.baking_auth_text, sizeof(global.ui.baking_auth_text), + G.baking_auth_text, sizeof(G.baking_auth_text), N_data.curve, pubkey); } } @@ -115,7 +117,7 @@ static void ui_idle(void) { ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), do_nothing, exit_app, 2); #else - global.ui.cxl_callback = exit_app; + G.cxl_callback = exit_app; main_menu(); #endif } @@ -129,14 +131,14 @@ void ui_initial_screen(void) { } static bool is_idling(void) { - return global.ui.cxl_callback == exit_app; + return G.cxl_callback == exit_app; } static void timeout(void) { if (is_idling()) { // Idle app timeout update_baking_idle_screens(); - global.ui.timeout_cycle_count = 0; + G.timeout_cycle_count = 0; UX_REDISPLAY(); } else { // Prompt timeout -- simulate cancel button @@ -148,10 +150,10 @@ static unsigned button_handler(unsigned button_mask, __attribute__((unused)) uns ui_callback_t callback; switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: - callback = global.ui.cxl_callback; + callback = G.cxl_callback; break; case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - callback = global.ui.ok_callback; + callback = G.ok_callback; break; default: return 0; @@ -174,7 +176,7 @@ const bagl_element_t *prepro(const bagl_element_t *element) { min = 4000; } - if (global.ui.ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { + if (G.ux_step == element->component.userid - 1 || element->component.userid == BAGL_SCROLLING_ELEMENT) { // timeouts are in millis UX_CALLBACK_SET_INTERVAL(MAX(min, (pause_millis + bagl_label_roundtrip_duration_ms(element, 7)) / div)); @@ -187,11 +189,11 @@ const bagl_element_t *prepro(const bagl_element_t *element) { void ui_display(const bagl_element_t *elems, size_t sz, ui_callback_t ok_c, ui_callback_t cxl_c, uint32_t step_count) { // Adapted from definition of UX_DISPLAY in header file - global.ui.timeout_cycle_count = 0; - global.ui.ux_step = 0; - global.ui.ux_step_count = step_count; - global.ui.ok_callback = ok_c; - global.ui.cxl_callback = cxl_c; + G.timeout_cycle_count = 0; + G.ux_step = 0; + G.ux_step_count = step_count; + G.ok_callback = ok_c; + G.cxl_callback = cxl_c; if (!is_idling()) { switch_screen(0); } @@ -225,15 +227,15 @@ unsigned char io_event(__attribute__((unused)) unsigned char channel) { ux.callback_interval_ms -= MIN(ux.callback_interval_ms, 100); if (ux.callback_interval_ms == 0) { // prepare next screen - global.ui.ux_step = (global.ui.ux_step + 1) % global.ui.ux_step_count; + G.ux_step = (G.ux_step + 1) % G.ux_step_count; if (!is_idling()) { - switch_screen(global.ui.ux_step); + switch_screen(G.ux_step); } // check if we've timed out - if (global.ui.ux_step == 0) { - global.ui.timeout_cycle_count++; - if (global.ui.timeout_cycle_count == PROMPT_CYCLES) { + if (G.ux_step == 0) { + G.timeout_cycle_count++; + if (G.timeout_cycle_count == PROMPT_CYCLES) { timeout(); break; // timeout() will often display a new screen } From 6e90ed491bc1c46555cedbc5314e1a45f183df38 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 57/92] Show chain ID with alias on baking idle screens --- src/globals.h | 3 +++ src/types.h | 5 +++++ src/ui.c | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/globals.h b/src/globals.h index e2e227d6..ad225eb8 100644 --- a/src/globals.h +++ b/src/globals.h @@ -74,6 +74,9 @@ typedef struct { char idle_text[16]; char baking_auth_text[PKH_STRING_SIZE]; + struct { + char chain[CHAIN_ID_BASE58_STRING_SIZE]; + } baking_idle_screens; struct { string_generation_callback callbacks[MAX_SCREEN_COUNT]; diff --git a/src/types.h b/src/types.h index 3db418c9..5fa48d47 100644 --- a/src/types.h +++ b/src/types.h @@ -165,3 +165,8 @@ struct parsed_operation_group { _Static_assert(x <= INS_MAX, "APDU instruction is out of bounds"); \ x; \ }) + +#define STRCPY(buff, x) ({ \ + _Static_assert(sizeof(buff) >= sizeof(x) && sizeof(*x) == sizeof(char), "String won't fit in buffer"); \ + strcpy(buff, x); \ +}) diff --git a/src/ui.c b/src/ui.c index 85c118ce..79954298 100644 --- a/src/ui.c +++ b/src/ui.c @@ -91,6 +91,27 @@ static const bagl_element_t ui_idle_screen[] = { NULL, NULL, NULL}, + + {{BAGL_LABELINE, 0x03, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, + BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, + "Chain", + 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}, + G.baking_idle_screens.chain, + 0, + 0, + 0, + NULL, + NULL, + NULL}, + }; static bool do_nothing(void) { @@ -102,20 +123,28 @@ static void update_baking_idle_screens(void) { number_to_string(G.idle_text, N_data.hwm.main.highest_level); if (N_data.bip32_path.length == 0) { - strcpy(G.baking_auth_text, "No Key Authorized"); + STRCPY(G.baking_auth_text, "No Key Authorized"); } else { cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); pubkey_to_pkh_string( G.baking_auth_text, sizeof(G.baking_auth_text), N_data.curve, pubkey); } + + if (N_data.main_chain_id.v == 0) { + STRCPY(G.baking_idle_screens.chain, "any"); + } else if (N_data.main_chain_id.v == mainnet_chain_id.v) { + STRCPY(G.baking_idle_screens.chain, "mainnet"); + } else { + chain_id_to_string(G.baking_idle_screens.chain, sizeof(G.baking_idle_screens.chain), N_data.main_chain_id); + } } static void ui_idle(void) { #ifdef BAKING_APP update_baking_idle_screens(); ui_display(ui_idle_screen, NUM_ELEMENTS(ui_idle_screen), - do_nothing, exit_app, 2); + do_nothing, exit_app, 3); #else G.cxl_callback = exit_app; main_menu(); From a1051eb24bd5b9b45131f6c1caf94e767c747d26 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 58/92] Move baking idle screen globals to better place --- src/globals.h | 4 ++-- src/to_string.h | 1 - src/types.h | 2 ++ src/ui.c | 10 +++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/globals.h b/src/globals.h index ad225eb8..b17d9d55 100644 --- a/src/globals.h +++ b/src/globals.h @@ -72,9 +72,9 @@ typedef struct { uint32_t timeout_cycle_count; - char idle_text[16]; - char baking_auth_text[PKH_STRING_SIZE]; struct { + char hwm[MAX_INT_DIGITS + 1]; // with null termination + char pkh[PKH_STRING_SIZE]; char chain[CHAIN_ID_BASE58_STRING_SIZE]; } baking_idle_screens; diff --git a/src/to_string.h b/src/to_string.h index 58541a09..0047a448 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -15,7 +15,6 @@ void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t h void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); void chain_id_to_string(char *buff, size_t const buff_size, chain_id_t const chain_id); -#define MAX_INT_DIGITS 20 // dest must be at least MAX_INT_DIGITS size_t number_to_string(char *dest, uint64_t number); diff --git a/src/types.h b/src/types.h index 5fa48d47..3e316df0 100644 --- a/src/types.h +++ b/src/types.h @@ -13,6 +13,8 @@ typedef uint32_t level_t; #define CHAIN_ID_BASE58_STRING_SIZE (15 + 1) // with null termination +#define MAX_INT_DIGITS 20 + typedef struct { uint32_t v; } chain_id_t; diff --git a/src/ui.c b/src/ui.c index 79954298..5b8c7f8e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -64,7 +64,7 @@ static const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - G.idle_text, + G.baking_idle_screens.hwm, 0, 0, 0, @@ -84,7 +84,7 @@ static const bagl_element_t ui_idle_screen[] = { {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - G.baking_auth_text, + G.baking_idle_screens.pkh, 0, 0, 0, @@ -120,14 +120,14 @@ static bool do_nothing(void) { #endif static void update_baking_idle_screens(void) { - number_to_string(G.idle_text, N_data.hwm.main.highest_level); + number_to_string(G.baking_idle_screens.hwm, N_data.hwm.main.highest_level); if (N_data.bip32_path.length == 0) { - STRCPY(G.baking_auth_text, "No Key Authorized"); + STRCPY(G.baking_idle_screens.pkh, "No Key Authorized"); } else { cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); pubkey_to_pkh_string( - G.baking_auth_text, sizeof(G.baking_auth_text), + G.baking_idle_screens.pkh, sizeof(G.baking_idle_screens.pkh), N_data.curve, pubkey); } From 4a40251bca9e8b4eede12c193c241bf4e4d044b8 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 59/92] Show chain ID aliases in setup prompts --- src/apdu_setup.c | 2 +- src/to_string.c | 16 ++++++++++++++++ src/to_string.h | 1 + src/ui.c | 8 +------- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/apdu_setup.c b/src/apdu_setup.c index 128b395f..b2ce6201 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -64,7 +64,7 @@ __attribute__((noreturn)) static void prompt_setup( SET_STATIC_UI_VALUE(TYPE_INDEX, "Baking?"); register_ui_callback(ADDRESS_INDEX, pubkey_to_string, &G.public_key); - register_ui_callback(CHAIN_INDEX, chain_id_to_string, &G.main_chain_id); + register_ui_callback(CHAIN_INDEX, chain_id_to_string_with_aliases, &G.main_chain_id); register_ui_callback(MAIN_HWM_INDEX, number_to_string_indirect32, &G.hwm.main); register_ui_callback(TEST_HWM_INDEX, number_to_string_indirect32, &G.hwm.test); diff --git a/src/to_string.c b/src/to_string.c index f40fb856..ef57e75f 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -127,6 +127,22 @@ void chain_id_to_string(char *const buff, size_t const buff_size, chain_id_t con if (!b58enc(buff, &out_size, &data, sizeof(data))) THROW(EXC_WRONG_LENGTH); } +#define STRCPY_OR_THROW(buff, size, x, exc) ({ \ + if (size < sizeof(x)) THROW(exc); \ + strcpy(buff, x); \ +}) + +void chain_id_to_string_with_aliases(char *const out, size_t const out_size, chain_id_t const *const chain_id) { + check_null(chain_id); + if (chain_id->v == 0) { + STRCPY_OR_THROW(out, out_size, "any", EXC_WRONG_LENGTH); + } else if (chain_id->v == mainnet_chain_id.v) { + STRCPY_OR_THROW(out, out_size, "mainnet", EXC_WRONG_LENGTH); + } else { + chain_id_to_string(out, out_size, *chain_id); + } +} + // These functions do not output terminating null bytes. // This function fills digits, potentially with all leading zeroes, from the end of the buffer backwards diff --git a/src/to_string.h b/src/to_string.h index 0047a448..8c214b61 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -14,6 +14,7 @@ void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); void chain_id_to_string(char *buff, size_t const buff_size, chain_id_t const chain_id); +void chain_id_to_string_with_aliases(char *const out, size_t const out_size, chain_id_t const *const chain_id); // dest must be at least MAX_INT_DIGITS size_t number_to_string(char *dest, uint64_t number); diff --git a/src/ui.c b/src/ui.c index 5b8c7f8e..c10ae64a 100644 --- a/src/ui.c +++ b/src/ui.c @@ -131,13 +131,7 @@ static void update_baking_idle_screens(void) { N_data.curve, pubkey); } - if (N_data.main_chain_id.v == 0) { - STRCPY(G.baking_idle_screens.chain, "any"); - } else if (N_data.main_chain_id.v == mainnet_chain_id.v) { - STRCPY(G.baking_idle_screens.chain, "mainnet"); - } else { - chain_id_to_string(G.baking_idle_screens.chain, sizeof(G.baking_idle_screens.chain), N_data.main_chain_id); - } + chain_id_to_string_with_aliases(G.baking_idle_screens.chain, sizeof(G.baking_idle_screens.chain), &N_data.main_chain_id); } static void ui_idle(void) { From 9d53371fface6da5db55117867b42e6699ac33c3 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 10:38:46 -0500 Subject: [PATCH 60/92] Add clarifications to some string size constants --- src/types.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/types.h b/src/types.h index 3e316df0..29803bd1 100644 --- a/src/types.h +++ b/src/types.h @@ -11,7 +11,7 @@ typedef uint32_t (*apdu_handler)(uint8_t instruction); typedef uint32_t level_t; -#define CHAIN_ID_BASE58_STRING_SIZE (15 + 1) // with null termination +#define CHAIN_ID_BASE58_STRING_SIZE sizeof("NetXdQprcVkpaWU") #define MAX_INT_DIGITS 20 @@ -73,25 +73,23 @@ typedef struct { } nvram_data; -#define PKH_STRING_SIZE 40 -#define PROTOCOL_HASH_BASE58_STRING_SIZE 52 // e.g. "ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96" plus null byte +#define PKH_STRING_SIZE 40 // includes null byte // TODO: use sizeof for this. +#define PROTOCOL_HASH_BASE58_STRING_SIZE sizeof("ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96") #define MAX_SCREEN_COUNT 7 // Current maximum usage #define PROMPT_WIDTH 16 #define VALUE_WIDTH PROTOCOL_HASH_BASE58_STRING_SIZE // Macros to wrap a static prompt and value strings and ensure they aren't too long. -#define PROMPT(str) \ - ({ \ +#define PROMPT(str) ({ \ _Static_assert(sizeof(str) <= PROMPT_WIDTH + 1/*null byte*/ , str " won't fit in the UI prompt."); \ str; \ - }) +}) -#define STATIC_UI_VALUE(str) \ - ({ \ +#define STATIC_UI_VALUE(str) ({ \ _Static_assert(sizeof(str) <= VALUE_WIDTH + 1/*null byte*/, str " won't fit in the UI.".); \ str; \ - }) +}) // Operations From cc7e7893f4bb323c41d9ec37ecdc66c42d2a21bb Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Feb 2019 13:46:51 -0500 Subject: [PATCH 61/92] Update baking idle screens any time NVRAM changes --- src/globals.c | 36 ++++++++++++++++++++++++++++++++---- src/globals.h | 27 +++++++++++++-------------- src/ui.c | 15 --------------- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/globals.c b/src/globals.c index 6cd6da16..4c7a97bf 100644 --- a/src/globals.c +++ b/src/globals.c @@ -1,7 +1,11 @@ #include "globals.h" +#include "exception.h" +#include "to_string.h" + #include + // WARNING: *************************************************** // Non-const globals MUST NOT HAVE AN INITIALIZER. // @@ -16,12 +20,36 @@ globals_t global; ux_state_t ux; unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; -// DO NOT TRY TO INIT THIS. This can only be written via an system call. -// The "N_" is *significant*. It tells the linker to put this in NVRAM. -WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? - void init_globals(void) { memset(&global, 0, sizeof(global)); memset(&ux, 0, sizeof(ux)); memset(G_io_seproxyhal_spi_buffer, 0, sizeof(G_io_seproxyhal_spi_buffer)); } + +// #ifdef BAKING_APP +// DO NOT TRY TO INIT THIS. This can only be written via an system call. +// The "N_" is *significant*. It tells the linker to put this in NVRAM. +WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? + +high_watermark_t *select_hwm_by_chain(chain_id_t const chain_id, nvram_data *const ram) { + check_null(ram); + return chain_id.v == ram->main_chain_id.v || ram->main_chain_id.v == 0 + ? &ram->hwm.main + : &ram->hwm.test; +} + +void update_baking_idle_screens(void) { + number_to_string(global.ui.baking_idle_screens.hwm, N_data.hwm.main.highest_level); + + if (N_data.bip32_path.length == 0) { + STRCPY(global.ui.baking_idle_screens.pkh, "No Key Authorized"); + } else { + cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); + pubkey_to_pkh_string( + global.ui.baking_idle_screens.pkh, sizeof(global.ui.baking_idle_screens.pkh), + N_data.curve, pubkey); + } + + chain_id_to_string_with_aliases(global.ui.baking_idle_screens.chain, sizeof(global.ui.baking_idle_screens.chain), &N_data.main_chain_id); +} +//#endif diff --git a/src/globals.h b/src/globals.h index b17d9d55..0ec31575 100644 --- a/src/globals.h +++ b/src/globals.h @@ -120,10 +120,21 @@ extern unsigned int app_stack_canary; // From SDK extern ux_state_t ux; extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; +static inline void throw_stack_size() { + uint8_t st; + // uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; + uint32_t tmp2 = (uint32_t)global.stack_root - (uint32_t)&st; + THROW(0x9000 + tmp2); +} + +// #ifdef BAKING_APP extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? #define N_data (*(WIDE nvram_data*)PIC(&N_data_real)) +void update_baking_idle_screens(void); +high_watermark_t *select_hwm_by_chain(chain_id_t const chain_id, nvram_data *const ram); + // Properly updates NVRAM data to prevent any clobbering of data. // 'out_param' defines the name of a pointer to the nvram_data struct // that 'body' can change to apply updates. @@ -132,18 +143,6 @@ extern WIDE nvram_data N_data_real; // TODO: What does WIDE actually mean? memcpy(&global.baking_auth.new_data, &N_data, sizeof(global.baking_auth.new_data)); \ body; \ nvm_write((void*)&N_data, &global.baking_auth.new_data, sizeof(N_data)); \ + update_baking_idle_screens(); \ }) - -static inline high_watermark_t *select_hwm_by_chain(chain_id_t const chain_id, nvram_data *const ram) { - check_null(ram); - return chain_id.v == ram->main_chain_id.v || ram->main_chain_id.v == 0 - ? &ram->hwm.main - : &ram->hwm.test; -} - -static inline void throw_stack_size() { - uint8_t st; - // uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; - uint32_t tmp2 = (uint32_t)global.stack_root - (uint32_t)&st; - THROW(0x9000 + tmp2); -} +//#endif diff --git a/src/ui.c b/src/ui.c index c10ae64a..69076574 100644 --- a/src/ui.c +++ b/src/ui.c @@ -119,21 +119,6 @@ static bool do_nothing(void) { } #endif -static void update_baking_idle_screens(void) { - number_to_string(G.baking_idle_screens.hwm, N_data.hwm.main.highest_level); - - if (N_data.bip32_path.length == 0) { - STRCPY(G.baking_idle_screens.pkh, "No Key Authorized"); - } else { - cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); - pubkey_to_pkh_string( - G.baking_idle_screens.pkh, sizeof(G.baking_idle_screens.pkh), - N_data.curve, pubkey); - } - - chain_id_to_string_with_aliases(G.baking_idle_screens.chain, sizeof(G.baking_idle_screens.chain), &N_data.main_chain_id); -} - static void ui_idle(void) { #ifdef BAKING_APP update_baking_idle_screens(); From 193abf7f2b33af27506c92fdc8b4b33d0735114c Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Mon, 4 Feb 2019 17:40:21 -0500 Subject: [PATCH 62/92] Pin nixpkgs and add docs for Nix scripts --- CONTRIBUTING.md | 16 ++++++++++++++++ default.nix | 2 +- nix/nixpkgs.nix | 10 ++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md create mode 100644 nix/nixpkgs.nix diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6c12e969 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,16 @@ +# Contributing + +## Hacking with [Nix](https://nixos.org/nix/) + +The `nix/` folder contains helper scripts for working with the ledger via Nix. + +### Developing +Use `nix/env.sh` to enter a shell where you can run `make` and it will just work. You can also pass a command instead, e.g. `nix/env.sh make clean`. + +For development, use `nix/watch.sh make APP=` to incrementally build on every change. Be sure to `nix/env.sh make clean` if you start watching a different `APP`. + +### Building +To do a full Nix build run `nix/build.sh`. You can pass `nix-build` arguments to this to build specific attributes, e.g. `nix/build.sh -A wallet`. + +### Installing +`nix/install.sh` will install both the wallet and baking apps. Use `nix/install.sh baking` to install just the baking app or `nix/install.sh wallet` to install just the wallet. diff --git a/default.nix b/default.nix index 4d3ee579..f1db63b6 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -{ pkgs ? import {}, commit, ... }: +{ pkgs ? import nix/nixpkgs.nix {}, commit, ... }: let fetchThunk = p: diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix new file mode 100644 index 00000000..a8470828 --- /dev/null +++ b/nix/nixpkgs.nix @@ -0,0 +1,10 @@ +# To bump: +# 1. Select channel from: http://howoldis.herokuapp.com/ +# 2. Copy the URL to a `nixexprs.tar.xz` file. It should include hashes (i.e. not be a redirect). +# 3. Run `nix-prefetch-url --unpack ` to get the SHA256 hash of the contents.* +# 4. Update the URL and SHA256 values below. + +import (builtins.fetchTarball { + url = "https://releases.nixos.org/nixpkgs/nixpkgs-19.03pre167327.11cf7d6e1ff/nixexprs.tar.xz"; + sha256 = "0y0fs0j6pb9p9hzv1zagcavvpv50z2anqnbri6kq5iy1j4yqaric"; +}) From a0eded0963edb26cb7c3832a913d2da95eea0d42 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Tue, 5 Feb 2019 16:41:46 -0500 Subject: [PATCH 63/92] Use pinned nixpkgs in nix/install.sh --- nix/ledgerblue.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/ledgerblue.nix b/nix/ledgerblue.nix index 430e58dc..efae60d9 100644 --- a/nix/ledgerblue.nix +++ b/nix/ledgerblue.nix @@ -1,4 +1,4 @@ -{ pkgs ? import {}, ... }: +{ pkgs ? import ../nix/nixpkgs.nix {}, ... }: rec { withLedgerblue = (pkgs.python36.withPackages (ps: with ps; [ ecpy hidapi pycrypto python-u2flib-host requests ledgerblue pillow pkgs.hidapi protobuf From a2595b815cf4eb0766dea98b9a90b43c8d4b582a Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 7 Feb 2019 13:38:57 -0500 Subject: [PATCH 64/92] Bump version to 2.0.0 --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ba1c1017..78d2d666 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,8 @@ APPNAME = "Tezos Wallet" endif APP_LOAD_PARAMS=--appFlags 0 --curve ed25519 --curve secp256k1 --curve prime256r1 --path "44'/1729'" $(COMMON_LOAD_PARAMS) VERSION_TAG ?= $(shell git describe --tags 2>/dev/null | cut -f1 -d-) -APPVERSION_M=1 -APPVERSION_N=5 +APPVERSION_M=2 +APPVERSION_N=0 APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) From 10ec88685632ba5010a89bb25d66a89398cec31f Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 65/92] Convert comment to clearer code for parsed_operations --- src/operations.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/operations.c b/src/operations.c index e7feeb7e..afff3fcb 100644 --- a/src/operations.c +++ b/src/operations.c @@ -105,19 +105,27 @@ static inline uint64_t parse_z(const void *data, size_t *ix, size_t length, uint val; \ }) -static inline void compute_pkh(cx_curve_t curve, bip32_path_t const *const bip32_path, - struct parsed_operation_group *const out) { +static inline void compute_pkh( + cx_ecfp_public_key_t *const compressed_pubkey_out, + struct parsed_contract *const contract_out, + cx_curve_t const curve, + bip32_path_t const *const bip32_path +) { check_null(bip32_path); - check_null(out); + check_null(compressed_pubkey_out); + check_null(contract_out); cx_ecfp_public_key_t const *const pubkey = generate_public_key(curve, bip32_path); - cx_ecfp_public_key_t const *const key = public_key_hash(out->signing.hash, curve, pubkey); - memcpy(&out->public_key, key, sizeof(out->public_key)); - out->signing.curve_code = curve_to_curve_code(curve); - out->signing.originated = 0; + cx_ecfp_public_key_t const *const compressed_pubkey = public_key_hash(contract_out->hash, curve, pubkey); + contract_out->curve_code = curve_to_curve_code(curve); + contract_out->originated = 0; + + memcpy(compressed_pubkey_out, compressed_pubkey, sizeof(*compressed_pubkey_out)); } -static inline void parse_implicit(struct parsed_contract *out, uint8_t curve_code, - const uint8_t hash[HASH_SIZE]) { +static inline void parse_implicit( + struct parsed_contract *out, uint8_t curve_code, + const uint8_t hash[HASH_SIZE] +) { out->originated = 0; out->curve_code = curve_code; memcpy(out->hash, hash, sizeof(out->hash)); @@ -145,7 +153,7 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, out->operation.tag = OPERATION_TAG_NONE; - compute_pkh(curve, bip32_path, out); // sets up "signing" and "public_key" members + compute_pkh(&out->public_key, &out->signing, curve, bip32_path); size_t ix = 0; From 9d3587c6b488b11c26a9283b410bb240441817ee Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 66/92] Use G for globals in apdu_sign.c --- src/apdu_sign.c | 84 +++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 4805da39..4a7ef8e0 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -17,42 +17,44 @@ #include +#define G global.u.sign + #define SIGN_HASH_SIZE 32 #define B2B_BLOCKBYTES 128 static void conditional_init_hash_state(void) { - if (!global.u.sign.is_hash_state_inited) { + if (!G.is_hash_state_inited) { b2b_init(&global.blake2b.hash_state, SIGN_HASH_SIZE); - global.u.sign.is_hash_state_inited = true; + G.is_hash_state_inited = true; } } static void hash_buffer(void) { - const uint8_t *current = global.u.sign.message_data; - while (global.u.sign.message_data_length > B2B_BLOCKBYTES) { + const uint8_t *current = G.message_data; + while (G.message_data_length > B2B_BLOCKBYTES) { conditional_init_hash_state(); b2b_update(&global.blake2b.hash_state, current, B2B_BLOCKBYTES); - global.u.sign.message_data_length -= B2B_BLOCKBYTES; + G.message_data_length -= B2B_BLOCKBYTES; current += B2B_BLOCKBYTES; } // TODO use circular buffer at some point - memmove(global.u.sign.message_data, current, global.u.sign.message_data_length); + memmove(G.message_data, current, G.message_data_length); } static void finish_hashing(uint8_t *hash, size_t hash_size) { hash_buffer(); conditional_init_hash_state(); - b2b_update(&global.blake2b.hash_state, global.u.sign.message_data, global.u.sign.message_data_length); + b2b_update(&global.blake2b.hash_state, G.message_data, G.message_data_length); b2b_final(&global.blake2b.hash_state, hash, hash_size); - global.u.sign.message_data_length = 0; - global.u.sign.is_hash_state_inited = false; + G.message_data_length = 0; + G.is_hash_state_inited = false; } static int perform_signature(bool hash_first); #ifdef BAKING_APP static bool bake_auth_ok(void) { - authorize_baking(global.u.sign.curve, &global.u.sign.bip32_path); + authorize_baking(G.curve, &G.bip32_path); int tx = perform_signature(true); delayed_send(tx); return true; @@ -72,11 +74,11 @@ static bool sign_unsafe_ok(void) { #endif static void clear_data(void) { - global.u.sign.bip32_path.length = 0; - global.u.sign.message_data_length = 0; - global.u.sign.is_hash_state_inited = false; - global.u.sign.magic_number = 0; - global.u.sign.hash_only = false; + G.bip32_path.length = 0; + G.message_data_length = 0; + G.is_hash_state_inited = false; + G.magic_number = 0; + G.hash_only = false; } static bool sign_reject(void) { @@ -88,12 +90,12 @@ static bool sign_reject(void) { #ifdef BAKING_APP uint32_t baking_sign_complete(void) { // Have raw data, can get insight into it - switch (global.u.sign.magic_number) { + switch (G.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: guard_baking_authorized( - global.u.sign.curve, global.u.sign.message_data, global.u.sign.message_data_length, - &global.u.sign.bip32_path); + G.curve, G.message_data, G.message_data_length, + &G.bip32_path); return perform_signature(true); case MAGIC_BYTE_UNSAFE_OP: @@ -106,8 +108,8 @@ uint32_t baking_sign_complete(void) { struct parsed_operation_group *ops = parse_operations( - global.u.sign.message_data, global.u.sign.message_data_length, - global.u.sign.curve, &global.u.sign.bip32_path, allowed); + G.message_data, G.message_data_length, + G.curve, &G.bip32_path, allowed); // With < nickel fee if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); @@ -433,18 +435,18 @@ uint32_t wallet_sign_complete(uint8_t instruction) { }; ui_prompt(prehashed_prompts, insecure_values, sign_unsafe_ok, sign_reject); } else { - switch (global.u.sign.magic_number) { + switch (G.magic_number) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: default: THROW(EXC_PARSE_ERROR); case MAGIC_BYTE_UNSAFE_OP: - if (global.u.sign.is_hash_state_inited) { + if (G.is_hash_state_inited) { goto unsafe; } if (!prompt_transaction( - global.u.sign.message_data, global.u.sign.message_data_length, global.u.sign.curve, - &global.u.sign.bip32_path, sign_ok, sign_reject)) { + G.message_data, G.message_data_length, G.curve, + &G.bip32_path, sign_ok, sign_reject)) { goto unsafe; } case MAGIC_BYTE_UNSAFE_OP2: @@ -471,19 +473,19 @@ unsigned int handle_apdu_sign(uint8_t instruction) { switch (p1 & ~P1_LAST_MARKER) { case P1_FIRST: clear_data(); - memset(global.u.sign.message_data, 0, sizeof(global.u.sign.message_data)); - global.u.sign.message_data_length = 0; - read_bip32_path(&global.u.sign.bip32_path, dataBuffer, dataLength); - global.u.sign.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + memset(G.message_data, 0, sizeof(G.message_data)); + G.message_data_length = 0; + read_bip32_path(&G.bip32_path, dataBuffer, dataLength); + G.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); return_ok(); #ifndef BAKING_APP case P1_HASH_ONLY_NEXT: // This is a debugging Easter egg - global.u.sign.hash_only = true; + G.hash_only = true; // FALL THROUGH #endif case P1_NEXT: - if (global.u.sign.bip32_path.length == 0) { + if (G.bip32_path.length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } break; @@ -491,8 +493,8 @@ unsigned int handle_apdu_sign(uint8_t instruction) { THROW(EXC_WRONG_PARAM); } - if (instruction != INS_SIGN_UNSAFE && global.u.sign.magic_number == 0) { - global.u.sign.magic_number = get_magic_byte(dataBuffer, dataLength); + if (instruction != INS_SIGN_UNSAFE && G.magic_number == 0) { + G.magic_number = get_magic_byte(dataBuffer, dataLength); } #ifndef BAKING_APP @@ -501,12 +503,12 @@ unsigned int handle_apdu_sign(uint8_t instruction) { } #endif - if (global.u.sign.message_data_length + dataLength > sizeof(global.u.sign.message_data)) { + if (G.message_data_length + dataLength > sizeof(G.message_data)) { THROW(EXC_PARSE_ERROR); } - os_memmove(global.u.sign.message_data + global.u.sign.message_data_length, dataBuffer, dataLength); - global.u.sign.message_data_length += dataLength; + os_memmove(G.message_data + G.message_data_length, dataBuffer, dataLength); + G.message_data_length += dataLength; if (!last) { return_ok(); @@ -521,11 +523,11 @@ unsigned int handle_apdu_sign(uint8_t instruction) { static int perform_signature(bool hash_first) { uint8_t hash[SIGN_HASH_SIZE]; - uint8_t *data = global.u.sign.message_data; - uint32_t datalen = global.u.sign.message_data_length; + uint8_t *data = G.message_data; + uint32_t datalen = G.message_data_length; #ifdef BAKING_APP - update_high_water_mark(global.u.sign.message_data, global.u.sign.message_data_length); + update_high_water_mark(G.message_data, G.message_data_length); #endif if (hash_first) { @@ -535,7 +537,7 @@ static int perform_signature(bool hash_first) { datalen = SIGN_HASH_SIZE; #ifndef BAKING_APP - if (global.u.sign.hash_only) { + if (G.hash_only) { memcpy(G_io_apdu_buffer, data, datalen); uint32_t tx = datalen; @@ -546,10 +548,10 @@ static int perform_signature(bool hash_first) { #endif } - struct key_pair *const pair = generate_key_pair(global.u.sign.curve, &global.u.sign.bip32_path); + struct key_pair *const pair = generate_key_pair(G.curve, &G.bip32_path); uint32_t tx; - switch (global.u.sign.curve) { + switch (G.curve) { case CX_CURVE_Ed25519: { tx = cx_eddsa_sign(&pair->private_key, 0, From e54c3437226a8c64ab86ef8ebc1f2e6931568cc3 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 67/92] Clarify member of parsed_contract --- src/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.h b/src/types.h index 29803bd1..cb0bb159 100644 --- a/src/types.h +++ b/src/types.h @@ -99,7 +99,7 @@ typedef struct { #define HASH_SIZE 20 struct parsed_contract { - uint8_t originated; + uint8_t originated; // a lightweight bool uint8_t curve_code; // TEZOS_NO_CURVE in originated case // An implicit contract with TEZOS_NO_CURVE means not present uint8_t hash[HASH_SIZE]; From 63b1c3a61494fb0bd8739059b0e973388571aec4 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 68/92] Make pubkey_to_pkh_string safer with nulls --- src/to_string.c | 13 ++++++++++--- src/to_string.h | 8 ++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/to_string.c b/src/to_string.c index ef57e75f..5e80d20a 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -28,11 +28,18 @@ void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct pars pkh_to_string(buff, buff_size, curve, contract->hash); } -void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, - const cx_ecfp_public_key_t *public_key) { +void pubkey_to_pkh_string( + char *const out, + size_t const out_size, + cx_curve_t const curve, + cx_ecfp_public_key_t const *const public_key +) { + check_null(out); + check_null(public_key); + uint8_t hash[HASH_SIZE]; public_key_hash(hash, curve, public_key); - pkh_to_string(buff, buff_size, curve, hash); + pkh_to_string(out, out_size, curve, hash); } void compute_hash_checksum(uint8_t out[TEZOS_HASH_CHECKSUM_SIZE], void const *const data, size_t size) { diff --git a/src/to_string.h b/src/to_string.h index 8c214b61..5eccda14 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -9,8 +9,12 @@ #include "types.h" #include "ui.h" -void pubkey_to_pkh_string(char *buff, uint32_t buff_size, cx_curve_t curve, - const cx_ecfp_public_key_t *public_key); +void pubkey_to_pkh_string( + char *const out, + size_t const out_size, + cx_curve_t const curve, + cx_ecfp_public_key_t const *const public_key +); void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); void chain_id_to_string(char *buff, size_t const buff_size, chain_id_t const chain_id); From 036649311d595d5fe3303f5ccba345d74b2c65e9 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 69/92] Unify curve and bip32_path in NVRAM --- src/apdu_baking.c | 4 ++-- src/apdu_pubkey.c | 4 ++-- src/apdu_setup.c | 4 ++-- src/baking_auth.c | 12 ++++++------ src/baking_auth.h | 2 +- src/globals.c | 6 +++--- src/types.h | 8 ++++++-- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 480d0864..b6bde9f6 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -85,13 +85,13 @@ unsigned int handle_apdu_main_hwm(__attribute__((unused)) uint8_t instruction) { unsigned int handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { - uint8_t const length = N_data.bip32_path.length; + uint8_t const length = N_data.baking_key.bip32_path.length; uint32_t tx = 0; G_io_apdu_buffer[tx++] = length; for (uint8_t i = 0; i < length; ++i) { - tx = send_word_big_endian(tx, N_data.bip32_path.components[i]); + tx = send_word_big_endian(tx, N_data.baking_key.bip32_path.components[i]); } G_io_apdu_buffer[tx++] = 0x90; diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 4109cc21..b5e93562 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -39,8 +39,8 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP if (cdata_size == 0 && instruction == INS_AUTHORIZE_BAKING) { - G.curve = N_data.curve; - copy_bip32_path(&G.bip32_path, &N_data.bip32_path); + G.curve = N_data.baking_key.curve; + copy_bip32_path(&G.bip32_path, &N_data.baking_key.bip32_path); } else { #endif read_bip32_path(&G.bip32_path, dataBuffer, cdata_size); diff --git a/src/apdu_setup.c b/src/apdu_setup.c index b2ce6201..40405518 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -23,8 +23,8 @@ struct setup_wire { static bool ok(void) { UPDATE_NVRAM(ram, { - ram->curve = G.curve; - copy_bip32_path(&ram->bip32_path, &G.bip32_path); + ram->baking_key.curve = G.curve; + copy_bip32_path(&ram->baking_key.bip32_path, &G.bip32_path); ram->main_chain_id = G.main_chain_id; ram->hwm.main.highest_level = G.hwm.main; ram->hwm.main.had_endorsement = false; diff --git a/src/baking_auth.c b/src/baking_auth.c index 02ac20e3..e4ab813a 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -27,13 +27,13 @@ static void write_high_watermark(parsed_baking_data_t const *const in) { }); } -void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path) { +void authorize_baking(cx_curve_t const curve, bip32_path_t const *const bip32_path) { check_null(bip32_path); - if (bip32_path->length > NUM_ELEMENTS(N_data.bip32_path.components) || bip32_path->length == 0) return; + if (bip32_path->length > NUM_ELEMENTS(N_data.baking_key.bip32_path.components) || bip32_path->length == 0) return; UPDATE_NVRAM(ram, { - ram->curve = curve; - copy_bip32_path(&ram->bip32_path, bip32_path); + ram->baking_key.curve = curve; + copy_bip32_path(&ram->baking_key.bip32_path, bip32_path); }); } @@ -51,9 +51,9 @@ static bool is_level_authorized(parsed_baking_data_t const *const baking_info) { bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path) { check_null(bip32_path); return - curve == N_data.curve && + curve == N_data.baking_key.curve && bip32_path->length > 0 && - bip32_paths_eq(bip32_path, &N_data.bip32_path); + bip32_paths_eq(bip32_path, &N_data.baking_key.bip32_path); } void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path) { diff --git a/src/baking_auth.h b/src/baking_auth.h index 1a57bb2f..245bfc07 100644 --- a/src/baking_auth.h +++ b/src/baking_auth.h @@ -8,7 +8,7 @@ #include #include -void authorize_baking(cx_curve_t curve, bip32_path_t const *const bip32_path); +void authorize_baking(cx_curve_t const curve, bip32_path_t const *const bip32_path); void guard_baking_authorized(cx_curve_t curve, void *data, int datalen, bip32_path_t const *const bip32_path); bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path); void update_high_water_mark(void *data, int datalen); diff --git a/src/globals.c b/src/globals.c index 4c7a97bf..a78fb597 100644 --- a/src/globals.c +++ b/src/globals.c @@ -41,13 +41,13 @@ high_watermark_t *select_hwm_by_chain(chain_id_t const chain_id, nvram_data *con void update_baking_idle_screens(void) { number_to_string(global.ui.baking_idle_screens.hwm, N_data.hwm.main.highest_level); - if (N_data.bip32_path.length == 0) { + if (N_data.baking_key.bip32_path.length == 0) { STRCPY(global.ui.baking_idle_screens.pkh, "No Key Authorized"); } else { - cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.curve, &N_data.bip32_path); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(N_data.baking_key.curve, &N_data.baking_key.bip32_path); pubkey_to_pkh_string( global.ui.baking_idle_screens.pkh, sizeof(global.ui.baking_idle_screens.pkh), - N_data.curve, pubkey); + N_data.baking_key.curve, pubkey); } chain_id_to_string_with_aliases(global.ui.baking_idle_screens.chain, sizeof(global.ui.baking_idle_screens.chain), &N_data.main_chain_id); diff --git a/src/types.h b/src/types.h index cb0bb159..ce3c256f 100644 --- a/src/types.h +++ b/src/types.h @@ -57,6 +57,11 @@ static inline bool bip32_paths_eq(bip32_path_t const *const a, bip32_path_t cons ); } +typedef struct { + bip32_path_t bip32_path; + cx_curve_t curve; +} bip32_path_with_curve_t; + typedef struct { level_t highest_level; bool had_endorsement; @@ -68,8 +73,7 @@ typedef struct { high_watermark_t main; high_watermark_t test; } hwm; - cx_curve_t curve; - bip32_path_t bip32_path; + bip32_path_with_curve_t baking_key; } nvram_data; From 7f244520e5a3c14f79ece8f911066854d25407aa Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 70/92] Unify bip32_path and curve in apdu_sign.c --- src/apdu_sign.c | 22 +++++++++++----------- src/globals.h | 3 +-- src/types.h | 31 ++++++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 4a7ef8e0..43f9d598 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -74,7 +74,7 @@ static bool sign_unsafe_ok(void) { #endif static void clear_data(void) { - G.bip32_path.length = 0; + G.key.bip32_path.length = 0; G.message_data_length = 0; G.is_hash_state_inited = false; G.magic_number = 0; @@ -94,8 +94,8 @@ uint32_t baking_sign_complete(void) { case MAGIC_BYTE_BLOCK: case MAGIC_BYTE_BAKING_OP: guard_baking_authorized( - G.curve, G.message_data, G.message_data_length, - &G.bip32_path); + G.key.curve, G.message_data, G.message_data_length, + &G.key.bip32_path); return perform_signature(true); case MAGIC_BYTE_UNSAFE_OP: @@ -109,7 +109,7 @@ uint32_t baking_sign_complete(void) { struct parsed_operation_group *ops = parse_operations( G.message_data, G.message_data_length, - G.curve, &G.bip32_path, allowed); + G.key.curve, &G.key.bip32_path, allowed); // With < nickel fee if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); @@ -445,8 +445,8 @@ uint32_t wallet_sign_complete(uint8_t instruction) { goto unsafe; } if (!prompt_transaction( - G.message_data, G.message_data_length, G.curve, - &G.bip32_path, sign_ok, sign_reject)) { + G.message_data, G.message_data_length, G.key.curve, + &G.key.bip32_path, sign_ok, sign_reject)) { goto unsafe; } case MAGIC_BYTE_UNSAFE_OP2: @@ -475,8 +475,8 @@ unsigned int handle_apdu_sign(uint8_t instruction) { clear_data(); memset(G.message_data, 0, sizeof(G.message_data)); G.message_data_length = 0; - read_bip32_path(&G.bip32_path, dataBuffer, dataLength); - G.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); + read_bip32_path(&G.key.bip32_path, dataBuffer, dataLength); + G.key.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); return_ok(); #ifndef BAKING_APP case P1_HASH_ONLY_NEXT: @@ -485,7 +485,7 @@ unsigned int handle_apdu_sign(uint8_t instruction) { // FALL THROUGH #endif case P1_NEXT: - if (G.bip32_path.length == 0) { + if (G.key.bip32_path.length == 0) { THROW(EXC_WRONG_LENGTH_FOR_INS); } break; @@ -548,10 +548,10 @@ static int perform_signature(bool hash_first) { #endif } - struct key_pair *const pair = generate_key_pair(G.curve, &G.bip32_path); + struct key_pair *const pair = generate_key_pair(G.key.curve, &G.key.bip32_path); uint32_t tx; - switch (G.curve) { + switch (G.key.curve) { case CX_CURVE_Ed25519: { tx = cx_eddsa_sign(&pair->private_key, 0, diff --git a/src/globals.h b/src/globals.h index 0ec31575..9101247d 100644 --- a/src/globals.h +++ b/src/globals.h @@ -49,8 +49,7 @@ typedef struct { } pubkey; struct { - bip32_path_t bip32_path; - cx_curve_t curve; + bip32_path_with_curve_t key; uint8_t message_data[TEZOS_BUFSIZE]; uint32_t message_data_length; diff --git a/src/types.h b/src/types.h index ce3c256f..6a37ad28 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,6 @@ #pragma once +#include "exception.h" #include "os.h" #include "os_io_seproxyhal.h" @@ -44,24 +45,44 @@ typedef struct { } bip32_path_t; static inline void copy_bip32_path(bip32_path_t *const out, bip32_path_t const *const in) { + check_null(out); + check_null(in); memcpy(out->components, in->components, in->length * sizeof(*in->components)); out->length = in->length; } static inline bool bip32_paths_eq(bip32_path_t const *const a, bip32_path_t const *const b) { return a == b || ( - a != NULL && - b != NULL && - a->length == b->length && - memcmp(a->components, b->components, a->length * sizeof(*a->components)) == 0 - ); + a != NULL && + b != NULL && + a->length == b->length && + memcmp(a->components, b->components, a->length * sizeof(*a->components)) == 0 + ); } + typedef struct { bip32_path_t bip32_path; cx_curve_t curve; } bip32_path_with_curve_t; +static inline void copy_bip32_path_with_curve(bip32_path_with_curve_t *const out, bip32_path_with_curve_t const *const in) { + check_null(out); + check_null(in); + copy_bip32_path(&out->bip32_path, &in->bip32_path); + out->curve = in->curve; +} + +static inline bool bip32_path_with_curve_eq(bip32_path_with_curve_t const *const a, bip32_path_with_curve_t const *const b) { + return a == b || ( + a != NULL && + b != NULL && + bip32_paths_eq(&a->bip32_path, &b->bip32_path) && + a->curve == b->curve + ); +} + + typedef struct { level_t highest_level; bool had_endorsement; From d89bd7c8a751a34ba0b1fb31a8a1c0238cf2894e Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 71/92] Inline setup globals struct --- src/globals.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/globals.h b/src/globals.h index 9101247d..c6b02986 100644 --- a/src/globals.h +++ b/src/globals.h @@ -22,17 +22,6 @@ struct priv_generate_key_pair { struct key_pair res; }; -struct apdu_setup_globals { - bip32_path_t bip32_path; - cx_curve_t curve; - cx_ecfp_public_key_t public_key; - chain_id_t main_chain_id; - struct { - level_t main; - level_t test; - } hwm; -}; - typedef struct { void *stack_root; apdu_handler handlers[INS_MAX]; @@ -59,7 +48,16 @@ typedef struct { bool hash_only; } sign; - struct apdu_setup_globals setup; + struct { + bip32_path_t bip32_path; + cx_curve_t curve; + cx_ecfp_public_key_t public_key; + chain_id_t main_chain_id; + struct { + level_t main; + level_t test; + } hwm; + } setup; } u; struct { From b53bc82a65d52c95e7c8f0b54197a6ae9658e69d Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 72/92] Unify curve and bip32_path in apdu_setup --- src/apdu_setup.c | 22 +++++++--------------- src/globals.h | 4 +--- src/to_string.c | 9 +++++++++ src/to_string.h | 5 +++++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/apdu_setup.c b/src/apdu_setup.c index 40405518..790e777c 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -23,22 +23,17 @@ struct setup_wire { static bool ok(void) { UPDATE_NVRAM(ram, { - ram->baking_key.curve = G.curve; - copy_bip32_path(&ram->baking_key.bip32_path, &G.bip32_path); + copy_bip32_path_with_curve(&ram->baking_key, &G.key); ram->main_chain_id = G.main_chain_id; ram->hwm.main.highest_level = G.hwm.main; ram->hwm.main.had_endorsement = false; ram->hwm.test.highest_level = G.hwm.test; ram->hwm.test.had_endorsement = false; }); - delayed_send(provide_pubkey(G_io_apdu_buffer, &G.public_key)); - return true; -} -static void pubkey_to_string(char *const out, size_t const out_size, cx_ecfp_public_key_t const *const pubkey) { - check_null(out); - check_null(pubkey); - pubkey_to_pkh_string(out, out_size, G.curve, pubkey); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.key.curve, &G.key.bip32_path); + delayed_send(provide_pubkey(G_io_apdu_buffer, pubkey)); + return true; } #define SET_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) @@ -63,7 +58,7 @@ __attribute__((noreturn)) static void prompt_setup( }; SET_STATIC_UI_VALUE(TYPE_INDEX, "Baking?"); - register_ui_callback(ADDRESS_INDEX, pubkey_to_string, &G.public_key); + register_ui_callback(ADDRESS_INDEX, bip32_path_with_curve_to_pkh_string, &G.key); register_ui_callback(CHAIN_INDEX, chain_id_to_string_with_aliases, &G.main_chain_id); register_ui_callback(MAIN_HWM_INDEX, number_to_string_indirect32, &G.hwm.main); register_ui_callback(TEST_HWM_INDEX, number_to_string_indirect32, &G.hwm.test); @@ -78,7 +73,7 @@ __attribute__((noreturn)) unsigned int handle_apdu_setup(__attribute__((unused)) if (buff_size < sizeof(struct setup_wire)) THROW(EXC_WRONG_LENGTH_FOR_INS); uint8_t const curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_CURVE]); - G.curve = curve_code_to_curve(curve_code); + G.key.curve = curve_code_to_curve(curve_code); { struct setup_wire const *const buff_as_setup = (struct setup_wire const *)&G_io_apdu_buffer[OFFSET_CDATA]; @@ -87,13 +82,10 @@ __attribute__((noreturn)) unsigned int handle_apdu_setup(__attribute__((unused)) G.main_chain_id.v = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->main_chain_id); G.hwm.main = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->hwm.main); G.hwm.test = CONSUME_UNALIGNED_BIG_ENDIAN(consumed, uint32_t, (uint8_t const *)&buff_as_setup->hwm.test); - consumed += read_bip32_path(&G.bip32_path, (uint8_t const *)&buff_as_setup->bip32_path, buff_size - consumed); + consumed += read_bip32_path(&G.key.bip32_path, (uint8_t const *)&buff_as_setup->bip32_path, buff_size - consumed); if (consumed != buff_size) THROW(EXC_WRONG_LENGTH); } - cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.curve, &G.bip32_path); - memcpy(&G.public_key, pubkey, sizeof(G.public_key)); - prompt_setup(ok, delay_reject); } diff --git a/src/globals.h b/src/globals.h index c6b02986..7116ad1b 100644 --- a/src/globals.h +++ b/src/globals.h @@ -49,9 +49,7 @@ typedef struct { } sign; struct { - bip32_path_t bip32_path; - cx_curve_t curve; - cx_ecfp_public_key_t public_key; + bip32_path_with_curve_t key; chain_id_t main_chain_id; struct { level_t main; diff --git a/src/to_string.c b/src/to_string.c index 5e80d20a..83033a94 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -42,6 +42,15 @@ void pubkey_to_pkh_string( pkh_to_string(out, out_size, curve, hash); } +void bip32_path_with_curve_to_pkh_string(char *const out, size_t const out_size, bip32_path_with_curve_t const *const key) { + check_null(out); + check_null(key); + + cx_ecfp_public_key_t const *const pubkey = generate_public_key(key->curve, &key->bip32_path); + pubkey_to_pkh_string(out, out_size, key->curve, pubkey); +} + + void compute_hash_checksum(uint8_t out[TEZOS_HASH_CHECKSUM_SIZE], void const *const data, size_t size) { uint8_t checksum[32]; cx_hash_sha256(data, size, checksum, sizeof(checksum)); diff --git a/src/to_string.h b/src/to_string.h index 5eccda14..a250f263 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -15,6 +15,11 @@ void pubkey_to_pkh_string( cx_curve_t const curve, cx_ecfp_public_key_t const *const public_key ); +void bip32_path_with_curve_to_pkh_string( + char *const out, + size_t const out_size, + bip32_path_with_curve_t const *const key +); void protocol_hash_to_string(char *buff, const size_t buff_size, const uint8_t hash[PROTOCOL_HASH_SIZE]); void parsed_contract_to_string(char *buff, uint32_t buff_size, const struct parsed_contract *contract); void chain_id_to_string(char *buff, size_t const buff_size, chain_id_t const chain_id); From 0e02ee20f051797f62a5ab1e6ea21dc86ff55039 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 73/92] Unify curve and bip32_path in apdu_pubkey --- src/apdu_pubkey.c | 15 +++++++-------- src/globals.h | 3 +-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index b5e93562..677c6d20 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -18,7 +18,7 @@ static bool pubkey_ok(void) { #ifdef BAKING_APP static bool baking_ok(void) { - authorize_baking(G.curve, &G.bip32_path); + authorize_baking(G.key.curve, &G.key.bip32_path); pubkey_ok(); return true; } @@ -33,22 +33,21 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { if (instruction == INS_GET_PUBLIC_KEY) require_hid(); uint8_t const curve_code = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_CURVE]); - G.curve = curve_code_to_curve(curve_code); + G.key.curve = curve_code_to_curve(curve_code); size_t const cdata_size = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]); #ifdef BAKING_APP if (cdata_size == 0 && instruction == INS_AUTHORIZE_BAKING) { - G.curve = N_data.baking_key.curve; - copy_bip32_path(&G.bip32_path, &N_data.baking_key.bip32_path); + copy_bip32_path_with_curve(&G.key, &N_data.baking_key); } else { #endif - read_bip32_path(&G.bip32_path, dataBuffer, cdata_size); + read_bip32_path(&G.key.bip32_path, dataBuffer, cdata_size); #ifdef BAKING_APP - if (G.bip32_path.length == 0) THROW(EXC_WRONG_LENGTH_FOR_INS); + if (G.key.bip32_path.length == 0) THROW(EXC_WRONG_LENGTH_FOR_INS); } #endif - cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.curve, &G.bip32_path); + cx_ecfp_public_key_t const *const pubkey = generate_public_key(G.key.curve, &G.key.bip32_path); memcpy(&G.public_key, pubkey, sizeof(G.public_key)); if (instruction == INS_GET_PUBLIC_KEY) { @@ -69,6 +68,6 @@ unsigned int handle_apdu_get_public_key(uint8_t instruction) { #ifdef BAKING_APP } #endif - prompt_address(bake, G.curve, &G.public_key, cb, delay_reject); + prompt_address(bake, G.key.curve, &G.public_key, cb, delay_reject); } } diff --git a/src/globals.h b/src/globals.h index 7116ad1b..5a02b142 100644 --- a/src/globals.h +++ b/src/globals.h @@ -32,9 +32,8 @@ typedef struct { } baking; struct { + bip32_path_with_curve_t key; cx_ecfp_public_key_t public_key; - bip32_path_t bip32_path; - cx_curve_t curve; } pubkey; struct { From 1fa87a35f651ab8cc6937229b9d03bb08d828672 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 74/92] Move SET_STATIC_UI_VALUE to a place it can be shared --- src/apdu_setup.c | 4 +--- src/apdu_sign.c | 22 ++++++++++------------ src/ui_prompt.h | 1 + 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/apdu_setup.c b/src/apdu_setup.c index 790e777c..9d0f3153 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -36,8 +36,6 @@ static bool ok(void) { return true; } -#define SET_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) - __attribute__((noreturn)) static void prompt_setup( ui_callback_t const ok_cb, ui_callback_t const cxl_cb) @@ -57,7 +55,7 @@ __attribute__((noreturn)) static void prompt_setup( NULL, }; - SET_STATIC_UI_VALUE(TYPE_INDEX, "Baking?"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Baking?"); register_ui_callback(ADDRESS_INDEX, bip32_path_with_curve_to_pkh_string, &G.key); register_ui_callback(CHAIN_INDEX, chain_id_to_string_with_aliases, &G.main_chain_id); register_ui_callback(MAIN_HWM_INDEX, number_to_string_indirect32, &G.hwm.main); diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 43f9d598..9eb3f732 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -142,8 +142,6 @@ const char *const insecure_values[] = { #define MAX_NUMBER_CHARS (MAX_INT_DIGITS + 2) // include decimal point and terminating null -#define SET_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) - // Return false if the transaction isn't easily parseable, otherwise prompt with given callbacks // and do not return, but rather throw ASYNC. static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve, @@ -206,7 +204,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve register_ui_callback(PERIOD_INDEX, number_to_string_indirect32, &ops->operation.proposal.voting_period); register_ui_callback(PROTOCOL_HASH_INDEX, protocol_hash_to_string, ops->operation.proposal.protocol_hash); - SET_STATIC_UI_VALUE(TYPE_INDEX, "Proposal"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Proposal"); ui_prompt(proposal_prompts, NULL, ok, cxl); } @@ -232,13 +230,13 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve switch (ops->operation.ballot.vote) { case BALLOT_VOTE_YEA: - SET_STATIC_UI_VALUE(TYPE_INDEX, "Yea"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Yea"); break; case BALLOT_VOTE_NAY: - SET_STATIC_UI_VALUE(TYPE_INDEX, "Nay"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Nay"); break; case BALLOT_VOTE_PASS: - SET_STATIC_UI_VALUE(TYPE_INDEX, "Pass"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Pass"); break; } @@ -295,7 +293,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve if (!(ops->operation.flags & ORIGINATION_FLAG_SPENDABLE)) return false; - SET_STATIC_UI_VALUE(TYPE_INDEX, "Origination"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Origination"); register_ui_callback(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); const char *const *prompts; @@ -307,14 +305,14 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve &ops->operation.delegate); } else if (delegatable && !has_delegate) { prompts = origination_prompts_delegatable; - SET_STATIC_UI_VALUE(DELEGATE_INDEX, "Any"); + REGISTER_STATIC_UI_VALUE(DELEGATE_INDEX, "Any"); } else if (!delegatable && has_delegate) { prompts = origination_prompts_fixed; register_ui_callback(DELEGATE_INDEX, parsed_contract_to_string, &ops->operation.delegate); } else if (!delegatable && !has_delegate) { prompts = origination_prompts_undelegatable; - SET_STATIC_UI_VALUE(DELEGATE_INDEX, "Disabled"); + REGISTER_STATIC_UI_VALUE(DELEGATE_INDEX, "Disabled"); } ui_prompt(prompts, NULL, ok, cxl); @@ -352,7 +350,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - SET_STATIC_UI_VALUE(TYPE_INDEX, "Delegation"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Delegation"); bool withdrawal = ops->operation.destination.originated == 0 && ops->operation.destination.curve_code == TEZOS_NO_CURVE; @@ -387,7 +385,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve &ops->total_storage_limit); register_ui_callback(AMOUNT_INDEX, microtez_to_string_indirect, &ops->operation.amount); - SET_STATIC_UI_VALUE(TYPE_INDEX, "Transaction"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "Transaction"); ui_prompt(transaction_prompts, NULL, ok, cxl); } @@ -410,7 +408,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve NULL, }; - SET_STATIC_UI_VALUE(TYPE_INDEX, "To Blockchain"); + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "To Blockchain"); register_ui_callback(STORAGE_INDEX, number_to_string_indirect64, &ops->total_storage_limit); register_ui_callback(SOURCE_INDEX, parsed_contract_to_string, &ops->operation.source); diff --git a/src/ui_prompt.h b/src/ui_prompt.h index bc05e8a8..54861fec 100644 --- a/src/ui_prompt.h +++ b/src/ui_prompt.h @@ -18,3 +18,4 @@ void clear_ui_callbacks(void); // This function registers how a value is to be produced void register_ui_callback(uint32_t which, string_generation_callback cb, const void *data); +#define REGISTER_STATIC_UI_VALUE(index, str) register_ui_callback(index, copy_string, STATIC_UI_VALUE(str)) From 56caf976f26dcebf05bbc0d7c0941294bc737baf Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 6 Feb 2019 18:21:01 -0500 Subject: [PATCH 75/92] Break authorize-to-bake via register-as-delegate Change logic in baking app to merely sign a register-as-delegate operation instead of having it also authorize the ledger to bake at that address. This now requires that the ledger already be authorized to bake for the delegate address. --- src/apdu_sign.c | 51 ++++++++++++++++++++++++++++++++----------------- src/globals.h | 4 ++++ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 9eb3f732..b28c4d78 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -52,20 +52,13 @@ static void finish_hashing(uint8_t *hash, size_t hash_size) { static int perform_signature(bool hash_first); -#ifdef BAKING_APP -static bool bake_auth_ok(void) { - authorize_baking(G.curve, &G.bip32_path); - int tx = perform_signature(true); - delayed_send(tx); - return true; -} -#else static bool sign_ok(void) { int tx = perform_signature(true); delayed_send(tx); return true; } +#ifndef BAKING_APP static bool sign_unsafe_ok(void) { int tx = perform_signature(false); delayed_send(tx); @@ -88,6 +81,29 @@ static bool sign_reject(void) { } #ifdef BAKING_APP + +__attribute__((noreturn)) static void prompt_register_delegate( + ui_callback_t const ok_cb, + ui_callback_t const cxl_cb +) { + static const size_t TYPE_INDEX = 0; + static const size_t ADDRESS_INDEX = 1; + static const size_t FEE_INDEX = 2; + + static const char *const prompts[] = { + PROMPT("Register"), + PROMPT("Address"), + PROMPT("Fee"), + NULL, + }; + + REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "as delegate?"); + register_ui_callback(ADDRESS_INDEX, bip32_path_with_curve_to_pkh_string, &G.key); + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &G.register_delegate.total_fee); + + ui_prompt(prompts, NULL, ok_cb, cxl_cb); +} + uint32_t baking_sign_complete(void) { // Have raw data, can get insight into it switch (G.magic_number) { @@ -111,18 +127,17 @@ uint32_t baking_sign_complete(void) { G.message_data, G.message_data_length, G.key.curve, &G.key.bip32_path, allowed); - // With < nickel fee - if (ops->total_fee > 50000) THROW(EXC_PARSE_ERROR); + // Must be self-delegation signed by the *authorized* baking key + if (bip32_path_with_curve_eq(&G.key, &N_data.baking_key) && - // Must be self-delegation signed by the same key - if (COMPARE(&ops->operation.source, &ops->signing)) { - THROW(EXC_PARSE_ERROR); - } - if (COMPARE(&ops->operation.destination, &ops->signing)) { - THROW(EXC_PARSE_ERROR); + // ops->signing is generated from G.bip32_path and G.curve + COMPARE(&ops->operation.source, &ops->signing) == 0 && + COMPARE(&ops->operation.destination, &ops->signing) == 0 + ) { + G.register_delegate.total_fee = ops->total_fee; + prompt_register_delegate(sign_ok, sign_reject); } - - prompt_contract_for_baking(&ops->signing, bake_auth_ok, sign_reject); + THROW(EXC_PARSE_ERROR); } case MAGIC_BYTE_UNSAFE_OP2: case MAGIC_BYTE_UNSAFE_OP3: diff --git a/src/globals.h b/src/globals.h index 5a02b142..2f29d61b 100644 --- a/src/globals.h +++ b/src/globals.h @@ -45,6 +45,10 @@ typedef struct { bool is_hash_state_inited; uint8_t magic_number; bool hash_only; + + struct { + uint64_t total_fee; + } register_delegate; } sign; struct { From a6fe2e31e8bd424c8b284ecdc5252d65a6eb1ff4 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Mon, 11 Feb 2019 17:54:44 -0500 Subject: [PATCH 76/92] Throw security error in baking app when trying to sign unauthorized self-delegation --- src/apdu_sign.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index b28c4d78..fd6a6b9e 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -137,7 +137,7 @@ uint32_t baking_sign_complete(void) { G.register_delegate.total_fee = ops->total_fee; prompt_register_delegate(sign_ok, sign_reject); } - THROW(EXC_PARSE_ERROR); + THROW(EXC_SECURITY); } case MAGIC_BYTE_UNSAFE_OP2: case MAGIC_BYTE_UNSAFE_OP3: From b6977cb78331ba1e105a435106ac182a957da5eb Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 20 Feb 2019 10:20:07 -0500 Subject: [PATCH 77/92] Add deauthorize APDU instruction to baking app --- src/apdu.h | 1 + src/apdu_baking.c | 13 +++++++++++++ src/apdu_baking.h | 1 + src/globals.h | 2 +- src/main.c | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/apdu.h b/src/apdu.h index 637b45b8..297fc7b1 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -34,6 +34,7 @@ #define INS_GIT 0x09 #define INS_SETUP 0x0A #define INS_QUERY_ALL_HWM 0x0B +#define INS_DEAUTHORIZE 0x0C __attribute__((noreturn)) void main_loop(apdu_handler handlers[INS_MAX]); diff --git a/src/apdu_baking.c b/src/apdu_baking.c index b6bde9f6..d601c5d2 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -98,3 +98,16 @@ unsigned int handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruct G_io_apdu_buffer[tx++] = 0x00; return tx; } + +unsigned int handle_apdu_deauthorize(__attribute__((unused)) uint8_t instruction) { + if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); + if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]) != 0) THROW(EXC_PARSE_ERROR); + UPDATE_NVRAM(ram, { + memset(&ram->baking_key, 0, sizeof(ram->baking_key)); + }); + + unsigned int tx = 0; + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + return tx; +} diff --git a/src/apdu_baking.h b/src/apdu_baking.h index 632d8690..75bc954c 100644 --- a/src/apdu_baking.h +++ b/src/apdu_baking.h @@ -6,3 +6,4 @@ unsigned int handle_apdu_reset(uint8_t instruction); unsigned int handle_apdu_query_auth_key(uint8_t instruction); unsigned int handle_apdu_main_hwm(uint8_t instruction); unsigned int handle_apdu_all_hwm(uint8_t instruction); +unsigned int handle_apdu_deauthorize(uint8_t instruction); diff --git a/src/globals.h b/src/globals.h index 2f29d61b..977518bf 100644 --- a/src/globals.h +++ b/src/globals.h @@ -24,7 +24,7 @@ struct priv_generate_key_pair { typedef struct { void *stack_root; - apdu_handler handlers[INS_MAX]; + apdu_handler handlers[INS_MAX + 1]; union { struct { diff --git a/src/main.c b/src/main.c index 38d5780f..78d28d9d 100644 --- a/src/main.c +++ b/src/main.c @@ -41,6 +41,7 @@ void app_main(void) { global.handlers[APDU_INS(INS_QUERY_MAIN_HWM)] = handle_apdu_main_hwm; global.handlers[APDU_INS(INS_SETUP)] = handle_apdu_setup; global.handlers[APDU_INS(INS_QUERY_ALL_HWM)] = handle_apdu_all_hwm; + global.handlers[APDU_INS(INS_DEAUTHORIZE)] = handle_apdu_deauthorize; #else global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif From a1eea3fbf52bacb95435899a56d96836aa18e083 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 20 Feb 2019 11:12:11 -0500 Subject: [PATCH 78/92] Factor out common APDU code and normalize --- src/apdu.c | 46 ++++++++++++++++++++-------------------------- src/apdu.h | 23 +++++++++++++++-------- src/apdu_baking.c | 41 ++++++++++++++--------------------------- src/apdu_baking.h | 11 ++++++----- src/apdu_pubkey.c | 2 +- src/apdu_pubkey.h | 2 +- src/apdu_setup.c | 2 +- src/apdu_setup.h | 3 ++- src/apdu_sign.c | 22 +++++++--------------- src/apdu_sign.h | 2 +- src/main.c | 2 +- src/types.h | 2 +- 12 files changed, 70 insertions(+), 88 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 1af9a409..81e9ea53 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -10,39 +10,31 @@ size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *cons io_buffer[tx++] = pubkey->W_len; memmove(io_buffer + tx, pubkey->W, pubkey->W_len); tx += pubkey->W_len; - io_buffer[tx++] = 0x90; - io_buffer[tx++] = 0x00; - return tx; + return finalize_successful_send(tx); } -unsigned int handle_apdu_error(uint8_t __attribute__((unused)) instruction) { +size_t handle_apdu_error(uint8_t __attribute__((unused)) instruction) { THROW(EXC_INVALID_INS); } -unsigned int handle_apdu_version(uint8_t __attribute__((unused)) instruction) { - int tx = 0; +size_t handle_apdu_version(uint8_t __attribute__((unused)) instruction) { memcpy(G_io_apdu_buffer, &version, sizeof(version_t)); - tx += sizeof(version_t); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + size_t tx = sizeof(version_t); + return finalize_successful_send(tx); } -unsigned int handle_apdu_git(uint8_t __attribute__((unused)) instruction) { +size_t handle_apdu_git(uint8_t __attribute__((unused)) instruction) { static const char commit[] = COMMIT; memcpy(G_io_apdu_buffer, commit, sizeof(commit)); - - uint32_t tx = sizeof(commit); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + size_t tx = sizeof(commit); + return finalize_successful_send(tx); } #define CLA 0x80 __attribute__((noreturn)) -void main_loop(apdu_handler handlers[INS_MAX]) { - volatile uint32_t rx = io_exchange(CHANNEL_APDU, 0); +void main_loop(apdu_handler const *const handlers, size_t const handlers_size) { + volatile size_t rx = io_exchange(CHANNEL_APDU, 0); while (true) { BEGIN_TRY { TRY { @@ -65,12 +57,12 @@ void main_loop(apdu_handler handlers[INS_MAX]) { THROW(EXC_WRONG_LENGTH); } - const uint8_t instruction = G_io_apdu_buffer[OFFSET_INS]; - const apdu_handler cb = (instruction >= INS_MAX) + uint8_t const instruction = G_io_apdu_buffer[OFFSET_INS]; + apdu_handler const cb = instruction >= handlers_size ? handle_apdu_error : handlers[instruction]; - const uint32_t tx = cb(instruction); + size_t const tx = cb(instruction); rx = io_exchange(CHANNEL_APDU, tx); } CATCH(ASYNC_EXCEPTION) { @@ -83,11 +75,13 @@ void main_loop(apdu_handler handlers[INS_MAX]) { sw = 0x6800 | (e & 0x7FF); // FALL THROUGH case 0x6000 ... 0x6FFF: - case 0x9000 ... 0x9FFF: - G_io_apdu_buffer[0] = sw >> 8; - G_io_apdu_buffer[1] = sw; - rx = io_exchange(CHANNEL_APDU, 2); - break; + case 0x9000 ... 0x9FFF: { + size_t tx = 0; + G_io_apdu_buffer[tx++] = sw >> 8; + G_io_apdu_buffer[tx++] = sw; + rx = io_exchange(CHANNEL_APDU, tx); + break; + } } } FINALLY { diff --git a/src/apdu.h b/src/apdu.h index 297fc7b1..d3074def 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -37,21 +37,28 @@ #define INS_DEAUTHORIZE 0x0C __attribute__((noreturn)) -void main_loop(apdu_handler handlers[INS_MAX]); +void main_loop(apdu_handler const *const handlers, size_t const handlers_size); static inline void return_ok(void) { THROW(EXC_NO_ERROR); } +static inline size_t finalize_successful_send(size_t tx) { + G_io_apdu_buffer[tx++] = 0x90; + G_io_apdu_buffer[tx++] = 0x00; + return tx; +} + // Send back response; do not restart the event loop -static inline void delayed_send(uint32_t tx) { +static inline void delayed_send(size_t tx) { io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); } static inline bool delay_reject(void) { - G_io_apdu_buffer[0] = EXC_REJECT >> 8; - G_io_apdu_buffer[1] = EXC_REJECT & 0xFF; - delayed_send(2); + size_t tx = 0; + G_io_apdu_buffer[tx++] = EXC_REJECT >> 8; + G_io_apdu_buffer[tx++] = EXC_REJECT & 0xFF; + delayed_send(tx); return true; } @@ -63,6 +70,6 @@ static inline void require_hid(void) { size_t provide_pubkey(uint8_t *const io_buffer, cx_ecfp_public_key_t const *const pubkey); -uint32_t handle_apdu_error(uint8_t instruction); -uint32_t handle_apdu_version(uint8_t instruction); -uint32_t handle_apdu_git(uint8_t instruction); +size_t handle_apdu_error(uint8_t instruction); +size_t handle_apdu_version(uint8_t instruction); +size_t handle_apdu_git(uint8_t instruction); diff --git a/src/apdu_baking.c b/src/apdu_baking.c index d601c5d2..7cfe99de 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -14,7 +14,7 @@ static bool reset_ok(void); -unsigned int handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { +size_t handle_apdu_reset(__attribute__((unused)) uint8_t instruction) { uint8_t *dataBuffer = G_io_apdu_buffer + OFFSET_CDATA; uint32_t dataLength = G_io_apdu_buffer[OFFSET_LC]; if (dataLength != sizeof(int)) { @@ -42,16 +42,12 @@ bool reset_ok(void) { ram->hwm.test.had_endorsement = false; }); - uint32_t tx = 0; - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - // Send back the response, do not restart the event loop - delayed_send(tx); + delayed_send(finalize_successful_send(0)); return true; } -uint32_t send_word_big_endian(uint32_t tx, uint32_t word) { +size_t send_word_big_endian(size_t tx, uint32_t word) { char word_bytes[sizeof(word)]; memcpy(word_bytes, &word, sizeof(word)); @@ -65,49 +61,40 @@ uint32_t send_word_big_endian(uint32_t tx, uint32_t word) { return tx + i; } -unsigned int handle_apdu_all_hwm(__attribute__((unused)) uint8_t instruction) { - uint32_t tx = 0; +size_t handle_apdu_all_hwm(__attribute__((unused)) uint8_t instruction) { + size_t tx = 0; tx = send_word_big_endian(tx, N_data.hwm.main.highest_level); tx = send_word_big_endian(tx, N_data.hwm.test.highest_level); tx = send_word_big_endian(tx, N_data.main_chain_id.v); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + return finalize_successful_send(tx); } -unsigned int handle_apdu_main_hwm(__attribute__((unused)) uint8_t instruction) { - uint32_t tx = 0; +size_t handle_apdu_main_hwm(__attribute__((unused)) uint8_t instruction) { + size_t tx = 0; tx = send_word_big_endian(tx, N_data.hwm.main.highest_level); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + return finalize_successful_send(tx); } -unsigned int handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { +size_t handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { uint8_t const length = N_data.baking_key.bip32_path.length; - uint32_t tx = 0; + size_t tx = 0; G_io_apdu_buffer[tx++] = length; for (uint8_t i = 0; i < length; ++i) { tx = send_word_big_endian(tx, N_data.baking_key.bip32_path.components[i]); } - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + return finalize_successful_send(tx); } -unsigned int handle_apdu_deauthorize(__attribute__((unused)) uint8_t instruction) { +size_t handle_apdu_deauthorize(__attribute__((unused)) uint8_t instruction) { if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]) != 0) THROW(EXC_PARSE_ERROR); UPDATE_NVRAM(ram, { memset(&ram->baking_key, 0, sizeof(ram->baking_key)); }); - unsigned int tx = 0; - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + return finalize_successful_send(0); } diff --git a/src/apdu_baking.h b/src/apdu_baking.h index 75bc954c..4234184e 100644 --- a/src/apdu_baking.h +++ b/src/apdu_baking.h @@ -1,9 +1,10 @@ #pragma once +#include #include -unsigned int handle_apdu_reset(uint8_t instruction); -unsigned int handle_apdu_query_auth_key(uint8_t instruction); -unsigned int handle_apdu_main_hwm(uint8_t instruction); -unsigned int handle_apdu_all_hwm(uint8_t instruction); -unsigned int handle_apdu_deauthorize(uint8_t instruction); +size_t handle_apdu_reset(uint8_t instruction); +size_t handle_apdu_query_auth_key(uint8_t instruction); +size_t handle_apdu_main_hwm(uint8_t instruction); +size_t handle_apdu_all_hwm(uint8_t instruction); +size_t handle_apdu_deauthorize(uint8_t instruction); diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 677c6d20..3105fbe1 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -24,7 +24,7 @@ static bool baking_ok(void) { } #endif -unsigned int handle_apdu_get_public_key(uint8_t instruction) { +size_t handle_apdu_get_public_key(uint8_t instruction) { uint8_t *dataBuffer = G_io_apdu_buffer + OFFSET_CDATA; if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); diff --git a/src/apdu_pubkey.h b/src/apdu_pubkey.h index 460ca218..ac58d796 100644 --- a/src/apdu_pubkey.h +++ b/src/apdu_pubkey.h @@ -2,4 +2,4 @@ #include "apdu.h" -unsigned int handle_apdu_get_public_key(uint8_t instruction); +size_t handle_apdu_get_public_key(uint8_t instruction); diff --git a/src/apdu_setup.c b/src/apdu_setup.c index 9d0f3153..bcc9ac30 100644 --- a/src/apdu_setup.c +++ b/src/apdu_setup.c @@ -64,7 +64,7 @@ __attribute__((noreturn)) static void prompt_setup( ui_prompt(prompts, NULL, ok_cb, cxl_cb); } -__attribute__((noreturn)) unsigned int handle_apdu_setup(__attribute__((unused)) uint8_t instruction) { +__attribute__((noreturn)) size_t handle_apdu_setup(__attribute__((unused)) uint8_t instruction) { if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); uint32_t const buff_size = READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]); diff --git a/src/apdu_setup.h b/src/apdu_setup.h index a61beb1b..fd2d2d0a 100644 --- a/src/apdu_setup.h +++ b/src/apdu_setup.h @@ -1,5 +1,6 @@ #pragma once +#include #include -unsigned int handle_apdu_setup(uint8_t instruction); +size_t handle_apdu_setup(uint8_t instruction); diff --git a/src/apdu_sign.c b/src/apdu_sign.c index fd6a6b9e..a6c24e82 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -53,15 +53,13 @@ static void finish_hashing(uint8_t *hash, size_t hash_size) { static int perform_signature(bool hash_first); static bool sign_ok(void) { - int tx = perform_signature(true); - delayed_send(tx); + delayed_send(perform_signature(true)); return true; } #ifndef BAKING_APP static bool sign_unsafe_ok(void) { - int tx = perform_signature(false); - delayed_send(tx); + delayed_send(perform_signature(false)); return true; } #endif @@ -477,7 +475,7 @@ uint32_t wallet_sign_complete(uint8_t instruction) { #define P1_HASH_ONLY_NEXT 0x03 // You only need it once #define P1_LAST_MARKER 0x80 -unsigned int handle_apdu_sign(uint8_t instruction) { +size_t handle_apdu_sign(uint8_t instruction) { uint8_t p1 = G_io_apdu_buffer[OFFSET_P1]; uint8_t *dataBuffer = G_io_apdu_buffer + OFFSET_CDATA; uint32_t dataLength = G_io_apdu_buffer[OFFSET_LC]; @@ -552,18 +550,15 @@ static int perform_signature(bool hash_first) { #ifndef BAKING_APP if (G.hash_only) { memcpy(G_io_apdu_buffer, data, datalen); - uint32_t tx = datalen; - - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - return tx; + size_t tx = datalen; + return finalize_successful_send(tx); } #endif } struct key_pair *const pair = generate_key_pair(G.key.curve, &G.key.bip32_path); - uint32_t tx; + size_t tx = 0; switch (G.key.curve) { case CX_CURVE_Ed25519: { tx = cx_eddsa_sign(&pair->private_key, @@ -601,10 +596,7 @@ static int perform_signature(bool hash_first) { memset(&pair->private_key, 0, sizeof(pair->private_key)); - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - clear_data(); - return tx; + return finalize_successful_send(tx); } diff --git a/src/apdu_sign.h b/src/apdu_sign.h index 0e5ad17c..8c3b8583 100644 --- a/src/apdu_sign.h +++ b/src/apdu_sign.h @@ -2,4 +2,4 @@ #include "apdu.h" -unsigned int handle_apdu_sign(uint8_t instruction); +size_t handle_apdu_sign(uint8_t instruction); diff --git a/src/main.c b/src/main.c index 78d28d9d..c2a40862 100644 --- a/src/main.c +++ b/src/main.c @@ -45,5 +45,5 @@ void app_main(void) { #else global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif - main_loop(global.handlers); + main_loop(global.handlers, sizeof(global.handlers)); } diff --git a/src/types.h b/src/types.h index 6a37ad28..96326cf3 100644 --- a/src/types.h +++ b/src/types.h @@ -8,7 +8,7 @@ #include // Return number of bytes to transmit (tx) -typedef uint32_t (*apdu_handler)(uint8_t instruction); +typedef size_t (*apdu_handler)(uint8_t instruction); typedef uint32_t level_t; From e7b6dde185c803f8beddd7e0e42da4ce026e117a Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Wed, 20 Feb 2019 11:54:20 -0500 Subject: [PATCH 79/92] Don't use exceptions to return success --- src/apdu.h | 4 ---- src/apdu_sign.c | 4 ++-- src/exception.h | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index d3074def..2ee01351 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -39,10 +39,6 @@ __attribute__((noreturn)) void main_loop(apdu_handler const *const handlers, size_t const handlers_size); -static inline void return_ok(void) { - THROW(EXC_NO_ERROR); -} - static inline size_t finalize_successful_send(size_t tx) { G_io_apdu_buffer[tx++] = 0x90; G_io_apdu_buffer[tx++] = 0x00; diff --git a/src/apdu_sign.c b/src/apdu_sign.c index a6c24e82..4f9dc7bc 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -488,7 +488,7 @@ size_t handle_apdu_sign(uint8_t instruction) { G.message_data_length = 0; read_bip32_path(&G.key.bip32_path, dataBuffer, dataLength); G.key.curve = curve_code_to_curve(G_io_apdu_buffer[OFFSET_CURVE]); - return_ok(); + return finalize_successful_send(0); #ifndef BAKING_APP case P1_HASH_ONLY_NEXT: // This is a debugging Easter egg @@ -522,7 +522,7 @@ size_t handle_apdu_sign(uint8_t instruction) { G.message_data_length += dataLength; if (!last) { - return_ok(); + return finalize_successful_send(0); } #ifdef BAKING_APP diff --git a/src/exception.h b/src/exception.h index ddd59292..b19397a3 100644 --- a/src/exception.h +++ b/src/exception.h @@ -15,7 +15,6 @@ #define EXC_SECURITY 0x6982 #define EXC_HID_REQUIRED 0x6983 #define EXC_CLASS 0x6E00 -#define EXC_NO_ERROR 0x9000 #define EXC_MEMORY_ERROR 0x9200 // Crashes can be harder to debug than exceptions and latency isn't a big concern From 4850c294e9b8f3225878c837f387ab78f256f32e Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 21 Feb 2019 12:41:28 -0500 Subject: [PATCH 80/92] Add APDU to return both curve and path of authorized baking key --- src/apdu.h | 1 + src/apdu_baking.c | 16 ++++++++++++++++ src/apdu_baking.h | 1 + src/exception.h | 4 ++++ src/keys.h | 2 +- src/main.c | 1 + src/operations.c | 1 + src/types.h | 2 +- 8 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index 2ee01351..14233d04 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -35,6 +35,7 @@ #define INS_SETUP 0x0A #define INS_QUERY_ALL_HWM 0x0B #define INS_DEAUTHORIZE 0x0C +#define INS_QUERY_AUTH_KEY_WITH_CURVE 0x0D __attribute__((noreturn)) void main_loop(apdu_handler const *const handlers, size_t const handlers_size); diff --git a/src/apdu_baking.c b/src/apdu_baking.c index 7cfe99de..665076d7 100644 --- a/src/apdu_baking.c +++ b/src/apdu_baking.c @@ -89,6 +89,22 @@ size_t handle_apdu_query_auth_key(__attribute__((unused)) uint8_t instruction) { return finalize_successful_send(tx); } +size_t handle_apdu_query_auth_key_with_curve(__attribute__((unused)) uint8_t instruction) { + uint8_t const curve_code = curve_to_curve_code(N_data.baking_key.curve); + if (curve_code == TEZOS_NO_CURVE) THROW(EXC_REFERENCED_DATA_NOT_FOUND); + + uint8_t const length = N_data.baking_key.bip32_path.length; + + size_t tx = 0; + G_io_apdu_buffer[tx++] = curve_code; + G_io_apdu_buffer[tx++] = length; + for (uint8_t i = 0; i < length; ++i) { + tx = send_word_big_endian(tx, N_data.baking_key.bip32_path.components[i]); + } + + return finalize_successful_send(tx); +} + size_t handle_apdu_deauthorize(__attribute__((unused)) uint8_t instruction) { if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_P1]) != 0) THROW(EXC_WRONG_PARAM); if (READ_UNALIGNED_BIG_ENDIAN(uint8_t, &G_io_apdu_buffer[OFFSET_LC]) != 0) THROW(EXC_PARSE_ERROR); diff --git a/src/apdu_baking.h b/src/apdu_baking.h index 4234184e..afff5f32 100644 --- a/src/apdu_baking.h +++ b/src/apdu_baking.h @@ -5,6 +5,7 @@ size_t handle_apdu_reset(uint8_t instruction); size_t handle_apdu_query_auth_key(uint8_t instruction); +size_t handle_apdu_query_auth_key_with_curve(uint8_t instruction); size_t handle_apdu_main_hwm(uint8_t instruction); size_t handle_apdu_all_hwm(uint8_t instruction); size_t handle_apdu_deauthorize(uint8_t instruction); diff --git a/src/exception.h b/src/exception.h index b19397a3..cb2cd9e5 100644 --- a/src/exception.h +++ b/src/exception.h @@ -5,12 +5,16 @@ // Throw this to indicate prompting #define ASYNC_EXCEPTION 0x2000 +// Standard APDU error codes: +// https://www.eftlab.co.uk/index.php/site-map/knowledge-base/118-apdu-response-list + #define EXC_WRONG_PARAM 0x6B00 #define EXC_WRONG_LENGTH 0x6C00 #define EXC_INVALID_INS 0x6D00 #define EXC_WRONG_LENGTH_FOR_INS 0x917E #define EXC_REJECT 0x6985 #define EXC_PARSE_ERROR 0x9405 +#define EXC_REFERENCED_DATA_NOT_FOUND 0x6A88 #define EXC_WRONG_VALUES 0x6A80 #define EXC_SECURITY 0x6982 #define EXC_HID_REQUIRED 0x6983 diff --git a/src/keys.h b/src/keys.h index 609c8c60..ec6763cf 100644 --- a/src/keys.h +++ b/src/keys.h @@ -39,7 +39,7 @@ static inline uint8_t curve_to_curve_code(cx_curve_t curve) { case CX_CURVE_SECP256R1: return TEZOS_SECP256R1; default: - THROW(EXC_MEMORY_ERROR); + return TEZOS_NO_CURVE; } } diff --git a/src/main.c b/src/main.c index c2a40862..4de5e543 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ void app_main(void) { global.handlers[APDU_INS(INS_SETUP)] = handle_apdu_setup; global.handlers[APDU_INS(INS_QUERY_ALL_HWM)] = handle_apdu_all_hwm; global.handlers[APDU_INS(INS_DEAUTHORIZE)] = handle_apdu_deauthorize; + global.handlers[APDU_INS(INS_QUERY_AUTH_KEY_WITH_CURVE)] = handle_apdu_query_auth_key_with_curve; #else global.handlers[APDU_INS(INS_SIGN_UNSAFE)] = handle_apdu_sign; #endif diff --git a/src/operations.c b/src/operations.c index afff3fcb..104eda1d 100644 --- a/src/operations.c +++ b/src/operations.c @@ -117,6 +117,7 @@ static inline void compute_pkh( cx_ecfp_public_key_t const *const pubkey = generate_public_key(curve, bip32_path); cx_ecfp_public_key_t const *const compressed_pubkey = public_key_hash(contract_out->hash, curve, pubkey); contract_out->curve_code = curve_to_curve_code(curve); + if (contract_out->curve_code == TEZOS_NO_CURVE) THROW(EXC_MEMORY_ERROR); contract_out->originated = 0; memcpy(compressed_pubkey_out, compressed_pubkey, sizeof(*compressed_pubkey_out)); diff --git a/src/types.h b/src/types.h index 96326cf3..7a11cb8a 100644 --- a/src/types.h +++ b/src/types.h @@ -184,7 +184,7 @@ struct parsed_operation_group { }; // Maximum number of APDU instructions -#define INS_MAX 0x0C +#define INS_MAX 0x0D #define APDU_INS(x) ({ \ _Static_assert(x <= INS_MAX, "APDU instruction is out of bounds"); \ From d782c57270e204f77eae22524805b5d10ab725a2 Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Thu, 28 Feb 2019 15:49:30 -0500 Subject: [PATCH 81/92] Include note on importing fundraiser account --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bfe11d5c..a002c5d9 100644 --- a/README.md +++ b/README.md @@ -963,6 +963,10 @@ We need as much context as possible to help troubleshoot. If you run `script ` it opens a new shell where everything output and typed is also output to that file, giving you a transcript of your terminal session. +### Importing a Fundraiser Account to a Ledger Device + +You currently cannot directly import a fundraiser account to the Ledger device. Instead, you'll first need to import your fundraiser account to a non-hardware wallet address from which you can send the funds to an address on the ledger. You can do so with wallet providers such as [Galleon](https://galleon-wallet.tech/) or [TezBox](https://tezbox.com/). + ### Two Ledger Devices at the Same Time Two Ledger devices with the same seed should not ever be plugged in at the same time. This confuses From 989acbe6d023b4cadf6fff8255a02547452966de Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Thu, 28 Feb 2019 16:01:53 -0500 Subject: [PATCH 82/92] Update daemon instructions in readme --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bfe11d5c..52d04eb8 100644 --- a/README.md +++ b/README.md @@ -765,11 +765,9 @@ Wallet application on the same Ledger Nano S should suffice. ### Start the baking daemon ``` -$ tezos-baker-alpha run with local node ~/.tezos-node ledger_<...>_ed_0_0 +$ tezos-baker-003-PsddFKi3 run with local node ~/.tezos-node ledger_<...>_ed_0_0 ``` -Alternatively, it might be called `tezos-alpha-baker`. - This won't actually be able bake successfully yet until you run the rest of these setup steps. This will run indefinitely, so you might want to do it in a dedicated terminal or in a `tmux` or `screen` session. @@ -777,14 +775,15 @@ a dedicated terminal or in a `tmux` or `screen` session. You will also want to start the endorser and accuser daemons: ``` -$ tezos-endorser-alpha run ledger_<...>_ed_0_0 -$ tezos-accuser-alpha run +$ tezos-endorser-003-PsddFKi3 run ledger_<...>_ed_0_0 +$ tezos-accuser-003-PsddFKi3 run ``` -Alternatively, they might might be called `tezos-alpha-endorser` and `tezos-alpha-accuser`. Again, each of these will run indefinitely, and each should be in its own terminal `tmux`, or `screen` window. +*Note*: The binaries shown above all correspond to current Tezos mainnet protocol. When the Tezos protocol upgrades, the binaries shown below will update to, for instance, `tezos-baker-004-********`. + ### Authorize public key You need to run a specific command to authorize a key for baking. Once a key is From 29a2aa2d29d2c6551c95cf3318b6b81ebe21992b Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Thu, 28 Feb 2019 17:01:43 -0500 Subject: [PATCH 83/92] Updates to voting section and intro --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 540da464..ce72890d 100644 --- a/README.md +++ b/README.md @@ -19,16 +19,16 @@ blocks. This repository contains two Ledger Nano S applications: - 1. The "Tezos Baking" application is for baking Tezos: signing new blocks, - endorsements, and denunciations. For more information about baking, see + 1. The "Tezos Baking" application is for baking: signing new blocks and + endorsements. For more information about baking, see [*Benefits and Risks of Home Baking*](https://medium.com/@tezos_91823/benefits-and-risks-of-home-baking-a631c9ca745). - 2. The "Tezos Wallet" application is for making XTZ transactions, and + 2. The "Tezos Wallet" application is for making XTZ transactions, originating contracts, delegation, and voting. Basically everything you might want to use the Ledger Nano S for on Tezos besides baking. It is possible to do all of these things without a hardware wallet, but using a hardware wallet provides you better security against key theft. -Currently, there is no GUI support, so everything in this document is +This documentation was originally written when there was no GUI support, so everything is tailored towards the command line. We recommend you read this entire document to understand the commands available, and which commands are most appropriate to your situation. This will require judgment on how @@ -38,10 +38,7 @@ help you understand that. This document is not a comprehensive guide to setting up Tezos software. While it covers some aspects of setting up and installing Tezos nodes and clients, especially as it interacts with the Ledger Nano S, -you should familiarize yourself with the Tezos community's own documentation, -including -[how to build a node](https://github.com/tezoscommunity/FAQ/blob/master/Compile_Betanet.md) -and the [Tezos technical FAQ](https://github.com/tezoscommunity/FAQ/wiki/Tezos-Technical-FAQ). +you should familiarize yourself with the [Tezos Documentation](https://tezos.gitlab.io/master/) and community resources such as Tezos Community's guide on [building a node](https://github.com/tezoscommunity/FAQ/blob/master/Compile_Mainnet.md). If you have questions, please ask them on the [Tezos Stack Exchange](https://tezos.stackexchange.com/). This document is also not a guide on how to use Linux. It assumes you know how to install and configure a Linux system to your general needs, @@ -722,7 +719,7 @@ Originated accounts have names beginning with `KT1` rather than `tz1`, `tz2` or ### Proposals and Voting -To submit (or upvote) a proposal, open the Wallet app on your ledger and run +To submit (or upvote) a proposal during the Proposal Period, open the Wallet app on your ledger and run ``` $ tezos-client submit proposals for @@ -732,7 +729,7 @@ The Wallet app will then ask you to confirm the various details of the proposal **Note:** While `tezos-client` will let you submit multiple proposals at once with this command, submitting more than one will cause the Wallet app to show "Sign Unverified?" instead of showing each field of each proposal for your confirmation. Signing an operation that you can't confirm is not safe and it is highly recommended that you simply submit each proposal one at a time so you can properly confirm the fields on the ledger device. -Voting for a proposal also requires that you have the Wallet app open. You can then run +Voting for a proposal during the Exploration or Promotion Vote Period also requires that you have the Wallet app open. You can then run ``` $ tezos-client submit ballot for @@ -740,7 +737,7 @@ $ tezos-client submit ballot for The Wallet app will ask you to confirm the details of your vote. -Keep in mind that only registered delegate accounts can submit proposals and vote. Each account can submit up to 20 proposals per proposal period and vote only once per voting period. For a full description of how voting works, refer to the [Tezos documentation](https://gitlab.com/tezos/tezos/blob/master/docs/whitedoc/voting.rst). +Keep in mind that only registered delegate accounts can submit proposals and vote. Each account can submit up to 20 proposals per proposal period and vote only once per voting period. For a more detailed post on participating during each phase of the amendment process, see this [Medium post](https://medium.com/@obsidian.systems/voting-on-tezos-with-your-ledger-nano-s-8d75f8c1f076). For a full description of how voting works, refer to the [Tezos documentation](https://gitlab.com/tezos/tezos/blob/master/docs/whitedoc/voting.rst). ## Using the Tezos Baking Application From e6a2108a4c36dc57420e25c27a892d76e84486dd Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Mon, 25 Feb 2019 18:49:06 -0500 Subject: [PATCH 84/92] Use new installer based on app tars with manifest --- default.nix | 41 +++++++++++++++++++++++++++++++++-------- install.sh | 1 - nix/build.sh | 2 +- nix/env.sh | 2 +- nix/install.sh | 25 +++++++++---------------- release-installer.sh | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 27 deletions(-) create mode 100644 release-installer.sh diff --git a/default.nix b/default.nix index f1db63b6..7ef131a7 100644 --- a/default.nix +++ b/default.nix @@ -11,7 +11,7 @@ let fhs = pkgs.callPackage nix/fhs.nix {}; bolosEnv = pkgs.callPackage nix/bolos-env.nix {}; bolosSdk = fetchThunk nix/dep/nanos-secure-sdk; - src = pkgs.lib.sources.cleanSource ./.; + src = pkgs.lib.sources.sourceFilesBySuffices (pkgs.lib.sources.cleanSource ./.) [".c" ".h" ".gif" "Makefile"]; app = bakingApp: pkgs.runCommand "ledger-app-tezos-${if bakingApp then "baking" else "wallet"}" {} '' set -Eeuo pipefail @@ -37,15 +37,40 @@ let echo ">>>> Application size: <<<<" '${pkgs.binutils-unwrapped}/bin/size' "$out/bin/app.elf" ''; -in rec { - wallet = app false; - baking = app true; - release = pkgs.runCommand "release.tar.gz" {} '' - cp '${wallet}/bin/app.hex' wallet.hex - cp '${baking}/bin/app.hex' baking.hex - tar czf "$out" wallet.hex baking.hex + + mkRelease = short_name: name: appDir: pkgs.runCommand "${short_name}-release-dir" {} '' + mkdir -p "$out" + + cp '${appDir + /bin/app.hex}' "$out/app.hex" + + cat > "$out/app.manifest" </dev/null) +commit=$(git describe --tags --abbrev=8 --always 2>/dev/null) echo >&2 "Git commit: $commit" exec nix-build --no-out-link --argstr commit "$commit" "$@" diff --git a/nix/env.sh b/nix/env.sh index 91a52d9c..25aebd87 100755 --- a/nix/env.sh +++ b/nix/env.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -commit=$(git describe --abbrev=8 --always 2>/dev/null) +commit=$(git describe --tags --abbrev=8 --always 2>/dev/null) echo >&2 "Git commit: $commit" shell_dir="$(nix-build -A env-shell --no-out-link --argstr commit "$commit" "${NIX_BUILD_ARGS:-}")" shell="$shell_dir/bin/env-shell" diff --git a/nix/install.sh b/nix/install.sh index da9f7a2f..ab6540fe 100755 --- a/nix/install.sh +++ b/nix/install.sh @@ -3,29 +3,22 @@ set -Eeuo pipefail root="$(git rev-parse --show-toplevel)" -: "${VERSION:=${2:-"$(git -C "$root" describe --tags | cut -f1 -d- | cut -f2 -dv)"}}" - -install-wallet() { - "$root/install.sh" 'Tezos Wallet' "$("$root/nix/build.sh" -A wallet)" "$VERSION" -} -install-baking() { - "$root/install.sh" 'Tezos Baking' "$("$root/nix/build.sh" -A baking)" "$VERSION" +install() { + local release_file + release_file=$("$root/nix/build.sh" -A "release.$1") + bash "$root/release-installer.sh" "$release_file" } export root -export VERSION -export -f install-wallet -export -f install-baking +export -f install nix-shell "$root/nix/ledgerblue.nix" -A shell --run "$(cat < Date: Mon, 25 Feb 2019 18:56:52 -0500 Subject: [PATCH 85/92] Update basic manual installation instructions --- README.md | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ce72890d..344ded8f 100644 --- a/README.md +++ b/README.md @@ -372,34 +372,12 @@ as you continue. You may want to read the rest of these instructions before you begin installing, as you will need to confirm and verify a few things during the process. -Still within the virtualenv, run the `./install.sh` command. This script is in -the root directory of this very repo, which means that in order to have it, you -must clone this repo and `cd` into the resulting directory: +Still within the virtualenv, run the `./install.sh` command included in the `release.tar.gz` +that you downloaded. -``` -$ git clone https://github.com/obsidiansystems/ledger-app-tezos.git -$ cd ledger-app-tezos/ -``` - -This `./install.sh` script takes two parameters, the first of which is -the *name* of the application you are installing, and the second is the -path to the `app.hex` file. (Up to date `app.hex` files can be found in -the releases for this repo, and can be unpacked in the same directory that -contains `./install.sh`): - -* If you are installing the baking app, we recommend using the name "Tezos - Baking". - - ``` - $ ./install.sh "Tezos Baking" baking.hex - ``` - -* If you are installing the transaction app, we recommend using the name "Tezos - Wallet". - - ``` - $ ./install.sh "Tezos Wallet" wallet.hex - ``` +This `./install.sh` script takes the path to an app directory. Two such directories +were included in the downloaded `release.tar.gz`. +Install both apps like this: `./install.sh wallet baking`. The first thing that should come up in your terminal is a message that looks like this: @@ -441,10 +419,7 @@ If you'd like to remove your app, you can do this. In the virtualenv described in the last sections, run this command: ``` -$ python \ - -m ledgerblue.deleteApp \ - --targetId 0x31100004 \ - --appName "Tezos" +$ python -m ledgerblue.deleteApp --targetId 0x31100004 --appName 'Tezos Wallet' ``` Replace the `appName` parameter "Tezos" with whatever app name you used when you @@ -455,7 +430,7 @@ Then follow the prompts on the Ledger Nano S screen. ### Confirming the Installation Worked You should now have two apps, `Tezos Baking` and `Tezos Wallet`. The `Tezos -Baking` app should display also a `0` on the screen, which is the highest block +Baking` app should display a `0` on the screen, which is the highest block level baked so far (`0` in case of no blocks). The `Tezos Wallet` app will just display `Tezos`. From 273c0877421409f3d242b3e20edb5e055df5ef84 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Thu, 28 Feb 2019 19:00:16 -0500 Subject: [PATCH 86/92] Fix crash due to null operations parsing output --- src/apdu_sign.c | 19 +++++++++---------- src/globals.h | 8 +------- src/memory.h | 5 +++++ src/operations.c | 18 ++++++++++-------- src/operations.h | 14 ++++++++------ 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 4f9dc7bc..6ad6d8ce 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -97,7 +97,7 @@ __attribute__((noreturn)) static void prompt_register_delegate( REGISTER_STATIC_UI_VALUE(TYPE_INDEX, "as delegate?"); register_ui_callback(ADDRESS_INDEX, bip32_path_with_curve_to_pkh_string, &G.key); - register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &G.register_delegate.total_fee); + register_ui_callback(FEE_INDEX, microtez_to_string_indirect, &G.ops.total_fee); ui_prompt(prompts, NULL, ok_cb, cxl_cb); } @@ -120,19 +120,18 @@ uint32_t baking_sign_complete(void) { allow_operation(&allowed, OPERATION_TAG_DELEGATION); allow_operation(&allowed, OPERATION_TAG_REVEAL); - struct parsed_operation_group *ops = - parse_operations( - G.message_data, G.message_data_length, - G.key.curve, &G.key.bip32_path, allowed); + parse_operations( + &G.ops, + G.message_data, G.message_data_length, + G.key.curve, &G.key.bip32_path, allowed); // Must be self-delegation signed by the *authorized* baking key if (bip32_path_with_curve_eq(&G.key, &N_data.baking_key) && // ops->signing is generated from G.bip32_path and G.curve - COMPARE(&ops->operation.source, &ops->signing) == 0 && - COMPARE(&ops->operation.destination, &ops->signing) == 0 + COMPARE(&G.ops.operation.source, &G.ops.signing) == 0 && + COMPARE(&G.ops.operation.destination, &G.ops.signing) == 0 ) { - G.register_delegate.total_fee = ops->total_fee; prompt_register_delegate(sign_ok, sign_reject); } THROW(EXC_SECURITY); @@ -162,7 +161,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve ui_callback_t ok, ui_callback_t cxl) { check_null(data); check_null(bip32_path); - struct parsed_operation_group *ops; + struct parsed_operation_group *const ops = &G.ops; #ifndef TEZOS_DEBUG BEGIN_TRY { // TODO: Eventually, "unsafe" operations will be another APDU, @@ -181,7 +180,7 @@ static bool prompt_transaction(const void *data, size_t length, cx_curve_t curve allow_operation(&allowed, OPERATION_TAG_TRANSACTION); // TODO: Add still other operations - ops = parse_operations(data, length, curve, bip32_path, allowed); + parse_operations(ops, data, length, curve, bip32_path, allowed); #ifndef TEZOS_DEBUG } CATCH_OTHER(e) { diff --git a/src/globals.h b/src/globals.h index 977518bf..e7fca542 100644 --- a/src/globals.h +++ b/src/globals.h @@ -46,9 +46,7 @@ typedef struct { uint8_t magic_number; bool hash_only; - struct { - uint64_t total_fee; - } register_delegate; + struct parsed_operation_group ops; } sign; struct { @@ -103,10 +101,6 @@ typedef struct { struct { cx_ecfp_public_key_t compressed; } public_key_hash; - - struct { - struct parsed_operation_group out; - } parse_operations; } priv; } globals_t; diff --git a/src/memory.h b/src/memory.h index dea9ea22..5ae024a9 100644 --- a/src/memory.h +++ b/src/memory.h @@ -2,8 +2,13 @@ #include +#include "exception.h" + + #define COMPARE(a, b) ({ \ _Static_assert(sizeof(*a) == sizeof(*b), "Size mismatch in COMPARE"); \ + check_null(a); \ + check_null(b); \ memcmp(a, b, sizeof(*a)); \ }) #define NUM_ELEMENTS(a) (sizeof(a)/sizeof(*a)) diff --git a/src/operations.c b/src/operations.c index 104eda1d..9799ede4 100644 --- a/src/operations.c +++ b/src/operations.c @@ -143,13 +143,17 @@ static inline void parse_contract(struct parsed_contract *out, const struct cont } } -struct parsed_operation_group *parse_operations(const void *data, size_t length, cx_curve_t curve, - bip32_path_t const *const bip32_path, - allowed_operation_set ops) { +void parse_operations( + struct parsed_operation_group *const out, + void const *const data, + size_t length, + cx_curve_t curve, + bip32_path_t const *const bip32_path, + allowed_operation_set ops +) { + check_null(out); check_null(data); check_null(bip32_path); - - struct parsed_operation_group *const out = &global.priv.parse_operations.out; memset(out, 0, sizeof(*out)); out->operation.tag = OPERATION_TAG_NONE; @@ -216,7 +220,7 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, // If the source is an implicit contract,... if (out->operation.source.originated == 0) { // ... it had better match our key, otherwise why are we signing it? - if (COMPARE(&out->operation.source, &out->signing)) return false; + if (COMPARE(&out->operation.source, &out->signing) != 0) PARSE_ERROR(); } // OK, it passes muster. @@ -317,6 +321,4 @@ struct parsed_operation_group *parse_operations(const void *data, size_t length, if (out->operation.tag == OPERATION_TAG_NONE && !out->has_reveal) { PARSE_ERROR(); // Must have at least one op } - - return out; // Success! } diff --git a/src/operations.h b/src/operations.h index c3d909dc..ce5f7ab3 100644 --- a/src/operations.h +++ b/src/operations.h @@ -28,9 +28,11 @@ static inline void clear_operation_set(allowed_operation_set *ops) { // Throws upon invalid data. // Allows arbitrarily many "REVEAL" operations but only one operation of any other type, // which is the one it puts into the group. - -// Returns pointer to static data -- non-reentrant as hell. -struct parsed_operation_group * -parse_operations(const void *data, size_t length, cx_curve_t curve, - bip32_path_t const *const bip32_path, - allowed_operation_set ops); +void parse_operations( + struct parsed_operation_group *const out, + void const *const data, + size_t length, + cx_curve_t curve, + bip32_path_t const *const bip32_path, + allowed_operation_set ops +); From 8cef8f8108cecb3d060d8b009d1da2a9b6577ac3 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Mar 2019 11:46:50 -0500 Subject: [PATCH 87/92] Don't let it happen again: Define type-safe true/false --- src/types.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types.h b/src/types.h index 7a11cb8a..8409d3ad 100644 --- a/src/types.h +++ b/src/types.h @@ -7,6 +7,12 @@ #include #include +// Type-safe versions of true/false +#undef true +#define true ((bool)1) +#undef false +#define false ((bool)0) + // Return number of bytes to transmit (tx) typedef size_t (*apdu_handler)(uint8_t instruction); From 58d570497b4921f42021fef0a2952c9f8ed1e593 Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Fri, 1 Mar 2019 16:41:40 -0500 Subject: [PATCH 88/92] updated sections to reference Ledger Live Manager I've also made some small changes outside that which are not controversial --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 344ded8f..21e3ce12 100644 --- a/README.md +++ b/README.md @@ -91,11 +91,7 @@ can fall into the wrong hands. You will write it down on paper, along with your PIN, and store it. If you will have a large amount of money, consider putting your paper in a safe or safe deposit box, but at the very least keep it away from places where children, -dogs, housekeepers, obnoxious neighbors, could inadvertently destroy it. -Additionally, we recommend storing your PIN in a password manager. - -Finally, if you will not be baking yourself, consider disconnecting your Ledger device -and closing your password manager when not in use. +dogs, housekeepers, or obnoxious neighbors could inadvertently destroy it. ### Protecting Your Key -- Further Advanced Reading @@ -105,11 +101,11 @@ or plausible deniability features should look at Note that Ledger devices with different seeds will appear to `tezos-client` to be different hardware wallets. Note also that it can change what key is authorized in -the Baking App. When using these features in a Ledger hardware wallet used for baking, -please exit and re-start the Baking App right before baking is supposed to +Tezos Baking. When using these features in a Ledger hardware wallet used for baking, +please exit and re-start Tezos Baking right before baking is supposed to happen, and manually verify that it displays the key you expect to bake for. -The Wallet App does not require such extra steps, and so these extra +Tezos Wallet does not require such extra steps, and so these extra protections are more appropriate for keys used for transaction than they are for keys used for baking. If you do use these features, one technique is that your tez be stored in the passphrase-protected and @@ -181,17 +177,25 @@ Once you have added this, run `sudo nixos-rebuild switch` to activate the configuration, and unplug your Ledger device and plug it in again for the changes to take effect. -## Obtaining the Ledger Nano S apps +## Installing the Applications with Ledger Live + +The easiest way to obtain and install the Tezos Ledger Nano S apps is to download them +from [Ledger Live](https://www.ledger.com/pages/ledger-live). Tezos Wallet is readily available +in Ledger Live's Manager. To download Tezos Baking, you'll need to enable 'Developer Mode' in Settings. + +If you've used Ledger Live for app installation, you can skip ahead to [Registering the Ledger Nano S with the node](#registering-the-ledger-nano-s-with-the-node). + +## Obtaining the Ledger Nano S apps without Ledger Live If you are using the [Nix package manager](https://nixos.org/nix/), you can skip this section and the next one; go directly to -[Tezos baking platform](https://gitlab.com/obsidian.systems/tezos-baking-platform) +[Tezos Baking Platform](https://gitlab.com/obsidian.systems/tezos-baking-platform) for simpler Nix-based installation, where documentation should be in `ledger/README.md`. Then return to this document and continue reading at *Using the Ledger Nano S apps*. -The easiest way to obtain the Tezos Ledger Nano S apps is to download the `.hex` files +The second easiest way to obtain both applications (after Ledger Live) is to download `.hex` files from the [releases](https://github.com/obsidiansystems/ledger-app-tezos/releases) -page. After doing so, skip ahead to *Installing the apps onto your Ledger device*. +page of this repo. After doing so, skip ahead to *Installing the apps onto your Ledger device*. You will need to expand the releases tarball somewhere and copy the baking.hex and wallet.hex files into the ledger-app-tezos directory. If you want to compile the applications yourself, keep reading this section. @@ -278,13 +282,9 @@ $ APP=tezos_baking make $ mv bin/app.hex baking.hex ``` -## Installing the apps onto your Ledger device +### Installing the apps onto your Ledger device without Ledger Live -Ledger's primary interface for loading an app onto a Ledger device is a Chrome -app called [Ledger Manager](https://www.ledgerwallet.com/apps/manager). However, -it only supports a limited set of officially-supported apps like Bitcoin and -Ethereum, and Tezos is not yet among these. So you will need to use a -command-line tool called the +Manually installing the apps requires a command-line tool called the [BOLOS Python Loader](https://ledger.readthedocs.io/projects/blue-loader-python/en/0.1.16/index.html). ### Installing BOLOS Python Loader From 93c01a78fe776bdc72d6694dcf59f38403d121aa Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Fri, 1 Mar 2019 16:58:14 -0500 Subject: [PATCH 89/92] fixed broken link to tezos documentation --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 344ded8f..88ef69ac 100644 --- a/README.md +++ b/README.md @@ -667,8 +667,7 @@ in the section on the baking app below. To delegate tez controlled by a Ledger device to someone else, you must first originate an account. Please read more -about this in the Tezos document, [How to bake on the -alphanet](http://doc.tzalpha.net/introduction/alphanet.html#how-to-bake-on-the-alphanet), to +about this in the Tezos documentation, [How to use Tezos](https://tezos.gitlab.io/master/introduction/howtouse.html), to understand why this is necessary and the semantics of delegation. To originate an account, the command is: From 74da3e6573ab9763fffcffd35835f3c8910ae848 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Fri, 1 Mar 2019 17:15:38 -0500 Subject: [PATCH 90/92] Add dirty flag to Git commit; update release instructions --- CONTRIBUTING.md | 5 +++++ Makefile | 2 +- nix/build.sh | 2 +- nix/env.sh | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c12e969..23501267 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,3 +14,8 @@ To do a full Nix build run `nix/build.sh`. You can pass `nix-build` arguments to ### Installing `nix/install.sh` will install both the wallet and baking apps. Use `nix/install.sh baking` to install just the baking app or `nix/install.sh wallet` to install just the wallet. + + +### Releasing + +`nix/build -A release.all` diff --git a/Makefile b/Makefile index 78d2d666..66a76b02 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ else endif endif -COMMIT ?= $(shell git describe --abbrev=8 --always 2>/dev/null) +COMMIT ?= $(shell git describe --tags --abbrev=8 --always --long --dirty 2>/dev/null) ifeq ($(COMMIT),) $(error COMMIT not specified and could not be determined with git) endif diff --git a/nix/build.sh b/nix/build.sh index d9f3f978..df93542b 100755 --- a/nix/build.sh +++ b/nix/build.sh @@ -3,6 +3,6 @@ set -Eeuo pipefail # Override package set by passing --arg pkgs -commit=$(git describe --tags --abbrev=8 --always 2>/dev/null) +commit=$(git describe --tags --abbrev=8 --always --long --dirty 2>/dev/null) echo >&2 "Git commit: $commit" exec nix-build --no-out-link --argstr commit "$commit" "$@" diff --git a/nix/env.sh b/nix/env.sh index 25aebd87..e5cdf57b 100755 --- a/nix/env.sh +++ b/nix/env.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -commit=$(git describe --tags --abbrev=8 --always 2>/dev/null) +commit=$(git describe --tags --abbrev=8 --always --long --dirty 2>/dev/null) echo >&2 "Git commit: $commit" shell_dir="$(nix-build -A env-shell --no-out-link --argstr commit "$commit" "${NIX_BUILD_ARGS:-}")" shell="$shell_dir/bin/env-shell" From da5f0fe086f8ffa2c11d25e1003b05a5ac35c2a8 Mon Sep 17 00:00:00 2001 From: Mike Reinhart Date: Mon, 4 Mar 2019 11:52:04 -0500 Subject: [PATCH 91/92] Update Readme for new Tezos Baking commands --- README.md | 126 ++++++++++++++++++++---------------------------------- 1 file changed, 47 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 614dff33..6c404571 100644 --- a/README.md +++ b/README.md @@ -715,11 +715,13 @@ Keep in mind that only registered delegate accounts can submit proposals and vot ## Using the Tezos Baking Application -The Tezos Baking Application supports 3 operations: +The Tezos Baking Application supports the following operations: - 1. Authorize/get public key - 2. Reset high watermark - 3. Sign + 1. Get public key + 2. Setup ledger for baking + 3. Reset high watermark + 4. Get high watermark + 5. Sign (blocks and endorsements) It will only sign block headers and endorsements, as the purpose of the baking app is that it cannot be co-opted to perform other types of operations (like @@ -753,26 +755,21 @@ $ tezos-accuser-003-PsddFKi3 run Again, each of these will run indefinitely, and each should be in its own terminal `tmux`, or `screen` window. -*Note*: The binaries shown above all correspond to current Tezos mainnet protocol. When the Tezos protocol upgrades, the binaries shown below will update to, for instance, `tezos-baker-004-********`. +*Note*: The binaries shown above all correspond to current Tezos mainnet protocol. When the Tezos protocol upgrades, the binaries shown above will update to, for instance, `tezos-baker-004-********`. -### Authorize public key +### Setup ledger device to bake and endorse You need to run a specific command to authorize a key for baking. Once a key is authorized for baking, the user will not have to approve this command again. If a key is not authorized for baking, signing endorsements and block headers with that key will be rejected. This authorization data is persisted across runs of -the application. Only one key can be authorized for baking per Ledger hardware wallet at a +the application, but not across app installations. Only one key can be authorized for baking per Ledger hardware wallet at a time. -In order to authorize a public key for baking, you can do either of the -following: - - * Use the APDU for authorizing a public key. - - The command for this is as follows: +In order to authorize a public key for baking, use the APDU for setting up the ledger device to bake: ``` - $ tezos-client authorize ledger to bake for + $ tezos-client setup ledger to bake for ``` This only authorizes the key for baking on the Ledger Nano S, but does @@ -780,10 +777,10 @@ following: be necessary if you re-install the app, or if you have a different paired Ledger device that you are using to bake for the first time. - * Have the baking app sign a self-delegation. This is explained in the next section. - ### Registering as a Delegate +*Note: The ledger device will not sign this operation unless you have already setup the device to bake using the command in the previous section.* + In order to bake from the Ledger Nano S account you need to register the key as a delegate. This is formally done by delegating the account to itself. As a non-originated account, an account directly stored on the Ledger device can only @@ -792,41 +789,20 @@ delegate to itself. Open the Tezos Baking Application on the device, and then run this: ``` -$ tezos-client register key ledger_<...>_ed_0_0 as delegate +$ tezos-client register key as delegate ``` This command is intended to inform the blockchain itself of your intention to -bake with this key. It can be signed with either the Wallet App or the Baking -App, but if you sign it with the Baking App, it also implies to the Ledger Nano S -that you want to authorize that key for baking on the device as well. - -Authorizing a key for baking on a specific Ledger Nano S and authorizing it for -baking in general on the blockchain are two distinct authorizations. This -command will do both of them if signed with the appropriate baking app, -whereas the `authorize ledger` command in the previous section will -only do it for the in question, which is appropriate if it is already -authorized to bake on the blockchain. - -The `register key` command is equivalent to: - -``` -$ tezos-client set delegate for ledger_<...>_ed_0_0 to ledger_<...>_ed_0_0 -``` - -The Baking App only signs self-delegations; the Wallet App is needed to sign -delegations of originated accounts controlled by a hardware wallet. The Baking App -also only signs delegations with fees less than 0.05 ęś©; to sign those with -more, you must use the Wallet App to authorize baking for the blockchain, -and the command in the previous section to authorize baking for the Ledger Nano S, -which is always available as an alternative to signing this with the Baking App. +bake with this key. It can be signed with either Tezos Wallet or Tezos Baking, however +Tezos Baking can only sign self-delegations. ### Sign The sign operation is for signing block headers and endorsements. -Block headers must have monotonically strictly increasing levels; that is, each +Block headers must have monotonically increasing levels; that is, each block must have a higher level than all previous blocks signed with the Ledger device. -This is intended to prevent double baking at the device level, as a security +This is intended to prevent double baking and double endorsing at the device level, as a security measure against potential vulnerabilities where the computer might be tricked into double baking. This feature will hopefully be a redundant precaution, but it's implemented at the device level because the point of the Ledger hardware wallet is to not @@ -843,71 +819,63 @@ self-delegation). With the exception of self-delegations, as long as the key is configured and the high watermark constraint is followed, there is no user prompting required for -signing. The baking app will only ever sign without prompting or reject an -attempt at signing; this operation is designed to be used unsupervised. +signing. Tezos Baking will only ever sign without prompting or reject an +attempt at signing; this operation is designed to be used unsupervised. As mentioned, + the only exception to this is self-delegation. -As mentioned, the only exception to this is self-delegation. This will prompt -and display the public key hash of the account. This will authorize the key for -baking on the hardware wallet. This is independent of what the signed self-delegation -will then do on the blockchain. If you are baking on a new device, or have -reinstalled the app, you might have to sign a self-delegation to authorize the -key on the Ledger Nano S, even if you are already registered for baking on the -block-chain. This will also have to be done if you have previously signed this -with the wallet app. +### Reset High Watermark -### Reset +When updating the version of Tezos Baking you are using or if you are switching baking to + a new ledger device, we recommend setting the HWM to the current head block level of the blockchain. +This can be accomplished with the reset command. The following command requires an explicit +confirmation from the user: -There is some possibility that the HWM will need to be reset. This could be due -to the computer somehow being tricked into sending a block with a high level to -the Ledger Nano S (either due to an attack or a bug on the computer side), or because -the owner of the device wants to move from the real Tezos network to a test -network, or to a different test network. This can be accomplished with the reset -command. - -This is intended as a testing/debug tool and as a possible recovery from -attempted attacks or bugs, not as a day to day command. It requires an explicit -confirmation from the user, and there is no specific utility to send this -command. To send it manually, you can use the following command line: +``` +$ tezos-client set ledger high watermark for "ledger:///" to +``` -`tezos-client set ledger high watermark for "ledger:///" to ` +`` indicates the new high watermark to reset to. Both the main and test chain HWMs will be +simultaneously changed to this value. -`` indicates the new high watermark to reset to. If you are joining a new -test network, `0` is a fitting number, as then all blocks will be allowed again. -You can also set it to the most recently baked level on that test net. +If you would like to know the current high watermark of the ledger device, you can run: -If you are not physically present at your computer, but are curious what the -Ledger device's high watermark is, you can run: +``` +$ tezos-client get ledger high watermark for "ledger:///" +``` -`tezos-client get ledger high watermark for "ledger:///"` +While the ledger device's UI displays the HWM of the main chain it is signing on, it will not +display the HWM of a test chain it may be signing on during the 3rd period of the Tezos Amendment Process. +Running this command will return both HWMs as well as the chain ID of the main chain. ## Upgrading When you want to upgrade to a new version, whether you built it yourself from source or whether it's a new release of the `app.hex` files, use the same commands as you did to originally install it. As the keys are generated from the device's seeds and the -derivation paths, you will have the same keys with every version of this Ledger Nano S app, +derivation paths, you will have the same keys with every version of this Ledger hardware wallet app, so there is no need to re-import the keys with `tezos-client`. ### Special Upgrading Considerations for Bakers -If you've already been baking on an old version of the Baking App, the new version will +If you've already been baking on an old version of Tezos Baking, the new version will not remember which key you are baking with nor the High Watermark. You will have to re-run -this command to remind the hardware wallet what key you intend to authorize for baking: +this command to remind the hardware wallet what key you intend to authorize for baking. As shown, it can +also set the HWM: ``` -$ tezos-client authorize ledger to bake for +$ tezos-client setup ledger to bake for --main-hwm ``` -You can also set the High Watermark to the level of the most recently baked block with: +Alternatively, you can also set the High Watermark to the level of the most recently baked block with a separate command: ``` -tezos-client set ledger high watermark for "ledger:///" to +$ tezos-client set ledger high watermark for "ledger:///" to ``` -This will require the correct URL for the Ledger Nano S acquired from: +The later will require the correct URL for the Ledger device acquired from: ``` -tezos-client list connected ledgers +$ tezos-client list connected ledgers ``` ## Troubleshooting From dbfd8547000264a2af9bcecb5e162788c8f66730 Mon Sep 17 00:00:00 2001 From: Elliot Cameron Date: Mon, 4 Mar 2019 12:01:42 -0500 Subject: [PATCH 92/92] Improve HWM for endorsements --- src/baking_auth.c | 5 +++-- src/types.h | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/baking_auth.c b/src/baking_auth.c index e4ab813a..cb8b78c2 100644 --- a/src/baking_auth.c +++ b/src/baking_auth.c @@ -22,7 +22,7 @@ static void write_high_watermark(parsed_baking_data_t const *const in) { UPDATE_NVRAM(ram, { // If the chain matches the main chain *or* the main chain is not set, then use 'main' HWM. high_watermark_t *const dest = select_hwm_by_chain(in->chain_id, ram); - dest->highest_level = in->level; + dest->highest_level = MAX(in->level, dest->highest_level); dest->had_endorsement = in->is_endorsement; }); } @@ -45,7 +45,8 @@ static bool is_level_authorized(parsed_baking_data_t const *const baking_info) { // Levels are tied. In order for this to be OK, this must be an endorsement, and we must not // have previously seen an endorsement. - || (baking_info->is_endorsement && !hwm->had_endorsement); + || (baking_info->level == hwm->highest_level + && baking_info->is_endorsement && !hwm->had_endorsement); } bool is_path_authorized(cx_curve_t curve, bip32_path_t const *const bip32_path) { diff --git a/src/types.h b/src/types.h index 8409d3ad..5e8f8859 100644 --- a/src/types.h +++ b/src/types.h @@ -201,3 +201,17 @@ struct parsed_operation_group { _Static_assert(sizeof(buff) >= sizeof(x) && sizeof(*x) == sizeof(char), "String won't fit in buffer"); \ strcpy(buff, x); \ }) + +#undef MAX +#define MAX(a, b) ({ \ + __typeof__(a) ____a_ = (a); \ + __typeof__(b) ____b_ = (b); \ + ____a_ > ____b_ ? ____a_ : ____b_; \ +}) + +#undef MIN +#define MIN(a, b) ({ \ + __typeof__(a) ____a_ = (a); \ + __typeof__(b) ____b_ = (b); \ + ____a_ < ____b_ ? ____a_ : ____b_; \ +})