Skip to content

Commit

Permalink
Implement client e2ee using HPKE (#355)
Browse files Browse the repository at this point in the history
This PR effectively integrates bitcoin-hpke, replacing the custom aead
algorithm for point #216 point 4

Some things to consider:

* `payjoin implements `serde::{Serialize,Deserialize}` for
`bitcoin-hpke` keys where they could be enabled with a feature, though
that would further depart from `rust-hpke`'s main branch.
* hpke pubkey compression for both client e2ee and OhttpKeys is done
manually from bytes.
  • Loading branch information
DanGould authored Sep 26, 2024
2 parents 6460c85 + 3b9769b commit 1d7feb1
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 448 deletions.
2 changes: 1 addition & 1 deletion Cargo-minimal.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1582,9 +1582,9 @@ dependencies = [
"bhttp",
"bip21",
"bitcoin",
"bitcoin-hpke",
"bitcoin-ohttp",
"bitcoind",
"chacha20poly1305 0.10.1",
"http",
"log",
"ohttp-relay",
Expand Down
2 changes: 1 addition & 1 deletion Cargo-recent.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1582,9 +1582,9 @@ dependencies = [
"bhttp",
"bip21",
"bitcoin",
"bitcoin-hpke",
"bitcoin-ohttp",
"bitcoind",
"chacha20poly1305 0.10.1",
"http",
"log",
"ohttp-relay",
Expand Down
2 changes: 1 addition & 1 deletion payjoin-cli/example.config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ ohttp_relay="https://pj.bobspacebkk.com"

# (v2 only, optional) The HPKE keys which need to be fetched ahead of time from the pj_endpoint for the payjoin packets to be encrypted.
# These can now be fetched and no longer need to be configured.
ohttp_keys="AQAg3c9qovMZvPzLh8XHgD8q86WG7SmPQvPamCTvEoueKBsABAABAAM"
ohttp_keys="./path/to/ohttp_keys"
12 changes: 6 additions & 6 deletions payjoin-cli/src/app/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ impl AppConfig {

#[cfg(feature = "v2")]
let builder = {
use payjoin::base64::prelude::BASE64_URL_SAFE_NO_PAD;
use payjoin::base64::Engine;
builder
.set_override_option(
"pj_directory",
Expand All @@ -104,11 +102,13 @@ impl AppConfig {
.set_override_option(
"ohttp_keys",
matches.get_one::<String>("ohttp_keys").and_then(|s| {
BASE64_URL_SAFE_NO_PAD
.decode(s)
std::fs::read(s)
.map_err(|e| {
log::error!("Failed to decode ohttp_keys: {}", e);
ConfigError::Message(format!("Invalid ohttp_keys: {}", e))
log::error!("Failed to read ohttp_keys file: {}", e);
ConfigError::Message(format!(
"Failed to read ohttp_keys file: {}",
e
))
})
.ok()
}),
Expand Down
2 changes: 1 addition & 1 deletion payjoin-cli/src/db/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::*;
impl Database {
pub(crate) fn insert_recv_session(&self, session: ActiveSession) -> Result<()> {
let recv_tree = self.0.open_tree("recv_sessions")?;
let key = &session.public_key().serialize();
let key = &session.id();
let value = serde_json::to_string(&session).map_err(Error::Serialize)?;
recv_tree.insert(key.as_slice(), IVec::from(value.as_str()))?;
recv_tree.flush()?;
Expand Down
7 changes: 2 additions & 5 deletions payjoin-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,8 @@ fn cli() -> ArgMatches {
.help("The directory to store payjoin requests")
.value_parser(value_parser!(Url)),
);
receive_cmd = receive_cmd.arg(
Arg::new("ohttp_keys")
.long("ohttp-keys")
.help("The ohttp key config as a base64 encoded string"),
);
receive_cmd = receive_cmd
.arg(Arg::new("ohttp_keys").long("ohttp-keys").help("The ohttp key config file path"));
}

cmd = cmd.subcommand(receive_cmd);
Expand Down
5 changes: 3 additions & 2 deletions payjoin-cli/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ mod e2e {
let ohttp_keys =
payjoin::io::fetch_ohttp_keys(ohttp_relay.clone(), directory.clone(), cert.clone())
.await?;
let ohttp_keys_path = temp_dir.join("ohttp_keys");
tokio::fs::write(&ohttp_keys_path, ohttp_keys.encode()?).await?;

let receiver_rpchost = format!("http://{}/wallet/receiver", bitcoind.params.rpc_socket);
let sender_rpchost = format!("http://{}/wallet/sender", bitcoind.params.rpc_socket);
Expand All @@ -268,13 +270,12 @@ mod e2e {
.arg("--pj-directory")
.arg(&directory)
.arg("--ohttp-keys")
.arg(&ohttp_keys.to_string())
.arg(&ohttp_keys_path)
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
.expect("Failed to execute payjoin-cli");
let bip21 = get_bip21_from_receiver(cli_receive_initiator).await;

let cli_send_initiator = Command::new(payjoin_cli)
.arg("--rpchost")
.arg(&sender_rpchost)
Expand Down
4 changes: 1 addition & 3 deletions payjoin-directory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ fn init_ohttp() -> Result<ohttp::Server> {

// create or read from file
let server_config = ohttp::KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC))?;
let encoded_config = server_config.encode()?;
let b64_config = BASE64_URL_SAFE_NO_PAD.encode(encoded_config);
info!("ohttp-keys server config base64 UrlSafe: {:?}", b64_config);
info!("Initialized a new OHTTP Key Configuration. GET /ohttp-keys to fetch it.");
Ok(ohttp::Server::new(server_config)?)
}

Expand Down
4 changes: 2 additions & 2 deletions payjoin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ exclude = ["tests"]
send = []
receive = ["bitcoin/rand"]
base64 = ["bitcoin/base64"]
v2 = ["bitcoin/rand", "bitcoin/serde", "chacha20poly1305", "dep:http", "bhttp", "ohttp", "serde", "url/serde"]
v2 = ["bitcoin/rand", "bitcoin/serde", "hpke", "dep:http", "bhttp", "ohttp", "serde", "url/serde"]
io = ["reqwest/rustls-tls"]
danger-local-https = ["io", "reqwest/rustls-tls", "rustls"]

[dependencies]
bitcoin = { version = "0.32.2", features = ["base64"] }
bip21 = "0.5.0"
chacha20poly1305 = { version = "0.10.1", optional = true }
hpke = { package = "bitcoin-hpke", version = "0.13.0", optional = true }
log = { version = "0.4.14"}
http = { version = "1", optional = true }
bhttp = { version = "=0.5.1", optional = true }
Expand Down
Loading

0 comments on commit 1d7feb1

Please sign in to comment.