Skip to content

Commit 3d89517

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 8efe975 commit 3d89517

File tree

3 files changed

+125
-72
lines changed

3 files changed

+125
-72
lines changed

payjoin/src/core/hpke.rs

Lines changed: 37 additions & 62 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, &[])?;
@@ -201,7 +199,10 @@ pub fn decrypt_message_a(
201199
let mut cursor = Cursor::new(message_a);
202200

203201
let mut enc_bytes = [0u8; ELLSWIFT_ENCODING_SIZE];
204-
cursor.read_exact(&mut enc_bytes).map_err(|_| HpkeError::PayloadTooShort)?;
202+
cursor.read_exact(&mut enc_bytes).map_err(|_| HpkeError::PayloadSize {
203+
actual: enc_bytes.len(),
204+
expected: ELLSWIFT_ENCODING_SIZE,
205+
})?;
205206
let enc = encapped_key_from_ellswift_bytes(&enc_bytes)?;
206207

207208
let mut decryption_ctx = hpke::setup_receiver::<
@@ -211,7 +212,10 @@ pub fn decrypt_message_a(
211212
>(&OpModeR::Base, &receiver_sk.0, &enc, INFO_A)?;
212213

213214
let mut ciphertext = Vec::new();
214-
cursor.read_to_end(&mut ciphertext).map_err(|_| HpkeError::PayloadTooShort)?;
215+
cursor.read_to_end(&mut ciphertext).map_err(|_| HpkeError::PayloadSize {
216+
actual: enc_bytes.len(),
217+
expected: ELLSWIFT_ENCODING_SIZE,
218+
})?;
215219
let plaintext = decryption_ctx.open(&ciphertext, &[])?;
216220

217221
let reply_pk = pubkey_from_compressed_bytes(&plaintext[..PUBLIC_KEY_SIZE])?;
@@ -223,7 +227,7 @@ pub fn decrypt_message_a(
223227

224228
/// Message B is sent from the receiver to the sender containing a Payjoin PSBT payload or an error
225229
pub fn encrypt_message_b(
226-
mut plaintext: Vec<u8>,
230+
body: &[u8; PADDED_PLAINTEXT_B_LENGTH],
227231
receiver_keypair: &HpkeKeyPair,
228232
sender_pk: &HpkePublicKey,
229233
) -> Result<Vec<u8>, HpkeError> {
@@ -237,8 +241,7 @@ pub fn encrypt_message_b(
237241
INFO_B,
238242
&mut OsRng,
239243
)?;
240-
let plaintext: &[u8] = pad_plaintext(&mut plaintext, PADDED_PLAINTEXT_B_LENGTH)?;
241-
let ciphertext = encryption_context.seal(plaintext, &[])?;
244+
let ciphertext = encryption_context.seal(body, &[])?;
242245
let mut message_b = ellswift_bytes_from_encapped_key(&encapsulated_key)?.to_vec();
243246
message_b.extend(&ciphertext);
244247
Ok(message_b)
@@ -249,34 +252,33 @@ pub fn decrypt_message_b(
249252
receiver_pk: HpkePublicKey,
250253
sender_sk: HpkeSecretKey,
251254
) -> Result<Vec<u8>, HpkeError> {
252-
let enc = message_b.get(..ELLSWIFT_ENCODING_SIZE).ok_or(HpkeError::PayloadTooShort)?;
255+
let enc = message_b.get(..ELLSWIFT_ENCODING_SIZE).ok_or(HpkeError::PayloadSize {
256+
actual: message_b.len(),
257+
expected: ELLSWIFT_ENCODING_SIZE,
258+
})?;
253259
let enc = encapped_key_from_ellswift_bytes(enc)?;
254260
let mut decryption_ctx = hpke::setup_receiver::<
255261
ChaCha20Poly1305,
256262
HkdfSha256,
257263
SecpK256HkdfSha256,
258264
>(&OpModeR::Auth(receiver_pk.0), &sender_sk.0, &enc, INFO_B)?;
259-
let plaintext = decryption_ctx
260-
.open(message_b.get(ELLSWIFT_ENCODING_SIZE..).ok_or(HpkeError::PayloadTooShort)?, &[])?;
265+
let plaintext = decryption_ctx.open(
266+
message_b.get(ELLSWIFT_ENCODING_SIZE..).ok_or(HpkeError::PayloadSize {
267+
actual: message_b.len(),
268+
expected: ELLSWIFT_ENCODING_SIZE,
269+
})?,
270+
&[],
271+
)?;
261272
Ok(plaintext)
262273
}
263274

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-
272275
/// Error from de/encrypting a v2 Hybrid Public Key Encryption payload.
273276
#[derive(Debug, PartialEq, Eq)]
274277
pub enum HpkeError {
275278
InvalidPublicKey,
276279
Hpke(hpke::HpkeError),
277280
InvalidKeyLength,
278-
PayloadTooLarge { actual: usize, max: usize },
279-
PayloadTooShort,
281+
PayloadSize { actual: usize, expected: usize },
280282
}
281283

282284
impl From<hpke::HpkeError> for HpkeError {
@@ -301,13 +303,12 @@ impl fmt::Display for HpkeError {
301303
match &self {
302304
Hpke(e) => e.fmt(f),
303305
InvalidKeyLength => write!(f, "Invalid Length"),
304-
PayloadTooLarge { actual, max } => {
306+
PayloadSize { actual, expected } => {
305307
write!(
306308
f,
307-
"Plaintext too large, max size is {max} bytes, actual size is {actual} bytes"
309+
"Plaintext length incorrect, expected size is {expected} bytes, actual size is {actual} bytes"
308310
)
309311
}
310-
PayloadTooShort => write!(f, "Payload too small"),
311312
InvalidPublicKey => write!(f, "Invalid public key"),
312313
}
313314
}
@@ -319,8 +320,8 @@ impl error::Error for HpkeError {
319320

320321
match &self {
321322
Hpke(e) => Some(e),
322-
PayloadTooLarge { .. } => None,
323-
InvalidKeyLength | PayloadTooShort => None,
323+
PayloadSize { .. } => None,
324+
InvalidKeyLength => None,
324325
InvalidPublicKey => None,
325326
}
326327
}
@@ -332,13 +333,13 @@ mod test {
332333

333334
#[test]
334335
fn message_a_round_trip() {
335-
let mut plaintext = "foo".as_bytes().to_vec();
336+
let mut plaintext = [0u8; PADDED_PLAINTEXT_A_LENGTH];
336337

337338
let reply_keypair = HpkeKeyPair::gen_keypair();
338339
let receiver_keypair = HpkeKeyPair::gen_keypair();
339340

340341
let message_a = encrypt_message_a(
341-
plaintext.clone(),
342+
&plaintext,
342343
reply_keypair.public_key(),
343344
receiver_keypair.public_key(),
344345
)
@@ -350,14 +351,12 @@ mod test {
350351

351352
assert_eq!(decrypted.0.len(), PADDED_PLAINTEXT_A_LENGTH);
352353

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

357356
// ensure full plaintext round trips
358357
plaintext[PADDED_PLAINTEXT_A_LENGTH - 1] = 42;
359358
let message_a = encrypt_message_a(
360-
plaintext.clone(),
359+
&plaintext,
361360
reply_keypair.public_key(),
362361
receiver_keypair.public_key(),
363362
)
@@ -387,30 +386,17 @@ mod test {
387386
decrypt_message_a(&corrupted_message_a, receiver_keypair.secret_key().clone()),
388387
Err(HpkeError::Hpke(hpke::HpkeError::OpenError))
389388
);
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-
);
403389
}
404390

405391
#[test]
406392
fn message_b_round_trip() {
407-
let mut plaintext = "foo".as_bytes().to_vec();
393+
let mut plaintext = [0u8; PADDED_PLAINTEXT_B_LENGTH];
408394

409395
let reply_keypair = HpkeKeyPair::gen_keypair();
410396
let receiver_keypair = HpkeKeyPair::gen_keypair();
411397

412398
let message_b =
413-
encrypt_message_b(plaintext.clone(), &receiver_keypair, reply_keypair.public_key())
399+
encrypt_message_b(&plaintext, &receiver_keypair, reply_keypair.public_key())
414400
.expect("encryption should work");
415401

416402
assert_eq!(message_b.len(), PADDED_MESSAGE_BYTES);
@@ -423,13 +409,11 @@ mod test {
423409
.expect("decryption should work");
424410

425411
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);
428412
assert_eq!(decrypted, plaintext.to_vec());
429413

430414
plaintext[PADDED_PLAINTEXT_B_LENGTH - 1] = 42;
431415
let message_b =
432-
encrypt_message_b(plaintext.clone(), &receiver_keypair, reply_keypair.public_key())
416+
encrypt_message_b(&plaintext, &receiver_keypair, reply_keypair.public_key())
433417
.expect("encryption should work");
434418

435419
assert_eq!(message_b.len(), PADDED_MESSAGE_BYTES);
@@ -481,15 +465,6 @@ mod test {
481465
),
482466
Err(HpkeError::Hpke(hpke::HpkeError::OpenError))
483467
);
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-
);
493468
}
494469

495470
/// Test that the encrypted payloads are uniform.
@@ -508,17 +483,17 @@ mod test {
508483
let receiver_keypair = HpkeKeyPair::gen_keypair();
509484
let reply_keypair = HpkeKeyPair::gen_keypair();
510485

511-
let plaintext_a = vec![0u8; PADDED_PLAINTEXT_A_LENGTH];
486+
let plaintext_a = [0u8; PADDED_PLAINTEXT_A_LENGTH];
512487
let message_a = encrypt_message_a(
513-
plaintext_a,
488+
&plaintext_a,
514489
reply_keypair.public_key(),
515490
receiver_keypair.public_key(),
516491
)
517492
.expect("encryption should work");
518493

519-
let plaintext_b = vec![0u8; PADDED_PLAINTEXT_B_LENGTH];
494+
let plaintext_b = [0u8; PADDED_PLAINTEXT_B_LENGTH];
520495
let message_b =
521-
encrypt_message_b(plaintext_b, &receiver_keypair, sender_keypair.public_key())
496+
encrypt_message_b(&plaintext_b, &receiver_keypair, sender_keypair.public_key())
522497
.expect("encryption should work");
523498

524499
messages_a.push(message_a);

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ use super::{
4343
common, InternalPayloadError, JsonReply, OutputSubstitutionError, ReplyableError,
4444
SelectionError,
4545
};
46-
use crate::hpke::{decrypt_message_a, encrypt_message_b, HpkeKeyPair, HpkePublicKey};
46+
use crate::hpke::{
47+
decrypt_message_a, encrypt_message_b, HpkeKeyPair, HpkePublicKey, PADDED_PLAINTEXT_B_LENGTH,
48+
};
4749
use crate::ohttp::{
4850
ohttp_encapsulate, process_get_res, process_post_res, OhttpEncapsulationError, OhttpKeys,
4951
};
@@ -1023,14 +1025,20 @@ impl Receiver<PayjoinProposal> {
10231025

10241026
if let Some(e) = &self.session_context.reply_key {
10251027
// Prepare v2 payload
1026-
let payjoin_bytes = self.psbt.serialize();
1028+
let mut payjoin_bytes = self.psbt.serialize();
1029+
payjoin_bytes.resize(PADDED_PLAINTEXT_B_LENGTH, 0);
10271030
let sender_mailbox = short_id_from_pubkey(e);
10281031
target_resource = self
10291032
.session_context
10301033
.directory
10311034
.join(&sender_mailbox.to_string())
10321035
.map_err(|e| ReplyableError::Implementation(ImplementationError::new(e)))?;
1033-
body = encrypt_message_b(payjoin_bytes, &self.session_context.receiver_key, e)?;
1036+
let payjoin_bytes = payjoin_bytes.try_into().map_err(|_| {
1037+
Error::ReplyToSender(ReplyableError::Implementation(ImplementationError::new(
1038+
std::io::Error::other("failed to pad PSBT to PADDED_B_LENGTH"),
1039+
)))
1040+
})?;
1041+
body = encrypt_message_b(&payjoin_bytes, &self.session_context.receiver_key, e)?;
10341042
method = "POST";
10351043
} else {
10361044
// Prepare v2 wrapped and backwards-compatible v1 payload

0 commit comments

Comments
 (0)