Wallet Policies for Descriptor Wallets
BIP: 388 Layer: Applications Title: Wallet Policies for Descriptor Wallets
No reviewsSpecification
BIP: 388 Layer: Applications Title: Wallet Policies for Descriptor Wallets Authors: Salvatore IngalaStatus: Complete Type: Specification Assigned: 2022-11-16 License: BSD-2-Clause Discussion: 2022-05-10: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-May/020423.html
Abstract
Software wallets and hardware signing devices typically partition funds into separate "accounts". When signing or visualizing a transaction, aggregate flows of funds of all accounts affected by the transaction may (and should) be displayed to the user.
Wallet policies build on top of output script descriptors to represent such accounts in a compact, reviewable way. An account encompasses a logical group of receive and change addresses, and each wallet policy represents all descriptors necessary to describe an account in its entirety.
We simplify the language to suit devices with limited memory, where even keeping the entire descriptor in memory could be a major hurdle, by reducing the generality of descriptors to just the essential features and by separating the extended pubkeys and other key information from the descriptor.
This results in a more compact representation and simplifies the inspection of the policy by the user.
The compilation of wallet policies to the corresponding descriptor is trivial, and the reverse process is easy for supported descriptors, because the language is kept similar to that of output script descriptors.
Copyright
This BIP is licensed under the BSD 2-clause license.
Motivation
Output Script Descriptors were introduced in Bitcoin Core as a way to represent collections of output scripts. It is a general and flexible language, designed to catch all the possible use-cases of bitcoin wallets (that is, if you know the script and you have the necessary keys, it will be possible to sign transactions with any descriptor-based software wallet).
Unfortunately, descriptors are not a perfect match for the typical usage of hardware signing devices (often also called hardware wallets). Most of them have some of the following limitations when compared to a general-purpose machine running Bitcoin Core:
- they are embedded devices with limited RAM and computational power;
- they cannot import additional private keys (that is, they can only sign with keys derived from a single seed via BIP-32);
- they have limited storage, or they might not have persistent storage at all (stateless design).
A more native, compact representation of the wallet receive and change addresses might also benefit the UX of software wallets when they use descriptors (possibly with miniscript) for representing complex locking conditions.
We remark that wallet policies are not related to the policy language, a higher level language that can be compiled to miniscript.
Security, privacy and UX concerns for hardware signing devices
The usage of complex scripts presents challenges in terms of both security and user experience for a hardware signing device.
Security issues
Hardware signing devices strive to guarantee that no action can be performed without the user’s consent as long as the user correctly verifies the information that is shown on the device’s screen before approving.
This must hold even in scenarios where the attacker has full control of the machine that is connected to the signing device, and can execute arbitrary requests, or tamper with the legitimate user's requests.
Therefore, it is not at all trivial to allow complex scripts, especially if they contain keys that belong to third parties. The hardware signing device must guarantee that the user knows precisely what "policy" is being used to spend the funds, and that any "unspent" funds (if any) that is sent to a change address will be protected by the same policy.
This makes it impossible for an attacker to surreptitiously modify the policy, therefore stealing or burning the user's funds.
Avoiding key reuse
Reusing public keys within a script is a source of malleability when using miniscript policies, which has potential security implications.
Reusing keys across different UTXOs harms user privacy by allowing external parties to link these UTXOs to the same entity once they are spent.
By constraining the derivation path patterns to have a uniform structure, wallet policies prevent key reuse among the same or different UTXOs of the same account.
It is strongly recommended to avoid key reuse across accounts. Distinct public keys per account can be guaranteed by using distinct hardened derivation paths. This specification does not mandate hardened derivation in order to maintain compatibility with existing deployments that do not adhere to this recommendation.
It is out of scope for this document to guarantee that users do not reuse extended public keys among different wallet accounts. This is still very important, but the responsibility is left to the users and their software wallet.
UX issues
Miniscript (and taproot trees) allow substantially more complex spending policies. It is a challenge to ensure that the user can practically verify such spending policies on the screen.
We set two fundamental design goals:
- Minimize the amount of information that is shown on screen - so that the user can actually validate it.
- Minimize the number of times the user has to validate such information.
Policy registration as a solution
A solution to address the security concerns, and part of the UX concerns, is to have a registration flow for the wallet policy in the hardware signing device. The wallet policy must contain enough information to generate all the relevant addresses/scripts, and for the hardware signing device to identify the keys that it controls and that are needed to spend the funds sent to those addresses.
Before a new policy is used for the first time, the user will register a wallet policy into the hardware device. While the details of the process are out of scope in this document, the flow should be something similar to the following:
# The software wallet initiates a wallet policy registration on the hardware signing device; the information should include the wallet policy, but also a unique name that identifies the policy. # The device shows the wallet policy to the user using the secure screen. # After inspecting the policy and comparing it with a trusted source (for example a printed backup), the user approves the policy. # If stateful, the hardware signing device persists the policy in its permanent memory; if stateless, it returns a "proof of registration".
The proof of registration will allow the hardware signer to verify that a certain policy was indeed previously approved by the user, and is therefore safe to use without repeating the expensive user verification procedure. The details of how to create a proof of registration are out of scope for this document; using a Message Authentication Code on a hash committing to the wallet policy, its name and any additional metadata is an effective solution if correctly executed.
Once a policy is registered, the hardware signing device can perform the typical operations securely:
- generating receive and change addresses;
- showing addresses on the secure screen;
- sign transactions spending from a wallet, while correctly identifying change addresses and computing the transaction fees.
Once the previously registered policy is correctly identified and approved by the user (for example by showing its name), and as long as the policy registration was executed securely, hardware signing devices can provide a user experience similar to the usual one for single-signature transactions.
Avoiding blowup in descriptor size
While reusing a pubkey in different branches of a miniscript is explicitly forbidden by miniscript (as it has certain negative security implications), it is still reasonable to reuse the same xpub in multiple places, albeit with different final steps of derivation (so that the actual pubkeys that are used in the script are indeed different).
In fact, there are many reasonable spending policies with a quadratic size in the number of participants. For example, using Taproot, a 3-of-5 threshold wallet could use:
- a key path with a 5-of-5 MuSig2 aggregated key
- a script tree with 11 leaves:
With each xpub being 118 bytes long, the repetition of xpubs makes the descriptor become extremely large.
Replacing the common part of the key with a short key placeholder and organizing all the key expressions in a separate list helps to keep the size of the wallet policy small, which is crucial to allow human inspection during the registration flow.
Specification
This section formally defines wallet policies, and how they relate to output script descriptors.
Formal definition
A wallet policy is composed of a wallet descriptor template, together with a vector of key information items.
Wallet descriptor template
A wallet descriptor template is a SCRIPT expression.
SCRIPT expressions:
- sh(SCRIPT) (top level only): P2SH embed the argument.
- wsh(SCRIPT) (top level or inside sh only): P2WSH embed the argument.
- pkh(KEY) (not inside tr): P2PKH output for the given public key.
- wpkh(KEY) (top level or inside sh only): P2WPKH output for the given compressed pubkey.
- multi(k,KEY_1,KEY_2,...,KEY_n) (inside sh or wsh only): k-of-n multisig script.
- sortedmulti(k,KEY_1,KEY_2,...,KEY_n) (inside sh or wsh only): k-of-n multisig script with keys sorted lexicographically in the resulting script.
- tr(KEY) or tr(KEY,TREE) (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths.
- any valid miniscript template (inside wsh or tr only).
TREE expressions:
- any SCRIPT expression
- An open brace {, a TREE expression, a comma ,, a TREE expression, and a closing brace }
KEY expressions consist of
- a KP expression
- always followed by either:
KP expressions (key placeholders) consist of either:
- a KI (key index) expression, or
- (only inside tr): musig(KI_1,KI_2,...,KI_n)
- a single character @
- followed by a non-negative decimal number, with no leading zeros (except for @0)
Note that while BIP-389 allows multipath /
SCRIPT, TREE and KEY expressions map directly to the corresponding concepts defined in BIP-380 for output script descriptors.
Each KEY expression always corresponds to a precise public key in the final bitcoin Script. Therefore, all the derivation steps in the BIP-32 hierarchy are included in a KEY expression.
Each KP (key placeholder) expression, on the other hand, maps to the root of all the corresponding public keys for all the possible UTXOs that belong to the account represented in the wallet policy. Therefore, no derivation steps are allowed in a KP expression.
A KI (key index) @i for some number i represents the i-th key in the vector of key information items (which must be of size at least i + 1, or the wallet policy is invalid).
Note: while descriptor templates for miniscript are not formally defined in this version of the document (pending standardization), it is straightforward to adapt this approach by adding additional SCRIPT expressions.
Key information vector
Each element of the key origin information vector is a KEY_INFO expression, containing an extended public key, and (optionally) its key origin.
- Optionally, key origin information, consisting of:
- Followed by the actual key, which is a serialized extended public key (as defined in BIP-32).
Additional rules
A wallet policy must have at least one key placeholder and the correspondin
[Content truncated — view full spec at source]
Discussion (0 threads)
Loading discussions...