Skip to content

Commit 74a8e35

Browse files
Merge pull request #760 from LedgerHQ/blind-signing-review-use-case
Blind signing review use cases
2 parents 8bbf8f3 + 170026d commit 74a8e35

File tree

2 files changed

+243
-44
lines changed

2 files changed

+243
-44
lines changed

lib_nbgl/include/nbgl_use_case.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,15 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType,
239239
const char *finishTitle,
240240
nbgl_choiceCallback_t choiceCallback);
241241

242+
void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationType,
243+
const nbgl_contentTagValueList_t *tagValueList,
244+
const nbgl_icon_details_t *icon,
245+
const char *reviewTitle,
246+
const char *reviewSubTitle,
247+
const char *finishTitle,
248+
const nbgl_tipBox_t *tipBox,
249+
nbgl_choiceCallback_t choiceCallback);
250+
242251
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType,
243252
const nbgl_contentTagValueList_t *tagValueList,
244253
const nbgl_icon_details_t *icon,
@@ -272,6 +281,12 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType,
272281
const char *reviewSubTitle,
273282
nbgl_choiceCallback_t choiceCallback);
274283

284+
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType,
285+
const nbgl_icon_details_t *icon,
286+
const char *reviewTitle,
287+
const char *reviewSubTitle,
288+
nbgl_choiceCallback_t choiceCallback);
289+
275290
void nbgl_useCaseReviewStreamingContinueExt(const nbgl_contentTagValueList_t *tagValueList,
276291
nbgl_choiceCallback_t choiceCallback,
277292
nbgl_callback_t skipCallback);

lib_nbgl/src/nbgl_use_case.c

Lines changed: 228 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
/* max number of char for reduced QR Code address */
4848
#define QRCODE_REDUCED_ADDR_LEN 128
4949

50+
// macros to ease access to shared contexts
51+
#define keypadContext sharedContext.keypad
52+
#define addressConfirmationContext sharedContext.addressConfirmation
53+
#define blindSigningContext sharedContext.blindSigning
54+
5055
/**********************
5156
* TYPEDEFS
5257
**********************/
@@ -100,6 +105,29 @@ typedef struct KeypadContext_s {
100105
} KeypadContext_t;
101106
#endif
102107

108+
typedef struct BlindSigningContext_s {
109+
bool isStreaming;
110+
nbgl_operationType_t operationType;
111+
const nbgl_contentTagValueList_t *tagValueList;
112+
const nbgl_icon_details_t *icon;
113+
const char *reviewTitle;
114+
const char *reviewSubTitle;
115+
const char *finishTitle;
116+
const nbgl_tipBox_t *tipBox;
117+
nbgl_choiceCallback_t choiceCallback;
118+
nbgl_layout_t *layoutCtx;
119+
} BlindSigningContext_t;
120+
121+
// this union is intended to save RAM for context storage
122+
// indeed, these three contexts cannot happen simultaneously
123+
typedef union {
124+
#ifdef NBGL_KEYPAD
125+
KeypadContext_t keypad;
126+
#endif
127+
AddressConfirmationContext_t addressConfirmation;
128+
BlindSigningContext_t blindSigning;
129+
} SharedContext_t;
130+
103131
typedef struct {
104132
nbgl_genericContents_t genericContents;
105133
int8_t currentContentIdx;
@@ -187,11 +215,8 @@ static NavType_t navType;
187215

188216
static DetailsContext_t detailsContext;
189217

190-
static AddressConfirmationContext_t addressConfirmationContext;
191-
192-
#ifdef NBGL_KEYPAD
193-
static KeypadContext_t keypadContext;
194-
#endif
218+
// multi-purpose context shared for non-concurrent usages
219+
static SharedContext_t sharedContext;
195220

196221
// contexts for generic navigation
197222
static GenericContext_t genericContext;
@@ -245,6 +270,23 @@ static void bundleNavStartSettingsAtPage(uint8_t initSettingPage);
245270
static void bundleNavStartSettings(void);
246271

247272
static void bundleNavReviewStreamingChoice(bool confirm);
273+
static void blindSigningWarning(void);
274+
static void useCaseReview(nbgl_operationType_t operationType,
275+
const nbgl_contentTagValueList_t *tagValueList,
276+
const nbgl_icon_details_t *icon,
277+
const char *reviewTitle,
278+
const char *reviewSubTitle,
279+
const char *finishTitle,
280+
const nbgl_tipBox_t *tipBox,
281+
nbgl_choiceCallback_t choiceCallback,
282+
bool isLight,
283+
bool playNotifSound);
284+
static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
285+
const nbgl_icon_details_t *icon,
286+
const char *reviewTitle,
287+
const char *reviewSubTitle,
288+
nbgl_choiceCallback_t choiceCallback,
289+
bool playNotifSound);
248290

249291
static void reset_callbacks(void)
250292
{
@@ -914,7 +956,8 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
914956
}
915957

916958
bool isFirstOrLastPage
917-
= (p_content->type == CENTERED_INFO) || (p_content->type == INFO_LONG_PRESS);
959+
= ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
960+
|| (p_content->type == INFO_LONG_PRESS);
918961
bool isStreamingNavAndBlindOperation
919962
= (navType == STREAMING_NAV)
920963
&& (bundleNavContext.reviewStreaming.operationType & BLIND_OPERATION);
@@ -1646,6 +1689,54 @@ static void bundleNavReviewStreamingChoice(bool confirm)
16461689
}
16471690
}
16481691

1692+
// function called when the warning page of Blind Signing review buttons are pressed
1693+
static void blindSigningWarningCallback(bool confirm)
1694+
{
1695+
if (confirm) { // top button to exit
1696+
blindSigningContext.choiceCallback(false);
1697+
}
1698+
else { // bottom button to continue to review
1699+
if (blindSigningContext.isStreaming) {
1700+
useCaseReviewStreamingStart(blindSigningContext.operationType,
1701+
blindSigningContext.icon,
1702+
blindSigningContext.reviewTitle,
1703+
blindSigningContext.reviewSubTitle,
1704+
blindSigningContext.choiceCallback,
1705+
false);
1706+
}
1707+
else {
1708+
useCaseReview(blindSigningContext.operationType,
1709+
blindSigningContext.tagValueList,
1710+
blindSigningContext.icon,
1711+
blindSigningContext.reviewTitle,
1712+
blindSigningContext.reviewSubTitle,
1713+
blindSigningContext.finishTitle,
1714+
blindSigningContext.tipBox,
1715+
blindSigningContext.choiceCallback,
1716+
false,
1717+
false);
1718+
}
1719+
}
1720+
}
1721+
1722+
// function used to display the warning page when starting a Bling Signing review
1723+
static void blindSigningWarning(void)
1724+
{
1725+
// Play notification sound
1726+
#ifdef HAVE_PIEZO_SOUND
1727+
io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1728+
#endif // HAVE_PIEZO_SOUND
1729+
nbgl_useCaseChoice(
1730+
&C_Warning_64px,
1731+
"Blind signing ahead",
1732+
"This transaction's details are not fully verifiable. If you sign it, you could lose all "
1733+
"your assets.",
1734+
"Back to safety",
1735+
"Continue anyway",
1736+
blindSigningWarningCallback);
1737+
}
1738+
1739+
// function to factorize code for all simple reviews
16491740
static void useCaseReview(nbgl_operationType_t operationType,
16501741
const nbgl_contentTagValueList_t *tagValueList,
16511742
const nbgl_icon_details_t *icon,
@@ -1654,7 +1745,8 @@ static void useCaseReview(nbgl_operationType_t operationType,
16541745
const char *finishTitle,
16551746
const nbgl_tipBox_t *tipBox,
16561747
nbgl_choiceCallback_t choiceCallback,
1657-
bool isLight)
1748+
bool isLight,
1749+
bool playNotifSound)
16581750
{
16591751
reset_callbacks();
16601752
memset(&genericContext, 0, sizeof(genericContext));
@@ -1708,10 +1800,58 @@ static void useCaseReview(nbgl_operationType_t operationType,
17081800
uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
17091801
prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
17101802

1803+
// Play notification sound if required
1804+
if (playNotifSound) {
17111805
#ifdef HAVE_PIEZO_SOUND
1712-
// Play notification sound
1713-
io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1806+
io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1807+
#endif // HAVE_PIEZO_SOUND
1808+
}
1809+
1810+
displayGenericContextPage(0, true);
1811+
}
1812+
1813+
// function to factorize code for all streaming reviews
1814+
static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
1815+
const nbgl_icon_details_t *icon,
1816+
const char *reviewTitle,
1817+
const char *reviewSubTitle,
1818+
nbgl_choiceCallback_t choiceCallback,
1819+
bool playNotifSound)
1820+
{
1821+
reset_callbacks();
1822+
memset(&genericContext, 0, sizeof(genericContext));
1823+
1824+
bundleNavContext.reviewStreaming.operationType = operationType;
1825+
bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
1826+
bundleNavContext.reviewStreaming.icon = icon;
1827+
1828+
// memorize context
1829+
onChoice = bundleNavReviewStreamingChoice;
1830+
navType = STREAMING_NAV;
1831+
pageTitle = NULL;
1832+
1833+
genericContext.genericContents.contentsList = localContentsList;
1834+
genericContext.genericContents.nbContents = 1;
1835+
memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
1836+
1837+
// First a centered info
1838+
STARTING_CONTENT.type = EXTENDED_CENTER;
1839+
prepareReviewFirstPage(
1840+
&STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1841+
1842+
// compute number of pages & fill navigation structure
1843+
bundleNavContext.reviewStreaming.stepPageNb
1844+
= nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
1845+
prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
1846+
// no back button on first page
1847+
navInfo.navWithButtons.backButton = false;
1848+
1849+
// Play notification sound if required
1850+
if (playNotifSound) {
1851+
#ifdef HAVE_PIEZO_SOUND
1852+
io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
17141853
#endif // HAVE_PIEZO_SOUND
1854+
}
17151855

17161856
displayGenericContextPage(0, true);
17171857
}
@@ -2833,7 +2973,8 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType,
28332973
finishTitle,
28342974
NULL,
28352975
choiceCallback,
2836-
false);
2976+
false,
2977+
true);
28372978
}
28382979

28392980
/**
@@ -2870,9 +3011,52 @@ void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType,
28703011
finishTitle,
28713012
tipBox,
28723013
choiceCallback,
2873-
false);
3014+
false,
3015+
true);
28743016
}
28753017

3018+
/**
3019+
* @brief Draws a flow of pages of a blind-signing review. The review is preceded by a warning page
3020+
*
3021+
* Navigation operates with either swipe or navigation
3022+
* keys at bottom right. The last page contains a long-press button with the given finishTitle and
3023+
* the given icon.
3024+
* @note All tag/value pairs are provided in the API and the number of pages is automatically
3025+
* computed, the last page being a long press one
3026+
*
3027+
* @param operationType type of operation (Operation, Transaction, Message)
3028+
* @param tagValueList list of tag/value pairs
3029+
* @param icon icon used on first and last review page
3030+
* @param reviewTitle string used in the first review page
3031+
* @param reviewSubTitle string to set under reviewTitle (can be NULL)
3032+
* @param finishTitle string used in the last review page
3033+
* @param tipBox parameter to build a tip-box and necessary modal (can be NULL)
3034+
* @param choiceCallback callback called when operation is accepted (param is true) or rejected
3035+
* (param is false)
3036+
*/
3037+
void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationType,
3038+
const nbgl_contentTagValueList_t *tagValueList,
3039+
const nbgl_icon_details_t *icon,
3040+
const char *reviewTitle,
3041+
const char *reviewSubTitle,
3042+
const char *finishTitle,
3043+
const nbgl_tipBox_t *tipBox,
3044+
nbgl_choiceCallback_t choiceCallback)
3045+
{
3046+
memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3047+
3048+
blindSigningContext.isStreaming = false;
3049+
blindSigningContext.operationType = operationType | BLIND_OPERATION;
3050+
blindSigningContext.tagValueList = tagValueList;
3051+
blindSigningContext.icon = icon;
3052+
blindSigningContext.reviewTitle = reviewTitle;
3053+
blindSigningContext.reviewSubTitle = reviewSubTitle;
3054+
blindSigningContext.finishTitle = finishTitle;
3055+
blindSigningContext.tipBox = tipBox;
3056+
blindSigningContext.choiceCallback = choiceCallback;
3057+
3058+
blindSigningWarning();
3059+
}
28763060
/**
28773061
* @brief Draws a flow of pages of a light review. Navigation operates with either swipe or
28783062
* navigation keys at bottom right. The last page contains a button/footer with the given
@@ -2905,6 +3089,7 @@ void nbgl_useCaseReviewLight(nbgl_operationType_t operationType,
29053089
finishTitle,
29063090
NULL,
29073091
choiceCallback,
3092+
true,
29083093
true);
29093094
}
29103095

@@ -2962,40 +3147,39 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType,
29623147
const char *reviewSubTitle,
29633148
nbgl_choiceCallback_t choiceCallback)
29643149
{
2965-
reset_callbacks();
2966-
memset(&genericContext, 0, sizeof(genericContext));
2967-
2968-
bundleNavContext.reviewStreaming.operationType = operationType;
2969-
bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
2970-
bundleNavContext.reviewStreaming.icon = icon;
2971-
2972-
// memorize context
2973-
onChoice = bundleNavReviewStreamingChoice;
2974-
navType = STREAMING_NAV;
2975-
pageTitle = NULL;
2976-
2977-
genericContext.genericContents.contentsList = localContentsList;
2978-
genericContext.genericContents.nbContents = 1;
2979-
memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
2980-
2981-
// First a centered info
2982-
STARTING_CONTENT.type = EXTENDED_CENTER;
2983-
prepareReviewFirstPage(
2984-
&STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2985-
2986-
// compute number of pages & fill navigation structure
2987-
bundleNavContext.reviewStreaming.stepPageNb
2988-
= nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
2989-
prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
2990-
// no back button on first page
2991-
navInfo.navWithButtons.backButton = false;
2992-
2993-
#ifdef HAVE_PIEZO_SOUND
2994-
// Play notification sound
2995-
io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2996-
#endif // HAVE_PIEZO_SOUND
3150+
useCaseReviewStreamingStart(
3151+
operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3152+
}
29973153

2998-
displayGenericContextPage(0, true);
3154+
/**
3155+
* @brief Start drawing the flow of pages of a blind-signing review. The review is preceded by a
3156+
* warning page
3157+
* @note This should be followed by calls to nbgl_useCaseReviewStreamingContinue and finally to
3158+
* nbgl_useCaseReviewStreamingFinish.
3159+
*
3160+
* @param operationType type of operation (Operation, Transaction, Message)
3161+
* @param icon icon used on first and last review page
3162+
* @param reviewTitle string used in the first review page
3163+
* @param reviewSubTitle string to set under reviewTitle (can be NULL)
3164+
* @param choiceCallback callback called when more operation data are needed (param is true) or
3165+
* operation is rejected (param is false)
3166+
*/
3167+
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType,
3168+
const nbgl_icon_details_t *icon,
3169+
const char *reviewTitle,
3170+
const char *reviewSubTitle,
3171+
nbgl_choiceCallback_t choiceCallback)
3172+
{
3173+
memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3174+
3175+
blindSigningContext.isStreaming = true;
3176+
blindSigningContext.operationType = operationType | BLIND_OPERATION;
3177+
blindSigningContext.icon = icon;
3178+
blindSigningContext.reviewTitle = reviewTitle;
3179+
blindSigningContext.reviewSubTitle = reviewSubTitle;
3180+
blindSigningContext.choiceCallback = choiceCallback;
3181+
3182+
blindSigningWarning();
29993183
}
30003184

30013185
/**

0 commit comments

Comments
 (0)