From 0fb6880a51557c45d245b1d1f45917132e24a12a Mon Sep 17 00:00:00 2001 From: Nicolas Roggeman Date: Fri, 21 Feb 2025 09:58:56 +0100 Subject: [PATCH 1/2] Rename nbgl_useCaseReviewWithWarning() to nbgl_useCaseAdvancedReview() --- lib_nbgl/doc/nbgl_use_case.dox | 12 +-- lib_nbgl/glyphs/64px/Info_Circle_64px.png | Bin 0 -> 993 bytes lib_nbgl/include/nbgl_use_case.h | 22 ++---- lib_nbgl/src/nbgl_use_case.c | 88 ++++++---------------- lib_nbgl/src/nbgl_use_case_nanos.c | 38 ---------- 5 files changed, 38 insertions(+), 122 deletions(-) create mode 100755 lib_nbgl/glyphs/64px/Info_Circle_64px.png diff --git a/lib_nbgl/doc/nbgl_use_case.dox b/lib_nbgl/doc/nbgl_use_case.dox index e9154ff94..576fd51a7 100644 --- a/lib_nbgl/doc/nbgl_use_case.dox +++ b/lib_nbgl/doc/nbgl_use_case.dox @@ -51,8 +51,8 @@ A few APIs are available to draw typical Use-Cases, such as: - @ref nbgl_useCaseReviewLight() to draw the pages of a transaction review with a simple button confirmation, when all info are available from the beginning (see @subpage use_case_review_light) - @ref nbgl_useCaseReviewStreamingStart() to draw the pages of a regular coin transaction review, when all info are not available from the beginning (see @subpage use_case_review_streaming) - for reviews with a warning prolog: - - @ref nbgl_useCaseReviewWithWarning() to draw the pages of a regular coin transaction review, when all info are available from the beginning, but with an identified risk requiring a warning prolog (see @subpage use_case_review_with_warning) - - @ref nbgl_useCaseReviewStreamingWithWarningStart() to draw the pages of a regular coin transaction review, when all info are not available from the beginning, but with an identified risk requiring a warning prolog (see @subpage use_case_review_with_warning and @subpage use_case_review_streaming) + - @ref nbgl_useCaseAdvancedReview() to draw the pages of a regular coin transaction review, when all info are available from the beginning, but with an identified risk requiring a warning prolog (see @subpage use_case_review_with_warning) + - @ref nbgl_useCaseAdvancedReviewStreamingStart() to draw the pages of a regular coin transaction review, when all info are not available from the beginning, but with an identified risk requiring a warning prolog (see @subpage use_case_review_with_warning and @subpage use_case_review_streaming) - for address verification: - @ref nbgl_useCaseAddressReview() to draw an address confirmation page, with a possibility to see it as QR Code and some extra tag/value pairs (see @subpage use_case_addr_review) - for keypad: @@ -469,7 +469,7 @@ The review itself behaves like in @subpage use_case_review. The main differences - The review itself is preceded by a warning page offering the possibility to cancel the review ("Back to safety") or to start it ("Continue anyway") - In the first and last pages of the actual review, a top-right button offers the possibility to get more information about the warning -The API to initiate the display of the series of pages is @ref nbgl_useCaseReviewWithWarning(), providing: +The API to initiate the display of the series of pages is @ref nbgl_useCaseAdvancedReview(), providing: - the type of operation to review (transaction, message or generic operation) - the list of tag/value pairs (or a callback to get them one by one) @@ -507,13 +507,13 @@ static void onReviewResult(bool confirm) { } void staticReview(void) { - warningDesc.predefinedSet = (1 << W3C_LOSING_SWAP_WARN) | (1 << BLIND_SIGNING_WARN); + warningDesc.predefinedSet = (1 << W3C_RISK_DETECTED_WARN) | (1 << BLIND_SIGNING_WARN); warningDesc.reportProvider = "Blockaid"; warningDesc.providerMessage = "Losing swap"; warningDesc.reportUrl = "url.com/od24xz"; // static review, providing the whole list of pairs - nbgl_useCaseReviewWithWarning(TYPE_TRANSACTION, // type of operation + nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, // type of operation &pairList, // list of tag/value pairs coinIcon, // icon of the coin "Review transaction\nto send coin", // title of the first page @@ -615,7 +615,7 @@ static void onReviewResult(bool confirm) { void staticReview(void) { // static review, providing the whole list of pairs - nbgl_useCaseReviewWithWarning(TYPE_TRANSACTION, // type of operation + nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, // type of operation &pairList, // list of tag/value pairs coinIcon, // icon of the coin "Review transaction\nto send coin", // title of the first page diff --git a/lib_nbgl/glyphs/64px/Info_Circle_64px.png b/lib_nbgl/glyphs/64px/Info_Circle_64px.png new file mode 100755 index 0000000000000000000000000000000000000000..5827c0cc825b55f187c99b0b90d40a7e10c9a544 GIT binary patch literal 993 zcmV<710MW|P)FI`DPiO9iA8NCiGuaCC4<1u<*Dqv2La2P*+{{$_UO zjD=V|R?FWC_~g2|L459NGqq7|54AnE+o;#BYVB`rueFu69ooNF2M_S6wxYKG6OH@W z;#{yrVxTkVAu$XVh(X+e_w88Q1`B|YcHn)>G2_(uS=$NWXN;ZYxy(tB(Bugl-e%f1 zzuSF6`_7OMcKjKB$5@;lrkx=|BS?rC&qx3d{sr?SstkT1*f&BX$m2k&#FPd2K~RP~ z2i@a*PRE4MRQ(wW`z_{Ui{a^@B7!U7aguw){ICzMtI_1dm|@w2?-&wcbc7Pz z^L|N#?_i%?CyOwG9P#G-(P2|@<%9wkvT<~jLkCyl(qQlC+u#DE(AT`a#uR`vzvO># z6d1_PWzZw00E{bwO~}p#3ytQ=q?jl&xSg!pGa6$bKH25*j}EUJBGQX<_O?Cb`& zy&b*_o&3Ujk$nH=YRsV_g$zNLC&W*J9fS*}fkFK69~yiN0hpt3oR7zLdkPp~=MsAG zL3c>cA7LAHKWqUWgodxsBjx)7n}e|JW309ScY!;Pg}?Art)-+CwuujO(p6-tq`(X<(c*nG2q%nv$c+zVm@rFTxxgUm%)X;Uq za1VVJ^Elj3f~C;O53CpIYjuf%%CjBL)jC0+R1qMB|KNum*EBHDC4~SPM!w-Hgq;fL zl0pFK><5j*Kz2^E`Xjag874-u6a}oP*0VuO0SN9-d%+mU4r*0m#S|ciQUn){j^-?h z@xir=lFvCtKyX)Gtf@ePL!U4bSHZ!WN@Oxj#8q&xh9apw6!2At&Y2TjbDr%5inyvS z>?A`-hp%3F(!6&;@Rz7&h|ea#4?-je{$lA^LKyhhCa6`1`q+CM>4ccN)`!r1Q4U#` zkh)$usC?ZPBS#~M!MP~iDzeA6O@2Y&C(M|L3By&}v8}*DR+IS>Q;dNdED(b$T{hQ; z(9BB>F}RN%A|<#~OJ_N`88q+cyM#x*b`_eJl?OB*Y54CFD_uJK$y#^=z*=5e&j#|3 P00000NkvXXu0mjfJ73CM literal 0 HcmV?d00001 diff --git a/lib_nbgl/include/nbgl_use_case.h b/lib_nbgl/include/nbgl_use_case.h index 31337941a..4975579ac 100644 --- a/lib_nbgl/include/nbgl_use_case.h +++ b/lib_nbgl/include/nbgl_use_case.h @@ -348,15 +348,6 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT const char *finishTitle, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback); -void nbgl_useCaseReviewWithWarning(nbgl_operationType_t operationType, - const nbgl_contentTagValueList_t *tagValueList, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const char *finishTitle, - const nbgl_tipBox_t *tipBox, - const nbgl_warning_t *warning, - nbgl_choiceCallback_t choiceCallback); void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, @@ -364,6 +355,7 @@ void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, + const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback); void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, @@ -396,12 +388,12 @@ void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t ope const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback); -void nbgl_useCaseReviewStreamingWithWarningStart(nbgl_operationType_t operationType, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const nbgl_warning_t *warning, - nbgl_choiceCallback_t choiceCallback); +void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operationType, + const nbgl_icon_details_t *icon, + const char *reviewTitle, + const char *reviewSubTitle, + const nbgl_warning_t *warning, + nbgl_choiceCallback_t choiceCallback); void nbgl_useCaseReviewStreamingContinueExt(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback, diff --git a/lib_nbgl/src/nbgl_use_case.c b/lib_nbgl/src/nbgl_use_case.c index dd37e0204..413ede956 100644 --- a/lib_nbgl/src/nbgl_use_case.c +++ b/lib_nbgl/src/nbgl_use_case.c @@ -3404,44 +3404,6 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType, true); } -/** - * @brief Draws a flow of pages of a review. Navigation operates with either swipe or navigation - * keys at bottom right. The last page contains a long-press button with the given finishTitle and - * the given icon. - * @note All tag/value pairs are provided in the API and the number of pages is automatically - * computed, the last page being a long press one - * - * @param operationType type of operation (Operation, Transaction, Message) - * @param tagValueList list of tag/value pairs - * @param icon icon used on first and last review page - * @param reviewTitle string used in the first review page - * @param reviewSubTitle string to set under reviewTitle (can be NULL) - * @param finishTitle string used in the last review page - * @param tipBox parameter to build a tip-box and necessary modal (can be NULL) - * @param choiceCallback callback called when operation is accepted (param is true) or rejected - * (param is false) - */ -void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, - const nbgl_contentTagValueList_t *tagValueList, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const char *finishTitle, - const nbgl_tipBox_t *tipBox, - nbgl_choiceCallback_t choiceCallback) -{ - useCaseReview(operationType, - tagValueList, - icon, - reviewTitle, - reviewSubTitle, - finishTitle, - tipBox, - choiceCallback, - false, - true); -} - /** * @brief Draws a flow of pages of a blind-signing review. The review is preceded by a warning page * @@ -3470,15 +3432,15 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback) { - nbgl_useCaseReviewWithWarning(operationType, - tagValueList, - icon, - reviewTitle, - reviewSubTitle, - finishTitle, - tipBox, - &blindSigningWarning, - choiceCallback); + nbgl_useCaseAdvancedReview(operationType, + tagValueList, + icon, + reviewTitle, + reviewSubTitle, + finishTitle, + tipBox, + &blindSigningWarning, + choiceCallback); } /** @@ -3503,15 +3465,15 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT * @param choiceCallback callback called when operation is accepted (param is true) or rejected * (param is false) */ -void nbgl_useCaseReviewWithWarning(nbgl_operationType_t operationType, - const nbgl_contentTagValueList_t *tagValueList, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const char *finishTitle, - const nbgl_tipBox_t *tipBox, - const nbgl_warning_t *warning, - nbgl_choiceCallback_t choiceCallback) +void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, + const nbgl_contentTagValueList_t *tagValueList, + const nbgl_icon_details_t *icon, + const char *reviewTitle, + const char *reviewSubTitle, + const char *finishTitle, + const nbgl_tipBox_t *tipBox, + const nbgl_warning_t *warning, + nbgl_choiceCallback_t choiceCallback) { memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); reviewWithWarnCtx.isStreaming = false; @@ -3641,7 +3603,7 @@ void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t ope const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback) { - nbgl_useCaseReviewStreamingWithWarningStart( + nbgl_useCaseAdvancedReviewStreamingStart( operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback); } @@ -3660,12 +3622,12 @@ void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t ope * @param choiceCallback callback called when more operation data are needed (param is true) or * operation is rejected (param is false) */ -void nbgl_useCaseReviewStreamingWithWarningStart(nbgl_operationType_t operationType, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const nbgl_warning_t *warning, - nbgl_choiceCallback_t choiceCallback) +void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operationType, + const nbgl_icon_details_t *icon, + const char *reviewTitle, + const char *reviewSubTitle, + const nbgl_warning_t *warning, + nbgl_choiceCallback_t choiceCallback) { memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); diff --git a/lib_nbgl/src/nbgl_use_case_nanos.c b/lib_nbgl/src/nbgl_use_case_nanos.c index ab30fddfe..e3e21ecc0 100644 --- a/lib_nbgl/src/nbgl_use_case_nanos.c +++ b/lib_nbgl/src/nbgl_use_case_nanos.c @@ -1724,44 +1724,6 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType, choiceCallback); } -/** - * @brief Draws a flow of pages of a review. Navigation operates with either swipe or navigation - * keys at bottom right. The last page contains a long-press button with the given finishTitle and - * the given icon. - * @note All tag/value pairs are provided in the API and the number of pages is automatically - * computed, the last page being a long press one - * - * @param operationType type of operation (Operation, Transaction, Message) - * @param tagValueList list of tag/value pairs - * @param icon icon used on first and last review page - * @param reviewTitle string used in the first review page - * @param reviewSubTitle string to set under reviewTitle (can be NULL) - * @param finishTitle string used in the last review page - * @param dummy inconsistent parameter on Nano devices (ignored) - * @param choiceCallback callback called when operation is accepted (param is true) or rejected - * (param is false) - */ -void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, - const nbgl_contentTagValueList_t *tagValueList, - const nbgl_icon_details_t *icon, - const char *reviewTitle, - const char *reviewSubTitle, - const char *finishTitle, - const nbgl_tipBox_t *dummy, - nbgl_choiceCallback_t choiceCallback) -{ - UNUSED(operationType); // TODO adapt accept and reject text depending on this value? - UNUSED(dummy); - - useCaseReview(REVIEW_USE_CASE, - tagValueList, - icon, - reviewTitle, - reviewSubTitle, - finishTitle, - choiceCallback); -} - /** * @brief Draws a flow of pages of a blind-signing review. The review is preceded by a warning page * From 657ac2d23b557c122ebc455d3c64abbd3964c5bd Mon Sep 17 00:00:00 2001 From: Nicolas Roggeman Date: Fri, 21 Feb 2025 10:19:01 +0100 Subject: [PATCH 2/2] Modify and enrich W3C Use Cases (and related ones) --- lib_nbgl/glyphs/32px/Question_32px.png | Bin 0 -> 334 bytes lib_nbgl/glyphs/40px/Question_40px.png | Bin 0 -> 365 bytes lib_nbgl/include/nbgl_content.h | 12 +- lib_nbgl/include/nbgl_use_case.h | 19 +- lib_nbgl/src/nbgl_layout.c | 2 +- lib_nbgl/src/nbgl_page.c | 2 +- lib_nbgl/src/nbgl_use_case.c | 691 ++++++++++-------- .../flows/wallet/app-sdk/flow_w3c_blind.json | 137 +++- .../flows/wallet/app-sdk/flow_w3c_dapp.json | 312 ++++++++ .../wallet/app-sdk/flow_w3c_no_dapp.json | 194 +++++ .../src/app/wallet/app_dogecoin_start.c | 61 +- .../src/app/wallet/app_xrp_start.c | 1 + 12 files changed, 1096 insertions(+), 335 deletions(-) create mode 100755 lib_nbgl/glyphs/32px/Question_32px.png create mode 100755 lib_nbgl/glyphs/40px/Question_40px.png create mode 100644 tests/screenshots/flows/wallet/app-sdk/flow_w3c_dapp.json create mode 100644 tests/screenshots/flows/wallet/app-sdk/flow_w3c_no_dapp.json diff --git a/lib_nbgl/glyphs/32px/Question_32px.png b/lib_nbgl/glyphs/32px/Question_32px.png new file mode 100755 index 0000000000000000000000000000000000000000..21624c40ca997e1c26ae77af2b7dbcceb1951908 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvg0|O(2 zr;B4q#jWH73D(649gK1c+ZZ0@DcJEJv2XlgFR-8Ok*R|W*O5tEHpMV@SU3Ezy%Lo3 zgIge=| z%07AJu!pxp-{E7S!aL?9a|JQkT$3J-1ZLkH0~yzRc9#W9|8*|rTd&k}$E#Ng@b=d#Wzp$Py>Xn79+ literal 0 HcmV?d00001 diff --git a/lib_nbgl/glyphs/40px/Question_40px.png b/lib_nbgl/glyphs/40px/Question_40px.png new file mode 100755 index 0000000000000000000000000000000000000000..655ef84f496be0b239ac6471169684243fe2848b GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIVMjR$B+ufx7RN6HW>)8J&1qI&=+7{z+yIm zcg^GjYHJn87rqtCy|%|DvehYchM9?6W5+emrfe z72i^7vgRjUeRJSIZh`l)^WQYXd|BE=E8;?b1YFgcyM z!Sq+Nro1pVvFHFp+pVKrJ?0V}HF;l6f3md-6n|$-x}3FW7pJx_FmxC^UHx3vIVCg! E0Ab#Y1poj5 literal 0 HcmV?d00001 diff --git a/lib_nbgl/include/nbgl_content.h b/lib_nbgl/include/nbgl_content.h index 1fa0fefe3..c7c41defa 100644 --- a/lib_nbgl/include/nbgl_content.h +++ b/lib_nbgl/include/nbgl_content.h @@ -140,7 +140,8 @@ typedef enum { NO_ALIAS_TYPE = 0, ENS_ALIAS, ///< alias comes from ENS ADDRESS_BOOK_ALIAS, ///< alias comes from Address Book - QR_CODE_ALIAS ///< alias is an address to be displayed as a QR Code + QR_CODE_ALIAS, ///< alias is an address to be displayed as a QR Code + INFO_LIST_ALIAS ///< alias is list of infos } nbgl_contentValueAliasType_t; /** @@ -153,7 +154,8 @@ typedef struct { ///< only used if aliasType is @ref NO_ALIAS_TYPE const char *title; ///< if not NULL and aliasType is @ref QR_CODE_ALIAS, is used as title of ///< the QR Code - nbgl_contentValueAliasType_t aliasType; ///< type of alias + const struct nbgl_contentInfoList_s *infolist; ///< if aliasType is INFO_LIST_ALIAS + nbgl_contentValueAliasType_t aliasType; ///< type of alias } nbgl_contentValueExt_t; /** @@ -172,8 +174,8 @@ typedef struct { int8_t forcePageStart : 1; ///< if set to 1, the tag will be displayed at the top of a new ///< review page int8_t centeredInfo : 1; ///< if set to 1, the tag will be displayed as a centered info - int8_t aliasValue : 1; ///< if set to 1, the value represents an alias and an icon enables to - ///< view the full value + int8_t aliasValue : 1; ///< if set to 1, the value represents an alias and an > icon enables to + ///< view the full value (extension field in union) #endif } nbgl_contentTagValue_t; @@ -279,7 +281,7 @@ typedef struct nbgl_pageSwitchesList_s { /** * @brief This structure contains data to build a @ref INFOS_LIST content */ -typedef struct { +typedef struct nbgl_contentInfoList_s { const char *const *infoTypes; ///< array of types of infos (in black/bold) const char *const *infoContents; ///< array of contents of infos (in black) const nbgl_contentValueExt_t diff --git a/lib_nbgl/include/nbgl_use_case.h b/lib_nbgl/include/nbgl_use_case.h index 4975579ac..7c9431341 100644 --- a/lib_nbgl/include/nbgl_use_case.h +++ b/lib_nbgl/include/nbgl_use_case.h @@ -184,7 +184,8 @@ typedef struct { const char *modalTitle; ///< title given to modal window displayed when tip-box is touched nbgl_contentType_t type; ///< type of page content in the following union union { - nbgl_contentInfoList_t infos; ///< infos pairs displayed in modal. + nbgl_contentInfoList_t + infos; ///< infos pairs displayed in modal, if type is @ref INFOS_LIST. }; } nbgl_tipBox_t; @@ -235,10 +236,11 @@ typedef struct nbgl_warningDetails_s { * */ typedef enum { - BLIND_SIGNING_WARN = 0, ///< Blind signing - W3C_ISSUE_WARN, ///< Web3 Checks issue - W3C_LOSING_SWAP_WARN, ///< Web3 Checks: Losing Swap risk - W3C_THREAT_DETECTED_WARN, ///< Web3 Checks: Thread detexted, malicious (know drainer) + W3C_ISSUE_WARN = 0, ///< Web3 Checks issue (not available) + W3C_RISK_DETECTED_WARN, ///< Web3 Checks: Risk detected (see reportRisk field) + W3C_THREAT_DETECTED_WARN, ///< Web3 Checks: Threat detected (see reportRisk field) + W3C_NO_THREAT_WARN, ///< Web3 Checks: No Threat detected + BLIND_SIGNING_WARN, ///< Blind signing NB_WARNING_TYPES } nbgl_warningType_t; @@ -252,10 +254,11 @@ typedef enum { * */ typedef struct { - uint32_t predefinedSet; ///< bitfield of pre-defined warnings, to be taken in @ref - ///< nbgl_warningType_t set it to 0 if not using pre-defined warnings + uint32_t predefinedSet; ///< bitfield of pre-defined warnings, to be taken in @ref + ///< nbgl_warningType_t set it to 0 if not using pre-defined warnings + const char *dAppProvider; ///< name of the dApp provider, used in some strings + const char *reportUrl; ///< URL of the report, used in some strings const char *reportProvider; ///< name of the security report provider, used in some strings - const char *reportUrl; ///< URL of the report, used in some strings const char *providerMessage; ///< Dedicated provider message. Default one will be used if NULL const nbgl_warningDetails_t *introDetails; ///< details displayed when top-right button is touched in intro page diff --git a/lib_nbgl/src/nbgl_layout.c b/lib_nbgl/src/nbgl_layout.c index edcfdf230..31500c084 100644 --- a/lib_nbgl/src/nbgl_layout.c +++ b/lib_nbgl/src/nbgl_layout.c @@ -299,7 +299,7 @@ static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType) return; } LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback); - if (layout->callback != NULL) { + if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) { #ifdef HAVE_PIEZO_SOUND if (layoutObj->tuneId < NBGL_NO_TUNE) { io_seproxyhal_play_tune(layoutObj->tuneId); diff --git a/lib_nbgl/src/nbgl_page.c b/lib_nbgl/src/nbgl_page.c index f3799e5e4..32115438d 100644 --- a/lib_nbgl/src/nbgl_page.c +++ b/lib_nbgl/src/nbgl_page.c @@ -93,7 +93,7 @@ static void addContent(nbgl_pageContent_t *content, break; case EXTENDED_CENTER: - if ((!headerAdded) && (content->extendedCenter.tipBox.text == NULL)) { + if (!headerAdded) { addEmptyHeader(layout, SMALL_CENTERING_HEADER); } nbgl_layoutAddContentCenter(layout, &content->extendedCenter.contentCenter); diff --git a/lib_nbgl/src/nbgl_use_case.c b/lib_nbgl/src/nbgl_use_case.c index 413ede956..485e299f0 100644 --- a/lib_nbgl/src/nbgl_use_case.c +++ b/lib_nbgl/src/nbgl_use_case.c @@ -61,6 +61,13 @@ */ #define RISKY_OPERATION (1 << 6) +/** + * @brief This is to use in @ref nbgl_operationType_t when the operation is concerned by an internal + * information. This is used to indicate an info with a top-right button in review first & last page + * + */ +#define NO_THREAT_OPERATION (1 << 7) + /********************** * TYPEDEFS **********************/ @@ -79,9 +86,11 @@ enum { REJECT_TOKEN, VALUE_ALIAS_TOKEN, INFO_ALIAS_TOKEN, + INFOS_TIP_BOX_TOKEN, BLIND_WARNING_TOKEN, WARNING_BUTTON_TOKEN, TIP_BOX_TOKEN, + QUIT_TIPBOX_MODAL_TOKEN, WARNING_CHOICE_TOKEN, DISMISS_QR_TOKEN, DISMISS_WARNING_TOKEN, @@ -121,7 +130,7 @@ typedef struct KeypadContext_s { } KeypadContext_t; #endif -typedef struct BlindSigningContext_s { +typedef struct ReviewWithWarningContext_s { bool isStreaming; nbgl_operationType_t operationType; const nbgl_contentTagValueList_t *tagValueList; @@ -129,7 +138,6 @@ typedef struct BlindSigningContext_s { const char *reviewTitle; const char *reviewSubTitle; const char *finishTitle; - const nbgl_tipBox_t *tipBox; const nbgl_warning_t *warning; nbgl_choiceCallback_t choiceCallback; nbgl_layout_t *layoutCtx; @@ -164,12 +172,14 @@ typedef struct { const char *detailsItem; const char *detailsvalue; bool detailsWrapping; + bool validWarningCtx; // set to true if the WarningContext is valid const nbgl_contentTagValue_t *currentPairs; // to be used to retrieve the pairs with value alias nbgl_contentTagValueCallback_t currentCallback; // to be used to retrieve the pairs with value alias nbgl_layout_t modalLayout; nbgl_layout_t backgroundLayout; + const nbgl_contentInfoList_t *currentInfos; } GenericContext_t; typedef struct { @@ -235,8 +245,7 @@ static nbgl_page_t *modalPageContext; static const char *pageTitle; // context for tip-box -static const char *tipBoxModalTitle; -static nbgl_contentInfoList_t tipBoxInfoList; +static nbgl_tipBox_t activeTipBox; // context for navigation use case static nbgl_pageNavigationInfo_t navInfo; @@ -276,19 +285,23 @@ static const uint8_t nbMaxElementsPerContentType[] = { }; static const SecurityReportItem_t securityReportItems[NB_WARNING_TYPES] = { - [BLIND_SIGNING_WARN] = {.icon = &WARNING_ICON, - .text = "Blind signing", - .subText = "This transaction cannot be fully decoded." }, - [W3C_ISSUE_WARN] = {.icon = &ROUND_WARN_ICON, - .text = "Web3 Checks issue", - .subText = "Web3 Checks could not verify this transaction." }, - [W3C_LOSING_SWAP_WARN] = {.icon = &ROUND_WARN_ICON, - .text = "Risk detected", - .subText = "This transaction was scanned as risky by Web3 Checks." }, + [BLIND_SIGNING_WARN] = {.icon = &WARNING_ICON, + .text = "Blind signing required", + .subText = "This transaction's details are not fully verifiable. If " + "you sign, you could lose all your assets." }, + [W3C_ISSUE_WARN] + = {.icon = &WARNING_ICON, .text = "Transaction Check not available", .subText = NULL}, + [W3C_RISK_DETECTED_WARN] = {.icon = &ROUND_WARN_ICON, + .text = "Risk detected", + .subText = "This transaction was scanned as risky by Web3 Checks." }, [W3C_THREAT_DETECTED_WARN] - = {.icon = &ROUND_WARN_ICON, - .text = "Threat detected", - .subText = "This transaction was scanned as malicious by Web3 Checks."} + = {.icon = &WARNING_ICON, + .text = "Critical threat", + .subText = "This transaction was scanned as malicious by Web3 Checks." }, + [W3C_NO_THREAT_WARN] = {.icon = NULL, + .text = "No threat detected", + .subText = "Transaction Check didn't find any threat, but always " + "review transaction details carefully." } }; // configuration of warning when using @ref nbgl_useCaseReviewBlindSigning() @@ -307,7 +320,9 @@ static void displayDetailsPage(uint8_t page, bool forceFullRefresh); static void displayFullValuePage(const char *backText, const char *aliasText, const nbgl_contentValueExt_t *extension); -static void displayTipBoxModal(void); +static void displayInfosListModal(const char *modalTitle, + const nbgl_contentInfoList_t *infos, + bool fromReview); static void displaySettingsPage(uint8_t page, bool forceFullRefresh); static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh); static void pageCallback(int token, uint8_t index); @@ -334,7 +349,8 @@ static void useCaseReview(nbgl_operationType_t operationType, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback, bool isLight, - bool playNotifSound); + bool playNotifSound, + bool validWarningCtx); static void useCaseReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, @@ -501,11 +517,18 @@ static void pageModalCallback(int token, uint8_t index) { LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index); - if (token == INFO_ALIAS_TOKEN) { + if (token == INFOS_TIP_BOX_TOKEN) { // the icon next to info type alias has been touched - displayFullValuePage(tipBoxInfoList.infoTypes[index], - tipBoxInfoList.infoContents[index], - &tipBoxInfoList.infoExtensions[index]); + displayFullValuePage(activeTipBox.infos.infoTypes[index], + activeTipBox.infos.infoContents[index], + &activeTipBox.infos.infoExtensions[index]); + return; + } + else if (token == INFO_ALIAS_TOKEN) { + // the icon next to info type alias has been touched, but coming from review + displayFullValuePage(genericContext.currentInfos->infoTypes[index], + genericContext.currentInfos->infoContents[index], + &genericContext.currentInfos->infoExtensions[index]); return; } nbgl_pageRelease(modalPageContext); @@ -520,11 +543,23 @@ static void pageModalCallback(int token, uint8_t index) displayDetailsPage(index, false); } } - if (token == QUIT_TOKEN) { + else if (token == QUIT_TOKEN) { // redraw the background layer nbgl_screenRedraw(); nbgl_refresh(); } + else if (token == QUIT_TIPBOX_MODAL_TOKEN) { + // if in "warning context", go back to security report + if (reviewWithWarnCtx.securityReportLevel == 2) { + reviewWithWarnCtx.securityReportLevel = 1; + displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet); + } + else { + // redraw the background layer + nbgl_screenRedraw(); + nbgl_refresh(); + } + } else if (token == SKIP_TOKEN) { if (index == 0) { // display the last forward only review page, whatever it is @@ -669,7 +704,14 @@ static void pageCallback(int token, uint8_t index) } } else if (token == TIP_BOX_TOKEN) { - displayTipBoxModal(); + // if warning context is valid and if W3C directly display same as top-right + if (genericContext.validWarningCtx && (reviewWithWarnCtx.warning->predefinedSet != 0)) { + reviewWithWarnCtx.securityReportLevel = 1; + displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet); + } + else { + displayInfosListModal(activeTipBox.modalTitle, &activeTipBox.infos, false); + } } else { // probably a control provided by caller if (onContentAction != NULL) { @@ -1049,11 +1091,12 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content, : ((navType == GENERIC_NAV) ? bundleNavContext.review.operationType : 0); // if first or last page of review and blind/risky operation, add the warning top-right button - if (isFirstOrLastPage && (operationType & (BLIND_OPERATION | RISKY_OPERATION))) { - // if issue is only Web3Checks issue, use '!' icon - if ((operationType & RISKY_OPERATION) - && (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_ISSUE_WARN))) { - pageContent->topRightIcon = &EXCLAMATION_ICON; + if (isFirstOrLastPage + && (operationType & (BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION))) { + // if issue is only Web3Checks "no threat", use privacy icon + if ((operationType & NO_THREAT_OPERATION) + && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) { + pageContent->topRightIcon = &PRIVACY_ICON; } else { pageContent->topRightIcon = &WARNING_ICON; @@ -1230,75 +1273,84 @@ static void displayFullValuePage(const char *backText, const char *aliasText, const nbgl_contentValueExt_t *extension) { - nbgl_layoutDescription_t layoutDescription = {.modal = true, - .withLeftBorder = true, - .onActionCallback = &modalLayoutTouchCallback, - .tapActionText = NULL}; - nbgl_layoutHeader_t headerDesc = {.type = HEADER_BACK_AND_TEXT, - .separationLine = false, - .backAndText.token = 0, - .backAndText.tuneId = TUNE_TAP_CASUAL, - .backAndText.text = PIC(backText)}; - genericContext.modalLayout = nbgl_layoutGet(&layoutDescription); - // add header with the tag part of the pair, to go back - nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc); - // add either QR Code or full value text - if (extension->aliasType == QR_CODE_ALIAS) { -#ifdef NBGL_QRCODE - nbgl_layoutQRCode_t qrCode - = {.url = extension->fullValue, - .text1 = (extension->title != NULL) ? extension->title : extension->fullValue, - .text2 = extension->explanation, - .centered = true, - .offsetY = 0}; - - nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode); -#endif // NBGL_QRCODE + if (extension->aliasType == INFO_LIST_ALIAS) { + genericContext.currentInfos = extension->infolist; + displayInfosListModal(PIC(backText), extension->infolist, true); } else { - const char *info; - // add full value text - if (extension->aliasType == ENS_ALIAS) { - info = "ENS names are resolved by Ledger backend."; - } - else if (extension->aliasType == ADDRESS_BOOK_ALIAS) { - info = "This account label comes from your Address Book in Ledger Live."; + nbgl_layoutDescription_t layoutDescription = {.modal = true, + .withLeftBorder = true, + .onActionCallback = &modalLayoutTouchCallback, + .tapActionText = NULL}; + nbgl_layoutHeader_t headerDesc = {.type = HEADER_BACK_AND_TEXT, + .separationLine = false, + .backAndText.token = 0, + .backAndText.tuneId = TUNE_TAP_CASUAL, + .backAndText.text = PIC(backText)}; + genericContext.modalLayout = nbgl_layoutGet(&layoutDescription); + // add header with the tag part of the pair, to go back + nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc); + // add either QR Code or full value text + if (extension->aliasType == QR_CODE_ALIAS) { +#ifdef NBGL_QRCODE + nbgl_layoutQRCode_t qrCode + = {.url = extension->fullValue, + .text1 = (extension->title != NULL) ? extension->title : extension->fullValue, + .text2 = extension->explanation, + .centered = true, + .offsetY = 0}; + + nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode); +#endif // NBGL_QRCODE } else { - info = extension->explanation; + const char *info; + // add full value text + if (extension->aliasType == ENS_ALIAS) { + info = "ENS names are resolved by Ledger backend."; + } + else if (extension->aliasType == ADDRESS_BOOK_ALIAS) { + info = "This account label comes from your Address Book in Ledger Live."; + } + else { + info = extension->explanation; + } + nbgl_layoutAddTextContent( + genericContext.modalLayout, aliasText, extension->fullValue, info); } - nbgl_layoutAddTextContent( - genericContext.modalLayout, aliasText, extension->fullValue, info); + // draw & refresh + nbgl_layoutDraw(genericContext.modalLayout); + nbgl_refresh(); } - // draw & refresh - nbgl_layoutDraw(genericContext.modalLayout); - nbgl_refresh(); } // function used to display the modal containing tip-box infos -static void displayTipBoxModal(void) -{ - nbgl_pageNavigationInfo_t info = {.activePage = 0, - .nbPages = 1, - .navType = NAV_WITH_BUTTONS, - .quitToken = QUIT_TOKEN, - .navWithButtons.navToken = NAV_TOKEN, - .navWithButtons.quitButton = false, - .navWithButtons.backButton = true, - .navWithButtons.quitText = NULL, - .progressIndicator = false, - .tuneId = TUNE_TAP_CASUAL}; - nbgl_pageContent_t content = {.type = INFOS_LIST, - .topRightIcon = NULL, - .infosList.nbInfos = tipBoxInfoList.nbInfos, - .infosList.withExtensions = tipBoxInfoList.withExtensions, - .infosList.infoTypes = tipBoxInfoList.infoTypes, - .infosList.infoContents = tipBoxInfoList.infoContents, - .infosList.infoExtensions = tipBoxInfoList.infoExtensions, - .infosList.token = INFO_ALIAS_TOKEN, - .title = tipBoxModalTitle, - .titleToken = QUIT_TOKEN, - .tuneId = TUNE_TAP_CASUAL}; +static void displayInfosListModal(const char *modalTitle, + const nbgl_contentInfoList_t *infos, + bool fromReview) +{ + nbgl_pageNavigationInfo_t info = {.activePage = 0, + .nbPages = 1, + .navType = NAV_WITH_BUTTONS, + .quitToken = QUIT_TIPBOX_MODAL_TOKEN, + .navWithButtons.navToken = NAV_TOKEN, + .navWithButtons.quitButton = false, + .navWithButtons.backButton = true, + .navWithButtons.quitText = NULL, + .progressIndicator = false, + .tuneId = TUNE_TAP_CASUAL}; + nbgl_pageContent_t content + = {.type = INFOS_LIST, + .topRightIcon = NULL, + .infosList.nbInfos = infos->nbInfos, + .infosList.withExtensions = infos->withExtensions, + .infosList.infoTypes = infos->infoTypes, + .infosList.infoContents = infos->infoContents, + .infosList.infoExtensions = infos->infoExtensions, + .infosList.token = fromReview ? INFO_ALIAS_TOKEN : INFOS_TIP_BOX_TOKEN, + .title = modalTitle, + .titleToken = QUIT_TIPBOX_MODAL_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; if (modalPageContext != NULL) { nbgl_pageRelease(modalPageContext); @@ -1443,16 +1495,17 @@ static void layoutTouchCallback(int token, uint8_t index) reviewWithWarnCtx.reviewTitle, reviewWithWarnCtx.reviewSubTitle, reviewWithWarnCtx.finishTitle, - reviewWithWarnCtx.tipBox, + &activeTipBox, reviewWithWarnCtx.choiceCallback, false, - false); + false, + true); } } } // top-right button in initial warning page else if (token == WARNING_BUTTON_TOKEN) { - // start at first level of security report + // start directly at first level of security report reviewWithWarnCtx.securityReportLevel = 1; // if preset is used if (reviewWithWarnCtx.warning->predefinedSet) { @@ -1478,7 +1531,7 @@ static void displaySkipWarning(void) = "If you're sure you don't need to review all fields, you can skip straight to signing.", .centeredInfo.text3 = NULL, .centeredInfo.style = LARGE_CASE_INFO, - .centeredInfo.icon = &C_Important_Circle_64px, + .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON, .centeredInfo.offsetY = 0, .confirmationText = "Yes, skip", .confirmationToken = SKIP_TOKEN, @@ -1807,7 +1860,8 @@ static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operati const char *title; const char *confirmText; // clear skip and blind bits - operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION | RISKY_OPERATION); + operationType + &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION); if (operationType == TYPE_TRANSACTION) { title = "Reject transaction?"; confirmText = "Go back to transaction"; @@ -1880,34 +1934,36 @@ static void displaySecurityReport(uint32_t set) // count the number of warnings for (i = 0; i < NB_WARNING_TYPES; i++) { - if (set & (1 << i)) { + if ((set & (1 << i)) && (i != W3C_NO_THREAT_WARN)) { nbWarnings++; } } - if ((nbWarnings > 1) && (reviewWithWarnCtx.securityReportLevel == 1)) { - // if more than one warning warning, so use a list of touchable bars + // display a list of touchable bars in some conditions: + // - we must be in the first level of security report + // - and be in the review itself (not from the intro/warning screen) + // + if ((reviewWithWarnCtx.securityReportLevel == 1) && (!reviewWithWarnCtx.isIntro)) { for (i = 0; i < NB_WARNING_TYPES; i++) { if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) { - nbgl_layoutBar_t bar; - if ((i == BLIND_SIGNING_WARN) + nbgl_layoutBar_t bar = {0}; + if ((i == BLIND_SIGNING_WARN) || (i == W3C_NO_THREAT_WARN) || (i == W3C_ISSUE_WARN) || (reviewWithWarnCtx.warning->providerMessage == NULL)) { bar.subText = securityReportItems[i].subText; } else { - snprintf(tmpString, - W3C_DESCRIPTION_MAX_LEN, - "Web3 Checks found a risk:\n%s.", - reviewWithWarnCtx.warning->providerMessage); - bar.subText = tmpString; + bar.subText = reviewWithWarnCtx.warning->providerMessage; } - bar.text = securityReportItems[i].text; - bar.iconRight = &PUSH_ICON; - bar.iconLeft = securityReportItems[i].icon; - bar.token = FIRST_WARN_BAR_TOKEN + i; - bar.tuneId = TUNE_TAP_CASUAL; - bar.large = false; - bar.inactive = false; + bar.text = securityReportItems[i].text; + if (i != W3C_NO_THREAT_WARN) { + bar.iconRight = &PUSH_ICON; + bar.token = FIRST_WARN_BAR_TOKEN + i; + bar.tuneId = TUNE_TAP_CASUAL; + } + else { + bar.token = NBGL_INVALID_TOKEN; + } + bar.iconLeft = securityReportItems[i].icon; nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar); nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout); } @@ -1924,126 +1980,56 @@ static void displaySecurityReport(uint32_t set) else { provider = "[unknown]"; } - if (set & (1 << BLIND_SIGNING_WARN)) { - if (reviewWithWarnCtx.isIntro) { -#ifdef NBGL_QRCODE - // display a QR Code if in intro - nbgl_layoutQRCode_t qrCode - = {.url = "ledger.com/e8", - .text1 = "ledger.com/e8", - .text2 = "Scan to learn about the risks of blind signing.", - .centered = true, - .offsetY = 0}; - nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode); - footerDesc.emptySpace.height = 24; -#endif // NBGL_QRCODE - headerDesc.backAndText.text = "Blind signing report"; - } - else { - // display a centered if in review - nbgl_contentCenter_t info = {0}; - info.icon = &C_Warning_64px; - info.title = "Blind Signing"; - info.description - = "This transaction's details are not fully verifiable. If you sign it, you could " - "lose all your assets.\n\n" - "Learn about blind signing:\nledger.com/e8"; - nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); - footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER; - headerDesc.separationLine = false; - } - } - else if (set & (1 << W3C_ISSUE_WARN)) { + if (set & (1 << W3C_ISSUE_WARN)) { // if W3 Checks issue, display a centered info nbgl_contentCenter_t info = {0}; - info.icon = &C_Important_Circle_64px; - info.title = "Web3 Checks could not verify this message"; - info.description = "An issue prevented Web3 Checks from running.\nGet help: ledger.com/e11"; + info.icon = &LARGE_WARNING_ICON; + info.title = "Transaction Check not available"; + info.description + = "If you're not using the\nLedger Live app, Transaction Check might not work. If your " + "are using Ledger Live, reject the transaction and try again.\n\nGet help at " + "ledger.com/e11"; nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER; headerDesc.separationLine = false; } - else if (set & (1 << W3C_THREAT_DETECTED_WARN)) { - if (reviewWithWarnCtx.isIntro) { -#ifdef NBGL_QRCODE - // display a QR Code if in intro - nbgl_layoutQRCode_t qrCode = {.url = reviewWithWarnCtx.warning->reportUrl, - .text1 = reviewWithWarnCtx.warning->reportUrl, - .text2 = tmpString, - .centered = true, - .offsetY = 0}; - snprintf(tmpString, - W3C_DESCRIPTION_MAX_LEN, - "Scan to view the threat report from %s.", - provider); - nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode); - footerDesc.emptySpace.height = 24; -#endif // NBGL_QRCODE - headerDesc.backAndText.text = "Web3 Checks threat report"; - } - else { - // display a centered if in review - nbgl_contentCenter_t info = {0}; - info.icon = &C_Warning_64px; - info.title = "Threat detected"; - info.description = tmpString; - if (reviewWithWarnCtx.warning->providerMessage != NULL) { - info.smallTitle = reviewWithWarnCtx.warning->providerMessage; - } - else { - info.smallTitle = "Known drainer contract"; - } - snprintf(tmpString, - W3C_DESCRIPTION_MAX_LEN, - "This transaction was scanned as malicious by Web3 Checks.\n\n" - "View full %s report:\n%s", - provider, - reviewWithWarnCtx.warning->reportUrl); - nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); - footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER; - headerDesc.separationLine = false; - } - } - else if (set & (1 << W3C_LOSING_SWAP_WARN)) { - if (reviewWithWarnCtx.isIntro) { + else if ((set & (1 << W3C_THREAT_DETECTED_WARN)) || (set & (1 << W3C_RISK_DETECTED_WARN))) { + size_t urlLen = 0; + char *destStr + = tmpString + + W3C_DESCRIPTION_MAX_LEN / 2; // use the second half of tmpString for strings #ifdef NBGL_QRCODE - // display a QR Code if in intro - nbgl_layoutQRCode_t qrCode = {.url = reviewWithWarnCtx.warning->reportUrl, - .text1 = reviewWithWarnCtx.warning->reportUrl, - .text2 = tmpString, - .centered = true, - .offsetY = 0}; - snprintf(tmpString, - W3C_DESCRIPTION_MAX_LEN, - "Scan to view the risk report from %s.", - provider); - nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode); - footerDesc.emptySpace.height = 24; + // display a QR Code + nbgl_layoutQRCode_t qrCode = {.url = tmpString, + .text1 = reviewWithWarnCtx.warning->reportUrl, + .text2 = "Scan to view full report", + .centered = true, + .offsetY = 0}; + // add "https://"" as prefix of the given URL + snprintf(destStr, + W3C_DESCRIPTION_MAX_LEN / 2, + "https://%s", + reviewWithWarnCtx.warning->reportUrl); + urlLen = strlen(destStr) + 1; + nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode); + footerDesc.emptySpace.height = 24; #endif // NBGL_QRCODE - headerDesc.backAndText.text = "Web3 Checks risk report"; - } - else { - // display a centered if in review - nbgl_contentCenter_t info = {0}; - info.icon = &C_Warning_64px; - info.title = "Risk detected"; - info.description = tmpString; - if (reviewWithWarnCtx.warning->providerMessage != NULL) { - info.smallTitle = reviewWithWarnCtx.warning->providerMessage; - } - else { - info.smallTitle = "Losing swap"; - } - snprintf(tmpString, - W3C_DESCRIPTION_MAX_LEN, - "This transaction was scanned as risky by Web3 Checks.\n\n" - "View full %s report:\n%s", - provider, - reviewWithWarnCtx.warning->reportUrl); - nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); - footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER; - headerDesc.separationLine = false; - } + // use the next part of destStr for back bar text + snprintf(destStr + urlLen, W3C_DESCRIPTION_MAX_LEN / 2 - urlLen, "%s report", provider); + headerDesc.backAndText.text = destStr + urlLen; + } + else if (set & (1 << BLIND_SIGNING_WARN)) { + // display a centered if in review + nbgl_contentCenter_t info = {0}; + info.icon = &LARGE_WARNING_ICON; + info.title = "This transaction cannot be Clear Signed"; + info.description + = "This transaction or message cannot be decoded fully. If you choose to sign, you " + "could be authorizing malicious actions that can drain your wallet.\n\nLearn more: " + "ledger.com/e8"; + nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); + footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER; + headerDesc.separationLine = false; } nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc); if (footerDesc.emptySpace.height > 0) { @@ -2120,6 +2106,8 @@ static void displayInitialWarning(void) nbgl_layoutHeader_t headerDesc = {.type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = MEDIUM_CENTERING_HEADER}; + uint32_t set = reviewWithWarnCtx.warning->predefinedSet + & ~((1 << W3C_NO_THREAT_WARN) | (1 << W3C_ISSUE_WARN)); reviewWithWarnCtx.isIntro = true; @@ -2133,72 +2121,68 @@ static void displayInitialWarning(void) reviewWithWarnCtx.layoutCtx = nbgl_layoutGet(&layoutDescription); nbgl_layoutAddHeader(reviewWithWarnCtx.layoutCtx, &headerDesc); - // do not display top-right icon if only Web3 Checks issue - if (reviewWithWarnCtx.warning->predefinedSet != (1 << W3C_ISSUE_WARN)) { - if (reviewWithWarnCtx.warning->predefinedSet != 0) { - nbgl_layoutAddTopRightButton( - reviewWithWarnCtx.layoutCtx, &PRIVACY_ICON, WARNING_BUTTON_TOKEN, TUNE_TAP_CASUAL); - } - else if (reviewWithWarnCtx.warning->introTopRightIcon != NULL) { - nbgl_layoutAddTopRightButton(reviewWithWarnCtx.layoutCtx, - reviewWithWarnCtx.warning->introTopRightIcon, - WARNING_BUTTON_TOKEN, - TUNE_TAP_CASUAL); - } + if (reviewWithWarnCtx.warning->predefinedSet != 0) { + nbgl_layoutAddTopRightButton( + reviewWithWarnCtx.layoutCtx, + (set == (1 << BLIND_SIGNING_WARN)) ? &INFO_I_ICON : &QRCODE_ICON, + WARNING_BUTTON_TOKEN, + TUNE_TAP_CASUAL); + } + else if (reviewWithWarnCtx.warning->introTopRightIcon != NULL) { + nbgl_layoutAddTopRightButton(reviewWithWarnCtx.layoutCtx, + reviewWithWarnCtx.warning->introTopRightIcon, + WARNING_BUTTON_TOKEN, + TUNE_TAP_CASUAL); } - // add button and footer on bottom - nbgl_layoutAddChoiceButtons(reviewWithWarnCtx.layoutCtx, &buttonsInfo); // add main content // if predefined content is configured, use it preferably if (reviewWithWarnCtx.warning->predefinedSet != 0) { nbgl_contentCenter_t info = {0}; - info.icon = &C_Warning_64px; - if (reviewWithWarnCtx.warning->predefinedSet == (1 << BLIND_SIGNING_WARN)) { - info.title = "Blind signing ahead"; - info.description - = "The details of this transaction or message are not fully verifiable. If " - "you sign it, you could lose all " - "your assets."; - } - else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_ISSUE_WARN)) { - info.icon = &C_Important_Circle_64px; - info.title = "Web3 Checks could not verify this message"; - info.description - = "An issue prevented Web3 Checks from running.\nGet help: ledger.com/e11"; - } - else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_LOSING_SWAP_WARN)) { - info.title = "Risk detected"; - info.description = "This transaction was scanned as ricky by Web3 Checks."; - if (reviewWithWarnCtx.warning->providerMessage != NULL) { - info.smallTitle = reviewWithWarnCtx.warning->providerMessage; + + const char *provider; + + // default icon + info.icon = &LARGE_WARNING_ICON; + + // use small title only if not Blind signing only + if (set != (1 << BLIND_SIGNING_WARN)) { + if (reviewWithWarnCtx.warning->reportProvider) { + provider = reviewWithWarnCtx.warning->reportProvider; } else { - info.smallTitle = "Losing swap"; + provider = "[unknown]"; } + info.smallTitle = tmpString; + snprintf(tmpString, W3C_DESCRIPTION_MAX_LEN, "Detected by %s", provider); } - else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_THREAT_DETECTED_WARN)) { - info.title = "Threat detected"; - info.description = "This transaction was scanned as malicious by Web3 Checks."; - if (reviewWithWarnCtx.warning->providerMessage != NULL) { - info.smallTitle = reviewWithWarnCtx.warning->providerMessage; + + if (set == (1 << BLIND_SIGNING_WARN)) { + info.title = "Blind signing ahead"; + info.description + = "This transaction's details are not fully verifiable. If you sign, you could " + "lose all your assets."; + buttonsInfo.bottomText = "Continue anyway"; + } + else if (set & (1 << W3C_RISK_DETECTED_WARN)) { + info.icon = &IMPORTANT_CIRCLE_ICON; + info.title = "Potential risk"; + if (reviewWithWarnCtx.warning->providerMessage == NULL) { + info.description = "Unidentified risk"; } else { - info.smallTitle = "Known drainer contract"; + info.description = reviewWithWarnCtx.warning->providerMessage; } + buttonsInfo.bottomText = "Accept risk and continue"; } - else { - // Case with Several warnings (e.g. Blind + W3C) - info.title = "Dangerous transaction"; - if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_ISSUE_WARN)) { - info.description - = "This transaction cannot be fully decoded, and was not verified by Web3 " - "Checks."; + else if (set & (1 << W3C_THREAT_DETECTED_WARN)) { + info.title = "Critical threat"; + if (reviewWithWarnCtx.warning->providerMessage == NULL) { + info.description = "Unidentified threat"; } else { - info.description - = "This transaction cannot be fully decoded, and was marked as risky by Web3 " - "Checks."; + info.description = reviewWithWarnCtx.warning->providerMessage; } + buttonsInfo.bottomText = "Accept threat and continue"; } nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, &info); } @@ -2206,6 +2190,8 @@ static void displayInitialWarning(void) // if no predefined content, use custom one nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, reviewWithWarnCtx.warning->info); } + // add button and footer on bottom + nbgl_layoutAddChoiceButtons(reviewWithWarnCtx.layoutCtx, &buttonsInfo); nbgl_layoutDraw(reviewWithWarnCtx.layoutCtx); nbgl_refresh(); @@ -2221,8 +2207,10 @@ static void useCaseReview(nbgl_operationType_t operationType, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback, bool isLight, - bool playNotifSound) + bool playNotifSound, + bool validWarningCtx) { + const char *predefinedTipBoxText = NULL; reset_callbacks(); memset(&genericContext, 0, sizeof(genericContext)); @@ -2234,6 +2222,7 @@ static void useCaseReview(nbgl_operationType_t operationType, navType = GENERIC_NAV; pageTitle = NULL; + genericContext.validWarningCtx = validWarningCtx; genericContext.genericContents.contentsList = localContentsList; genericContext.genericContents.nbContents = 3; memset(localContentsList, 0, 3 * sizeof(nbgl_content_t)); @@ -2242,23 +2231,59 @@ static void useCaseReview(nbgl_operationType_t operationType, STARTING_CONTENT.type = EXTENDED_CENTER; prepareReviewFirstPage( &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle); - if (tipBox != NULL) { + + // if warning is valid and a warning requiring a tip box + if (validWarningCtx) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_ISSUE_WARN)) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) { + predefinedTipBoxText = "Transaction Check unavailable.\nBlind signing required."; + } + else { + predefinedTipBoxText = "Transaction Check unavailable"; + } + } + else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN)) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) { + predefinedTipBoxText = "Critical threat detected.\nBlind signing required."; + } + else { + predefinedTipBoxText = "Critical threat detected."; + } + } + else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN)) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) { + predefinedTipBoxText = "Potential risk detected.\nBlind signing required."; + } + else { + predefinedTipBoxText = "Potential risk detected."; + } + } + else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_NO_THREAT_WARN)) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) { + predefinedTipBoxText + = "No threat detected by Transaction Check but blind signing required."; + } + else { + predefinedTipBoxText = "No threat detected by Transaction Check."; + } + } + else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) { + predefinedTipBoxText = "Blind signing required."; + } + } + if ((tipBox != NULL) || (predefinedTipBoxText != NULL)) { // do not display "Swipe to review" if a tip-box is displayed STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL; - - STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon; - STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text; + if (predefinedTipBoxText != NULL) { + STARTING_CONTENT.content.extendedCenter.tipBox.icon = NULL; + STARTING_CONTENT.content.extendedCenter.tipBox.text = predefinedTipBoxText; + } + else { + STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon; + STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text; + } STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN; STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL; - tipBoxModalTitle = tipBox->modalTitle; - // the only supported type yet is @ref INFOS_LIST - if (tipBox->type == INFOS_LIST) { - tipBoxInfoList.nbInfos = tipBox->infos.nbInfos; - tipBoxInfoList.withExtensions = tipBox->infos.withExtensions; - tipBoxInfoList.infoTypes = PIC(tipBox->infos.infoTypes); - tipBoxInfoList.infoContents = PIC(tipBox->infos.infoContents); - tipBoxInfoList.infoExtensions = PIC(tipBox->infos.infoExtensions); - } } // Then the tag/value pairs @@ -3033,7 +3058,7 @@ void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t qui else { nbgl_pageInfoDescription_t info = {.bottomButtonStyle = NO_BUTTON_STYLE, .footerText = NULL, - .centeredInfo.icon = &C_Denied_Circle_64px, + .centeredInfo.icon = &DENIED_CIRCLE_ICON, .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2, .centeredInfo.onTop = false, .centeredInfo.style = LARGE_CASE_INFO, @@ -3168,7 +3193,7 @@ void nbgl_useCaseConfirm(const char *message, .centeredInfo.text2 = subMessage, .centeredInfo.text3 = NULL, .centeredInfo.style = LARGE_CASE_INFO, - .centeredInfo.icon = &C_Important_Circle_64px, + .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON, .centeredInfo.offsetY = 0, .confirmationText = confirmText, .confirmationToken = CHOICE_TOKEN, @@ -3401,7 +3426,8 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType, NULL, choiceCallback, false, - true); + true, + false); } /** @@ -3444,7 +3470,7 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT } /** - * @brief Draws a flow of pages of a review requiring a warning page before the review. + * @brief Draws a flow of pages of a review requiring if necessary a warning page before the review. * Moreover, the first and last pages of review display a top-right button, that displays more * information about the warnings * @@ -3461,7 +3487,7 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT * @param reviewSubTitle string to set under reviewTitle (can be NULL) * @param finishTitle string used in the last review page * @param tipBox parameter to build a tip-box and necessary modal (can be NULL) - * @param warning structure to build the initial warning page (cannot be NULL) + * @param warning structure to build the initial warning page (can be NULL) * @param choiceCallback callback called when operation is accepted (param is true) or rejected * (param is false) */ @@ -3475,18 +3501,66 @@ void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback) { + // memorize tipBox because it can be in the call stack of the caller + if (tipBox != NULL) { + memcpy(&activeTipBox, tipBox, sizeof(activeTipBox)); + } + else { + memset(&activeTipBox, 0, sizeof(activeTipBox)); + } + // if no warning at all, it's a simple review + if ((warning == NULL) + || ((warning->predefinedSet == 0) && (warning->introDetails == NULL) + && (warning->reviewDetails == NULL))) { + useCaseReview(operationType, + tagValueList, + icon, + reviewTitle, + reviewSubTitle, + finishTitle, + tipBox, + choiceCallback, + false, + true, + false); + return; + } + if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) { + operationType |= NO_THREAT_OPERATION; + } + else { + operationType |= RISKY_OPERATION; + } + memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); reviewWithWarnCtx.isStreaming = false; - reviewWithWarnCtx.operationType = operationType | RISKY_OPERATION; + reviewWithWarnCtx.operationType = operationType; reviewWithWarnCtx.tagValueList = tagValueList; reviewWithWarnCtx.icon = icon; reviewWithWarnCtx.reviewTitle = reviewTitle; reviewWithWarnCtx.reviewSubTitle = reviewSubTitle; reviewWithWarnCtx.finishTitle = finishTitle; - reviewWithWarnCtx.tipBox = tipBox; reviewWithWarnCtx.warning = warning; reviewWithWarnCtx.choiceCallback = choiceCallback; + // display the initial warning only of a risk/threat or blind signing + if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN)) + && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN)) + && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) { + useCaseReview(operationType, + tagValueList, + icon, + reviewTitle, + reviewSubTitle, + finishTitle, + tipBox, + choiceCallback, + false, + true, + true); + return; + } + displayInitialWarning(); } @@ -3523,7 +3597,8 @@ void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, NULL, choiceCallback, true, - true); + true, + false); } /** @@ -3609,7 +3684,7 @@ void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t ope /** * @brief Start drawing the flow of pages of a blind-signing review. The review is preceded by a - * warning page + * warning page if needed * @note This should be followed by calls to @ref nbgl_useCaseReviewStreamingContinue and finally * to * @ref nbgl_useCaseReviewStreamingFinish. @@ -3629,16 +3704,38 @@ void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operati const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback) { + // if no warning at all, it's a simple review + if ((warning == NULL) + || ((warning->predefinedSet == 0) && (warning->introDetails == NULL) + && (warning->reviewDetails == NULL))) { + useCaseReviewStreamingStart( + operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true); + return; + } + if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) { + operationType |= NO_THREAT_OPERATION; + } + else { + operationType |= RISKY_OPERATION; + } memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); reviewWithWarnCtx.isStreaming = true; - reviewWithWarnCtx.operationType = operationType | RISKY_OPERATION; + reviewWithWarnCtx.operationType = operationType; reviewWithWarnCtx.icon = icon; reviewWithWarnCtx.reviewTitle = reviewTitle; reviewWithWarnCtx.reviewSubTitle = reviewSubTitle; reviewWithWarnCtx.choiceCallback = choiceCallback; reviewWithWarnCtx.warning = warning; + // display the initial warning only of a risk/threat or blind signing + if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN)) + && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN)) + && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) { + useCaseReviewStreamingStart( + operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true); + return; + } displayInitialWarning(); } diff --git a/tests/screenshots/flows/wallet/app-sdk/flow_w3c_blind.json b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_blind.json index 08ad8421e..b9274402d 100644 --- a/tests/screenshots/flows/wallet/app-sdk/flow_w3c_blind.json +++ b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_blind.json @@ -15,12 +15,25 @@ { "name": "w3c-disabled-blind-01", "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-disabled-blind-04" + }, { "object": "CHOICE_2", "page": "w3c-disabled-blind-02" } ] }, + { + "name": "w3c-disabled-blind-04", + "targets": [ + { + "app_event": "BACK_BUTTON", + "page": "w3c-disabled-blind-01" + } + ] + }, { "name": "w3c-disabled-blind-02", "targets": [ @@ -28,6 +41,10 @@ "object": "TOP_RIGHT_BUTTON", "page": "w3c-disabled-blind-03" }, + { + "object": "TIP_BOX", + "page": "w3c-disabled-blind-03-bis" + }, { "app_event": "DOGE_SIGN", "param": "5", @@ -40,20 +57,68 @@ "name": "w3c-disabled-blind-03", "targets": [ { - "app_event": "BACK_BUTTON", + "object": "CONTROLS,0", + "page": "w3c-disabled-blind-04-bis" + }, + { + "object": "BACK_BUTTON", "page": "w3c-disabled-blind-02" } ] }, + { + "name": "w3c-disabled-blind-04-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-disabled-blind-03" + } + ] + }, + { + "name": "w3c-disabled-blind-03-bis", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-disabled-blind-04-ter" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-disabled-blind-02" + } + ] + }, + { + "name": "w3c-disabled-blind-04-ter", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-disabled-blind-03-bis" + } + ] + }, { "name": "w3c-no-threat-blind-01", "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-no-threat-blind-04" + }, { "object": "CHOICE_2", "page": "w3c-no-threat-blind-02" } ] }, + { + "name": "w3c-no-threat-blind-04", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-blind-01" + } + ] + }, { "name": "w3c-no-threat-blind-02", "targets": [ @@ -61,6 +126,10 @@ "object": "TOP_RIGHT_BUTTON", "page": "w3c-no-threat-blind-03" }, + { + "object": "TIP_BOX", + "page": "w3c-no-threat-blind-03-bis" + }, { "app_event": "DOGE_SIGN", "param": "21", @@ -73,20 +142,68 @@ "name": "w3c-no-threat-blind-03", "targets": [ { - "app_event": "BACK_BUTTON", + "object": "CONTROLS,1", + "page": "w3c-no-threat-blind-04-bis" + }, + { + "object": "BACK_BUTTON", "page": "w3c-no-threat-blind-02" } ] }, + { + "name": "w3c-no-threat-blind-04-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-blind-03" + } + ] + }, + { + "name": "w3c-no-threat-blind-03-bis", + "targets": [ + { + "object": "CONTROLS,1", + "page": "w3c-no-threat-blind-04-ter" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-blind-02" + } + ] + }, + { + "name": "w3c-no-threat-blind-04-ter", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-blind-03-bis" + } + ] + }, { "name": "w3c-threat-blind-01", "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-threat-blind-01-bis" + }, { "object": "CHOICE_2", "page": "w3c-threat-blind-02" } ] }, + { + "name": "w3c-threat-blind-01-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-blind-01" + } + ] + }, { "name": "w3c-threat-blind-02", "targets": [ @@ -106,15 +223,15 @@ "name": "w3c-threat-blind-03", "targets": [ { - "object": "CONTROLS,1", + "object": "CONTROLS,0", "page": "w3c-threat-blind-04" }, { - "object": "CONTROLS,0", + "object": "CONTROLS,1", "page": "w3c-threat-blind-05" }, { - "app_event": "BACK_BUTTON", + "object": "BACK_BUTTON", "page": "w3c-threat-blind-02" } ] @@ -123,7 +240,7 @@ "name": "w3c-threat-blind-04", "targets": [ { - "app_event": "BACK_BUTTON", + "object": "BACK_BUTTON", "page": "w3c-threat-blind-03" } ] @@ -132,7 +249,7 @@ "name": "w3c-threat-blind-05", "targets": [ { - "app_event": "BACK_BUTTON", + "object": "BACK_BUTTON", "page": "w3c-threat-blind-03" } ] @@ -159,11 +276,11 @@ "name": "w3c-issue-blind-03", "targets": [ { - "object": "CONTROLS,1", + "object": "CONTROLS,0", "page": "w3c-issue-blind-04" }, { - "object": "CONTROLS,0", + "object": "CONTROLS,1", "page": "w3c-issue-blind-05" } ] @@ -172,7 +289,7 @@ "name": "w3c-issue-blind-04", "targets": [ { - "app_event": "BACK_BUTTON", + "object": "BACK_BUTTON", "page": "w3c-issue-blind-03" } ] diff --git a/tests/screenshots/flows/wallet/app-sdk/flow_w3c_dapp.json b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_dapp.json new file mode 100644 index 000000000..d8e9f7ec5 --- /dev/null +++ b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_dapp.json @@ -0,0 +1,312 @@ +{ + "title": "w3c_dapp", + "pages": [ + { + "name": "Entry", + "targets": [ + { + "app_event": "DOGE_SIGN", + "param": "2", + "page": "w3c-disabled-clear-dapp-01", + "comments": "blind=0, dApp=1, w3c_enabled=0, w3c_issue=0, w3c_threat=0" + } + ] + }, + { + "name": "w3c-disabled-clear-dapp-01", + "targets": [ + { + "object": "RIGHT_BUTTON", + "page": "w3c-disabled-clear-dapp-02" + } + ] + }, + { + "name": "w3c-disabled-clear-dapp-02", + "targets": [ + { + "object": "VALUE_BUTTON_1", + "page": "w3c-disabled-clear-dapp-03" + }, + { + "app_event": "DOGE_SIGN", + "param": "6", + "page": "w3c-no-threat-clear-dapp-01", + "comments": "blind=0, dApp=1, w3c_enabled=1, w3c_issue=0, w3c_threat=0" + } + ] + }, + { + "name": "w3c-disabled-clear-dapp-03", + "targets": [ + { + "object": "VALUE_BUTTON_2", + "page": "w3c-disabled-clear-dapp-04" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-disabled-clear-dapp-02" + } + ] + }, + { + "name": "w3c-disabled-clear-dapp-04", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-disabled-clear-dapp-03" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-01", + "targets": [ + { + "object": "TIP_BOX", + "page": "w3c-no-threat-clear-dapp-02" + }, + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-no-threat-clear-dapp-02-bis" + }, + { + "object": "RIGHT_BUTTON", + "page": "w3c-no-threat-clear-dapp-03" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-02", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-clear-dapp-01" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-02-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-clear-dapp-01" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-03", + "targets": [ + { + "object": "VALUE_BUTTON_1", + "page": "w3c-no-threat-clear-dapp-04" + }, + { + "app_event": "DOGE_SIGN", + "param": "22", + "page": "w3c-threat-clear-dapp-01", + "comments": "blind=0, dApp=1, w3c_enabled=1, w3c_issue=0, w3c_threat=1" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-04", + "targets": [ + { + "object": "VALUE_BUTTON_2", + "page": "w3c-no-threat-clear-dapp-05" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-clear-dapp-03" + } + ] + }, + { + "name": "w3c-no-threat-clear-dapp-05", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-clear-dapp-04" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-01", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-threat-clear-dapp-01-bis" + }, + { + "object": "CHOICE_2", + "page": "w3c-threat-clear-dapp-02" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-01-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-dapp-01" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-02", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-threat-clear-dapp-03" + }, + { + "object": "RIGHT_BUTTON", + "page": "w3c-threat-clear-dapp-05" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-03", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-threat-clear-dapp-04" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-dapp-02" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-04", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-dapp-03" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-05", + "targets": [ + { + "object": "VALUE_BUTTON_1", + "page": "w3c-threat-clear-dapp-06" + }, + { + "app_event": "DOGE_SIGN", + "param": "14", + "page": "w3c-issue-clear-dapp-01", + "comments": "blind=0, dApp=1, w3c_enabled=1, w3c_issue=1, w3c_threat=0" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-06", + "targets": [ + { + "object": "VALUE_BUTTON_2", + "page": "w3c-threat-clear-dapp-07" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-dapp-05" + } + ] + }, + { + "name": "w3c-threat-clear-dapp-07", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-dapp-06" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-01", + "targets": [ + { + "object": "TIP_BOX", + "page": "w3c-issue-clear-dapp-02" + }, + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-issue-clear-dapp-02-bis" + }, + { + "object": "RIGHT_BUTTON", + "page": "w3c-issue-clear-dapp-03" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-02", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-issue-clear-dapp-04" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-dapp-01" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-04", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-dapp-02" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-02-bis", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-issue-clear-dapp-04-bis" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-dapp-01" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-04-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-dapp-02-bis" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-03", + "targets": [ + { + "object": "VALUE_BUTTON_1", + "page": "w3c-issue-clear-dapp-05" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-05", + "targets": [ + { + "object": "VALUE_BUTTON_2", + "page": "w3c-issue-clear-dapp-06" + } + ] + }, + { + "name": "w3c-issue-clear-dapp-06", + "targets": [ + ] + } + ] +} diff --git a/tests/screenshots/flows/wallet/app-sdk/flow_w3c_no_dapp.json b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_no_dapp.json new file mode 100644 index 000000000..25090cd63 --- /dev/null +++ b/tests/screenshots/flows/wallet/app-sdk/flow_w3c_no_dapp.json @@ -0,0 +1,194 @@ +{ + "title": "w3c_no_dapp", + "pages": [ + { + "name": "Entry", + "targets": [ + { + "app_event": "DOGE_SIGN", + "param": "0", + "page": "w3c-disabled-clear-no-dapp-01", + "comments": "blind=0, dApp=0, w3c_enabled=0, w3c_issue=0, w3c_threat=0" + } + ] + }, + { + "name": "w3c-disabled-clear-no-dapp-01", + "targets": [ + { + "app_event": "DOGE_SIGN", + "param": "4", + "page": "w3c-no-threat-clear-no-dapp-01", + "comments": "blind=0, dApp=0, w3c_enabled=1, w3c_issue=0, w3c_threat=0" + } + ] + }, + { + "name": "w3c-disabled-clear-no-dapp-03", + "targets": [ + { + "app_event": "BACK_BUTTON", + "page": "w3c-disabled-clear-no-dapp-02" + } + ] + }, + { + "name": "w3c-no-threat-clear-no-dapp-01", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-no-threat-clear-no-dapp-02" + }, + { + "app_event": "DOGE_SIGN", + "param": "20", + "page": "w3c-threat-clear-no-dapp-01", + "comments": "blind=0, dApp=0, w3c_enabled=1, w3c_issue=0, w3c_threat=1" + } + ] + }, + { + "name": "w3c-no-threat-clear-no-dapp-02", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-no-threat-clear-no-dapp-01" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-01", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-threat-clear-no-dapp-04" + }, + { + "object": "CHOICE_2", + "page": "w3c-threat-clear-no-dapp-02" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-04", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-no-dapp-01" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-02", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-threat-clear-no-dapp-03" + }, + { + "object": "TIP_BOX", + "page": "w3c-threat-clear-no-dapp-03-bis" + }, + { + "app_event": "DOGE_SIGN", + "param": "12", + "page": "w3c-issue-clear-no-dapp-01", + "comments": "blind=0, dApp=0, w3c_enabled=1, w3c_issue=1, w3c_threat=0" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-03", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-threat-clear-no-dapp-04-bis" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-no-dapp-02" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-04-bis", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-no-dapp-03" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-03-bis", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-threat-clear-no-dapp-04-ter" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-no-dapp-02" + } + ] + }, + { + "name": "w3c-threat-clear-no-dapp-04-ter", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-threat-clear-no-dapp-03-bis" + } + ] + }, + { + "name": "w3c-issue-clear-no-dapp-01", + "targets": [ + { + "object": "TOP_RIGHT_BUTTON", + "page": "w3c-issue-clear-no-dapp-02" + }, + { + "object": "TIP_BOX", + "page": "w3c-issue-clear-no-dapp-02-bis" + } + ] + }, + { + "name": "w3c-issue-clear-no-dapp-02", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-issue-clear-no-dapp-03" + }, + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-no-dapp-01" + } + ] + }, + { + "name": "w3c-issue-clear-no-dapp-03", + "targets": [ + { + "object": "BACK_BUTTON", + "page": "w3c-issue-clear-no-dapp-02" + } + ] + }, + { + "name": "w3c-issue-clear-no-dapp-02-bis", + "targets": [ + { + "object": "CONTROLS,0", + "page": "w3c-issue-clear-no-dapp-03-bis" + } + ] + }, + { + "name": "w3c-issue-clear-no-dapp-03-bis", + "targets": [ + ] + } + ] +} diff --git a/tests/screenshots/src/app/wallet/app_dogecoin_start.c b/tests/screenshots/src/app/wallet/app_dogecoin_start.c index a2c9735d8..781d1bb98 100644 --- a/tests/screenshots/src/app/wallet/app_dogecoin_start.c +++ b/tests/screenshots/src/app/wallet/app_dogecoin_start.c @@ -31,7 +31,11 @@ static nbgl_contentValueExt_t extension .explanation = NULL, .aliasType = ENS_ALIAS}; +static nbgl_contentValueExt_t dAppExtension; +static nbgl_contentInfoList_t dAppInfoList; + static nbgl_contentTagValue_t pairs[] = { + {.item = "Interaction with", .value = "Uniswap"}, {.item = "Amount", .value = "0.0001 BTC"}, {.item = "To", .value = "toto.eth", .extension = &extension, .aliasValue = 1}, {.item = "Fees", .value = "0.000000698 BTC"}, @@ -52,6 +56,18 @@ static const nbgl_contentInfoList_t infoContentsList static nbgl_warning_t warning = {0}; +static const char *dAppsInfoTypes[3] = {"Contract owner", "Contract", "Deployed on"}; +static const char *dAppsInfoValues[3] + = {"Uniswap Labs\nwww.uniswap.org", "Uniswap v3 Router 2", "2023-04-28"}; +static const nbgl_contentValueExt_t dAppsInfoExtensions[3] = { + {0}, + {.aliasType = QR_CODE_ALIAS, + .title = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + .fullValue = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + .explanation = "Scan to view on Etherscan"}, + {0} +}; + /********************** * VARIABLES **********************/ @@ -122,22 +138,41 @@ void app_dogecoinSignTransaction(bool blind, else if (w3c_threat) { warning.predefinedSet |= 1 << W3C_THREAT_DETECTED_WARN; } + else { + warning.predefinedSet |= 1 << W3C_NO_THREAT_WARN; + } + } + if (dApp) { + pairList.pairs = pairs; + pairs[0].aliasValue = 1; + pairs[0].extension = &dAppExtension; + dAppExtension.aliasType = INFO_LIST_ALIAS; + dAppExtension.infolist = &dAppInfoList; + dAppInfoList.nbInfos = 3; + dAppInfoList.infoTypes = dAppsInfoTypes; + dAppInfoList.infoContents = dAppsInfoValues; + dAppInfoList.infoExtensions = dAppsInfoExtensions; + dAppInfoList.withExtensions = true; + } + else { + pairList.pairs = &pairs[1]; } - UNUSED(dApp); - warning.reportProvider = "Blockaid"; - warning.providerMessage = "Losing swap"; - warning.reportUrl = "url.com/od24xz"; + warning.reportProvider = "Blockaid"; + warning.providerMessage + = "This transaction involves a malicious address. Your assets will most likely be stolen."; + warning.reportUrl = "url.com/od24xz"; + warning.dAppProvider = "Uniswap"; // Start review - nbgl_useCaseReviewWithWarning(TYPE_TRANSACTION, - &pairList, - &C_ic_asset_doge_64, - "Review transaction\nto send Dogecoin", - NULL, - "Sign transaction to\nsend Dogecoin?", - NULL, - &warning, - onTransactionAccept); + nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, + &pairList, + &C_ic_asset_eth_64, + "Review transaction", + NULL, + "Sign transaction to\nsend Dogecoin?", + NULL, + &warning, + onTransactionAccept); } /** diff --git a/tests/screenshots/src/app/wallet/app_xrp_start.c b/tests/screenshots/src/app/wallet/app_xrp_start.c index 533a7d8bc..d3bcf2ed0 100644 --- a/tests/screenshots/src/app/wallet/app_xrp_start.c +++ b/tests/screenshots/src/app/wallet/app_xrp_start.c @@ -167,6 +167,7 @@ void app_xrpSignMessage(void) NULL, "Sign transaction to\nsend Etherum?", &tipBox, + NULL, onTransactionAccept); }