Skip to content

Commit 10d90cf

Browse files
authored
Add initial warnings against OHTTP request reuse (payjoin#828)
# Document OHTTP Request Non-Reusability This PR addresses payjoin#819 by documenting that OHTTP-encapsulated requests must not be reused or retried, due to the risk of retransmitting ciphertext and violating OHTTP's privacy guarantees. ### What's Included: A top-level warning was added to the receive module docs (//!) to highlight the security implications of request reuse. Closes payjoin#819
2 parents 3b7230b + 372c360 commit 10d90cf

File tree

7 files changed

+47
-1
lines changed

7 files changed

+47
-1
lines changed

payjoin-ffi/src/send/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ impl WithReplyKey {
214214
}
215215

216216
/// Extract serialized Request and Context from a Payjoin Proposal.
217+
///
218+
/// Important: This request must not be retried or reused on failure.
219+
/// Retransmitting the same ciphertext breaks OHTTP privacy properties.
220+
/// The specific concern is that the relay can see that a request is being retried.
217221
pub fn extract_v2(
218222
&self,
219223
ohttp_relay: String,

payjoin-ffi/src/send/uni.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ impl WithReplyKey {
229229

230230
/// Extract serialized Request and Context from a Payjoin Proposal.
231231
///
232+
/// Important: This request must not be retried or reused on failure.
233+
/// Retransmitting the same ciphertext breaks OHTTP privacy properties.
234+
/// The specific concern is that the relay can see that a request is being retried,
235+
/// which leaks that it's all the same request.
236+
///
232237
/// This method requires the `rs` pubkey to be extracted from the endpoint
233238
/// and has no fallback to v1.
234239
pub fn extract_v2(

payjoin/src/core/receive/v1/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
//! 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) request URI.
2424
//!
2525
//! [reference implementation](https://github.com/payjoin/rust-payjoin/tree/master/payjoin-cli)
26+
//!
27+
//! OHTTP Privacy Warning
28+
//! Encapsulated requests whether GET or POST—**must not be retried or reused**.
29+
//! Retransmitting the same ciphertext (including via automatic retries) breaks the unlinkability and privacy guarantees of OHTTP,
30+
//! as it allows the relay to correlate requests by comparing ciphertexts.
31+
//! Note: Even fresh requests may be linkable via metadata (e.g. client IP, request timing),
32+
//! but request reuse makes correlation trivial for the relay.
2633
2734
use std::cmp::{max, min};
2835
use std::collections::BTreeMap;

payjoin/src/core/receive/v2/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
//! Receive BIP 77 Payjoin v2
2+
//!
3+
//! OHTTP Privacy Warning
4+
//! Encapsulated requests whether GET or POST—**must not be retried or reused**.
5+
//! Retransmitting the same ciphertext (including via automatic retries) breaks the unlinkability and privacy guarantees of OHTTP,
6+
//! as it allows the relay to correlate requests by comparing ciphertexts.
7+
//! Note: Even fresh requests may be linkable via metadata (e.g. client IP, request timing),
8+
//! but request reuse makes correlation trivial for the relay.
29
use std::str::FromStr;
310
use std::time::{Duration, SystemTime};
411

payjoin/src/core/send/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
//!
99
//! If you specifically need to use
1010
//! version 1, refer to the `send::v1` module documentation after enabling the `v1` feature.
11+
//!
12+
//! OHTTP Privacy Warning
13+
//! Encapsulated requests whether GET or POST—**must not be retried or reused**.
14+
//! Retransmitting the same ciphertext (including via automatic retries) breaks the unlinkability and privacy guarantees of OHTTP,
15+
//! as it allows the relay to correlate requests by comparing ciphertexts.
16+
//! Note: Even fresh requests may be linkable via metadata (e.g. client IP, request timing),
17+
//! but request reuse makes correlation trivial for the relay.
1118
1219
use std::str::FromStr;
1320

payjoin/src/core/send/multiparty/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ impl<'a> SenderBuilder<'a> {
3838
pub struct Sender(v2::Sender<v2::WithReplyKey>);
3939

4040
impl Sender {
41+
/// Important: This request must not be retried or reused on failure.
42+
/// Retransmitting the same ciphertext breaks OHTTP privacy properties.
43+
/// The specific concern is that the relay can see that a request is being retried,
44+
/// which leaks that it's all the same request.
4145
pub fn extract_v2(
4246
&self,
4347
ohttp_relay: impl IntoUrl,

payjoin/src/core/send/v2/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
//! [`nolooking`](https://github.com/chaincase-app/nolooking) for LND, or
2121
//! [`bitmask-core`](https://github.com/diba-io/bitmask-core) BDK integration. Bring your own
2222
//! wallet and http client.
23+
//!
24+
//! OHTTP Privacy Warning
25+
//! Encapsulated requests whether GET or POST—**must not be retried or reused**.
26+
//! Retransmitting the same ciphertext (including via automatic retries) breaks the unlinkability and privacy guarantees of OHTTP,
27+
//! as it allows the relay to correlate requests by comparing ciphertexts.
28+
//! Note: Even fresh requests may be linkable via metadata (e.g. client IP, request timing),
29+
//! but request reuse makes correlation trivial for the relay.
2330
2431
use bitcoin::hashes::{sha256, Hash};
2532
pub use error::{CreateRequestError, EncapsulationError};
@@ -206,11 +213,16 @@ pub struct WithReplyKey {
206213
impl State for WithReplyKey {}
207214

208215
impl Sender<WithReplyKey> {
209-
/// Extract serialized V1 Request and Context from a Payjoin Proposal
216+
/// Extract serialized V1 Request and Context from a Payjoin Proposal.
210217
pub fn extract_v1(&self) -> (Request, v1::V1Context) { self.v1.extract_v1() }
211218

212219
/// Extract serialized Request and Context from a Payjoin Proposal.
213220
///
221+
/// Important: This request must not be retried or reused on failure.
222+
/// Retransmitting the same ciphertext breaks OHTTP privacy properties.
223+
/// The specific concern is that the relay can see that a request is being retried,
224+
/// which leaks that it's all the same request.
225+
///
214226
/// This method requires the `rs` pubkey to be extracted from the endpoint
215227
/// and has no fallback to v1.
216228
pub fn extract_v2(

0 commit comments

Comments
 (0)