From b06e6f229755abff3f5da38a32447a1e341cd402 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Mon, 8 Jul 2024 16:21:55 +0200 Subject: [PATCH] Add streaming review support in NBGL module --- ledger_device_sdk/Cargo.toml | 2 +- .../examples/nbgl_streaming_review.rs | 66 +++++++ ledger_device_sdk/src/nbgl.rs | 170 +++++++++++++++++- 3 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 ledger_device_sdk/examples/nbgl_streaming_review.rs diff --git a/ledger_device_sdk/Cargo.toml b/ledger_device_sdk/Cargo.toml index f2db4fdd..34528aa3 100644 --- a/ledger_device_sdk/Cargo.toml +++ b/ledger_device_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_device_sdk" -version = "1.11.1" +version = "1.12.0" authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"] edition = "2021" license.workspace = true diff --git a/ledger_device_sdk/examples/nbgl_streaming_review.rs b/ledger_device_sdk/examples/nbgl_streaming_review.rs new file mode 100644 index 00000000..1ccbe166 --- /dev/null +++ b/ledger_device_sdk/examples/nbgl_streaming_review.rs @@ -0,0 +1,66 @@ +#![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, Field, NbglGlyph, NbglStreamingReview, TransactionType}; +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 FERRIS: NbglGlyph = + NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); + + let mut review: NbglStreamingReview = NbglStreamingReview::new() + .glyph(&FERRIS) + .tx_type(TransactionType::Message); + + review.start("Example Title", "Example Subtitle"); + + let fields = [ + Field { + name: "Name1", + value: "Value1", + }, + Field { + name: "Name2", + value: "Value2", + }, + Field { + name: "Name3", + value: "Value3", + }, + Field { + name: "Name4", + value: "Value4", + }, + Field { + name: "Name5", + value: "Value5", + }, + ]; + + for i in 0..fields.len() { + review.continue_review(&fields[i..i + 1]); + } + + review.finish("Sign to send token\n"); +} diff --git a/ledger_device_sdk/src/nbgl.rs b/ledger_device_sdk/src/nbgl.rs index 00f2a919..e5f323a5 100644 --- a/ledger_device_sdk/src/nbgl.rs +++ b/ledger_device_sdk/src/nbgl.rs @@ -80,6 +80,50 @@ impl<'a> Into for &NbglGlyph<'a> { } } +pub enum TransactionType { + Transaction, + Message, + Operation, +} + +impl From<&TransactionType> for nbgl_operationType_t { + fn from(t: &TransactionType) -> nbgl_operationType_t { + match t { + TransactionType::Transaction => TYPE_TRANSACTION.into(), + TransactionType::Message => TYPE_MESSAGE.into(), + TransactionType::Operation => TYPE_OPERATION.into(), + } + } +} + +impl TransactionType { + pub fn to_message(&self, success: bool) -> nbgl_reviewStatusType_t { + match self { + TransactionType::Transaction => { + if success { + STATUS_TYPE_TRANSACTION_SIGNED + } else { + STATUS_TYPE_TRANSACTION_REJECTED + } + } + TransactionType::Message => { + if success { + STATUS_TYPE_MESSAGE_SIGNED + } else { + STATUS_TYPE_MESSAGE_REJECTED + } + } + TransactionType::Operation => { + if success { + STATUS_TYPE_OPERATION_SIGNED + } else { + STATUS_TYPE_OPERATION_REJECTED + } + } + } + } +} + /// Initialize the global COMM_REF variable with the provided Comm instance. /// This function should be called from the main function of the application. /// The COMM_REF variable is used by the NBGL API to detect touch events and @@ -290,6 +334,7 @@ pub struct NbglReview<'a> { subtitle: CString, finish_title: CString, glyph: Option<&'a NbglGlyph<'a>>, + tx_type: TransactionType, } impl<'a> NbglReview<'a> { @@ -299,9 +344,14 @@ impl<'a> NbglReview<'a> { subtitle: CString::new("").unwrap(), finish_title: CString::new("").unwrap(), glyph: None, + tx_type: TransactionType::Transaction, } } + pub fn tx_type(self, tx_type: TransactionType) -> NbglReview<'a> { + NbglReview { tx_type, ..self } + } + pub fn titles( self, title: &'a str, @@ -358,7 +408,7 @@ impl<'a> NbglReview<'a> { // Show the review on the device. let sync_ret = ledger_secure_sdk_sys::ux_sync_review( - TYPE_TRANSACTION.into(), + (&self.tx_type).into(), &tag_value_list as *const nbgl_contentTagValueList_t, &icon as *const nbgl_icon_details_t, self.title.as_ptr() as *const c_char, @@ -369,11 +419,11 @@ 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(STATUS_TYPE_TRANSACTION_SIGNED); + ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(true)); return true; } _ => { - ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_REJECTED); + ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(false)); return false; } } @@ -821,6 +871,120 @@ impl NbglGenericReview { } } +pub struct NbglStreamingReview { + icon: nbgl_icon_details_t, + tx_type: TransactionType, +} + +impl NbglStreamingReview { + pub fn new() -> NbglStreamingReview { + NbglStreamingReview { + icon: nbgl_icon_details_t::default(), + tx_type: TransactionType::Transaction, + } + } + + pub fn tx_type(self, tx_type: TransactionType) -> NbglStreamingReview { + NbglStreamingReview { tx_type, ..self } + } + + pub fn glyph(self, glyph: &NbglGlyph) -> NbglStreamingReview { + NbglStreamingReview { + icon: glyph.into(), + ..self + } + } + + pub fn start(&mut self, title: &str, subtitle: &str) -> bool { + unsafe { + let title = CString::new(title).unwrap(); + let subtitle = CString::new(subtitle).unwrap(); + + let sync_ret = ux_sync_reviewStreamingStart( + (&self.tx_type).into(), + &self.icon as *const nbgl_icon_details_t, + title.as_ptr() as *const c_char, + subtitle.as_ptr() as *const c_char, + ); + + // Return true if the user approved the transaction, false otherwise. + match sync_ret { + UX_SYNC_RET_APPROVED => { + return true; + } + _ => { + ux_sync_reviewStatus(self.tx_type.to_message(false)); + return false; + } + } + } + } + + pub fn continue_review(&mut self, fields: &[Field]) -> bool { + unsafe { + let v: Vec = fields + .iter() + .map(|f| CField { + name: CString::new(f.name).unwrap(), + value: CString::new(f.value).unwrap(), + }) + .collect(); + + // Fill the tag_value_array with the fields converted to nbgl_contentTagValue_t + let mut tag_value_array: Vec = Vec::new(); + for field in v.iter() { + let val = nbgl_contentTagValue_t { + item: field.name.as_ptr() as *const i8, + value: field.value.as_ptr() as *const i8, + ..Default::default() + }; + tag_value_array.push(val); + } + + // Create the tag_value_list with the tag_value_array. + let tag_value_list = nbgl_contentTagValueList_t { + pairs: tag_value_array.as_ptr() as *const nbgl_contentTagValue_t, + nbPairs: fields.len() as u8, + ..Default::default() + }; + + let sync_ret = ux_sync_reviewStreamingContinue( + &tag_value_list as *const nbgl_contentTagValueList_t, + ); + + // Return true if the user approved the transaction, false otherwise. + match sync_ret { + UX_SYNC_RET_APPROVED => { + return true; + } + _ => { + ux_sync_reviewStatus(self.tx_type.to_message(false)); + return false; + } + } + } + } + + pub fn finish(&mut self, finish_title: &str) -> bool { + unsafe { + let finish_title = CString::new(finish_title).unwrap(); + let sync_ret = ux_sync_reviewStreamingFinish(finish_title.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 => { + ux_sync_reviewStatus(self.tx_type.to_message(true)); + return true; + } + _ => { + ux_sync_reviewStatus(self.tx_type.to_message(false)); + return false; + } + } + } + } +} + /// A wrapper around the synchronous NBGL ux_sync_addressReview C API binding. /// Used to display address confirmation screens. pub struct NbglAddressReview<'a> {