|
Embedded SDK
Embedded SDK
|
lib_standard_app is a C framework that provides the common infrastructure every Ledger application needs:
All public symbols that have a sensible default are declared WEAK so any application can override them without relinking the whole library.
The only mandatory symbol an application must provide is:
Files: main.c, main_std_app.h
Provides the main() entry point (placed in .boot). On startup it detects the launch mode:
arg0 == 0):standalone_app_main() → common_app_init() → app_main().arg0 != 0, requires HAVE_SWAP):library_app_main() which dispatches SIGN_TRANSACTION, CHECK_ADDRESS, or GET_PRINTABLE_AMOUNT to the coin-application callbacks.| Function | Description |
|---|---|
| app_exit() | Stops I/O and returns to the dashboard via os_sched_exit(-1). Declared noreturn. |
| common_app_init() | Initialises UX, seproxyhal I/O, and — when HAVE_APP_STORAGE is defined — NVM storage. |
| standalone_app_main() | Default standalone entry; resets swap globals, calls common_app_init() then app_main(). |
| library_app_main() | Exchange library dispatcher (requires HAVE_SWAP). |
WEAK and may be overridden by the application.
A portable APDU I/O layer on top of seproxyhal.
The typical receive/dispatch loop in app_main() looks like:
| Function | Description |
|---|---|
| io_init() | Arms the deferred I/O start; must be called once before the first io_recv_command(). |
| io_recv_command() | Blocks until a full APDU is received into G_io_apdu_buffer; returns byte count or -1. |
| io_send_response_buffers(rdatalist, count, sw) | Assembles a response from an array of buffer_t fragments, appends sw, and transmits. |
| io_send_response_pointer(ptr, size, sw) | Convenience wrapper for a raw pointer. |
| io_send_response_buffer(rdata, sw) | Convenience wrapper for a single buffer_t. |
| io_send_sw(sw) | Sends a status-word-only response (no data). |
| app_ticker_event_callback() | Empty WEAK hook called on each ticker event; override to receive periodic ticks. |
HAVE_SWAP is active and G_swap_response_ready == true, io_send_response_buffers() signals Exchange and calls os_lib_end() instead of returning normally.
Parses a raw APDU byte buffer into a structured command_t:
| Function | Description |
|---|---|
| apdu_parser(cmd, buf, buf_len) | Fills cmd from buf. Returns false if the buffer is too short or the declared Lc doesn't match the actual remaining bytes. |
Byte-offset constants (OFFSET_CLA = 0, OFFSET_INS = 1, … OFFSET_CDATA = 5) are defined in offsets.h.
A cursor-based byte-buffer API. All operations advance (or set) the internal offset field and return false on a bounds violation.
Reading
| Function | Description |
|---|---|
| buffer_can_read(b, n) | Returns true if at least n unread bytes remain. |
| buffer_read_u8/u16/u32/u64(b, val, endianness) | Reads 1/2/4/8 bytes and advances the cursor. endianness is BE or LE. |
| buffer_read_varint(b, val) | Reads a Bitcoin-style variable-length integer. |
| buffer_read_bip32_path(b, out, out_len) | Reads out_len big-endian 32-bit BIP32 path components. |
| buffer_get_path_bip32(b, path) | Reads a length-prefixed BIP32 path into a path_bip32_t. |
| buffer_read_bytes(b, out, n) | Reads n raw bytes. |
| buffer_peek(b, val) | Reads the next byte without advancing. |
| buffer_peek_n(b, n, val) | Reads byte at offset+n without advancing. |
Writing
| Function | Description |
|---|---|
| buffer_write_u8/u16/u32/u64(b, val, endianness) | Writes 1/2/4/8 bytes and advances. |
| buffer_write_bytes(b, data, n) | Writes n raw bytes and advances. |
Cursor control
| Function | Description |
|---|---|
| buffer_seek_set(b, offset) | Seeks to an absolute position. |
| buffer_seek_cur(b, offset) | Advances the cursor by offset bytes. |
| buffer_seek_end(b, offset) | Sets the cursor to size - offset. |
| buffer_get_cur(b) | Returns a pointer to the current position. |
Output
| Function | Description |
|---|---|
| buffer_copy(b, out, out_len) | Copies remaining bytes to out without advancing (used for APDU TX). Requires size - offset <= out_len. |
| buffer_move(b, out, out_len) | Copies remaining bytes and advances by out_len. |
Parsing, serialisation, and formatting of BIP32 derivation paths.
MAX_BIP32_PATH is 10. The hardened-child bit mask is BIP32_HARDENED_MASK (0x80000000).
| Function | Description |
|---|---|
| bip32_path_read(in, in_len, out, out_len) | Deserialises out_len big-endian 32-bit components from in. |
| bip32_path_format(path, len, out, out_len) | Formats the path as ‘"0’/1/2'"` (hardened components get a trailing `'`). |
| bip32_path_format_simple(bip32, out, out_len) | Convenience wrapper for path_bip32_t. |
Files: crypto_helpers.h, crypto_helpers.c
High-level wrappers that combine BIP32 key derivation with cryptographic operations. Every function:
cx_err_t and is annotated WARN_UNUSED_RESULT,explicit_bzero on both success and failure,Key derivation
| Function | Description |
|---|---|
| bip32_derive_init_privkey_256(curve, path, path_len, privkey, chain_code) | Derives and initialises an ECFP private key. |
| bip32_derive_get_pubkey_256(curve, path, path_len, raw_pubkey, chain_code, hashID) | Derives the private key and generates the uncompressed 65-byte public key. |
ECDSA signing
| Function | Description |
|---|---|
| bip32_derive_ecdsa_sign_hash_256(curve, path, path_len, sign_mode, hashID, hash, hash_len, sig, sig_len, info) | Signs a pre-computed hash; output is DER-encoded (30‖L‖02‖Lr‖r‖02‖Ls‖s). |
| bip32_derive_ecdsa_sign_rs_hash_256(curve, path, path_len, sign_mode, hashID, hash, hash_len, r, s) | Like above but writes raw r and s into separate 32-byte buffers. |
EdDSA signing
| Function | Description |
|---|---|
| bip32_derive_eddsa_sign_hash_256(curve, path, path_len, hashID, hash, hash_len, sig, sig_len) | Signs with EdDSA; sig_len is updated to domain_param_size * 2. |
Each function has a _with_seed_ variant that accepts a custom seed/seed_len for non-standard SLIP-21 derivations.
Pass NULL, 0 to use the standard BIP32 device seed.
Human-readable string formatting for display on device screens.
| Function | Description |
|---|---|
| format_i64(dst, dst_len, value) | Formats a signed 64-bit integer as decimal. |
| format_u64(dst, dst_len, value) | Formats an unsigned 64-bit integer as decimal. |
| format_fpu64(dst, dst_len, value, decimals) | Fixed-point decimal with decimals fractional digits (e.g. 12345 with decimals=2 → "123.45"). |
| format_fpu64_trimmed(dst, dst_len, value, decimals) | Like format_fpu64() but trims trailing zeros and the decimal point. |
| format_hex(in, in_len, out, out_len) | Uppercase hex dump; requires out_len >= 2*in_len+1. Returns bytes written or -1. |
Bitcoin Base58 alphabet encode/decode.
| Function | Description |
|---|---|
| base58_encode(in, in_len, out, out_len) | Binary → Base58 string; returns character count or -1. Max input: MAX_ENC_INPUT_SIZE (120 bytes). |
| base58_decode(in, in_len, out, out_len) | Base58 string → binary; returns byte count or -1. Max input: MAX_DEC_INPUT_SIZE (164 chars). |
Leading zero bytes are encoded as the ‘'1’` character (Bitcoin convention).
Bitcoin-protocol variable-length integer (1/3/5/9 bytes).
| Function | Description |
|---|---|
| varint_size(value) | Returns the serialised byte count for value. |
| varint_read(in, in_len, value) | Parses a varint; returns bytes consumed or -1. |
| varint_write(out, offset, value) | Writes a varint at out + offset; returns bytes written or -1. |
Files: read.h, read.c, write.h, write.c
Bounds-unchecked integer accessors used internally by buffer.c and varint.c. All functions take a (ptr, offset) pair and read/write big-endian or little-endian integers of width 16, 32, or 64 bits.
Files: app_storage_internal.h, app_storage.c
Requires (in the application Makefile):
Provides a CRC32-protected persistent storage area in NVM (nvm_write), placed in the .storage_section linker section. The on-disk layout is:
The header carries
"NVRA" tag,struct_version,properties bitfield,data_version counter. The CRC is verified at every init();if the check fails the storage is reset.
| Function | Description |
|---|---|
| app_storage_write(buf, nbyte, offset) | Writes nbyte bytes and updates the CRC. Returns nbyte or a negative error code. |
| app_storage_read(buf, nbyte, offset) | Reads nbyte bytes. Returns APP_STORAGE_ERR_NO_DATA_AVAILABLE if offset+nbyte exceeds the written size. |
| app_storage_reset() | Erases the entire data area and resets the header. |
| app_storage_get_size() | Returns the current written data size. |
| app_storage_get_data_version() | Returns the application data version counter. |
| app_storage_increment_data_version() | Increments (and wraps) the data version. |
| app_storage_get_properties() | Returns the properties bitfield (APP_STORAGE_PROP_SETTINGS, APP_STORAGE_PROP_DATA). |
Error codes:
APP_STORAGE_SUCCESS,APP_STORAGE_ERR_INVALID_ARGUMENT,APP_STORAGE_ERR_NO_DATA_AVAILABLE,APP_STORAGE_ERR_OVERFLOW,APP_STORAGE_ERR_INVALID_HEADER,APP_STORAGE_ERR_CORRUPTED.
Files: swap.h (umbrella), swap_entrypoints.h, swap_lib_calls.h, swap_utils.h, swap_utils.c, swap_error_code_helpers.h, swap_error_code_helpers.c
Requires (in the application Makefile):
Integrates with the Ledger Exchange application for atomic cross-currency swaps. When launched in library mode, main() dispatches one of three commands:
| Command constant | Value | Description |
|---|---|---|
CHECK_ADDRESS | 3 | Verify an address belongs to this device. |
GET_PRINTABLE_AMOUNT | 4 | Format an amount for on-screen display. |
SIGN_TRANSACTION | 2 | Sign the swap output transaction. |
The coin application must implement these three functions:
| Global | Type | Description |
|---|---|---|
| G_called_from_swap | volatile bool | true when running as an Exchange library. |
| G_swap_response_ready | volatile bool | Set to true by the coin app when signing is complete; triggers os_lib_end() in io.c. |
| Function | Description |
|---|---|
| swap_str_to_u64(src, length, result) | Converts a big-endian byte array (≤ 8 bytes) to a uint64_t amount. |
| swap_parse_config(config, config_len, ticker, ticker_buf_len, decimals) | Parses the coin_configuration blob: length-prefixed ticker string + decimals byte. |
| Function | Description |
|---|---|
| send_swap_error_simple(sw, common, app_specific) | Sends a swap error APDU with a 2-byte structured error code and terminates (noreturn). |
| send_swap_error_with_buffer(sw, common, app_specific, buffer_data) | Same with one extra data buffer. |
| send_swap_error_with_buffers(sw, common, app_specific, buffers, count) | Same with up to 8 data buffers. |
send_swap_error_with_string(sw, common, app_specific, format, ...) | Same with a printf-style message (macro). |
The 2-byte error code is
Standard common codes are defined by swap_error_common_code_t:
| Enumerator | Value | Meaning |
|---|---|---|
SWAP_EC_ERROR_INTERNAL | 0x00 | Internal firmware error |
SWAP_EC_ERROR_WRONG_AMOUNT | 0x01 | Amount mismatch |
SWAP_EC_ERROR_WRONG_DESTINATION | 0x02 | Destination address mismatch |
SWAP_EC_ERROR_WRONG_FEES | 0x03 | Fee mismatch |
SWAP_EC_ERROR_WRONG_METHOD | 0x04 | Invalid method |
SWAP_EC_ERROR_CROSSCHAIN_WRONG_MODE | 0x05 | Unsupported cross-chain mode |
SWAP_EC_ERROR_CROSSCHAIN_WRONG_METHOD | 0x06 | Invalid cross-chain method |
SWAP_EC_ERROR_CROSSCHAIN_WRONG_HASH | 0x07 | Cross-chain hash mismatch |
SWAP_EC_ERROR_GENERIC | 0xFF | Generic / unspecified error |
File: macros.h
| Macro | Description |
|---|---|
| WEAK | __attribute((weak)) — marks a function as overridable by application code. |
| MEMBER_SIZE(type, member) | Size of a struct member without an instance. |
| ARRAY_LENGTH(array) | Number of elements in a statically allocated array. |
WEAK — allow an application to override a default SDK implementation:
MEMBER_SIZE — size of a struct field without declaring a variable:
ARRAY_LENGTH — number of elements in a stack or global array:
File: tokens.h
| Macro | Value | Description |
|---|---|---|
MAX_TICKER_SIZE | 50 | Maximum buffer size for a token ticker string (including null terminator). |