BIP 77: Async Payjoin (#1483)

* Draft payjoin v2 BIP

* Include mailing list feedback

* Include TABConf feedback

* Include padding

* Include production reference implementation

* Adopt BIP-77 for payjoin v2

* Distinguish payjoin directory from OHTTP Relay

* Detail OHTTP Key Configuration mechanism

* Fix punctuation

* Make base64URL references consistent

* Reference standardized Secp256k1 DHKEM for HPKE

* Add Comments-URI

* fixup: Format and spell check

Co-authored-by: spacebear <144076611+grizznaut@users.noreply.github.com>

* Add BIP 77 to README

* Add Payjoin V2 overview diagram

* Add Oblivious HTTP Sequence Diagram

* Correct links and spelling

Co-authored-by: thebrandonlucas <38222767+thebrandonlucas@users.noreply.github.com>

* Wrap <code> blocks

* Fix basic scheme actors

* Fix dead samourai links

* Orient motivation around a problem

* fix links

* Keyconfig s/should/must/ be provided

* Fix typos

Co-authored-by: thebrandonlucas <38222767+thebrandonlucas@users.noreply.github.com>

* s/pubkey/public key

* Incorporate jonatack's suggestions

* Incorporate more jonatack suggestions

* Incorporate satsie's suggesetions

* Rename "Async Payjoin"

* Replace BIP21 params with fragment params

* Revise document to describe Payjoin Sessions

Enrollment was a less clear than sessions

* Revise Sequence Diagram

* Spell initialize

* Update the bip to represent the stable protocol

* Spell according to Type Checks's job

* Mention the format of the ohttp fragment

* Reference BIP 78 attack vectors

* Remove straggling text

* Specify authorization mechanism

The specifics of a credential issuance are left out, however

* Use implicit session initialization

* Specify cryptographic handshake based on Noise IK

Co-authored-by: Yuval Kogman <nothingmuch@woobling.org>

* Add Spacebear's clarifications

Co-authored-by: spacebear <git@spacebear.dev>

* Document subdirectory Short IDs

* Require uppercase URL

bech32 fragment prefixes are case sensitive, and
alphanumeric mode only works on capital letters.

* Specify bech32 fragment parameter definitions

* Uppercase URL specifically only after subdirectory

* Note payload uniformity via padding and ellswift

* Include Message Byte Representations

This is the most straightforward way to explain the various padding
requirements.

* Document HPKE `info` strings

* Truncate lines to 120 characters

* Receiver's Original PSBT, not proposal

* Specify no mixed [output script]

* Remove extraneous pipe character

* Require BIPS 21, 78, 174

* Update checklist MUST/MUST NOT sections

MUST NOT contained MUST details. Move them to MUST.

* inputs ⇒ input

* Clarify BIP 78 payjoin version 1 connection

* Fix backwards compat language

* Payjoin version 2 URIs

* Reference Binary HTTP RFC

* Payjoin version 1 Proposal PSBTs

* Oblivous -> Oblivious

* Rm reference to 'production relays'

* Repeat the active agent by name

* Add Post-History

* Title 'Async Payjoin'

* Check spelling

* directory -> mailbox

* Move ohttp= fragment param to link to frag spec

* Mention URI keys as bootstrap mechanism

* Mailbox Discovery

* Remove superfluous word

* Clarify motivation

* Revise backwards compatiblity section for clarity

* Remove related protocol details

* Mv copyright out of flow

* Fix grammar (should be plural)

* Weaken language around addressing CIOH

"solves" implies this is the end of the story. Clarify that the problem
is the sole *explicit* problem mentioned in the paper.

* Simplify overview

- describe happy path protocol sequence
- introduce non-obvious key terms inherited from BIP 78
- no need for technical details that are clarified in the specification

* Describe optionality in overview

* Nitpicky sequence diagram fixes

* Clarify receiver's initial message in sequence diagram

* Simplify Basic Scheme section

* Mention OHTTP abbreviation on first mention

* Move sequence diagram up

* fragment parameter encoding corrections

- base64url was replaced by bech32
- formatting fixes
- some clarifications

* Use SHA-256 at independent mentions for consistency

* bootstrap grammar fix & correction

bootstrap would use a tor exit node, not a hidden service

* clarify proposal PSBT encryption layers

clarify which key is used for which layer of encryption (payjoin v2 e2ee vs.
OHTTP)

the message is not "authenticated" by the sender, rather it is tagged, it can be
authenticated during decryption.

* format original/proposal PSBT terms using italic, not <code>

* HRP of short ID is an implementation detail

it doesn't matter what is used since it's stripped after encoding

* Clarify checklist requirements

* "by intersection" unclear and unnecessary

* the fragment doesn't follow the pj param, it's part of it

* fix message diagram line intersections

* Correct encapsulated OHTTP diagram

The binary HTTP request is encrypted, and the AEAD tag is at the end, not the
beginning

* Clarifications for HPKE keys

Remove noise protocol framework mention. The IK pattern is not accurate, the
closest patterns are N or possibl NN, but neither is a perfect fit (N defines the
key as static, which it isn't, and NN is an interactive pattern)

* Remove note about forward secrecy

This is inaccurate, forward secrecy is defined with respect to long term
sessions, so the definition doesn't really extend to the request and response
messages, each of which is encrypted with ephemeral keys.

* Clarify OHTTP-relay bypassing by use of tor hidden service

* Update HPKE mode used for sender's message

Previously the reply key was included before the HPKE ciphertext, and the Auth
mode was used using this key. Since they are delivered together that only proves
the key was usable by the sender, not that the ciphertext is authentic. With the
key included as part of the encrypted plaintext, the HPKE mode was changed to
the base encryption to a public key mode with no authentication key.

* keep mailbox, but rename mailroom back to directory

Partly reverts a4d4065fa6f736f058e9173aa852e4fd12e75650, this change is hardly
more than a find & replace of mailroom to directory, and does not revert grammar
changes etc in addition to not reverting the subdirectory -> mailbox rename
which was the main point of confusion.

* Clarify allowed_purposes mechanism

First explain RFC 9540, then explain the extension mechanism.

Make roles in the interaction more explicit by changing the heading, "Directory
Discovery" sort of implies that clients discover these, when it describes relay
to directory interaction.

Clarify centralization pressure, that is alleviated by making senders' and
receivers' choices independent of each other.

* Correct payload uniformity section

We forgot about the OHTTP header which is 7 bytes of cleartext that also
specifies the DHKEM algoritm.

Additional clarifications and some restructuring to describe the details two
classes of messages each in its own self contained paragraph.

* rewrap paragraph to fix broken link

* fix bullet list formatting

- unindent to avoid <pre>
- fix broken URLs
- fix bullet items split into paragraphs

* rewrap section to fix broken links

* rewrap more paragraphs to fix broken links

* make attack vectors level 2 heading

as level 3 heading it was displayed under rationale in the table of contents

* Grammar/style fixes

* Order Requires

* Describe 'what' in the first sentence of the abstract.

* Be more specific about motivation.

* Make goal more explicit and consise

* Standardize "Common-input-ownership heuristic"

bitcoin wiki uses this.

* Replace Request expiration with Session Expiration

* Specify BIP 78 `v` parameter as redundant.

* Separate Short ID length rationale from spec

* Clairfy key nomeclature

- mailbox key
- reply key
- receiver key

as well as ephemerality and session nomeclature.

* Place byte diagrams with there respective message description.

* Include bitcoin URI subsection

* Top half reorg

* Add Yuval Kogman as Co-author

* NO mak typo

* Fix heirarchy

* Convert mediawiki to markdown

nix shell nixpkgs#pandoc --command bash -lc '
  pandoc -f mediawiki -t gfm bip-0077.mediawiki -o bip-0077.md'

rm bip-0077.mediawiki
reference bip-0077.md in README
surround bip-0077.md preamble in ``` to satisfy CI

* Strip link titles from mediawiki -> md conversion

sed -i.bak -E 's/\]\(([^ )]+) "[^"]*"\)/](\1)/g' bip-0077.md

* Strip leading/trailing spaces from inside links

sed -i.bak -E 's/\[[[:space:]]+/[/g; s/[[:space:]]+\]/]/g' bip-0077.md

* Fix spacing around inline code

* Take bitcoin URI example out of md link syntax

* Fence byte diagrams in backtics

* Replace sequence diagrams with mermaid

Better rendering and semantic source

* Collapse overview, basic scheme, and protocol sequence

These were all inconsitent levels of detail for the same thing. Leave the overview
the highest level and link to the specifics.

* Consistent short id singularity

* Remove straggling whitespace

* Link whitepaper

* Fix motivation flow

* Clarify abstract

* Clarify motivation

* Clarify overview

* Clarify bootstrapping

* Use singular to describe Payjoin URI

* Clarify mailbox endpoint

Specify that v2 mailboxes are OHTTP Targets.
Mention backwards compatibility.

* Clarify Receiver Fragment Parameters

* Revise messaging for clarity

* Add rationale for allowed_purposes

* Define ElligatorSwift according to BIP 324

* Clarify attacks, backwards compatibility

* Fix Receiver Proposal PSBT messaging header

for link.

* Add activation to sequence

* Correct #64-bit-short-id-length link

Co-authored-by: Yuval Kogman <nothingmuch@woobling.org>

* Clarify why not AES-GCM rationale

* Specify serialization of reply key in plaintext

* Specify the wire format for ChaCha20-Poly1305 ciphertext and tag

* Specify details of HPKE message wire format

Also clarifies that HPKE auth mode is used with the receiver's key,
authenticating the receiver as the sender of the encrypted Proposal PSBT.

* Correct diagram for OHTTP encapsulation

The order according to RFC 9458 and the code is is header, followed by
encapsulated key, followed by the ciphertext.

* OHTTP message encoding according to RFC 9458

* Rephrase abstract in active voice

* Deduplicate motivation word choice

- 'suitable for widespread implementation' vs appropriate, it's stronger
- 'mature solutions' to express that we chose those already based on iteration
- 'proven bitcoin primitives' to reflect the use of those battle tested like
  ElligatorSwift

* Simplify output batching motivation

* Reduce verbosity of linking exemplar conclusion

* Use PSBT 'update' verb in overview

Say 'appropriate intputs and/or outputs' because outputs might be merely
replaced, not necessarily added.

* Mention mutual exclusivity of Original and Proposal PSBTs

* Capitalize Uri -> URI

* Clarify URI parameter key/value distinction

* Backwards-compatible receivers *disable* pjos

* Use bech32 character set, not bech32

* Clarify session-specific parameter encoding

* Say 33-byte compressed public key

* Clarify v2 optional sender parameters application

* Clarify receiver session initiation overview

Co-authored-by: nothingmuch <nothingmuch@woobling.org>

* Mention sender's ephemeral mailbox in overview

Co-authored-by: nothingmuch <nothingmuch@woobling.org>

* Clarify cut-through optimization

* Replace mention of v1/v2 payjoin

Instead use 'This proposal', 'BIP 78', 'BIP 77', or omit the mention.

* Mention BIP 174 for PSBTv0

* Mention sender's *corresponding* public key

* Hyphenate '16-byte'

* Clarify who can post messagese direct to mailbox

* liu -> lieu

* Simplify cut through overview sentence structure

* Replace 'Payjoin exemplar' with 'A natural application..'

* Make motivation CIOH mention easier to read

Use language from sataoshi and don't mention input batching since the next
sentence already does.

* Specify Proposal PSBT MUST/MAY input/output inclusion rules

* remove duplicate 'and'

* Remove duplicate 'preserve'

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* The HRP is used as the parameter key

Co-authored-by: Yuval Kogman <nothingmuch@woobling.org>

* Add rationale for random padding in OHTTP

* Use "zero" instead of "0"

Co-authored-by: Mark "Murch" Erhardt <murch@murch.one>

* epehmeral -> ephemeral

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* subject match tense

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* Capitalize Payjoin for protocol

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* Capitalize Payjoin for protocol

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* Capitalize Payjoin for protocol

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* Capitalize Payjoin for protocol

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* Capitalize Payjoin for protocol

Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>

* ("Version 2") relative to and described in ("Version 1")

Co-authored-by: Jon Atack <jon@atack.com>

* BIP78's requirements for Payjoin Version 1

Co-authored-by: Jon Atack <jon@atack.com>

* Include missing period

Co-authored-by: Jon Atack <jon@atack.com>

* which -> that

Co-authored-by: Jon Atack <jon@atack.com>

* Separate independent clauses with a semicolon

Co-authored-by: Jon Atack <jon@atack.com>

* Remove duplicate "at"

Co-authored-by: Jon Atack <jon@atack.com>

* Hyphenate "short-lived"

Co-authored-by: Jon Atack <jon@atack.com>

* Fix Attack Vectors URL

Co-authored-by: Jon Atack <jon@atack.com>

* which -> that

Co-authored-by: Jon Atack <jon@atack.com>

* Include colon to reference Oblivious HTTP Relay impl

Co-authored-by: Jon Atack <jon@atack.com>

* consist -> consists

Co-authored-by: Jon Atack <jon@atack.com>

* Remove double "the"

Co-authored-by: Jon Atack <jon@atack.com>

* Remove double "the"

Co-authored-by: Jon Atack <jon@atack.com>

* Correct Padded BHTTP Response length

144 bytes not 104

See: 87042266d1/payjoin-directory/src/lib.rs (L30-L31)

* which -> , which

* Note TLS is not available in Bitcoin Core

* Link to BIP21 forwards compatibility `reqparam`

* Require rev. lexicographical frag. param. order

A specific order might create a fingerprint for a specific wallet, imposing a privacy
risk. It seems impossible to impose an order on BIP21 parameters, but BIP 77 clients
may error on out-of-order fragment parameters to at least avoid some fingerprint there.

Reverse lecicographical ordering was chosen because that is how the existing implmentation
serializes the parameters already, so that no breaking change needs to be made.

Co-authored-by: nothingmuch <nothingmuch@woobling.org>

---------

Co-authored-by: spacebear <144076611+grizznaut@users.noreply.github.com>
Co-authored-by: thebrandonlucas <38222767+thebrandonlucas@users.noreply.github.com>
Co-authored-by: Yuval Kogman <nothingmuch@woobling.org>
Co-authored-by: spacebear <git@spacebear.dev>
Co-authored-by: spacebear <144076611+spacebear21@users.noreply.github.com>
Co-authored-by: Brandon Lucas <thebrandonlucas@gmail.com>
Co-authored-by: Mark "Murch" Erhardt <murch@murch.one>
Co-authored-by: Jon Atack <jon@atack.com>
This commit is contained in:
Dan Gould 2025-05-28 14:49:12 -04:00 committed by GitHub
parent 5c7120f418
commit ee587c5d2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 744 additions and 0 deletions

View File

@ -428,6 +428,13 @@ Those proposing changes should consider that ultimately consent may rest with th
| Standard
| Final
|-
| [[bip-0077.md|77]]
| Applications
| Async Payjoin
| Dan Gould, Yuval Kogman
| Standard
| Draft
|-
| [[bip-0078.mediawiki|78]]
| Applications
| A Simple Payjoin Proposal

737
bip-0077.md Normal file
View File

@ -0,0 +1,737 @@
```
BIP: 77
Layer: Applications
Title: Async Payjoin
Author: Dan Gould <d@ngould.dev>
Yuval Kogman <nothingmuch@woobling.org>
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0077
Post-History: https://github.com/bitcoin/bips/pull/1483
https://gnusha.org/pi/bitcoindev/7B11AE34-27A7-46ED-95BF-66CA13BA26F3@ngould.dev/#t
https://gnusha.org/pi/bitcoindev/3C0A6E4C-444E-4E75-829C-1A21D8EE40E0@ngould.dev/#t
Status: Draft
Type: Standards Track
Created: 2023-08-08
License: BSD-2-Clause
Requires: 21, 78, 173, 174
```
## Copyright
This BIP is licensed under the 2-clause BSD license.
## Abstract
Payjoin lets Bitcoin senders and receivers interact to make batched
transactions.
This document proposes a second, backwards-compatible, asynchronous version of
the Payjoin protocol ("Version 2") relative to and described in [BIP 78](bip-0078.mediawiki) ("Version 1"). An untrusted
third-party "directory server" replaces the requirement
for a receiver to host a secure public endpoint for interactions. HTTP clients
access the directory server using an asynchronous protocol and authenticated,
encrypted payloads. The design preserves complete Payjoin receiver
functionality, including payment
output substitution. Authenticated encryption depends only on cryptographic
primitives available in Bitcoin Core. Requests use [Oblivious
HTTP](https://www.ietf.org/rfc/rfc9458.html) (OHTTP) to
prevent the directory and other Payjoin clients from linking requests to client
IP addresses.
## Motivation
Satoshi Nakamoto pointed out one specific privacy risk in the
[whitepaper](https://bitcoin.org/en/bitcoin-paper),
that transactions with multiple inputs "necessarily reveal that
their inputs were owned by the same owner."
Payjoin addresses that risk, the _common-input-ownership heuristic_,
by making it practical to spend inputs owned by multiple parties
in one transaction.
While addressing Bitcoin's primal privacy risk, Payjoin *input* batching
also improves on the widespread non-interactive *output* batching practice
deployed by exchanges. When combined, the same movement of funds can use
less block weight and save fees.
A natural application of Payjoin would be to combine
getting paid with consolidating UTXOs into one transaction. But Payjoin
can also secure [transaction
cut-through](https://bitcointalk.org/index.php?topic=281848.0),
allowing a sender to transfer funds to a receiver who also transfers
funds to a third party in the same transaction. For example, deposits to an
exchange may "cut through" in a single transaction that also satisfies
withdrawals instead of with a second transaction that spends the deposited
funds. Payjoin enables more blockspace-efficient transactions that
reduce fees while addressing privacy risks.
However, BIP 78's requirements for Payjoin Version 1 have proven to be an
obstacle to adoption. Version 1 receivers must host a secured
public-facing HTTP server. Mobile and web environments limit the ability
to fulfil such a requirement. Version 1 also requires synchronous
communication. Both sender and receiver must be online simultaneously.
Wallet developers [
regard](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-January/018358.html)
these requirements as barriers to Payjoin adoption.
To address these limitations, our goal is to specify a practical
coordination mechanism suitable for widespread implementation. This proposal
leverages mature solutions to common problems, building on established web
standards and proven Bitcoin primitives.
## Overview
A Payjoin *sender* and *receiver* interact so that they may both contribute to a
transaction. In this proposal, they exchange asynchronous end-to-end
encrypted messages by relaying them to a store-and-forward *directory* server
using OHTTP.
Before initiating the protocol, the receiver must secure communications with
the directory by [bootstrapping](#ohttp-bootstrapping).
- The receiver [initiates a Payjoin Session](#session-initiation)
by sharing a [Payjoin URI](#payjoin-uri) that includes the URL of an
ephemeral mailbox hosted on the directory, where it can receive a message
from the sender.
- The sender [posts a message](#sender-original-psbt-messaging)
containing a fully signed fallback transaction, known as the *Original PSBT*,
to the mailbox.
- The receiver gets this message and
[posts a message containing a *Proposal
PSBT*](#receiver-proposal-psbt-messaging)
to the sender's ephemeral mailbox, by updating the Original PSBT with
appropriate inputs and/or outputs.
- The sender gets the Proposal PSBT, [checks it, signs, and
broadcasts](#sender-signing-and-broadcast) the final transaction.
At any point, either party may choose to broadcast the
fallback transaction described by the Original PSBT instead of proceeding.
Because the Original PSBT and Proposal PSBT spend the same input(s) they are
mutually exclusive and only one can be confirmed.
Messages are buffered in the directory, allowing both parties to tolerate
temporary disconnections and resume communication by polling.
### Sequence Diagram
```mermaid
sequenceDiagram
title Async Payjoin Sequence Diagram
participant R as Receiver
participant D as Directory
participant S as Sender
participant N as Network
R-)S: Payjoin URI (BIP 21) out of band
R-->>D: Poll GET Requests<br/>for Original PSBT
activate D 
S->>D: POST Request<br/>Original PSBT
D->>R: GET Response<br/>Original PSBT
deactivate D
S-->>D: Poll GET Requests<br/>for Proposal PSBT
activate D
R->>D: POST Request<br/>Proposal PSBT
D->>S: GET Response<br/>Proposal PSBT
deactivate D
S->>N: Broadcast Payjoin
```
## Specification
### OHTTP Bootstrapping
Before initiating a Payjoin Session a receiver must first discover the
directory's
[OHTTP Key Configuration](https://www.ietf.org/rfc/rfc9458.html#section-3.1),
via an authenticated
bootstrap mechanism. The key configuration contains information to establish
[Hybrid Public Key Encryption](#secp256k1-hybrid-public-key-encryption) (HPKE) in order to secure communications between the client and the directory in
lieu of TLS.
The bootstrap mechanism may vary by implementation but must
follow [OHTTP Consistency
Requirements](https://datatracker.ietf.org/doc/html/draft-ietf-privacypass-key-consistency-01)
and should not reveal a receiver IP address to the directory. Some
examples of suitable mechanisms include getting a key configuration
from a Payjoin URI, a trusted application binary, or fetching using https-in-http
CONNECT method, https-in-WebSocket, Tor, or a VPN.
Directory OHTTP Gateways MUST support [RFC 9540 Key Configuration
Fetching](https://www.rfc-editor.org/rfc/rfc9540.html#name-key-configuration-fetching)
via GET request. RFC 9540 defines the
gateway location as `/.well-known/ohttp-gateway`.
### Session Initiation
A receiver initiates a session by sharing a Payjoin URI. Because a URI
contains sensitive information, such as a receiver address, it should be shared
over a confidential channel.
#### Payjoin URI
Bitcoin URIs ([BIP
21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
or [BIP
321](https://github.com/bitcoin/bips/blob/master/bip-0321.mediawiki))
are a standard way to request bitcoin.
A Payjoin URI is a Bitcoin URI that contains a `pj` parameter. The `pj`
parameter value is a URL in both BIP 78 and BIP 77.
Senders that understand Bitcoin URI but don't support Payjoin will just
ignore the `pj` parameter and proceed to typical address-based
transaction flows.
A `req-pj` parameter may be used as a [BIP 21 forwards compatibility `reqparam`](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki#forward-compatibility) instead of
`pj` to signal that Payjoin is required.
`pj` to signal that Payjoin is required.
The parameter value must be [uppercased and the parameter should be placed last in the URI](#uppercase-url).
Since BIP 78 payloads are neither encrypted nor authenticated,
a directory used for backwards-compatible payloads is known
as an ["unsecured payjoin server" in BIP 78
parlance](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#unsecured-payjoin-server).
Backwards-compatible receivers MUST disable output substitution
by setting `pjos=0` to prevent modification by a malicious directory.
##### Mailbox endpoint
In this proposal the URL in the `pj` parameter value is the mailbox
endpoint URL. Mailboxes are shared HTTP resources hosted by the
directory and serve as OHTTP Target Resources. Clients use these endpoints
to relay encrypted messages. They `POST` messages to and `GET` messages from
mailbox endpoints via OHTTP.
Senders that support BIP 78 but not this proposal may POST messages directly to
mailbox endpoints for [backwards compatibility](#backwards-compatibility).
###### Short ID
A Short ID identifies a mailbox based on its associated public key. The Short
ID is the path component of the mailbox endpoint. One is derived by hashing the
33-byte compressed public key encoding with SHA-256, truncating it to
[8 bytes (64 bits)](#64-bit-short-id-length), and encoding it in
[uppercase](#uppercase-url) using the bech32 character set (like a bech32 string without the HRP, separator and checksum).
##### Receiver fragment parameters
This proposal introduces session-specific parameters which the
receiver shares encoded in the URI.
Instead of defining new Bitcoin URI parameters, the session-specific
parameters are encoded in the [
fragment](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5)
of the the mailbox endpoint URL.
The `#` fragment separator character must be [RFC 3986
percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1)
as `%23`, because it separates the
fragment of the mailbox endpoint URL included in the `pj` parameter, not the
fragment of the Bitcoin URI.
These session-specific parameters use a bech32-inspired encoding.
The HRP is used as the parameter key, followed by the '1' separator,
followed by the parameter value encoded using the bech32 character set in
[uppercase](#uppercase-url). No checksum is used. Parameters are separated
by a `+` character.
The following parameters are defined, and must be provided in reverse
lexicographical order:
- `RK`: encodes the *receiver key* as a 33-byte compressed public key.
Senders will initiate HPKE with the receiver using this key.
- `OH`: encodes an alternate format of the OHTTP Key Configuration of
the directory. It consists of a 33-byte compressed public key of the
directory's OHTTP Gateway, prefixed by the 2-byte Key Identifier. A [
RFC 9458 Key
Configuration](https://www.ietf.org/rfc/rfc9458.html#section-3.1)
is reconstructed by assuming the HPKE KEM ID and Symmetric Algorithms
are [fixed](#secp256k1-hybrid-public-key-encryption).
- `EX`: specifies a [session
expiration](#session-expiration) in [unix
time](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16).
For example, a properly encoded endpoint Bitcoin URI looks like this
`bitcoin:tb1q6q6de88mj8qkg0q5lupmpfexwnqjsr4d2gvx2p?amount=0.00666666&pjos=0&pj=HTTPS://PAYJO.IN/TXJCGKTKXLUUZ%23RK1Q0DJS3VVDXWQQTLQ8022QGXSX7ML9PHZ6EDSF6AKEWQG758JPS2EV+OH1QYPM59NK2LXXS4890SUAXXYT25Z2VAPHP0X7YEYCJXGWAG6UG9ZU6NQ+EX1WKV8CEC`
### Sender Original PSBT Messaging
The sender constructs the fallback transaction, a typical transaction
spending funds to the receiver's address specified in the Payjoin URI.
This transaction is serialized as a BIP 174 PSBTv0, satisfying
[the receiver checklist](#receivers-original-psbt-checklist).
The Original PSBT MUST:
- Include complete UTXO data.
- Be fully signed.
- Exclude unnecessary fields such as global xpubs or keypath
information.
- Be broadcastable.
The Original PSBT MAY:
- Include outputs unrelated to the sender-receiver transfer for batching
purposes.
This *Original PSBT* is encoded as base64, followed by the query
parameter string on a new line containing [optional sender
parameters](#optional-sender-parameters).
The sender generates an ephemeral mailbox key. The corresponding public key is
known as the *reply key*, and it is prepended to the base64 plaintext string,
serialized in compressed form as 33 bytes.
This plaintext string is encrypted to the receiver key according to [HPKE Base
mode](https://www.rfc-editor.org/rfc/rfc9180.html#name-encryption-to-a-public-key).
The HPKE `info` string, used for domain separation, is `PjV2MsgA`. The
ciphertext ensures message secrecy and integrity when passed to the receiver
using the mailbox endpoint. The 16-byte authentication tag is appended to the
ciphertext.
RFC 9180 [does not
specify](https://www.rfc-editor.org/rfc/rfc9180.html#section-10) the wire format
encoding of HPKE messages. To construct an HPKE payload, the secp256k1 public
key from the DHKEM is encoded using ElligatorSwift in 64 bytes. Note that
ElligatorSwift is only the wire format; when deriving shared secrets, the curve
point is re-serialized in uncompressed form.
```
PjV2MsgA Byte Representation (7168 bytes total)
+---------------------------------------------------------------------------------------+
| ElligatorSwift | Ciphertext |
| (64 bytes) | (7104 bytes) |
| +-----------------------+---------------------------------+------------+
| | Reply Key | Padded Plaintext | AEAD Tag |
| | (33 bytes) | (7055 bytes = 7168-64-33-16) | (16 bytes) |
+---------------------------------------------------------------------------------------+
```
The resulting HPKE payload is the body of a POST request to the
receiver's mailbox. This request is then [
encapsulated](#client-directory-interactions) according to
Oblivious HTTP to the directory's OHTTP Gateway. OHTTP serializes the
inner request as BHTTP, and provides another layer of HPKE encryption,
between the client and directory.
Upon receipt, the directory's OHTTP Gateway decapsulates the OHTTP
request and handles the inner POST request at the receiver's mailbox
endpoint, which stores the HPKE encrypted payload to be forwarded to the
receiver.
The sender then polls OHTTP encapsulated GET requests to the sender's
mailbox endpoint until it receives a response from the directory
containing the receiver's *Proposal PSBT*, and proceeds to
[sign and broadcast](#sender-signing-and-broadcast).
It stops polling after expiration.
#### Optional sender parameters
[BIP 78's optional sender parameters](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#optional-parameters)
may be used in this proposal, but must be included in
the body as part of the ciphertext rather than as a query string.
HPKE binds ciphertexts to application-specific `info` strings. Because
of this domain separation, BIP 78's `v` parameter is redundant and
should be omitted for this proposal.
### Receiver Proposal PSBT Messaging
After sharing the Payjoin URI with the sender, the receiver polls via
OHTTP encapsulated GET requests to the receiver's mailbox endpoint. So
long as the mailbox contains no message, the directory responds with
status 202 ACCEPTED. Once a mailbox contains a message, the directory
returns it in the response body with status 200 OK.
Upon receiving an encapsulated 200 OK response, the receiver decrypts
the payload and checks the *Original PSBT* therein according to the
[receiver checklist](#receivers-original-psbt-checklist).
The receiver then updates the *Original PSBT* to include new signed
inputs and outputs, invalidating the sender's signature(s). The receiver
may also adjust the transaction fee. The result, called the *Proposal
PSBT*, must satisfy the [sender checklist](#senders-proposal-psbt-checklist)
The Proposal PSBT MUST:
- Include complete UTXO data.
- Include all inputs from the Original PSBT.
- Include all outputs which do not belong to the receiver from the
Original PSBT.
- Use a random index if additional inputs or outputs are added.
The Proposal PSBT sender MAY:
- Add inputs at random indices.
- Add outputs at random indices.
- Remove or modify Original PSBT outputs under the control of the
receiver (i.e. not sender change).
The Proposal PSBT MUST NOT:
- Shuffle the order of inputs or outputs contained in the Original PSBT.
- Decrease the absolute fee of the Original PSBT.
The receiver encrypts the *Proposal PSBT* to the sender's reply key according to
[HPKE Auth
mode](https://www.rfc-editor.org/rfc/rfc9180.html#name-authentication-using-an-asy),
using the receiver's key for authentication. The HPKE `info` string is
`PjV2MsgB`. The HPKE wire format is the same as in the [sender's
message](#sender-original-psbt-messaging).
```
PjV2MsgB Byte Representation (7168 bytes total)
+---------------------------------------------------------------------------------------+
| ElligatorSwift | Ciphertext |
| (64 bytes) | (7104 bytes) |
| +---------------------------------------------------------+------------+
| | Padded Plaintext | AEAD Tag |
| | (7088 bytes = 7168-64-16) | (16 bytes) |
+---------------------------------------------------------------------------------------+
```
The receiver makes the resulting HPKE payload the body of a POST request to the
sender's mailbox whose Short ID is derived from the sender's reply key. This request is then [
encapsulated](#client-directory-interactions) according to
Oblivious HTTP to the directory's OHTTP Gateway. OHTTP serializes the
inner request as BHTTP, and provides another layer of HPKE encryption,
between the client and directory.
Once the receiver makes this request, they wait for either transaction from the
Original PSBT or Proposal PSBT to be broadcast to the Bitcoin network.
#### Receiver's Original PSBT checklist
The receiver checklist is the same as [the BIP 78 receiver
checklist](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#receivers-original-psbt-checklist).
### Sender signing and broadcast
The sender validates the *Proposal PSBT* it receives against a
checklist. If the checks pass, it may sign and broadcast the resulting
Payjoin transaction.
#### Sender's Proposal PSBT checklist
This proposal's sender checklist is the same as [the BIP 78 sender
checklist](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#senders-payjoin-proposal-checklist).
### Client/Directory interactions
The Payjoin Directory provides a rendezvous point for senders and
receivers to exchange messages. The directory stores Payjoin payloads to
support asynchronous communication. Async Payjoin requests must be
submitted as encapsulated messages to the directory's OHTTP Gateway.
The wire format OHTTP request is specified in [RFC
9458](https://www.ietf.org/rfc/rfc9458.html#name-hpke-encapsulation). HPKE
requires the directory's OHTTP key configuration. The plaintext is a binary
encoded HTTP request ([RFC 9292](https://www.rfc-editor.org/rfc/rfc9292.html))
intended for the OHTTP target resource, usually a mailbox endpoint, padded to
8104 bytes with [random data](#random-padding).
```
OHTTP Encapsulated Request Byte Representation (8192 bytes total)
+--------------+-------------------------+------------------------------------------+
| OHTTP Header | HPKE KEM | Ciphertext |
| (7 bytes) | Uncompressed Public Key | (8120 bytes = 8192-65-7) +
| | (65 bytes) +-----------------------------+------------+
| | | Padded BHTTP Request | AEAD Tag |
| | | (8104 bytes = 8192-65-16-7) | (16 bytes) |
+--------------+-------------------------+------------------------------------------+
```
Response encryption uses the Export functionality of the request HPKE context to
establish a shared secret, and therefore consists of a 32 byte nonce followed by
the AEAD ciphertext and tag.
```
OHTTP Encapsulated Response Byte Representation (8192 bytes total)
+---------------------+------------------------------------------+
| Nonce | Ciphertext |
| (32 bytes) | (8160 bytes = 8192-32) +
| +-----------------------------+------------+
| | Padded BHTTP Response | AEAD Tag |
| | (8144 bytes = 8192-32-16) | (16 bytes) |
+---------------------+------------------------------------------+
```
GET requests on an empty mailbox should block until a message is posted
or a timeout occurs. The timeout should be 30 seconds because that will
not exceed the default timeout for most HTTP clients.
The directory may optionally accept HTTP/1.1 POST requests without OHTTP
to mailbox endpoint URLs for backwards compatibility with BIP 78 senders.
#### OHTTP Sequence Diagram
```mermaid
sequenceDiagram
title OHTTP Sequence Diagram
participant C as Client
participant R as OHTTP Relay
box PaleVioletRed Payjoin Directory
participant G as OHTTP Gateway
participant D as HTTP Resource
end
C->>R: Relay Request<br/>FROM: Client IP<br/>[+ Encapsulated Request]
R->>G: Gateway Request<br/>FROM: Relay IP<br/>[+ Encapsulated Request]
G->>D: Request
D->>G: Response
G->>R: Gateway Response<br/>TO: Relay IP<br/>[+ Encapsulated Response]
R->>C: Relay Response<br/>TO: Client IP<br/>[+ Encapsulated Response]
```
### Relay/Directory interactions
RFC 9458 requires each OHTTP Relay to be configured to forward requests
to exactly one OHTTP Gateway. This requirement prevents receivers from
being able to choose any directory, and senders from choosing relays
independently. Without addressing this limitation, senders would have to
know which relays are appropriate to use for each directory, creating a
tendency for one directory and its affiliated relays to monopolize the
protocol.
In order to allow OHTTP Relays to be used with any directory, a
directory's OHTTP Gateway may advertise this allowed purpose. This
advertisement prevents OHTTP Relays from acting as open internet proxies,
which would otherwise allow anonymized access to arbitrary resources and
expose them to denial-of-service attacks, as well as other forms of abuse.
When the directory receives a GET request to the `/.well-known/ohttp-gateway`
path with an `allowed_purposes` query parameter, its response body
should contain a magic string in the same format as a TLS ALPN protocol
list (a U16BE length encoded list of U8 length encoded strings). The
magic string is `BIP77 454403bb-9f7b-4385-b31f-acd2dae20b7e`, offering
an unambiguous signal to relays that this OHTTP Gateway will accept
requests associated with this purpose from any relay.
By supporting this `allowed_purposes` parameter, the directory signals
to OHTTP Relays that it is willing to handle requests related to BIP 77,
removing the RFC 9458's requirement that relays and
Gateways be configured in a one-to-one relationship.
## Rationale
### Uppercase URL
In order to simplify parsing and allow QR encoders to use [Alphanumeric
QR
mode](https://www.rfc-editor.org/rfc/rfc9285.html#name-the-alphabet-used-in-base45),
which is more compact than Byte mode, the mailbox endpoint URL,
including the fragment parameters, is encoded in uppercase.
Unlike Bitcoin URI parameters, which require switching back to Byte
mode, the use of the URL fragment for session-specific parameters makes
it possible to stay in Alphanumeric mode.
### Parameter Ordering
The order of fragment parameters, Bitcoin URI parameters, as well as in the
sender's optional parameters have no defined meaning.
In the BIP 21 URI, the `pj` parameter mailbox endpoint URL SHOULD be the last
parameter to avoid QR mode switching.
Since variations might create a fingerprint for particular wallet software,
this document requires that fragment parameters MUST appear in reverse
lexicographical order.
### Session Expiration
The directory may hold a message for an offline Payjoin client until that
client comes online. However, the BIP 78 spec [
recommends](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#receiver-does-not-need-to-be-a-full-node)
broadcasting Original PSBTs in the case of an offline counterparty.
Doing so exposes a naïve, surveillance-vulnerable transaction, which
Payjoin intends to avoid.
Because BIP 78 is a synchronous protocol without a standard expiration
mechanism, and automated receivers are vulnerable to probing attacks,
BIP 78 encourages receivers to broadcast the Original PSBT after some
undefined expiration time.
Because BIP 77 is an asynchronous protocol, it requires an explicit [
session-specific fragment
parameter](#receiver-fragment-parameters), `EX`, to
communicate this expiration time to the sender.
There is no way for a sender to prevent a receiver from broadcasting the
fallback transaction extracted from the Original PSBT before the
receiver-specified expiration time.
### 64-bit Short ID Length
64 bits are sufficient to make the probability of experiencing a random
collision negligible. As of writing, the UTXO set has ~2^28 elements.
This is a very loose upper bound for the number of concurrent (non-spam)
sessions, for which the probability of a random collision will be less
than 1%. The actual number of sessions will of course be (orders of
magnitudes) lower given that sessions are short-lived. With ~2^21
sessions (a loose bound on number of transactions that can be confirmed
in 24 hours) the probability is less than 1e-6. These figures bound the
probability of a collision existing anywhere in the entire set, whereas
the probability for an individual session to experience a collision is
\<\< 1e-10 in either case.
### Complete UTXO Data
Complete UTXO data is required because this information is required for
signing and calculating fees for some input types.
### HTTP
HTTP is ubiquitous. Using simple HTTP polling allows even Bitcoin Core
to consider an implementation. Unlike a WebSockets protocol, plain HTTP
can benefit from metadata protection by using Oblivious HTTP.
### Oblivious HTTP
OHTTP protects sender and receiver IP addresses both from one another
and from the directory. This makes it more difficult for a directory to
correlate many Payjoin transactions with specific IP addresses.
OHTTP relays can be run as basic HTTP proxies from wallet providers or
third parties.
### Uniform Payloads
Encapsulated OHTTP payloads seen by the relay and directory, and
encrypted messages seen by the directory, are constructed to be uniform
so that these third-party services are unable to distinguish between
them.
Encapsulated OHTTP messages are 8192 bytes long, and begin with a
cleartext OHTTP header and an uncompressed key which is distinguishable
from random bytes but uniform across different encapsulated requests.
End-to-end encrypted messages are 7168 bytes long, and should be
indistinguishable from uniformly random bytes.
[ElligatorSwift as defined in BIP 324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#elligatorswift-encoding-of-curve-x-coordinates)
is used to encode encapsulated HPKE public keys prepended to the HPKE ciphertext
so that the directory can't distinguish between key material, the
ciphertext, and randomness. This ensures the two different protocol
messages are indistinguishable from each other as well as any protocol
extensions.
These padded sizes are sufficient for most PSBTs without exceeding the [
8KB
limit](https://www.geekersdigest.com/max-http-request-header-size-server-comparison/)
of many HTTP/1.1 web servers. 8KB is also too small for image sharing,
making misuse of the directory impractical.
#### Random Padding
The typical [zero padding recommended by the BHTTP
specification](https://www.rfc-editor.org/rfc/rfc9292.html#name-padding-and-truncation)
would make future use of [multi-hop OHTTP inspired by the Sphinx mix
format](https://github.com/orgs/payjoin/discussions/582) detectable from the
point of view of the directory. Random padding is allowed so long as the BHTTP
encoded request is not truncated.
By randomly padding OHTTP messages, any future use of such techniques would be
indistinguishable from clients that only implement standardized OHTTP. Since
this would limit a malicious directory's ability to censor any such requests in
the future, and such requests significantly bolster the privacy threat model
against malicious OHTTP relays or traffic analysis by a global passive
adversary, it is desirable to do so for standard OHTTP requests as well.
### Secp256k1 Hybrid Public Key Encryption
[RFC 9180 Hybrid Public Key
Encryption](https://www.rfc-editor.org/rfc/rfc9180.html)
(HPKE) is a modern IETF standard for secure
message exchange without TLS, since TLS is not available in Bitcoin Core.
This proposal uses `DHKEM(Secp256k1, HKDF-SHA256)` and
`ChaCha20Poly1305` AEAD for both OHTTP encapsulation and for end-to-end
encryption between the sender and receiver.
The receiver transmits its receiver key in [receiver fragment
parameters](#receiver-fragment-parameters). The sender shares
its reply key along with the Original PSBT. These keys are ephemeral and
must only be used for a single Payjoin Session.
#### Secp256k1-based DHKEM
[Secp256k1-based DHKEM for
HPKE](https://www.ietf.org/archive/id/draft-wahby-cfrg-hpke-kem-secp256k1-01.html)
is most appropriate because of secp256k1's availability in bitcoin
contexts.
#### ChaCha20Poly1305 AEAD
This authenticated encryption with additional data [
algorithm](https://en.wikipedia.org/wiki/ChaCha20-Poly1305)
is standardized in [RFC
8439](https://www.rfc-editor.org/rfc/rfc8439) and has high
performance. ChaCha20Poly1305 AEAD has been implemented [in Bitcoin
Core](https://github.com/bitcoin/bitcoin/pull/15649) for [
BIP 324 Encrypted
Transport](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki)
as well. This has widespread support in browsers and common
cryptographic libraries. AES-GCM is more widespread but slower without
hardware support and not typically already a dependency in bitcoin software.
#### HKDF-SHA256
SHA-256 is necessarily available in bitcoin contexts.
## Attack vectors
In addition to the attack vectors and mitigations in
[BIP 78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#attack-vectors),
this proposal has the following attack vectors.
### Directory Denial of Service
Since each mailbox stores arbitrary encrypted payloads, directories are
vulnerable to flooding. To mitigate such denial of service attacks,
directory operators may respond with `401` unauthorized unless an
authorization token is provided. Authorization tokens must be unlinkable
to preserve client privacy. A specific unlinkable authorization token
mechanism is out of the scope of this proposal.
### Network privacy
Oblivious HTTP must be used to protect the IP addresses of both sender
and receiver from the directory. This requires an OHTTP Key
Configuration to be shared in the Payjoin URI and for the directory to
support Oblivious HTTP.
Unlike BIP 78 implementations, sender and receiver clients will only see
the IP address of the directory and not that of the client they are
interacting with.
Senders that submit requests directly to the directory, without using
an OHTTP Relay, may reveal their IP address to the receiver since that
receiver also specifies the directory.
## Backwards compatibility
Senders not supporting Payjoin will just ignore the `pj` parameter and
proceed to typical address-based transaction flows.
All Payjoin versions use [Bitcoin URIs](#payjoin-uri).
Receivers may choose to accept BIP 78 payloads at their discretion.
A BIP 78 sender posts their request to the directory, which stores
and forwards it to the BIP 77 receiver. A backwards-compatible
receiver proceeds with the BIP 78 checks if the encapsulated response
body is UTF-8 plaintext, signifying BIP 78. In order to service the
request, a BIP 78 response must be returned to the sender within 30
seconds or else the directory should respond with an `unavailable` JSON
error code as [defined in BIP
78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#receivers-well-known-errors).
## Reference implementation
A production reference implementation client can be found at
<https://crates.io/crates/payjoin-cli>. Source code for the clients, the
directory, and development kit may be found here:
<https://github.com/payjoin/rust-payjoin>. Source code for an Oblivious
HTTP relay implementation may be found here:
<https://github.com/payjoin/ohttp-relay>.