Skip to content

Commit

Permalink
Add address confirmation and clean-up, rename some functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
agrojean-ledger committed Jan 17, 2024
1 parent 00475c3 commit 5791bd1
Showing 1 changed file with 158 additions and 104 deletions.
262 changes: 158 additions & 104 deletions ledger_device_sdk/src/nbgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct NbglContext {
icon: Option<&'static [u8]>,
name: [u8; 100],
info_contents: [[u8; 20]; 2],
review_confirm_string: [u8; 40],
review_reject_string: [u8; 40],
review_pairs: [ledger_secure_sdk_sys::nbgl_layoutTagValue_t; 10],
nb_pairs: u8,
review_status: ReviewStatus,
Expand Down Expand Up @@ -78,7 +80,131 @@ impl<'a> NbglUI<'a> {
self
}

fn settings() {
pub fn get_events<T: TryFrom<ApduHeader>>(&mut self) -> Event<T>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
loop {
if let Some(event) = self.get_event::<T>() {
return event;
}
}
}

pub fn show_home<T: TryFrom<ApduHeader>>(&mut self) -> Event<T>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
Self::display_home_cbk();
return self.get_events();
}

pub fn review_transaction(&mut self, fields: &[Field]) -> bool {
unsafe {
let icon = nbgl_icon_details_t {
width: 64,
height: 64,
bpp: 2,
isFile: true,
bitmap: CTX.icon.unwrap().as_ptr(),
};

for (i, field) in fields.iter().enumerate() {
if i >= CTX.review_pairs.len() {
break;
}
CTX.review_pairs[i] = nbgl_layoutTagValue_t {
item: field.name.as_ptr() as *const ::core::ffi::c_char,
value: field.value.as_ptr() as *const ::core::ffi::c_char,
valueIcon: core::ptr::null(),
};
}

CTX.nb_pairs = if fields.len() < CTX.review_pairs.len() {
fields.len()
} else {
CTX.review_pairs.len()
} as u8;

CTX.review_status = ReviewStatus::Pending;

ledger_secure_sdk_sys::nbgl_useCaseReviewStart(
&icon as *const nbgl_icon_details_t,
"Review tx\0".as_ptr() as *const ::core::ffi::c_char,
"Subtitle\0".as_ptr() as *const ::core::ffi::c_char,
"Reject transaction\0".as_ptr() as *const ::core::ffi::c_char,
transmute((|| Self::display_static_review_cbk()) as fn()),
None,
);

return self.wait_review_result();
}
}

pub fn verify_address(&mut self, address: &str) -> bool {
unsafe {
let icon = nbgl_icon_details_t {
width: 64,
height: 64,
bpp: 2,
isFile: true,
bitmap: CTX.icon.unwrap().as_ptr(),
};

CTX.nb_pairs = 1;
CTX.review_pairs[0] = nbgl_layoutTagValue_t {
item: "\0".as_ptr() as *const ::core::ffi::c_char, // Unused
value: address.as_ptr() as *const ::core::ffi::c_char,
valueIcon: core::ptr::null(),
};
CTX.review_status = ReviewStatus::Pending;

ledger_secure_sdk_sys::nbgl_useCaseReviewStart(
&icon as *const nbgl_icon_details_t,
"Verify address\0".as_ptr() as *const ::core::ffi::c_char,
core::ptr::null(),
"Cancel\0".as_ptr() as *const ::core::ffi::c_char,
transmute((|| Self::display_address_confirmation_cbk()) as fn()),
None,
);

return self.wait_review_result();
}
}

fn get_event<T: TryFrom<ApduHeader>>(&mut self) -> Option<Event<T>>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
match &mut self.comm {
None => None,
Some(comm) => {
if let Event::Command(ins) = comm.next_event() {
return Some(Event::Command(ins));
}
else
{
return None;
}
}
}
}

fn wait_review_result(&mut self) -> bool {
loop {
self.get_event::<DummyEvent>();
unsafe {
if CTX.review_status == ReviewStatus::Validate {
return true;
}
else if CTX.review_status == ReviewStatus::Reject {
return false;
}
}
}
}

fn display_settings_cbk() {
unsafe {
let nav = |page: u8, content: *mut nbgl_pageContent_s| {
if page == 0 {
Expand All @@ -101,14 +227,14 @@ impl<'a> NbglUI<'a> {
0 as u8,
1 as u8,
false as bool,
transmute((|| Self::home()) as fn()),
transmute((|| Self::display_home_cbk()) as fn()),
transmute(nav as fn(u8, *mut nbgl_pageContent_s) -> bool),
transmute((|| exit_app(12)) as fn()),
);
}
}

fn home() {
fn display_home_cbk() {
unsafe {
let icon = nbgl_icon_details_t {
width: 64,
Expand All @@ -123,41 +249,32 @@ impl<'a> NbglUI<'a> {
&icon as *const nbgl_icon_details_t,
core::ptr::null(),
true as bool,
transmute((|| Self::settings()) as fn()),
transmute((|| Self::display_settings_cbk()) as fn()),
transmute((|| exit_app(12)) as fn()),
);
}
}

pub fn show_home(&mut self) {
Self::home();
}
fn display_review_result_cbk(confirm: bool) {

fn choice(confirm: bool) {
let show_home = || {
Self::home();
};

let result_string: *const ::core::ffi::c_char;
unsafe {
if confirm {
CTX.review_status = ReviewStatus::Validate;
result_string = "TRANSACTION\nSIGNED\0".as_ptr() as *const ::core::ffi::c_char;
result_string = CTX.review_confirm_string.as_ptr() as *const ::core::ffi::c_char;
} else {
CTX.review_status = ReviewStatus::Reject;
result_string = "Transaction\nRejected\0".as_ptr() as *const ::core::ffi::c_char;
result_string = CTX.review_reject_string.as_ptr() as *const ::core::ffi::c_char;
}
}

unsafe {

ledger_secure_sdk_sys::nbgl_useCaseStatus(
result_string,
confirm,
transmute(show_home as fn()));
transmute((|| Self::display_home_cbk()) as fn()));
}
}

fn static_review() {
fn display_static_review_cbk() {
unsafe {
let tag_value_list: nbgl_layoutTagValueList_t = nbgl_layoutTagValueList_t {
pairs: CTX.review_pairs.as_mut_ptr() as *mut nbgl_layoutTagValue_t,
Expand All @@ -171,14 +288,19 @@ impl<'a> NbglUI<'a> {
};

// Using this icon causes a crash
// let icon = nbgl_icon_details_t {
// width: 64,
// height: 64,
// bpp: 2,
// isFile: true,
// bitmap: CTX.icon.unwrap().as_ptr(),
// };
let icon = nbgl_icon_details_t {
width: 32,
height: 32,
bpp: 2,
isFile: true,
bitmap: CTX.icon.unwrap().as_ptr(),
};

let confirm_string = "TRANSACTION\nSIGNED\0";
CTX.review_confirm_string[..confirm_string.len()].copy_from_slice(confirm_string.as_bytes());
let reject_string = "Transaction\nRejected\0";
CTX.review_reject_string[..reject_string.len()].copy_from_slice(reject_string.as_bytes());

let info_long_press: nbgl_pageInfoLongPress_t = nbgl_pageInfoLongPress_t {
text: "Validate tx\0".as_ptr() as *const ::core::ffi::c_char,
icon: core::ptr::null(),
Expand All @@ -191,91 +313,23 @@ impl<'a> NbglUI<'a> {
&tag_value_list as *const nbgl_layoutTagValueList_t,
&info_long_press as *const nbgl_pageInfoLongPress_t,
"Reject tx\0".as_ptr() as *const ::core::ffi::c_char,
// None,
transmute((|confirm| Self::choice(confirm)) as fn(confirm: bool)),
transmute((|confirm| Self::display_review_result_cbk(confirm)) as fn(confirm: bool)),
);
}
}

pub fn show_review_and_wait_validation(&mut self, fields: &[Field]) -> bool {
unsafe {
let icon = nbgl_icon_details_t {
width: 64,
height: 64,
bpp: 2,
isFile: true,
bitmap: CTX.icon.unwrap().as_ptr(),
};

for (i, field) in fields.iter().enumerate() {
if i >= CTX.review_pairs.len() {
break;
}
CTX.review_pairs[i] = nbgl_layoutTagValue_t {
item: field.name.as_ptr() as *const ::core::ffi::c_char,
value: field.value.as_ptr() as *const ::core::ffi::c_char,
valueIcon: core::ptr::null(),
};
}

CTX.nb_pairs = if fields.len() < CTX.review_pairs.len() {
fields.len()
} else {
CTX.review_pairs.len()
} as u8;
fn display_address_confirmation_cbk() {
unsafe {

CTX.review_status = ReviewStatus::Pending;
let confirm_string = "ADDRESS\nVERIFIED\0";
CTX.review_confirm_string[..confirm_string.len()].copy_from_slice(confirm_string.as_bytes());
let reject_string = "Address verification\ncancelled\0";
CTX.review_reject_string[..reject_string.len()].copy_from_slice(reject_string.as_bytes());

ledger_secure_sdk_sys::nbgl_useCaseReviewStart(
&icon as *const nbgl_icon_details_t,
"Review tx\0".as_ptr() as *const ::core::ffi::c_char,
"Subtitle\0".as_ptr() as *const ::core::ffi::c_char,
"Reject transaction\0".as_ptr() as *const ::core::ffi::c_char,
transmute((|| Self::static_review()) as fn()),
None,
ledger_secure_sdk_sys::nbgl_useCaseAddressConfirmation(
CTX.review_pairs[0].value as *const ::core::ffi::c_char,
transmute((|confirm| Self::display_review_result_cbk(confirm)) as fn(confirm: bool)),
);

loop {
self.get_event::<DummyEvent>();

if CTX.review_status == ReviewStatus::Validate {
debug_print("Validate\n");
return true;
}
else if CTX.review_status == ReviewStatus::Reject {
debug_print("Reject\n");
return false;
}
}
}
}

fn get_event<T: TryFrom<ApduHeader>>(&mut self) -> Option<Event<T>>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
match &mut self.comm {
None => None,
Some(comm) => {
if let Event::Command(ins) = comm.next_event() {
return Some(Event::Command(ins));
}
else
{
return None;
}
}
}
}

pub fn get_events<T: TryFrom<ApduHeader>>(&mut self) -> Event<T>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
loop {
if let Some(event) = self.get_event::<T>() {
return event;
}
}
}
}

0 comments on commit 5791bd1

Please sign in to comment.