-
Notifications
You must be signed in to change notification settings - Fork 72
Remove pad plaintext from hpke #525
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
base: master
Are you sure you want to change the base?
Conversation
717a1a9 to
18e6985
Compare
Pull Request Test Coverage Report for Build 17480395424Details
💛 - Coveralls |
81a3c3c to
f00820b
Compare
f00820b to
f92af6c
Compare
|
@benalleng was there something blocking this PR? |
|
I need to come back to this, I totally forgot it existed. I think the issue I was having had to do with trying to simplify this down to an array with some dynamic length while also trying to stay true to this original comment from #405. https://github.com/DanGould/rust-payjoin/blob/4998108a1ac86bad14cdb9b2332a629f2e23b7e4/payjoin/src/hpke.rs#L247-L255 |
7887f4c to
b8a9582
Compare
|
A follow-up question, where is an appropriate place to actually pad this message to equal the array length of the |
I'm not sure I actually understand the motivation for this PR. To me |
| /// Message A is sent from the sender to the receiver containing an Original PSBT payload | ||
| pub fn encrypt_message_a( | ||
| body: Vec<u8>, | ||
| body: &[u8; PADDED_PLAINTEXT_A_LENGTH], |
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.
@spacebear21 I think the main motivation was just to get these function types to be strongly typed with this function signature. Other than that, I think it was a style thing to get rid of the function. How exactly it's implemented is not of the absolute greatest importance.
|
I guess the logical place to pad the message is right before |
b8a9582 to
3d89517
Compare
nothingmuch
left a comment
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.
oops i had a pending review comment from last week i never posted
a8e4477 to
afe3076
Compare
payjoin/src/core/send/v2/mod.rs
Outdated
| if body_len > PADDED_PLAINTEXT_A_LENGTH { | ||
| return Err(CreateRequestError::from(InternalCreateRequestError::Hpke( | ||
| crate::hpke::HpkeError::PayloadSize { | ||
| actual: body_len, | ||
| expected: PADDED_PLAINTEXT_A_LENGTH, | ||
| }, | ||
| ))); | ||
| } | ||
|
|
||
| let _ = write!(&mut &mut buf[..], "{base64}\n{query_params}"); |
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.
the main intention behind my suggestion was that length checking can be simplified, but even if not then at least the result should be .expect()ed instead of assigning to _
| if body_len > PADDED_PLAINTEXT_A_LENGTH { | |
| return Err(CreateRequestError::from(InternalCreateRequestError::Hpke( | |
| crate::hpke::HpkeError::PayloadSize { | |
| actual: body_len, | |
| expected: PADDED_PLAINTEXT_A_LENGTH, | |
| }, | |
| ))); | |
| } | |
| let _ = write!(&mut &mut buf[..], "{base64}\n{query_params}"); | |
| write!(&mut &mut buf[..], "{base64}\n{query_params}").map_err(|e| match e.kind() { | |
| std::io::ErrorKind::WriteZero => Err(CreateRequestError::from(InternalCreateRequestError::Hpke( | |
| crate::hpke::HpkeError::PayloadSize { | |
| actual: body_len, | |
| expected: PADDED_PLAINTEXT_A_LENGTH, | |
| }, | |
| _ => panic!("Uknown error: {e}"), // std::io::Write for &mut [u8] never fails, and write_all may fail if it didn't write all bytes | |
| }))))?; |
and if we eliminate actual from this errore, and FWIW i don't think PayloadSize is the correct representation of this failure. it makes sense for incoming data but for outgoing it's that the payload is too large, it's not that we expect it to be a certain size just under a certain size, then psbt's to_string can be used indirectly through fmt, eliminating more temp vars and length checks (since we won't need to calculate body_len at all)
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.
maybe more readable: map_err(|e| { assert!(e.kind() == WriteZero; Err(...) })
afe3076 to
833a789
Compare
a851044 to
b841c19
Compare
payjoin/src/core/receive/v2/mod.rs
Outdated
| Error::Implementation(ImplementationError::new(std::io::Error::other( | ||
| "failed to pad PSBT to PADDED_B_LENGTH", | ||
| ))) |
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 does not seem to be an error with the downstream Implementation. Do we need another variant?
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.
perhaps, not really sure how to categorize this error as I'm not really sure what a failed to convert to array error would be in this case
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 try_into() is a Result since there needs to be a check to ensure that the incoming Vec<u8> from serialize() is the same length as PADDED_PLAINTEXT_B_LENGTH. Since we are resizing just before this do you think it is ok to panic if this fails?
let mut payjoin_bytes = self.psbt.serialize();
payjoin_bytes.resize(PADDED_PLAINTEXT_B_LENGTH, 0);
let plaintext_array: [u8; PADDED_PLAINTEXT_B_LENGTH] =
payjoin_bytes.try_into().expect("Resized vec to array conversion should not fail");
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.
Here is some code an llm cooked up that skips the intermediate resizing entirely.
let plaintext_array = self
.psbt
.serialize()
.into_iter()
.chain(std::iter::repeat(0))
.take(PADDED_PLAINTEXT_B_LENGTH)
.collect::<Vec<_>>()
.try_into()
.expect("Resized vec to array conversion should not fail");
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 am leaning more towards the first option for readability alone if we're ok with an expect 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.
why not this suggestion? #525 (comment)
seems like if chaining "\n" and serialization of the params to that iter expression would get a bit much, if it's allocated as an array to the right size then just writing the PSBT followed by the params seems simplest to me, requires no expect, and clarifies the error condition to just the one relevant case (the serialized size of the psbt and the params was too large)
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.
Hmm ok, i am not sure i fully understand where we get the params from in this stage in the receiver flow to properly build this same syntax 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.
oops, my bad, i mixed up the threads and thought this was about sender code. no \n or params in this one... nonetheless, i still think the simplest approach for both is write!(&mut &mut buf[..], "{psbt}") or write!(&mut &mut buf[..], "{psbt}\n{params}") and checking the Result for the length check
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.
Ok, I have stumped myself on what seems to be something downstream from this but the sender accepts the buffer written as the raw psbt plaintext + padding, i.e.
cHNidP8BAFICAAAAAYZkepjEDPihy6ASG32F1d55chWrcPpNhkwPM+34gHr8AAAAAAD+////AZLxBSoBAAAAFgAU0ZXZmIg5DH3lYReEqMWoHb/s94sAAAAAAAEAmgIAAAACpKpk+qaExNhC1gUfjFf+/4HpwRDuK/Gjev+lQazVoeAAAAAAAP7////DmKKqh+wCyRLwpzhKrGvyCG7gskUIAhC10mUZLltf7gAAAAAA/v///wIA8gUqAQAAABYAFEIePkhXOqDf2b0+1WE7895FxfP2wOEFKgEAAAAWABSdggRMgtGkP+HEF95JoclVr/3wNmcAAAABAR8A8gUqAQAAABYAFEIePkhXOqDf2b0+1WE7895FxfP2AQhrAkcwRAIgaPMzXFsGOe2o6rsbwQ9MK/XiJ32r9B9KfCzVjLBaY1gCIHtibgdA+Ay3UerUkIlyDCgqbP5h1gnlBinv2tUa3sqVASEDjn1Mt9G9MsYDrbsKWnO6lYknOJjCEWbzi+lIumjVZcIAAA==...000000000000000000000...
However the reciever is only expecting the psbt serialized as it was before. Do we want to modify this to be able to accept psbt.to_string downstream like the sender?
I just went with write_all() for now but curious if you think it matters for this to try and get the sender and receiver format the same?
f97cbc1 to
6992469
Compare
This commit forces the message encryption to only accept a message of a length equal to the padded_plaintext length to just keep the function signatures strongly typed to accept only a specific value.
6992469 to
d0aa49f
Compare
We had a function that checked and then padded the length of the plaintext when running message encryption. Instead we should use a fixed array length and return an error if the message is of the wrong length.
I mentioned this is related to #402 but really that issue only has to do with public api calls