From 7798a1db88b3d5e28262a1308e0d09b8bb14c739 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 26 Jul 2024 13:28:13 +0200 Subject: [PATCH 1/4] Add "choice" use case to NBGL lib. --- ledger_device_sdk/examples/nbgl_choice.rs | 51 +++++++++++++ ledger_device_sdk/icons/Warning_64px.gif | Bin 0 -> 571 bytes ledger_device_sdk/src/nbgl.rs | 86 ++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 ledger_device_sdk/examples/nbgl_choice.rs create mode 100644 ledger_device_sdk/icons/Warning_64px.gif diff --git a/ledger_device_sdk/examples/nbgl_choice.rs b/ledger_device_sdk/examples/nbgl_choice.rs new file mode 100644 index 00000000..fa33ca0e --- /dev/null +++ b/ledger_device_sdk/examples/nbgl_choice.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +// Force boot section to be embedded in +use ledger_device_sdk as _; + +use include_gif::include_gif; +use ledger_device_sdk::io::*; +use ledger_device_sdk::nbgl::{init_comm, NbglChoice, NbglGlyph}; +use ledger_secure_sdk_sys::*; + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + exit_app(1); +} + +#[no_mangle] +extern "C" fn sample_main() { + unsafe { + nbgl_refreshReset(); + } + + let mut comm = Comm::new(); + // Initialize reference to Comm instance for NBGL + // API calls. + init_comm(&mut comm); + + // Load glyph from 64x64 4bpp gif file with include_gif macro. Creates an NBGL compatible glyph. + const WARNING: NbglGlyph = + NbglGlyph::from_include(include_gif!("icons/Warning_64px.gif", NBGL)); + + let back_to_safety = NbglChoice::new().glyph(&WARNING) + .status_text(Some("Transaction rejected"),None) + .show( + "Security risk detected", + "It may not be safe to sign this transaction. To continue, you'll need to review the risk.", + "Back to safety", + "Review risk", + ); + + if !back_to_safety { + NbglChoice::new() + .status_text(Some("Transaction confirmed"), Some("Transaction rejected")) + .show( + "The transaction cannot be trusted", + "Your Ledger cannot decode this transaction. If you sign it, you could be authorizing malicious actions that can drain your wallet.\n\nLearn more: ledger.com/e8", + "I accept the risk", + "Reject transaction" + ); + } +} diff --git a/ledger_device_sdk/icons/Warning_64px.gif b/ledger_device_sdk/icons/Warning_64px.gif new file mode 100644 index 0000000000000000000000000000000000000000..7eb50308d597698732f056c5fcee7fe4e5bebabe GIT binary patch literal 571 zcmV-B0>u4CNk%w1VL$*t0OJq<0RaI=M@M60V|aLYiHV7qmzSlbrLeHDy1Kf>#l_Fh z&)C@5-{0Tx@bLKf`2PO>A^s6Va%Ew3Wn>_CX>@2HM@dak04x9i002M$KmY&){_x33 ztGzhu&HHR%;l?&#@X zVTl;;@S0iWUPm!oD8dtN=`;7(+uj zj4evZvsY3>CNw-kjwG3412tkUN*& NbglAddressReview<'a> { } } +/// A wrapper around the synchronous NBGL ux_sync_status C API binding. +/// Draws a generic choice page, described in a centered info (with configurable icon), +/// thanks to a button and a footer at the bottom of the page. +pub struct NbglChoice<'a> { + glyph: Option<&'a NbglGlyph<'a>>, + confirmed_text: Option, + cancelled_text: Option, +} + +impl<'a> NbglChoice<'a> { + pub fn new() -> NbglChoice<'a> { + NbglChoice { + glyph: None, + confirmed_text: None, + cancelled_text: None, + } + } + + pub fn glyph(self, glyph: &'a NbglGlyph) -> NbglChoice<'a> { + NbglChoice { + glyph: Some(glyph), + ..self + } + } + + pub fn status_text( + self, + confirmed: Option<&'a str>, + cancelled: Option<&'a str>, + ) -> NbglChoice<'a> { + let confirmed_text = confirmed.map(|s| CString::new(s).unwrap()); + let cancelled_text = cancelled.map(|s| CString::new(s).unwrap()); + NbglChoice { + confirmed_text, + cancelled_text, + ..self + } + } + + pub fn show( + self, + message: &str, + sub_message: &str, + confirm_text: &str, + cancel_text: &str, + ) -> bool { + unsafe { + let icon: nbgl_icon_details_t = match self.glyph { + Some(g) => g.into(), + None => nbgl_icon_details_t::default(), + }; + let message = CString::new(message).unwrap(); + let sub_message = CString::new(sub_message).unwrap(); + let confirm_text = CString::new(confirm_text).unwrap(); + let cancel_text = CString::new(cancel_text).unwrap(); + + let sync_ret = ux_sync_choice( + &icon as *const nbgl_icon_details_t, + message.as_ptr() as *const c_char, + sub_message.as_ptr() as *const c_char, + confirm_text.as_ptr() as *const c_char, + cancel_text.as_ptr() as *const c_char, + ); + + // Return true if the user approved the transaction, false otherwise. + match sync_ret { + ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { + if let Some(text) = self.confirmed_text { + ledger_secure_sdk_sys::ux_sync_status(text.as_ptr() as *const c_char, true); + } + return true; + } + _ => { + if let Some(text) = self.cancelled_text { + ledger_secure_sdk_sys::ux_sync_status( + text.as_ptr() as *const c_char, + false, + ); + } + return false; + } + } + } + } +} + #[derive(Copy, Clone)] pub enum TuneIndex { Reserved, From a1b08fc3f94d3b28521f05d022a2b7d1cb7c89cd Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 26 Jul 2024 13:28:31 +0200 Subject: [PATCH 2/4] Add warning screens when user reviews transaction in blind mode. --- ledger_device_sdk/src/nbgl.rs | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ledger_device_sdk/src/nbgl.rs b/ledger_device_sdk/src/nbgl.rs index 90fd41bd..23f813c5 100644 --- a/ledger_device_sdk/src/nbgl.rs +++ b/ledger_device_sdk/src/nbgl.rs @@ -6,6 +6,7 @@ use alloc::ffi::CString; use alloc::vec::Vec; use core::ffi::{c_char, c_int}; use core::mem::transmute; +use include_gif::include_gif; use ledger_secure_sdk_sys::*; #[no_mangle] @@ -332,6 +333,35 @@ impl<'a> NbglHomeAndSettings<'a> { } } +/// Private helper function to display a warning screen when a transaction +/// is reviewed in "blind" mode. The user can choose to go back to safety +/// or review the risk. If the user chooses to review the risk, a second screen +/// is displayed with the option to accept the risk or reject the transaction. +/// Used in NbglReview and NbglStreamingReview. +fn show_blind_warning() -> bool { + const WARNING: NbglGlyph = + NbglGlyph::from_include(include_gif!("icons/Warning_64px.gif", NBGL)); + + let back_to_safety = NbglChoice::new().glyph(&WARNING).show( + "Security risk detected", + "It may not be safe to sign this transaction. To continue, you'll need to review the risk.", + "Back to safety", + "Review risk", + ); + + if !back_to_safety { + NbglChoice::new() + .show( + "The transaction cannot be trusted", + "Your Ledger cannot decode this transaction. If you sign it, you could be authorizing malicious actions that can drain your wallet.\n\nLearn more: ledger.com/e8", + "I accept the risk", + "Reject transaction" + ) + } else { + false + } +} + /// A wrapper around the synchronous NBGL ux_sync_review C API binding. /// Used to display transaction review screens. pub struct NbglReview<'a> { @@ -420,6 +450,13 @@ impl<'a> NbglReview<'a> { None => nbgl_icon_details_t::default(), }; + if self.blind { + if !show_blind_warning() { + ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(false)); + return false; + } + } + // Show the review on the device. let sync_ret = ledger_secure_sdk_sys::ux_sync_review( self.tx_type.to_c_type(self.blind, false), @@ -929,6 +966,13 @@ impl NbglStreamingReview { let title = CString::new(title).unwrap(); let subtitle = CString::new(subtitle).unwrap(); + if self.blind { + if !show_blind_warning() { + ux_sync_reviewStatus(self.tx_type.to_message(false)); + return false; + } + } + let sync_ret = ux_sync_reviewStreamingStart( self.tx_type.to_c_type(self.blind, false), &self.icon as *const nbgl_icon_details_t, From fba8016cefeeaadf89f9edb630575a5ce53c3019 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 26 Jul 2024 13:31:01 +0200 Subject: [PATCH 3/4] Code cleanup : remove useless ledger_secure_sdk_sys:: mentions --- ledger_device_sdk/src/nbgl.rs | 47 ++++++++++++++--------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/ledger_device_sdk/src/nbgl.rs b/ledger_device_sdk/src/nbgl.rs index 23f813c5..0fb99537 100644 --- a/ledger_device_sdk/src/nbgl.rs +++ b/ledger_device_sdk/src/nbgl.rs @@ -308,7 +308,7 @@ impl<'a> NbglHomeAndSettings<'a> { nbContents: if self.nb_settings > 0 { 1 } else { 0 }, }; - match ledger_secure_sdk_sys::ux_sync_homeAndSettings( + match ux_sync_homeAndSettings( info_contents[0], &icon as *const nbgl_icon_details_t, core::ptr::null(), @@ -317,7 +317,7 @@ impl<'a> NbglHomeAndSettings<'a> { &info_list as *const nbgl_contentInfoList_t, core::ptr::null(), ) { - ledger_secure_sdk_sys::UX_SYNC_RET_APDU_RECEIVED => { + UX_SYNC_RET_APDU_RECEIVED => { if let Some(comm) = COMM_REF.as_mut() { if let Some(value) = comm.check_event() { return value; @@ -452,13 +452,13 @@ impl<'a> NbglReview<'a> { if self.blind { if !show_blind_warning() { - ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(false)); + ux_sync_reviewStatus(self.tx_type.to_message(false)); return false; } } // Show the review on the device. - let sync_ret = ledger_secure_sdk_sys::ux_sync_review( + let sync_ret = ux_sync_review( self.tx_type.to_c_type(self.blind, false), &tag_value_list as *const nbgl_contentTagValueList_t, &icon as *const nbgl_icon_details_t, @@ -469,12 +469,12 @@ impl<'a> NbglReview<'a> { // Return true if the user approved the transaction, false otherwise. match sync_ret { - ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { - ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(true)); + UX_SYNC_RET_APPROVED => { + ux_sync_reviewStatus(self.tx_type.to_message(true)); return true; } _ => { - ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(false)); + ux_sync_reviewStatus(self.tx_type.to_message(false)); return false; } } @@ -907,18 +907,12 @@ impl NbglGenericReview { // Return true if the user approved the transaction, false otherwise. match sync_ret { - ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { - ledger_secure_sdk_sys::ux_sync_status( - succeed_cstring.as_ptr() as *const c_char, - true, - ); + UX_SYNC_RET_APPROVED => { + ux_sync_status(succeed_cstring.as_ptr() as *const c_char, true); return true; } _ => { - ledger_secure_sdk_sys::ux_sync_status( - rejected_cstring.as_ptr() as *const c_char, - false, - ); + ux_sync_status(rejected_cstring.as_ptr() as *const c_char, false); return false; } } @@ -1045,7 +1039,7 @@ impl NbglStreamingReview { // Return true if the user approved the transaction, false otherwise. match sync_ret { - ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { + UX_SYNC_RET_APPROVED => { ux_sync_reviewStatus(self.tx_type.to_message(true)); return true; } @@ -1107,12 +1101,12 @@ impl<'a> NbglAddressReview<'a> { // Return true if the user approved the address, false otherwise. match sync_ret { - ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { - ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_VERIFIED); + UX_SYNC_RET_APPROVED => { + ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_VERIFIED); return true; } - ledger_secure_sdk_sys::UX_SYNC_RET_REJECTED => { - ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_REJECTED); + UX_SYNC_RET_REJECTED => { + ux_sync_reviewStatus(STATUS_TYPE_ADDRESS_REJECTED); return false; } _ => { @@ -1123,7 +1117,7 @@ impl<'a> NbglAddressReview<'a> { } } -/// A wrapper around the synchronous NBGL ux_sync_status C API binding. +/// A wrapper around the synchronous NBGL ux_sync_status C API binding. /// Draws a generic choice page, described in a centered info (with configurable icon), /// thanks to a button and a footer at the bottom of the page. pub struct NbglChoice<'a> { @@ -1189,18 +1183,15 @@ impl<'a> NbglChoice<'a> { // Return true if the user approved the transaction, false otherwise. match sync_ret { - ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => { + UX_SYNC_RET_APPROVED => { if let Some(text) = self.confirmed_text { - ledger_secure_sdk_sys::ux_sync_status(text.as_ptr() as *const c_char, true); + ux_sync_status(text.as_ptr() as *const c_char, true); } return true; } _ => { if let Some(text) = self.cancelled_text { - ledger_secure_sdk_sys::ux_sync_status( - text.as_ptr() as *const c_char, - false, - ); + ux_sync_status(text.as_ptr() as *const c_char, false); } return false; } From 83cd9d9a5ce1b113416c3c50756580d10444b425 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 26 Jul 2024 16:17:23 +0200 Subject: [PATCH 4/4] Bump SDK version. --- ledger_device_sdk/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ledger_device_sdk/Cargo.toml b/ledger_device_sdk/Cargo.toml index bfa023ab..769c3969 100644 --- a/ledger_device_sdk/Cargo.toml +++ b/ledger_device_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_device_sdk" -version = "1.12.0" +version = "1.13.0" authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"] edition = "2021" license.workspace = true