Removing DERIVE_MASTER flag, enforcing the derivation paths
What changed, and why it matters
This commit removes a special 'derive master key' permission from the Ledger Bitcoin app and replaces direct master-key derivation with a safer OS-provided fingerprint. It also enforces BIP-44 coin-type restrictions (mainnet 0' or testnet 1') at app-load time, closing a path where the app could previously be asked to derive keys outside those intended coin types. The change is defensive: it reduces the app's privileges and makes path enforcement stricter.
Treat as a security-hardening fix. Users should upgrade to a build containing this commit. Developers should verify that no other code paths still rely on DERIVE_MASTER or on deriving from an empty BIP32 path, and confirm that the OS-level path restrictions are correctly applied for both mainnet and testnet builds.
Security signals we found
Removal of high-privilege HAVE_APPLICATION_FLAG_DERIVE_MASTER flag
Enforcement of BIP-44 coin_type derivation paths via PATH_APP_LOAD_PARAMS
Replacement of empty-BIP32-path master-key derivation with OS master-key identifier API
Deletion of TODO comment noting that path restrictions were not previously enforced
Defensive hardening of key derivation surface
Evidence from the diff
The patch removes HAVE_APPLICATION_FLAG_DERIVE_MASTER from the Makefile and deletes PATH_APP_LOAD_PARAMS = “” (which allowed any path). It then sets PATH_APP_LOAD_PARAMS to ‘/0’’ for mainnet and ‘/1’’ for testnet, restricting derivation to the correct BIP-44 coin_type. In src/crypto.c, crypto_get_master_key_fingerprint() no longer derives the master public key via an empty BIP32 path; instead it calls os_perso_get_master_key_identifier() and returns the first four bytes of the resulting identifier. This eliminates a code path that relied on the DERIVE_MASTER flag.
Changed components
Ledger Bitcoin app Makefile build configurationLedger Bitcoin app BIP32/BIP44 derivation path enforcementsrc/crypto.c master key fingerprint computationInspect captured patch +14 / −22
diff --git a/Makefile b/Makefile
index 71bb424..c39c028 100644
--- a/Makefile
+++ b/Makefile
@@ -19,26 +19,9 @@ ifeq ($(BOLOS_SDK),)
$(error Environment variable BOLOS_SDK is not set)
endif
-# TODO: Compile with the right path restrictions
-#
-# The right path restriction would be something like
-# --path "*'/0'"
-# for mainnet, and
-# --path "*'/1'"
-# for testnet.
-#
-# That is, restrict the BIP-44 coin_type, but not the purpose.
-# However, such wildcards are not currently supported by the OS.
-#
-# Note that the app still requires explicit user approval before exporting
-# any xpub outside of a small set of allowed standard paths.
-
# Application allowed derivation curves.
CURVE_APP_LOAD_PARAMS = secp256k1
-# Application allowed derivation paths.
-PATH_APP_LOAD_PARAMS = ""
-
# Allowed SLIP21 paths
PATH_SLIP21_APP_LOAD_PARAMS = "LEDGER-Wallet policy"
@@ -73,12 +56,14 @@ endif
########################################
# Application custom permissions #
########################################
-HAVE_APPLICATION_FLAG_DERIVE_MASTER = 1
HAVE_APPLICATION_FLAG_GLOBAL_PIN = 1
HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1
HAVE_APPLICATION_FLAG_LIBRARY = 1
ifeq ($(COIN),bitcoin_testnet)
+ # Application allowed derivation paths (testnet).
+ PATH_APP_LOAD_PARAMS = "*/1'"
+
# Bitcoin testnet, no legacy support
DEFINES += BIP32_PUBKEY_VERSION=0x043587CF
DEFINES += BIP44_COIN_TYPE=1
@@ -89,6 +74,9 @@ ifeq ($(COIN),bitcoin_testnet)
APPNAME = "Bitcoin Test"
else ifeq ($(COIN),bitcoin)
+ # Application allowed derivation paths (mainnet).
+ PATH_APP_LOAD_PARAMS = "*/0'"
+
# the version for performance tests automatically approves all requests
# there is no reason to ever compile the mainnet app with this flag
ifneq ($(AUTOAPPROVE_FOR_PERF_TESTS),0)
diff --git a/src/crypto.c b/src/crypto.c
index 58cff67..32f477a 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -271,10 +271,14 @@ uint32_t crypto_get_key_fingerprint(const uint8_t pub_key[static 33]) {
}
uint32_t crypto_get_master_key_fingerprint() {
- uint8_t master_pub_key[33];
- uint32_t bip32_path[] = {};
- crypto_get_compressed_pubkey_at_path(bip32_path, 0, master_pub_key, NULL);
- return crypto_get_key_fingerprint(master_pub_key);
+ uint8_t master_key_identifier[CX_RIPEMD160_SIZE] = {0};
+
+ int res = os_perso_get_master_key_identifier(master_key_identifier, CX_RIPEMD160_SIZE);
+ LEDGER_ASSERT(
+ res == CX_OK,
+ "Unexpected error in os_perso_get_master_key_identifier computation. Returned: %d",
+ res);
+ return read_u32_be(master_key_identifier, 0);
}
bool crypto_derive_symmetric_key(const char *label, size_t label_len, uint8_t key[static 32]) {
Why this scored 61/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.