decred / dcrwallet

A secure Decred wallet daemon written in Go (golang).
https://decred.org
ISC License
219 stars 155 forks source link

Feature to share address privkeys with VSPs #1613

Open itswisdomagain opened 4 years ago

itswisdomagain commented 4 years ago

This issue is the result of collaborative research between myself and @raedah, taking into account suggestions and inputs from various other devs in chat.

Current approach to sharing ticket voting rights with VSPs

In order for VSPs to vote on user-purchased tickets, they need to have voting rights to the ticket address. Currently, VSPs generate 1-of-2 multisigs using pubkey addresses derived from the user's wallet and the VSPs' voting wallet.

Problem - Privacy onchain with tickets purchased using VSPs

A significant problem with the current VSP system is voting address reuse per user, making ticket correlation possible which impacts privacy. The solution proposed below recommends solving this problem by providing a means for users to export and share different voting address privkeys with VSPs. VSPs will however need to first migrate away from a user account-based system in order to support multiple voting addresses per user. https://github.com/decred/dcrstakepool/issues/568 presents details of this transition.

Another problem with the current approach is described by @matheusd in https://github.com/decred/dcrwallet/issues/1217:

"One downside of the current voting pool integration is that you're required to backup the redeem script for your tickets of a given pool before at least one vote or revoke has been published for them, since it's possible (even though unlikely) that both the pool and your wallet are lost before that happens and you need to send a vote or revocation tx (which requires having access to the script).

This adds another bit of information that a conscientious user needs to back up besides their seed, and this is not very prominently advertised in our documentation and UI."

Proposed Solution

It was proposed in the issue referenced above "to use a separate branch of the bip32 wallet (possibly starting at a different purpose branch) to provide the source of data to share to the voting pool".

This would make it possible for users to regain voting rights to VSP tickets while restoring their wallet from seed, without depending on a VSP to retrieve redeem scripts and without having to maintain a separate record of VSP-generated redeem scripts.

It was initially proposed to allow users share extended private keys with VSPs. Instead of one multisig address per user, VSPs would store the user-shared xpriv per user account; and use this xpriv to derive different voting addresses for the same user as a step towards reducing ticket correlation but this only partly improves anonymity as VSPs will continue to be able to link multiple tickets to a user.

It was later decided to export and share single address private keys per ticket with VSPs instead of an xpriv.

The proposed solution (as revised) is to derive ticket/voting addresses off the user's HD wallet and the private key for each ticket address shared with a VSP. With the ticket/voting address coming solely from the user's HD wallet, the private key for ticket addresses can be re-derived during wallet restoration from seed without needing to communicate with any VSP.

Discussions around this proposed solution has birthed the following 3 possible approaches:

Proposed Approach 1 - Derive ticket/voting addresses from a new BIP43 purpose field of the user's HD wallet

The derivation path for addresses derived using this approach will be m / <purpose_field>' / <address_index>'. It has been suggested to use "vsp" unicode value (14679) as number. Hardened derivation is to be used on both purpose field and address levels of this HD path.

Pros

Cons

Proposed Approach 2 - Derive ticket/voting addresses from a custom BIP44 branch index of the user's BIP32 wallet

The derivation path for addresses derived using this approach will be m / 44' / <coin_type>' / <account>' / <custom_branch>' / <address_index>'. A custom branch index (other than 0 and 1 which are reserved as defined in the BIP44 spec) will need to be selected. Also, hardened derivation will be used at the branch and address levels to eliminate the possibility of an attacker being able to derive the extended privkey for this account given access to the account's xpub and any child address privkey.

Additionally (perhaps optionally), as noted by @davecgh, dcrwallet should also "not allow exporting the extended public key associated with the hardened branch as a matter of safety because, while using hardened address indexes will prevent a mailicious actor from reconstructing the associated extended private key should they happen to get ahold of the private key shared with a VSP and the extended public key of the hardened branch, the extended public key alone will still leak all of the address used for the voting addresses and thus destory privacy, especially if you're mixing."

Aside from deciding on a constant custom branch index to use, there's also a need to decide the account index off of which the branch and addresses will be derived using this approach. 2 possibilities exist here:

Pros

Cons

Proposed Approach 3 - Derive ticket/voting addresses from a regular BIP44 account of the user's BIP32 wallet

The derivation path for addresses derived using this approach will be m / 44' / <coin_type>' / <reserved_account>' / 0 / <address_index>. An account number will need to be dedicated for this purpose as is done for the imported account. An alternative that has been mentioned is to allow users decide which account to set aside for this purpose; and during wallet restore from seed, we rely on the nature of tx ntfns gotten to infer the voting account.

Pros

Cons

Recommended Approach - Approach 2

Approach 2 - Derive ticket/voting addresses from a custom BIP44 branch index stands out as requiring the least amount of work to implement while providing security against malicious re-calculation of account extended keys from xpubs and any child address privkey. For this recommended implementation, each ticket's voting address and shareable privkey would be derived off the custom branch index of the same account used to purchase the ticket.

xaur commented 4 years ago

Great work compiling this together.

I like sharing individual ticket voting privkeys with the VSP more than other solutions seen so far:

(please correct if I got it wrong)

Can't tell which approach is better, but I'd like to have all privkeys that are shared with a 3rd party to be somehow isolated from the rest of wallet keys.

From perspective of the user that doesn't mind reseeding, what are the advantages of Approach 1 over Approach 2?

jrick commented 4 years ago

I'm generally leaning towards some solution with a new purpose key (requires reseed, but only if you require these keys).

From perspective of the user that doesn't mind reseeding, what are the advantages of Approach 1 over Approach 2?

For implementers, it makes the code a little clearer (and possibly reusable, if other wallets are ported, they can continue using their existing BIP0044 designs with minimal changes) since we would not be modifying or extending BIP0044 with what it does not specify. We also need to take into consideration that further designs or versions of BIP0044 may build on the current BIP0044 by using extra branches, and we would be unable to satisify these requirements if we impede on these branches.

By separating the current account handling from these keys, we also can prevent a lot of misuse of these keys just by the design, e.g. by not allowing xpubs to be exported.

Key handling in the wallet db is already abstract enough that we don't really need to worry about where the keys are derived from just to save them, as they can be derived on the fly using just their path and a parent key.

Finally, extending BIP0044 may mean complicating the account and address discovery phases especially if we use new branches under existing BIP0044 accounts. This would increase the cost to discover used accounts by 50%, and would similarly increase the cost to rediscover each branch's last used address as there would be more branches to check.

The only downside I see is the requirement to reseed or reenter the seed, but this input can be deferred until it is actually needed to use this feature. Non-stakers and solo stakers would not need to use these keys.

xaur commented 4 years ago

I always prefer "do it cleaner now" over "keep suffering later". Decred is 3 years old, but on the other hand, it is still not big/popular enough to make such changes too breaking.

If we go with the reseed/seed re-entry, we will upset the users with the migration pain but we can plan to minimize it: