diff --git a/ledger_app.toml b/ledger_app.toml index f067760..5266444 100644 --- a/ledger_app.toml +++ b/ledger_app.toml @@ -1,7 +1,7 @@ [app] build_directory = "./" sdk = "Rust" -devices = ["nanos", "nanox", "nanos+", "stax"] +devices = ["nanos", "nanox", "nanos+", "stax", "flex"] [tests] pytest_directory = "./tests/" diff --git a/src/app_ui/menu.rs b/src/app_ui/menu.rs index f84a50f..3d4eb22 100644 --- a/src/app_ui/menu.rs +++ b/src/app_ui/menu.rs @@ -24,10 +24,14 @@ use ledger_device_sdk::ui::bitmaps::{Glyph, BACK, CERTIFICATE, DASHBOARD_X}; use ledger_device_sdk::ui::gadgets::{EventOrPageIndex, MultiPageMenu, Page}; #[cfg(any(target_os = "stax", target_os = "flex"))] -use ledger_device_sdk::nbgl::{NbglGlyph, NbglHome}; +use crate::settings::Settings; +#[cfg(any(target_os = "stax", target_os = "flex"))] +use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; use crate::Instruction; +// use ledger_device_sdk::nvm::*; + #[cfg(not(any(target_os = "stax", target_os = "flex")))] fn ui_about_menu(comm: &mut Comm) -> Event { let pages = [ @@ -68,10 +72,17 @@ pub fn ui_menu_main(comm: &mut Comm) -> Event { pub fn ui_menu_main(comm: &mut Comm) -> Event { // Load glyph from 64x64 4bpp gif file with include_gif macro. Creates an NBGL compatible glyph. const FERRIS: NbglGlyph = NbglGlyph::from_include(include_gif!("crab_64x64.gif", NBGL)); + + let settings_strings = [["Display Memo", "Allow display of transaction memo."]]; + let settings = Settings::default(); // Display the home screen. - NbglHome::new(comm) + NbglHomeAndSettings::new(comm) .glyph(&FERRIS) - // .app_name("Boilerplate") - .infos("Boilerplate", env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_AUTHORS")) + .settings(settings.get_mut_ref(), &settings_strings) + .infos( + "Boilerplate", + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_AUTHORS"), + ) .show() } diff --git a/src/app_ui/sign.rs b/src/app_ui/sign.rs index d07a512..a1cf6eb 100644 --- a/src/app_ui/sign.rs +++ b/src/app_ui/sign.rs @@ -23,6 +23,8 @@ use ledger_device_sdk::ui::bitmaps::{CROSSMARK, EYE, VALIDATE_14}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::gadgets::{Field, MultiFieldReview}; +#[cfg(any(target_os = "stax", target_os = "flex"))] +use crate::settings::Settings; #[cfg(any(target_os = "stax", target_os = "flex"))] use include_gif::include_gif; #[cfg(any(target_os = "stax", target_os = "flex"))] @@ -101,6 +103,13 @@ pub fn ui_display_tx(tx: &Tx) -> Result { "Sign transaction\nto send CRAB", ) .glyph(&FERRIS); - Ok(review.show(&my_fields)) + + // If first setting switch is disabled do not display the transaction memo + let settings = Settings::default(); + if settings.get_element(0) == 0 { + return Ok(review.show(&my_fields[0..2])); + } else { + return Ok(review.show(&my_fields)); + } } } diff --git a/src/main.rs b/src/main.rs index a9c0c76..c6c18b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,8 @@ mod handlers { pub mod sign_tx; } +mod settings; + use app_ui::menu::ui_menu_main; use handlers::{ get_public_key::handler_get_public_key, diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..3ff59e7 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,39 @@ +use ledger_device_sdk::nvm::*; +use ledger_device_sdk::NVMData; + +// This is necessary to store the object in NVM and not in RAM +const SETTINGS_SIZE: usize = 10; +#[link_section = ".nvm_data"] +static mut DATA: NVMData> = + NVMData::new(AtomicStorage::new(&[0u8; 10])); + +#[derive(Clone, Copy)] +pub struct Settings; + +impl Default for Settings { + fn default() -> Self { + Settings + } +} + +impl Settings { + #[inline(never)] + pub fn get_mut_ref(&self) -> &mut AtomicStorage<[u8; SETTINGS_SIZE]> { + return unsafe { DATA.get_mut() }; + } + + pub fn get_element(&self, index: usize) -> u8 { + let settings = unsafe { DATA.get_mut() }; + settings.get_ref()[index] + } + + #[allow(unused)] + // Not used in this boilerplate, but can be used to set a value in the settings + pub fn set_element(&self, index: usize, value: u8) { + let mut updated_data: [u8; SETTINGS_SIZE] = unsafe { DATA.get_mut().get_ref().clone() }; + updated_data[index] = value; + unsafe { + DATA.get_mut().update(&updated_data); + } + } +} diff --git a/tests/snapshots/flex/test_app_mainmenu/00000.png b/tests/snapshots/flex/test_app_mainmenu/00000.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_app_mainmenu/00000.png and b/tests/snapshots/flex/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/flex/test_app_mainmenu/00001.png b/tests/snapshots/flex/test_app_mainmenu/00001.png index cbc814e..ca9fbbe 100644 Binary files a/tests/snapshots/flex/test_app_mainmenu/00001.png and b/tests/snapshots/flex/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/flex/test_app_mainmenu/00002.png b/tests/snapshots/flex/test_app_mainmenu/00002.png index adf10d9..f0a7aba 100644 Binary files a/tests/snapshots/flex/test_app_mainmenu/00002.png and b/tests/snapshots/flex/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/flex/test_app_mainmenu/00003.png b/tests/snapshots/flex/test_app_mainmenu/00003.png new file mode 100644 index 0000000..7a59203 Binary files /dev/null and b/tests/snapshots/flex/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png and b/tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_refused/00002.png b/tests/snapshots/flex/test_get_public_key_confirm_refused/00002.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_get_public_key_confirm_refused/00002.png and b/tests/snapshots/flex/test_get_public_key_confirm_refused/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_long_tx/00005.png b/tests/snapshots/flex/test_sign_tx_long_tx/00005.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_sign_tx_long_tx/00005.png and b/tests/snapshots/flex/test_sign_tx_long_tx/00005.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/00001.png b/tests/snapshots/flex/test_sign_tx_refused/00001.png index e1fe554..5d919f7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/00001.png and b/tests/snapshots/flex/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/00005.png b/tests/snapshots/flex/test_sign_tx_refused/00005.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/00005.png and b/tests/snapshots/flex/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx/00004.png b/tests/snapshots/flex/test_sign_tx_short_tx/00004.png index adf10d9..7a59203 100644 Binary files a/tests/snapshots/flex/test_sign_tx_short_tx/00004.png and b/tests/snapshots/flex/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00000.png b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00000.png new file mode 100644 index 0000000..5af5c41 Binary files /dev/null and b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00001.png b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00001.png new file mode 100644 index 0000000..2d0c474 Binary files /dev/null and b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00002.png b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00002.png new file mode 100644 index 0000000..2bba7ab Binary files /dev/null and b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00003.png b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00003.png differ diff --git a/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00004.png b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00004.png new file mode 100644 index 0000000..7a59203 Binary files /dev/null and b/tests/snapshots/flex/test_sign_tx_short_tx_no_memo/00004.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00000.png b/tests/snapshots/stax/test_app_mainmenu/00000.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_app_mainmenu/00000.png and b/tests/snapshots/stax/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00001.png b/tests/snapshots/stax/test_app_mainmenu/00001.png index 20112a9..1b31f9b 100644 Binary files a/tests/snapshots/stax/test_app_mainmenu/00001.png and b/tests/snapshots/stax/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00002.png b/tests/snapshots/stax/test_app_mainmenu/00002.png index 61e774e..443855b 100644 Binary files a/tests/snapshots/stax/test_app_mainmenu/00002.png and b/tests/snapshots/stax/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00003.png b/tests/snapshots/stax/test_app_mainmenu/00003.png new file mode 100644 index 0000000..c1ad3b8 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/00002.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_refused/00002.png and b/tests/snapshots/stax/test_get_public_key_confirm_refused/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_long_tx/00005.png b/tests/snapshots/stax/test_sign_tx_long_tx/00005.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_sign_tx_long_tx/00005.png and b/tests/snapshots/stax/test_sign_tx_long_tx/00005.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/00001.png b/tests/snapshots/stax/test_sign_tx_refused/00001.png index 11d6c13..55c7550 100644 Binary files a/tests/snapshots/stax/test_sign_tx_refused/00001.png and b/tests/snapshots/stax/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/00005.png b/tests/snapshots/stax/test_sign_tx_refused/00005.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_sign_tx_refused/00005.png and b/tests/snapshots/stax/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00004.png b/tests/snapshots/stax/test_sign_tx_short_tx/00004.png index 61e774e..c1ad3b8 100644 Binary files a/tests/snapshots/stax/test_sign_tx_short_tx/00004.png and b/tests/snapshots/stax/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00000.png b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00000.png new file mode 100644 index 0000000..6487264 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00001.png b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00001.png new file mode 100644 index 0000000..91bfbb1 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00002.png b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00002.png new file mode 100644 index 0000000..24c4459 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00003.png b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00003.png new file mode 100644 index 0000000..2ba6d27 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00004.png b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00004.png new file mode 100644 index 0000000..c1ad3b8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx_no_memo/00004.png differ diff --git a/tests/test_app_mainmenu.py b/tests/test_app_mainmenu.py index c41c5d0..af00e25 100644 --- a/tests/test_app_mainmenu.py +++ b/tests/test_app_mainmenu.py @@ -15,6 +15,7 @@ def test_app_mainmenu(firmware, navigator, test_name): else: instructions = [ NavInsID.USE_CASE_HOME_SETTINGS, + NavInsID.USE_CASE_SUB_SETTINGS_NEXT, NavInsID.USE_CASE_SUB_SETTINGS_EXIT ] navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, instructions, diff --git a/tests/test_sign_cmd.py b/tests/test_sign_cmd.py index c9da750..aae724b 100644 --- a/tests/test_sign_cmd.py +++ b/tests/test_sign_cmd.py @@ -4,7 +4,7 @@ from application_client.boilerplate_command_sender import BoilerplateCommandSender, Errors from application_client.boilerplate_response_unpacker import unpack_get_public_key_response, unpack_sign_tx_response from ragger.error import ExceptionRAPDU -from ragger.navigator import NavInsID +from ragger.navigator import NavIns, NavInsID from utils import ROOT_SCREENSHOT_PATH, check_signature_validity # In this tests we check the behavior of the device when asked to sign a transaction @@ -31,7 +31,15 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name): to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae", memo="For u EthDev" ).serialize() - + + # Enable display of transaction memo (NBGL devices only) + if not firmware.device.startswith("nano"): + navigator.navigate([NavInsID.USE_CASE_HOME_SETTINGS, + NavIns(NavInsID.TOUCH, (200, 113)), + NavInsID.USE_CASE_SUB_SETTINGS_EXIT], + screen_change_before_first_instruction=False, + screen_change_after_last_instruction=False) + # Send the sign device instruction. # As it requires on-screen validation, the function is asynchronous. # It will yield the result when the navigation is done @@ -56,6 +64,49 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name): _, der_sig, _ = unpack_sign_tx_response(response) assert check_signature_validity(public_key, der_sig, transaction) + +# In this test se send to the device a transaction to sign and validate it on screen +# The transaction is short and will be sent in one chunk +# We will ensure that the displayed information is correct by using screenshots comparison +# The transaction memo should not be displayed as we have not enabled it in the app settings. +def test_sign_tx_short_tx_no_memo(firmware, backend, navigator, test_name): + if firmware.device.startswith("nano"): + pytest.skip("Skipping this test for Nano devices") + + # Use the app interface instead of raw interface + client = BoilerplateCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/1'/0'/0/0" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + # Create the transaction that will be sent to the device for signing + transaction = Transaction( + nonce=1, + coin="CRAB", + value=777, + to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae", + memo="For u EthDev" + ).serialize() + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_tx(path=path, transaction=transaction): + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.WAIT_FOR_HOME_SCREEN], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, der_sig, _ = unpack_sign_tx_response(response) + + assert check_signature_validity(public_key, der_sig, transaction) # In this test se send to the device a transaction to sign and validate it on screen @@ -79,6 +130,14 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name): "As the maximum chunk size is 255 bytes we will make this memo greater than 255 characters. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam.") ).serialize() + + # Enable display of transaction memo (NBGL devices only) + if not firmware.device.startswith("nano"): + navigator.navigate([NavInsID.USE_CASE_HOME_SETTINGS, + NavIns(NavInsID.TOUCH, (200, 113)), + NavInsID.USE_CASE_SUB_SETTINGS_EXIT], + screen_change_before_first_instruction=False, + screen_change_after_last_instruction=False) with client.sign_tx(path=path, transaction=transaction): if firmware.device.startswith("nano"):