From 29b3c3ed0e73f58daf77ff6f2c900cb606c802a1 Mon Sep 17 00:00:00 2001 From: Nicolas Roggeman Date: Thu, 9 Jan 2025 11:00:23 +0100 Subject: [PATCH] Add privacy report warning in NBGL Use Case --- lib_nbgl/glyphs/32px/Privacy_32px.png | Bin 0 -> 663 bytes .../glyphs/40px/Important_Circle_40px.png | Bin 0 -> 652 bytes lib_nbgl/glyphs/40px/Privacy_40px.png | Bin 0 -> 833 bytes lib_nbgl/include/nbgl_obj.h | 4 + lib_nbgl/include/nbgl_use_case.h | 88 ++- lib_nbgl/src/nbgl_use_case.c | 614 ++++++++++++++---- 6 files changed, 586 insertions(+), 120 deletions(-) create mode 100755 lib_nbgl/glyphs/32px/Privacy_32px.png create mode 100755 lib_nbgl/glyphs/40px/Important_Circle_40px.png create mode 100755 lib_nbgl/glyphs/40px/Privacy_40px.png diff --git a/lib_nbgl/glyphs/32px/Privacy_32px.png b/lib_nbgl/glyphs/32px/Privacy_32px.png new file mode 100755 index 0000000000000000000000000000000000000000..700f60a21d4ff9c57d055c639e8fe636e6c90696 GIT binary patch literal 663 zcmV;I0%-k-P)PAx$FbmV=!OAJ`{^G|@pO?vP*Q zQaFw+tFpSnd~^I}rxHlunu8J!%qcTx;@C9jCqH`@;FMD-F`bS!VebQf72?QV;Vv;R zcdn5*WOgxwYy;+t$_c;~>fnsPW8fnPyus^+jlSPe(HUz6+>7KC^al%&pglpywP9cS zt8P)iCCYG^m;KgAPWa2hB3js|snG%^@!M*b^4Z9VQW!B8w>EsfiSa=TxJF*U1Il<5 zV^@ebk$^E_v{2KaN+-z)zn2P3B7s+vCNZ`uq;Vt0`c>>xHtPGaJ#N(`{jTsxW37Ch zvJb-6YGv|$piA8=yS2GI=6!x=*mL4$>N;g3K}Qu}V80FQJH$rCXfxuf6gt#jJRF_riz91L}^T)n|%mVfO%fwog3wMw|`{l1Dh?dq8Z{ x9X{t;|3=8^BNEqq9j6e#4G45gDqcV>JOT9DE47hM$XEaX002ovPDHLkV1j9f9IOBU literal 0 HcmV?d00001 diff --git a/lib_nbgl/glyphs/40px/Important_Circle_40px.png b/lib_nbgl/glyphs/40px/Important_Circle_40px.png new file mode 100755 index 0000000000000000000000000000000000000000..5037b98d5e44f1e99efb87e3cf6bd4645ec90e76 GIT binary patch literal 652 zcmV;70(1R|P)w$f!vcpWx*|J928>}xPyH?t9ti=r*clS=;t*IOeVPPpu~9MQ*`edxx0 zU`g!47)B%~H`nM7ROG}MM&yXx+@L?uzL_zMNV-UC^oO0M!ZC?9BA;6g`@}b~8zOHS zwTK zTmWbmR=66ZCKyxgXBawAj%F6-1kLH3S;z&{$d{_Uooh(LeKA8uK9M5Cx1Aw#_#<|@ z8JfX)^>LzMkIB$O;5vvgA4i5eIMOP(iUT<}c?Mfxi3kYTWtJJz;i}G+NPvo71-lw1 z5VsGmU>B@{$}6Y9aJp#vt~=qdOMl8w;PAG(>Y5cgC_V_dOLw7+Egc4n-QbGiauoKt mwufjI_SG>!4Q5TQ{};cGK{!VNt?}&u000059vpaYT$AQjlIfJ+6YgXHAX4J+NQKHwPt@tfJ< z?zCDxX+ItY5W)>?h?eFJh_yC2HmPS_x+999&mR5EMw!)%1;!msx!7~4RecPY|M@-}PaEbTAj6aE$=nORKpwP|QdU5t< zl{O4Q4{F+INy8fDFi6J~>#hX0q_1%w?tOzIN)7T*o|p@2ozdHPU4=Xv^8Dd}Z~oWk4P^7ZTn6esGBs>h+Vn{pWzB&==6z~f!xw7js*b!{U6J(FkKciJlZk)**2VlMt+41 zLN_ds#{&66ts)&I8KZ7k3Oeytw-OAx2|iXj42FH!fs+aoX>+h$bTDF zj7{+z7-JGCTgW8$kk5g!yktnu{J7Gh1h5EKMkRC&`2Q8uU52qMk<=)}5rr{9jL&g) zu>n6Z8o(T1ex*B{jUnXo@i~s|^IQKitem, pair->value, pair->extension); } - else if (token == BLIND_WARNING_TOKEN) { - displayBlindWarning(); + else if (token == WARNING_BUTTON_TOKEN) { + // top-right button, display the security modal + reviewWithWarnCtx.securityReportLevel = 1; + // if preset is used + if (reviewWithWarnCtx.warning->predefinedSet) { + displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet); + } + else { + // use customized warning + if (reviewWithWarnCtx.isIntro) { + displayCustomizedSecurityReport(reviewWithWarnCtx.warning->introDetails); + } + else { + displayCustomizedSecurityReport(reviewWithWarnCtx.warning->reviewDetails); + } + } } else if (token == TIP_BOX_TOKEN) { displayTipBoxModal(); @@ -975,13 +1023,13 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content, bool isStreamingNavAndBlindOperation = (navType == STREAMING_NAV) && (bundleNavContext.reviewStreaming.operationType & BLIND_OPERATION); - bool isGenericNavAndBlindOperation + bool isGenericNavAndRiskyOperation = (navType == GENERIC_NAV) && (bundleNavContext.review.operationType & BLIND_OPERATION); // if first or last page of review and blind operation, add the warning top-right button - if (isFirstOrLastPage && (isStreamingNavAndBlindOperation || isGenericNavAndBlindOperation)) { + if (isFirstOrLastPage && (isStreamingNavAndBlindOperation || isGenericNavAndRiskyOperation)) { pageContent->topRightIcon = &WARNING_ICON; - pageContent->topRightToken = BLIND_WARNING_TOKEN; + pageContent->topRightToken = WARNING_BUTTON_TOKEN; } return true; @@ -1196,37 +1244,6 @@ static void displayFullValuePage(const char *backText, nbgl_refresh(); } -// function used to display the modal warning when touching the alert symbol of a blind review -static void displayBlindWarning(void) -{ - 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 = NULL}; - nbgl_layoutCenteredInfo_t centeredInfo - = {.icon = NULL, .text3 = NULL, .style = LARGE_CASE_INFO, .offsetY = 0, .onTop = false}; - centeredInfo.text1 = "Security risk detected"; - centeredInfo.text2 - = "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\n" - "Learn more: ledger.com/e8"; - - genericContext.modalLayout = nbgl_layoutGet(&layoutDescription); - // add header with the tag part of the pair, to go back - nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc); - // add full value text - nbgl_layoutAddCenteredInfo(genericContext.modalLayout, ¢eredInfo); - - // draw & refresh - nbgl_layoutDraw(genericContext.modalLayout); - nbgl_refresh(); -} - // function used to display the modal containing tip-box infos static void displayTipBoxModal(void) { @@ -1266,7 +1283,7 @@ static void displayAddressQRCode(void) // display the address as QR Code nbgl_layoutDescription_t layoutDescription = {.modal = true, .withLeftBorder = true, - .onActionCallback = &addressLayoutTouchCallbackQR, + .onActionCallback = &modalLayoutTouchCallback, .tapActionText = NULL}; nbgl_layoutHeader_t headerDesc = { .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER}; @@ -1298,38 +1315,128 @@ static void displayAddressQRCode(void) nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode); - nbgl_layoutAddFooter(addressConfirmationContext.modalLayout, "Close", 0, TUNE_TAP_CASUAL); + nbgl_layoutAddFooter( + addressConfirmationContext.modalLayout, "Close", DISMISS_QR_TOKEN, TUNE_TAP_CASUAL); nbgl_layoutDraw(addressConfirmationContext.modalLayout); nbgl_refresh(); } -// called when quit button is touched on Address verification page -static void addressLayoutTouchCallbackQR(int token, uint8_t index) -{ - UNUSED(token); - UNUSED(index); - - // dismiss modal - nbgl_layoutRelease(addressConfirmationContext.modalLayout); - addressConfirmationContext.modalLayout = NULL; - nbgl_screenRedraw(); - nbgl_refresh(); -} #endif // NBGL_QRCODE // called when header is touched on modal page, to dismiss it static void modalLayoutTouchCallback(int token, uint8_t index) { - UNUSED(token); UNUSED(index); - - // dismiss modal - nbgl_layoutRelease(genericContext.modalLayout); - genericContext.modalLayout = NULL; - nbgl_screenRedraw(); + if (token == DISMISS_QR_TOKEN) { + // dismiss modal + nbgl_layoutRelease(addressConfirmationContext.modalLayout); + addressConfirmationContext.modalLayout = NULL; + nbgl_screenRedraw(); + } + else if (token == DISMISS_WARNING_TOKEN) { + // dismiss modal + nbgl_layoutRelease(reviewWithWarnCtx.modalLayout); + // if already at first level, simply redraw the background + if (reviewWithWarnCtx.securityReportLevel <= 1) { + reviewWithWarnCtx.modalLayout = NULL; + nbgl_screenRedraw(); + } + else { + // We are at level 2 of warning modal, so re-display the level 1 + reviewWithWarnCtx.securityReportLevel = 1; + // if preset is used + if (reviewWithWarnCtx.warning->predefinedSet) { + displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet); + } + else { + // use customized warning + const nbgl_warningDetails_t *details + = (reviewWithWarnCtx.isIntro) ? reviewWithWarnCtx.warning->introDetails + : reviewWithWarnCtx.warning->reviewDetails; + displayCustomizedSecurityReport(details); + } + return; + } + } + else if ((token >= FIRST_WARN_BAR_TOKEN) && (token <= LAST_WARN_BAR_TOKEN)) { + // dismiss modal before creating a new one + nbgl_layoutRelease(reviewWithWarnCtx.modalLayout); + reviewWithWarnCtx.securityReportLevel = 2; + // if preset is used + if (reviewWithWarnCtx.warning->predefinedSet) { + displaySecurityReport(1 << (token - FIRST_WARN_BAR_TOKEN)); + } + else { + // use customized warning + const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro) + ? reviewWithWarnCtx.warning->introDetails + : reviewWithWarnCtx.warning->reviewDetails; + if (details->type == BAR_LIST_WARNING) { + displayCustomizedSecurityReport( + &details->barList.details[token - FIRST_WARN_BAR_TOKEN]); + } + } + return; + } + else { + // dismiss modal + nbgl_layoutRelease(genericContext.modalLayout); + genericContext.modalLayout = NULL; + nbgl_screenRedraw(); + } nbgl_refresh(); } +// called when item are touched in layout +static void layoutTouchCallback(int token, uint8_t index) +{ + // choice buttons in initial warning page + if (token == WARNING_CHOICE_TOKEN) { + if (index == 0) { // top button to exit + reviewWithWarnCtx.choiceCallback(false); + } + else { // bottom button to continue to review + reviewWithWarnCtx.isIntro = false; + if (reviewWithWarnCtx.isStreaming) { + useCaseReviewStreamingStart(reviewWithWarnCtx.operationType, + reviewWithWarnCtx.icon, + reviewWithWarnCtx.reviewTitle, + reviewWithWarnCtx.reviewSubTitle, + reviewWithWarnCtx.choiceCallback, + false); + } + else { + useCaseReview(reviewWithWarnCtx.operationType, + reviewWithWarnCtx.tagValueList, + reviewWithWarnCtx.icon, + reviewWithWarnCtx.reviewTitle, + reviewWithWarnCtx.reviewSubTitle, + reviewWithWarnCtx.finishTitle, + reviewWithWarnCtx.tipBox, + reviewWithWarnCtx.choiceCallback, + false, + false); + } + } + } + // top-right button in initial warning page + else if (token == WARNING_BUTTON_TOKEN) { + // start at first level of security report + reviewWithWarnCtx.securityReportLevel = 1; + // if preset is used + if (reviewWithWarnCtx.warning->predefinedSet) { + displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet); + } + else { + // use customized warning + const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro) + ? reviewWithWarnCtx.warning->introDetails + : reviewWithWarnCtx.warning->reviewDetails; + displayCustomizedSecurityReport(details); + } + } +} + // called when skip button is touched in footer, during forward only review static void displaySkipWarning(void) { @@ -1718,51 +1825,275 @@ static void bundleNavReviewStreamingChoice(bool confirm) } } -// function called when the warning page of Blind Signing review buttons are pressed -static void blindSigningWarningCallback(bool confirm) +// function used to display the security level page in modal +// it can be either a single page with text or QR code, or a list of touchable bars leading +// to single pages +static void displaySecurityReport(uint32_t set) { - if (confirm) { // top button to exit - blindSigningContext.choiceCallback(false); + nbgl_layoutDescription_t layoutDescription = {.modal = true, + .withLeftBorder = true, + .onActionCallback = modalLayoutTouchCallback, + .tapActionText = NULL}; + nbgl_layoutHeader_t headerDesc = {.type = HEADER_BACK_AND_TEXT, + .separationLine = true, + .backAndText.icon = NULL, + .backAndText.tuneId = TUNE_TAP_CASUAL, + .backAndText.token = DISMISS_WARNING_TOKEN}; + uint8_t i; + uint8_t nbWarnings = 0; + + reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription); + + for (i = 0; i < NB_WARNING_TYPES; i++) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) { + nbWarnings++; + } + } + + if ((reviewWithWarnCtx.securityReportLevel == 1) && (nbWarnings > 1)) { + // if more than one warning warning, so use a list of touchable bars + for (i = 0; i < NB_WARNING_TYPES; i++) { + if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) { + nbgl_layoutBar_t bar; + bar.text = securityReportItems[i].text; + bar.subText = securityReportItems[i].subText; + 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; + nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar); + nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout); + } + } + headerDesc.backAndText.text = "Security report"; + nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc); + nbgl_layoutDraw(reviewWithWarnCtx.modalLayout); + nbgl_refresh(); + return; } - else { // bottom button to continue to review - if (blindSigningContext.isStreaming) { - useCaseReviewStreamingStart(blindSigningContext.operationType, - blindSigningContext.icon, - blindSigningContext.reviewTitle, - blindSigningContext.reviewSubTitle, - blindSigningContext.choiceCallback, - false); + if (set & (1 << BLIND_SIGNING_WARN)) { + if (reviewWithWarnCtx.isIntro) { + // 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); + 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); + headerDesc.separationLine = false; + } + } + 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"; + nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); + headerDesc.separationLine = false; + } + else if (set & (1 << W3C_THREAT_DETECTED_WARN)) { + if (reviewWithWarnCtx.isIntro) { + // display a QR Code if in intro + nbgl_layoutQRCode_t qrCode = {.url = "url.com/od24xz", + .text1 = "url.com/od24xz", + .text2 = "Scan to view the threat report from Blockaid.", + .centered = true, + .offsetY = 0}; + nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &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.smallTitle = "Known drainer contract"; + info.description + = "This transaction was scanned as malicious by Web3 Checks.\n\n" + "View full Blockaid report:\nurl.com/od24xz"; + nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); + headerDesc.separationLine = false; + } + } + else if (set & (1 << W3C_LOSING_SWAP_WARN)) { + if (reviewWithWarnCtx.isIntro) { + // display a QR Code if in intro + nbgl_layoutQRCode_t qrCode = {.url = "url.com/od24xz", + .text1 = "url.com/od24xz", + .text2 = "Scan to view the risk report from Blockaid.", + .centered = true, + .offsetY = 0}; + nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode); + headerDesc.backAndText.text = "Web3 Checks risk report"; } else { - useCaseReview(blindSigningContext.operationType, - blindSigningContext.tagValueList, - blindSigningContext.icon, - blindSigningContext.reviewTitle, - blindSigningContext.reviewSubTitle, - blindSigningContext.finishTitle, - blindSigningContext.tipBox, - blindSigningContext.choiceCallback, - false, - false); + // display a centered if in review + nbgl_contentCenter_t info = {0}; + info.icon = &C_Warning_64px; + info.title = "Risk detected"; + info.smallTitle = "Losing swap"; + info.description + = "This transaction was scanned as risky by Web3 Checks.\n\n" + "View full Blockaid report:\\nurl.com/od24xz"; + nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info); + headerDesc.separationLine = false; } } + else { + } + nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc); + nbgl_layoutDraw(reviewWithWarnCtx.modalLayout); + nbgl_refresh(); } -// function used to display the warning page when starting a Bling Signing review -static void blindSigningWarning(void) +// function used to display the security level page in modal +// it can be either a single page with text or QR code, or a list of touchable bars leading +// to single pages +static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details) +{ + nbgl_layoutDescription_t layoutDescription = {.modal = true, + .withLeftBorder = true, + .onActionCallback = modalLayoutTouchCallback, + .tapActionText = NULL}; + nbgl_layoutHeader_t headerDesc = {.type = HEADER_BACK_AND_TEXT, + .separationLine = true, + .backAndText.icon = NULL, + .backAndText.tuneId = TUNE_TAP_CASUAL, + .backAndText.token = DISMISS_WARNING_TOKEN}; + uint8_t i; + + reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription); + headerDesc.backAndText.text = details->title; + nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc); + if (details->type == BAR_LIST_WARNING) { + // if more than one warning warning, so use a list of touchable bars + for (i = 0; i < details->barList.nbBars; i++) { + nbgl_layoutBar_t bar; + bar.text = details->barList.texts[i]; + bar.subText = details->barList.subTexts[i]; + bar.iconRight = &PUSH_ICON; + bar.iconLeft = details->barList.icons[i]; + bar.token = FIRST_WARN_BAR_TOKEN + i; + bar.tuneId = TUNE_TAP_CASUAL; + bar.large = false; + bar.inactive = false; + nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar); + nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout); + } + } + else if (details->type == QRCODE_WARNING) { + // display a QR Code + nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &details->qrCode); + headerDesc.backAndText.text = details->title; + } + else if (details->type == CENTERED_INFO_WARNING) { + // display a centered info + nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &details->centeredInfo); + headerDesc.separationLine = false; + } + nbgl_layoutDraw(reviewWithWarnCtx.modalLayout); + nbgl_refresh(); +} + +// function used to display the initial warning page when starting a "review with warning" +static void displayInitialWarning(void) { // Play notification sound #ifdef HAVE_PIEZO_SOUND io_seproxyhal_play_tune(TUNE_LOOK_AT_ME); #endif // HAVE_PIEZO_SOUND - nbgl_useCaseChoice(&C_Warning_64px, - "Blind signing ahead", - "The details of this transaction or message are not fully verifiable. If " - "you sign it, you could lose all " - "your assets.", - "Back to safety", - "Continue anyway", - blindSigningWarningCallback); + nbgl_layoutDescription_t layoutDescription; + nbgl_layoutChoiceButtons_t buttonsInfo = {.bottomText = "Continue anyway", + .token = WARNING_CHOICE_TOKEN, + .topText = "Back to safety", + .style = ROUNDED_AND_FOOTER_STYLE, + .tuneId = TUNE_TAP_CASUAL}; + nbgl_layoutHeader_t headerDesc = {.type = HEADER_EMPTY, + .separationLine = false, + .emptySpace.height = MEDIUM_CENTERING_HEADER}; + + reviewWithWarnCtx.isIntro = true; + + layoutDescription.modal = false; + layoutDescription.withLeftBorder = true; + + layoutDescription.onActionCallback = layoutTouchCallback; + layoutDescription.tapActionText = NULL; + + layoutDescription.ticker.tickerCallback = NULL; + 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)) + && ((reviewWithWarnCtx.warning->predefinedSet != 0) + || (reviewWithWarnCtx.warning->introTopRightIcon != NULL))) { + if (reviewWithWarnCtx.warning->predefinedSet != 0) { + nbgl_layoutAddTopRightButton( + reviewWithWarnCtx.layoutCtx, &PRIVACY_ICON, WARNING_BUTTON_TOKEN, TUNE_TAP_CASUAL); + } + else { + 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_THREAT_DETECTED_WARN)) { + info.title = "Threat detected"; + info.smallTitle = "Known drainer contract"; + info.description = "This transaction was scanned as malicious by Web3 Checks."; + } + else { + info.title = "Dangerous transaction"; + info.description + = "This transaction cannot be fully decoded, and was not verified by Web3 Checks."; + } + nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, &info); + } + else if (reviewWithWarnCtx.warning->info != NULL) { + // if no predefined content, use custom one + nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, reviewWithWarnCtx.warning->info); + } + + nbgl_layoutDraw(reviewWithWarnCtx.layoutCtx); + nbgl_refresh(); } // function to factorize code for all simple reviews @@ -3042,20 +3373,64 @@ void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationT const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback) { - memset(&blindSigningContext, 0, sizeof(blindSigningContext)); - - blindSigningContext.isStreaming = false; - blindSigningContext.operationType = operationType | BLIND_OPERATION; - blindSigningContext.tagValueList = tagValueList; - blindSigningContext.icon = icon; - blindSigningContext.reviewTitle = reviewTitle; - blindSigningContext.reviewSubTitle = reviewSubTitle; - blindSigningContext.finishTitle = finishTitle; - blindSigningContext.tipBox = tipBox; - blindSigningContext.choiceCallback = choiceCallback; + nbgl_useCaseReviewWithWarning(operationType, + tagValueList, + icon, + reviewTitle, + reviewSubTitle, + finishTitle, + tipBox, + &blindSigningWarning, + choiceCallback); +} - blindSigningWarning(); +/** + * @brief Draws a flow of pages of a review requiring 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 + * + * 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 warning structure to build the initial warning page (cannot be NULL) + * @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) +{ + memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); + reviewWithWarnCtx.isStreaming = false; + reviewWithWarnCtx.operationType = operationType | BLIND_OPERATION; + reviewWithWarnCtx.tagValueList = tagValueList; + reviewWithWarnCtx.icon = icon; + reviewWithWarnCtx.reviewTitle = reviewTitle; + reviewWithWarnCtx.reviewSubTitle = reviewSubTitle; + reviewWithWarnCtx.finishTitle = finishTitle; + reviewWithWarnCtx.tipBox = tipBox; + reviewWithWarnCtx.warning = warning; + reviewWithWarnCtx.choiceCallback = choiceCallback; + + displayInitialWarning(); } + /** * @brief Draws a flow of pages of a light review. Navigation operates with either swipe or * navigation keys at bottom right. The last page contains a button/footer with the given @@ -3169,16 +3544,17 @@ void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t ope const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback) { - memset(&blindSigningContext, 0, sizeof(blindSigningContext)); + memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx)); - blindSigningContext.isStreaming = true; - blindSigningContext.operationType = operationType | BLIND_OPERATION; - blindSigningContext.icon = icon; - blindSigningContext.reviewTitle = reviewTitle; - blindSigningContext.reviewSubTitle = reviewSubTitle; - blindSigningContext.choiceCallback = choiceCallback; + reviewWithWarnCtx.isStreaming = true; + reviewWithWarnCtx.operationType = operationType | BLIND_OPERATION; + reviewWithWarnCtx.icon = icon; + reviewWithWarnCtx.reviewTitle = reviewTitle; + reviewWithWarnCtx.reviewSubTitle = reviewSubTitle; + reviewWithWarnCtx.choiceCallback = choiceCallback; + reviewWithWarnCtx.warning = &blindSigningWarning; - blindSigningWarning(); + displayInitialWarning(); } /**