← index

Avoiding xpub+derivation reuse across wallets, in a UX-friendly manner

An archive of delvingbitcoin.org · view original topic →

Kevin Loaec · #1 ·

Introduction:

Signing devices can be reused for multiple wallets, but xpubs+derivation should not. Some simple examples are when rotating to a different wallet to change one key in a multisig setup, or when rotating the wallet to push an absolute timelock date. For privacy, it is important to not reuse xpubs+derivation even if the same devices are still present in the setup.

Moreover, it is unreasonable to assume users (will) stick to a single software, they might use a multisig on one, a timelocked wallet on another…

It is also unreasonable to assume a user remembers or knows every (any?) paths they have used, and to remember to increase their derivation path each time they create a new wallet.

I’m writing this post as we are implementing “multi wallet” in Liana and I am not happy with any commonly used solution to avoid xpub reuse.

The issue:

One should not reuse xpubs+derivation across different wallets, it is bad for privacy. A signing device should be easy to reuse for different wallets without requiring the user to switch keys (passphrase or other), as a different derivation path is enough.

An extra layer of complexity is when dealing with air-gapped signers, the user is now expected to understand the difference between BIP numbers and wallet types.

The current ways this is addressed:

The first is yet another confusing thing users need to learn and keep track of, the second is specific to one software. Key reuse can still happen if reused on a different software.

I am not aware of other solutions currently used in prod, let me know if there are some.

Better UX to avoid key reuse - ideas

Since output descriptors technically supersede “BIPxx” standard derivation paths by explicitly recording the derivation paths used, and because multisig and more advanced/complex wallets shouldn’t rely on brute force reproduction of the script anyway, I believe it is time to think of a better UX than “increase your account number” of BIP48.

That being said, output descriptors also apply to single sig wallets… where users have been taught that keeping their Mnemonic is enough. Moving away from this for single sig wallets will not be trivial. This post is mainly intended at multisig or more advanced wallets, but if we can fix single sig key reuse too, that’d be a win.

I would like to define yet another “standard” that would reasonably guarantee that a user will not reuse keys, even on different software wallets, when they reuse a signing device.

Desired properties:

Some proposed ideas

Hardened path, requires the signing device to generate the wallet

  1. Random path. As “standard” derivation paths are irrelevant to output descriptors, we can simply randomize the paths. [FINGERPRINT/xx’/Random’]. This is the simplest of all solutions and would work pretty great for all cases. For very large signers (belonging to many thousands of wallets, typically cosigners), another derivation layer could be added. [FINGERPRINT/xx’/Random’/Random2’]

  2. Deterministic path. For example using UNIX time at the time the xpub is requested to the device. [FINGERPRINT/xx’/1745925’/432’]. In this example I used the UNIX time. I arbitrarily split it at the 1 000sec digit. It would fit in a single level but will hit the 32-bits limit in 80 years. Another approach would be to use a human readable date, for example [FINGERPRINT/xx’/20250429’/124630’], where the first level is the date, the second is the time in Hours Minutes Seconds. This is quite easy from a UX perspective

  3. Storing state at each xpub request to the signing device. As the same seed can be imported to multiple hardware devices, this state has to be synchronized, therefore be shared with a server (public or not). The “account” can then be increased by 1 for each request. [FINGERPRINT/48’/0’/x’/2’] or whatever format, where x is increased. This method would need to be multi-vendor compatible

Out of these options, my preferred is 2, followed by 1.

1- Random path:

2- Deterministic:

3- State is synchronized:

Other considerations:

For the deterministic wallet, we cannot assume the software will be at the correct time/date. I believe with precision level at the second it should be OK, but it is simple to increase this to a millisec if believed useful. One UX issue I am aware of is that some hardware wallets will display a warning for “non-standard” derivation paths. This is definitely a drawback of these methods, but a much lesser evil than reusing xpubs.

Non-hardened paths, to rotate without access to the signing device

One use-case that multiple wallets are offering now (Casa, Nunchuk, Keeper, Unchained…) is to rotate/migrate wallets for key replacement or timelock management. To make the UX “one click”, some of these wallets are using a non-hardened path, and incrementing that counter.

While this is acceptable, it is currently not a fit for our cross-vendor compatibility. One should be able to import a wallet created on a software, to another, and then rotate. The main issue here is that if a user wants to import a backup from one vendor to another, each vendor will potentially know about all other such wallets of this user (unhardened paths).

I suppose for this specific use case, the Random option is the best one but is becoming quite heavy, adding a few extra levels of non-hardened paths, preventing brute forcing.

Or just avoid rotations without the signing device being present: we stick to hardened paths.

What about single sig!?

That one is a tough one. The problem persists for single sig, as you may reuse a signing device across software/wallets.

Getting rid of the “standard” derivation paths is a battle I know won’t be worth it. If people don’t want descriptors, then they will have to sacrifice something: privacy if they need a sync server for example. But in that case (sync server), why not just use said server to keep an encrypted backup of your descriptor instead?

Other discarded ideas (from myself and others) were:



Thanks for reading, I hope we can find a good way to move forward without the risk of users reusing keys across wallets. The idea here is to come up with a best practice for wallet devs. I don’t pretend to have the perfect answer.

Kevin Loaec · #2 ·

Regarding air gapped wallets, we might want to define a “xpub request” QR from the software to the device, instead of the user typing the derivation.

salvatoshi · #3 ·

Thanks for raising the discussion. I think it’s an important problem, and it will become increasingly more important in the future. I’ll add some of my thoughts for consideration in the discussion.

Save the coin_type'

A little note that while I agree that descriptors mostly supersede the various derivation standards, there’s still value in using hardened derivations to partition key hierarchies. Arguably, of the three level defined in BIP-44 (purpose'/coin_type'/account'), the coin_type' is still quite useful to partition keys across networks/coins. In fact, Ledger (and I suspect other vendors) uses it to disallow the acceptable derivation paths for most apps.

If a BIP-XXX emerges from this discussion, it could still make sense to have XXX'/coin_type' as the first two derivation steps.

Concerns for hardware signing device UX

About the scheme, a concern is that it increases the amount of information content (entropy) of the descriptor. That information has to be displayed to the user, and failure to check it could lead to ransom attacks if the software wallet is compromised (that is: malware tricks the user into registering a descriptor with a wrong backup. Then, all they need to do in order to lock the user out of their funds is to delete their software wallet. When the user tries to recover from their incorrect backup, the attacker can ransom them in exchange for the correct descriptor).

Therefore, this is making a tradeoff: improving privacy by default, but also making the UX a bit worse by default - at a security-critical moment like the on-device descriptor registration).

Unfortunately, minimizing the entropy while at the same time obtaining privacy by default seems to inherently require state - and I don’t think keeping the state on the signing devices is viable.

:light_bulb: Monotonic backups?

A possible solution I was toying with (but certainly nontrivial to implement) could be to have small storage providers that are only trusted to store small amounts of encrypted data. That would kind of generalize what I proposed for descriptors and wallet policies, in that you would backup some extra information, and you’d want to be able to update it dynamically over time (therefore, some more care is needed for the cryptography, compared to the linked scheme).

Information like I used xpub m/xx'/yy'/zz is very small, and monotonically updated over time (you never un-use a used xpub). Therefore, you could (should!) have multiple digital copies of this information, and it is straightforward to reconcile diverging copies: you store the union of the two sets of used xpubs. Since the loss of this information is not catastrophic (at most you end up re-using an xpub), something that works almost always might be good enough in practice.

Small storage providers could be: the user’s own devices; cloud storage accounts; semi-trusted nostr contacts; service providers for wallets (like Liana); etc.

Such a backup system could also be used for other monotonic info wallets might want to store (BIP-329 labels?).

However, building the tooling for such a system (standards, storage types and management, reconciliation) is rather large endeavor which extends much beyond the scope of your post.

benk10 · #4 ·

When dealing with xpubs we have 2 main considerations, privacy and UX. The current BIP43 standard is lacking in both good privacy and UX. It pushes users to reuse pub keys, and also it forces users to manage many xpubs, making a bad UX.

I think UX wise both the random option and the unix options would be a negative.

  1. It would mean requiring the user to connect all devices when they create, rotate, or renew a wallet. This is already a difficult UX when the user has all keys together, but when keys are kept separately (with different people or at different locations), especially common in rotating or renewing, forcing the user to get new xpubs from each signing device would make the process much more complicated (for example a 3 of 5, rotating out 1 key would mean you’d need to first get xpubs from all other 4 keys, not just 3 keys which need to sign the rotation tx).
  2. Showing random numbers or unix time is a very bad UX, we already have users confused with many random numbers throughout the entire process, and adding more of these in the UI will make it even harder to reason about.

From a user perspective, the ideal UX would be to only ever “add” a key/ device to their software once. This would be feasible only if we use unhardened derivations to manage different wallets, but I don’t think this is a big issue nowdays. For normal use cases unhardened derivations should be fine, and the UX gains are significant.

benk10 · #5 ·

Also for single sig, since there is only one way to use it, I think the maintaining the current standard, or keeping something similar where there is still just purpose and account (and of course coin type) derivation is good, with the account and purpose parts unhardened though.

Then for multisig, backing up of the descriptor is already a recommended action, so using Unix time may be a good approach there, but only with unhardened derivation, otherwise the friction with hardware wallet will just make users reuse their xpubs.

Kevin Loaec · #6 · · in reply to #5

I understand the concerns, and would definitely like to find a good way to do it with unhardened paths.

I suppose random works, but as you said it’s another nonsensical thing for the user to check. Not sure if we are willing to accept this compromise.

It could be to keep a “standard” path for export, and add a few unhardened depths of randomness on top.

m/48'/0'/0'/2'/[RANDOM]/[RANDOM]/[RANDOM]/[...]

or equivalent. The export from the signing device would still be the xpub at m/48’/0’/0’/2’, but it would need to be able to accept a descriptor with a few extra unhardened depths for signing and address verification.

I’m not sure it is supported by hardware signers right now. I’m also not sure how many depth we would need to offer a reasonable privacy protection.

I wouldn’t go for unix or date/time completely unhardened, as I want to be able to switch software (and import my descriptor) without breaking the privacy of my other wallets. Unix or Date/time reduces the range too much and would need randomness on top.

Edit: I clarified that the xpub would be still shared on the “standard” path, but then random unhardened derivations could be added on top, on the software/service side, without access to the hardware signer

Sjors Provoost · #7 · · in reply to #6

m/87'/0'/0'/[UNIX-TIME]/{0,1}/* would work well with the backup scheme @salvatoshi proposed.

A date is easy to render in a user friendly way “did you create this wallet on …”.

One downside is that co-signers know your xpub. This is what makes the descriptor backup scheme work. They can’t find your single-sig wallet, as that has different purpose derivation.

It would take them some brute force to find the other multisigs (pick a random date, derive N pub keys, see if they appear in a OP_CHECKSIGADD). If the user uses MuSig2 in these other multisigs and never a recovery path, it would be impossible to find, unless you also know the xpub of their cosigner in the other multisig.

Wallets could recommend that the user manually picks a unique account number. As long as that’s below 1 million or so it wouldn’t break the backup scheme, as during recovery it can iterate through many account numbers.