Gateway opt-in makes onion routing over relays possible #582
Replies: 6 comments
-
|
Having looked into this in more detail, we can do better than what I initially suggested, by borrowing from Sphinx similar to how LN does and avoid variable length packets. Sphinx style onion wrapping
The main difference is that the addressing data is variable length, when in Sphinx's original description it's fixed size. It's not clear how to handle HPKE parameter selection, the KEM has to be fixed, but the AEAD and KDF could be allowed to vary but this seems like asking for trouble with no clear motivation. A more rigid specification can also specify how to deviate from from RFC 8439's concatenation, while still maintaining compatibility with it apart from putting the tag at the beginning, but doing this in general while leaving the AEAD mode free to be parameterized seems like a recipe for disaster. References RFC 9548 (OHTTP) and 9292 (binary HTTP) and 9180 (HPKE), and Sphinx (original paper, improved construction, and a general framework according to whom's taxonomy we probably want RFC 9292 privacy leaksRFC 9292 specifies that lengths should be specified using a variable sized encoding, but does not require using the smallest byte size for a given numerical value, which means it's potentially non-canonical:
Secondly, RFC 9113, on which RFC 9292 is based, does not specify a canonical order for pseudo fields. This is a potential client fingerprint, and arguably a gap in the OHTTP specification, which should probably recommend that the encapsulated request be canonically encoded. Reply Blocks & Privacy Preserving IntroductionReply blocks are not required since OHTTP responses are given interactively. However, since the directory implements a store and forward service, similar to Sphinx nymservers, this suggests the possibility of using reply blocks in a manner similar to Pudding, which would allow establishment of a secure channel, e.g. for sender initiated payjoins (#559) and coalition formation with arbitrary co-spenders (#412) |
Beta Was this translation helpful? Give feedback.
-
|
I spent some time working out the details. The coding effort required to make The main conclusion is that we should use random padding instead of 0 padding in Actually implementing this will be painful since practically none of the public APIs for hpke, bhttp, or ohttp can be used in the manner described here. For this reason I first describe a simplified version, and separately Sphinx's KEM reuse trick. The construction is similar to Sphinx, but with a payload size of 0 and a OHTTP configuration is assumed to be fixed as it currently is in rust-payjoin. The KEM can actually vary, but for the KEM blinding trick it needs to at least be consistent and support blinding like DH based KEM does. The symmetric crypto for the requests is arbitrary, though lengths are important, but for fixed size responses this assumes Poly1305, although it seems that a similar hack could be used for e.g. an HMAC the same one would have to be used throughout. The symmetric cipher and the KDF can vary along the route, for simplicity this assumes fixed parameters. If they are really fixed then the 7 byte OHTTP header could be reduced to just a 1 byte key ID per hop. Minimal ApproachIn this variation, only filler strings are used to pad as in Sphinx. Variables are as in the Sphinx paper:
With the following additions:
Determining per request overhead (variable)First we can construct known size BHTTP request templates, one for each onion The per hop overhead is variable because each intermediate relay URI authority At hop
In principle (but not easily using the bhttp public API) this could be done in a This envelope is analogous to Sphinx's The length of these envelopes is denoted These cumulative sums of overheads are the lengths of the filler strings (which The total onion routing overhead Padding Innermost PayloadWith the total overhead computed, we can pad the innermost string. The innermost The only difference from normal OHTTP handling from the point of view of the For this reason in order to make the use of onion routing vs. only a single diff --git a/payjoin/src/ohttp.rs b/payjoin/src/ohttp.rs
index fb1a744..986ac1b 100644
--- a/payjoin/src/ohttp.rs
+++ b/payjoin/src/ohttp.rs
@@ -3,6 +3,7 @@ use std::{error, fmt};
use bitcoin::bech32::{self, EncodeError};
use bitcoin::key::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE;
+use hpke::rand_core::{OsRng, RngCore};
use crate::directory::ENCAPSULATED_MESSAGE_BYTES;
@@ -41,6 +42,7 @@ pub fn ohttp_encapsulate(
}
let mut bhttp_req = [0u8; PADDED_BHTTP_REQ_BYTES];
+ OsRng.fill_bytes(&mut bhttp_req);
bhttp_message.write_bhttp(bhttp::Mode::KnownLength, &mut bhttp_req.as_mut_slice())?;
let (encapsulated, ohttp_ctx) = ctx.encapsulate(&bhttp_req)?;Without this change, the directory will not only be able to detect whether or The innermost plaintext should therefore padded with random bytes to length Computing filler stringsFiller strings have lengths corresponding the cumulative overhead lengths The outermost filler string Finally From a code perspective, it's not clear how to best do this without changing the EncryptionEncryption proceed from the innermost encapsulated request to the outermost. After padding the innermost string, the filler string The plaintext has the following structure, apart from random padding this is the That is then encapsulated as the following OHTTP message, same as what is This is too large to encapsulate in an 8192 byte OHTTP request, but the last Note that the AEAD tag must be preserved, and ChaCha20-Poly1305 specifies that This can then be encoded similarly a normal OHTTP message: This OHTTP onion message similarly has This continues until the ciphertext for relay 1 is constructed, and it too can Note that at this first hop OHTTP requests and OHTTP onion requests may be Decryption & ForwardingDecryption of an onion request works exactly the same as with unmodified OHTTP, The AEAD tag and BHTTP request containing the truncated internal OHTTP message Response HandlingFor response handling, each relay encrypts the data, but instead of appending The client decrypts by XORing the keystreams with the ciphertext, and computing Unfortunately if using this trick for constant size responses, corruption is not Security should be argued more rigorously, but a hand-wavy argument for why this That said, for now this trick should still be considered a crime against KEM with blindingIn the Sphinx paper, This means that the 65 bytes of secp256k1 DHKEM overhead can be shared between In the bitcoin-hpke crate, an
First, a single sender ephemeral key Then The KDF for the encryption key and the blinding t erm must be domain separated |
Beta Was this translation helpful? Give feedback.
-
|
If we assume an network combined relay+directory nodes, then this design also lends itself to async multi-hop OHTTP requests, i.e. a mixnet instead of just onion routed OHTTP. An optional field on the proposal can include a reply block, again based on the Sphinx design, allowing the receiver to communicate with the sender on an arbitrary directory while hiding their chosen directory from the receiver. The sender polls the mailbox named as the final destination in the reply block(s) as they wait for responses. Multi hop OHTTP requests could be posted to something that isn't quite an OHTTP relay but an async mix node. This kind of relay returns immediately, with the HTTP status only indicating the OHTTP request was queued (i.e. no A BHTTP pseudo-field can include a reply block as well or instead of the reply block given to the receiver. If such a reply block is included in an async onion OHTTP message, the nested OHTTP (multi hop) message should be forwarded synchronously, and the reply block should be used by the relay to deliver the OHTTP response back to the client. If the All of this would be backwards compatible with plain OHTTP clients and receivers that don't support reply blocks. |
Beta Was this translation helpful? Give feedback.
-
|
A https://iacr.org/archive/asiacrypt2021/130900100/130900100.pdf Concrete updatable encryption (UAE with Bounded Updates) using nested symmetric crypto for reply blocks, provides padding with integrity protection analogous to forward paths: |
Beta Was this translation helpful? Give feedback.
-
|
this was a very helpful resource for me when implementing onion wrapping for the BOSS program assignment https://ellemouton.com/posts/sphinx/ also the LN spec is informative (and they have test data) https://github.com/lightning/bolts/blob/master/04-onion-routing.md I can share my code for this assignment if desirable (rust) |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
With payjoin/ohttp-relay#58, relays could also signal opt-in to accepting OHTTP traffic from other relays, exactly like the directory signals in #369.
When making an OHTTP request, the client constructs a request for the relay, and serializes it using binary HTTP. When this is a POST request with a PSBT, the body of the request is first padded to 7168 bytes, and then encrypted using the receiver or the sender's key. Then the binary HTTP request is padded to 8192 bytes encrypted that to the directory.
The encapsulation overhead is not 1024. The HPKE encapsulation overhead is 64 bytes for ECDH, uses uncompressed key, 16 bytes for AEAD tag. The binary HTTP overhead is 7 bytes, plus the HTTP method and target resource URI, which will vary.
When decapsulating, if an incoming message fails to decrypt, we could consider truncating it at 128 byte intervals, so 7 more decryption attempts (I believe the costly part is parsing the ECDH point and checking if it's on the curve [that only needs to be done once since it's a shared prefix], the AEAD and symmetric encryption should be comparatively cheap [and can be done by just checking if the last 128 byte aligned tags blocks match the tag, returning the one that matches if one matches]).Instead of padding to 8192 bytes, clients could pad to one of these lengths: 7296, 7424, 7552, 7680, 7808, 7936, 8064, 8192 (ideally using randomness in the choice), before encrypting the next layer. The last request would be padded with random data that does not get encrypted, up to 8192 and sent to the first relay. Upon decrypting the first binary http request, that relay would then pad the result with random data to 8192, and forward to the next relay in the chain, etc.Clients that don't use this mechanism should randomize the padding of the first request, so as to not reveal the fact that the next target resource is a directory instead of a relay, and to not reveal to the gateway whether or not this onion encryption mechanism was used prior to the most proximate relay.See below for how to do constant size.
This suggests that for maximizing privacy against a global passive adversary, relay operators should also be directory operators and vice versa, and also opt-in to sending and receiving cover traffic to each other, consider relaying traffic as in a Poisson mix (c.f. loopix), and that relays should use http2 or http3 to communicate with gateways in order to take advantage of multiplexing, making traffic analysis more difficult.
Beta Was this translation helpful? Give feedback.
All reactions