From c23021e6d6223998e47624ec75eea303778b2077 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 19 Oct 2024 15:29:10 -0400 Subject: [PATCH] Serialize Subdirectory PubKey as ElligatorSwift ElligatorSwift encodes public keys in such a way that they are indistinguishable from uniform randomness. --- payjoin/src/hpke.rs | 17 +++++++++++++++++ payjoin/src/receive/v2/mod.rs | 4 ++-- payjoin/src/send/mod.rs | 4 ++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/payjoin/src/hpke.rs b/payjoin/src/hpke.rs index cdbbf097..71cede48 100644 --- a/payjoin/src/hpke.rs +++ b/payjoin/src/hpke.rs @@ -89,6 +89,23 @@ impl HpkePublicKey { compressed_key.serialize_uncompressed().as_slice(), )?)) } + + pub fn from_ellswift_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 64 { + return Err(HpkeError::InvalidKeyLength); + } + let mut array = [0u8; 64]; + array.copy_from_slice(bytes); + let ellswift = bitcoin::secp256k1::ellswift::ElligatorSwift::from_array(array); + let pk = bitcoin::secp256k1::PublicKey::from_ellswift(ellswift); + Ok(HpkePublicKey(PublicKey::from_bytes(pk.serialize_uncompressed().as_slice())?)) + } + + pub fn to_ellswift_bytes(&self) -> Result<[u8; 64], HpkeError> { + let pk = bitcoin::secp256k1::PublicKey::from_slice(&self.0.to_bytes())?; + let ellswift_pk = bitcoin::secp256k1::ellswift::ElligatorSwift::from_pubkey(pk); + Ok(ellswift_pk.to_array()) + } } impl Deref for HpkePublicKey { diff --git a/payjoin/src/receive/v2/mod.rs b/payjoin/src/receive/v2/mod.rs index 5181146f..6f0a80e6 100644 --- a/payjoin/src/receive/v2/mod.rs +++ b/payjoin/src/receive/v2/mod.rs @@ -47,7 +47,7 @@ where } fn subdir_path_from_pubkey(pubkey: &HpkePublicKey) -> String { - BASE64_URL_SAFE_NO_PAD.encode(pubkey.to_compressed_bytes()) + BASE64_URL_SAFE_NO_PAD.encode(pubkey.to_ellswift_bytes().unwrap()) } /// A payjoin V2 receiver, allowing for polled requests to the @@ -201,7 +201,7 @@ impl Receiver { } /// The per-session public key to use as an identifier - pub fn id(&self) -> [u8; 33] { self.context.s.public_key().to_compressed_bytes() } + pub fn id(&self) -> [u8; 64] { self.context.s.public_key().to_ellswift_bytes().unwrap() } } /// The sender's original PSBT and optional parameters diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index aa3e8b55..b2771053 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -360,7 +360,7 @@ impl Sender { .decode(subdirectory) .map_err(ParseSubdirectoryError::SubdirectoryNotBase64)?; - HpkePublicKey::from_compressed_bytes(&pubkey_bytes) + HpkePublicKey::from_ellswift_bytes(&pubkey_bytes) .map_err(ParseSubdirectoryError::SubdirectoryInvalidPubkey) } @@ -435,7 +435,7 @@ impl V2GetContext { use crate::uri::UrlExt; let mut url = self.endpoint.clone(); let subdir = BASE64_URL_SAFE_NO_PAD - .encode(self.hpke_ctx.reply_pair.public_key().to_compressed_bytes()); + .encode(self.hpke_ctx.reply_pair.public_key().to_ellswift_bytes().unwrap()); url.set_path(&subdir); let body = encrypt_message_a( Vec::new(),