mirror of
https://github.com/bitcoin/bips.git
synced 2025-06-30 12:42:43 +00:00
737 lines
32 KiB
Markdown
737 lines
32 KiB
Markdown
|
```
|
|||
|
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.
|
|||
|
|
|||
|
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>.
|