-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Session Initialization Implicit #364
Conversation
8f5aca5
to
a6c2a8a
Compare
b307fa2
to
f050e77
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just some nits. still familiarizing myself with the code, so not yet comfortable expressing stronger opinions
oh, one more thing: maybe consider renaming |
Perhaps even just |
60e41fb
to
c2eafff
Compare
The tests appear to be flakier than they were before, getting "Ohttp relay is long running" from the ohttp_relay shut down before the test completes on occasion 🤔 |
927d742
to
88d5da7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a rather large PR. Probably going to review in chunks. ApproachACK. Leaving Nits in a few places. Please feel free to ignore or tell me why i'm wrong about certain comments. This is the first time im looking at a PR for this repo. Thanks
@@ -4,8 +4,8 @@ use futures::StreamExt; | |||
use redis::{AsyncCommands, Client, ErrorKind, RedisError, RedisResult}; | |||
use tracing::debug; | |||
|
|||
const RES_COLUMN: &str = "res"; | |||
const REQ_COLUMN: &str = "req"; | |||
const DEFAULT_COLUMN: &str = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something descriptive maybe useful here when debugging in prod. even "default-col"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loving this protocol upgrade.
I'm confused by the sender polling in long_poll_post
- it looks like they're polling POST requests but based on my understanding of the BIP77 updates and PR description shouldn't they be polling GETs?
loop { | ||
let (req, ohttp_ctx) = v2_ctx.extract_req(self.config.ohttp_relay.clone())?; | ||
let response = http | ||
.post(req.url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the sender be polling GET requests here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohttp requests are always POSTs so that they're indistinguishable. Only when the OHTTP encapsulation is removed does the target server see the inner GET request.
I wonder if there's a better way to represent this in the state machine than POST/GET to avoid this confusion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see now the GET request is constructed in extract_req
, makes sense. It might help if the OHTTP post was extracted into a make_ohttp_request(req)
function or something along those lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea I plan to write for both v2.rs and integration.rs in a follow up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
messages a and b are distinguishable due to the use of Auth mode in message a
ed39bac
to
3756c47
Compare
A session is now initialized by generating keys and sharing them out of band. The semantics of the protocol are otherwise unchanged. Because sessions are implicit the typestate is now called `Receiver`.
3756c47
to
5d83737
Compare
rebased |
Encrypting Message A with an ephemeral "encapsulation key" allows the sender "reply key" corresponding to its subdirectory to be hidden from the directory.
Since the encapsulation keypair was ephemeral and not known to the receiver, but used in the Auth pattern it was included as authenticated associated data in the payload. This means that encrypt_message_a and encrypt_message_b had distinguishable bit patterns, the former starting with two uncompressed curve points (one for the DHKEM and one for this auth key), whereas the latter only had one (the DHKEM point). Since the sender's first message establishes a reply key, that key could be used in a second Auth HPKE setup after the Base setup, in order to prove that the sender can decrypt the receiver's reply. However, incentives are for the sender to provide a valid point, and the reply key is included in AEAD ciphertext, so this would add complexity without meaningful improving security or incentive compatibility.
The constants PADDED_PLAINTEXT_{A,B}_LENGTH now represent the maximum payload size for the input, whereas before the message A constant included the reply key size. This makes the PayloadTooLarge error represent a maximum and actual size that correspond to the inputs to encrypt_message_a and encrypt_message_b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 96c0eb6. I haven't looked very closely at the hpke changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 96c0eb6
I coudln't review everything in detail since I'm still familiarizing myself with the codebase, and as such I can't really call my fiddling "tested". However the parts I was able to scrutinize I'm comfortable with.
Perhaps consider adding a randomized test for the encrypted payloads' bit uniformity:
- generate 8 each of messages a and b, and 16 each of only a, and only b in separate runs
- for each of the 256 pairwise combinations, ensure their lengths are equal, XOR the two messages together, and OR this into an accumulator that starts as all 0x00s
- if at the end any of the bytes of this accumulator are still 0x00, fail the test
Such a test will generate a false negative with negligible probability if all encrypted messages share an identical byte at a given position by chance. It should fail deterministically before the ellswift changes due to the first byte being 0x01 in all messages in all runs (a only, b only, a+b). It should fail deterministically for a only messages because of the second 0x01 byte at position 65 (the previously aad'd reply key).
close #371 This randomized test will generate a false negative with negligible probabilityif all encrypted messages share an identical byte at a given position by chance. It should fail deterministically if any bit position has a fixed value. re #364 (review) from @nothingmuch I did check that this test would indeed fail before the ellswift changes by cherry-picking the test on 9c4880c
Close #363
A session is now initialized by generating keys and sharing them out
of band.
A POST request pushes data to a subdirectory, by convention defined as the receiver's public key. GET requests attempt to fetch data. For the sender, this means they poll GET requests instead of POST requests so bandwidth use is reduced.
Payjoin v1 backwards compatibility is supported by adding v1 aware handlers, one for POST requests directly at the relay, since v1 is unaware of OHTTP, and a PUT handler on the subdirectory's OHTTP endpoint that saves the updated Payjoin PSBT to another Redis column. The v1 POST handler is waiting on an update to that column to repond to the v1 sender before the request times out.
Lastly, Message A is now encrypted with an ephemeral "encapsulation key" so that the sender's "reply key," used to identify the sender subdirectory where Message B is stored is encrypted inside Message A's ciphertext in order to prevent the Payjoin Directory from finding it. This prevents the Payjoin Directory from being able to relate the sender and receiver subdirectories by searching for the subdirectory identifying "reply key" in Message A's associated data. All they'll find is the ephemeral key that has no relation to the subdirectory id.