@@ -6,6 +6,7 @@ use alloc::ffi::CString;
6
6
use alloc:: vec:: Vec ;
7
7
use core:: ffi:: { c_char, c_int} ;
8
8
use core:: mem:: transmute;
9
+ use include_gif:: include_gif;
9
10
use ledger_secure_sdk_sys:: * ;
10
11
11
12
#[ no_mangle]
@@ -307,7 +308,7 @@ impl<'a> NbglHomeAndSettings<'a> {
307
308
nbContents : if self . nb_settings > 0 { 1 } else { 0 } ,
308
309
} ;
309
310
310
- match ledger_secure_sdk_sys :: ux_sync_homeAndSettings (
311
+ match ux_sync_homeAndSettings (
311
312
info_contents[ 0 ] ,
312
313
& icon as * const nbgl_icon_details_t ,
313
314
core:: ptr:: null ( ) ,
@@ -316,7 +317,7 @@ impl<'a> NbglHomeAndSettings<'a> {
316
317
& info_list as * const nbgl_contentInfoList_t ,
317
318
core:: ptr:: null ( ) ,
318
319
) {
319
- ledger_secure_sdk_sys :: UX_SYNC_RET_APDU_RECEIVED => {
320
+ UX_SYNC_RET_APDU_RECEIVED => {
320
321
if let Some ( comm) = COMM_REF . as_mut ( ) {
321
322
if let Some ( value) = comm. check_event ( ) {
322
323
return value;
@@ -332,6 +333,35 @@ impl<'a> NbglHomeAndSettings<'a> {
332
333
}
333
334
}
334
335
336
+ /// Private helper function to display a warning screen when a transaction
337
+ /// is reviewed in "blind" mode. The user can choose to go back to safety
338
+ /// or review the risk. If the user chooses to review the risk, a second screen
339
+ /// is displayed with the option to accept the risk or reject the transaction.
340
+ /// Used in NbglReview and NbglStreamingReview.
341
+ fn show_blind_warning ( ) -> bool {
342
+ const WARNING : NbglGlyph =
343
+ NbglGlyph :: from_include ( include_gif ! ( "icons/Warning_64px.gif" , NBGL ) ) ;
344
+
345
+ let back_to_safety = NbglChoice :: new ( ) . glyph ( & WARNING ) . show (
346
+ "Security risk detected" ,
347
+ "It may not be safe to sign this transaction. To continue, you'll need to review the risk." ,
348
+ "Back to safety" ,
349
+ "Review risk" ,
350
+ ) ;
351
+
352
+ if !back_to_safety {
353
+ NbglChoice :: new ( )
354
+ . show (
355
+ "The transaction cannot be trusted" ,
356
+ "Your Ledger cannot decode this transaction. If you sign it, you could be authorizing malicious actions that can drain your wallet.\n \n Learn more: ledger.com/e8" ,
357
+ "I accept the risk" ,
358
+ "Reject transaction"
359
+ )
360
+ } else {
361
+ false
362
+ }
363
+ }
364
+
335
365
/// A wrapper around the synchronous NBGL ux_sync_review C API binding.
336
366
/// Used to display transaction review screens.
337
367
pub struct NbglReview < ' a > {
@@ -420,8 +450,15 @@ impl<'a> NbglReview<'a> {
420
450
None => nbgl_icon_details_t:: default ( ) ,
421
451
} ;
422
452
453
+ if self . blind {
454
+ if !show_blind_warning ( ) {
455
+ ux_sync_reviewStatus ( self . tx_type . to_message ( false ) ) ;
456
+ return false ;
457
+ }
458
+ }
459
+
423
460
// Show the review on the device.
424
- let sync_ret = ledger_secure_sdk_sys :: ux_sync_review (
461
+ let sync_ret = ux_sync_review (
425
462
self . tx_type . to_c_type ( self . blind , false ) ,
426
463
& tag_value_list as * const nbgl_contentTagValueList_t ,
427
464
& icon as * const nbgl_icon_details_t ,
@@ -432,12 +469,12 @@ impl<'a> NbglReview<'a> {
432
469
433
470
// Return true if the user approved the transaction, false otherwise.
434
471
match sync_ret {
435
- ledger_secure_sdk_sys :: UX_SYNC_RET_APPROVED => {
436
- ledger_secure_sdk_sys :: ux_sync_reviewStatus ( self . tx_type . to_message ( true ) ) ;
472
+ UX_SYNC_RET_APPROVED => {
473
+ ux_sync_reviewStatus ( self . tx_type . to_message ( true ) ) ;
437
474
return true ;
438
475
}
439
476
_ => {
440
- ledger_secure_sdk_sys :: ux_sync_reviewStatus ( self . tx_type . to_message ( false ) ) ;
477
+ ux_sync_reviewStatus ( self . tx_type . to_message ( false ) ) ;
441
478
return false ;
442
479
}
443
480
}
@@ -870,18 +907,12 @@ impl NbglGenericReview {
870
907
871
908
// Return true if the user approved the transaction, false otherwise.
872
909
match sync_ret {
873
- ledger_secure_sdk_sys:: UX_SYNC_RET_APPROVED => {
874
- ledger_secure_sdk_sys:: ux_sync_status (
875
- succeed_cstring. as_ptr ( ) as * const c_char ,
876
- true ,
877
- ) ;
910
+ UX_SYNC_RET_APPROVED => {
911
+ ux_sync_status ( succeed_cstring. as_ptr ( ) as * const c_char , true ) ;
878
912
return true ;
879
913
}
880
914
_ => {
881
- ledger_secure_sdk_sys:: ux_sync_status (
882
- rejected_cstring. as_ptr ( ) as * const c_char ,
883
- false ,
884
- ) ;
915
+ ux_sync_status ( rejected_cstring. as_ptr ( ) as * const c_char , false ) ;
885
916
return false ;
886
917
}
887
918
}
@@ -929,6 +960,13 @@ impl NbglStreamingReview {
929
960
let title = CString :: new ( title) . unwrap ( ) ;
930
961
let subtitle = CString :: new ( subtitle) . unwrap ( ) ;
931
962
963
+ if self . blind {
964
+ if !show_blind_warning ( ) {
965
+ ux_sync_reviewStatus ( self . tx_type . to_message ( false ) ) ;
966
+ return false ;
967
+ }
968
+ }
969
+
932
970
let sync_ret = ux_sync_reviewStreamingStart (
933
971
self . tx_type . to_c_type ( self . blind , false ) ,
934
972
& self . icon as * const nbgl_icon_details_t ,
@@ -1001,7 +1039,7 @@ impl NbglStreamingReview {
1001
1039
1002
1040
// Return true if the user approved the transaction, false otherwise.
1003
1041
match sync_ret {
1004
- ledger_secure_sdk_sys :: UX_SYNC_RET_APPROVED => {
1042
+ UX_SYNC_RET_APPROVED => {
1005
1043
ux_sync_reviewStatus ( self . tx_type . to_message ( true ) ) ;
1006
1044
return true ;
1007
1045
}
@@ -1063,12 +1101,12 @@ impl<'a> NbglAddressReview<'a> {
1063
1101
1064
1102
// Return true if the user approved the address, false otherwise.
1065
1103
match sync_ret {
1066
- ledger_secure_sdk_sys :: UX_SYNC_RET_APPROVED => {
1067
- ledger_secure_sdk_sys :: ux_sync_reviewStatus ( STATUS_TYPE_ADDRESS_VERIFIED ) ;
1104
+ UX_SYNC_RET_APPROVED => {
1105
+ ux_sync_reviewStatus ( STATUS_TYPE_ADDRESS_VERIFIED ) ;
1068
1106
return true ;
1069
1107
}
1070
- ledger_secure_sdk_sys :: UX_SYNC_RET_REJECTED => {
1071
- ledger_secure_sdk_sys :: ux_sync_reviewStatus ( STATUS_TYPE_ADDRESS_REJECTED ) ;
1108
+ UX_SYNC_RET_REJECTED => {
1109
+ ux_sync_reviewStatus ( STATUS_TYPE_ADDRESS_REJECTED ) ;
1072
1110
return false ;
1073
1111
}
1074
1112
_ => {
@@ -1079,6 +1117,89 @@ impl<'a> NbglAddressReview<'a> {
1079
1117
}
1080
1118
}
1081
1119
1120
+ /// A wrapper around the synchronous NBGL ux_sync_status C API binding.
1121
+ /// Draws a generic choice page, described in a centered info (with configurable icon),
1122
+ /// thanks to a button and a footer at the bottom of the page.
1123
+ pub struct NbglChoice < ' a > {
1124
+ glyph : Option < & ' a NbglGlyph < ' a > > ,
1125
+ confirmed_text : Option < CString > ,
1126
+ cancelled_text : Option < CString > ,
1127
+ }
1128
+
1129
+ impl < ' a > NbglChoice < ' a > {
1130
+ pub fn new ( ) -> NbglChoice < ' a > {
1131
+ NbglChoice {
1132
+ glyph : None ,
1133
+ confirmed_text : None ,
1134
+ cancelled_text : None ,
1135
+ }
1136
+ }
1137
+
1138
+ pub fn glyph ( self , glyph : & ' a NbglGlyph ) -> NbglChoice < ' a > {
1139
+ NbglChoice {
1140
+ glyph : Some ( glyph) ,
1141
+ ..self
1142
+ }
1143
+ }
1144
+
1145
+ pub fn status_text (
1146
+ self ,
1147
+ confirmed : Option < & ' a str > ,
1148
+ cancelled : Option < & ' a str > ,
1149
+ ) -> NbglChoice < ' a > {
1150
+ let confirmed_text = confirmed. map ( |s| CString :: new ( s) . unwrap ( ) ) ;
1151
+ let cancelled_text = cancelled. map ( |s| CString :: new ( s) . unwrap ( ) ) ;
1152
+ NbglChoice {
1153
+ confirmed_text,
1154
+ cancelled_text,
1155
+ ..self
1156
+ }
1157
+ }
1158
+
1159
+ pub fn show (
1160
+ self ,
1161
+ message : & str ,
1162
+ sub_message : & str ,
1163
+ confirm_text : & str ,
1164
+ cancel_text : & str ,
1165
+ ) -> bool {
1166
+ unsafe {
1167
+ let icon: nbgl_icon_details_t = match self . glyph {
1168
+ Some ( g) => g. into ( ) ,
1169
+ None => nbgl_icon_details_t:: default ( ) ,
1170
+ } ;
1171
+ let message = CString :: new ( message) . unwrap ( ) ;
1172
+ let sub_message = CString :: new ( sub_message) . unwrap ( ) ;
1173
+ let confirm_text = CString :: new ( confirm_text) . unwrap ( ) ;
1174
+ let cancel_text = CString :: new ( cancel_text) . unwrap ( ) ;
1175
+
1176
+ let sync_ret = ux_sync_choice (
1177
+ & icon as * const nbgl_icon_details_t ,
1178
+ message. as_ptr ( ) as * const c_char ,
1179
+ sub_message. as_ptr ( ) as * const c_char ,
1180
+ confirm_text. as_ptr ( ) as * const c_char ,
1181
+ cancel_text. as_ptr ( ) as * const c_char ,
1182
+ ) ;
1183
+
1184
+ // Return true if the user approved the transaction, false otherwise.
1185
+ match sync_ret {
1186
+ UX_SYNC_RET_APPROVED => {
1187
+ if let Some ( text) = self . confirmed_text {
1188
+ ux_sync_status ( text. as_ptr ( ) as * const c_char , true ) ;
1189
+ }
1190
+ return true ;
1191
+ }
1192
+ _ => {
1193
+ if let Some ( text) = self . cancelled_text {
1194
+ ux_sync_status ( text. as_ptr ( ) as * const c_char , false ) ;
1195
+ }
1196
+ return false ;
1197
+ }
1198
+ }
1199
+ }
1200
+ }
1201
+ }
1202
+
1082
1203
#[ derive( Copy , Clone ) ]
1083
1204
pub enum TuneIndex {
1084
1205
Reserved ,
0 commit comments