diff --git a/psbt-privacy.md b/psbt-privacy.md new file mode 100644 index 0000000..99d4d25 --- /dev/null +++ b/psbt-privacy.md @@ -0,0 +1,381 @@ + + +# BIP: XXX Privacy PSBT + +## Abstract + +PSBT creation, construction, updating, and signing may all add privacy +sensitive information to PSBTs. + +When exchanging (modifiable) PSBTs during construction (BIP 370 or otherwise) +or signing with semi-trusted or untrusted counterparties, only some fields are +necessary to share, and privacy can be harmed significantly if certain fields +that are required for a signing device for example are leaked to an untrusted +party. + +This document specifies what fields can be shared with trusted and untrusted parties such that privacy is preserved. + +## Rationale + +Historically, wallets exchanged PSBTs only with trusted peers. For example, one owner, one organization, or mutually trusting cosigners. Interactive protocols now pass PSBTs across trust boundaries (payjoin, coordinator-less coinjoin, external combiners). Treat each outbound handoff as a field-selection problem: specify exactly which PSBT entries the next party needs and strip everything else so privacy does not leak by default. + + +## Trust Domain (find better name TODO) + + + +Model the trust domain as a single authorization boundary: every PSBT role inside it may observe the full PSBT, including signing material and wallet-internal metadata. You rely on those parties not to relay that data outside the domain. When a PSBT leaves the domain, hand off only the entries the external role needs for its next step. Scrub every privacy-sensitive field listed below at each trust-boundary crossing unless policy explicitly documents an exception. + +If a PSBT re-enters the trust domain, it may regain the fields that were stripped. + +### Motivating examples + +BIP 77/78 example: Payjoin requires the sender to strip derivation fields before responding to their counter party. +The counterparty adds their inputs and outputs, updates with information neccecary to signs the psbt and then strips that information again before responding +to the sender. Either party only learns the information needed to complete the protocol. + +multisig example 2:3 with central coordinator and untrusting independent signers: +Psbt gets created, coins are selected and ouputs are created. The change output includes enough informatoin s.t each signer can verify the change is created correctly. +The updater creates 3 different PSBT with signing fields specific to a signer. Signers strips signing fields before distributing back to combiner -- they only include +a partial sig. +Signers do not learn each others derivations paths. In case a signer is replaced, they will not be able to identify the wallets future outputs based on onchain information. + +Chaincode delegation case: the recovery signer is blinded to their own onchain key they must certainly never learn the derivation paths, partial sigs, pubkeys of the other signatories. + +BIP174 Manual CoinJoin Workflow: +This is a example of an interactive tx construction between three untrusting wallets. Alice creates the PSBT, adds her inputs. At this point the PSBT should only contain public fields. Bob similarly does the same thing. Carol adds inputs, but signs them. This is not explictly notes but this may mean signing fields were present. Carol (and the other signers) must strip siging fields before returning PSBT + +## Specification + +### Scrubber Role + +Run the scrubber at the trust boundary on the sending side. It reads the in-domain PSBT, copies only the non-sensitive fields defined below into a fresh outbound PSBT, and drops everything else before handoff. The scrubber must sit inside the wallet trust domain: it needs the full PSBT to scrub correctly, and only the scrubbed copy crosses the trust domain. + + + +## Fields + +We categorize PSBT fields from the [BIP 174 type registry](https://github.com/bitcoin/bips/blob/master/bip-0174/type-registry.mediawiki) by privacy sensitivity. Each table lists the registry name, key type, minimum PSBT version, and the BIP that introduces the field. + +### Insensative Fields + +These include public fields that the final transaction reveals onchain. + +Some of these fields will always be available on-chain. Others are conditional based on the output type and/or the wallet policy. Ones that are optional should not be shared with the other signers or combiners. If they are non-optional they can be shared unconditionally. + +Some of these fields will always be available on-chain. Others are conditional based on the output type and/or the wallet policy. Optional ones should not be shared with other signers or combiners; non-optional ones can be shared unconditionally. + +For example, a combiner does not need witness scripts (only a signer does). `PSBT_IN_SEQUENCE` is fine to share and is necessary when merge conflicts arise. + +#### Global + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Unsigned Transaction | `PSBT_GLOBAL_UNSIGNED_TX = 0x00` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Transaction Version | `PSBT_GLOBAL_TX_VERSION = 0x02` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Fallback Locktime | `PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Input Count | `PSBT_GLOBAL_INPUT_COUNT = 0x04` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Output Count | `PSBT_GLOBAL_OUTPUT_COUNT = 0x05` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | + +#### Input + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Non-Witness UTXO | `PSBT_IN_NON_WITNESS_UTXO = 0x00` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Witness UTXO | `PSBT_IN_WITNESS_UTXO = 0x01` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Sighash Type | `PSBT_IN_SIGHASH_TYPE = 0x03` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Redeem Script | `PSBT_IN_REDEEM_SCRIPT = 0x04` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Witness Script | `PSBT_IN_WITNESS_SCRIPT = 0x05` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Finalized scriptSig | `PSBT_IN_FINAL_SCRIPTSIG = 0x07` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Finalized scriptWitness | `PSBT_IN_FINAL_SCRIPTWITNESS = 0x08` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Previous TXID | `PSBT_IN_PREVIOUS_TXID = 0x0e` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Spent Output Index | `PSBT_IN_OUTPUT_INDEX = 0x0f` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Sequence Number | `PSBT_IN_SEQUENCE = 0x10` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Required Time-based Locktime | `PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Required Height-based Locktime | `PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Taproot Key Spend Signature | `PSBT_IN_TAP_KEY_SIG = 0x13` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Script Spend Signature | `PSBT_IN_TAP_SCRIPT_SIG = 0x14` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Leaf Script | `PSBT_IN_TAP_LEAF_SCRIPT = 0x15` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | + +/// Tapscript sig must be treated as sensative if it may not end up on chain. i.e the PSBT can be finalized in > 1 way. +/// Contrived: example: you have both a keyspend sig and a tapscript sig. Some other party decides to broadcast one. That counterparty learns of your taptree strucutre. + +// Same reasoning as above fir tap script sig + +#### Output + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Output Amount | `PSBT_OUT_AMOUNT = 0x03` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| Output Script | `PSBT_OUT_SCRIPT = 0x04` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki), [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | + +#### Internal PSBT Fields + +These fields govern internal PSBT logic and can be shared without loss of privacy. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Transaction Modifiable Flags | `PSBT_GLOBAL_TX_MODIFIABLE = 0x06` | 2 | [370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) | +| PSBT Version Number | `PSBT_GLOBAL_VERSION = 0xFB` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | + +### Sensitive Fields + +These include signing fields and metadata a signer needs to derive keys, identify wallet-controlled outputs, verify wallet policy, or verify protocol-specific rules. + +Air-gapped cold signers need these fields explicitly because they may not maintain wallet state or derive the required context from another source. + +#### Partial Signatures + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Partial Signature | `PSBT_IN_PARTIAL_SIG = 0x02` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | + +#### Derivation Fields + +Untrusted parties can derive future and previous (unhardened) addresses. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Extended Public Key | `PSBT_GLOBAL_XPUB = 0x01` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| BIP 32 Derivation Path | `PSBT_IN_BIP32_DERIVATION = 0x06` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| BIP 32 Derivation Path | `PSBT_OUT_BIP32_DERIVATION = 0x02` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Taproot Key BIP 32 Derivation Path | `PSBT_IN_TAP_BIP32_DERIVATION = 0x16` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Key BIP 32 Derivation Path | `PSBT_OUT_TAP_BIP32_DERIVATION = 0x07` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Silent Payment Spend Key BIP 32 Derivation Path | `PSBT_IN_SP_SPEND_BIP32_DERIVATION = 0x1f` | 2 | [376](https://github.com/bitcoin/bips/blob/master/bip-0376.mediawiki) | + +#### Proprietary and Unknown Fields + +Global, input, and output proprietary and unknown fields are application-defined: their meaning is not specified by the PSBT standards, so a scrubber cannot classify them as safe to retain. **By default, remove them** before any handoff where the recipient is not trusted. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Proprietary Use Type | `PSBT_GLOBAL_PROPRIETARY = 0xFC` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Proprietary Use Type | `PSBT_IN_PROPRIETARY = 0xFC` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Proprietary Use Type | `PSBT_OUT_PROPRIETARY = 0xFC` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Unknown type keys | *(any unregistered ``)* | 0, 2 | — | + +#### Pre-images + +These fields supply preimages for hash-lock conditions in scripts (HTLCs). TODO: expand rationale. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| RIPEMD160 preimage | `PSBT_IN_RIPEMD160 = 0x0a` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| SHA256 preimage | `PSBT_IN_SHA256 = 0x0b` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| HASH160 preimage | `PSBT_IN_HASH160 = 0x0c` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| HASH256 preimage | `PSBT_IN_HASH256 = 0x0d` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | + +#### Taptree Fields + +Taproot hides optional script paths from untrusted parties. Internal key and related fields reveal whether script paths exist. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Taproot Internal Key | `PSBT_IN_TAP_INTERNAL_KEY = 0x17` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Merkle Root | `PSBT_IN_TAP_MERKLE_ROOT = 0x18` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Internal Key | `PSBT_OUT_TAP_INTERNAL_KEY = 0x05` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | +| Taproot Tree | `PSBT_OUT_TAP_TREE = 0x06` | 2 | [371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki) | + +#### Output Redeem Script / Witness Script + +This data may eventually appear on-chain; revealing it early leaks the spending condition ppre. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Redeem Script | `PSBT_OUT_REDEEM_SCRIPT = 0x00` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | +| Witness Script | `PSBT_OUT_WITNESS_SCRIPT = 0x01` | 0, 2 | [174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) | + +#### MuSig Fields + +MuSig2 signers usually sit in the same trust domain. An untrusted party that learns participant keys can correlate them with the aggregate key and weaken MuSig’s single-key-spend obfuscation. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| MuSig2 Participant Public Keys | `PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a` | 2 | [373](https://github.com/bitcoin/bips/blob/master/bip-0373.mediawiki) | +| MuSig2 Public Nonce | `PSBT_IN_MUSIG2_PUB_NONCE = 0x1b` | 2 | [373](https://github.com/bitcoin/bips/blob/master/bip-0373.mediawiki) | +| MuSig2 Participant Partial Signature | `PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c` | 2 | [373](https://github.com/bitcoin/bips/blob/master/bip-0373.mediawiki) | +| MuSig2 Participant Public Keys | `PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08` | 2 | [373](https://github.com/bitcoin/bips/blob/master/bip-0373.mediawiki) | + +/// I dont think musig2 keys are sensative the same pks in a OP_CHECKMULTISIG are. During setup to generate the aggregate key you nececarily need to know the entire key set to commit to it. So the signers are neccecarily in the same trust domain but what about the combiner? + +Generally, in musig2 signers are within a the same trust domain. However, if a untrusting party learn of the participant keys they can correlate the aggregate key with individual signatories of that multisig. Or worse it defeats the whole point of musig: to obfuscate a multisig behind a single key spend. + +// What if you dont know the other participants? This assumes you always assumes trusted ? + +// Partial sig added for hte same reason as above. + +#### Silent Payment Fields + +Silent payment outputs should look like ordinary P2TR key spends. Reveal silent-payment fields only across a trust boundary when the protocol requires it. + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Silent Payment Global ECDH Share | `PSBT_GLOBAL_SP_ECDH_SHARE = 0x07` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | +| Silent Payment Global DLEQ Proof | `PSBT_GLOBAL_SP_DLEQ = 0x08` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | +| Silent Payment Input ECDH Share | `PSBT_IN_SP_ECDH_SHARE = 0x1d` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | +| Silent Payment Input DLEQ Proof | `PSBT_IN_SP_DLEQ = 0x1e` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | +| Silent Payment Tweak | `PSBT_IN_SP_TWEAK = 0x20` | 2 | [376](https://github.com/bitcoin/bips/blob/master/bip-0376.mediawiki) | +| Silent Payment Data | `PSBT_OUT_SP_V0_INFO = 0x09` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | +| Silent Payment Label | `PSBT_OUT_SP_V0_LABEL = 0x0a` | 2 | [375](https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki) | + +// Tweak the input s.t it can spend the output. +// If you reveal your tweak, a untrusting participant and link your input to a sp output. And verify the output is a sp output + +// TODO: are these sensative: yes just bc they label an output as sp when untrusting parties wouldnt otherwise know. +// Created when input set is not unilateral +// Created and shared when all inputs are owned by same owner +// This neccecarily implies common input owner. +// TODO: maybe this is sensative because of the CIH reasoning but the per input ones are not? + +#### Misc + +| Name | `` | PSBT version | BIP | +| --- | --- | --- | --- | +| Generic Signed Message | `PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE = 0x09` | 2 | [322](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki) | +| Proof-of-reserves commitment | `PSBT_IN_POR_COMMITMENT = 0x09` | 0, 2 | [127](https://github.com/bitcoin/bips/blob/master/bip-0127.mediawiki) | +| BIP 353 DNSSEC proof | `PSBT_OUT_DNSSEC_PROOF = 0x35` | 2 | [353](https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki) | + +// PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE falls into a similar catagory as proprietarty and unknown. Depending on what the message contents are they may be appropriate to be shared with untrusting entities. But by default should be removed. + +// PSBT_IN_POR_COMMITMENT is neccecarily mean to be shared with trusting entities. Signers must only share their commitment with trusting parties. + +// PSBT_OUT_DNSSEC_PROOF = 0x35 ? + +From bip 373. +>If the updater has this derivation information, it should also add PSBT_IN_TAP_BIP32_DERIVATION for each participant public key. + +Caveat to this, if the signers are mutually untrusting the updater must create N psbts with each signer's xpubs. + +### Modifiable Flag Note + +If the inputs or outputs modifiable flags are set the scrubber can still remove individual fields. + +// add to footnotes + + + +### Field transfer sequence + +TODO: example where one signer is not trusted amongs n-1 other signers e.g bip89. +TODO: example where the combiner is not trusted and the signers are. + +Example: wallet trust domain versus an external combiner. Creator through Scrubber sit in one domain and may handle signing metadata under your internal policy. Scrubber to Combiner is the trust-boundary crossing: the scrubber builds the outbound PSBT copy that keeps only what merge needs (typically the public transaction skeleton plus that signer’s partial witness material) and drops every other privacy-sensitive field listed below. + +```mermaid +sequenceDiagram + box rgba(230,242,255,0.55) Wallet trust domain + participant Creator + participant Constructor + participant Updater + participant Signer + participant Scrubber + end + box rgba(255,235,235,0.45) External to wallet trust domain + participant Combiner + participant Finalizer + participant Extractor + end + + Creator->>Constructor: Empty PSBT + Constructor->>Updater: Public transaction fields + + Updater->>Signer: Public transaction fields + Updater->>Signer: Signing fields scoped to this signer + + Signer->>Scrubber: PSBT with signing metadata and partial witness + Note over Signer,Scrubber: Scrubber stays inside the wallet trust domain and may read the full PSBT. + + Scrubber->>Combiner: Public transaction fields + Scrubber->>Combiner: Partial witness fields for merge + Note over Scrubber,Combiner: Trust boundary. Outbound copy omits sensitive fields. + + Combiner->>Finalizer: Public transaction fields + Combiner->>Finalizer: Combined partial witness fields + + Finalizer->>Extractor: Finalization fields + Note over Extractor: Extract broadcast-ready transaction +``` + +### Refrence Implementation + + + +### Test Vectors + +TODO