What changed, and why it matters
This small code change fixes how the Bitcoin app builds extended public keys (xpubs) for the very first account level (called 'L1', meaning a BIP32 path with just one number, like m/44'). Before, the code always tried to look up a parent public key one level above the requested path. For a single-level path, there is no usable parent above it except the master key, so that lookup could fail or behave incorrectly. The patch adds a special case: when the path has exactly one element, it uses the master key's fingerprint as the parent fingerprint instead of trying to derive a non-existent parent. This is a correctness/reliability fix rather than an obvious security vulnerability, but a broken xpub could mislead wallet software about which key hierarchy it is dealing with.
Treat as a low-severity correctness fix. Review whether any prior firmware version produced xpubs with an incorrect parent fingerprint for L1 paths, and assess whether wallet integrations could have cached or relied on those xpubs. No immediate exploit is evident from the diff alone, but the fix should be included in the next release and noted in release notes.
Security signals we found
Incorrect parent fingerprint in extended public key serialization
BIP32 path edge case (path length 1 / L1 account level)
Master key fingerprint used as fallback for top-level derivation
No explicit bounds or authorization changes in the diff
Evidence from the diff
In src/crypto.c, get_extended_pubkey_at_path() previously called crypto_get_compressed_pubkey_at_path(bip32_path, bip32_path_len - 1, …) unconditionally when bip32_path_len > 0. For bip32_path_len == 1, that call derives at index 0 of the path array, not the master key, so the parent fingerprint was computed from the wrong key. The patch branches: if bip32_path_len == 1, parent_fingerprint is set via crypto_get_master_key_fingerprint(); otherwise it keeps the old parent-key derivation and fingerprinting. This makes L1 xpubs (e.g., account-level xpubs with a path length of 1) include the correct parent fingerprint (the master key fingerprint) in the serialized xpub, matching BIP32 serialization expectations.
Changed components
src/crypto.cget_extended_pubkey_at_path()BIP32 extended public key (xpub) derivationInspect captured patch +15 / −12
diff --git a/src/crypto.c b/src/crypto.c
index d827748..f91f7f7 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -328,19 +328,22 @@ cx_err_t get_extended_pubkey_at_path(const uint32_t bip32_path[],
cx_err_t error = CX_OK;
if (bip32_path_len > 0) {
- // here we reuse the storage for the parent keys that we will later use
- // for the response, in order to save memory
-
- uint8_t parent_pubkey[33];
- error = crypto_get_compressed_pubkey_at_path(bip32_path,
- bip32_path_len - 1,
- parent_pubkey,
- NULL);
- if (error != CX_OK) {
- return error;
+ if (bip32_path_len == 1) {
+ // In te case of L1 path the parent is the master key so we use special function
+ parent_fingerprint = crypto_get_master_key_fingerprint();
+ } else {
+ uint8_t parent_pubkey[33];
+ error = crypto_get_compressed_pubkey_at_path(bip32_path,
+ bip32_path_len - 1,
+ parent_pubkey,
+ NULL);
+ if (error != CX_OK) {
+ PRINTF("%s: returning %u\n", __func__, error);
+ return error;
+ }
+
+ parent_fingerprint = crypto_get_key_fingerprint(parent_pubkey);
}
-
- parent_fingerprint = crypto_get_key_fingerprint(parent_pubkey);
child_number = bip32_path[bip32_path_len - 1];
}
Why this scored 29/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.