1
0
mirror of https://github.com/bitcoin/bips.git synced 2026-10-19 19:29:18 +00:00

Merge pull request #2300 from Sjors/2026/09/bip138-recipients

bip138: clarify key selection and apply exclusions across expressions
This commit is contained in:
Murch
2026-09-24 20:19:29 -07:00
committed by GitHub
3 changed files with 168 additions and 64 deletions

View File

@@ -10,8 +10,8 @@
Discussion: https://delvingbitcoin.org/t/a-simple-backup-scheme-for-wallet-accounts/1607
https://groups.google.com/g/bitcoindev/c/5NgJbpVDgEc/m/TtGK9sF9BgAJ
https://github.com/bitcoin/bips/pull/1951
Version: 0.1.2
Requires: 32, 340, 380, 388, 389
Version: 0.1.3
Requires: 32, 340, 380, 388, 389, 390
```
## Introduction
@@ -98,7 +98,8 @@ Note: in the following sections, the operator ⊕ refers to the bitwise XOR oper
### Secret generation
- Let $p_1, p_2, \dots, p_n$, be the public keys in the descriptor/wallet policy, in
- Let $p_1, p_2, \dots, p_n$, be the distinct public keys across all descriptor
and policy sets and all content items in the payload, in
increasing lexicographical order. The scheme is defined for any $n \geq 1$,
in particular it supports single-signature descriptors ($n = 1$). Each $p_i$
is the x-only-normalized root public key of one *allowed* key expression
@@ -113,8 +114,8 @@ hash construction from [BIP340](bip-0340.mediawiki#design).
Because $s$ and $s_i$ use distinct domain-separation tags, $s \neq s_i$ and
therefore $c_i$ is never the all-zero string.
Keys published on chain are already kept out of $\{p_1, \dots, p_n\}$ by the
[Descriptor key requirements](#descriptor-key-requirements).
The [Descriptor key requirements](#descriptor-key-requirements) exclude
keys exposed by literal or bare-xpub expressions in the backed-up scripts.
Implementations MUST exclude the key with x coordinate
`50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0` (the BIP341 NUMS
point). Applications MAY exclude any further key they know to be public.
@@ -138,8 +139,8 @@ accounts whose xpub was never sent to such a server, such as BIP48 or BIP87 acco
### Descriptor key requirements
Only key expressions of the extended-public-key type with a trailing
derivation step or wildcard contribute to $\{p_1, \dots, p_n\}$. Without
Outside `musig()`, only key expressions of the extended-public-key type with a
trailing derivation step or wildcard contribute to $\{p_1, \dots, p_n\}$. Without
that derivation, the xpub's root pubkey, used to seed $s$, would also be
the on-chain pubkey of every spend, so observing one spend would let
anyone recompute $s$ and decrypt the backup.[^trailing-derivation] The
@@ -150,9 +151,9 @@ rule generalizes to any network prefix (`xpub`/`tpub`/`Vpub`/...).
a wildcard, breaks the identity between the xpub root pubkey and the
on-chain pubkey. Fixed-derivation expressions like `xpub.../0/5` are
allowed because the on-chain key (`xpub/0/5`) already differs from the
encryption seed (the xpub root). Literal pubkeys and bare xpubs are
disallowed for the same reason: the literal value is exactly what goes
on-chain, so $s$ would become recoverable from a single observed spend.
encryption seed (the xpub root). Outside `musig()`, literal pubkeys and
bare xpubs are disallowed for the same reason: the literal value is exactly
what goes on-chain, so $s$ would become recoverable from a single observed spend.
Allowed forms (origin information `[...]` is optional and orthogonal):
@@ -163,15 +164,27 @@ Allowed forms (origin information `[...]` is optional and orthogonal):
| `<xpub>/<path>/*` (fixed derivation followed by a wildcard) | `xpub.../0/*` |
| `<xpub>/<a;b;...>` (multipath, no wildcard) | `xpub.../<0;1>` |
| `<xpub>/<a;b;...>/*` (multipath followed by a wildcard) | `xpub.../<0;1>/*` |
| `<xpub>` (only as a `musig()` participant) | `xpub...` |
Within a [BIP390](bip-0390.mediawiki) `musig()` expression, participant xpubs
are allowed with or without trailing derivation: the aggregate on-chain key
does not reveal the individual participant keys. For encryption, use each
participant's xpub root rather than the aggregate key. Literal public-key
participants remain excluded.
A candidate root MUST be excluded if its x-coordinate matches a literal key
or a bare xpub root used outside `musig()` in any descriptor or resolved
policy in the payload. For example,
`wsh(or_i(pk(X/0/*),pk(X)))` exposes the root of xpub `X` on spending, so that
root must be excluded even though `X/0/*` is otherwise eligible.
Implementations:
- MUST exclude every other form (literal pubkeys, bare xpubs, ...) from
$\{p_1, \dots, p_n\}$.
- MUST exclude every other form (literal pubkeys, bare xpubs outside `musig()`,
...) from $\{p_1, \dots, p_n\}$.
- MUST refuse to encode a backup if the resulting set is empty.
- MUST make the user aware of each excluded expression, since the
cosigner holding that key will be unable to decrypt the backup with
their key.
- MUST make the user aware of each excluded expression and candidate root,
since its holder may be unable to decrypt the backup with that key.
### Key Normalization
@@ -590,8 +603,11 @@ The version field enables possible future enhancements:
### Test Vectors
[key_types.json](./bip-0138/test_vectors/keys_types.json) contains test
vectors for key serialisations.
[recipient_keys.json](./bip-0138/test_vectors/recipient_keys.json) contains
test vectors for recipient selection. `descriptors` lists the payload's descriptor
strings; `expected_keys` lists the sorted, deduplicated x-only recipient roots
before application-specific exclusions. `null` requires refusing to encode.
[content_type.json](./bip-0138/test_vectors/content_type.json) contains test
vectors for content types serialisations.
[payload.json](./bip-0138/test_vectors/payload.json) contains test vectors for payload parsing.
@@ -633,6 +649,10 @@ their reviews and suggestions.
## Changelog
* __0.1.3__ (2026-09-24):
* Rename, generalize and expand the key-selection test vectors.
* Clarify MuSig participant eligibility and recipient selection across the payload.
* Exclude roots exposed by another key expression.
* __0.1.2__ (2026-09-24):
* Specify empty AEAD associated data.
* Clarify that `0x00` ends payload parsing and add test vectors.

View File

@@ -1,47 +0,0 @@
[
{
"description": "Xpub with origin and multipath wildcard",
"key": "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>/*",
"expected": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
},
{
"description": "Xpub with single-path wildcard",
"key": "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/*",
"expected": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
},
{
"description": "Xpub with bare wildcard (no preceding path)",
"key": "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/*",
"expected": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
},
{
"description": "Xpub with fixed deep derivation, no wildcard",
"key": "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/5",
"expected": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
},
{
"description": "Xpub with multipath, no wildcard",
"key": "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>",
"expected": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
},
{
"description": "Bare xpub (no derivation, no wildcard); DISALLOWED",
"key": "[d4ab66f1/48'/1'/1'/2']tpubDFTxBKyUCgkwp5enwZh3t2FJ5AMJqmCWoh1NRT13qNYQb1iKTUrAG6u5gpsDYhG8cZGXouYWuQtzcuSVjPStTc4dwU6JqPMFtgaLGvSQXhi",
"expected": null
},
{
"description": "Compressed literal pubkey; DISALLOWED",
"key": "02ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708",
"expected": null
},
{
"description": "X-only literal pubkey; DISALLOWED",
"key": "ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708",
"expected": null
},
{
"description": "Uncompressed literal pubkey; DISALLOWED",
"key": "04ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b517089e956909c4c07e8529f45f3ff8904d28df5a181619e21bdf748a896322530039",
"expected": null
}
]

View File

@@ -0,0 +1,131 @@
[
{
"description": "Xpub with origin and multipath wildcard",
"descriptors": [
"tr([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>/*)"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Xpub with single-path wildcard",
"descriptors": [
"tr([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/*)"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Xpub with bare wildcard (no preceding path)",
"descriptors": [
"tr([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/*)"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Xpub with fixed deep derivation, no wildcard",
"descriptors": [
"tr([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/5)"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Xpub with multipath, no wildcard",
"descriptors": [
"tr([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>)"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Bare xpub (no derivation, no wildcard); DISALLOWED",
"descriptors": [
"tr([d4ab66f1/48'/1'/1'/2']tpubDFTxBKyUCgkwp5enwZh3t2FJ5AMJqmCWoh1NRT13qNYQb1iKTUrAG6u5gpsDYhG8cZGXouYWuQtzcuSVjPStTc4dwU6JqPMFtgaLGvSQXhi)"
],
"expected_keys": null
},
{
"description": "Compressed literal pubkey; DISALLOWED",
"descriptors": [
"tr(02ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708)"
],
"expected_keys": null
},
{
"description": "X-only literal pubkey; DISALLOWED",
"descriptors": [
"tr(ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708)"
],
"expected_keys": null
},
{
"description": "Uncompressed literal pubkey; DISALLOWED",
"descriptors": [
"pk(04ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b517089e956909c4c07e8529f45f3ff8904d28df5a181619e21bdf748a896322530039)"
],
"expected_keys": null
},
{
"description": "Bare MuSig participant xpubs",
"descriptors": [
"tr(musig(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw,tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba))"
],
"expected_keys": [
"8b42cd4776376c82791b494155151f56c2d7b471e0c7a526a7ce60dd872e3867",
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Derivation after MuSig aggregation",
"descriptors": [
"tr(musig(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw,tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba)/<0;1>/*)"
],
"expected_keys": [
"8b42cd4776376c82791b494155151f56c2d7b471e0c7a526a7ce60dd872e3867",
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Same root in several descriptor sets contributes once",
"descriptors": [
"tr(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/*)",
"tr(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/1/*)",
"tr(tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba/*)"
],
"expected_keys": [
"8b42cd4776376c82791b494155151f56c2d7b471e0c7a526a7ce60dd872e3867",
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
},
{
"description": "Root used both bare and derived is excluded",
"descriptors": [
"wsh(or_i(pk(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/0/*),pk(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw)))"
],
"expected_keys": null
},
{
"description": "Xpub root (03ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708) excluded by an opposite-parity literal in another descriptor set",
"descriptors": [
"tr(tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/*)",
"wpkh(02ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708)"
],
"expected_keys": null
},
{
"description": "MuSig participant exposed in a script leaf",
"descriptors": [
"tr(musig(tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba,tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw)/0/*,pk(tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba))"
],
"expected_keys": [
"ebd252ca0877aae09b9d058219682775aa3cbcd049c12f07832f2cf6a3b51708"
]
}
]