Skip to content

Commit

Permalink
WIP : add NBGL generic review.
Browse files Browse the repository at this point in the history
  • Loading branch information
agrojean-ledger committed Jun 18, 2024
1 parent ed70077 commit 1157c0d
Show file tree
Hide file tree
Showing 4 changed files with 356 additions and 6 deletions.
1 change: 1 addition & 0 deletions ledger_device_sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ rand_core = { version = "0.6.3", default_features = false }
zeroize = { version = "1.6.0", default_features = false }
numtoa = "0.2.4"
const-zero = "0.1.1"
array-init = "2.1.0"

[features]
speculos = []
Expand Down
2 changes: 2 additions & 0 deletions ledger_device_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub mod seph;

pub mod testing;

// extern crate alloc;

#[cfg(any(target_os = "stax", target_os = "flex"))]
pub mod nbgl;
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
Expand Down
346 changes: 345 additions & 1 deletion ledger_device_sdk/src/nbgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use core::ffi::{c_char, CStr};
use core::mem::transmute;
use ledger_secure_sdk_sys::*;

// use alloc::vec::Vec;
use array_init::array_init;

#[no_mangle]
pub static mut G_ux_params: bolos_ux_params_t = unsafe { const_zero!(bolos_ux_params_t) };

Expand All @@ -20,6 +23,41 @@ pub struct Field<'a> {
pub value: &'a str,
}

#[derive(Copy, Clone)]
pub struct CField<'a> {
pub name: &'a CStr,
pub value: &'a CStr,
}

impl<'a> From<CField<'a>> for nbgl_contentTagValue_t {
fn from(field: CField) -> Self {
nbgl_contentTagValue_t {
item: field.name.as_ptr() as *const i8,
value: field.value.as_ptr() as *const i8,
valueIcon: core::ptr::null() as *const nbgl_icon_details_t,
_bitfield_align_1: [0; 0],
_bitfield_1: __BindgenBitfieldUnit::new([0; 1usize]),
__bindgen_padding_0: [0; 3usize],
}
}
}

// Custom trait for converting an array to an array of nbgl_contentTagValue_t
trait ToTagValueArray<const N: usize> {
fn to_tag_value_array(self) -> [nbgl_contentTagValue_t; N];
}

impl<'a, const N: usize> ToTagValueArray<N> for [CField<'a>; N] {
fn to_tag_value_array(self) -> [nbgl_contentTagValue_t; N] {
array_init(|i| self[i].into())
}
}

// Public function to convert CField array to nbgl_contentTagValue_t array
pub fn convert_fields<const N: usize>(fields: [CField; N]) -> [nbgl_contentTagValue_t; N] {
fields.to_tag_value_array()
}

pub struct NbglGlyph<'a> {
pub width: u16,
pub height: u16,
Expand Down Expand Up @@ -473,6 +511,311 @@ impl<'a, const MAX_FIELD_NUMBER: usize, const STRING_BUFFER_SIZE: usize>
}
}

#[derive(Copy, Clone)]
pub enum CenteredInfoStyle {
LargeCaseInfo = 0,
LargeCaseBoldInfo,
NormalInfo,
PluginInfo,
}

impl From<CenteredInfoStyle> for nbgl_contentCenteredInfoStyle_t {
fn from(style: CenteredInfoStyle) -> nbgl_contentCenteredInfoStyle_t {
match style {
CenteredInfoStyle::LargeCaseInfo => LARGE_CASE_INFO,
CenteredInfoStyle::LargeCaseBoldInfo => LARGE_CASE_BOLD_INFO,
CenteredInfoStyle::NormalInfo => NORMAL_INFO,
CenteredInfoStyle::PluginInfo => PLUGIN_INFO,
}
}
}

#[derive(Copy, Clone)]
pub struct CenteredInfo<'a> {
pub text1: &'a CStr,
pub text2: &'a CStr,
pub text3: &'a CStr,
pub icon: Option<&'a nbgl_icon_details_t>,
pub on_top: bool,
pub style: CenteredInfoStyle,
pub offset_y: i16,
}

impl<'a> From<CenteredInfo<'a>> for nbgl_contentCenteredInfo_t {
fn from(info: CenteredInfo) -> Self {
nbgl_contentCenteredInfo_t {
text1: info.text1.as_ptr() as *const c_char,
text2: info.text2.as_ptr() as *const c_char,
text3: info.text3.as_ptr() as *const c_char,
icon: if info.icon.is_some() {
info.icon.unwrap()
} else {
core::ptr::null()
},
onTop: info.on_top,
style: info.style.into(),
offsetY: info.offset_y,
}
}
}

#[derive(Copy, Clone)]
pub struct InfoLongPress<'a> {
pub text: &'a CStr,
pub icon: Option<&'a nbgl_icon_details_t>,
pub long_press_text: &'a CStr,
pub tune_id: TuneIndex,
}

impl<'a> From<InfoLongPress<'a>> for nbgl_contentInfoLongPress_t {
fn from(info: InfoLongPress) -> Self {
nbgl_contentInfoLongPress_t {
text: info.text.as_ptr() as *const c_char,
icon: if info.icon.is_some() {
info.icon.unwrap()
} else {
core::ptr::null()
},
longPressText: info.long_press_text.as_ptr() as *const c_char,
longPressToken: FIRST_USER_TOKEN as u8,
tuneId: info.tune_id as u8,
}
}
}

#[derive(Copy, Clone)]
pub struct InfoButton<'a> {
pub text: &'a CStr,
pub icon: Option<&'a nbgl_icon_details_t>,
pub button_text: &'a CStr,
pub tune_id: TuneIndex,
}

impl<'a> From<InfoButton<'a>> for nbgl_contentInfoButton_t {
fn from(info: InfoButton) -> Self {
nbgl_contentInfoButton_t {
text: info.text.as_ptr() as *const c_char,
icon: if info.icon.is_some() {
info.icon.unwrap()
} else {
core::ptr::null()
},
buttonText: info.button_text.as_ptr() as *const c_char,
buttonToken: FIRST_USER_TOKEN as u8,
tuneId: info.tune_id as u8,
}
}
}

#[derive(Copy, Clone)]
pub struct TagValueList<'a> {
pub pairs: &'a [nbgl_contentTagValue_t],
pub nb_max_lines_for_value: u8,
pub small_case_for_value: bool,
pub wrapping: bool,
}

impl<'a> From<TagValueList<'a>> for nbgl_contentTagValueList_t {
fn from(list: TagValueList) -> Self {
nbgl_contentTagValueList_t {
pairs: list.pairs.as_ptr() as *const nbgl_contentTagValue_t,
callback: None,
nbPairs: list.pairs.len() as u8,
startIndex: 0,
nbMaxLinesForValue: list.nb_max_lines_for_value,
token: FIRST_USER_TOKEN as u8,
smallCaseForValue: list.small_case_for_value,
wrapping: list.wrapping,
}
}
}

#[derive(Copy, Clone)]
pub struct TagValueConfirm<'a> {
pub tag_value_list: TagValueList<'a>,
pub tune_id: TuneIndex,
pub confirmation_text: &'a CStr,
pub cancel_text: &'a CStr,
}

impl<'a> From<TagValueConfirm<'a>> for nbgl_contentTagValueConfirm_t {
fn from(confirm: TagValueConfirm) -> Self {
nbgl_contentTagValueConfirm_t {
tagValueList: confirm.tag_value_list.into(),
detailsButtonIcon: core::ptr::null(),
detailsButtonText: core::ptr::null(),
detailsButtonToken: (FIRST_USER_TOKEN + 2) as u8,
tuneId: confirm.tune_id as u8,
confirmationText: confirm.confirmation_text.as_ptr() as *const c_char,
cancelText: confirm.cancel_text.as_ptr() as *const c_char,
confirmationToken: FIRST_USER_TOKEN as u8,
cancelToken: (FIRST_USER_TOKEN + 1) as u8,
}
}
}

#[derive(Copy, Clone)]
pub enum NbglPageContent<'a> {
CenteredInfo(CenteredInfo<'a>),
InfoLongPress(InfoLongPress<'a>),
InfoButton(InfoButton<'a>),
TagValueList(TagValueList<'a>),
TagValueConfirm(TagValueConfirm<'a>),
// InfosList(InfosList),
}

impl<'a> From<NbglPageContent<'a>>
for (
nbgl_content_u,
nbgl_contentType_t,
nbgl_contentActionCallback_t,
)
{
fn from(
content: NbglPageContent,
) -> (
nbgl_content_u,
nbgl_contentType_t,
nbgl_contentActionCallback_t,
) {
match content {
NbglPageContent::CenteredInfo(data) => (
nbgl_content_u {
centeredInfo: data.into(),
},
CENTERED_INFO,
None,
),
NbglPageContent::InfoLongPress(data) => (
nbgl_content_u {
infoLongPress: data.into(),
},
INFO_LONG_PRESS,
Some(generic_content_action_callback),
),
NbglPageContent::InfoButton(data) => (
nbgl_content_u {
infoButton: data.into(),
},
INFO_BUTTON,
Some(generic_content_action_callback),
),
NbglPageContent::TagValueList(data) => (
nbgl_content_u {
tagValueList: data.into(),
},
TAG_VALUE_LIST,
None,
),
NbglPageContent::TagValueConfirm(data) => (
nbgl_content_u {
tagValueConfirm: data.into(),
},
TAG_VALUE_CONFIRM,
Some(generic_content_action_callback),
),
}
}
}

pub fn convert_content_array<'a, const N: usize>(
contents: [NbglPageContent<'a>; N],
) -> [nbgl_content_t; N] {
array_init(|i| convert_content(contents[i]))
}

pub fn convert_content(content: NbglPageContent) -> nbgl_content_t {
let (c_content, content_type, action_callback) = content.into();
nbgl_content_t {
type_: content_type,
content: c_content,
contentActionCallback: action_callback,
}
}

pub struct NbglGenericReview;
// pub struct NbglGenericReview {
// content_list: Vec<nbgl_content_t>,
// }

impl NbglGenericReview {
pub fn new() -> NbglGenericReview {
NbglGenericReview {
// content_list: Vec::new(),
}
}

// pub fn add_content(mut self, content: NbglPageContent) -> NbglGenericReview {
// let (c_content, content_type, action_callback) = content.into();
// self.content_list.push(nbgl_content_t {
// type_: content_type,
// content: c_content,
// contentActionCallback: action_callback,
// });
// self
// }

pub fn show_content_array(&mut self, contents: &[nbgl_content_t], reject_str: &CStr) -> bool {
unsafe {
let content_struct = nbgl_genericContents_t {
callbackCallNeeded: false,
__bindgen_anon_1: nbgl_genericContents_t__bindgen_ty_1 {
contentsList: contents.as_ptr() as *const nbgl_content_t,
},
nbContents: contents.len() as u8,
};

let sync_ret = ux_sync_genericReview(
&content_struct as *const nbgl_genericContents_t,
reject_str.as_ptr() as *const c_char,
);

// Return true if the user approved the transaction, false otherwise.
// TODO : allow customizable review status messages
match sync_ret {
ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => {
ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_SIGNED);
return true;
}
_ => {
ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_REJECTED);
return false;
}
}
}
}

// pub fn show(&mut self, reject_str: &CStr) -> bool {
// unsafe {
// let content_struct = nbgl_genericContents_t {
// callbackCallNeeded: false,
// __bindgen_anon_1: nbgl_genericContents_t__bindgen_ty_1 {
// contentsList: self.content_list.as_ptr() as *const nbgl_content_t,
// },
// nbContents: self.content_list.len() as u8,
// };

// let sync_ret = ux_sync_genericReview(
// &content_struct as *const nbgl_genericContents_t,
// reject_str.as_ptr() as *const c_char,
// );

// // Return true if the user approved the transaction, false otherwise.
// // TODO : allow customizable review status messages
// match sync_ret {
// ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => {
// ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_SIGNED);
// return true;
// }
// _ => {
// ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_REJECTED);
// return false;
// }
// }
// }
// }
}

/// A wrapper around the synchronous NBGL ux_sync_addressReview C API binding.
/// Used to display address confirmation screens.
pub struct NbglAddressReview<'a> {
Expand Down Expand Up @@ -539,7 +882,8 @@ impl<'a> NbglAddressReview<'a> {
}
}

enum TuneIndex {
#[derive(Copy, Clone)]
pub enum TuneIndex {
Reserved,
Boot,
Charging,
Expand Down
Loading

0 comments on commit 1157c0d

Please sign in to comment.