Skip to content

Commit 6992469

Browse files
committed
Use array ref for padded plaintext params
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.
1 parent 31258eb commit 6992469

File tree

4 files changed

+125
-60
lines changed

4 files changed

+125
-60
lines changed

payjoin/src/core/hpke.rs

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'de> serde::Deserialize<'de> for HpkePublicKey {
171171

172172
/// Message A is sent from the sender to the receiver containing an Original PSBT payload
173173
pub fn encrypt_message_a(
174-
body: Vec<u8>,
174+
body: &[u8; PADDED_PLAINTEXT_A_LENGTH],
175175
reply_pk: &HpkePublicKey,
176176
receiver_pk: &HpkePublicKey,
177177
) -> Result<Vec<u8>, HpkeError> {
@@ -182,8 +182,6 @@ pub fn encrypt_message_a(
182182
INFO_A,
183183
&mut OsRng,
184184
)?;
185-
let mut body = body;
186-
pad_plaintext(&mut body, PADDED_PLAINTEXT_A_LENGTH)?;
187185
let mut plaintext = compressed_bytes_from_pubkey(reply_pk).to_vec();
188186
plaintext.extend(body);
189187
let ciphertext = encryption_context.seal(&plaintext, &[])?;
@@ -223,7 +221,7 @@ pub fn decrypt_message_a(
223221

224222
/// Message B is sent from the receiver to the sender containing a Payjoin PSBT payload or an error
225223
pub fn encrypt_message_b(
226-
mut plaintext: Vec<u8>,
224+
body: &[u8; PADDED_PLAINTEXT_B_LENGTH],
227225
receiver_keypair: &HpkeKeyPair,
228226
sender_pk: &HpkePublicKey,
229227
) -> Result<Vec<u8>, HpkeError> {
@@ -237,8 +235,7 @@ pub fn encrypt_message_b(
237235
INFO_B,
238236
&mut OsRng,
239237
)?;
240-
let plaintext: &[u8] = pad_plaintext(&mut plaintext, PADDED_PLAINTEXT_B_LENGTH)?;
241-
let ciphertext = encryption_context.seal(plaintext, &[])?;
238+
let ciphertext = encryption_context.seal(body, &[])?;
242239
let mut message_b = ellswift_bytes_from_encapped_key(&encapsulated_key)?.to_vec();
243240
message_b.extend(&ciphertext);
244241
Ok(message_b)
@@ -261,14 +258,6 @@ pub fn decrypt_message_b(
261258
Ok(plaintext)
262259
}
263260

264-
fn pad_plaintext(msg: &mut Vec<u8>, padded_length: usize) -> Result<&[u8], HpkeError> {
265-
if msg.len() > padded_length {
266-
return Err(HpkeError::PayloadTooLarge { actual: msg.len(), max: padded_length });
267-
}
268-
msg.resize(padded_length, 0);
269-
Ok(msg)
270-
}
271-
272261
/// Error from de/encrypting a v2 Hybrid Public Key Encryption payload.
273262
#[derive(Debug, PartialEq, Eq)]
274263
pub enum HpkeError {
@@ -304,7 +293,7 @@ impl fmt::Display for HpkeError {
304293
PayloadTooLarge { actual, max } => {
305294
write!(
306295
f,
307-
"Plaintext too large, max size is {max} bytes, actual size is {actual} bytes"
296+
"Plaintext length incorrect, expected size is {max} bytes, actual size is {actual} bytes"
308297
)
309298
}
310299
PayloadTooShort => write!(f, "Payload too small"),
@@ -332,13 +321,13 @@ mod test {
332321

333322
#[test]
334323
fn message_a_round_trip() {
335-
let mut plaintext = "foo".as_bytes().to_vec();
324+
let mut plaintext = [0u8; PADDED_PLAINTEXT_A_LENGTH];
336325

337326
let reply_keypair = HpkeKeyPair::gen_keypair();
338327
let receiver_keypair = HpkeKeyPair::gen_keypair();
339328

340329
let message_a = encrypt_message_a(
341-
plaintext.clone(),
330+
&plaintext,
342331
reply_keypair.public_key(),
343332
receiver_keypair.public_key(),
344333
)
@@ -350,14 +339,12 @@ mod test {
350339

351340
assert_eq!(decrypted.0.len(), PADDED_PLAINTEXT_A_LENGTH);
352341

353-
// decrypted plaintext is padded, so pad the expected plaintext
354-
plaintext.resize(PADDED_PLAINTEXT_A_LENGTH, 0);
355342
assert_eq!(decrypted, (plaintext.to_vec(), reply_keypair.public_key().clone()));
356343

357344
// ensure full plaintext round trips
358345
plaintext[PADDED_PLAINTEXT_A_LENGTH - 1] = 42;
359346
let message_a = encrypt_message_a(
360-
plaintext.clone(),
347+
&plaintext,
361348
reply_keypair.public_key(),
362349
receiver_keypair.public_key(),
363350
)
@@ -387,30 +374,17 @@ mod test {
387374
decrypt_message_a(&corrupted_message_a, receiver_keypair.secret_key().clone()),
388375
Err(HpkeError::Hpke(hpke::HpkeError::OpenError))
389376
);
390-
391-
plaintext.resize(PADDED_PLAINTEXT_A_LENGTH + 1, 0);
392-
assert_eq!(
393-
encrypt_message_a(
394-
plaintext.clone(),
395-
reply_keypair.public_key(),
396-
receiver_keypair.public_key(),
397-
),
398-
Err(HpkeError::PayloadTooLarge {
399-
actual: PADDED_PLAINTEXT_A_LENGTH + 1,
400-
max: PADDED_PLAINTEXT_A_LENGTH,
401-
})
402-
);
403377
}
404378

405379
#[test]
406380
fn message_b_round_trip() {
407-
let mut plaintext = "foo".as_bytes().to_vec();
381+
let mut plaintext = [0u8; PADDED_PLAINTEXT_B_LENGTH];
408382

409383
let reply_keypair = HpkeKeyPair::gen_keypair();
410384
let receiver_keypair = HpkeKeyPair::gen_keypair();
411385

412386
let message_b =
413-
encrypt_message_b(plaintext.clone(), &receiver_keypair, reply_keypair.public_key())
387+
encrypt_message_b(&plaintext, &receiver_keypair, reply_keypair.public_key())
414388
.expect("encryption should work");
415389

416390
assert_eq!(message_b.len(), PADDED_MESSAGE_BYTES);
@@ -423,13 +397,11 @@ mod test {
423397
.expect("decryption should work");
424398

425399
assert_eq!(decrypted.len(), PADDED_PLAINTEXT_B_LENGTH);
426-
// decrypted plaintext is padded, so pad the expected plaintext
427-
plaintext.resize(PADDED_PLAINTEXT_B_LENGTH, 0);
428400
assert_eq!(decrypted, plaintext.to_vec());
429401

430402
plaintext[PADDED_PLAINTEXT_B_LENGTH - 1] = 42;
431403
let message_b =
432-
encrypt_message_b(plaintext.clone(), &receiver_keypair, reply_keypair.public_key())
404+
encrypt_message_b(&plaintext, &receiver_keypair, reply_keypair.public_key())
433405
.expect("encryption should work");
434406

435407
assert_eq!(message_b.len(), PADDED_MESSAGE_BYTES);
@@ -481,15 +453,6 @@ mod test {
481453
),
482454
Err(HpkeError::Hpke(hpke::HpkeError::OpenError))
483455
);
484-
485-
plaintext.resize(PADDED_PLAINTEXT_B_LENGTH + 1, 0);
486-
assert_eq!(
487-
encrypt_message_b(plaintext.clone(), &receiver_keypair, reply_keypair.public_key()),
488-
Err(HpkeError::PayloadTooLarge {
489-
actual: PADDED_PLAINTEXT_B_LENGTH + 1,
490-
max: PADDED_PLAINTEXT_B_LENGTH
491-
})
492-
);
493456
}
494457

495458
/// Test that the encrypted payloads are uniform.
@@ -508,17 +471,17 @@ mod test {
508471
let receiver_keypair = HpkeKeyPair::gen_keypair();
509472
let reply_keypair = HpkeKeyPair::gen_keypair();
510473

511-
let plaintext_a = vec![0u8; PADDED_PLAINTEXT_A_LENGTH];
474+
let plaintext_a = [0u8; PADDED_PLAINTEXT_A_LENGTH];
512475
let message_a = encrypt_message_a(
513-
plaintext_a,
476+
&plaintext_a,
514477
reply_keypair.public_key(),
515478
receiver_keypair.public_key(),
516479
)
517480
.expect("encryption should work");
518481

519-
let plaintext_b = vec![0u8; PADDED_PLAINTEXT_B_LENGTH];
482+
let plaintext_b = [0u8; PADDED_PLAINTEXT_B_LENGTH];
520483
let message_b =
521-
encrypt_message_b(plaintext_b, &receiver_keypair, sender_keypair.public_key())
484+
encrypt_message_b(&plaintext_b, &receiver_keypair, sender_keypair.public_key())
522485
.expect("encryption should work");
523486

524487
messages_a.push(message_a);

payjoin/src/core/receive/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{error, fmt};
33
use crate::error_codes::ErrorCode::{
44
self, NotEnoughMoney, OriginalPsbtRejected, Unavailable, VersionUnsupported,
55
};
6+
// use crate::hpke::HpkeError::PayloadTooLarge;
67

78
/// The top-level error type for the payjoin receiver
89
#[derive(Debug)]
@@ -14,13 +15,18 @@ pub enum Error {
1415
///
1516
/// e.g. database errors, network failures, wallet errors
1617
Implementation(crate::ImplementationError),
18+
PayloadTooLarge {
19+
actual: usize,
20+
max: usize,
21+
},
1722
}
1823

1924
impl From<&Error> for JsonReply {
2025
fn from(e: &Error) -> Self {
2126
match e {
2227
Error::Protocol(e) => e.into(),
2328
Error::Implementation(_) => JsonReply::new(Unavailable, "Receiver error"),
29+
Error::PayloadTooLarge { actual: _, max: _ } => todo!("unimplemented"),
2430
}
2531
}
2632
}
@@ -34,6 +40,12 @@ impl fmt::Display for Error {
3440
match self {
3541
Error::Protocol(e) => write!(f, "Protocol error: {e}"),
3642
Error::Implementation(e) => write!(f, "Implementation error: {e}"),
43+
Error::PayloadTooLarge { actual, max } => {
44+
write!(
45+
f,
46+
"Plaintext length incorrect, expected size is {max} bytes, actual size is {actual} bytes"
47+
)
48+
}
3749
}
3850
}
3951
}
@@ -43,6 +55,7 @@ impl error::Error for Error {
4355
match self {
4456
Error::Protocol(e) => e.source(),
4557
Error::Implementation(e) => e.source(),
58+
Error::PayloadTooLarge { .. } => None,
4659
}
4760
}
4861
}

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ use super::error::{Error, InputContributionError};
4242
use super::{
4343
common, InternalPayloadError, JsonReply, OutputSubstitutionError, ProtocolError, SelectionError,
4444
};
45-
use crate::hpke::{decrypt_message_a, encrypt_message_b, HpkeKeyPair, HpkePublicKey};
45+
use crate::hpke::{
46+
decrypt_message_a, encrypt_message_b, HpkeKeyPair, HpkePublicKey, PADDED_PLAINTEXT_B_LENGTH,
47+
};
4648
use crate::ohttp::{
4749
ohttp_encapsulate, process_get_res, process_post_res, OhttpEncapsulationError, OhttpKeys,
4850
};
@@ -1032,10 +1034,22 @@ impl Receiver<PayjoinProposal> {
10321034

10331035
if let Some(e) = &self.session_context.reply_key {
10341036
// Prepare v2 payload
1037+
// let base64 = self.psbt.to_string();
10351038
let payjoin_bytes = self.psbt.serialize();
10361039
let sender_mailbox = short_id_from_pubkey(e);
10371040
target_resource = mailbox_endpoint(&self.session_context.directory, &sender_mailbox);
1038-
body = encrypt_message_b(payjoin_bytes, &self.session_context.receiver_key, e)?;
1041+
1042+
let mut buf = [0u8; PADDED_PLAINTEXT_B_LENGTH];
1043+
1044+
buf[..payjoin_bytes.len()].copy_from_slice(&payjoin_bytes);
1045+
// write!(&mut &mut buf[..], "{base64}\nquery_param").map_err(|e| {
1046+
// assert!(e.kind() == std::io::ErrorKind::WriteZero);
1047+
// Error::PayloadTooLarge {
1048+
// actual: self.psbt.to_string().len(),
1049+
// max: PADDED_PLAINTEXT_B_LENGTH,
1050+
// }
1051+
// })?;
1052+
body = encrypt_message_b(&buf, &self.session_context.receiver_key, e)?;
10391053
method = "POST";
10401054
} else {
10411055
// Prepare v2 wrapped and backwards-compatible v1 payload

0 commit comments

Comments
 (0)