### List Available Examples Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Lists all available example Rust files in the `examples/` directory. ```bash ls examples/*.rs ``` -------------------------------- ### Install and Setup cargo-ledger Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Installs the `cargo-ledger` tool and sets up custom targets for building Ledger applications. This setup is a one-time operation. ```bash cargo install cargo-ledger cargo ledger setup ``` -------------------------------- ### Build and Run Commands for Ledger Devices Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Commands for setting up the Rust toolchain, installing cargo-ledger, building for specific devices, loading applications, and running examples with Speculos. ```bash # Install toolchain prerequisites rustup default nightly-2025-12-05 rustup component add rust-src sudo apt install clang gcc-arm-none-eabi gcc-multilib # Install cargo-ledger cargo install --git https://github.com/LedgerHQ/cargo-ledger cargo ledger setup # Build for each supported device cargo ledger build nanox # Nano X cargo ledger build nanosplus # Nano S+ cargo ledger build stax # Stax cargo ledger build flex # Flex cargo ledger build apex_p # Apex P # Build and load to a connected device cargo ledger build nanosplus --load # Run an example with Speculos emulator (requires dev-tools Docker image) docker run --rm -v "$(pwd):/app" \ ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \ cargo run --example nbgl_review --target stax --release \ --features io_new --config examples/config.toml ``` -------------------------------- ### Run NBGL Example on Nano Devices Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Builds and runs the `nbgl_home_and_settings` example on a Nano S+ device using Speculos emulator. Requires the `nano_nbgl` feature and a configuration file. ```bash cargo run --example nbgl_home_and_settings --target nanosplus --release \ --features nano_nbgl --config examples/config.toml ``` -------------------------------- ### Run NBGL Example on Touchscreen Devices Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Builds and runs the `nbgl_home_and_settings` example on a Stax device using Speculos emulator. Requires a release build and a configuration file. ```bash cargo run --example nbgl_home_and_settings --target stax --release \ --config examples/config.toml ``` -------------------------------- ### Install cargo-ledger Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/README.md Install the cargo-ledger CLI tool, which is required for building Ledger applications. This command installs directly from the git repository. ```bash cargo install --git https://github.com/LedgerHQ/ledger-ledger ``` -------------------------------- ### Install ARM GCC Toolchain (Debian/Ubuntu) Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Installs the necessary Clang and ARM GCC toolchain for building Ledger applications on Debian-based systems. ```bash sudo apt install clang gcc-arm-none-eabi gcc-multilib ``` -------------------------------- ### Install ARM GCC Toolchain (ArchLinux) Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Installs the necessary Clang and ARM GCC toolchain for building Ledger applications on Arch Linux. ```bash sudo pacman -S clang arm-none-eabi-gcc arm-none-eabi-newlib ``` -------------------------------- ### Cryptographic Hashing Examples Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Provides examples for one-shot and incremental hashing using SHA-2, SHA-3, Keccak, and other algorithms. Ensure the digest buffer is correctly sized for the chosen algorithm. ```rust use ledger_device_sdk::hash::{HashInit, sha2::Sha2_256, sha3::Keccak256, sha2::Sha2_512}; fn hash_examples(data: &[u8]) { // One-shot SHA-256 let mut sha256 = Sha2_256::new(); let mut digest = [0u8; 32]; sha256.hash(data, &mut digest).expect("hash failed"); // digest now holds SHA-256(data) // Incremental Keccak-256 (Ethereum hashing) let mut keccak = Keccak256::new(); let mut out = [0u8; 32]; keccak.update(&data[..data.len() / 2]).unwrap(); keccak.update(&data[data.len() / 2..]).unwrap(); keccak.finalize(&mut out).unwrap(); // SHA-512 let mut sha512 = Sha2_512::new(); let mut out512 = [0u8; 64]; sha512.hash(data, &mut out512).unwrap(); let _ = sha256.get_size(); // returns 32 } ``` -------------------------------- ### Install ARM GCC Toolchain (Fedora/RHEL) Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Installs the necessary Clang and ARM GCC toolchain for building Ledger applications on Fedora or Red Hat Enterprise Linux. ```bash sudo dnf install clang arm-none-eabi-gcc arm-none-eabi-newlib ``` -------------------------------- ### Build and Run Ledger App with Docker Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/README.md Use the Docker image to build your Rust application for Ledger devices using cargo-ledger. Then, run examples with Speculos using the dev-tools image. ```bash # Pull the Docker image docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest # Build your app (from your project directory) using cargo-ledger docker run --rm -v "$(pwd):/app" \ ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest \ cargo ledger build stax # Run examples with Speculos (dev-tools image) docker run --rm -v "$(pwd):/app" \ ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \ cargo run --example nbgl_home_and_settings --target stax --release ``` -------------------------------- ### HMAC Hashing Examples Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Illustrates one-shot and streaming HMAC computations using Ripemd160, SHA-256, and SHA-512. The output buffer size must match the HMAC algorithm's digest size. ```rust use ledger_device_sdk::hmac::{HMACInit, sha2::HmacSha2_256, ripemd::Ripemd160}; fn hmac_examples(key: &[u8], msg: &[u8]) { // One-shot HMAC-SHA256 let mut mac = HmacSha2_256::new(key); let mut out = [0u8; 32]; mac.hmac(msg, &mut out).expect("hmac failed"); // Streaming HMAC-RIPEMD160 let mut mac2 = Ripemd160::new(key); let mut out2 = [0u8; 20]; mac2.update(&msg[..4]).unwrap(); mac2.update(&msg[4..]).unwrap(); let len = mac2.finalize(&mut out2).unwrap(); // returns MAC length assert_eq!(len, 20); } ``` -------------------------------- ### Build Ledger App with plain cargo Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Builds a Rust application using plain `cargo` with a specified target. This method requires manual setup of custom targets. ```bash cargo build --release --target=nanox # Nano X cargo build --release --target=nanosplus # Nano S+ cargo build --release --target=stax # Stax cargo build --release --target=flex # Flex cargo build --release --target=apex_p # Apex P ``` -------------------------------- ### Atomic NVM Storage for Fixed-Size Array Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Shows how to use AtomicStorage to manage a fixed-size array in NVM. Updates involve getting a mutable reference, modifying the array, and then updating the storage. ```rust // --- Fixed-size settings array --- #[unsafe(link_section = ".nvm_data")] static mut SETTINGS: NVMData> = NVMData::new(AtomicStorage::new(&[0u8; 16])); fn set_setting(idx: usize, val: u8) { let data = &raw mut SETTINGS; let storage = unsafe { (*data).get_mut() }; let mut arr = *storage.get_ref(); arr[idx] = val; unsafe { storage.update(&arr) }; } ``` -------------------------------- ### NbglHomeAndSettings Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Displays the app's home screen with name, version, and author. Optionally includes a settings page with toggle switches backed by NVM storage. The `show_and_return()` method blocks until the user exits the settings page. ```APIDOC ## `NbglHomeAndSettings` — Home screen with optional settings Displays the app's home screen, app name, version, and author. Optionally shows a settings page with toggle switches backed by NVM storage. `show_and_return()` blocks until the user exits the settings page. ```rust use include_gif::include_gif; use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings, init_comm}; use ledger_device_sdk::NVMData; use ledger_device_sdk::nvm::AtomicStorage; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); const SETTINGS_SIZE: usize = 2; #[unsafe(link_section = ".nvm_data")] static mut DATA: NVMData> = NVMData::new(AtomicStorage::new(&[0u8; SETTINGS_SIZE])); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); // Two toggle settings, each with a title and subtitle let settings_strings = [ ["Expert mode", "Show advanced details"], ["Blind signing", "Enable at your own risk"], ]; let nvm = unsafe { (&raw mut DATA as *mut NVMData>).as_mut().unwrap() }; let storage = nvm.get_mut(); let mut home = NbglHomeAndSettings::new() .glyph(&ICON) .infos("My App", env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_AUTHORS")) .tagline("A sample Ledger application") .settings(storage, &settings_strings); home.show_and_return(); // blocks until user exits settings } ``` ``` -------------------------------- ### Display Home Screen with Settings Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use NbglHomeAndSettings to display the app's home screen with name, version, and author. Optionally includes a settings page with toggle switches backed by NVM storage. The show_and_return() method blocks until the user exits the settings. ```rust use include_gif::include_gif; use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings, init_comm}; use ledger_device_sdk::NVMData; use ledger_device_sdk::nvm::AtomicStorage; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); const SETTINGS_SIZE: usize = 2; #[unsafe(link_section = ".nvm_data")] static mut DATA: NVMData> = NVMData::new(AtomicStorage::new(&[0u8; SETTINGS_SIZE])); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); // Two toggle settings, each with a title and subtitle let settings_strings = [ ["Expert mode", "Show advanced details"], ["Blind signing", "Enable at your own risk"], ]; let nvm = unsafe { (&raw mut DATA as *mut NVMData>).as_mut().unwrap() }; let storage = nvm.get_mut(); let mut home = NbglHomeAndSettings::new() .glyph(&ICON) .infos("My App", env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_AUTHORS")) .tagline("A sample Ledger application") .settings(storage, &settings_strings); home.show_and_return(); // blocks until user exits settings } ``` -------------------------------- ### PIC Wrapper for ROM Constants Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Demonstrates using Pic to wrap ROM constants, ensuring they are accessed via address translation. Use .get_ref() to access the data. ```rust use ledger_device_sdk::Pic; // Constant ROM data — accessed via address translation static LOOKUP_TABLE: Pic<[u8; 4]> = Pic::new([0xDE, 0xAD, 0xBE, 0xEF]); fn use_table() -> u8 { let table = LOOKUP_TABLE.get_ref(); // performs pic_rs translation table[0] // 0xDE } ``` -------------------------------- ### Add ledger_device_sdk to Cargo.toml Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Add the ledger_device_sdk crate to your project's Cargo.toml file. The 'io_new' feature enables the recommended new I/O module. ```toml ledger_device_sdk = { version = "1.35.0", features = ["io_new"] } [profile.release] opt-level = 's' lto = true ``` -------------------------------- ### make_bip32_path Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Compiles a BIP32 path string into a `[u32; N]` array at compile time. It supports hardened components and panics if the format is invalid. ```APIDOC ## `make_bip32_path` — Compile-time BIP32 path parser Parses a BIP32 path string (format `b"m/44'/coin'/account'/change/index"`) into a `[u32; N]` array at compile time. Hardened components (with `'`) have `0x80000000` added. Panics at compile time if the format is invalid. ```rust use ledger_device_sdk::ecc::make_bip32_path; // Evaluated entirely at compile time const ETH_PATH: [u32; 5] = make_bip32_path(b"m/44'/60'/0'/0/0"); const BTC_PATH: [u32; 5] = make_bip32_path(b"m/44'/0'/0'/0/0"); const CUSTOM: [u32; 3] = make_bip32_path(b"m/44'/535348'/0'"); // ETH_PATH == [0x8000002C, 0x8000003C, 0x80000000, 0, 0] // BTC_PATH == [0x8000002C, 0x80000000, 0x80000000, 0, 0] ``` ``` -------------------------------- ### Build Rust App using Docker Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Builds a Rust application for Ledger devices using the provided Docker container, ensuring a reproducible build environment. ```bash docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest docker run --rm -v "$(pwd):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest \ cargo ledger build stax ``` -------------------------------- ### Build Ledger App with Features Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Build a Ledger app for a specific device (e.g., Stax) with conditional features enabled. Ensure the target device and features are correctly specified. ```bash cargo ledger build stax -- --features io_new,nano_nbgl ``` -------------------------------- ### NVM Collection for Fixed-Size Items Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Illustrates using Collection for a fixed-capacity NVM set. Items can be added, and the collection is iterable. Be aware of the StorageFullError if the collection is full. ```rust // --- Collection: NVM set of fixed-size items --- #[derive(Copy, Clone)] struct Entry { pub key: u32, pub value: [u8; 8] } #[unsafe(link_section = ".nvm_data")] static mut ENTRIES: NVMData> = NVMData::new(Collection::new(Entry { key: 0, value: [0u8; 8] })); fn add_entry(e: &Entry) { let data = &raw mut ENTRIES; let col = unsafe { (*data).get_mut() }; let _ = col.add(e); // returns Err(StorageFullError) if full } fn list_entries() { let data = &raw const ENTRIES; let col = unsafe { (*data).get_ref() }; for item in col { // implements IntoIterator let _ = item.key; } } ``` -------------------------------- ### Build Ledger App with cargo-ledger Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_device_sdk/README.md Builds a Rust application for a specified target device using `cargo-ledger`. Available targets include nanox, nanosplus, stax, flex, and apex_p. ```bash # Build for your target device cargo ledger build nanox # Nano X cargo ledger build nanosplus # Nano S+ cargo ledger build stax # Stax cargo ledger build flex # Flex cargo ledger build apex_p # Apex P # Build and load to device cargo ledger build nanosplus --load ``` -------------------------------- ### Compile-time BIP32 Path Parsing Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use `make_bip32_path` to parse BIP32 path strings into `[u32; N]` arrays entirely at compile time. Invalid formats will cause a compile-time panic. ```rust use ledger_device_sdk::ecc::make_bip32_path; // Evaluated entirely at compile time const ETH_PATH: [u32; 5] = make_bip32_path(b"m/44'/60'/0'/0/0"); const BTC_PATH: [u32; 5] = make_bip32_path(b"m/44'/0'/0'/0/0"); const CUSTOM: [u32; 3] = make_bip32_path(b"m/44'/535348'/0'"); // ETH_PATH == [0x8000002C, 0x8000003C, 0x80000000, 0, 0] // BTC_PATH == [0x8000002C, 0x80000000, 0x80000000, 0, 0] ``` -------------------------------- ### Build with Custom C SDK Path Source: https://github.com/ledgerhq/ledger-device-rust-sdk/blob/develop/ledger_secure_sdk_sys/README.md Specify a custom path to the C SDK during the build process using the `LEDGER_SDK_PATH` environment variable or Cargo's config flag. This is useful if you have a pre-cloned C SDK. ```sh cargo build --target nanosplus --config env.LEDGER_SDK_PATH="../ledger-secure-sdk/" ``` -------------------------------- ### Initialize APDU communication channel with define_comm! and init_comm Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use define_comm! to declare static storage for Comm and init_comm to construct and register a Comm instance with NBGL. This initializes the APDU communication channel for NBGL integration. Only one Comm instance can exist per application. ```rust #![no_std] #![no_main] use ledger_device_sdk::io::{ApduHeader, StatusWords}; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); // Declare static storage for Comm (default 273-byte buffer) ledger_device_sdk::define_comm!(COMM); // Or with a custom buffer size: // ledger_device_sdk::define_comm!(COMM, 512); pub enum Instruction { GetVersion, Sign, } impl TryFrom for Instruction { type Error = StatusWords; fn try_from(h: ApduHeader) -> Result { match h.ins { 0x01 => Ok(Instruction::GetVersion), 0x02 => Ok(Instruction::Sign), _ => Err(StatusWords::BadIns), } } } #[unsafe(no_mangle)] extern "C" fn sample_main() { // Initialize comm and register NBGL callbacks let comm = ledger_device_sdk::nbgl::init_comm(&COMM); comm.set_expected_cla(0xE0); // auto-reject APDUs with wrong CLA loop { let cmd = comm.next_command(); // blocks until a non-BOLOS APDU arrives match cmd.decode::() { Ok(Instruction::GetVersion) => { let _ = cmd.reply(&[0, 1, 0], StatusWords::Ok); } Ok(Instruction::Sign) => { let data = cmd.get_data(); // &[u8] payload let _ = cmd.reply(data, StatusWords::Ok); } Err(sw) => { let _ = cmd.reply(&[], sw); } } } } ``` -------------------------------- ### Event Loop with Comm::next_event Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use this for handling device events and commands in the legacy IO mode. It supports various event types like commands, button presses, and tickers. ```rust use ledger_device_sdk::io::{ApduHeader, Comm, Event, StatusWords}; use core::convert::TryFrom; pub enum Ins { GetPubKey, SignTx, } impl TryFrom for Ins { type Error = StatusWords; fn try_from(h: ApduHeader) -> Result { match h.ins { 0x02 => Ok(Ins::GetPubKey), 0x04 => Ok(Ins::SignTx), _ => Err(StatusWords::BadIns), } } } fn run() { let mut comm = Comm::new().set_expected_cla(0xE0); loop { match comm.next_event::() { Event::Command(Ins::GetPubKey) => { let pubkey = [0x04u8; 65]; // placeholder comm.append(&pubkey); comm.reply_ok(); // sends 0x9000 } Event::Command(Ins::SignTx) => { let data = comm.get_data().unwrap_or(&[]); comm.append(data); comm.reply(StatusWords::Ok); } #[cfg(any(target_os = "nanosplus", target_os = "nanox"))] Event::Button(_btn) => { /* handle button */ } Event::Ticker => { /* periodic tick */ } _ => {} // Ignore other events } } } ``` -------------------------------- ### Verify Address with Metadata Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use NbglAddressReview to display an address for user verification, optionally with extra tag-value fields. The show() method returns true if confirmed, and NbglReviewStatus::show() displays the outcome with the Address status type. ```rust use ledger_device_sdk::nbgl::{Field, NbglAddressReview, NbglGlyph, NbglReviewStatus, StatusType, init_comm}; use include_gif::include_gif; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); let address = "0x1234567890ABCDEF1234567890ABCDEF12345678"; // Simple address review let ok = NbglAddressReview::new() .glyph(&ICON) .review_title("Verify Ethereum address") .review_subtitle("Check carefully before confirming") .show(comm, address); NbglReviewStatus::new().status_type(StatusType::Address).show(comm, ok); // Address review with extra metadata fields let extra = [ Field { name: "Network", value: "Ethereum Mainnet" }, Field { name: "Derivation", value: "m/44'/60'/0'/0/0" }, ]; let ok2 = NbglAddressReview::new() .glyph(&ICON) .review_title("Verify address") .set_tag_value_list(&extra) .show(comm, address); NbglReviewStatus::new().status_type(StatusType::Address).show(comm, ok2); } ``` -------------------------------- ### NbglReview Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Presents a list of `Field { name, value }` pairs for user approval. Supports standard, light, and blind-signing modes. The `show()` method returns `true` if the user approved. Follow with `NbglReviewStatus::show()` to display the outcome. ```APIDOC ## `NbglReview` — Transaction field review screen Presents a list of `Field { name, value }` pairs for user approval. Supports standard, light, and blind-signing modes. `show()` returns `true` if the user approved. Follow with `NbglReviewStatus::show()` to display the outcome. ```rust use ledger_device_sdk::nbgl::{Field, NbglGlyph, NbglReview, NbglReviewStatus, init_comm}; use include_gif::include_gif; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); let fields = [ Field { name: "Amount", value: "1.5 ETH" }, Field { name: "To", value: "0xAbCd...1234" }, Field { name: "Gas fee", value: "0.001 ETH" }, Field { name: "Nonce", value: "42" }, ]; // Standard review let approved = NbglReview::new() .titles("Review transaction", "Ethereum transfer", "Sign transaction") .glyph(&ICON) .show(comm, &fields); NbglReviewStatus::new().show(comm, approved); // Blind signing (shows a warning before the review) let approved_blind = NbglReview::new() .titles("Review transaction", "Unverified contract", "Sign transaction") .glyph(&ICON) .blind() .show(comm, &fields); NbglReviewStatus::new().show(comm, approved_blind); } ``` ``` -------------------------------- ### Embed PNG/GIF for NBGL Devices Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Uses the include_gif! macro to embed PNG or GIF files as byte arrays for NBGL (touchscreen) devices at compile time. Specify NBGL as the second argument. ```rust use include_gif::include_gif; use ledger_device_sdk::nbgl::NbglGlyph; // Embed a PNG for NBGL (touchscreen) devices #[cfg(any(target_os = "stax", target_os = "flex"))] const APP_ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); // Embed a small PNG for Nano devices #[cfg(any(target_os = "nanosplus", target_os = "nanox"))] const APP_ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_14x14.png", NBGL)); // Embed for Apex P #[cfg(target_os = "apex_p")] const APP_ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_48x48.png", NBGL)); ``` -------------------------------- ### Ed25519 Signing and SLIP10 Derivation Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Demonstrates Ed25519 signing using standard BIP32 and SLIP10 derivation paths. Supports incremental signing for large messages. Ensure correct path formatting for derivation. ```rust use ledger_device_sdk::ecc::{Ed25519, SeedDerive, Ed25519Stream, make_bip32_path}; use ledger_secure_sdk_sys::CX_SHA512; const PATH: [u32; 5] = make_bip32_path(b"m/44'/535348'/0'/0/0"); fn eddsa_example(msg: &[u8]) { // Standard BIP32 derivation let sk = Ed25519::derive_from_path(&PATH); let pk = sk.public_key().unwrap(); let (sig, sig_len) = sk.sign(msg).expect("signing failed"); assert!(pk.verify((&sig, sig_len), msg, CX_SHA512)); // SLIP10 (fully-hardened paths like m/44'/501'/0'/0') let slip10_path: [u32; 4] = make_bip32_path(b"m/44'/501'/0'/0'"); let sk_slip10 = Ed25519::derive_from_path_slip10(&slip10_path); // Streaming (incremental) signing for large messages let mut streamer = Ed25519Stream::default(); streamer.init(&sk).unwrap(); streamer.sign_update(b"chunk one ").unwrap(); streamer.sign_update(b"chunk two").unwrap(); streamer.sign_finalize(&sk).unwrap(); // computes R streamer.sign_update(b"chunk one ").unwrap(); streamer.sign_update(b"chunk two").unwrap(); streamer.sign_finalize(&sk).unwrap(); // computes S // streamer.signature: [u8; 64] — the final EdDSA signature } ``` -------------------------------- ### Paginated Transaction Review with NbglStreamingReview Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use for large transactions that exceed single buffer capacity. Fields are streamed in chunks. Supports skippable pages and blind signing warnings. ```rust use ledger_device_sdk::nbgl::{ Field, NbglGlyph, NbglReviewStatus, NbglStreamingReview, NbglStreamingReviewStatus, TransactionType, init_comm, }; use include_gif::include_gif; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); // Simulated large transaction fields fetched in chunks let all_fields = [ Field { name: "Param 1", value: "Value 1" }, Field { name: "Param 2", value: "Value 2" }, Field { name: "Param 3", value: "Value 3" }, ]; let review = NbglStreamingReview::new() .glyph(&ICON) .skippable() // allow user to skip remaining fields .tx_type(TransactionType::Transaction); if !review.start("Review contract call", Some("ERC-20 Transfer")) { NbglReviewStatus::new().show(comm, false); ledger_device_sdk::exit_app(0); } let mut result: Option = None; for chunk in all_fields.chunks(1) { match review.next(chunk) { NbglStreamingReviewStatus::Next => {} // keep going NbglStreamingReviewStatus::Skipped => { // user pressed skip result = Some(review.finish("Approve transaction")); break; } NbglStreamingReviewStatus::Rejected => { // user rejected result = Some(false); break; } } } let success = result.unwrap_or_else(|| review.finish("Approve transaction")); NbglReviewStatus::new().show(comm, success); ledger_device_sdk::exit_app(0); } ``` -------------------------------- ### Ed25519 Signing and SLIP10 Derivation Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Demonstrates Ed25519 signing using standard BIP32 and SLIP10 derivation paths, as well as incremental signing with Ed25519Stream. ```APIDOC ## `Ed25519` — EdDSA signing and SLIP10 derivation `Ed25519::derive_from_path` uses standard BIP32 derivation; `Ed25519::derive_from_path_slip10` uses SLIP10 (required for fully-hardened paths). `sign` produces a 64-byte EdDSA signature. `Ed25519Stream` supports incremental multi-chunk signing. ```rust use ledger_device_sdk::ecc::{Ed25519, SeedDerive, Ed25519Stream, make_bip32_path}; use ledger_secure_sdk_sys::CX_SHA512; const PATH: [u32; 5] = make_bip32_path(b"m/44'/535348'/0'/0/0"); fn eddsa_example(msg: &[u8]) { // Standard BIP32 derivation let sk = Ed25519::derive_from_path(&PATH); let pk = sk.public_key().unwrap(); let (sig, sig_len) = sk.sign(msg).expect("signing failed"); assert!(pk.verify((&sig, sig_len), msg, CX_SHA512)); // SLIP10 (fully-hardened paths like m/44'/501'/0'/0') let slip10_path: [u32; 4] = make_bip32_path(b"m/44'/501'/0'/0'"); let sk_slip10 = Ed25519::derive_from_path_slip10(&slip10_path); // Streaming (incremental) signing for large messages let mut streamer = Ed25519Stream::default(); streamer.init(&sk).unwrap(); streamer.sign_update(b"chunk one ").unwrap(); streamer.sign_update(b"chunk two").unwrap(); streamer.sign_finalize(&sk).unwrap(); // computes R streamer.sign_update(b"chunk one ").unwrap(); streamer.sign_update(b"chunk two").unwrap(); streamer.sign_finalize(&sk).unwrap(); // computes S // streamer.signature: [u8; 64] — the final EdDSA signature } ``` ``` -------------------------------- ### Comm::next_event / Comm::next_command Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Handles the event loop for legacy I/O. `next_event` retrieves various events like commands, button presses, or tickers. `next_command` specifically filters for command events. ```APIDOC ## `Comm::next_event` / `Comm::next_command` — Event loop (legacy io) Without the `io_new` feature, use the legacy `Comm` struct. `next_event` returns `Event::Command(T)`, `Event::Button(ButtonEvent)` (Nano devices), `Event::TouchEvent` (touchscreen devices), or `Event::Ticker`. `next_command` discards non-command events. ```rust use ledger_device_sdk::io::{ApduHeader, Comm, Event, StatusWords}; use core::convert::TryFrom; pub enum Ins { GetPubKey, SignTx, } impl TryFrom for Ins { type Error = StatusWords; fn try_from(h: ApduHeader) -> Result { match h.ins { 0x02 => Ok(Ins::GetPubKey), 0x04 => Ok(Ins::SignTx), _ => Err(StatusWords::BadIns), } } } fn run() { let mut comm = Comm::new().set_expected_cla(0xE0); loop { match comm.next_event::() { Event::Command(Ins::GetPubKey) => { let pubkey = [0x04u8; 65]; // placeholder comm.append(&pubkey); comm.reply_ok(); // sends 0x9000 } Event::Command(Ins::SignTx) => { let data = comm.get_data().unwrap_or(&[]); comm.append(data); comm.reply(StatusWords::Ok); } #[cfg(any(target_os = "nanosplus", target_os = "nanox"))] Event::Button(_btn) => { /* handle button */ } Event::Ticker => { /* periodic tick */ } _ => {} // Other events are ignored } } } ``` ``` -------------------------------- ### NbglAddressReview Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Displays an address for the user to verify on the device screen. Optionally shows extra tag-value fields. Returns `true` if the user confirms. ```APIDOC ## `NbglAddressReview` — Address verification screen Displays an address for the user to verify on the device screen. Optionally shows extra tag-value fields. Returns `true` if the user confirms. ```rust use ledger_device_sdk::nbgl::{Field, NbglAddressReview, NbglGlyph, NbglReviewStatus, StatusType, init_comm}; use include_gif::include_gif; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); let address = "0x1234567890ABCDEF1234567890ABCDEF12345678"; // Simple address review let ok = NbglAddressReview::new() .glyph(&ICON) .review_title("Verify Ethereum address") .review_subtitle("Check carefully before confirming") .show(comm, address); NbglReviewStatus::new().status_type(StatusType::Address).show(comm, ok); // Address review with extra metadata fields let extra = [ Field { name: "Network", value: "Ethereum Mainnet" }, Field { name: "Derivation", value: "m/44'/60'/0'/0/0" }, ]; let ok2 = NbglAddressReview::new() .glyph(&ICON) .review_title("Verify address") .set_tag_value_list(&extra) .show(comm, address); NbglReviewStatus::new().status_type(StatusType::Address).show(comm, ok2); } ``` ``` -------------------------------- ### Atomic NVM Storage for Scalar Counter Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Demonstrates using AtomicStorage for a mutable scalar counter stored in NVM. Ensure correct unsafe block usage for NVM data access. ```rust use ledger_device_sdk::NVMData; use ledger_device_sdk::nvm::{AtomicStorage, Collection, SingleStorage}; // --- Scalar counter stored atomically in NVM --- #[unsafe(link_section = ".nvm_data")] static mut COUNTER: NVMData> = NVMData::new(AtomicStorage::new(&0u32)); fn increment_counter() { let data = &raw mut COUNTER; let storage = unsafe { (*data).get_mut() }; let current = *storage.get_ref(); unsafe { storage.update(&(current + 1)) }; } ``` -------------------------------- ### Register a panic handler with set_panic! Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Every Ledger app must define a #[panic_handler]. The set_panic! macro wires a function with the signature fn(&PanicInfo) -> ! as the handler. exiting_panic is the standard built-in that replies with StatusWords::Panic and exits the app. ```rust #![no_std] #![no_main] ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); // Custom panic handler example: // ledger_device_sdk::set_panic!(|info| { // ledger_device_sdk::exiting_panic(info); // }); ``` -------------------------------- ### Verify Signature with Ledger PKI Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Validates a signature against a hash using Ledger's PKI. Used for authenticating external data. Requires payload, signature, key usage, and curve ID. ```rust use ledger_device_sdk::ecc::CurvesId; use ledger_device_sdk::hash::{HashInit, sha2::Sha2_256}; use ledger_device_sdk::pki::pki_check_signature; fn verify_payload(payload: &[u8], signature: &mut [u8]) -> bool { // Hash the payload let mut hasher = Sha2_256::new(); let mut hash = [0u8; 32]; hasher.hash(payload, &mut hash).expect("hash failed"); // Verify signature against Ledger PKI (key_usage = 8) match pki_check_signature(&mut hash, 8u8, CurvesId::Secp256k1, signature) { Ok(()) => true, Err(_) => false, } } ``` -------------------------------- ### Review Transaction Fields Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Use NbglReview to present transaction fields for user approval. Supports standard, light, and blind-signing modes. The show() method returns true if approved, and NbglReviewStatus::show() displays the outcome. ```rust use ledger_device_sdk::nbgl::{Field, NbglGlyph, NbglReview, NbglReviewStatus, init_comm}; use include_gif::include_gif; ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); ledger_device_sdk::define_comm!(COMM); #[unsafe(no_mangle)] extern "C" fn sample_main() { let comm = init_comm(&COMM); #[cfg(any(target_os = "stax", target_os = "flex"))] const ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); let fields = [ Field { name: "Amount", value: "1.5 ETH" }, Field { name: "To", value: "0xAbCd...1234" }, Field { name: "Gas fee", value: "0.001 ETH" }, Field { name: "Nonce", value: "42" }, ]; // Standard review let approved = NbglReview::new() .titles("Review transaction", "Ethereum transfer", "Sign transaction") .glyph(&ICON) .show(comm, &fields); NbglReviewStatus::new().show(comm, approved); // Blind signing (shows a warning before the review) let approved_blind = NbglReview::new() .titles("Review transaction", "Unverified contract", "Sign transaction") .glyph(&ICON) .blind() .show(comm, &fields); NbglReviewStatus::new().show(comm, approved_blind); } ``` -------------------------------- ### Cryptographic Hashing Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Provides functions for various cryptographic hash algorithms including SHA-2, SHA-3, Keccak, BLAKE2, and RIPEMD, supporting both one-shot and incremental hashing. ```APIDOC ## `hash` module — Cryptographic hashing (SHA-2, SHA-3, Keccak, BLAKE2, RIPEMD) All hash types implement the `HashInit` trait. `hash()` hashes a single input in one call. `update()` + `finalize()` handle incremental / chunked hashing. Available types: `Sha2_224`, `Sha2_256`, `Sha2_384`, `Sha2_512`, `Sha3_256`, `Sha3_512`, `Keccak256`, `Keccak512`, `Blake2b`, `Ripemd160`. ```rust use ledger_device_sdk::hash::{HashInit, sha2::Sha2_256, sha3::Keccak256, sha2::Sha2_512}; fn hash_examples(data: &[u8]) { // One-shot SHA-256 let mut sha256 = Sha2_256::new(); let mut digest = [0u8; 32]; sha256.hash(data, &mut digest).expect("hash failed"); // digest now holds SHA-256(data) // Incremental Keccak-256 (Ethereum hashing) let mut keccak = Keccak256::new(); let mut out = [0u8; 32]; keccak.update(&data[..data.len() / 2]).unwrap(); keccak.update(&data[data.len() / 2..]).unwrap(); keccak.finalize(&mut out).unwrap(); // SHA-512 let mut sha512 = Sha2_512::new(); let mut out512 = [0u8; 64]; sha512.hash(data, &mut out512).unwrap(); let _ = sha256.get_size(); // returns 32 } ``` ``` -------------------------------- ### HMAC Operations Source: https://context7.com/ledgerhq/ledger-device-rust-sdk/llms.txt Details HMAC operations for Ripemd160, SHA-256, and SHA-512, supporting both one-call computation and streaming updates. ```APIDOC ## `hmac` module — HMAC (Ripemd160, SHA-256, SHA-512) All HMAC types implement the `HMACInit` trait. `hmac()` computes in one call; `update()` + `finalize()` support streaming. Available types: `hmac::ripemd::Ripemd160`, `hmac::sha2::HmacSha2_256`, `hmac::sha2::HmacSha2_512`. ```rust use ledger_device_sdk::hmac::{HMACInit, sha2::HmacSha2_256, ripemd::Ripemd160}; fn hmac_examples(key: &[u8], msg: &[u8]) { // One-shot HMAC-SHA256 let mut mac = HmacSha2_256::new(key); let mut out = [0u8; 32]; mac.hmac(msg, &mut out).expect("hmac failed"); // Streaming HMAC-RIPEMD160 let mut mac2 = Ripemd160::new(key); let mut out2 = [0u8; 20]; mac2.update(&msg[..4]).unwrap(); mac2.update(&msg[4..]).unwrap(); let len = mac2.finalize(&mut out2).unwrap(); // returns MAC length assert_eq!(len, 20); } ``` ```