Skip to content

Commit

Permalink
make payloads uniform by removing sender auth key
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nothingmuch authored and DanGould committed Oct 19, 2024
1 parent 604cb8f commit da11771
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 28 deletions.
28 changes: 8 additions & 20 deletions payjoin/src/hpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,27 +129,21 @@ impl<'de> serde::Deserialize<'de> for HpkePublicKey {
#[cfg(feature = "send")]
pub fn encrypt_message_a(
body: Vec<u8>,
encapsulation_pair: &HpkeKeyPair,
reply_pk: &HpkePublicKey,
receiver_pk: &HpkePublicKey,
) -> Result<Vec<u8>, HpkeError> {
let (encapsulated_key, mut encryption_context) =
hpke::setup_sender::<ChaCha20Poly1305, HkdfSha256, SecpK256HkdfSha256, _>(
&OpModeS::Auth((
encapsulation_pair.secret_key().0.clone(),
encapsulation_pair.public_key().0.clone(),
)),
&OpModeS::Base,
&receiver_pk.0,
INFO_A,
&mut OsRng,
)?;
let aad = encapsulation_pair.public_key().to_bytes().to_vec();
let mut plaintext = reply_pk.to_bytes().to_vec();
plaintext.extend(body);
let plaintext = pad_plaintext(&mut plaintext, PADDED_PLAINTEXT_A_LENGTH)?;
let ciphertext = encryption_context.seal(plaintext, &aad)?;
let ciphertext = encryption_context.seal(plaintext, &[])?;
let mut message_a = encapsulated_key.to_bytes().to_vec();
message_a.extend(&aad);
message_a.extend(&ciphertext);
Ok(message_a.to_vec())
}
Expand All @@ -167,21 +161,15 @@ pub fn decrypt_message_a(
cursor.read_exact(&mut enc).map_err(|_| HpkeError::PayloadTooShort)?;
let enc = EncappedKey::from_bytes(&enc)?;

let mut aad = [0u8; UNCOMPRESSED_PUBLIC_KEY_SIZE];
cursor.read_exact(&mut aad).map_err(|_| HpkeError::PayloadTooShort)?;
let encapsulation_pk = PublicKey::from_bytes(&aad)?;

let mut decryption_ctx =
hpke::setup_receiver::<ChaCha20Poly1305, HkdfSha256, SecpK256HkdfSha256>(
&OpModeR::Auth(encapsulation_pk.clone()),
&receiver_sk.0,
&enc,
INFO_A,
)?;
let mut decryption_ctx = hpke::setup_receiver::<
ChaCha20Poly1305,
HkdfSha256,
SecpK256HkdfSha256,
>(&OpModeR::Base, &receiver_sk.0, &enc, INFO_A)?;

let mut ciphertext = Vec::new();
cursor.read_to_end(&mut ciphertext).map_err(|_| HpkeError::PayloadTooShort)?;
let plaintext = decryption_ctx.open(&ciphertext, &aad)?;
let plaintext = decryption_ctx.open(&ciphertext, &[])?;

let reply_pk_bytes = &plaintext[..UNCOMPRESSED_PUBLIC_KEY_SIZE];
let reply_pk = HpkePublicKey(PublicKey::from_bytes(reply_pk_bytes)?);
Expand Down
9 changes: 1 addition & 8 deletions payjoin/src/send/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,6 @@ impl Sender {
let hpke_ctx = HpkeContext::new(rs);
let body = encrypt_message_a(
body,
&hpke_ctx.encapsulation_pair.clone(),
&hpke_ctx.reply_pair.public_key().clone(),
&hpke_ctx.receiver.clone(),
)
Expand Down Expand Up @@ -440,7 +439,6 @@ impl V2GetContext {
url.set_path(&subdir);
let body = encrypt_message_a(
Vec::new(),
&self.hpke_ctx.encapsulation_pair.clone(),
&self.hpke_ctx.reply_pair.public_key().clone(),
&self.hpke_ctx.receiver.clone(),
)
Expand Down Expand Up @@ -496,18 +494,13 @@ pub struct PsbtContext {
#[cfg(feature = "v2")]
struct HpkeContext {
receiver: HpkePublicKey,
encapsulation_pair: HpkeKeyPair,
reply_pair: HpkeKeyPair,
}

#[cfg(feature = "v2")]
impl HpkeContext {
pub fn new(receiver: HpkePublicKey) -> Self {
Self {
receiver,
encapsulation_pair: HpkeKeyPair::gen_keypair(),
reply_pair: HpkeKeyPair::gen_keypair(),
}
Self { receiver, reply_pair: HpkeKeyPair::gen_keypair() }
}
}

Expand Down

0 comments on commit da11771

Please sign in to comment.