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:
54
bip-0138.md
54
bip-0138.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
131
bip-0138/test_vectors/recipient_keys.json
Normal file
131
bip-0138/test_vectors/recipient_keys.json
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user